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
56static int dmenu_mode_init(Mode *sw);
57static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens,
58 unsigned int index);
59static cairo_surface_t *
60dmenu_get_icon(const Mode *sw, unsigned int selected_line, unsigned int height);
61static char *dmenu_get_message(const Mode *sw);
62
63static 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
70static 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
76typedef struct {
78 // 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.
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
103 GAsyncQueue *async_queue;
104 gboolean async;
105 FILE *fd_file;
106 int fd;
107 int pipefd[2];
108 int pipefd2[2];
110 gboolean loading;
111
115
116#define BLOCK_LINES_SIZE 2048
117typedef struct {
118 unsigned int length;
121} Block;
122
123static 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
155static 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
194static gboolean dmenu_async_read_proc(gint fd, GIOCondition condition,
195 gpointer 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
234static 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}
250static gpointer read_input_thread(gpointer 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
348static 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
403static 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
414static 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
421static 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
455static 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"
482Mode 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
498static 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) {
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
635static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens,
636 unsigned int index) {
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}
672static char *dmenu_get_message(const Mode *sw) {
674 if (pd->message) {
675 return g_strdup(pd->message);
676 }
677 return NULL;
678}
679static 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 =
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) {
732 } else {
733 rofi_set_return_code(EXIT_SUCCESS);
734 }
736 rofi_view_free(state);
738}
739
740static 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
762static 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 ;
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
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) {
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 }
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 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 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 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
static gchar * dmenu_format_output_string(const DmenuModePrivateData *pd, const char *input, const unsigned int index, gboolean multi_select)
Definition dmenu.c:355
#define BLOCK_LINES_SIZE
Definition dmenu.c:116
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
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition helper.c:263
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
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
void * mode_get_private_data(const Mode *mode)
Definition mode.c:159
MenuReturn
Definition mode.h:65
void mode_set_private_data(Mode *mode, void *pd)
Definition mode.c:164
@ 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
Mode * rofi_view_get_mode(RofiViewState *state)
Definition view.c:2320
void rofi_view_reload(void)
Definition view.c:528
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
RofiViewState * rofi_view_get_active(void)
Definition view.c:549
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
const char * rofi_view_get_user_input(const RofiViewState *state)
Definition view.c:638
void rofi_view_set_selected_line(RofiViewState *state, unsigned int selected_line)
Definition view.c:581
void rofi_view_free(RofiViewState *state)
Definition view.c:599
RofiViewState * rofi_view_create(Mode *sw, const char *input, MenuFlags menu_flags, void(*finalize)(RofiViewState *))
Definition view.c:2103
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
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
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
char * name
void * private_data
ThemeWidget * rofi_theme_find_or_create_name(ThemeWidget *base, const char *name)
Definition theme.c:73
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
void rofi_theme_property_free(Property *p)
Definition theme.c:195
ThemeWidget * rofi_theme
Definition theme.h:93