29 #define G_LOG_DOMAIN "Dialogs.DRun"
44 #include <sys/types.h>
61 #define DRUN_CACHE_FILE "rofi3.druncache"
64 #define DRUN_DESKTOP_CACHE_FILE "rofi-drun-desktop.cache"
166 .enabled_match = TRUE,
167 .enabled_display = TRUE,
170 .entry_field_name =
"generic",
171 .enabled_match = TRUE,
172 .enabled_display = TRUE,
175 .entry_field_name =
"exec",
176 .enabled_match = TRUE,
177 .enabled_display = TRUE,
180 .entry_field_name =
"categories",
181 .enabled_match = TRUE,
182 .enabled_display = TRUE,
185 .entry_field_name =
"keywords",
186 .enabled_match = TRUE,
187 .enabled_display = TRUE,
190 .entry_field_name =
"comment",
191 .enabled_match = FALSE,
192 .enabled_display = FALSE,
235 match = g_match_info_fetch(info, 0);
242 g_string_append(res,
e->
path);
248 g_string_append_printf(res,
"--icon %s",
e->e->
icon_name);
260 g_string_append(res,
"%");
264 char *esc = g_shell_quote(
e->e->
path);
265 g_string_append(res, esc);
271 char *esc = g_shell_quote(
e->e->
name);
272 g_string_append(res, esc);
289 GKeyFile *kf = g_key_file_new();
290 GError *error = NULL;
291 gboolean res = g_key_file_load_from_file(kf,
e->
path, 0, &error);
295 g_warning(
"[%s] [%s] Failed to parse desktop file because: %s.",
304 if (url == NULL || strlen(url) == 0) {
305 g_warning(
"[%s] [%s] No URL found.",
e->
app_id,
e->
path);
312 gchar *command = g_newa(gchar, command_len);
316 g_debug(
"Link launch command: |%s|", command);
325 GError *error = NULL;
326 GRegex *reg = g_regex_new(
"%[a-zA-Z%]", 0, 0, &error);
328 g_warning(
"Internal error, failed to create regex: %s.", error->message);
336 g_warning(
"Internal error, failed replace field codes: %s.",
343 g_warning(
"Invalid field code in Exec line: %s.",
e->
exec);
348 g_warning(
"Nothing to execute after processing: %s.",
e->
exec);
352 g_debug(
"Parsed command: |%s| into |%s|.",
e->
exec, str);
355 GKeyFile *kf = g_key_file_new();
356 GError *key_error = NULL;
357 gboolean res = g_key_file_load_from_file(kf,
e->
path, 0, &key_error);
361 g_warning(
"[%s] [%s] Failed to parse desktop file because: %s.",
363 g_error_free(key_error);
370 const gchar *fp = g_strstrip(str);
373 if (exec_path != NULL && strlen(exec_path) == 0) {
386 gchar *wmclass = NULL;
401 g_free(drun_cach_path);
409 const char *
const *field) {
410 for (
int i = 0; categories && categories[i]; i++) {
411 for (
int j = 0; field[j]; j++) {
412 if (g_str_equal(categories[i], field[j])) {
423 const char *
path,
const gchar *basename,
424 const char *action) {
430 const ssize_t id_len = strlen(
path) - strlen(root);
432 g_strlcpy(
id, &(
path[strlen(root) + 1]), id_len);
433 for (
int index = 0; index < id_len; index++) {
434 if (
id[index] ==
'/') {
441 g_debug(
"[%s] [%s] Skipping, was previously seen.",
id,
path);
444 GKeyFile *kf = g_key_file_new();
445 GError *error = NULL;
446 gboolean res = g_key_file_load_from_file(kf,
path, 0, &error);
449 g_debug(
"[%s] [%s] Failed to parse desktop file because: %s.",
id,
path,
456 if (g_key_file_has_group(kf, action) == FALSE) {
458 g_debug(
"[%s] [%s] Invalid desktop file: No %s group",
id,
path, action);
466 g_debug(
"[%s] [%s] Invalid desktop file: No type indicated",
id,
path);
470 if (!g_strcmp0(key,
"Application")) {
472 }
else if (!g_strcmp0(key,
"Link")) {
474 }
else if (!g_strcmp0(key,
"Service")) {
476 g_debug(
"Service file detected.");
479 "[%s] [%s] Skipping desktop file: Not of type Application or Link (%s)",
489 g_debug(
"[%s] [%s] Invalid desktop file: no 'Name' key present.",
id,
path);
497 "[%s] [%s] Adding desktop file to disabled list: 'Hidden' key is true",
504 gboolean show = TRUE;
510 "OnlyShowIn", &llength, NULL);
513 for (gsize lle = 0; !show && lle < llength; lle++) {
520 if (show && g_key_file_has_key(kf,
DRUN_GROUP_NAME,
"NotShowIn", NULL)) {
523 "NotShowIn", &llength, NULL);
526 for (gsize lle = 0; show && lle < llength; lle++) {
535 g_debug(
"[%s] [%s] Adding desktop file to disabled list: "
536 "'OnlyShowIn'/'NotShowIn' keys don't match current desktop",
545 g_debug(
"[%s] [%s] Adding desktop file to disabled list: 'NoDisplay' key "
556 g_debug(
"[%s] [%s] Unsupported desktop file: no 'Exec' key present for "
564 g_debug(
"[%s] [%s] Unsupported desktop file: no 'Exec' key present for "
572 g_debug(
"[%s] [%s] Unsupported desktop file: no 'URL' key present for type "
580 char *te = g_key_file_get_string(kf,
DRUN_GROUP_NAME,
"TryExec", NULL);
581 if (!g_path_is_absolute(te)) {
582 char *fp = g_find_program_in_path(te);
590 if (g_file_test(te, G_FILE_TEST_IS_EXECUTABLE) == FALSE) {
599 char **categories = NULL;
601 categories = g_key_file_get_locale_string_list(
605 g_strfreev(categories);
631 g_strndup(basename, strlen(basename) - strlen(
".desktop"));
636 gchar *na = g_key_file_get_locale_string(kf, action,
"Name", NULL, NULL);
637 gchar *l = g_strdup_printf(
"%s - %s", n, na);
643 gchar *gn = g_key_file_get_locale_string(kf,
DRUN_GROUP_NAME,
"GenericName",
649 g_key_file_get_locale_string_list(kf,
DRUN_GROUP_NAME,
"Keywords", NULL,
668 g_strfreev(categories);
674 g_key_file_get_string(kf, action,
"Exec", NULL);
694 g_debug(
"[%s] Using file %s.",
id,
path);
698 gsize actions_length = 0;
699 char **actions = g_key_file_get_string_list(kf,
DRUN_GROUP_NAME,
"Actions",
700 &actions_length, NULL);
701 for (gsize iter = 0; iter < actions_length; iter++) {
702 char *new_action = g_strdup_printf(
"Desktop Action %s", actions[iter]);
715 const char *dirname) {
718 g_debug(
"Checking directory %s for desktop files.", dirname);
719 dir = opendir(dirname);
725 gchar *filename = NULL;
727 while ((file = readdir(dir)) != NULL) {
728 if (file->d_name[0] ==
'.') {
731 switch (file->d_type) {
736 filename = g_build_filename(dirname, file->d_name, NULL);
744 if (file->d_type == DT_LNK || file->d_type == DT_UNKNOWN) {
745 file->d_type = DT_UNKNOWN;
746 if (stat(filename, &st) == 0) {
747 if (S_ISDIR(st.st_mode)) {
748 file->d_type = DT_DIR;
749 }
else if (S_ISREG(st.st_mode)) {
750 file->d_type = DT_REG;
755 switch (file->d_type) {
758 if (g_str_has_suffix(file->d_name,
".desktop")) {
784 TICK_N(
"Start drun history");
785 unsigned int length = 0;
788 for (
unsigned int index = 0; index < length; index++) {
791 unsigned int sort_index = length - index;
792 if (G_LIKELY(sort_index < INT_MAX)) {
803 TICK_N(
"Stop drun history");
807 G_GNUC_UNUSED gpointer user_data) {
812 if (da->
name == NULL && db->
name == NULL) {
815 if (da->
name == NULL) {
818 if (db->
name == NULL) {
821 return g_utf8_collate(da->
name, db->
name);
831 #define CACHE_VERSION 2
833 size_t l = (str == NULL ? 0 : strlen(str));
834 fwrite(&l,
sizeof(l), 1, fd);
838 fwrite(str, 1, l + 1, fd);
842 fwrite(&val,
sizeof(val), 1, fd);
845 if (fread(type,
sizeof(int32_t), 1, fd) != 1) {
846 g_warning(
"Failed to read entry, cache corrupt?");
853 if (fread(&l,
sizeof(l), 1, fd) != 1) {
854 g_warning(
"Failed to read entry, cache corrupt?");
861 (*str) = g_malloc(l);
862 if (fread((*str), 1, l, fd) != l) {
863 g_warning(
"Failed to read entry, cache corrupt?");
868 guint vl = (str == NULL ? 0 : g_strv_length(str));
869 fwrite(&vl,
sizeof(vl), 1, fd);
870 for (guint index = 0; index < vl; index++) {
877 if (fread(&vl,
sizeof(vl), 1, fd) != 1) {
878 g_warning(
"Failed to read entry, cache corrupt?");
883 (*str) = g_malloc0((vl + 1) *
sizeof(**str));
884 for (guint index = 0; index < vl; index++) {
894 TICK_N(
"DRUN Write CACHE: start");
896 FILE *fd = fopen(cache_file,
"w");
898 g_warning(
"Failed to write to cache file");
902 fwrite(&version,
sizeof(version), 1, fd);
926 TICK_N(
"DRUN Write CACHE: end");
933 const char *cache_file) {
941 TICK_N(
"DRUN Read CACHE: start");
942 FILE *fd = fopen(cache_file,
"r");
944 TICK_N(
"DRUN Read CACHE: stop");
951 if (fread(&version,
sizeof(version), 1, fd) != 1) {
953 g_warning(
"Cache corrupt, ignoring.");
954 TICK_N(
"DRUN Read CACHE: stop");
960 g_warning(
"Cache file wrong version, ignoring.");
961 TICK_N(
"DRUN Read CACHE: stop");
967 g_warning(
"Cache corrupt, ignoring.");
968 TICK_N(
"DRUN Read CACHE: stop");
1000 TICK_N(
"DRUN Read CACHE: stop");
1006 TICK_N(
"Get Desktop apps (start)");
1015 dir = g_build_filename(g_get_user_data_dir(),
"applications", NULL);
1018 TICK_N(
"Get Desktop apps (user dir)");
1025 const gchar *
const *sys = g_get_system_data_dirs();
1026 for (
const gchar *
const *iter = sys; *iter != NULL; ++iter) {
1027 gboolean unique = TRUE;
1029 for (
const gchar *
const *iterd = sys; iterd != iter; ++iterd) {
1030 if (g_strcmp0(*iter, *iterd) == 0) {
1035 if (unique && (**iter) !=
'\0') {
1036 char *dir = g_build_filename(*iter,
"applications", NULL);
1041 TICK_N(
"Get Desktop apps (system dirs)");
1056 char *savept = NULL;
1059 const char *
const sep =
",#";
1065 for (
char *token = strtok_r(switcher_str, sep, &savept); token != NULL;
1066 token = strtok_r(NULL, sep, &savept)) {
1067 if (strcmp(token,
"all") == 0) {
1074 gboolean matched = FALSE;
1077 if (g_ascii_strcasecmp(token, entry_name) == 0) {
1084 g_warning(
"Invalid entry name :%s", token);
1088 g_free(switcher_str);
1096 gchar *search_term =
1101 g_free(search_term);
1111 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1114 const char *current_desktop = g_getenv(
"XDG_CURRENT_DESKTOP");
1116 current_desktop ? g_strsplit(current_desktop,
":", 0) : NULL;
1135 if (e->
icon != NULL) {
1136 cairo_surface_destroy(e->
icon);
1154 unsigned int selected_line) {
1175 selected_line, &path);
1193 g_assert_not_reached();
1196 *input[0] !=
'\0') {
1201 run_in_term ? &context : NULL)) {
1221 if (selected_line < rmpd->cmd_list_length) {
1225 GRegex *regex = g_regex_new(
"%[fFuU]", 0, 0, NULL);
1227 if (g_regex_match(regex, rmpd->
entry_list[selected_line].
exec, 0,
1241 g_regex_unref(regex);
1271 int *state, G_GNUC_UNUSED GList **list,
1277 state, list, get_entry);
1285 return g_strdup(
"Failed");
1291 char *tcats = g_strjoinv(
",", dr->
categories);
1293 cats = g_markup_escape_text(tcats, -1);
1297 gchar *keywords = NULL;
1299 char *tkeyw = g_strjoinv(
",", dr->
keywords);
1301 keywords = g_markup_escape_text(tkeyw, -1);
1313 en = g_markup_escape_text(dr->
name, -1);
1316 ec = g_markup_escape_text(dr->
comment, -1);
1321 ec,
"{exec}", dr->
exec,
"{categories}", cats,
"{keywords}", keywords,
1347 g_return_val_if_fail(pd->
entry_list != NULL, NULL);
1371 return g_strdup(dr->
name);
1373 return g_strdup_printf(
"%s", dr->
name);
1377 unsigned int index) {
1385 for (
int j = 0; match && tokens != NULL && tokens[j] != NULL; j++) {
1409 if (test == tokens[j]->invert) {
1411 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter];
1419 if (test == tokens[j]->invert) {
1421 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter];
1458 g_strdup_printf(
"File complete for: %s\n%s",
1463 return g_strdup_printf(
"File complete for: %s",
1472 .cfg_name_key =
"display-drun",
1482 ._preprocess_input = NULL,
1483 .private_data = NULL,
static ModeMode drun_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
static void read_desktop_file(DRunModePrivateData *pd, const char *root, const char *path, const gchar *basename, const char *action)
static char * _get_display_value(const Mode *sw, unsigned int selected_line, int *state, G_GNUC_UNUSED GList **list, int get_entry)
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, int height)
static void get_apps(DRunModePrivateData *pd)
static void write_cache(DRunModePrivateData *pd, const char *cache_file)
static char * drun_get_completion(const Mode *sw, unsigned int index)
static char * drun_get_message(const Mode *sw)
static void drun_mode_parse_display_format()
#define DRUN_DESKTOP_CACHE_FILE
@ DRUN_MATCH_FIELD_KEYWORDS
@ DRUN_MATCH_FIELD_COMMENT
@ DRUN_MATCH_FIELD_CATEGORIES
@ DRUN_MATCH_FIELD_GENERIC
static void drun_mode_parse_entry_fields()
@ DRUN_DESKTOP_ENTRY_TYPE_APPLICATION
@ DRUN_DESKTOP_ENTRY_TYPE_LINK
@ DRUN_DESKTOP_ENTRY_TYPE_DIRECTORY
@ DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED
@ DRUN_DESKTOP_ENTRY_TYPE_SERVICE
static gboolean rofi_strv_contains(const char *const *categories, const char *const *field)
static void drun_write_str(FILE *fd, const char *str)
static void delete_entry_history(const DRunModeEntry *entry)
static void drun_write_integer(FILE *fd, int32_t val)
static void drun_write_strv(FILE *fd, char **str)
static void get_apps_history(DRunModePrivateData *pd)
static int drun_token_match(const Mode *data, rofi_int_matcher **tokens, unsigned int index)
static void drun_entry_clear(DRunModeEntry *e)
static unsigned int drun_mode_get_num_entries(const Mode *sw)
static gint drun_int_sort_list(gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer user_data)
static gboolean drun_helper_eval_cb(const GMatchInfo *info, GString *res, gpointer data)
static gboolean drun_read_cache(DRunModePrivateData *pd, const char *cache_file)
static cairo_surface_t * fallback_icon(DRunModePrivateData *pd, int height)
static void drun_read_integer(FILE *fd, int32_t *type)
static int drun_mode_init(Mode *sw)
static void drun_read_string(FILE *fd, char **str)
static void drun_mode_destroy(Mode *sw)
static void launch_link_entry(DRunModeEntry *e)
static void exec_cmd_entry(DRunModeEntry *e, const char *path)
static void walk_dir(DRunModePrivateData *pd, const char *root, const char *dirname)
static void drun_read_stringv(FILE *fd, char ***str)
static DRunEntryField matching_entry_fields[DRUN_MATCH_NUM_FIELDS]
ModeMode file_browser_mode_completer(Mode *sw, int mretv, char **input, unsigned int selected_line, char **path)
Mode * create_new_file_browser(void)
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_set_private_data(Mode *mode, void *pd)
char * mode_get_message(const Mode *mode)
void * mode_get_private_data(const Mode *mode)
const char * entry_field_name
DRunDesktopEntryType type
char * application_fallback_icon
gboolean drun_reload_desktop_cache
unsigned int drun_show_actions
char * drun_display_format
gboolean drun_use_desktop_cache
unsigned int cmd_list_length_actual
cairo_surface_t * fallback_icon
uint32_t fallback_icon_fetch_uid
unsigned int cmd_list_length
unsigned int disabled_entries_length
char * old_completer_input
gchar ** current_desktop_list
GHashTable * disabled_entries
DRunModeEntry * entry_list
unsigned int expected_line_height
__mode_get_num_entries _get_num_entries
_mode_token_match _token_match
_mode_get_display_value _get_display_value
ThemeWidget * rofi_config_find_widget(const char *name, const char *state, gboolean exact)
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)