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)  

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 struct menu_data {
27  struct cmdq_item *item;
28  int flags;
29 
30  struct cmd_find_state fs;
31  struct screen s;
32 
33  u_int px;
34  u_int py;
35 
36  struct menu *menu;
37  int choice;
38 
40  void *data;
41 };
42 
43 void
44 menu_add_items(struct menu *menu, const struct menu_item *items,
45  struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
46 {
47  const struct menu_item *loop;
48 
49  for (loop = items; loop->name != NULL; loop++)
50  menu_add_item(menu, loop, qitem, c, fs);
51 }
52 
53 void
54 menu_add_item(struct menu *menu, const struct menu_item *item,
55  struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
56 {
57  struct menu_item *new_item;
58  const char *key, *cmd;
59  char *s, *name;
60  u_int width;
61  int line;
62 
63  line = (item == NULL || item->name == NULL || *item->name == '\0');
64  if (line && menu->count == 0)
65  return;
66 
68  sizeof *menu->items);
69  new_item = &menu->items[menu->count++];
70  memset(new_item, 0, sizeof *new_item);
71 
72  if (line)
73  return;
74 
75  if (fs != NULL)
76  s = format_single_from_state(qitem, item->name, c, fs);
77  else
78  s = format_single(qitem, item->name, c, NULL, NULL, NULL);
79  if (*s == '\0') { /* no item if empty after format expanded */
80  menu->count--;
81  return;
82  }
83  if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) {
84  key = key_string_lookup_key(item->key, 0);
85  xasprintf(&name, "%s#[default] #[align=right](%s)", s, key);
86  } else
87  xasprintf(&name, "%s", s);
88  new_item->name = name;
89  free(s);
90 
91  cmd = item->command;
92  if (cmd != NULL) {
93  if (fs != NULL)
94  s = format_single_from_state(qitem, cmd, c, fs);
95  else
96  s = format_single(qitem, cmd, c, NULL, NULL, NULL);
97  } else
98  s = NULL;
99  new_item->command = s;
100  new_item->key = item->key;
101 
102  width = format_width(new_item->name);
103  if (width > menu->width)
104  menu->width = width;
105 }
106 
107 struct menu *
108 menu_create(const char *title)
109 {
110  struct menu *menu;
111 
112  menu = xcalloc(1, sizeof *menu);
113  menu->title = xstrdup(title);
115 
116  return (menu);
117 }
118 
119 void
121 {
122  u_int i;
123 
124  for (i = 0; i < menu->count; i++) {
125  free((void *)menu->items[i].name);
126  free((void *)menu->items[i].command);
127  }
128  free(menu->items);
129 
130  free((void *)menu->title);
131  free(menu);
132 }
133 
134 static struct screen *
135 menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy)
136 {
137  struct menu_data *md = c->overlay_data;
138 
139  return (&md->s);
140 }
141 
142 static void
144 {
145  struct menu_data *md = c->overlay_data;
146  struct tty *tty = &c->tty;
147  struct screen *s = &md->s;
148  struct menu *menu = md->menu;
149  struct screen_write_ctx ctx;
150  u_int i, px = md->px, py = md->py;
151  struct grid_cell gc;
152 
153  style_apply(&gc, c->session->curw->window->options, "mode-style", NULL);
154 
155  screen_write_start(&ctx, s);
156  screen_write_clearscreen(&ctx, 8);
157  screen_write_menu(&ctx, menu, md->choice, &gc);
158  screen_write_stop(&ctx);
159 
160  for (i = 0; i < screen_size_y(&md->s); i++) {
161  tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i,
162  &grid_default_cell, NULL);
163  }
164 }
165 
166 static void
168 {
169  struct menu_data *md = c->overlay_data;
170 
171  if (md->item != NULL)
172  cmdq_continue(md->item);
173 
174  if (md->cb != NULL)
175  md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
176 
177  screen_free(&md->s);
178  menu_free(md->menu);
179  free(md);
180 }
181 
182 static int
183 menu_key_cb(struct client *c, struct key_event *event)
184 {
185  struct menu_data *md = c->overlay_data;
186  struct menu *menu = md->menu;
187  struct mouse_event *m = &event->m;
188  u_int i;
189  int count = menu->count, old = md->choice;
190  const char *name = NULL;
191  const struct menu_item *item;
192  struct cmdq_state *state;
193  enum cmd_parse_status status;
194  char *error;
195 
196  if (KEYC_IS_MOUSE(event->key)) {
197  if (md->flags & MENU_NOMOUSE) {
198  if (MOUSE_BUTTONS(m->b) != 0)
199  return (1);
200  return (0);
201  }
202  if (m->x < md->px ||
203  m->x > md->px + 4 + menu->width ||
204  m->y < md->py + 1 ||
205  m->y > md->py + 1 + count - 1) {
206  if (~md->flags & MENU_STAYOPEN) {
207  if (MOUSE_RELEASE(m->b))
208  return (1);
209  } else {
210  if (!MOUSE_RELEASE(m->b) &&
211  MOUSE_WHEEL(m->b) == 0 &&
212  !MOUSE_DRAG(m->b))
213  return (1);
214  }
215  if (md->choice != -1) {
216  md->choice = -1;
218  }
219  return (0);
220  }
221  if (~md->flags & MENU_STAYOPEN) {
222  if (MOUSE_RELEASE(m->b))
223  goto chosen;
224  } else {
225  if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b))
226  goto chosen;
227  }
228  md->choice = m->y - (md->py + 1);
229  if (md->choice != old)
231  return (0);
232  }
233  for (i = 0; i < (u_int)count; i++) {
234  name = menu->items[i].name;
235  if (name == NULL || *name == '-')
236  continue;
237  if (event->key == menu->items[i].key) {
238  md->choice = i;
239  goto chosen;
240  }
241  }
242  switch (event->key & ~KEYC_MASK_FLAGS) {
243  case KEYC_UP:
244  case 'k':
245  if (old == -1)
246  old = 0;
247  do {
248  if (md->choice == -1 || md->choice == 0)
249  md->choice = count - 1;
250  else
251  md->choice--;
252  name = menu->items[md->choice].name;
253  } while ((name == NULL || *name == '-') && md->choice != old);
255  return (0);
256  case KEYC_BSPACE:
257  if (~md->flags & MENU_TAB)
258  break;
259  return (1);
260  case '\011': /* Tab */
261  if (~md->flags & MENU_TAB)
262  break;
263  if (md->choice == count - 1)
264  return (1);
265  /* FALLTHROUGH */
266  case KEYC_DOWN:
267  case 'j':
268  if (old == -1)
269  old = 0;
270  do {
271  if (md->choice == -1 || md->choice == count - 1)
272  md->choice = 0;
273  else
274  md->choice++;
275  name = menu->items[md->choice].name;
276  } while ((name == NULL || *name == '-') && md->choice != old);
278  return (0);
279  case 'g':
280  case KEYC_PPAGE:
281  case '\002': /* C-b */
282  if (md->choice > 5)
283  md->choice -= 5;
284  else
285  md->choice = 0;
286  while (md->choice != count && (name == NULL || *name == '-'))
287  md->choice++;
288  if (md->choice == count)
289  md->choice = -1;
291  break;
292  case 'G':
293  case KEYC_NPAGE:
294  if (md->choice > count - 6)
295  md->choice = count - 1;
296  else
297  md->choice += 5;
298  while (md->choice != -1 && (name == NULL || *name == '-'))
299  md->choice--;
301  break;
302  case '\006': /* C-f */
303  break;
304  case '\r':
305  goto chosen;
306  case '\033': /* Escape */
307  case '\003': /* C-c */
308  case '\007': /* C-g */
309  case 'q':
310  return (1);
311  }
312  return (0);
313 
314 chosen:
315  if (md->choice == -1)
316  return (1);
317  item = &menu->items[md->choice];
318  if (item->name == NULL || *item->name == '-') {
319  if (md->flags & MENU_STAYOPEN)
320  return (0);
321  return (1);
322  }
323  if (md->cb != NULL) {
324  md->cb(md->menu, md->choice, item->key, md->data);
325  md->cb = NULL;
326  return (1);
327  }
328 
329  if (md->item != NULL)
330  event = cmdq_get_event(md->item);
331  else
332  event = NULL;
333  state = cmdq_new_state(&md->fs, event, 0);
334 
335  status = cmd_parse_and_append(item->command, NULL, c, state, &error);
336  if (status == CMD_PARSE_ERROR) {
337  cmdq_append(c, cmdq_get_error(error));
338  free(error);
339  }
340  cmdq_free_state(state);
341 
342  return (1);
343 }
344 
345 int
346 menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
347  u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
348  void *data)
349 {
350  struct menu_data *md;
351  u_int i;
352  const char *name;
353 
354  if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
355  return (-1);
356  if (px + menu->width + 4 > c->tty.sx)
357  px = c->tty.sx - menu->width - 4;
358  if (py + menu->count + 2 > c->tty.sy)
359  py = c->tty.sy - menu->count - 2;
360 
361  md = xcalloc(1, sizeof *md);
362  md->item = item;
363  md->flags = flags;
364 
365  if (fs != NULL)
366  cmd_find_copy_state(&md->fs, fs);
367  screen_init(&md->s, menu->width + 4, menu->count + 2, 0);
368  if (~md->flags & MENU_NOMOUSE)
369  md->s.mode |= MODE_MOUSE_ALL;
370  md->s.mode &= ~~MODE_CURSOR;
371 
372  md->px = px;
373  md->py = py;
374 
375  md->menu = menu;
376  if (md->flags & MENU_NOMOUSE) {
377  for (i = 0; i < menu->count; i++) {
378  name = menu->items[i].name;
379  if (name != NULL && *name != '-')
380  break;
381  }
382  if (i != menu->count)
383  md->choice = i;
384  else
385  md->choice = -1;
386  } else
387  md->choice = -1;
388 
389  md->cb = cb;
390  md->data = data;
391 
394  return (0);
395 }
void cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src)
Definition: cmd-find.c:689
enum cmd_parse_status cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, struct client *c, struct cmdq_state *state, char **error)
Definition: cmd-parse.c:641
struct cmdq_item * cmdq_get_error(const char *error)
Definition: cmd-queue.c:671
void cmdq_free_state(struct cmdq_state *state)
Definition: cmd-queue.c:244
struct key_event * cmdq_get_event(struct cmdq_item *item)
Definition: cmd-queue.c:185
struct cmdq_item * cmdq_append(struct client *c, struct cmdq_item *item)
Definition: cmd-queue.c:288
struct cmdq_state * cmdq_new_state(struct cmd_find_state *current, struct key_event *event, int flags)
Definition: cmd-queue.c:206
void cmdq_continue(struct cmdq_item *item)
Definition: cmd-queue.c:434
#define __unused
Definition: compat.h:60
u_int format_width(const char *expanded)
Definition: format-draw.c:1002
char * format_single(struct cmdq_item *item, const char *fmt, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp)
Definition: format.c:4621
char * format_single_from_state(struct cmdq_item *item, const char *fmt, struct client *c, struct cmd_find_state *fs)
Definition: format.c:4635
const struct grid_cell grid_default_cell
Definition: grid.c:39
key_code key
Definition: key-string.c:32
const char * key_string_lookup_key(key_code key, int with_flags)
Definition: key-string.c:265
const char * name
Definition: layout-set.c:38
void menu_add_items(struct menu *menu, const struct menu_item *items, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
Definition: menu.c:44
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
static int menu_key_cb(struct client *c, struct key_event *event)
Definition: menu.c:183
static void menu_free_cb(struct client *c)
Definition: menu.c:167
static struct screen * menu_mode_cb(struct client *c, u_int *cx, u_int *cy)
Definition: menu.c:135
static void menu_draw_cb(struct client *c, struct screen_redraw_ctx *ctx0)
Definition: menu.c:143
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
void screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice, const struct grid_cell *choice_gc)
Definition: screen-write.c:626
void screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
Definition: screen-write.c:284
void screen_write_stop(struct screen_write_ctx *ctx)
Definition: screen-write.c:296
void screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
Definition: screen.c:74
void screen_free(struct screen *s)
Definition: screen.c:122
void server_client_set_overlay(struct client *c, u_int delay, overlay_check_cb checkcb, overlay_mode_cb modecb, overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb, void *data)
Definition: server-client.c:90
Definition: tmux.h:1608
struct tty tty
Definition: tmux.h:1640
void * overlay_data
Definition: tmux.h:1757
struct session * session
Definition: tmux.h:1743
uint64_t flags
Definition: tmux.h:1703
Definition: cmd.c:212
struct key_event event
Definition: cmd-queue.c:82
int flags
Definition: cmd-queue.c:78
key_code key
Definition: tmux.h:1267
Definition: menu.c:26
struct cmdq_item * item
Definition: menu.c:27
menu_choice_cb cb
Definition: menu.c:39
struct cmd_find_state fs
Definition: menu.c:30
struct menu * menu
Definition: menu.c:36
void * data
Definition: menu.c:40
int flags
Definition: menu.c:28
int choice
Definition: menu.c:37
u_int py
Definition: menu.c:34
struct screen s
Definition: menu.c:31
u_int px
Definition: menu.c:33
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
struct menu_item * items
Definition: tmux.h:894
const char * title
Definition: tmux.h:893
u_int count
Definition: tmux.h:895
u_int width
Definition: tmux.h:896
u_int y
Definition: tmux.h:1247
u_int b
Definition: tmux.h:1248
u_int x
Definition: tmux.h:1246
Definition: tmux.h:816
u_int cy
Definition: tmux.h:824
int mode
Definition: tmux.h:832
u_int cx
Definition: tmux.h:823
struct winlink * curw
Definition: tmux.h:1192
Definition: tmux.h:1304
u_int sx
Definition: tmux.h:1308
u_int sy
Definition: tmux.h:1309
struct options * options
Definition: tmux.h:1085
void style_apply(struct grid_cell *gc, struct options *oo, const char *name, struct format_tree *ft)
Definition: style.c:298
#define KEYC_UNKNOWN
Definition: tmux.h:114
#define MENU_NOMOUSE
Definition: tmux.h:3062
#define KEYC_IS_MOUSE(key)
Definition: tmux.h:144
#define MOUSE_RELEASE(b)
Definition: tmux.h:1234
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, u_int, u_int, const struct grid_cell *, int *)
Definition: tty.c:1281
#define CLIENT_REDRAWOVERLAY
Definition: tmux.h:1679
#define KEYC_NONE
Definition: tmux.h:113
cmd_parse_status
Definition: tmux.h:1483
@ CMD_PARSE_ERROR
Definition: tmux.h:1485
#define KEYC_MASK_FLAGS
Definition: tmux.h:137
#define screen_size_y(s)
Definition: tmux.h:882
#define MOUSE_DRAG(b)
Definition: tmux.h:1233
#define MODE_MOUSE_ALL
Definition: tmux.h:610
#define MENU_TAB
Definition: tmux.h:3063
@ KEYC_UP
Definition: tmux.h:246
@ KEYC_PPAGE
Definition: tmux.h:242
@ KEYC_DOWN
Definition: tmux.h:247
@ KEYC_BSPACE
Definition: tmux.h:222
@ KEYC_NPAGE
Definition: tmux.h:241
#define MENU_STAYOPEN
Definition: tmux.h:3064
void(* menu_choice_cb)(struct menu *, u_int, key_code, void *)
Definition: tmux.h:898
#define MOUSE_WHEEL(b)
Definition: tmux.h:1232
#define MOUSE_BUTTONS(b)
Definition: tmux.h:1231
void * xreallocarray(void *ptr, size_t nmemb, size_t size)
Definition: xmalloc.c:61
int xasprintf(char **ret, const char *fmt,...)
Definition: xmalloc.c:109
void * xcalloc(size_t nmemb, size_t size)
Definition: xmalloc.c:41
char * xstrdup(const char *str)
Definition: xmalloc.c:89