rofi  1.7.5
dmenu.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2022 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
29 #define G_LOG_DOMAIN "Modes.DMenu"
30 
31 #include "modes/dmenu.h"
32 #include "helper.h"
33 #include "rofi-icon-fetcher.h"
34 #include "rofi.h"
35 #include "settings.h"
36 #include "view.h"
37 #include "widgets/textbox.h"
38 #include "xrmoptions.h"
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <gio/gio.h>
43 #include <gio/gunixinputstream.h>
44 #include <glib-unix.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <unistd.h>
53 
55 
56 static int dmenu_mode_init(Mode *sw);
57 static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens,
58  unsigned int index);
59 static cairo_surface_t *
60 dmenu_get_icon(const Mode *sw, unsigned int selected_line, unsigned int height);
61 static char *dmenu_get_message(const Mode *sw);
62 
63 static inline unsigned int bitget(uint32_t const *const array,
64  unsigned int index) {
65  uint32_t bit = index % 32;
66  uint32_t val = array[index / 32];
67  return (val >> bit) & 1;
68 }
69 
70 static inline void bittoggle(uint32_t *const array, unsigned int index) {
71  uint32_t bit = index % 32;
72  uint32_t *v = &array[index / 32];
73  *v ^= 1 << bit;
74 }
75 
76 typedef struct {
78  // Separator.
79  char separator;
80 
81  unsigned int selected_line;
82  char *message;
83  char *format;
85  unsigned int num_urgent_list;
87  unsigned int num_active_list;
88  uint32_t *selected_list;
89  unsigned int num_selected_list;
90  unsigned int do_markup;
91  // List with entries.
93  unsigned int cmd_list_real_length;
94  unsigned int cmd_list_length;
95  unsigned int only_selected;
96  unsigned int selected_count;
97 
98  gchar **columns;
100  gboolean multi_select;
101 
102  GThread *reading_thread;
103  GAsyncQueue *async_queue;
104  gboolean async;
105  FILE *fd_file;
106  int fd;
107  int pipefd[2];
108  int pipefd2[2];
109  guint wake_source;
110  gboolean loading;
111 
115 
116 #define BLOCK_LINES_SIZE 2048
117 typedef struct {
118  unsigned int length;
121 } Block;
122 
123 static void read_add_block(DmenuModePrivateData *pd, Block **block, char *data,
124  gsize len) {
125 
126  if ((*block) == NULL) {
127  (*block) = g_malloc0(sizeof(Block));
128  (*block)->pd = pd;
129  (*block)->length = 0;
130  }
131  gsize data_len = len;
132  // Init.
133  (*block)->values[(*block)->length].icon_fetch_uid = 0;
134  (*block)->values[(*block)->length].icon_fetch_size = 0;
135  (*block)->values[(*block)->length].icon_name = NULL;
136  (*block)->values[(*block)->length].meta = NULL;
137  (*block)->values[(*block)->length].info = NULL;
138  (*block)->values[(*block)->length].nonselectable = FALSE;
139  char *end = data;
140  while (end < data + len && *end != '\0') {
141  end++;
142  }
143  if (end != data + len) {
144  data_len = end - data;
145  dmenuscript_parse_entry_extras(NULL, &((*block)->values[(*block)->length]),
146  end + 1, len - data_len);
147  }
148  char *utfstr = rofi_force_utf8(data, data_len);
149  (*block)->values[(*block)->length].entry = utfstr;
150  (*block)->values[(*block)->length + 1].entry = NULL;
151 
152  (*block)->length++;
153 }
154 
155 static void read_add(DmenuModePrivateData *pd, char *data, gsize len) {
156  gsize data_len = len;
157  if ((pd->cmd_list_length + 2) > pd->cmd_list_real_length) {
158  pd->cmd_list_real_length = MAX(pd->cmd_list_real_length * 2, 512);
159  pd->cmd_list = g_realloc(pd->cmd_list, (pd->cmd_list_real_length) *
160  sizeof(DmenuScriptEntry));
161  }
162  // Init.
165  pd->cmd_list[pd->cmd_list_length].icon_name = NULL;
166  pd->cmd_list[pd->cmd_list_length].meta = NULL;
167  pd->cmd_list[pd->cmd_list_length].info = NULL;
168  pd->cmd_list[pd->cmd_list_length].nonselectable = FALSE;
169  char *end = data;
170  while (end < data + len && *end != '\0') {
171  end++;
172  }
173  if (end != data + len) {
174  data_len = end - data;
176  end + 1, len - data_len);
177  }
178  char *utfstr = rofi_force_utf8(data, data_len);
179  pd->cmd_list[pd->cmd_list_length].entry = utfstr;
180  pd->cmd_list[pd->cmd_list_length + 1].entry = NULL;
181 
182  pd->cmd_list_length++;
183 }
184 
194 static gboolean dmenu_async_read_proc(gint fd, GIOCondition condition,
195  gpointer user_data) {
196  DmenuModePrivateData *pd = (DmenuModePrivateData *)user_data;
197  char command;
198  // Only interrested in read events.
199  if ((condition & G_IO_IN) != G_IO_IN) {
200  return G_SOURCE_CONTINUE;
201  }
202  // Read the entry from the pipe that was used to signal this action.
203  if (read(fd, &command, 1) == 1) {
204  if (command == 'r') {
205  Block *block = NULL;
206  gboolean changed = FALSE;
207  // Empty out the AsyncQueue (that is thread safe) from all blocks pushed
208  // into it.
209  while ((block = g_async_queue_try_pop(pd->async_queue)) != NULL) {
210 
211  if (pd->cmd_list_real_length < (pd->cmd_list_length + block->length)) {
212  pd->cmd_list_real_length = MAX(pd->cmd_list_real_length * 2, 4096);
213  pd->cmd_list = g_realloc(pd->cmd_list, sizeof(DmenuScriptEntry) *
215  }
216  memcpy(&(pd->cmd_list[pd->cmd_list_length]), &(block->values[0]),
217  sizeof(DmenuScriptEntry) * block->length);
218  pd->cmd_list_length += block->length;
219  g_free(block);
220  changed = TRUE;
221  }
222  if (changed) {
224  }
225  } else if (command == 'q') {
226  if (pd->loading) {
228  }
229  }
230  }
231  return G_SOURCE_CONTINUE;
232 }
233 
234 static void read_input_sync(DmenuModePrivateData *pd, unsigned int pre_read) {
235  ssize_t nread = 0;
236  size_t len = 0;
237  char *line = NULL;
238  while (pre_read > 0 &&
239  (nread = getdelim(&line, &len, pd->separator, pd->fd_file)) != -1) {
240  if (line[nread - 1] == pd->separator) {
241  nread--;
242  line[nread] = '\0';
243  }
244  read_add(pd, line, nread);
245  pre_read--;
246  }
247  free(line);
248  return;
249 }
250 static gpointer read_input_thread(gpointer userdata) {
251  DmenuModePrivateData *pd = (DmenuModePrivateData *)userdata;
252  ssize_t nread = 0;
253  ssize_t len = 0;
254  char *line = NULL;
255  // Create the message passing queue to the UI thread.
256  pd->async_queue = g_async_queue_new();
257  Block *block = NULL;
258 
259  GTimer *tim = g_timer_new();
260  int fd = pd->fd;
261  while (1) {
262  // Wait for input from the input or from the main thread.
263  fd_set rfds;
264  // We wait for 0.25 seconds, before we flush what we have.
265  struct timeval tv = {.tv_sec = 0, .tv_usec = 250000};
266 
267  FD_ZERO(&rfds);
268  FD_SET(fd, &rfds);
269  FD_SET(pd->pipefd[0], &rfds);
270 
271  int retval = select(MAX(fd, pd->pipefd[0]) + 1, &rfds, NULL, NULL, &tv);
272  if (retval == -1) {
273  g_warning("select failed, giving up.");
274  break;
275  } else if (retval) {
276  // We get input from the UI thread, this is always an abort.
277  if (FD_ISSET(pd->pipefd[0], &rfds)) {
278  break;
279  }
280  // Input data is available.
281  if (FD_ISSET(fd, &rfds)) {
282  ssize_t readbytes = 0;
283  if ((nread + 1024) > len) {
284  line = g_realloc(line, (nread + 1024));
285  len = nread + 1024;
286  }
287  readbytes = read(fd, &line[nread], 1023);
288  if (readbytes > 0) {
289  nread += readbytes;
290  line[nread] = '\0';
291  ssize_t i = 0;
292  while (i < nread) {
293  if (line[i] == pd->separator) {
294  line[i] = '\0';
295  read_add_block(pd, &block, line, i);
296  memmove(&line[0], &line[i + 1], nread - (i + 1));
297  nread -= (i + 1);
298  i = 0;
299  if (block) {
300  double elapsed = g_timer_elapsed(tim, NULL);
301  if ( elapsed >= 0.1 || block->length == BLOCK_LINES_SIZE) {
302  g_timer_start(tim);
303  g_async_queue_push(pd->async_queue, block);
304  block = NULL;
305  write(pd->pipefd2[1], "r", 1);
306  }
307  }
308  } else {
309  i++;
310  }
311  }
312  } else {
313  // remainder in buffer, then quit.
314  if (nread > 0) {
315  line[nread] = '\0';
316  read_add_block(pd, &block, line, nread);
317  }
318  if (block) {
319  g_timer_start(tim);
320  g_async_queue_push(pd->async_queue, block);
321  block = NULL;
322  write(pd->pipefd2[1], "r", 1);
323  }
324  break;
325  }
326  }
327  } else {
328  // Timeout, pushout remainder data.
329  if (nread > 0) {
330  line[nread] = '\0';
331  read_add_block(pd, &block, line, nread);
332  nread = 0;
333  }
334  if (block) {
335  g_timer_start(tim);
336  g_async_queue_push(pd->async_queue, block);
337  block = NULL;
338  write(pd->pipefd2[1], "r", 1);
339  }
340  }
341  }
342  g_timer_destroy(tim);
343  free(line);
344  write(pd->pipefd2[1], "q", 1);
345  return NULL;
346 }
347 
348 static unsigned int dmenu_mode_get_num_entries(const Mode *sw) {
349  const DmenuModePrivateData *rmpd =
351  unsigned int retv = rmpd->cmd_list_length;
352  return retv;
353 }
354 
356  const char *input,
357  const unsigned int index,
358  gboolean multi_select) {
359  if (pd->columns == NULL) {
360  if (multi_select) {
361  if (pd->selected_list && bitget(pd->selected_list, index) == TRUE) {
362  return g_strdup_printf("%s%s", pd->ballot_selected, input);
363  } else {
364  return g_strdup_printf("%s%s", pd->ballot_unselected, input);
365  }
366  }
367  return g_strdup(input);
368  }
369  char *retv = NULL;
370  char **splitted =
371  g_regex_split_simple(pd->column_separator, input, G_REGEX_CASELESS, 00);
372  uint32_t ns = 0;
373  for (; splitted && splitted[ns]; ns++) {
374  ;
375  }
376  GString *str_retv = g_string_new("");
377 
378  if (multi_select) {
379  if (pd->selected_list && bitget(pd->selected_list, index) == TRUE) {
380  g_string_append(str_retv, pd->ballot_selected);
381  } else {
382  g_string_append(str_retv, pd->ballot_unselected);
383  }
384  }
385  for (uint32_t i = 0; pd->columns && pd->columns[i]; i++) {
386  unsigned int index =
387  (unsigned int)g_ascii_strtoull(pd->columns[i], NULL, 10);
388  if (index <= ns && index > 0) {
389  if (index == 1) {
390  g_string_append(str_retv, splitted[index - 1]);
391  } else {
392  g_string_append_c(str_retv, '\t');
393  g_string_append(str_retv, splitted[index - 1]);
394  }
395  }
396  }
397  g_strfreev(splitted);
398  retv = str_retv->str;
399  g_string_free(str_retv, FALSE);
400  return retv;
401 }
402 
403 static inline unsigned int get_index(unsigned int length, int index) {
404  if (index >= 0) {
405  return index;
406  }
407  if (((unsigned int)-index) <= length) {
408  return length + index;
409  }
410  // Out of range.
411  return UINT_MAX;
412 }
413 
414 static char *dmenu_get_completion_data(const Mode *data, unsigned int index) {
415  Mode *sw = (Mode *)data;
418  return dmenu_format_output_string(pd, retv[index].entry, index, FALSE);
419 }
420 
421 static char *get_display_data(const Mode *data, unsigned int index, int *state,
422  G_GNUC_UNUSED GList **list, int get_entry) {
423  Mode *sw = (Mode *)data;
426  for (unsigned int i = 0; i < pd->num_active_list; i++) {
427  unsigned int start =
429  unsigned int stop = get_index(pd->cmd_list_length, pd->active_list[i].stop);
430  if (index >= start && index <= stop) {
431  *state |= ACTIVE;
432  }
433  }
434  for (unsigned int i = 0; i < pd->num_urgent_list; i++) {
435  unsigned int start =
437  unsigned int stop = get_index(pd->cmd_list_length, pd->urgent_list[i].stop);
438  if (index >= start && index <= stop) {
439  *state |= URGENT;
440  }
441  }
442  if (pd->selected_list && bitget(pd->selected_list, index) == TRUE) {
443  *state |= SELECTED;
444  }
445  if (pd->do_markup) {
446  *state |= MARKUP;
447  }
448  char *my_retv =
449  (get_entry ? dmenu_format_output_string(pd, retv[index].entry, index,
450  pd->multi_select)
451  : NULL);
452  return my_retv;
453 }
454 
455 static void dmenu_mode_free(Mode *sw) {
456  if (mode_get_private_data(sw) == NULL) {
457  return;
458  }
460  if (pd != NULL) {
461 
462  for (size_t i = 0; i < pd->cmd_list_length; i++) {
463  if (pd->cmd_list[i].entry) {
464  g_free(pd->cmd_list[i].entry);
465  g_free(pd->cmd_list[i].icon_name);
466  g_free(pd->cmd_list[i].meta);
467  g_free(pd->cmd_list[i].info);
468  }
469  }
470  g_free(pd->cmd_list);
471  g_free(pd->urgent_list);
472  g_free(pd->active_list);
473  g_free(pd->selected_list);
474 
475  g_free(pd);
476  mode_set_private_data(sw, NULL);
477  }
478 }
479 
480 #include "mode-private.h"
482 Mode dmenu_mode = {.name = "dmenu",
483  .cfg_name_key = "display-combi",
484  ._init = dmenu_mode_init,
485  ._get_num_entries = dmenu_mode_get_num_entries,
486  ._result = NULL,
487  ._destroy = dmenu_mode_free,
488  ._token_match = dmenu_token_match,
489  ._get_display_value = get_display_data,
490  ._get_icon = dmenu_get_icon,
491  ._get_completion = dmenu_get_completion_data,
492  ._preprocess_input = NULL,
493  ._get_message = dmenu_get_message,
494  .private_data = NULL,
495  .free = NULL,
496  .display_name = "dmenu"};
497 
498 static int dmenu_mode_init(Mode *sw) {
499  if (mode_get_private_data(sw) != NULL) {
500  return TRUE;
501  }
502  mode_set_private_data(sw, g_malloc0(sizeof(DmenuModePrivateData)));
504 
505  pd->async = TRUE;
506 
507  // For now these only work in sync mode.
508  if (find_arg("-sync") >= 0 || find_arg("-dump") >= 0 ||
509  find_arg("-select") >= 0 || find_arg("-no-custom") >= 0 ||
510  find_arg("-only-match") >= 0 || config.auto_select ||
511  find_arg("-selected-row") >= 0) {
512  pd->async = FALSE;
513  }
514 
515  pd->separator = '\n';
516  pd->selected_line = UINT32_MAX;
517 
518  find_arg_str("-mesg", &(pd->message));
519 
520  // Input data separator.
521  find_arg_char("-sep", &(pd->separator));
522 
523  find_arg_uint("-selected-row", &(pd->selected_line));
524  // By default we print the unescaped line back.
525  pd->format = "s";
526 
527  // Allow user to override the output format.
528  find_arg_str("-format", &(pd->format));
529  // Urgent.
530  char *str = NULL;
531  find_arg_str("-u", &str);
532  if (str != NULL) {
533  parse_ranges(str, &(pd->urgent_list), &(pd->num_urgent_list));
534  }
535  // Active
536  str = NULL;
537  find_arg_str("-a", &str);
538  if (str != NULL) {
539  parse_ranges(str, &(pd->active_list), &(pd->num_active_list));
540  }
541 
542  // DMENU COMPATIBILITY
543  unsigned int lines = DEFAULT_MENU_LINES;
544  find_arg_uint("-l", &(lines));
545  if (lines != DEFAULT_MENU_LINES) {
547  p->name = g_strdup("lines");
548  p->value.i = lines;
551  GHashTable *table =
552  g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
553  (GDestroyNotify)rofi_theme_property_free);
554 
555  g_hash_table_replace(table, p->name, p);
557  g_hash_table_destroy(table);
558  }
559 
560  str = NULL;
561  find_arg_str("-window-title", &str);
562  if (str) {
563  dmenu_mode.display_name = str;
564  }
565 
570  if (find_arg("-b") >= 0) {
571  config.location = 6;
572  }
573  /* -i case insensitive */
574  config.case_sensitive = TRUE;
575  if (find_arg("-i") >= 0) {
576  config.case_sensitive = FALSE;
577  }
578  if (pd->async) {
579  pd->fd = STDIN_FILENO;
580  if (find_arg_str("-input", &str)) {
581  char *estr = rofi_expand_path(str);
582  pd->fd = open(str, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
583  if (pd->fd == -1) {
584  char *msg = g_markup_printf_escaped(
585  "Failed to open file: <b>%s</b>:\n\t<i>%s</i>", estr,
586  g_strerror(errno));
587  rofi_view_error_dialog(msg, TRUE);
588  g_free(msg);
589  g_free(estr);
590  return TRUE;
591  }
592  g_free(estr);
593  }
594 
595  if (pipe(pd->pipefd) == -1) {
596  g_error("Failed to create pipe");
597  }
598  if (pipe(pd->pipefd2) == -1) {
599  g_error("Failed to create pipe");
600  }
601  pd->wake_source =
602  g_unix_fd_add(pd->pipefd2[0], G_IO_IN, dmenu_async_read_proc, pd);
603  pd->reading_thread =
604  g_thread_new("dmenu-read", (GThreadFunc)read_input_thread, pd);
605  pd->loading = TRUE;
606  } else {
607  pd->fd_file = stdin;
608  str = NULL;
609  if (find_arg_str("-input", &str)) {
610  char *estr = rofi_expand_path(str);
611  pd->fd_file = fopen(str, "r");
612  if (pd->fd_file == NULL) {
613  char *msg = g_markup_printf_escaped(
614  "Failed to open file: <b>%s</b>:\n\t<i>%s</i>", estr,
615  g_strerror(errno));
616  rofi_view_error_dialog(msg, TRUE);
617  g_free(msg);
618  g_free(estr);
619  return TRUE;
620  }
621  g_free(estr);
622  }
623 
624  read_input_sync(pd, -1);
625  }
626  gchar *columns = NULL;
627  if (find_arg_str("-display-columns", &columns)) {
628  pd->columns = g_strsplit(columns, ",", 0);
629  pd->column_separator = "\t";
630  find_arg_str("-display-column-separator", &pd->column_separator);
631  }
632  return TRUE;
633 }
634 
635 static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens,
636  unsigned int index) {
637  DmenuModePrivateData *rmpd =
639 
641  char *esc = NULL;
642  if (rmpd->do_markup) {
643  pango_parse_markup(rmpd->cmd_list[index].entry, -1, 0, NULL, &esc, NULL,
644  NULL);
645  } else {
646  esc = rmpd->cmd_list[index].entry;
647  }
648  if (esc) {
649  // int retv = helper_token_match ( tokens, esc );
650  int match = 1;
651  if (tokens) {
652  for (int j = 0; match && tokens[j] != NULL; j++) {
653  rofi_int_matcher *ftokens[2] = {tokens[j], NULL};
654  int test = 0;
655  test = helper_token_match(ftokens, esc);
656  if (test == tokens[j]->invert && rmpd->cmd_list[index].meta) {
657  test = helper_token_match(ftokens, rmpd->cmd_list[index].meta);
658  }
659 
660  if (test == 0) {
661  match = 0;
662  }
663  }
664  }
665  if (rmpd->do_markup) {
666  g_free(esc);
667  }
668  return match;
669  }
670  return FALSE;
671 }
672 static char *dmenu_get_message(const Mode *sw) {
674  if (pd->message) {
675  return g_strdup(pd->message);
676  }
677  return NULL;
678 }
679 static cairo_surface_t *dmenu_get_icon(const Mode *sw,
680  unsigned int selected_line,
681  unsigned int height) {
683 
684  g_return_val_if_fail(pd->cmd_list != NULL, NULL);
685  DmenuScriptEntry *dr = &(pd->cmd_list[selected_line]);
686  if (dr->icon_name == NULL) {
687  return NULL;
688  }
689  if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) {
691  }
692  uint32_t uid = dr->icon_fetch_uid =
693  rofi_icon_fetcher_query(dr->icon_name, height);
694  dr->icon_fetch_size = height;
695 
696  return rofi_icon_fetcher_get(uid);
697 }
698 
700  int retv) {
701 
702  if (pd->reading_thread) {
703  // Stop listinig to new messages from reading thread.
704  if (pd->wake_source > 0) {
705  g_source_remove(pd->wake_source);
706  }
707  // signal stop.
708  write(pd->pipefd[1], "q", 1);
709  g_thread_join(pd->reading_thread);
710  pd->reading_thread = NULL;
711  /* empty the queue, remove idle callbacks if still pending. */
712  g_async_queue_lock(pd->async_queue);
713  Block *block = NULL;
714  while ((block = g_async_queue_try_pop_unlocked(pd->async_queue)) != NULL) {
715  g_free(block);
716  }
717  g_async_queue_unlock(pd->async_queue);
718  g_async_queue_unref(pd->async_queue);
719  pd->async_queue = NULL;
720  close(pd->pipefd[0]);
721  close(pd->pipefd[1]);
722  }
723  if (pd->fd_file != NULL) {
724  if (pd->fd_file != stdin) {
725  fclose(pd->fd_file);
726  }
727  }
728  if (retv == FALSE) {
729  rofi_set_return_code(EXIT_FAILURE);
730  } else if (retv >= 10) {
731  rofi_set_return_code(retv);
732  } else {
733  rofi_set_return_code(EXIT_SUCCESS);
734  }
735  rofi_view_set_active(NULL);
736  rofi_view_free(state);
738 }
739 
740 static void dmenu_print_results(DmenuModePrivateData *pd, const char *input) {
741  DmenuScriptEntry *cmd_list = pd->cmd_list;
742  int seen = FALSE;
743  if (pd->selected_list != NULL) {
744  for (unsigned int st = 0; st < pd->cmd_list_length; st++) {
745  if (bitget(pd->selected_list, st)) {
746  seen = TRUE;
747  rofi_output_formatted_line(pd->format, cmd_list[st].entry, st, input);
748  }
749  }
750  }
751  if (!seen) {
752  const char *cmd = input;
753  if (pd->selected_line != UINT32_MAX) {
754  cmd = cmd_list[pd->selected_line].entry;
755  }
756  if (cmd) {
757  rofi_output_formatted_line(pd->format, cmd, pd->selected_line, input);
758  }
759  }
760 }
761 
762 static void dmenu_finalize(RofiViewState *state) {
763  int retv = FALSE;
766 
767  unsigned int cmd_list_length = pd->cmd_list_length;
768  DmenuScriptEntry *cmd_list = pd->cmd_list;
769 
770  char *input = g_strdup(rofi_view_get_user_input(state));
772  ;
773  MenuReturn mretv = rofi_view_get_return_value(state);
774  unsigned int next_pos = rofi_view_get_next_position(state);
775  int restart = 0;
776  // Special behavior.
777  if (pd->only_selected) {
781  restart = 1;
782  // Skip if no valid item is selected.
783  if ((mretv & MENU_CANCEL) == MENU_CANCEL) {
784  // In no custom mode we allow canceling.
785  restart = (find_arg("-only-match") >= 0);
786  } else if (pd->selected_line != UINT32_MAX) {
787  if ((mretv & MENU_CUSTOM_ACTION) && pd->multi_select) {
788  restart = TRUE;
789  pd->loading = FALSE;
790  if (pd->selected_list == NULL) {
791  pd->selected_list =
792  g_malloc0(sizeof(uint32_t) * (pd->cmd_list_length / 32 + 1));
793  }
794  pd->selected_count +=
795  (bitget(pd->selected_list, pd->selected_line) ? (-1) : (1));
797  // Move to next line.
798  pd->selected_line = MIN(next_pos, cmd_list_length - 1);
799  if (pd->selected_count > 0) {
800  char *str =
801  g_strdup_printf("%u/%u", pd->selected_count, pd->cmd_list_length);
802  rofi_view_set_overlay(state, str);
803  g_free(str);
804  } else {
805  rofi_view_set_overlay(state, NULL);
806  }
807  } else if ((mretv & (MENU_OK | MENU_CUSTOM_COMMAND)) &&
808  cmd_list[pd->selected_line].entry != NULL) {
809  if (cmd_list[pd->selected_line].nonselectable == TRUE) {
810  g_free(input);
811  return;
812  }
813  dmenu_print_results(pd, input);
814  retv = TRUE;
815  if ((mretv & MENU_CUSTOM_COMMAND)) {
816  retv = 10 + (mretv & MENU_LOWER_MASK);
817  }
818  g_free(input);
819  dmenu_finish(pd, state, retv);
820  return;
821  } else {
822  pd->selected_line = next_pos - 1;
823  }
824  }
825  // Restart
826  rofi_view_restart(state);
828  if (!restart) {
829  dmenu_finish(pd, state, retv);
830  }
831  return;
832  }
833  // We normally do not want to restart the loop.
834  restart = FALSE;
835  // Normal mode
836  if ((mretv & MENU_OK) && pd->selected_line != UINT32_MAX &&
837  cmd_list[pd->selected_line].entry != NULL) {
838  // Check if entry is non-selectable.
839  if (cmd_list[pd->selected_line].nonselectable == TRUE) {
840  g_free(input);
841  return;
842  }
843  if ((mretv & MENU_CUSTOM_ACTION) && pd->multi_select) {
844  restart = TRUE;
845  if (pd->selected_list == NULL) {
846  pd->selected_list =
847  g_malloc0(sizeof(uint32_t) * (pd->cmd_list_length / 32 + 1));
848  }
849  pd->selected_count +=
850  (bitget(pd->selected_list, pd->selected_line) ? (-1) : (1));
852  // Move to next line.
853  pd->selected_line = MIN(next_pos, cmd_list_length - 1);
854  if (pd->selected_count > 0) {
855  char *str =
856  g_strdup_printf("%u/%u", pd->selected_count, pd->cmd_list_length);
857  rofi_view_set_overlay(state, str);
858  g_free(str);
859  } else {
860  rofi_view_set_overlay(state, NULL);
861  }
862  } else {
863  dmenu_print_results(pd, input);
864  }
865  retv = TRUE;
866  }
867  // Custom input
868  else if ((mretv & (MENU_CUSTOM_INPUT))) {
869  dmenu_print_results(pd, input);
870 
871  retv = TRUE;
872  }
873  // Quick switch with entry selected.
874  else if ((mretv & MENU_CUSTOM_COMMAND)) {
875  dmenu_print_results(pd, input);
876 
877  restart = FALSE;
878  retv = 10 + (mretv & MENU_LOWER_MASK);
879  }
880  g_free(input);
881  if (restart) {
882  rofi_view_restart(state);
884  } else {
885  dmenu_finish(pd, state, retv);
886  }
887 }
888 
889 int dmenu_mode_dialog(void) {
891  MenuFlags menu_flags = MENU_NORMAL;
893 
894  char *input = NULL;
895  unsigned int cmd_list_length = pd->cmd_list_length;
896  DmenuScriptEntry *cmd_list = pd->cmd_list;
897 
898  pd->only_selected = FALSE;
899  pd->multi_select = FALSE;
900  pd->ballot_selected = "☑ ";
901  pd->ballot_unselected = "☐ ";
902  find_arg_str("-ballot-selected-str", &(pd->ballot_selected));
903  find_arg_str("-ballot-unselected-str", &(pd->ballot_unselected));
904  if (find_arg("-multi-select") >= 0) {
905  pd->multi_select = TRUE;
906  }
907  if (find_arg("-markup-rows") >= 0) {
908  pd->do_markup = TRUE;
909  }
910  if (find_arg("-only-match") >= 0 || find_arg("-no-custom") >= 0) {
911  pd->only_selected = TRUE;
912  if (cmd_list_length == 0) {
913  return TRUE;
914  }
915  }
916  if (config.auto_select && cmd_list_length == 1) {
917  rofi_output_formatted_line(pd->format, cmd_list[0].entry, 0, config.filter);
918  return TRUE;
919  }
920  if (find_arg("-password") >= 0) {
921  menu_flags |= MENU_PASSWORD;
922  }
923  /* copy filter string */
924  input = g_strdup(config.filter);
925 
926  char *select = NULL;
927  find_arg_str("-select", &select);
928  if (select != NULL) {
930  unsigned int i = 0;
931  for (i = 0; i < cmd_list_length; i++) {
932  if (helper_token_match(tokens, cmd_list[i].entry)) {
933  pd->selected_line = i;
934  break;
935  }
936  }
937  helper_tokenize_free(tokens);
938  }
939  if (find_arg("-dump") >= 0) {
942  unsigned int i = 0;
943  for (i = 0; i < cmd_list_length; i++) {
944  if (tokens == NULL || helper_token_match(tokens, cmd_list[i].entry)) {
945  rofi_output_formatted_line(pd->format, cmd_list[i].entry, i,
946  config.filter);
947  }
948  }
949  helper_tokenize_free(tokens);
951  g_free(input);
952  return TRUE;
953  }
955  RofiViewState *state =
956  rofi_view_create(&dmenu_mode, input, menu_flags, dmenu_finalize);
957 
958  if (find_arg("-keep-right") >= 0) {
960  }
962  rofi_view_set_active(state);
963  if (pd->loading) {
964  rofi_view_set_overlay(state, "Loading.. ");
965  }
966 
967  return FALSE;
968 }
969 
971  int is_term = isatty(fileno(stdout));
973  "-mesg", "[string]",
974  "Print a small user message under the prompt (uses pango markup)", NULL,
975  is_term);
976  print_help_msg("-p", "[string]", "Prompt to display left of entry field",
977  NULL, is_term);
978  print_help_msg("-selected-row", "[integer]", "Select row", NULL, is_term);
979  print_help_msg("-format", "[string]", "Output format string", "s", is_term);
980  print_help_msg("-u", "[list]", "List of row indexes to mark urgent", NULL,
981  is_term);
982  print_help_msg("-a", "[list]", "List of row indexes to mark active", NULL,
983  is_term);
984  print_help_msg("-l", "[integer] ", "Number of rows to display", NULL,
985  is_term);
986  print_help_msg("-window-title", "[string] ", "Set the dmenu window title",
987  NULL, is_term);
988  print_help_msg("-i", "", "Set filter to be case insensitive", NULL, is_term);
989  print_help_msg("-only-match", "",
990  "Force selection to be given entry, disallow no match", NULL,
991  is_term);
992  print_help_msg("-no-custom", "", "Don't accept custom entry, allow no match",
993  NULL, is_term);
994  print_help_msg("-select", "[string]", "Select the first row that matches",
995  NULL, is_term);
996  print_help_msg("-password", "",
997  "Do not show what the user inputs. Show '*' instead.", NULL,
998  is_term);
999  print_help_msg("-markup-rows", "",
1000  "Allow and render pango markup as input data.", NULL, is_term);
1001  print_help_msg("-sep", "[char]", "Element separator.", "'\\n'", is_term);
1002  print_help_msg("-input", "[filename]",
1003  "Read input from file instead from standard input.", NULL,
1004  is_term);
1005  print_help_msg("-sync", "",
1006  "Force dmenu to first read all input data, then show dialog.",
1007  NULL, is_term);
1008  print_help_msg("-w", "windowid", "Position over window with X11 windowid.",
1009  NULL, is_term);
1010  print_help_msg("-keep-right", "", "Set ellipsize to end.", NULL, is_term);
1011  print_help_msg("-display-columns", "", "Only show the selected columns", NULL,
1012  is_term);
1013  print_help_msg("-display-column-separator", "\t",
1014  "Separator to use to split columns (regex)", NULL, is_term);
1015  print_help_msg("-ballot-selected-str", "\t",
1016  "When multi-select is enabled prefix this string when element "
1017  "is selected.",
1018  NULL, is_term);
1019  print_help_msg("-ballot-unselected-str", "\t",
1020  "When multi-select is enabled prefix this string when element "
1021  "is not selected.",
1022  NULL, is_term);
1023 }
static void dmenu_finish(DmenuModePrivateData *pd, RofiViewState *state, int retv)
Definition: dmenu.c:699
static void read_add(DmenuModePrivateData *pd, char *data, gsize len)
Definition: dmenu.c:155
static cairo_surface_t * dmenu_get_icon(const Mode *sw, unsigned int selected_line, unsigned int height)
Definition: dmenu.c:679
Mode dmenu_mode
Definition: dmenu.c:482
static gboolean dmenu_async_read_proc(gint fd, GIOCondition condition, gpointer user_data)
Definition: dmenu.c:194
static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: dmenu.c:635
static unsigned int get_index(unsigned int length, int index)
Definition: dmenu.c:403
static char * get_display_data(const Mode *data, unsigned int index, int *state, G_GNUC_UNUSED GList **list, int get_entry)
Definition: dmenu.c:421
static void dmenu_mode_free(Mode *sw)
Definition: dmenu.c:455
static void read_add_block(DmenuModePrivateData *pd, Block **block, char *data, gsize len)
Definition: dmenu.c:123
static char * dmenu_get_message(const Mode *sw)
Definition: dmenu.c:672
#define BLOCK_LINES_SIZE
Definition: dmenu.c:116
static gchar * dmenu_format_output_string(const DmenuModePrivateData *pd, const char *input, const unsigned int index, gboolean multi_select)
Definition: dmenu.c:355
static char * dmenu_get_completion_data(const Mode *data, unsigned int index)
Definition: dmenu.c:414
static gpointer read_input_thread(gpointer userdata)
Definition: dmenu.c:250
static void dmenu_print_results(DmenuModePrivateData *pd, const char *input)
Definition: dmenu.c:740
static void bittoggle(uint32_t *const array, unsigned int index)
Definition: dmenu.c:70
static void dmenu_finalize(RofiViewState *state)
Definition: dmenu.c:762
static int dmenu_mode_init(Mode *sw)
Definition: dmenu.c:498
static void read_input_sync(DmenuModePrivateData *pd, unsigned int pre_read)
Definition: dmenu.c:234
static unsigned int dmenu_mode_get_num_entries(const Mode *sw)
Definition: dmenu.c:348
static unsigned int bitget(uint32_t const *const array, unsigned int index)
Definition: dmenu.c:63
void print_help_msg(const char *option, const char *type, const char *text, const char *def, int isatty)
Definition: xrmoptions.c:975
int dmenu_mode_dialog(void)
Definition: dmenu.c:889
void print_dmenu_options(void)
Definition: dmenu.c:970
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length)
Definition: helper.c:1182
int find_arg_char(const char *const key, char *val)
Definition: helper.c:408
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition: helper.c:119
void rofi_output_formatted_line(const char *format, const char *string, int selected_line, const char *filter)
Definition: helper.c:1199
char * rofi_expand_path(const char *input)
Definition: helper.c:740
int find_arg_str(const char *const key, char **val)
Definition: helper.c:311
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition: helper.c:263
int find_arg_uint(const char *const key, unsigned int *val)
Definition: helper.c:350
int find_arg(const char *const key)
Definition: helper.c:302
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition: helper.c:518
char * rofi_force_utf8(const gchar *data, ssize_t length)
Definition: helper.c:814
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
void mode_destroy(Mode *mode)
Definition: mode.c:52
int mode_init(Mode *mode)
Definition: mode.c:43
MenuReturn
Definition: mode.h:65
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:164
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:159
@ MENU_CUSTOM_COMMAND
Definition: mode.h:79
@ MENU_LOWER_MASK
Definition: mode.h:87
@ MENU_CANCEL
Definition: mode.h:69
@ MENU_CUSTOM_ACTION
Definition: mode.h:85
@ MENU_OK
Definition: mode.h:67
@ MENU_CUSTOM_INPUT
Definition: mode.h:73
void rofi_set_return_code(int code)
Definition: rofi.c:145
@ SELECTED
Definition: textbox.h:108
@ URGENT
Definition: textbox.h:104
@ ACTIVE
Definition: textbox.h:106
@ MARKUP
Definition: textbox.h:110
void rofi_view_set_overlay(RofiViewState *state, const char *text)
Definition: view.c:2322
void rofi_view_reload(void)
Definition: view.c:528
Mode * rofi_view_get_mode(RofiViewState *state)
Definition: view.c:2320
RofiViewState * rofi_view_get_active(void)
Definition: view.c:549
int rofi_view_error_dialog(const char *msg, int markup)
Definition: view.c:2191
void rofi_view_set_active(RofiViewState *state)
Definition: view.c:558
void rofi_view_restart(RofiViewState *state)
Definition: view.c:544
MenuFlags
Definition: view.h:48
MenuReturn rofi_view_get_return_value(const RofiViewState *state)
Definition: view.c:617
void rofi_view_set_selected_line(RofiViewState *state, unsigned int selected_line)
Definition: view.c:581
RofiViewState * rofi_view_create(Mode *sw, const char *input, MenuFlags menu_flags, void(*finalize)(RofiViewState *))
Definition: view.c:2103
void rofi_view_free(RofiViewState *state)
Definition: view.c:599
const char * rofi_view_get_user_input(const RofiViewState *state)
Definition: view.c:638
unsigned int rofi_view_get_selected_line(const RofiViewState *state)
Definition: view.c:621
unsigned int rofi_view_get_next_position(const RofiViewState *state)
Definition: view.c:625
@ MENU_PASSWORD
Definition: view.h:52
@ MENU_NORMAL
Definition: view.h:50
void rofi_view_ellipsize_start(RofiViewState *state)
Definition: view.c:2343
@ P_INTEGER
Definition: rofi-types.h:12
void dmenuscript_parse_entry_extras(G_GNUC_UNUSED Mode *sw, DmenuScriptEntry *entry, char *buffer, G_GNUC_UNUSED size_t length)
Definition: script.c:82
Settings config
#define DEFAULT_MENU_LINES
Definition: settings.h:190
Definition: dmenu.c:117
DmenuModePrivateData * pd
Definition: dmenu.c:120
DmenuScriptEntry values[BLOCK_LINES_SIZE]
Definition: dmenu.c:119
unsigned int length
Definition: dmenu.c:118
unsigned int num_urgent_list
Definition: dmenu.c:85
uint32_t * selected_list
Definition: dmenu.c:88
DmenuScriptEntry * cmd_list
Definition: dmenu.c:92
unsigned int cmd_list_real_length
Definition: dmenu.c:93
gboolean async
Definition: dmenu.c:104
GThread * reading_thread
Definition: dmenu.c:102
unsigned int num_selected_list
Definition: dmenu.c:89
unsigned int do_markup
Definition: dmenu.c:90
unsigned int selected_count
Definition: dmenu.c:96
char * ballot_unselected
Definition: dmenu.c:113
gchar * column_separator
Definition: dmenu.c:99
unsigned int num_active_list
Definition: dmenu.c:87
gboolean loading
Definition: dmenu.c:110
struct rofi_range_pair * urgent_list
Definition: dmenu.c:84
gboolean multi_select
Definition: dmenu.c:100
unsigned int cmd_list_length
Definition: dmenu.c:94
unsigned int only_selected
Definition: dmenu.c:95
GAsyncQueue * async_queue
Definition: dmenu.c:103
gchar ** columns
Definition: dmenu.c:98
char * ballot_selected
Definition: dmenu.c:112
unsigned int selected_line
Definition: dmenu.c:81
struct rofi_range_pair * active_list
Definition: dmenu.c:86
char * message
Definition: dmenu.c:82
PropertyValue value
Definition: rofi-types.h:297
char * name
Definition: rofi-types.h:293
WindowLocation location
Definition: settings.h:84
unsigned int auto_select
Definition: settings.h:126
char * filter
Definition: settings.h:139
unsigned int case_sensitive
Definition: settings.h:114
char * display_name
Definition: mode-private.h:165
char * name
Definition: mode-private.h:163
void * private_data
Definition: mode-private.h:192
Property * rofi_theme_property_create(PropertyType type)
Definition: theme.c:93
void rofi_theme_widget_add_properties(ThemeWidget *widget, GHashTable *table)
Definition: theme.c:651
ThemeWidget * rofi_theme_find_or_create_name(ThemeWidget *base, const char *name)
Definition: theme.c:73
void rofi_theme_property_free(Property *p)
Definition: theme.c:195
ThemeWidget * rofi_theme
Definition: theme.h:93