tmux  3.2a
About: tmux is a terminal multiplexer that lets you switch easily between several programs in one terminal.
  Fossies Dox: tmux-3.2a.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

cmd-display-menu.c
Go to the documentation of this file.
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 
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 
62 };
63 
64 static int
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. */
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;
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')) {
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)
391  else if (args_has(args, 'E'))
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 }
int args_has(struct args *args, u_char flag)
Definition: arguments.c:259
const char * args_get(struct args *args, u_char flag)
Definition: arguments.c:295
long long args_percentage(struct args *args, u_char flag, long long minval, long long maxval, long long curval, char **cause)
Definition: arguments.c:381
static enum cmd_retval cmd_display_menu_exec(struct cmd *, struct cmdq_item *)
static int cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h)
const struct cmd_entry cmd_display_popup_entry
const struct cmd_entry cmd_display_menu_entry
static enum cmd_retval cmd_display_popup_exec(struct cmd *, struct cmdq_item *)
struct client * cmdq_get_target_client(struct cmdq_item *item)
Definition: cmd-queue.c:157
struct cmd_find_state * cmdq_get_target(struct cmdq_item *item)
Definition: cmd-queue.c:171
struct key_event * cmdq_get_event(struct cmdq_item *item)
Definition: cmd-queue.c:185
void cmdq_error(struct cmdq_item *item, const char *fmt,...)
Definition: cmd-queue.c:833
struct args * cmd_get_args(struct cmd *cmd)
Definition: cmd.c:393
#define _PATH_BSHELL
Definition: compat.h:98
char * format_single_from_target(struct cmdq_item *item, const char *fmt)
Definition: format.c:4643
void format_add(struct format_tree *ft, const char *key, const char *fmt,...)
Definition: format.c:3126
char * format_expand(struct format_tree *ft, const char *fmt)
Definition: format.c:4609
struct format_tree * format_create_from_target(struct cmdq_item *item)
Definition: format.c:4675
key_code key_string_lookup_string(const char *string)
Definition: key-string.c:165
key_code key
Definition: key-string.c:32
const char * name
Definition: layout-set.c:38
void log_debug(const char *msg,...)
Definition: log.c:130
void menu_add_item(struct menu *menu, const struct menu_item *item, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
Definition: menu.c:54
struct menu * menu_create(const char *title)
Definition: menu.c:108
int menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, void *data)
Definition: menu.c:346
void menu_free(struct menu *menu)
Definition: menu.c:120
const char * options_get_string(struct options *oo, const char *name)
Definition: options.c:686
long long options_get_number(struct options *oo, const char *name)
Definition: options.c:699
int popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd, struct client *c, struct session *s, popup_close_cb cb, void *arg)
Definition: popup.c:321
void server_client_clear_overlay(struct client *c)
const char * server_client_get_cwd(struct client *c, struct session *s)
int status_at_line(struct client *c)
Definition: status.c:210
u_int status_line_size(struct client *c)
Definition: status.c:223
Definition: tmux.h:1435
int argc
Definition: tmux.h:1437
char ** argv
Definition: tmux.h:1438
Definition: tmux.h:1608
struct tty tty
Definition: tmux.h:1640
struct status_line status
Definition: tmux.h:1652
struct session * session
Definition: tmux.h:1743
overlay_draw_cb overlay_draw
Definition: tmux.h:1754
Definition: tmux.h:1525
const char * name
Definition: tmux.h:1526
struct window_pane * wp
Definition: tmux.h:1454
struct session * s
Definition: tmux.h:1451
struct winlink * wl
Definition: tmux.h:1452
Definition: cmd.c:212
struct window_pane * wp
Definition: format.c:136
struct session * s
Definition: format.c:133
struct winlink * wl
Definition: format.c:134
struct window * w
Definition: format.c:135
struct cmdq_item * item
Definition: format.c:139
struct mouse_event m
Definition: format.c:144
key_code key
Definition: tmux.h:889
const char * command
Definition: tmux.h:890
const char * name
Definition: tmux.h:888
Definition: tmux.h:892
u_int count
Definition: tmux.h:895
u_int width
Definition: tmux.h:896
u_int y
Definition: tmux.h:1247
u_int x
Definition: tmux.h:1246
Definition: tmux.h:1179
struct options * options
Definition: tmux.h:1199
struct style_ranges ranges
Definition: tmux.h:1554
struct status_line_entry entries[5]
Definition: tmux.h:1564
u_int argument
Definition: tmux.h:782
enum style_range_type type
Definition: tmux.h:781
u_int start
Definition: tmux.h:784
Definition: tmux.h:1304
u_int sx
Definition: tmux.h:1308
u_int sy
Definition: tmux.h:1309
int flags
Definition: tmux.h:1355
u_int xoff
Definition: tmux.h:971
u_int yoff
Definition: tmux.h:972
u_int sy
Definition: tmux.h:969
u_int sx
Definition: tmux.h:968
int checkshell(const char *shell)
Definition: tmux.c:80
#define MENU_NOMOUSE
Definition: tmux.h:3062
cmd_retval
Definition: tmux.h:1475
@ CMD_RETURN_NORMAL
Definition: tmux.h:1477
@ CMD_RETURN_ERROR
Definition: tmux.h:1476
@ CMD_RETURN_WAIT
Definition: tmux.h:1478
#define CMD_CLIENT_CFLAG
Definition: tmux.h:1542
#define CMD_AFTERHOOK
Definition: tmux.h:1541
#define CMD_TARGET_PANE_USAGE
Definition: tmux.h:1858
#define POPUP_CLOSEEXITZERO
Definition: tmux.h:3079
#define MENU_STAYOPEN
Definition: tmux.h:3064
#define POPUP_CLOSEEXIT
Definition: tmux.h:3078
@ STYLE_RANGE_WINDOW
Definition: tmux.h:778
@ CMD_FIND_PANE
Definition: tmux.h:1443
int tty_window_offset(struct tty *, u_int *, u_int *, u_int *, u_int *)
Definition: tty.c:780
char * xstrdup(const char *str)
Definition: xmalloc.c:89