rofi 1.7.5
helper.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2012 Sean Pringle <sean.pringle@gmail.com>
6 * Copyright © 2013-2022 Qball Cow <qball@gmpclient.org>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 */
28
30#define G_LOG_DOMAIN "Helper"
31
32#include "config.h"
33#include "display.h"
34#include "helper-theme.h"
35#include "rofi.h"
36#include "settings.h"
37#include "view.h"
38#include "xcb.h"
39#include <ctype.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <glib.h>
43#include <glib/gstdio.h>
44#include <limits.h>
45#include <pango/pango-fontmap.h>
46#include <pango/pango.h>
47#include <pango/pangocairo.h>
48#include <pwd.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <sys/file.h>
53#include <sys/stat.h>
54#include <sys/types.h>
55#include <unistd.h>
56
60const char *const monitor_position_entries[] = {
61 "on focused monitor", "on focused window", "at mouse pointer",
62 "on monitor with focused window", "on monitor that has mouse pointer"};
66char **stored_argv = NULL;
67
68char *helper_string_replace_if_exists_v(char *string, GHashTable *h);
69
70void cmd_set_arguments(int argc, char **argv) {
71 stored_argc = argc;
72 stored_argv = argv;
73}
74
75int helper_parse_setup(char *string, char ***output, int *length, ...) {
76 GError *error = NULL;
77 GHashTable *h;
78 h = g_hash_table_new(g_str_hash, g_str_equal);
79 // By default, we insert terminal and ssh-client
80 g_hash_table_insert(h, "{terminal}", config.terminal_emulator);
81 g_hash_table_insert(h, "{ssh-client}", config.ssh_client);
82 // Add list from variable arguments.
83 va_list ap;
84 va_start(ap, length);
85 while (1) {
86 char *key = va_arg(ap, char *);
87 if (key == (char *)0) {
88 break;
89 }
90 char *value = va_arg(ap, char *);
91 if (value == (char *)0) {
92 break;
93 }
94 g_hash_table_insert(h, key, value);
95 }
96 va_end(ap);
97
98 char *res = helper_string_replace_if_exists_v(string, h);
99 // Destroy key-value storage.
100 g_hash_table_destroy(h);
101 // Parse the string into shell arguments.
102 if (g_shell_parse_argv(res, length, output, &error)) {
103 g_free(res);
104 return TRUE;
105 }
106 g_free(res);
107 // Throw error if shell parsing fails.
108 if (error) {
109 char *msg = g_strdup_printf("Failed to parse: '%s'\nError: '%s'", string,
110 error->message);
111 rofi_view_error_dialog(msg, FALSE);
112 g_free(msg);
113 // print error.
114 g_error_free(error);
115 }
116 return FALSE;
117}
118
120 for (size_t i = 0; tokens && tokens[i]; i++) {
121 g_regex_unref((GRegex *)tokens[i]->regex);
122 g_free(tokens[i]);
123 }
124 g_free(tokens);
125}
126
127static gchar *glob_to_regex(const char *input) {
128 gchar *r = g_regex_escape_string(input, -1);
129 size_t str_l = strlen(r);
130 for (size_t i = 0; i < str_l; i++) {
131 if (r[i] == '\\') {
132 if (r[i + 1] == '*') {
133 r[i] = '.';
134 } else if (r[i + 1] == '?') {
135 r[i + 1] = 'S';
136 }
137 i++;
138 }
139 }
140 return r;
141}
142static gchar *fuzzy_to_regex(const char *input) {
143 GString *str = g_string_new("");
144 gchar *r = g_regex_escape_string(input, -1);
145 gchar *iter;
146 int first = 1;
147 for (iter = r; iter && *iter != '\0'; iter = g_utf8_next_char(iter)) {
148 if (first) {
149 g_string_append(str, "(");
150 } else {
151 g_string_append(str, ".*?(");
152 }
153 if (*iter == '\\') {
154 g_string_append_c(str, '\\');
155 iter = g_utf8_next_char(iter);
156 // If EOL, break out of for loop.
157 if ((*iter) == '\0') {
158 break;
159 }
160 }
161 g_string_append_unichar(str, g_utf8_get_char(iter));
162 g_string_append(str, ")");
163 first = 0;
164 }
165 g_free(r);
166 char *retv = str->str;
167 g_string_free(str, FALSE);
168 return retv;
169}
170
171static gchar *prefix_regex(const char *input) {
172 gchar *r = g_regex_escape_string(input, -1);
173 char *retv = g_strconcat("\\b", r, NULL);
174 g_free(r);
175 return retv;
176}
177
178static char *utf8_helper_simplify_string(const char *s) {
179 gunichar buf2[G_UNICHAR_MAX_DECOMPOSITION_LENGTH] = {
180 0,
181 };
182 char buf[6] = {
183 0,
184 };
185 // Compose the string in maximally composed form.
186 ssize_t str_size = (g_utf8_strlen(s, -1) * 6 + 2 + 1) * sizeof(char);
187 char *str = g_malloc0(str_size);
188 char *striter = str;
189 for (const char *iter = s; iter && *iter; iter = g_utf8_next_char(iter)) {
190 gunichar uc = g_utf8_get_char(iter);
191 int l = 0;
192 gsize dl = g_unichar_fully_decompose(uc, FALSE, buf2,
193 G_UNICHAR_MAX_DECOMPOSITION_LENGTH);
194 if (dl) {
195 l = g_unichar_to_utf8(buf2[0], buf);
196 } else {
197 l = g_unichar_to_utf8(uc, buf);
198 }
199 memcpy(striter, buf, l);
200 striter += l;
201 }
202
203 return str;
204}
205
206// Macro for quickly generating regex for matching.
207static inline GRegex *R(const char *s, int case_sensitive) {
209 char *str = utf8_helper_simplify_string(s);
210
211 GRegex *r = g_regex_new(
212 str, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0,
213 NULL);
214
215 g_free(str);
216 return r;
217 }
218 return g_regex_new(
219 s, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0, NULL);
220}
221
222static rofi_int_matcher *create_regex(const char *input, int case_sensitive) {
223 GRegex *retv = NULL;
224 gchar *r;
225 rofi_int_matcher *rv = g_malloc0(sizeof(rofi_int_matcher));
226 if (input && input[0] == config.matching_negate_char) {
227 rv->invert = 1;
228 input++;
229 }
230 switch (config.matching_method) {
231 case MM_GLOB:
232 r = glob_to_regex(input);
233 retv = R(r, case_sensitive);
234 g_free(r);
235 break;
236 case MM_REGEX:
237 retv = R(input, case_sensitive);
238 if (retv == NULL) {
239 r = g_regex_escape_string(input, -1);
240 retv = R(r, case_sensitive);
241 g_free(r);
242 }
243 break;
244 case MM_FUZZY:
245 r = fuzzy_to_regex(input);
246 retv = R(r, case_sensitive);
247 g_free(r);
248 break;
249 case MM_PREFIX:
250 r = prefix_regex(input);
251 retv = R(r, case_sensitive);
252 g_free(r);
253 break;
254 default:
255 r = g_regex_escape_string(input, -1);
256 retv = R(r, case_sensitive);
257 g_free(r);
258 break;
259 }
260 rv->regex = retv;
261 return rv;
262}
263rofi_int_matcher **helper_tokenize(const char *input, int case_sensitive) {
264 if (input == NULL) {
265 return NULL;
266 }
267 size_t len = strlen(input);
268 if (len == 0) {
269 return NULL;
270 }
271
272 char *saveptr = NULL, *token;
273 rofi_int_matcher **retv = NULL;
274 if (!config.tokenize) {
275 retv = g_malloc0(sizeof(rofi_int_matcher *) * 2);
276 retv[0] = create_regex(input, case_sensitive);
277 return retv;
278 }
279
280 // First entry is always full (modified) stringtext.
281 int num_tokens = 0;
282
283 // Copy the string, 'strtok_r' modifies it.
284 char *str = g_strdup(input);
285
286 // Iterate over tokens.
287 // strtok should still be valid for utf8.
288 const char *const sep = " ";
289 for (token = strtok_r(str, sep, &saveptr); token != NULL;
290 token = strtok_r(NULL, sep, &saveptr)) {
291 retv = g_realloc(retv, sizeof(rofi_int_matcher *) * (num_tokens + 2));
292 retv[num_tokens] = create_regex(token, case_sensitive);
293 retv[num_tokens + 1] = NULL;
294 num_tokens++;
295 }
296 // Free str.
297 g_free(str);
298 return retv;
299}
300
301// cli arg handling
302int find_arg(const char *const key) {
303 int i;
304
305 for (i = 0; i < stored_argc && strcasecmp(stored_argv[i], key); i++) {
306 ;
307 }
308
309 return i < stored_argc ? i : -1;
310}
311int find_arg_str(const char *const key, char **val) {
312 int i = find_arg(key);
313
314 if (val != NULL && i > 0 && i < stored_argc - 1) {
315 *val = stored_argv[i + 1];
316 return TRUE;
317 }
318 return FALSE;
319}
320
321const char **find_arg_strv(const char *const key) {
322 const char **retv = NULL;
323 int length = 0;
324 for (int i = 0; i < stored_argc; i++) {
325 if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
326 length++;
327 }
328 }
329 if (length > 0) {
330 retv = g_malloc0((length + 1) * sizeof(char *));
331 int index = 0;
332 for (int i = 0; i < stored_argc; i++) {
333 if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
334 retv[index++] = stored_argv[i + 1];
335 }
336 }
337 }
338 return retv;
339}
340
341int find_arg_int(const char *const key, int *val) {
342 int i = find_arg(key);
343
344 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
345 *val = strtol(stored_argv[i + 1], NULL, 10);
346 return TRUE;
347 }
348 return FALSE;
349}
350int find_arg_uint(const char *const key, unsigned int *val) {
351 int i = find_arg(key);
352
353 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
354 *val = strtoul(stored_argv[i + 1], NULL, 10);
355 return TRUE;
356 }
357 return FALSE;
358}
359
360char helper_parse_char(const char *arg) {
361 const size_t len = strlen(arg);
362 // If the length is 1, it is not escaped.
363 if (len == 1) {
364 return arg[0];
365 }
366 // If the length is 2 and the first character is '\', we unescape it.
367 if (len == 2 && arg[0] == '\\') {
368 switch (arg[1]) {
369 // New line
370 case 'n':
371 return '\n';
372 // Bell
373 case 'a':
374 return '\a';
375 // Backspace
376 case 'b':
377 return '\b';
378 // Tab
379 case 't':
380 return '\t';
381 // Vertical tab
382 case 'v':
383 return '\v';
384 // Form feed
385 case 'f':
386 return '\f';
387 // Carriage return
388 case 'r':
389 return '\r';
390 // Forward slash
391 case '\\':
392 return '\\';
393 // 0 line.
394 case '0':
395 return '\0';
396 default:
397 break;
398 }
399 }
400 if (len > 2 && arg[0] == '\\' && arg[1] == 'x') {
401 return (char)strtol(&arg[2], NULL, 16);
402 }
403 g_warning("Failed to parse character string: \"%s\"", arg);
404 // for now default to newline.
405 return '\n';
406}
407
408int find_arg_char(const char *const key, char *val) {
409 int i = find_arg(key);
410
411 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
412 *val = helper_parse_char(stored_argv[i + 1]);
413 return TRUE;
414 }
415 return FALSE;
416}
417
418void helper_token_match_set_pango_attr_on_style(PangoAttrList *retv, int start,
419 int end,
421 if (th.style & ROFI_HL_BOLD) {
422 PangoAttribute *pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
423 pa->start_index = start;
424 pa->end_index = end;
425 pango_attr_list_insert(retv, pa);
426 }
427#if PANGO_VERSION_CHECK(1, 50, 0)
428 if (th.style & ROFI_HL_UPPERCASE) {
429 PangoAttribute *pa =
430 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_UPPERCASE);
431 pa->start_index = start;
432 pa->end_index = end;
433 pango_attr_list_insert(retv, pa);
434 }
435 if (th.style & ROFI_HL_LOWERCASE) {
436 PangoAttribute *pa =
437 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_LOWERCASE);
438 pa->start_index = start;
439 pa->end_index = end;
440 pango_attr_list_insert(retv, pa);
441 }
442 if (th.style & ROFI_HL_CAPITALIZE) {
443#if 0
444 PangoAttribute *pa =
445 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_CAPITALIZE);
446 pa->start_index = start;
447 pa->end_index = end;
448 pango_attr_list_insert(retv, pa);
449#endif
450 // Disabled because of bug in pango
451 }
452#endif
453 if (th.style & ROFI_HL_UNDERLINE) {
454 PangoAttribute *pa = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
455 pa->start_index = start;
456 pa->end_index = end;
457 pango_attr_list_insert(retv, pa);
458 }
459 if (th.style & ROFI_HL_STRIKETHROUGH) {
460 PangoAttribute *pa = pango_attr_strikethrough_new(TRUE);
461 pa->start_index = start;
462 pa->end_index = end;
463 pango_attr_list_insert(retv, pa);
464 }
465 if (th.style & ROFI_HL_ITALIC) {
466 PangoAttribute *pa = pango_attr_style_new(PANGO_STYLE_ITALIC);
467 pa->start_index = start;
468 pa->end_index = end;
469 pango_attr_list_insert(retv, pa);
470 }
471 if (th.style & ROFI_HL_COLOR) {
472 PangoAttribute *pa = pango_attr_foreground_new(
473 th.color.red * 65535, th.color.green * 65535, th.color.blue * 65535);
474 pa->start_index = start;
475 pa->end_index = end;
476 pango_attr_list_insert(retv, pa);
477
478 if (th.color.alpha < 1.0) {
479 pa = pango_attr_foreground_alpha_new(th.color.alpha * 65535);
480 pa->start_index = start;
481 pa->end_index = end;
482 pango_attr_list_insert(retv, pa);
483 }
484 }
485}
486
488 rofi_int_matcher **tokens,
489 const char *input,
490 PangoAttrList *retv) {
491 // Disable highlighting for normalize match, not supported atm.
493 return retv;
494 }
495 // Do a tokenized match.
496 if (tokens) {
497 for (int j = 0; tokens[j]; j++) {
498 GMatchInfo *gmi = NULL;
499 if (tokens[j]->invert) {
500 continue;
501 }
502 g_regex_match(tokens[j]->regex, input, G_REGEX_MATCH_PARTIAL, &gmi);
503 while (g_match_info_matches(gmi)) {
504 int count = g_match_info_get_match_count(gmi);
505 for (int index = (count > 1) ? 1 : 0; index < count; index++) {
506 int start, end;
507 g_match_info_fetch_pos(gmi, index, &start, &end);
508 helper_token_match_set_pango_attr_on_style(retv, start, end, th);
509 }
510 g_match_info_next(gmi, NULL);
511 }
512 g_match_info_free(gmi);
513 }
514 }
515 return retv;
516}
517
518int helper_token_match(rofi_int_matcher *const *tokens, const char *input) {
519 int match = TRUE;
520 // Do a tokenized match.
521 if (tokens) {
523 char *r = utf8_helper_simplify_string(input);
524 for (int j = 0; match && tokens[j]; j++) {
525 match = g_regex_match(tokens[j]->regex, r, 0, NULL);
526 match ^= tokens[j]->invert;
527 }
528 g_free(r);
529 } else {
530 for (int j = 0; match && tokens[j]; j++) {
531 match = g_regex_match(tokens[j]->regex, input, 0, NULL);
532 match ^= tokens[j]->invert;
533 }
534 }
535 }
536 return match;
537}
538
539int execute_generator(const char *cmd) {
540 char **args = NULL;
541 int argv = 0;
542 helper_parse_setup(config.run_command, &args, &argv, "{cmd}", cmd, (char *)0);
543
544 int fd = -1;
545 GError *error = NULL;
546 g_spawn_async_with_pipes(NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
547 NULL, NULL, &fd, NULL, &error);
548
549 if (error != NULL) {
550 char *msg = g_strdup_printf("Failed to execute: '%s'\nError: '%s'", cmd,
551 error->message);
552 rofi_view_error_dialog(msg, FALSE);
553 g_free(msg);
554 // print error.
555 g_error_free(error);
556 fd = -1;
557 }
558 g_strfreev(args);
559 return fd;
560}
561
562int create_pid_file(const char *pidfile, gboolean kill_running) {
563 if (pidfile == NULL) {
564 return -1;
565 }
566
567 int fd = g_open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
568 if (fd < 0) {
569 g_warning("Failed to create pid file: '%s'.", pidfile);
570 return -1;
571 }
572 // Set it to close the File Descriptor on exit.
573 int flags = fcntl(fd, F_GETFD, NULL);
574 flags = flags | FD_CLOEXEC;
575 if (fcntl(fd, F_SETFD, flags, NULL) < 0) {
576 g_warning("Failed to set CLOEXEC on pidfile.");
577 remove_pid_file(fd);
578 return -1;
579 }
580 // Try to get exclusive write lock on FD
581 int retv = flock(fd, LOCK_EX | LOCK_NB);
582 if (retv != 0) {
583 g_warning("Failed to set lock on pidfile: Rofi already running?");
584 g_warning("Got error: %d %s", retv, g_strerror(errno));
585 if (kill_running) {
586 char buffer[64] = {
587 0,
588 };
589 ssize_t l = read(fd, &buffer, 64);
590 if (l > 1) {
591 pid_t pid = g_ascii_strtoll(buffer, NULL, 0);
592 kill(pid, SIGTERM);
593 while (1) {
594 retv = flock(fd, LOCK_EX | LOCK_NB);
595 if (retv == 0) {
596 break;
597 }
598 g_usleep(100);
599 }
600 }
601 remove_pid_file(fd);
602 return create_pid_file(pidfile, FALSE);
603 }
604
605 remove_pid_file(fd);
606 return -1;
607 }
608 if (ftruncate(fd, (off_t)0) == 0) {
609 // Write pid, not needed, but for completeness sake.
610 char buffer[64];
611 int length = snprintf(buffer, 64, "%i", getpid());
612 ssize_t l = 0;
613 while (l < length) {
614 l += write(fd, &buffer[l], length - l);
615 }
616 }
617 return fd;
618}
619
620void remove_pid_file(int fd) {
621 if (fd >= 0) {
622 if (close(fd)) {
623 g_warning("Failed to close pidfile: '%s'", g_strerror(errno));
624 }
625 }
626}
627
628gboolean helper_validate_font(PangoFontDescription *pfd, const char *font) {
629 const char *fam = pango_font_description_get_family(pfd);
630 int size = pango_font_description_get_size(pfd);
631 if (fam == NULL || size == 0) {
632 g_debug("Pango failed to parse font: '%s'", font);
633 g_debug("Got family: <b>%s</b> at size: <b>%d</b>", fam ? fam : "{unknown}",
634 size);
635 return FALSE;
636 }
637 return TRUE;
638}
639
648 int found_error = FALSE;
649 GString *msg =
650 g_string_new("<big><b>The configuration failed to validate:</b></big>\n");
651
653 if (g_strcmp0(config.sorting_method, "normal") == 0) {
655 } else if (g_strcmp0(config.sorting_method, "levenshtein") == 0) {
657 } else if (g_strcmp0(config.sorting_method, "fzf") == 0) {
659 } else {
660 g_string_append_printf(
661 msg,
662 "\t<b>config.sorting_method</b>=%s is not a valid sorting "
663 "strategy.\nValid options are: normal or fzf.\n",
665 found_error = 1;
666 }
667 }
668
669 if (config.matching) {
670 if (g_strcmp0(config.matching, "regex") == 0) {
672 } else if (g_strcmp0(config.matching, "glob") == 0) {
674 } else if (g_strcmp0(config.matching, "fuzzy") == 0) {
676 } else if (g_strcmp0(config.matching, "normal") == 0) {
678 ;
679 } else if (g_strcmp0(config.matching, "prefix") == 0) {
681 } else {
682 g_string_append_printf(msg,
683 "\t<b>config.matching</b>=%s is not a valid "
684 "matching strategy.\nValid options are: glob, "
685 "regex, fuzzy, prefix or normal.\n",
687 found_error = 1;
688 }
689 }
690
691 if (config.element_height < 1) {
692 g_string_append_printf(msg,
693 "\t<b>config.element_height</b>=%d is invalid. An "
694 "element needs to be at least 1 line high.\n",
697 found_error = TRUE;
698 }
699 if (!(config.location >= 0 && config.location <= 8)) {
700 g_string_append_printf(msg,
701 "\t<b>config.location</b>=%d is invalid. Value "
702 "should be between %d and %d.\n",
703 config.location, 0, 8);
705 found_error = 1;
706 }
707
708 // Check size
709 {
711 if (!monitor_active(&mon)) {
712 const char *name = config.monitor;
713 if (name && name[0] == '-') {
714 int index = name[1] - '0';
715 if (index < 5 && index > 0) {
716 name = monitor_position_entries[index - 1];
717 }
718 }
719 g_string_append_printf(
720 msg, "\t<b>config.monitor</b>=%s Could not find monitor.\n", name);
721 found_error = TRUE;
722 }
723 }
724
725 if (g_strcmp0(config.monitor, "-3") == 0) {
726 // On -3, set to location 1.
727 config.location = 1;
728 }
729
730 if (found_error) {
731 g_string_append(msg, "Please update your configuration.");
733 return TRUE;
734 }
735
736 g_string_free(msg, TRUE);
737 return FALSE;
738}
739
740char *rofi_expand_path(const char *input) {
741 char **str = g_strsplit(input, G_DIR_SEPARATOR_S, -1);
742 for (unsigned int i = 0; str && str[i]; i++) {
743 // Replace ~ with current user homedir.
744 if (str[i][0] == '~' && str[i][1] == '\0') {
745 g_free(str[i]);
746 str[i] = g_strdup(g_get_home_dir());
747 }
748 // If other user, ask getpwnam.
749 else if (str[i][0] == '~') {
750 struct passwd *p = getpwnam(&(str[i][1]));
751 if (p != NULL) {
752 g_free(str[i]);
753 str[i] = g_strdup(p->pw_dir);
754 }
755 } else if (i == 0) {
756 char *s = str[i];
757 if (input[0] == G_DIR_SEPARATOR) {
758 str[i] = g_strdup_printf("%s%s", G_DIR_SEPARATOR_S, s);
759 g_free(s);
760 }
761 }
762 }
763 char *retv = g_build_filenamev(str);
764 g_strfreev(str);
765 return retv;
766}
767
769#define MIN3(a, b, c) \
770 ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
771
772unsigned int levenshtein(const char *needle, const glong needlelen,
773 const char *haystack, const glong haystacklen) {
774 if (needlelen == G_MAXLONG) {
775 // String to long, we cannot handle this.
776 return UINT_MAX;
777 }
778 unsigned int column[needlelen + 1];
779 for (glong y = 0; y < needlelen; y++) {
780 column[y] = y;
781 }
782 // Removed out of the loop, otherwise static code analyzers think it is
783 // unset.. silly but true. old loop: for ( glong y = 0; y <= needlelen; y++)
784 column[needlelen] = needlelen;
785 for (glong x = 1; x <= haystacklen; x++) {
786 const char *needles = needle;
787 column[0] = x;
788 gunichar haystackc = g_utf8_get_char(haystack);
789 if (!config.case_sensitive) {
790 haystackc = g_unichar_tolower(haystackc);
791 }
792 for (glong y = 1, lastdiag = x - 1; y <= needlelen; y++) {
793 gunichar needlec = g_utf8_get_char(needles);
794 if (!config.case_sensitive) {
795 needlec = g_unichar_tolower(needlec);
796 }
797 unsigned int olddiag = column[y];
798 column[y] = MIN3(column[y] + 1, column[y - 1] + 1,
799 lastdiag + (needlec == haystackc ? 0 : 1));
800 lastdiag = olddiag;
801 needles = g_utf8_next_char(needles);
802 }
803 haystack = g_utf8_next_char(haystack);
804 }
805 return column[needlelen];
806}
807
808char *rofi_latin_to_utf8_strdup(const char *input, gssize length) {
809 gsize slength = 0;
810 return g_convert_with_fallback(input, length, "UTF-8", "latin1", "\uFFFD",
811 NULL, &slength, NULL);
812}
813
814char *rofi_force_utf8(const gchar *data, ssize_t length) {
815 if (data == NULL) {
816 return NULL;
817 }
818 const char *end;
819 GString *string;
820
821 if (g_utf8_validate(data, length, &end)) {
822 return g_memdup(data, length + 1);
823 }
824 string = g_string_sized_new(length + 16);
825
826 do {
827 /* Valid part of the string */
828 g_string_append_len(string, data, end - data);
829 /* Replacement character */
830 g_string_append(string, "\uFFFD");
831 length -= (end - data) + 1;
832 data = end + 1;
833 } while (!g_utf8_validate(data, length, &end));
834
835 if (length) {
836 g_string_append_len(string, data, length);
837 }
838
839 return g_string_free(string, FALSE);
840}
841
842/****
843 * FZF like scorer
844 */
845
847#define FUZZY_SCORER_MAX_LENGTH 256
849#define MIN_SCORE (INT_MIN / 2)
851#define LEADING_GAP_SCORE -4
853#define GAP_SCORE -5
855#define WORD_START_SCORE 50
857#define NON_WORD_SCORE 40
859#define CAMEL_SCORE (WORD_START_SCORE + GAP_SCORE - 1)
861#define CONSECUTIVE_SCORE (WORD_START_SCORE + GAP_SCORE)
863#define PATTERN_NON_START_MULTIPLIER 1
865#define PATTERN_START_MULTIPLIER 2
866
871 /* Lower case */
873 /* Upper case */
875 /* Number */
877 /* non word character */
880
887 if (g_unichar_islower(c)) {
888 return LOWER;
889 }
890 if (g_unichar_isupper(c)) {
891 return UPPER;
892 }
893 if (g_unichar_isdigit(c)) {
894 return DIGIT;
895 }
896 return NON_WORD;
897}
898
907static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr) {
908 if (prev == NON_WORD && curr != NON_WORD) {
909 return WORD_START_SCORE;
910 }
911 if ((prev == LOWER && curr == UPPER) || (prev != DIGIT && curr == DIGIT)) {
912 return CAMEL_SCORE;
913 }
914 if (curr == NON_WORD) {
915 return NON_WORD_SCORE;
916 }
917 return 0;
918}
919
920int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
921 glong slen) {
922 if (slen > FUZZY_SCORER_MAX_LENGTH) {
923 return -MIN_SCORE;
924 }
925 glong pi, si;
926 // whether we are aligning the first character of pattern
927 gboolean pfirst = TRUE;
928 // whether the start of a word in pattern
929 gboolean pstart = TRUE;
930 // score for each position
931 int *score = g_malloc_n(slen, sizeof(int));
932 // dp[i]: maximum value by aligning pattern[0..pi] to str[0..si]
933 int *dp = g_malloc_n(slen, sizeof(int));
934 // uleft: value of the upper left cell; ulefts: maximum value of uleft and
935 // cells on the left. The arbitrary initial values suppress warnings.
936 int uleft = 0, ulefts = 0, left, lefts;
937 const gchar *pit = pattern, *sit;
938 enum CharClass prev = NON_WORD;
939 for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
940 enum CharClass cur = rofi_scorer_get_character_class(g_utf8_get_char(sit));
941 score[si] = rofi_scorer_get_score_for(prev, cur);
942 prev = cur;
943 dp[si] = MIN_SCORE;
944 }
945 for (pi = 0; pi < plen; pi++, pit = g_utf8_next_char(pit)) {
946 gunichar pc = g_utf8_get_char(pit), sc;
947 if (g_unichar_isspace(pc)) {
948 pstart = TRUE;
949 continue;
950 }
951 lefts = MIN_SCORE;
952 for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
953 left = dp[si];
954 lefts = MAX(lefts + GAP_SCORE, left);
955 sc = g_utf8_get_char(sit);
957 ? pc == sc
958 : g_unichar_tolower(pc) == g_unichar_tolower(sc)) {
959 int t = score[si] * (pstart ? PATTERN_START_MULTIPLIER
961 dp[si] = pfirst ? LEADING_GAP_SCORE * si + t
962 : MAX(uleft + CONSECUTIVE_SCORE, ulefts + t);
963 } else {
964 dp[si] = MIN_SCORE;
965 }
966 uleft = left;
967 ulefts = lefts;
968 }
969 pfirst = pstart = FALSE;
970 }
971 lefts = MIN_SCORE;
972 for (si = 0; si < slen; si++) {
973 lefts = MAX(lefts + GAP_SCORE, dp[si]);
974 }
975 g_free(score);
976 g_free(dp);
977 return -lefts;
978}
979
991int utf8_strncmp(const char *a, const char *b, size_t n) {
992 char *na = g_utf8_normalize(a, -1, G_NORMALIZE_ALL_COMPOSE);
993 char *nb = g_utf8_normalize(b, -1, G_NORMALIZE_ALL_COMPOSE);
994 *g_utf8_offset_to_pointer(na, n) = '\0';
995 *g_utf8_offset_to_pointer(nb, n) = '\0';
996 int r = g_utf8_collate(na, nb);
997 g_free(na);
998 g_free(nb);
999 return r;
1000}
1001
1002gboolean helper_execute(const char *wd, char **args, const char *error_precmd,
1003 const char *error_cmd,
1004 RofiHelperExecuteContext *context) {
1005 gboolean retv = TRUE;
1006 GError *error = NULL;
1007
1008 GSpawnChildSetupFunc child_setup = NULL;
1009 gpointer user_data = NULL;
1010
1011 display_startup_notification(context, &child_setup, &user_data);
1012
1013 g_spawn_async(wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data,
1014 NULL, &error);
1015 if (error != NULL) {
1016 char *msg = g_strdup_printf("Failed to execute: '%s%s'\nError: '%s'",
1017 error_precmd, error_cmd, error->message);
1018 rofi_view_error_dialog(msg, FALSE);
1019 g_free(msg);
1020 // print error.
1021 g_error_free(error);
1022 retv = FALSE;
1023 }
1024
1025 // Free the args list.
1026 g_strfreev(args);
1027 return retv;
1028}
1029
1030gboolean helper_execute_command(const char *wd, const char *cmd,
1031 gboolean run_in_term,
1032 RofiHelperExecuteContext *context) {
1033 char **args = NULL;
1034 int argc = 0;
1035
1036 if (run_in_term) {
1037 helper_parse_setup(config.run_shell_command, &args, &argc, "{cmd}", cmd,
1038 (char *)0);
1039 } else {
1040 helper_parse_setup(config.run_command, &args, &argc, "{cmd}", cmd,
1041 (char *)0);
1042 }
1043
1044 if (args == NULL) {
1045 return FALSE;
1046 }
1047
1048 if (context != NULL) {
1049 if (context->name == NULL) {
1050 context->name = args[0];
1051 }
1052 if (context->binary == NULL) {
1053 context->binary = args[0];
1054 }
1055 if (context->description == NULL) {
1056 gsize l = strlen("Launching '' via rofi") + strlen(cmd) + 1;
1057 gchar *description = g_newa(gchar, l);
1058
1059 g_snprintf(description, l, "Launching '%s' via rofi", cmd);
1060 context->description = description;
1061 }
1062 if (context->command == NULL) {
1063 context->command = cmd;
1064 }
1065 }
1066
1067 return helper_execute(wd, args, "", cmd, context);
1068}
1069
1070char *helper_get_theme_path(const char *file, const char *ext) {
1071 char *filename = rofi_expand_path(file);
1072 g_debug("Opening theme, testing: %s\n", filename);
1073 if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
1074 return filename;
1075 }
1076 g_free(filename);
1077
1078 if (g_str_has_suffix(file, ext)) {
1079 filename = g_strdup(file);
1080 } else {
1081 filename = g_strconcat(file, ext, NULL);
1082 }
1083 // Check config's themes directory.
1084 const char *cpath = g_get_user_config_dir();
1085 if (cpath) {
1086 char *themep = g_build_filename(cpath, "rofi", "themes", filename, NULL);
1087 g_debug("Opening theme, testing: %s", themep);
1088 if (themep && g_file_test(themep, G_FILE_TEST_EXISTS)) {
1089 g_free(filename);
1090 return themep;
1091 }
1092 g_free(themep);
1093 }
1094 // Check config directory.
1095 if (cpath) {
1096 char *themep = g_build_filename(cpath, "rofi", filename, NULL);
1097 g_debug("Opening theme, testing: %s", themep);
1098 if (g_file_test(themep, G_FILE_TEST_EXISTS)) {
1099 g_free(filename);
1100 return themep;
1101 }
1102 g_free(themep);
1103 }
1104 const char *datadir = g_get_user_data_dir();
1105 if (datadir) {
1106 char *theme_path =
1107 g_build_filename(datadir, "rofi", "themes", filename, NULL);
1108 if (theme_path) {
1109 g_debug("Opening theme, testing: %s", theme_path);
1110 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1111 g_free(filename);
1112 return theme_path;
1113 }
1114 g_free(theme_path);
1115 }
1116 }
1117
1118 const gchar *const *system_data_dirs = g_get_system_data_dirs();
1119 if (system_data_dirs) {
1120 for (uint_fast32_t i = 0; system_data_dirs[i] != NULL; i++) {
1121 const char *const sdatadir = system_data_dirs[i];
1122 g_debug("Opening theme directory: %s", sdatadir);
1123 char *theme_path =
1124 g_build_filename(sdatadir, "rofi", "themes", filename, NULL);
1125 if (theme_path) {
1126 g_debug("Opening theme, testing: %s", theme_path);
1127 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1128 g_free(filename);
1129 return theme_path;
1130 }
1131 g_free(theme_path);
1132 }
1133 }
1134 }
1135
1136 char *theme_path = g_build_filename(THEME_DIR, filename, NULL);
1137 if (theme_path) {
1138 g_debug("Opening theme, testing: %s", theme_path);
1139 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1140 g_free(filename);
1141 return theme_path;
1142 }
1143 g_free(theme_path);
1144 }
1145 return filename;
1146}
1147
1148static gboolean parse_pair(char *input, rofi_range_pair *item) {
1149 // Skip leading blanks.
1150 while (input != NULL && isblank(*input)) {
1151 ++input;
1152 }
1153
1154 if (input == NULL) {
1155 return FALSE;
1156 }
1157
1158 const char *sep[] = {"-", ":"};
1159 int pythonic = (strchr(input, ':') || input[0] == '-') ? 1 : 0;
1160 int index = 0;
1161
1162 for (char *token = strsep(&input, sep[pythonic]); token != NULL;
1163 token = strsep(&input, sep[pythonic])) {
1164 if (index == 0) {
1165 item->start = item->stop = (int)strtol(token, NULL, 10);
1166 index++;
1167 continue;
1168 }
1169
1170 if (token[0] == '\0') {
1171 item->stop = -1;
1172 continue;
1173 }
1174
1175 item->stop = (int)strtol(token, NULL, 10);
1176 if (pythonic) {
1177 --item->stop;
1178 }
1179 }
1180 return TRUE;
1181}
1182void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length) {
1183 char *endp;
1184 if (input == NULL) {
1185 return;
1186 }
1187 const char *const sep = ",";
1188 for (char *token = strtok_r(input, sep, &endp); token != NULL;
1189 token = strtok_r(NULL, sep, &endp)) {
1190 // Make space.
1191 *list =
1192 g_realloc((*list), ((*length) + 1) * sizeof(struct rofi_range_pair));
1193 // Parse a single pair.
1194 if (parse_pair(token, &((*list)[*length]))) {
1195 (*length)++;
1196 }
1197 }
1198}
1199void rofi_output_formatted_line(const char *format, const char *string,
1200 int selected_line, const char *filter) {
1201 for (int i = 0; format && format[i]; i++) {
1202 if (format[i] == 'i') {
1203 fprintf(stdout, "%d", selected_line);
1204 } else if (format[i] == 'd') {
1205 fprintf(stdout, "%d", (selected_line + 1));
1206 } else if (format[i] == 's') {
1207 fputs(string, stdout);
1208 } else if (format[i] == 'p') {
1209 char *esc = NULL;
1210 pango_parse_markup(string, -1, 0, NULL, &esc, NULL, NULL);
1211 if (esc) {
1212 fputs(esc, stdout);
1213 g_free(esc);
1214 } else {
1215 fputs("invalid string", stdout);
1216 }
1217 } else if (format[i] == 'q') {
1218 char *quote = g_shell_quote(string);
1219 fputs(quote, stdout);
1220 g_free(quote);
1221 } else if (format[i] == 'f') {
1222 if (filter) {
1223 fputs(filter, stdout);
1224 }
1225 } else if (format[i] == 'F') {
1226 if (filter) {
1227 char *quote = g_shell_quote(filter);
1228 fputs(quote, stdout);
1229 g_free(quote);
1230 }
1231 } else {
1232 fputc(format[i], stdout);
1233 }
1234 }
1235 fputc('\n', stdout);
1236 fflush(stdout);
1237}
1238
1239static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res,
1240 gpointer data) {
1241 gchar *match;
1242 // Get the match
1243 int num_match = g_match_info_get_match_count(info);
1244 // Just {text} This is inside () 5.
1245 if (num_match == 5) {
1246 match = g_match_info_fetch(info, 4);
1247 if (match != NULL) {
1248 // Lookup the match, so we can replace it.
1249 gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1250 if (r != NULL) {
1251 // Append the replacement to the string.
1252 g_string_append(res, r);
1253 }
1254 // Free match.
1255 g_free(match);
1256 }
1257 }
1258 // {} with [] guard around it.
1259 else if (num_match == 4) {
1260 match = g_match_info_fetch(info, 2);
1261 if (match != NULL) {
1262 // Lookup the match, so we can replace it.
1263 gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1264 if (r != NULL) {
1265 // Add (optional) prefix
1266 gchar *prefix = g_match_info_fetch(info, 1);
1267 g_string_append(res, prefix);
1268 g_free(prefix);
1269 // Append the replacement to the string.
1270 g_string_append(res, r);
1271 // Add (optional) postfix
1272 gchar *post = g_match_info_fetch(info, 3);
1273 g_string_append(res, post);
1274 g_free(post);
1275 }
1276 // Free match.
1277 g_free(match);
1278 }
1279 }
1280 // Else we have an invalid match.
1281 // Continue replacement.
1282 return FALSE;
1283}
1284
1285char *helper_string_replace_if_exists(char *string, ...) {
1286 GHashTable *h;
1287 h = g_hash_table_new(g_str_hash, g_str_equal);
1288 va_list ap;
1289 va_start(ap, string);
1290 // Add list from variable arguments.
1291 while (1) {
1292 char *key = va_arg(ap, char *);
1293 if (key == (char *)0) {
1294 break;
1295 }
1296 char *value = va_arg(ap, char *);
1297 g_hash_table_insert(h, key, value);
1298 }
1299 char *retv = helper_string_replace_if_exists_v(string, h);
1300 va_end(ap);
1301 // Destroy key-value storage.
1302 g_hash_table_destroy(h);
1303 return retv;
1304}
1320char *helper_string_replace_if_exists_v(char *string, GHashTable *h) {
1321 GError *error = NULL;
1322 char *res = NULL;
1323
1324 // Replace hits within {-\w+}.
1325 GRegex *reg = g_regex_new("\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})",
1326 G_REGEX_UNGREEDY, 0, &error);
1327 if (error == NULL) {
1328 res =
1329 g_regex_replace_eval(reg, string, -1, 0, 0, helper_eval_cb2, h, &error);
1330 }
1331 // Free regex.
1332 g_regex_unref(reg);
1333 // Throw error if shell parsing fails.
1334 if (error != NULL) {
1335 char *msg = g_strdup_printf("Failed to parse: '%s'\nError: '%s'", string,
1336 error->message);
1337 rofi_view_error_dialog(msg, FALSE);
1338 g_free(msg);
1339 // print error.
1340 g_error_free(error);
1341 g_free(res);
1342 return NULL;
1343 }
1344 return res;
1345}
@ WL_CENTER
Definition rofi-types.h:237
@ MM_NORMAL
Definition settings.h:39
@ MM_REGEX
Definition settings.h:40
@ MM_PREFIX
Definition settings.h:43
@ MM_FUZZY
Definition settings.h:42
@ MM_GLOB
Definition settings.h:41
void helper_token_match_set_pango_attr_on_style(PangoAttrList *retv, int start, int end, RofiHighlightColorStyle th)
Definition helper.c:418
PangoAttrList * helper_token_match_get_pango_attr(RofiHighlightColorStyle th, rofi_int_matcher **tokens, const char *input, PangoAttrList *retv)
Definition helper.c:487
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
Definition helper.c:628
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
Definition helper.c:808
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length)
Definition helper.c:1182
void cmd_set_arguments(int argc, char **argv)
Definition helper.c:70
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
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
Definition helper.c:1030
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition helper.c:119
char helper_parse_char(const char *arg)
Definition helper.c:360
void rofi_output_formatted_line(const char *format, const char *string, int selected_line, const char *filter)
Definition helper.c:1199
gboolean helper_execute(const char *wd, char **args, const char *error_precmd, const char *error_cmd, RofiHelperExecuteContext *context)
Definition helper.c:1002
unsigned int levenshtein(const char *needle, const glong needlelen, const char *haystack, const glong haystacklen)
Definition helper.c:772
char * helper_string_replace_if_exists(char *string,...)
Definition helper.c:1285
const char ** find_arg_strv(const char *const key)
Definition helper.c:321
int helper_parse_setup(char *string, char ***output, int *length,...)
Definition helper.c:75
char * helper_get_theme_path(const char *file, const char *ext)
Definition helper.c:1070
int execute_generator(const char *cmd)
Definition helper.c:539
int find_arg_int(const char *const key, int *val)
Definition helper.c:341
char * rofi_expand_path(const char *input)
Definition helper.c:740
void remove_pid_file(int fd)
Definition helper.c:620
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str, glong slen)
Definition helper.c:920
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
int create_pid_file(const char *pidfile, gboolean kill_running)
Definition helper.c:562
int config_sanity_check(void)
Definition helper.c:647
char * rofi_force_utf8(const gchar *data, ssize_t length)
Definition helper.c:814
void rofi_add_error_message(GString *str)
Definition rofi.c:90
int rofi_view_error_dialog(const char *msg, int markup)
Definition view.c:2191
#define CONSECUTIVE_SCORE
Definition helper.c:861
#define GAP_SCORE
Definition helper.c:853
#define LEADING_GAP_SCORE
Definition helper.c:851
static gchar * prefix_regex(const char *input)
Definition helper.c:171
char ** stored_argv
Definition helper.c:66
#define MIN3(a, b, c)
Definition helper.c:769
#define CAMEL_SCORE
Definition helper.c:859
static gchar * glob_to_regex(const char *input)
Definition helper.c:127
const char *const monitor_position_entries[]
Definition helper.c:60
static enum CharClass rofi_scorer_get_character_class(gunichar c)
Definition helper.c:886
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition helper.c:991
#define MIN_SCORE
Definition helper.c:849
#define PATTERN_NON_START_MULTIPLIER
Definition helper.c:863
char * helper_string_replace_if_exists_v(char *string, GHashTable *h)
Definition helper.c:1320
static char * utf8_helper_simplify_string(const char *s)
Definition helper.c:178
#define WORD_START_SCORE
Definition helper.c:855
#define FUZZY_SCORER_MAX_LENGTH
Definition helper.c:847
static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res, gpointer data)
Definition helper.c:1239
#define PATTERN_START_MULTIPLIER
Definition helper.c:865
static rofi_int_matcher * create_regex(const char *input, int case_sensitive)
Definition helper.c:222
static GRegex * R(const char *s, int case_sensitive)
Definition helper.c:207
#define NON_WORD_SCORE
Definition helper.c:857
static gchar * fuzzy_to_regex(const char *input)
Definition helper.c:142
CharClass
Definition helper.c:870
@ DIGIT
Definition helper.c:876
@ LOWER
Definition helper.c:872
@ NON_WORD
Definition helper.c:878
@ UPPER
Definition helper.c:874
int stored_argc
Definition helper.c:64
static gboolean parse_pair(char *input, rofi_range_pair *item)
Definition helper.c:1148
static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr)
Definition helper.c:907
@ ROFI_HL_UPPERCASE
Definition rofi-types.h:66
@ ROFI_HL_STRIKETHROUGH
Definition rofi-types.h:60
@ ROFI_HL_ITALIC
Definition rofi-types.h:62
@ ROFI_HL_UNDERLINE
Definition rofi-types.h:58
@ ROFI_HL_CAPITALIZE
Definition rofi-types.h:70
@ ROFI_HL_BOLD
Definition rofi-types.h:56
@ ROFI_HL_LOWERCASE
Definition rofi-types.h:68
@ ROFI_HL_COLOR
Definition rofi-types.h:64
char * pidfile
Definition rofi.c:81
Settings config
@ SORT_FZF
Definition settings.h:49
@ SORT_NORMAL
Definition settings.h:49
const gchar * binary
Definition helper.h:290
const gchar * description
Definition helper.h:292
const gchar * name
Definition helper.h:288
const gchar * command
Definition helper.h:300
RofiHighlightStyle style
Definition rofi-types.h:221
WindowLocation location
Definition settings.h:84
char * matching
Definition settings.h:133
MatchingMethod matching_method
Definition settings.h:134
unsigned int tokenize
Definition settings.h:135
char * run_command
Definition settings.h:71
gboolean normalize_match
Definition settings.h:175
char * terminal_emulator
Definition settings.h:65
char * run_shell_command
Definition settings.h:73
unsigned int case_sensitive
Definition settings.h:114
char * sorting_method
Definition settings.h:100
char matching_negate_char
Definition settings.h:160
SortingMethod sorting_method_enum
Definition settings.h:98
char * ssh_client
Definition settings.h:67
int element_height
Definition settings.h:118
char * monitor
Definition settings.h:137
double blue
Definition rofi-types.h:164
double green
Definition rofi-types.h:162
double red
Definition rofi-types.h:160
double alpha
Definition rofi-types.h:166
workarea mon
Definition view.c:111
MenuFlags flags
Definition view.c:107
unsigned long long count
Definition view.c:120
int monitor_active(workarea *mon)
Definition xcb.c:992
void display_startup_notification(RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data)
Definition xcb.c:711