rofi  1.7.0
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-2021 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 
60 const 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"};
64 int stored_argc = 0;
66 char **stored_argv = NULL;
67 
68 char *helper_string_replace_if_exists_v(char *string, GHashTable *h);
69 
70 void cmd_set_arguments(int argc, char **argv) {
71  stored_argc = argc;
72  stored_argv = argv;
73 }
74 
75 int 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 
127 static 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 }
142 static 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 
171 static 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 
178 static 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  char *str = g_malloc0((g_utf8_strlen(s, 0) * 6 + 2));
187  char *striter = str;
188  for (const char *iter = s; iter && *iter; iter = g_utf8_next_char(iter)) {
189  gunichar uc = g_utf8_get_char(iter);
190  int l = 0;
191  gsize dl = g_unichar_fully_decompose(uc, FALSE, buf2,
192  G_UNICHAR_MAX_DECOMPOSITION_LENGTH);
193  if (dl) {
194  l = g_unichar_to_utf8(buf2[0], buf);
195  } else {
196  l = g_unichar_to_utf8(uc, buf);
197  }
198  memcpy(striter, buf, l);
199  striter += l;
200  }
201 
202  return str;
203 }
204 
205 // Macro for quickly generating regex for matching.
206 static inline GRegex *R(const char *s, int case_sensitive) {
207  if (config.normalize_match) {
208  char *str = utf8_helper_simplify_string(s);
209 
210  GRegex *r = g_regex_new(
211  str, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0,
212  NULL);
213 
214  g_free(str);
215  return r;
216  }
217  return g_regex_new(
218  s, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0, NULL);
219 }
220 
221 static rofi_int_matcher *create_regex(const char *input, int case_sensitive) {
222  GRegex *retv = NULL;
223  gchar *r;
224  rofi_int_matcher *rv = g_malloc0(sizeof(rofi_int_matcher));
225  if (input && input[0] == config.matching_negate_char) {
226  rv->invert = 1;
227  input++;
228  }
229  switch (config.matching_method) {
230  case MM_GLOB:
231  r = glob_to_regex(input);
232  retv = R(r, case_sensitive);
233  g_free(r);
234  break;
235  case MM_REGEX:
236  retv = R(input, case_sensitive);
237  if (retv == NULL) {
238  r = g_regex_escape_string(input, -1);
239  retv = R(r, case_sensitive);
240  g_free(r);
241  }
242  break;
243  case MM_FUZZY:
244  r = fuzzy_to_regex(input);
245  retv = R(r, case_sensitive);
246  g_free(r);
247  break;
248  case MM_PREFIX:
249  r = prefix_regex(input);
250  retv = R(r, case_sensitive);
251  g_free(r);
252  break;
253  default:
254  r = g_regex_escape_string(input, -1);
255  retv = R(r, case_sensitive);
256  g_free(r);
257  break;
258  }
259  rv->regex = retv;
260  return rv;
261 }
262 rofi_int_matcher **helper_tokenize(const char *input, int case_sensitive) {
263  if (input == NULL) {
264  return NULL;
265  }
266  size_t len = strlen(input);
267  if (len == 0) {
268  return NULL;
269  }
270 
271  char *saveptr = NULL, *token;
272  rofi_int_matcher **retv = NULL;
273  if (!config.tokenize) {
274  retv = g_malloc0(sizeof(rofi_int_matcher *) * 2);
275  retv[0] = create_regex(input, case_sensitive);
276  return retv;
277  }
278 
279  // First entry is always full (modified) stringtext.
280  int num_tokens = 0;
281 
282  // Copy the string, 'strtok_r' modifies it.
283  char *str = g_strdup(input);
284 
285  // Iterate over tokens.
286  // strtok should still be valid for utf8.
287  const char *const sep = " ";
288  for (token = strtok_r(str, sep, &saveptr); token != NULL;
289  token = strtok_r(NULL, sep, &saveptr)) {
290  retv = g_realloc(retv, sizeof(rofi_int_matcher *) * (num_tokens + 2));
291  retv[num_tokens] = create_regex(token, case_sensitive);
292  retv[num_tokens + 1] = NULL;
293  num_tokens++;
294  }
295  // Free str.
296  g_free(str);
297  return retv;
298 }
299 
300 // cli arg handling
301 int find_arg(const char *const key) {
302  int i;
303 
304  for (i = 0; i < stored_argc && strcasecmp(stored_argv[i], key); i++) {
305  ;
306  }
307 
308  return i < stored_argc ? i : -1;
309 }
310 int find_arg_str(const char *const key, char **val) {
311  int i = find_arg(key);
312 
313  if (val != NULL && i > 0 && i < stored_argc - 1) {
314  *val = stored_argv[i + 1];
315  return TRUE;
316  }
317  return FALSE;
318 }
319 
320 const char **find_arg_strv(const char *const key) {
321  const char **retv = NULL;
322  int length = 0;
323  for (int i = 0; i < stored_argc; i++) {
324  if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
325  length++;
326  }
327  }
328  if (length > 0) {
329  retv = g_malloc0((length + 1) * sizeof(char *));
330  int index = 0;
331  for (int i = 0; i < stored_argc; i++) {
332  if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
333  retv[index++] = stored_argv[i + 1];
334  }
335  }
336  }
337  return retv;
338 }
339 
340 int find_arg_int(const char *const key, int *val) {
341  int i = find_arg(key);
342 
343  if (val != NULL && i > 0 && i < (stored_argc - 1)) {
344  *val = strtol(stored_argv[i + 1], NULL, 10);
345  return TRUE;
346  }
347  return FALSE;
348 }
349 int find_arg_uint(const char *const key, unsigned int *val) {
350  int i = find_arg(key);
351 
352  if (val != NULL && i > 0 && i < (stored_argc - 1)) {
353  *val = strtoul(stored_argv[i + 1], NULL, 10);
354  return TRUE;
355  }
356  return FALSE;
357 }
358 
359 char helper_parse_char(const char *arg) {
360  const size_t len = strlen(arg);
361  // If the length is 1, it is not escaped.
362  if (len == 1) {
363  return arg[0];
364  }
365  // If the length is 2 and the first character is '\', we unescape it.
366  if (len == 2 && arg[0] == '\\') {
367  switch (arg[1]) {
368  // New line
369  case 'n':
370  return '\n';
371  // Bell
372  case 'a':
373  return '\a';
374  // Backspace
375  case 'b':
376  return '\b';
377  // Tab
378  case 't':
379  return '\t';
380  // Vertical tab
381  case 'v':
382  return '\v';
383  // Form feed
384  case 'f':
385  return '\f';
386  // Carriage return
387  case 'r':
388  return '\r';
389  // Forward slash
390  case '\\':
391  return '\\';
392  // 0 line.
393  case '0':
394  return '\0';
395  default:
396  break;
397  }
398  }
399  if (len > 2 && arg[0] == '\\' && arg[1] == 'x') {
400  return (char)strtol(&arg[2], NULL, 16);
401  }
402  g_warning("Failed to parse character string: \"%s\"", arg);
403  // for now default to newline.
404  return '\n';
405 }
406 
407 int find_arg_char(const char *const key, char *val) {
408  int i = find_arg(key);
409 
410  if (val != NULL && i > 0 && i < (stored_argc - 1)) {
411  *val = helper_parse_char(stored_argv[i + 1]);
412  return TRUE;
413  }
414  return FALSE;
415 }
416 
418  rofi_int_matcher **tokens,
419  const char *input,
420  PangoAttrList *retv) {
421  // Disable highlighting for normalize match, not supported atm.
422  if (config.normalize_match) {
423  return retv;
424  }
425  // Do a tokenized match.
426  if (tokens) {
427  for (int j = 0; tokens[j]; j++) {
428  GMatchInfo *gmi = NULL;
429  if (tokens[j]->invert) {
430  continue;
431  }
432  g_regex_match(tokens[j]->regex, input, G_REGEX_MATCH_PARTIAL, &gmi);
433  while (g_match_info_matches(gmi)) {
434  int count = g_match_info_get_match_count(gmi);
435  for (int index = (count > 1) ? 1 : 0; index < count; index++) {
436  int start, end;
437  g_match_info_fetch_pos(gmi, index, &start, &end);
438  if (th.style & ROFI_HL_BOLD) {
439  PangoAttribute *pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
440  pa->start_index = start;
441  pa->end_index = end;
442  pango_attr_list_insert(retv, pa);
443  }
444  if (th.style & ROFI_HL_UNDERLINE) {
445  PangoAttribute *pa =
446  pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
447  pa->start_index = start;
448  pa->end_index = end;
449  pango_attr_list_insert(retv, pa);
450  }
451  if (th.style & ROFI_HL_STRIKETHROUGH) {
452  PangoAttribute *pa = pango_attr_strikethrough_new(TRUE);
453  pa->start_index = start;
454  pa->end_index = end;
455  pango_attr_list_insert(retv, pa);
456  }
457  if (th.style & ROFI_HL_SMALL_CAPS) {
458  PangoAttribute *pa =
459  pango_attr_variant_new(PANGO_VARIANT_SMALL_CAPS);
460  pa->start_index = start;
461  pa->end_index = end;
462  pango_attr_list_insert(retv, pa);
463  }
464  if (th.style & ROFI_HL_ITALIC) {
465  PangoAttribute *pa = pango_attr_style_new(PANGO_STYLE_ITALIC);
466  pa->start_index = start;
467  pa->end_index = end;
468  pango_attr_list_insert(retv, pa);
469  }
470  if (th.style & ROFI_HL_COLOR) {
471  PangoAttribute *pa = pango_attr_foreground_new(
472  th.color.red * 65535, th.color.green * 65535,
473  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  g_match_info_next(gmi, NULL);
487  }
488  g_match_info_free(gmi);
489  }
490  }
491  return retv;
492 }
493 
494 int helper_token_match(rofi_int_matcher *const *tokens, const char *input) {
495  int match = TRUE;
496  // Do a tokenized match.
497  if (tokens) {
498  if (config.normalize_match) {
499  char *r = utf8_helper_simplify_string(input);
500  for (int j = 0; match && tokens[j]; j++) {
501  match = g_regex_match(tokens[j]->regex, r, 0, NULL);
502  match ^= tokens[j]->invert;
503  }
504  g_free(r);
505  } else {
506  for (int j = 0; match && tokens[j]; j++) {
507  match = g_regex_match(tokens[j]->regex, input, 0, NULL);
508  match ^= tokens[j]->invert;
509  }
510  }
511  }
512  return match;
513 }
514 
515 int execute_generator(const char *cmd) {
516  char **args = NULL;
517  int argv = 0;
518  helper_parse_setup(config.run_command, &args, &argv, "{cmd}", cmd, (char *)0);
519 
520  int fd = -1;
521  GError *error = NULL;
522  g_spawn_async_with_pipes(NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
523  NULL, NULL, &fd, NULL, &error);
524 
525  if (error != NULL) {
526  char *msg = g_strdup_printf("Failed to execute: '%s'\nError: '%s'", cmd,
527  error->message);
528  rofi_view_error_dialog(msg, FALSE);
529  g_free(msg);
530  // print error.
531  g_error_free(error);
532  fd = -1;
533  }
534  g_strfreev(args);
535  return fd;
536 }
537 
538 int create_pid_file(const char *pidfile) {
539  if (pidfile == NULL) {
540  return -1;
541  }
542 
543  int fd = g_open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
544  if (fd < 0) {
545  g_warning("Failed to create pid file: '%s'.", pidfile);
546  return -1;
547  }
548  // Set it to close the File Descriptor on exit.
549  int flags = fcntl(fd, F_GETFD, NULL);
550  flags = flags | FD_CLOEXEC;
551  if (fcntl(fd, F_SETFD, flags, NULL) < 0) {
552  g_warning("Failed to set CLOEXEC on pidfile.");
553  remove_pid_file(fd);
554  return -1;
555  }
556  // Try to get exclusive write lock on FD
557  int retv = flock(fd, LOCK_EX | LOCK_NB);
558  if (retv != 0) {
559  g_warning("Failed to set lock on pidfile: Rofi already running?");
560  g_warning("Got error: %d %s", retv, g_strerror(errno));
561  remove_pid_file(fd);
562  return -1;
563  }
564  if (ftruncate(fd, (off_t)0) == 0) {
565  // Write pid, not needed, but for completeness sake.
566  char buffer[64];
567  int length = snprintf(buffer, 64, "%i", getpid());
568  ssize_t l = 0;
569  while (l < length) {
570  l += write(fd, &buffer[l], length - l);
571  }
572  }
573  return fd;
574 }
575 
576 void remove_pid_file(int fd) {
577  if (fd >= 0) {
578  if (close(fd)) {
579  g_warning("Failed to close pidfile: '%s'", g_strerror(errno));
580  }
581  }
582 }
583 
584 gboolean helper_validate_font(PangoFontDescription *pfd, const char *font) {
585  const char *fam = pango_font_description_get_family(pfd);
586  int size = pango_font_description_get_size(pfd);
587  if (fam == NULL || size == 0) {
588  g_debug("Pango failed to parse font: '%s'", font);
589  g_debug("Got family: <b>%s</b> at size: <b>%d</b>", fam ? fam : "{unknown}",
590  size);
591  return FALSE;
592  }
593  return TRUE;
594 }
595 
604  int found_error = FALSE;
605  GString *msg =
606  g_string_new("<big><b>The configuration failed to validate:</b></big>\n");
607 
608  if (config.sorting_method) {
609  if (g_strcmp0(config.sorting_method, "normal") == 0) {
611  } else if (g_strcmp0(config.sorting_method, "levenshtein") == 0) {
613  } else if (g_strcmp0(config.sorting_method, "fzf") == 0) {
615  } else {
616  g_string_append_printf(
617  msg,
618  "\t<b>config.sorting_method</b>=%s is not a valid sorting "
619  "strategy.\nValid options are: normal or fzf.\n",
621  found_error = 1;
622  }
623  }
624 
625  if (config.matching) {
626  if (g_strcmp0(config.matching, "regex") == 0) {
628  } else if (g_strcmp0(config.matching, "glob") == 0) {
630  } else if (g_strcmp0(config.matching, "fuzzy") == 0) {
632  } else if (g_strcmp0(config.matching, "normal") == 0) {
634  ;
635  } else if (g_strcmp0(config.matching, "prefix") == 0) {
637  } else {
638  g_string_append_printf(msg,
639  "\t<b>config.matching</b>=%s is not a valid "
640  "matching strategy.\nValid options are: glob, "
641  "regex, fuzzy, prefix or normal.\n",
642  config.matching);
643  found_error = 1;
644  }
645  }
646 
647  if (config.element_height < 1) {
648  g_string_append_printf(msg,
649  "\t<b>config.element_height</b>=%d is invalid. An "
650  "element needs to be at least 1 line high.\n",
653  found_error = TRUE;
654  }
655  if (!(config.location >= 0 && config.location <= 8)) {
656  g_string_append_printf(msg,
657  "\t<b>config.location</b>=%d is invalid. Value "
658  "should be between %d and %d.\n",
659  config.location, 0, 8);
661  found_error = 1;
662  }
663 
664  // Check size
665  {
666  workarea mon;
667  if (!monitor_active(&mon)) {
668  const char *name = config.monitor;
669  if (name && name[0] == '-') {
670  int index = name[1] - '0';
671  if (index < 5 && index > 0) {
672  name = monitor_position_entries[index - 1];
673  }
674  }
675  g_string_append_printf(
676  msg, "\t<b>config.monitor</b>=%s Could not find monitor.\n", name);
677  found_error = TRUE;
678  }
679  }
680 
681  if (config.menu_font) {
682  PangoFontDescription *pfd =
683  pango_font_description_from_string(config.menu_font);
684  const char *fam = pango_font_description_get_family(pfd);
685  int size = pango_font_description_get_size(pfd);
686  if (fam == NULL || size == 0) {
687  g_string_append_printf(msg, "Pango failed to parse font: '%s'\n",
688  config.menu_font);
689  g_string_append_printf(msg,
690  "Got font family: <b>%s</b> at size <b>%d</b>\n",
691  fam ? fam : "{unknown}", size);
692  config.menu_font = NULL;
693  found_error = TRUE;
694  }
695  pango_font_description_free(pfd);
696  }
697 
698  if (g_strcmp0(config.monitor, "-3") == 0) {
699  // On -3, set to location 1.
700  config.location = 1;
701  }
702 
703  if (found_error) {
704  g_string_append(msg, "Please update your configuration.");
706  return TRUE;
707  }
708 
709  g_string_free(msg, TRUE);
710  return FALSE;
711 }
712 
713 char *rofi_expand_path(const char *input) {
714  char **str = g_strsplit(input, G_DIR_SEPARATOR_S, -1);
715  for (unsigned int i = 0; str && str[i]; i++) {
716  // Replace ~ with current user homedir.
717  if (str[i][0] == '~' && str[i][1] == '\0') {
718  g_free(str[i]);
719  str[i] = g_strdup(g_get_home_dir());
720  }
721  // If other user, ask getpwnam.
722  else if (str[i][0] == '~') {
723  struct passwd *p = getpwnam(&(str[i][1]));
724  if (p != NULL) {
725  g_free(str[i]);
726  str[i] = g_strdup(p->pw_dir);
727  }
728  } else if (i == 0) {
729  char *s = str[i];
730  if (input[0] == G_DIR_SEPARATOR) {
731  str[i] = g_strdup_printf("%s%s", G_DIR_SEPARATOR_S, s);
732  g_free(s);
733  }
734  }
735  }
736  char *retv = g_build_filenamev(str);
737  g_strfreev(str);
738  return retv;
739 }
740 
742 #define MIN3(a, b, c) \
743  ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
744 
745 unsigned int levenshtein(const char *needle, const glong needlelen,
746  const char *haystack, const glong haystacklen) {
747  if (needlelen == G_MAXLONG) {
748  // String to long, we cannot handle this.
749  return UINT_MAX;
750  }
751  unsigned int column[needlelen + 1];
752  for (glong y = 0; y < needlelen; y++) {
753  column[y] = y;
754  }
755  // Removed out of the loop, otherwise static code analyzers think it is
756  // unset.. silly but true. old loop: for ( glong y = 0; y <= needlelen; y++)
757  column[needlelen] = needlelen;
758  for (glong x = 1; x <= haystacklen; x++) {
759  const char *needles = needle;
760  column[0] = x;
761  gunichar haystackc = g_utf8_get_char(haystack);
762  if (!config.case_sensitive) {
763  haystackc = g_unichar_tolower(haystackc);
764  }
765  for (glong y = 1, lastdiag = x - 1; y <= needlelen; y++) {
766  gunichar needlec = g_utf8_get_char(needles);
767  if (!config.case_sensitive) {
768  needlec = g_unichar_tolower(needlec);
769  }
770  unsigned int olddiag = column[y];
771  column[y] = MIN3(column[y] + 1, column[y - 1] + 1,
772  lastdiag + (needlec == haystackc ? 0 : 1));
773  lastdiag = olddiag;
774  needles = g_utf8_next_char(needles);
775  }
776  haystack = g_utf8_next_char(haystack);
777  }
778  return column[needlelen];
779 }
780 
781 char *rofi_latin_to_utf8_strdup(const char *input, gssize length) {
782  gsize slength = 0;
783  return g_convert_with_fallback(input, length, "UTF-8", "latin1", "\uFFFD",
784  NULL, &slength, NULL);
785 }
786 
787 gchar *rofi_escape_markup(gchar *text) {
788  if (text == NULL) {
789  return NULL;
790  }
791  gchar *ret = g_markup_escape_text(text, -1);
792  g_free(text);
793  return ret;
794 }
795 
796 char *rofi_force_utf8(const gchar *data, ssize_t length) {
797  if (data == NULL) {
798  return NULL;
799  }
800  const char *end;
801  GString *string;
802 
803  if (g_utf8_validate(data, length, &end)) {
804  return g_memdup(data, length + 1);
805  }
806  string = g_string_sized_new(length + 16);
807 
808  do {
809  /* Valid part of the string */
810  g_string_append_len(string, data, end - data);
811  /* Replacement character */
812  g_string_append(string, "\uFFFD");
813  length -= (end - data) + 1;
814  data = end + 1;
815  } while (!g_utf8_validate(data, length, &end));
816 
817  if (length) {
818  g_string_append_len(string, data, length);
819  }
820 
821  return g_string_free(string, FALSE);
822 }
823 
824 /****
825  * FZF like scorer
826  */
827 
829 #define FUZZY_SCORER_MAX_LENGTH 256
831 #define MIN_SCORE (INT_MIN / 2)
833 #define LEADING_GAP_SCORE -4
835 #define GAP_SCORE -5
837 #define WORD_START_SCORE 50
839 #define NON_WORD_SCORE 40
841 #define CAMEL_SCORE (WORD_START_SCORE + GAP_SCORE - 1)
843 #define CONSECUTIVE_SCORE (WORD_START_SCORE + GAP_SCORE)
845 #define PATTERN_NON_START_MULTIPLIER 1
847 #define PATTERN_START_MULTIPLIER 2
848 
852 enum CharClass {
853  /* Lower case */
855  /* Upper case */
857  /* Number */
859  /* non word character */
860  NON_WORD
861 };
862 
868 static enum CharClass rofi_scorer_get_character_class(gunichar c) {
869  if (g_unichar_islower(c)) {
870  return LOWER;
871  }
872  if (g_unichar_isupper(c)) {
873  return UPPER;
874  }
875  if (g_unichar_isdigit(c)) {
876  return DIGIT;
877  }
878  return NON_WORD;
879 }
880 
889 static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr) {
890  if (prev == NON_WORD && curr != NON_WORD) {
891  return WORD_START_SCORE;
892  }
893  if ((prev == LOWER && curr == UPPER) || (prev != DIGIT && curr == DIGIT)) {
894  return CAMEL_SCORE;
895  }
896  if (curr == NON_WORD) {
897  return NON_WORD_SCORE;
898  }
899  return 0;
900 }
901 
902 int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
903  glong slen) {
904  if (slen > FUZZY_SCORER_MAX_LENGTH) {
905  return -MIN_SCORE;
906  }
907  glong pi, si;
908  // whether we are aligning the first character of pattern
909  gboolean pfirst = TRUE;
910  // whether the start of a word in pattern
911  gboolean pstart = TRUE;
912  // score for each position
913  int *score = g_malloc_n(slen, sizeof(int));
914  // dp[i]: maximum value by aligning pattern[0..pi] to str[0..si]
915  int *dp = g_malloc_n(slen, sizeof(int));
916  // uleft: value of the upper left cell; ulefts: maximum value of uleft and
917  // cells on the left. The arbitrary initial values suppress warnings.
918  int uleft = 0, ulefts = 0, left, lefts;
919  const gchar *pit = pattern, *sit;
920  enum CharClass prev = NON_WORD;
921  for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
922  enum CharClass cur = rofi_scorer_get_character_class(g_utf8_get_char(sit));
923  score[si] = rofi_scorer_get_score_for(prev, cur);
924  prev = cur;
925  dp[si] = MIN_SCORE;
926  }
927  for (pi = 0; pi < plen; pi++, pit = g_utf8_next_char(pit)) {
928  gunichar pc = g_utf8_get_char(pit), sc;
929  if (g_unichar_isspace(pc)) {
930  pstart = TRUE;
931  continue;
932  }
933  lefts = MIN_SCORE;
934  for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
935  left = dp[si];
936  lefts = MAX(lefts + GAP_SCORE, left);
937  sc = g_utf8_get_char(sit);
939  ? pc == sc
940  : g_unichar_tolower(pc) == g_unichar_tolower(sc)) {
941  int t = score[si] * (pstart ? PATTERN_START_MULTIPLIER
943  dp[si] = pfirst ? LEADING_GAP_SCORE * si + t
944  : MAX(uleft + CONSECUTIVE_SCORE, ulefts + t);
945  } else {
946  dp[si] = MIN_SCORE;
947  }
948  uleft = left;
949  ulefts = lefts;
950  }
951  pfirst = pstart = FALSE;
952  }
953  lefts = MIN_SCORE;
954  for (si = 0; si < slen; si++) {
955  lefts = MAX(lefts + GAP_SCORE, dp[si]);
956  }
957  g_free(score);
958  g_free(dp);
959  return -lefts;
960 }
961 
973 int utf8_strncmp(const char *a, const char *b, size_t n) {
974  char *na = g_utf8_normalize(a, -1, G_NORMALIZE_ALL_COMPOSE);
975  char *nb = g_utf8_normalize(b, -1, G_NORMALIZE_ALL_COMPOSE);
976  *g_utf8_offset_to_pointer(na, n) = '\0';
977  *g_utf8_offset_to_pointer(nb, n) = '\0';
978  int r = g_utf8_collate(na, nb);
979  g_free(na);
980  g_free(nb);
981  return r;
982 }
983 
984 gboolean helper_execute(const char *wd, char **args, const char *error_precmd,
985  const char *error_cmd,
986  RofiHelperExecuteContext *context) {
987  gboolean retv = TRUE;
988  GError *error = NULL;
989 
990  GSpawnChildSetupFunc child_setup = NULL;
991  gpointer user_data = NULL;
992 
993  display_startup_notification(context, &child_setup, &user_data);
994 
995  g_spawn_async(wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data,
996  NULL, &error);
997  if (error != NULL) {
998  char *msg = g_strdup_printf("Failed to execute: '%s%s'\nError: '%s'",
999  error_precmd, error_cmd, error->message);
1000  rofi_view_error_dialog(msg, FALSE);
1001  g_free(msg);
1002  // print error.
1003  g_error_free(error);
1004  retv = FALSE;
1005  }
1006 
1007  // Free the args list.
1008  g_strfreev(args);
1009  return retv;
1010 }
1011 
1012 gboolean helper_execute_command(const char *wd, const char *cmd,
1013  gboolean run_in_term,
1014  RofiHelperExecuteContext *context) {
1015  char **args = NULL;
1016  int argc = 0;
1017 
1018  if (run_in_term) {
1019  helper_parse_setup(config.run_shell_command, &args, &argc, "{cmd}", cmd,
1020  (char *)0);
1021  } else {
1022  helper_parse_setup(config.run_command, &args, &argc, "{cmd}", cmd,
1023  (char *)0);
1024  }
1025 
1026  if (args == NULL) {
1027  return FALSE;
1028  }
1029 
1030  if (context != NULL) {
1031  if (context->name == NULL) {
1032  context->name = args[0];
1033  }
1034  if (context->binary == NULL) {
1035  context->binary = args[0];
1036  }
1037  if (context->description == NULL) {
1038  gsize l = strlen("Launching '' via rofi") + strlen(cmd) + 1;
1039  gchar *description = g_newa(gchar, l);
1040 
1041  g_snprintf(description, l, "Launching '%s' via rofi", cmd);
1042  context->description = description;
1043  }
1044  if (context->command == NULL) {
1045  context->command = cmd;
1046  }
1047  }
1048 
1049  return helper_execute(wd, args, "", cmd, context);
1050 }
1051 
1052 char *helper_get_theme_path(const char *file, const char *ext) {
1053  char *filename = rofi_expand_path(file);
1054  g_debug("Opening theme, testing: %s\n", filename);
1055  if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
1056  return filename;
1057  }
1058  g_free(filename);
1059 
1060  if (g_str_has_suffix(file, ext)) {
1061  filename = g_strdup(file);
1062  } else {
1063  filename = g_strconcat(file, ext, NULL);
1064  }
1065  // Check config's themes directory.
1066  const char *cpath = g_get_user_config_dir();
1067  if (cpath) {
1068  char *themep = g_build_filename(cpath, "rofi", "themes", filename, NULL);
1069  g_debug("Opening theme, testing: %s\n", themep);
1070  if (themep && g_file_test(themep, G_FILE_TEST_EXISTS)) {
1071  g_free(filename);
1072  return themep;
1073  }
1074  g_free(themep);
1075  }
1076  // Check config directory.
1077  if (cpath) {
1078  char *themep = g_build_filename(cpath, "rofi", filename, NULL);
1079  g_debug("Opening theme, testing: %s\n", themep);
1080  if (g_file_test(themep, G_FILE_TEST_EXISTS)) {
1081  g_free(filename);
1082  return themep;
1083  }
1084  g_free(themep);
1085  }
1086  const char *datadir = g_get_user_data_dir();
1087  if (datadir) {
1088  char *theme_path =
1089  g_build_filename(datadir, "rofi", "themes", filename, NULL);
1090  g_debug("Opening theme, testing: %s\n", theme_path);
1091  if (theme_path) {
1092  if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1093  g_free(filename);
1094  return theme_path;
1095  }
1096  g_free(theme_path);
1097  }
1098  }
1099 
1100  char *theme_path = g_build_filename(THEME_DIR, filename, NULL);
1101  if (theme_path) {
1102  g_debug("Opening theme, testing: %s\n", theme_path);
1103  if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1104  g_free(filename);
1105  return theme_path;
1106  }
1107  g_free(theme_path);
1108  }
1109  return filename;
1110 }
1111 
1112 static gboolean parse_pair(char *input, rofi_range_pair *item) {
1113  // Skip leading blanks.
1114  while (input != NULL && isblank(*input)) {
1115  ++input;
1116  }
1117 
1118  if (input == NULL) {
1119  return FALSE;
1120  }
1121 
1122  const char *sep[] = {"-", ":"};
1123  int pythonic = (strchr(input, ':') || input[0] == '-') ? 1 : 0;
1124  int index = 0;
1125 
1126  for (char *token = strsep(&input, sep[pythonic]); token != NULL;
1127  token = strsep(&input, sep[pythonic])) {
1128  if (index == 0) {
1129  item->start = item->stop = (int)strtol(token, NULL, 10);
1130  index++;
1131  continue;
1132  }
1133 
1134  if (token[0] == '\0') {
1135  item->stop = -1;
1136  continue;
1137  }
1138 
1139  item->stop = (int)strtol(token, NULL, 10);
1140  if (pythonic) {
1141  --item->stop;
1142  }
1143  }
1144  return TRUE;
1145 }
1146 void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length) {
1147  char *endp;
1148  if (input == NULL) {
1149  return;
1150  }
1151  const char *const sep = ",";
1152  for (char *token = strtok_r(input, sep, &endp); token != NULL;
1153  token = strtok_r(NULL, sep, &endp)) {
1154  // Make space.
1155  *list =
1156  g_realloc((*list), ((*length) + 1) * sizeof(struct rofi_range_pair));
1157  // Parse a single pair.
1158  if (parse_pair(token, &((*list)[*length]))) {
1159  (*length)++;
1160  }
1161  }
1162 }
1163 void rofi_output_formatted_line(const char *format, const char *string,
1164  int selected_line, const char *filter) {
1165  for (int i = 0; format && format[i]; i++) {
1166  if (format[i] == 'i') {
1167  fprintf(stdout, "%d", selected_line);
1168  } else if (format[i] == 'd') {
1169  fprintf(stdout, "%d", (selected_line + 1));
1170  } else if (format[i] == 's') {
1171  fputs(string, stdout);
1172  } else if (format[i] == 'p') {
1173  char *esc = NULL;
1174  pango_parse_markup(string, -1, 0, NULL, &esc, NULL, NULL);
1175  if (esc) {
1176  fputs(esc, stdout);
1177  g_free(esc);
1178  } else {
1179  fputs("invalid string", stdout);
1180  }
1181  } else if (format[i] == 'q') {
1182  char *quote = g_shell_quote(string);
1183  fputs(quote, stdout);
1184  g_free(quote);
1185  } else if (format[i] == 'f') {
1186  if (filter) {
1187  fputs(filter, stdout);
1188  }
1189  } else if (format[i] == 'F') {
1190  if (filter) {
1191  char *quote = g_shell_quote(filter);
1192  fputs(quote, stdout);
1193  g_free(quote);
1194  }
1195  } else {
1196  fputc(format[i], stdout);
1197  }
1198  }
1199  fputc('\n', stdout);
1200  fflush(stdout);
1201 }
1202 
1203 static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res,
1204  gpointer data) {
1205  gchar *match;
1206  // Get the match
1207  int num_match = g_match_info_get_match_count(info);
1208  // Just {text} This is inside () 5.
1209  if (num_match == 5) {
1210  match = g_match_info_fetch(info, 4);
1211  if (match != NULL) {
1212  // Lookup the match, so we can replace it.
1213  gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1214  if (r != NULL) {
1215  // Append the replacement to the string.
1216  g_string_append(res, r);
1217  }
1218  // Free match.
1219  g_free(match);
1220  }
1221  }
1222  // {} with [] guard around it.
1223  else if (num_match == 4) {
1224  match = g_match_info_fetch(info, 2);
1225  if (match != NULL) {
1226  // Lookup the match, so we can replace it.
1227  gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1228  if (r != NULL) {
1229  // Add (optional) prefix
1230  gchar *prefix = g_match_info_fetch(info, 1);
1231  g_string_append(res, prefix);
1232  g_free(prefix);
1233  // Append the replacement to the string.
1234  g_string_append(res, r);
1235  // Add (optional) postfix
1236  gchar *post = g_match_info_fetch(info, 3);
1237  g_string_append(res, post);
1238  g_free(post);
1239  }
1240  // Free match.
1241  g_free(match);
1242  }
1243  }
1244  // Else we have an invalid match.
1245  // Continue replacement.
1246  return FALSE;
1247 }
1248 
1249 char *helper_string_replace_if_exists(char *string, ...) {
1250  GHashTable *h;
1251  h = g_hash_table_new(g_str_hash, g_str_equal);
1252  va_list ap;
1253  va_start(ap, string);
1254  // Add list from variable arguments.
1255  while (1) {
1256  char *key = va_arg(ap, char *);
1257  if (key == (char *)0) {
1258  break;
1259  }
1260  char *value = va_arg(ap, char *);
1261  g_hash_table_insert(h, key, value);
1262  }
1263  char *retv = helper_string_replace_if_exists_v(string, h);
1264  va_end(ap);
1265  // Destroy key-value storage.
1266  g_hash_table_destroy(h);
1267  return retv;
1268 }
1284 char *helper_string_replace_if_exists_v(char *string, GHashTable *h) {
1285  GError *error = NULL;
1286  char *res = NULL;
1287 
1288  // Replace hits within {-\w+}.
1289  GRegex *reg = g_regex_new("\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})",
1290  G_REGEX_UNGREEDY, 0, &error);
1291  if (error == NULL) {
1292  res =
1293  g_regex_replace_eval(reg, string, -1, 0, 0, helper_eval_cb2, h, &error);
1294  }
1295  // Free regex.
1296  g_regex_unref(reg);
1297  // Throw error if shell parsing fails.
1298  if (error != NULL) {
1299  char *msg = g_strdup_printf("Failed to parse: '%s'\nError: '%s'", string,
1300  error->message);
1301  rofi_view_error_dialog(msg, FALSE);
1302  g_free(msg);
1303  // print error.
1304  g_error_free(error);
1305  g_free(res);
1306  return NULL;
1307  }
1308  return res;
1309 }
@ WL_CENTER
Definition: rofi-types.h:230
@ 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
PangoAttrList * helper_token_match_get_pango_attr(RofiHighlightColorStyle th, rofi_int_matcher **tokens, const char *input, PangoAttrList *retv)
Definition: helper.c:417
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
Definition: helper.c:584
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length)
Definition: helper.c:1146
void cmd_set_arguments(int argc, char **argv)
Definition: helper.c:70
int find_arg_char(const char *const key, char *val)
Definition: helper.c:407
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
Definition: helper.c:1012
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition: helper.c:119
char helper_parse_char(const char *arg)
Definition: helper.c:359
void rofi_output_formatted_line(const char *format, const char *string, int selected_line, const char *filter)
Definition: helper.c:1163
gboolean helper_execute(const char *wd, char **args, const char *error_precmd, const char *error_cmd, RofiHelperExecuteContext *context)
Definition: helper.c:984
unsigned int levenshtein(const char *needle, const glong needlelen, const char *haystack, const glong haystacklen)
Definition: helper.c:745
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
Definition: helper.c:781
int create_pid_file(const char *pidfile)
Definition: helper.c:538
const char ** find_arg_strv(const char *const key)
Definition: helper.c:320
int helper_parse_setup(char *string, char ***output, int *length,...)
Definition: helper.c:75
int execute_generator(const char *cmd)
Definition: helper.c:515
gchar * rofi_escape_markup(gchar *text)
Definition: helper.c:787
int find_arg_int(const char *const key, int *val)
Definition: helper.c:340
void remove_pid_file(int fd)
Definition: helper.c:576
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str, glong slen)
Definition: helper.c:902
char * rofi_expand_path(const char *input)
Definition: helper.c:713
int find_arg_str(const char *const key, char **val)
Definition: helper.c:310
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition: helper.c:262
char * helper_get_theme_path(const char *file, const char *ext)
Definition: helper.c:1052
int find_arg_uint(const char *const key, unsigned int *val)
Definition: helper.c:349
char * helper_string_replace_if_exists(char *string,...)
Definition: helper.c:1249
int find_arg(const char *const key)
Definition: helper.c:301
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition: helper.c:494
int config_sanity_check(void)
Definition: helper.c:603
char * rofi_force_utf8(const gchar *data, ssize_t length)
Definition: helper.c:796
void rofi_add_error_message(GString *str)
Definition: rofi.c:89
int rofi_view_error_dialog(const char *msg, int markup)
Definition: view.c:2018
#define CONSECUTIVE_SCORE
Definition: helper.c:843
#define GAP_SCORE
Definition: helper.c:835
#define LEADING_GAP_SCORE
Definition: helper.c:833
char ** stored_argv
Definition: helper.c:66
#define MIN3(a, b, c)
Definition: helper.c:742
#define CAMEL_SCORE
Definition: helper.c:841
const char *const monitor_position_entries[]
Definition: helper.c:60
static char * utf8_helper_simplify_string(const char *s)
Definition: helper.c:178
static enum CharClass rofi_scorer_get_character_class(gunichar c)
Definition: helper.c:868
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:973
static gchar * glob_to_regex(const char *input)
Definition: helper.c:127
#define MIN_SCORE
Definition: helper.c:831
#define PATTERN_NON_START_MULTIPLIER
Definition: helper.c:845
char * helper_string_replace_if_exists_v(char *string, GHashTable *h)
Definition: helper.c:1284
#define WORD_START_SCORE
Definition: helper.c:837
#define FUZZY_SCORER_MAX_LENGTH
Definition: helper.c:829
static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res, gpointer data)
Definition: helper.c:1203
#define PATTERN_START_MULTIPLIER
Definition: helper.c:847
static rofi_int_matcher * create_regex(const char *input, int case_sensitive)
Definition: helper.c:221
#define NON_WORD_SCORE
Definition: helper.c:839
static gchar * prefix_regex(const char *input)
Definition: helper.c:171
static GRegex * R(const char *s, int case_sensitive)
Definition: helper.c:206
CharClass
Definition: helper.c:852
@ DIGIT
Definition: helper.c:858
@ LOWER
Definition: helper.c:854
@ NON_WORD
Definition: helper.c:860
@ UPPER
Definition: helper.c:856
static gchar * fuzzy_to_regex(const char *input)
Definition: helper.c:142
int stored_argc
Definition: helper.c:64
static gboolean parse_pair(char *input, rofi_range_pair *item)
Definition: helper.c:1112
static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr)
Definition: helper.c:889
@ ROFI_HL_STRIKETHROUGH
Definition: rofi-types.h:60
@ ROFI_HL_SMALL_CAPS
Definition: rofi-types.h:62
@ ROFI_HL_ITALIC
Definition: rofi-types.h:64
@ ROFI_HL_UNDERLINE
Definition: rofi-types.h:58
@ ROFI_HL_BOLD
Definition: rofi-types.h:56
@ ROFI_HL_COLOR
Definition: rofi-types.h:66
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:298
const gchar * description
Definition: helper.h:300
const gchar * name
Definition: helper.h:296
const gchar * command
Definition: helper.h:308
RofiHighlightStyle style
Definition: rofi-types.h:214
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:173
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 * menu_font
Definition: settings.h:59
char matching_negate_char
Definition: settings.h:158
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:157
double green
Definition: rofi-types.h:155
double red
Definition: rofi-types.h:153
double alpha
Definition: rofi-types.h:159
Definition: xcb.h:94
workarea mon
Definition: view.c:111
MenuFlags flags
Definition: view.c:107
unsigned long long count
Definition: view.c:117
int monitor_active(workarea *mon)
Definition: xcb.c:937
void display_startup_notification(RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data)
Definition: xcb.c:689