29#define G_LOG_DOMAIN "Modes.DRun"
61#define DRUN_CACHE_FILE "rofi3.druncache"
64#define DRUN_DESKTOP_CACHE_FILE "rofi-drun-desktop.cache"
67char *DRUN_GROUP_NAME =
"Desktop Entry";
72typedef struct _DRunModePrivateData DRunModePrivateData;
79 DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED = 0,
81 DRUN_DESKTOP_ENTRY_TYPE_APPLICATION,
83 DRUN_DESKTOP_ENTRY_TYPE_LINK,
85 DRUN_DESKTOP_ENTRY_TYPE_SERVICE,
87 DRUN_DESKTOP_ENTRY_TYPE_DIRECTORY,
88} DRunDesktopEntryType;
95 DRunModePrivateData *pd;
114 cairo_surface_t *
icon;
132 uint32_t icon_fetch_uid;
133 uint32_t icon_fetch_size;
135 DRunDesktopEntryType type;
139 const char *entry_field_name;
140 gboolean enabled_match;
141 gboolean enabled_display;
147 DRUN_MATCH_FIELD_NAME,
149 DRUN_MATCH_FIELD_GENERIC,
151 DRUN_MATCH_FIELD_EXEC,
153 DRUN_MATCH_FIELD_CATEGORIES,
155 DRUN_MATCH_FIELD_KEYWORDS,
157 DRUN_MATCH_FIELD_COMMENT,
159 DRUN_MATCH_NUM_FIELDS,
164static DRunEntryField matching_entry_fields[DRUN_MATCH_NUM_FIELDS] = {
166 .entry_field_name =
"name",
167 .enabled_match = TRUE,
168 .enabled_display = TRUE,
171 .entry_field_name =
"generic",
172 .enabled_match = TRUE,
173 .enabled_display = TRUE,
176 .entry_field_name =
"exec",
177 .enabled_match = TRUE,
178 .enabled_display = TRUE,
181 .entry_field_name =
"categories",
182 .enabled_match = TRUE,
183 .enabled_display = TRUE,
186 .entry_field_name =
"keywords",
187 .enabled_match = TRUE,
188 .enabled_display = TRUE,
191 .entry_field_name =
"comment",
192 .enabled_match = FALSE,
193 .enabled_display = FALSE,
196struct _DRunModePrivateData {
197 DRunModeEntry *entry_list;
198 unsigned int cmd_list_length;
199 unsigned int cmd_list_length_actual;
201 GHashTable *disabled_entries;
202 unsigned int disabled_entries_length;
203 unsigned int expected_line_height;
205 char **show_categories;
208 const gchar *icon_theme;
210 gchar **current_desktop_list;
212 gboolean file_complete;
214 char *old_completer_input;
215 uint32_t selected_line;
225static gboolean drun_helper_eval_cb(
const GMatchInfo *info, GString *res,
228 struct RegexEvalArg *e = (
struct RegexEvalArg *)data;
232 match = g_match_info_fetch(info, 0);
239 g_string_append(res, e->path);
244 if (e->e && e->e->icon) {
245 g_string_append_printf(res,
"--icon %s", e->e->icon_name);
257 g_string_append(res,
"%");
261 char *esc = g_shell_quote(e->e->path);
262 g_string_append(res, esc);
268 char *esc = g_shell_quote(e->e->name);
269 g_string_append(res, esc);
284static void launch_link_entry(DRunModeEntry *e) {
285 if (e->key_file == NULL) {
286 GKeyFile *kf = g_key_file_new();
287 GError *error = NULL;
288 gboolean res = g_key_file_load_from_file(kf, e->path, 0, &error);
292 g_warning(
"[%s] [%s] Failed to parse desktop file because: %s.",
293 e->app_id, e->path, error->message);
300 gchar *url = g_key_file_get_string(e->key_file, e->action,
"URL", NULL);
301 if (url == NULL || strlen(url) == 0) {
302 g_warning(
"[%s] [%s] No URL found.", e->app_id, e->path);
309 gchar *command = g_newa(gchar, command_len);
313 g_debug(
"Link launch command: |%s|", command);
315 char *path = g_build_filename(
cache_dir, DRUN_CACHE_FILE, NULL);
321static void exec_cmd_entry(DRunModeEntry *e,
const char *path) {
322 GError *error = NULL;
323 GRegex *reg = g_regex_new(
"%[a-zA-Z%]", 0, 0, &error);
325 g_warning(
"Internal error, failed to create regex: %s.", error->message);
329 struct RegexEvalArg earg = {.e = e, .path = path, .success = TRUE};
330 char *str = g_regex_replace_eval(reg, e->exec, -1, 0, 0, drun_helper_eval_cb,
333 g_warning(
"Internal error, failed replace field codes: %s.",
339 if (earg.success == FALSE) {
340 g_warning(
"Invalid field code in Exec line: %s.", e->exec);
345 g_warning(
"Nothing to execute after processing: %s.", e->exec);
349 g_debug(
"Parsed command: |%s| into |%s|.", e->exec, str);
351 if (e->key_file == NULL) {
352 GKeyFile *kf = g_key_file_new();
353 GError *key_error = NULL;
354 gboolean res = g_key_file_load_from_file(kf, e->path, 0, &key_error);
358 g_warning(
"[%s] [%s] Failed to parse desktop file because: %s.",
359 e->app_id, e->path, key_error->message);
360 g_error_free(key_error);
367 const gchar *fp = g_strstrip(str);
369 g_key_file_get_string(e->key_file, e->action,
"Path", NULL);
370 if (exec_path != NULL && strlen(exec_path) == 0) {
378 .icon = e->icon_name,
382 g_key_file_get_boolean(e->key_file, e->action,
"StartupNotify", NULL);
383 gchar *wmclass = NULL;
385 g_key_file_has_key(e->key_file, e->action,
"StartupWMClass", NULL)) {
387 g_key_file_get_string(e->key_file, e->action,
"StartupWMClass", NULL);
393 g_key_file_get_boolean(e->key_file, e->action,
"Terminal", NULL);
395 char *drun_cach_path = g_build_filename(
cache_dir, DRUN_CACHE_FILE, NULL);
398 g_free(drun_cach_path);
405static gboolean rofi_strv_contains(
const char *
const *categories,
406 const char *
const *field) {
407 for (
int i = 0; categories && categories[i]; i++) {
408 for (
int j = 0; field[j]; j++) {
409 if (g_str_equal(categories[i], field[j])) {
419static void read_desktop_file(DRunModePrivateData *pd,
const char *root,
420 const char *path,
const gchar *basename,
421 const char *action) {
422 DRunDesktopEntryType desktop_entry_type =
423 DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED;
427 const ssize_t id_len = strlen(path) - strlen(root);
429 g_strlcpy(
id, &(path[strlen(root) + 1]), id_len);
430 for (
int index = 0; index < id_len; index++) {
431 if (
id[index] ==
'/') {
437 if (g_hash_table_contains(pd->disabled_entries,
id) && !parse_action) {
438 g_debug(
"[%s] [%s] Skipping, was previously seen.",
id, path);
441 GKeyFile *kf = g_key_file_new();
442 GError *error = NULL;
443 gboolean res = g_key_file_load_from_file(kf, path, 0, &error);
446 g_debug(
"[%s] [%s] Failed to parse desktop file because: %s.",
id, path,
453 if (g_key_file_has_group(kf, action) == FALSE) {
455 g_debug(
"[%s] [%s] Invalid desktop file: No %s group",
id, path, action);
460 gchar *key = g_key_file_get_string(kf, DRUN_GROUP_NAME,
"Type", NULL);
463 g_debug(
"[%s] [%s] Invalid desktop file: No type indicated",
id, path);
467 if (!g_strcmp0(key,
"Application")) {
468 desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_APPLICATION;
469 }
else if (!g_strcmp0(key,
"Link")) {
470 desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_LINK;
471 }
else if (!g_strcmp0(key,
"Service")) {
472 desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_SERVICE;
473 g_debug(
"Service file detected.");
476 "[%s] [%s] Skipping desktop file: Not of type Application or Link (%s)",
485 if (!g_key_file_has_key(kf, DRUN_GROUP_NAME,
"Name", NULL)) {
486 g_debug(
"[%s] [%s] Invalid desktop file: no 'Name' key present.",
id, path);
492 if (g_key_file_get_boolean(kf, DRUN_GROUP_NAME,
"Hidden", NULL)) {
494 "[%s] [%s] Adding desktop file to disabled list: 'Hidden' key is true",
497 g_hash_table_add(pd->disabled_entries, g_strdup(
id));
500 if (pd->current_desktop_list) {
501 gboolean show = TRUE;
503 if (g_key_file_has_key(kf, DRUN_GROUP_NAME,
"OnlyShowIn", NULL)) {
506 gchar **list = g_key_file_get_string_list(kf, DRUN_GROUP_NAME,
507 "OnlyShowIn", &llength, NULL);
509 for (gsize lcd = 0; !show && pd->current_desktop_list[lcd]; lcd++) {
510 for (gsize lle = 0; !show && lle < llength; lle++) {
511 show = (g_strcmp0(pd->current_desktop_list[lcd], list[lle]) == 0);
517 if (show && g_key_file_has_key(kf, DRUN_GROUP_NAME,
"NotShowIn", NULL)) {
519 gchar **list = g_key_file_get_string_list(kf, DRUN_GROUP_NAME,
520 "NotShowIn", &llength, NULL);
522 for (gsize lcd = 0; show && pd->current_desktop_list[lcd]; lcd++) {
523 for (gsize lle = 0; show && lle < llength; lle++) {
524 show = !(g_strcmp0(pd->current_desktop_list[lcd], list[lle]) == 0);
532 g_debug(
"[%s] [%s] Adding desktop file to disabled list: "
533 "'OnlyShowIn'/'NotShowIn' keys don't match current desktop",
536 g_hash_table_add(pd->disabled_entries, g_strdup(
id));
541 if (g_key_file_get_boolean(kf, DRUN_GROUP_NAME,
"NoDisplay", NULL)) {
542 g_debug(
"[%s] [%s] Adding desktop file to disabled list: 'NoDisplay' key "
546 g_hash_table_add(pd->disabled_entries, g_strdup(
id));
551 if (desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION &&
552 !g_key_file_has_key(kf, DRUN_GROUP_NAME,
"Exec", NULL)) {
553 g_debug(
"[%s] [%s] Unsupported desktop file: no 'Exec' key present for "
559 if (desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_SERVICE &&
560 !g_key_file_has_key(kf, DRUN_GROUP_NAME,
"Exec", NULL)) {
561 g_debug(
"[%s] [%s] Unsupported desktop file: no 'Exec' key present for "
567 if (desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_LINK &&
568 !g_key_file_has_key(kf, DRUN_GROUP_NAME,
"URL", NULL)) {
569 g_debug(
"[%s] [%s] Unsupported desktop file: no 'URL' key present for type "
576 if (g_key_file_has_key(kf, DRUN_GROUP_NAME,
"TryExec", NULL)) {
577 char *te = g_key_file_get_string(kf, DRUN_GROUP_NAME,
"TryExec", NULL);
578 if (!g_path_is_absolute(te)) {
579 char *fp = g_find_program_in_path(te);
587 if (g_file_test(te, G_FILE_TEST_IS_EXECUTABLE) == FALSE) {
596 char **categories = NULL;
597 if (pd->show_categories) {
598 categories = g_key_file_get_locale_string_list(
599 kf, DRUN_GROUP_NAME,
"Categories", NULL, NULL, NULL);
600 if (!rofi_strv_contains((
const char *
const *)categories,
601 (
const char *
const *)pd->show_categories)) {
602 g_strfreev(categories);
608 size_t nl = ((pd->cmd_list_length) + 1);
609 if (nl >= pd->cmd_list_length_actual) {
610 pd->cmd_list_length_actual += 256;
611 pd->entry_list = g_realloc(pd->entry_list, pd->cmd_list_length_actual *
612 sizeof(*(pd->entry_list)));
616 if (G_UNLIKELY(pd->cmd_list_length > INT_MAX)) {
618 pd->entry_list[pd->cmd_list_length].sort_index = INT_MIN;
620 pd->entry_list[pd->cmd_list_length].sort_index = -nl;
622 pd->entry_list[pd->cmd_list_length].icon_size = 0;
623 pd->entry_list[pd->cmd_list_length].icon_fetch_uid = 0;
624 pd->entry_list[pd->cmd_list_length].icon_fetch_size = 0;
625 pd->entry_list[pd->cmd_list_length].root = g_strdup(root);
626 pd->entry_list[pd->cmd_list_length].path = g_strdup(path);
627 pd->entry_list[pd->cmd_list_length].desktop_id = g_strdup(
id);
628 pd->entry_list[pd->cmd_list_length].app_id =
629 g_strndup(basename, strlen(basename) - strlen(
".desktop"));
631 g_key_file_get_locale_string(kf, DRUN_GROUP_NAME,
"Name", NULL, NULL);
633 if (action != DRUN_GROUP_NAME) {
634 gchar *na = g_key_file_get_locale_string(kf, action,
"Name", NULL, NULL);
635 gchar *l = g_strdup_printf(
"%s - %s", n, na);
639 pd->entry_list[pd->cmd_list_length].name = n;
640 pd->entry_list[pd->cmd_list_length].action = DRUN_GROUP_NAME;
641 gchar *gn = g_key_file_get_locale_string(kf, DRUN_GROUP_NAME,
"GenericName",
643 pd->entry_list[pd->cmd_list_length].generic_name = gn;
644 if (matching_entry_fields[DRUN_MATCH_FIELD_KEYWORDS].enabled_match ||
645 matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled_display) {
646 pd->entry_list[pd->cmd_list_length].keywords =
647 g_key_file_get_locale_string_list(kf, DRUN_GROUP_NAME,
"Keywords", NULL,
650 pd->entry_list[pd->cmd_list_length].keywords = NULL;
653 if (matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled_match ||
654 matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled_display) {
656 pd->entry_list[pd->cmd_list_length].categories = categories;
659 pd->entry_list[pd->cmd_list_length].categories =
660 g_key_file_get_locale_string_list(kf, DRUN_GROUP_NAME,
"Categories",
664 pd->entry_list[pd->cmd_list_length].categories = NULL;
666 g_strfreev(categories);
668 pd->entry_list[pd->cmd_list_length].type = desktop_entry_type;
669 if (desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION ||
670 desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_SERVICE) {
671 pd->entry_list[pd->cmd_list_length].exec =
672 g_key_file_get_string(kf, action,
"Exec", NULL);
674 pd->entry_list[pd->cmd_list_length].exec = NULL;
677 if (matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled_match ||
678 matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled_display) {
679 pd->entry_list[pd->cmd_list_length].comment = g_key_file_get_locale_string(
680 kf, DRUN_GROUP_NAME,
"Comment", NULL, NULL);
682 pd->entry_list[pd->cmd_list_length].comment = NULL;
684 pd->entry_list[pd->cmd_list_length].icon_name =
685 g_key_file_get_locale_string(kf, DRUN_GROUP_NAME,
"Icon", NULL, NULL);
686 pd->entry_list[pd->cmd_list_length].icon = NULL;
689 pd->entry_list[pd->cmd_list_length].key_file = kf;
691 g_hash_table_add(pd->disabled_entries, g_strdup(
id));
692 g_debug(
"[%s] Using file %s.",
id, path);
693 (pd->cmd_list_length)++;
696 gsize actions_length = 0;
697 char **actions = g_key_file_get_string_list(kf, DRUN_GROUP_NAME,
"Actions",
698 &actions_length, NULL);
699 for (gsize iter = 0; iter < actions_length; iter++) {
700 char *new_action = g_strdup_printf(
"Desktop Action %s", actions[iter]);
701 read_desktop_file(pd, root, path, basename, new_action);
712static void walk_dir(DRunModePrivateData *pd,
const char *root,
713 const char *dirname) {
716 g_debug(
"Checking directory %s for desktop files.", dirname);
717 dir = opendir(dirname);
723 gchar *filename = NULL;
725 while ((file = readdir(dir)) != NULL) {
726 if (file->d_name[0] ==
'.') {
729 switch (file->d_type) {
734 filename = g_build_filename(dirname, file->d_name, NULL);
742 if (file->d_type == DT_LNK || file->d_type == DT_UNKNOWN) {
743 file->d_type = DT_UNKNOWN;
744 if (stat(filename, &st) == 0) {
745 if (S_ISDIR(st.st_mode)) {
746 file->d_type = DT_DIR;
747 }
else if (S_ISREG(st.st_mode)) {
748 file->d_type = DT_REG;
753 switch (file->d_type) {
756 if (g_str_has_suffix(file->d_name,
".desktop")) {
757 read_desktop_file(pd, root, filename, file->d_name, DRUN_GROUP_NAME);
761 walk_dir(pd, root, filename);
775static void delete_entry_history(
const DRunModeEntry *entry) {
776 char *path = g_build_filename(
cache_dir, DRUN_CACHE_FILE, NULL);
781static void get_apps_history(DRunModePrivateData *pd) {
782 TICK_N(
"Start drun history");
783 unsigned int length = 0;
784 gchar *path = g_build_filename(
cache_dir, DRUN_CACHE_FILE, NULL);
786 for (
unsigned int index = 0; index < length; index++) {
787 for (
size_t i = 0; i < pd->cmd_list_length; i++) {
788 if (g_strcmp0(pd->entry_list[i].desktop_id, retv[index]) == 0) {
789 unsigned int sort_index = length - index;
790 if (G_LIKELY(sort_index < INT_MAX)) {
791 pd->entry_list[i].sort_index = sort_index;
794 pd->entry_list[i].sort_index = INT_MAX;
801 TICK_N(
"Stop drun history");
804static gint drun_int_sort_list(gconstpointer a, gconstpointer b,
805 G_GNUC_UNUSED gpointer user_data) {
806 DRunModeEntry *da = (DRunModeEntry *)a;
807 DRunModeEntry *db = (DRunModeEntry *)b;
809 if (da->sort_index < 0 && db->sort_index < 0) {
810 if (da->name == NULL && db->name == NULL) {
813 if (da->name == NULL) {
816 if (db->name == NULL) {
819 return g_utf8_collate(da->name, db->name);
821 return db->sort_index - da->sort_index;
829#define CACHE_VERSION 2
830static void drun_write_str(FILE *fd,
const char *str) {
831 size_t l = (str == NULL ? 0 : strlen(str));
832 fwrite(&l,
sizeof(l), 1, fd);
836 fwrite(str, 1, l + 1, fd);
839static void drun_write_integer(FILE *fd, int32_t val) {
840 fwrite(&val,
sizeof(val), 1, fd);
842static void drun_read_integer(FILE *fd, int32_t *type) {
843 if (fread(type,
sizeof(int32_t), 1, fd) != 1) {
844 g_warning(
"Failed to read entry, cache corrupt?");
848static void drun_read_string(FILE *fd,
char **str) {
851 if (fread(&l,
sizeof(l), 1, fd) != 1) {
852 g_warning(
"Failed to read entry, cache corrupt?");
859 (*str) = g_malloc(l);
860 if (fread((*str), 1, l, fd) != l) {
861 g_warning(
"Failed to read entry, cache corrupt?");
865static void drun_write_strv(FILE *fd,
char **str) {
866 guint vl = (str == NULL ? 0 : g_strv_length(str));
867 fwrite(&vl,
sizeof(vl), 1, fd);
868 for (guint index = 0; index < vl; index++) {
869 drun_write_str(fd, str[index]);
872static void drun_read_stringv(FILE *fd,
char ***str) {
875 if (fread(&vl,
sizeof(vl), 1, fd) != 1) {
876 g_warning(
"Failed to read entry, cache corrupt?");
881 (*str) = g_malloc0((vl + 1) *
sizeof(**str));
882 for (guint index = 0; index < vl; index++) {
883 drun_read_string(fd, &((*str)[index]));
888static void write_cache(DRunModePrivateData *pd,
const char *cache_file) {
892 TICK_N(
"DRUN Write CACHE: start");
894 FILE *fd = fopen(cache_file,
"w");
896 g_warning(
"Failed to write to cache file");
899 uint8_t version = CACHE_VERSION;
900 fwrite(&version,
sizeof(version), 1, fd);
902 fwrite(&(pd->cmd_list_length),
sizeof(pd->cmd_list_length), 1, fd);
903 for (
unsigned int index = 0; index < pd->cmd_list_length; index++) {
904 DRunModeEntry *entry = &(pd->entry_list[index]);
906 drun_write_str(fd, entry->action);
907 drun_write_str(fd, entry->root);
908 drun_write_str(fd, entry->path);
909 drun_write_str(fd, entry->app_id);
910 drun_write_str(fd, entry->desktop_id);
911 drun_write_str(fd, entry->icon_name);
912 drun_write_str(fd, entry->exec);
913 drun_write_str(fd, entry->name);
914 drun_write_str(fd, entry->generic_name);
916 drun_write_strv(fd, entry->categories);
917 drun_write_strv(fd, entry->keywords);
919 drun_write_str(fd, entry->comment);
920 drun_write_integer(fd, (int32_t)entry->type);
924 TICK_N(
"DRUN Write CACHE: end");
930static gboolean drun_read_cache(DRunModePrivateData *pd,
931 const char *cache_file) {
939 TICK_N(
"DRUN Read CACHE: start");
940 FILE *fd = fopen(cache_file,
"r");
942 TICK_N(
"DRUN Read CACHE: stop");
949 if (fread(&version,
sizeof(version), 1, fd) != 1) {
951 g_warning(
"Cache corrupt, ignoring.");
952 TICK_N(
"DRUN Read CACHE: stop");
956 if (version != CACHE_VERSION) {
958 g_warning(
"Cache file wrong version, ignoring.");
959 TICK_N(
"DRUN Read CACHE: stop");
963 if (fread(&(pd->cmd_list_length),
sizeof(pd->cmd_list_length), 1, fd) != 1) {
965 g_warning(
"Cache corrupt, ignoring.");
966 TICK_N(
"DRUN Read CACHE: stop");
970 pd->cmd_list_length_actual = pd->cmd_list_length;
973 g_malloc0(pd->cmd_list_length_actual *
sizeof(*(pd->entry_list)));
975 for (
unsigned int index = 0; index < pd->cmd_list_length; index++) {
976 DRunModeEntry *entry = &(pd->entry_list[index]);
978 drun_read_string(fd, &(entry->action));
979 drun_read_string(fd, &(entry->root));
980 drun_read_string(fd, &(entry->path));
981 drun_read_string(fd, &(entry->app_id));
982 drun_read_string(fd, &(entry->desktop_id));
983 drun_read_string(fd, &(entry->icon_name));
984 drun_read_string(fd, &(entry->exec));
985 drun_read_string(fd, &(entry->name));
986 drun_read_string(fd, &(entry->generic_name));
988 drun_read_stringv(fd, &(entry->categories));
989 drun_read_stringv(fd, &(entry->keywords));
991 drun_read_string(fd, &(entry->comment));
993 drun_read_integer(fd, &(type));
998 TICK_N(
"DRUN Read CACHE: stop");
1002static void get_apps(DRunModePrivateData *pd) {
1003 char *cache_file = g_build_filename(
cache_dir, DRUN_DESKTOP_CACHE_FILE, NULL);
1004 TICK_N(
"Get Desktop apps (start)");
1005 if (drun_read_cache(pd, cache_file)) {
1013 dir = g_build_filename(g_get_user_data_dir(),
"applications", NULL);
1014 walk_dir(pd, dir, dir);
1016 TICK_N(
"Get Desktop apps (user dir)");
1023 const gchar *
const *sys = g_get_system_data_dirs();
1024 for (
const gchar *
const *iter = sys; *iter != NULL; ++iter) {
1025 gboolean unique = TRUE;
1027 for (
const gchar *
const *iterd = sys; iterd != iter; ++iterd) {
1028 if (g_strcmp0(*iter, *iterd) == 0) {
1033 if (unique && (**iter) !=
'\0') {
1034 char *dir = g_build_filename(*iter,
"applications", NULL);
1035 walk_dir(pd, dir, dir);
1039 TICK_N(
"Get Desktop apps (system dirs)");
1041 get_apps_history(pd);
1043 g_qsort_with_data(pd->entry_list, pd->cmd_list_length,
1044 sizeof(DRunModeEntry), drun_int_sort_list, NULL);
1048 write_cache(pd, cache_file);
1053static void drun_mode_parse_entry_fields() {
1054 char *savept = NULL;
1057 const char *
const sep =
",#";
1059 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++) {
1060 matching_entry_fields[i].enabled_match = FALSE;
1061 matching_entry_fields[i].enabled_display = FALSE;
1063 for (
char *token = strtok_r(switcher_str, sep, &savept); token != NULL;
1064 token = strtok_r(NULL, sep, &savept)) {
1065 if (strcmp(token,
"all") == 0) {
1066 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++) {
1067 matching_entry_fields[i].enabled_match = TRUE;
1068 matching_entry_fields[i].enabled_display = TRUE;
1072 gboolean matched = FALSE;
1073 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++) {
1074 const char *entry_name = matching_entry_fields[i].entry_field_name;
1075 if (g_ascii_strcasecmp(token, entry_name) == 0) {
1076 matching_entry_fields[i].enabled_match = TRUE;
1077 matching_entry_fields[i].enabled_display = TRUE;
1082 g_warning(
"Invalid entry name :%s", token);
1086 g_free(switcher_str);
1089static void drun_mode_parse_display_format() {
1090 for (
int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++) {
1091 if (matching_entry_fields[i].enabled_display)
1094 gchar *search_term =
1095 g_strdup_printf(
"{%s}", matching_entry_fields[i].entry_field_name);
1097 matching_entry_fields[i].enabled_match = TRUE;
1099 g_free(search_term);
1103static int drun_mode_init(
Mode *sw) {
1107 DRunModePrivateData *pd = g_malloc0(
sizeof(*pd));
1108 pd->disabled_entries =
1109 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1112 const char *current_desktop = g_getenv(
"XDG_CURRENT_DESKTOP");
1113 pd->current_desktop_list =
1114 current_desktop ? g_strsplit(current_desktop,
":", 0) : NULL;
1120 drun_mode_parse_entry_fields();
1121 drun_mode_parse_display_format();
1124 pd->completer = NULL;
1127static void drun_entry_clear(DRunModeEntry *e) {
1131 g_free(e->desktop_id);
1132 if (e->icon != NULL) {
1133 cairo_surface_destroy(e->icon);
1135 g_free(e->icon_name);
1138 g_free(e->generic_name);
1140 if (e->action != DRUN_GROUP_NAME) {
1143 g_strfreev(e->categories);
1144 g_strfreev(e->keywords);
1146 g_key_file_free(e->key_file);
1150static ModeMode drun_mode_result(
Mode *sw,
int mretv,
char **input,
1151 unsigned int selected_line) {
1155 if (rmpd->file_complete == TRUE) {
1160 g_free(rmpd->old_completer_input);
1161 rmpd->old_completer_input = *input;
1163 if (rmpd->selected_line < rmpd->cmd_list_length) {
1164 (*input) = g_strdup(rmpd->old_input);
1166 rmpd->file_complete = FALSE;
1172 selected_line, &path);
1174 exec_cmd_entry(&(rmpd->entry_list[rmpd->selected_line]), path);
1181 switch (rmpd->entry_list[selected_line].type) {
1182 case DRUN_DESKTOP_ENTRY_TYPE_SERVICE:
1183 case DRUN_DESKTOP_ENTRY_TYPE_APPLICATION:
1184 exec_cmd_entry(&(rmpd->entry_list[selected_line]), NULL);
1186 case DRUN_DESKTOP_ENTRY_TYPE_LINK:
1187 launch_link_entry(&(rmpd->entry_list[selected_line]));
1190 g_assert_not_reached();
1193 *input[0] !=
'\0') {
1198 run_in_term ? &context : NULL)) {
1202 selected_line < rmpd->cmd_list_length) {
1204 if (rmpd->entry_list[selected_line].sort_index >= 0) {
1205 delete_entry_history(&(rmpd->entry_list[selected_line]));
1206 drun_entry_clear(&(rmpd->entry_list[selected_line]));
1207 memmove(&(rmpd->entry_list[selected_line]),
1208 &rmpd->entry_list[selected_line + 1],
1209 sizeof(DRunModeEntry) *
1210 (rmpd->cmd_list_length - selected_line - 1));
1211 rmpd->cmd_list_length--;
1218 if (selected_line < rmpd->cmd_list_length) {
1219 switch (rmpd->entry_list[selected_line].type) {
1220 case DRUN_DESKTOP_ENTRY_TYPE_SERVICE:
1221 case DRUN_DESKTOP_ENTRY_TYPE_APPLICATION: {
1222 GRegex *regex = g_regex_new(
"%[fFuU]", 0, 0, NULL);
1224 if (g_regex_match(regex, rmpd->entry_list[selected_line].exec, 0,
1226 rmpd->selected_line = selected_line;
1229 g_free(rmpd->old_input);
1230 rmpd->old_input = g_strdup(*input);
1234 *input = g_strdup(rmpd->old_completer_input);
1238 rmpd->file_complete = TRUE;
1240 g_regex_unref(regex);
1249static void drun_mode_destroy(
Mode *sw) {
1252 for (
size_t i = 0; i < rmpd->cmd_list_length; i++) {
1253 drun_entry_clear(&(rmpd->entry_list[i]));
1255 g_hash_table_destroy(rmpd->disabled_entries);
1256 g_free(rmpd->entry_list);
1258 g_free(rmpd->old_completer_input);
1259 g_free(rmpd->old_input);
1260 if (rmpd->completer != NULL) {
1262 g_free(rmpd->completer);
1265 g_strfreev(rmpd->current_desktop_list);
1266 g_strfreev(rmpd->show_categories);
1273 int *state, G_GNUC_UNUSED GList **list,
1277 if (pd->file_complete) {
1278 return pd->completer->_get_display_value(pd->completer, selected_line,
1279 state, list, get_entry);
1285 if (pd->entry_list == NULL) {
1287 return g_strdup(
"Failed");
1290 DRunModeEntry *dr = &(pd->entry_list[selected_line]);
1292 if (dr->categories) {
1293 char *tcats = g_strjoinv(
",", dr->categories);
1295 cats = g_markup_escape_text(tcats, -1);
1299 gchar *keywords = NULL;
1301 char *tkeyw = g_strjoinv(
",", dr->keywords);
1303 keywords = g_markup_escape_text(tkeyw, -1);
1311 if (dr->generic_name) {
1312 egn = g_markup_escape_text(dr->generic_name, -1);
1315 en = g_markup_escape_text(dr->name, -1);
1318 ec = g_markup_escape_text(dr->comment, -1);
1323 ec,
"{exec}", dr->exec,
"{categories}", cats,
"{keywords}", keywords,
1332static cairo_surface_t *
_get_icon(
const Mode *sw,
unsigned int selected_line,
1333 unsigned int height) {
1335 if (pd->file_complete) {
1336 return pd->completer->_get_icon(pd->completer, selected_line, height);
1338 g_return_val_if_fail(pd->entry_list != NULL, NULL);
1339 DRunModeEntry *dr = &(pd->entry_list[selected_line]);
1340 if (dr->icon_name != NULL) {
1341 if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) {
1346 dr->icon_fetch_size = height;
1353static char *drun_get_completion(
const Mode *sw,
unsigned int index) {
1356 DRunModeEntry *dr = &(pd->entry_list[index]);
1357 if (dr->generic_name == NULL) {
1358 return g_strdup(dr->name);
1360 return g_strdup_printf(
"%s", dr->name);
1364 unsigned int index) {
1365 DRunModePrivateData *rmpd =
1367 if (rmpd->file_complete) {
1368 return rmpd->completer->_token_match(rmpd->completer, tokens, index);
1372 for (
int j = 0; match && tokens[j] != NULL; j++) {
1376 if (matching_entry_fields[DRUN_MATCH_FIELD_NAME].enabled_match) {
1377 if (rmpd->entry_list[index].name) {
1381 if (matching_entry_fields[DRUN_MATCH_FIELD_GENERIC].enabled_match) {
1383 if (test == tokens[j]->invert && rmpd->entry_list[index].generic_name) {
1388 if (matching_entry_fields[DRUN_MATCH_FIELD_EXEC].enabled_match) {
1390 if (test == tokens[j]->invert && rmpd->entry_list[index].exec) {
1394 if (matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled_match) {
1396 if (test == tokens[j]->invert) {
1397 gchar **list = rmpd->entry_list[index].categories;
1398 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter];
1404 if (matching_entry_fields[DRUN_MATCH_FIELD_KEYWORDS].enabled_match) {
1406 if (test == tokens[j]->invert) {
1407 gchar **list = rmpd->entry_list[index].keywords;
1408 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter];
1414 if (matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled_match) {
1417 if (test == tokens[j]->invert && rmpd->entry_list[index].comment) {
1430static unsigned int drun_mode_get_num_entries(
const Mode *sw) {
1431 const DRunModePrivateData *pd =
1433 if (pd->file_complete) {
1434 return pd->completer->_get_num_entries(pd->completer);
1436 return pd->cmd_list_length;
1438static char *drun_get_message(
const Mode *sw) {
1440 if (pd->file_complete) {
1441 if (pd->selected_line < pd->cmd_list_length) {
1445 g_strdup_printf(
"File complete for: %s\n%s",
1446 pd->entry_list[pd->selected_line].name, msg);
1450 return g_strdup_printf(
"File complete for: %s",
1451 pd->entry_list[pd->selected_line].name);
1459 .cfg_name_key =
"display-drun",
1460 ._init = drun_mode_init,
1461 ._get_num_entries = drun_mode_get_num_entries,
1462 ._result = drun_mode_result,
1463 ._destroy = drun_mode_destroy,
1464 ._token_match = drun_token_match,
1465 ._get_message = drun_get_message,
1466 ._get_completion = drun_get_completion,
1469 ._preprocess_input = NULL,
1470 .private_data = NULL,
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, unsigned int height)
static char * _get_display_value(const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry)
const char * icon_name[NUM_FILE_TYPES]
Mode * create_new_file_browser(void)
ModeMode file_browser_mode_completer(Mode *sw, int mretv, char **input, unsigned int selected_line, char **path)
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
char * helper_string_replace_if_exists(char *string,...)
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
void history_set(const char *filename, const char *entry)
void history_remove(const char *filename, const char *entry)
char ** history_get_list(const char *filename, unsigned int *length)
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)
int mode_init(Mode *mode)
void * mode_get_private_data(const Mode *mode)
char * mode_get_message(const Mode *mode)
void mode_set_private_data(Mode *mode, void *pd)
static void get_apps(KeysHelpModePrivateData *pd)
gboolean drun_reload_desktop_cache
unsigned int drun_show_actions
char * drun_display_format
gboolean drun_use_desktop_cache
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
ThemeWidget * rofi_config_find_widget(const char *name, const char *state, gboolean exact)