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