29 #define G_LOG_DOMAIN "Dialogs.DRun"
40 #include <sys/types.h>
58 #define DRUN_CACHE_FILE "rofi3.druncache"
59 #define DRUN_DESKTOP_CACHE_FILE "rofi-drun-desktop.cache"
61 char *DRUN_GROUP_NAME =
"Desktop Entry";
63 typedef struct _DRunModePrivateData DRunModePrivateData;
67 DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED = 0,
68 DRUN_DESKTOP_ENTRY_TYPE_APPLICATION,
69 DRUN_DESKTOP_ENTRY_TYPE_LINK,
70 DRUN_DESKTOP_ENTRY_TYPE_DIRECTORY,
71 } DRunDesktopEntryType;
79 DRunModePrivateData *pd;
97 cairo_surface_t *
icon;
115 uint32_t icon_fetch_uid;
117 DRunDesktopEntryType type;
122 const char *entry_field_name;
128 DRUN_MATCH_FIELD_NAME,
129 DRUN_MATCH_FIELD_GENERIC,
130 DRUN_MATCH_FIELD_EXEC,
131 DRUN_MATCH_FIELD_CATEGORIES,
132 DRUN_MATCH_FIELD_KEYWORDS,
133 DRUN_MATCH_FIELD_COMMENT,
134 DRUN_MATCH_NUM_FIELDS,
135 } DRunMatchingFields;
137 static DRunEntryField matching_entry_fields[DRUN_MATCH_NUM_FIELDS] = {
138 { .entry_field_name =
"name", .enabled = TRUE, },
139 { .entry_field_name =
"generic", .enabled = TRUE, },
140 { .entry_field_name =
"exec", .enabled = TRUE, },
141 { .entry_field_name =
"categories", .enabled = TRUE, },
142 { .entry_field_name =
"keywords", .enabled = TRUE, },
143 { .entry_field_name =
"comment", .enabled = FALSE, }
146 struct _DRunModePrivateData
148 DRunModeEntry *entry_list;
149 unsigned int cmd_list_length;
150 unsigned int cmd_list_length_actual;
152 GHashTable *disabled_entries;
153 unsigned int disabled_entries_length;
154 unsigned int expected_line_height;
156 char **show_categories;
159 const gchar *icon_theme;
161 gchar **current_desktop_list;
170 static gboolean drun_helper_eval_cb (
const GMatchInfo *info, GString *res, gpointer data )
173 struct RegexEvalArg *e = (
struct RegexEvalArg *) data;
177 match = g_match_info_fetch ( info, 0 );
178 if ( match != NULL ) {
196 g_string_append ( res,
"%" );
200 char *esc = g_shell_quote ( e->e->path );
201 g_string_append ( res, esc );
207 char *esc = g_shell_quote ( e->e->name );
208 g_string_append ( res, esc );
223 static void launch_link_entry ( DRunModeEntry *e )
225 if ( e->key_file == NULL ) {
226 GKeyFile *kf = g_key_file_new ();
227 GError *error = NULL;
228 gboolean res = g_key_file_load_from_file ( kf, e->path, 0, &error );
233 g_warning (
"[%s] [%s] Failed to parse desktop file because: %s.", e->app_id, e->path, error->message );
234 g_error_free ( error );
235 g_key_file_free ( kf );
240 gchar *url = g_key_file_get_string ( e->key_file, e->action,
"URL", NULL );
241 if ( url == NULL || strlen ( url ) == 0 ) {
242 g_warning (
"[%s] [%s] No URL found.", e->app_id, e->path );
248 gchar *command = g_newa ( gchar, command_len );
252 g_debug (
"Link launch command: |%s|", command );
254 char *path = g_build_filename (
cache_dir, DRUN_CACHE_FILE, NULL );
260 static void exec_cmd_entry ( DRunModeEntry *e )
262 GError *error = NULL;
263 GRegex *reg = g_regex_new (
"%[a-zA-Z%]", 0, 0, &error );
264 if ( error != NULL ) {
265 g_warning (
"Internal error, failed to create regex: %s.", error->message );
266 g_error_free ( error );
269 struct RegexEvalArg earg = { .e = e, .success = TRUE };
270 char *str = g_regex_replace_eval ( reg, e->exec, -1, 0, 0, drun_helper_eval_cb, &earg, &error );
271 if ( error != NULL ) {
272 g_warning (
"Internal error, failed replace field codes: %s.", error->message );
273 g_error_free ( error );
276 g_regex_unref ( reg );
277 if ( earg.success == FALSE ) {
278 g_warning (
"Invalid field code in Exec line: %s.", e->exec );;
282 g_warning (
"Nothing to execute after processing: %s.", e->exec );;
285 g_debug (
"Parsed command: |%s| into |%s|.", e->exec, str );
287 if ( e->key_file == NULL ) {
288 GKeyFile *kf = g_key_file_new ();
289 GError *error = NULL;
290 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.", e->app_id, e->path, error->message );
296 g_error_free ( error );
297 g_key_file_free ( kf );
303 const gchar *fp = g_strstrip ( str );
304 gchar *exec_path = g_key_file_get_string ( e->key_file, e->action,
"Path", NULL );
305 if ( exec_path != NULL && strlen ( exec_path ) == 0 ) {
307 g_free ( exec_path );
313 .icon = e->icon_name,
316 gboolean sn = g_key_file_get_boolean ( e->key_file, e->action,
"StartupNotify", NULL );
317 gchar *wmclass = NULL;
318 if ( sn && g_key_file_has_key ( e->key_file, e->action,
"StartupWMClass", NULL ) ) {
319 context.
wmclass = wmclass = g_key_file_get_string ( e->key_file, e->action,
"StartupWMClass", NULL );
323 gboolean terminal = g_key_file_get_boolean ( e->key_file, e->action,
"Terminal", NULL );
325 char *path = g_build_filename (
cache_dir, DRUN_CACHE_FILE, NULL );
331 g_free ( exec_path );
335 static gboolean rofi_strv_contains (
const char *
const *categories,
const char *
const *field )
337 for (
int i = 0; categories && categories[i]; i++ ) {
338 for (
int j = 0; field[j]; j++ ) {
339 if ( g_str_equal ( categories[i], field[j] ) ) {
349 static void read_desktop_file ( DRunModePrivateData *pd,
const char *root,
const char *path,
const gchar *basename,
const char *action )
351 DRunDesktopEntryType desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED;
355 const ssize_t id_len = strlen ( path ) - strlen ( root );
357 g_strlcpy (
id, &( path[strlen ( root ) + 1] ), id_len );
358 for (
int index = 0; index < id_len; index++ ) {
359 if (
id[index] ==
'/' ) {
365 if ( g_hash_table_contains ( pd->disabled_entries,
id ) && !parse_action ) {
366 g_debug (
"[%s] [%s] Skipping, was previously seen.",
id, path );
369 GKeyFile *kf = g_key_file_new ();
370 GError *error = NULL;
371 gboolean res = g_key_file_load_from_file ( kf, path, 0, &error );
374 g_debug (
"[%s] [%s] Failed to parse desktop file because: %s.",
id, path, error->message );
375 g_error_free ( error );
376 g_key_file_free ( kf );
380 if ( g_key_file_has_group ( kf, action ) == FALSE ) {
382 g_debug (
"[%s] [%s] Invalid desktop file: No %s group",
id, path, action );
383 g_key_file_free ( kf );
387 gchar *key = g_key_file_get_string ( kf, DRUN_GROUP_NAME,
"Type", NULL );
390 g_debug (
"[%s] [%s] Invalid desktop file: No type indicated",
id, path );
391 g_key_file_free ( kf );
394 if ( !g_strcmp0 ( key,
"Application" ) ) {
395 desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_APPLICATION;
397 else if ( !g_strcmp0 ( key,
"Link" ) ) {
398 desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_LINK;
401 g_debug (
"[%s] [%s] Skipping desktop file: Not of type Application or Link (%s)",
id, path, key );
403 g_key_file_free ( kf );
409 if ( !g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"Name", NULL ) ) {
410 g_debug (
"[%s] [%s] Invalid desktop file: no 'Name' key present.",
id, path );
411 g_key_file_free ( kf );
416 if ( g_key_file_get_boolean ( kf, DRUN_GROUP_NAME,
"Hidden", NULL ) ) {
417 g_debug (
"[%s] [%s] Adding desktop file to disabled list: 'Hidden' key is true",
id, path );
418 g_key_file_free ( kf );
419 g_hash_table_add ( pd->disabled_entries, g_strdup (
id ) );
422 if ( pd->current_desktop_list ) {
423 gboolean show = TRUE;
425 if ( g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"OnlyShowIn", NULL ) ) {
428 gchar **list = g_key_file_get_string_list ( kf, DRUN_GROUP_NAME,
"OnlyShowIn", &llength, NULL );
430 for ( gsize lcd = 0; !show && pd->current_desktop_list[lcd]; lcd++ ) {
431 for ( gsize lle = 0; !show && lle < llength; lle++ ) {
432 show = ( g_strcmp0 ( pd->current_desktop_list[lcd], list[lle] ) == 0 );
438 if ( show && g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"NotShowIn", NULL ) ) {
440 gchar **list = g_key_file_get_string_list ( kf, DRUN_GROUP_NAME,
"NotShowIn", &llength, NULL );
442 for ( gsize lcd = 0; show && pd->current_desktop_list[lcd]; lcd++ ) {
443 for ( gsize lle = 0; show && lle < llength; lle++ ) {
444 show = !( g_strcmp0 ( pd->current_desktop_list[lcd], list[lle] ) == 0 );
452 g_debug (
"[%s] [%s] Adding desktop file to disabled list: 'OnlyShowIn'/'NotShowIn' keys don't match current desktop",
id, path );
453 g_key_file_free ( kf );
454 g_hash_table_add ( pd->disabled_entries, g_strdup (
id ) );
459 if ( g_key_file_get_boolean ( kf, DRUN_GROUP_NAME,
"NoDisplay", NULL ) ) {
460 g_debug (
"[%s] [%s] Adding desktop file to disabled list: 'NoDisplay' key is true",
id, path );
461 g_key_file_free ( kf );
462 g_hash_table_add ( pd->disabled_entries, g_strdup (
id ) );
467 if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION
468 && !g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"Exec", NULL ) ) {
469 g_debug (
"[%s] [%s] Unsupported desktop file: no 'Exec' key present for type Application.",
id, path );
470 g_key_file_free ( kf );
473 if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_LINK
474 && !g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"URL", NULL ) ) {
475 g_debug (
"[%s] [%s] Unsupported desktop file: no 'URL' key present for type Link.",
id, path );
476 g_key_file_free ( kf );
480 if ( g_key_file_has_key ( kf, DRUN_GROUP_NAME,
"TryExec", NULL ) ) {
481 char *te = g_key_file_get_string ( kf, DRUN_GROUP_NAME,
"TryExec", NULL );
482 if ( !g_path_is_absolute ( te ) ) {
483 char *fp = g_find_program_in_path ( te );
486 g_key_file_free ( kf );
492 if ( g_file_test ( te, G_FILE_TEST_IS_EXECUTABLE ) == FALSE ) {
494 g_key_file_free ( kf );
501 char **categories = NULL;
502 if ( pd->show_categories ) {
503 categories = g_key_file_get_locale_string_list ( kf, DRUN_GROUP_NAME,
"Categories", NULL, NULL, NULL );
504 if ( !rofi_strv_contains ( (
const char *
const *) categories, (
const char *
const *) pd->show_categories ) ) {
505 g_strfreev ( categories );
506 g_key_file_free ( kf );
511 size_t nl = ( ( pd->cmd_list_length ) + 1 );
512 if ( nl >= pd->cmd_list_length_actual ) {
513 pd->cmd_list_length_actual += 256;
514 pd->entry_list = g_realloc ( pd->entry_list, pd->cmd_list_length_actual * sizeof ( *( pd->entry_list ) ) );
518 if ( G_UNLIKELY ( pd->cmd_list_length > INT_MAX ) ) {
520 pd->entry_list[pd->cmd_list_length].sort_index = INT_MIN;
523 pd->entry_list[pd->cmd_list_length].sort_index = -nl;
525 pd->entry_list[pd->cmd_list_length].icon_size = 0;
526 pd->entry_list[pd->cmd_list_length].icon_fetch_uid = 0;
527 pd->entry_list[pd->cmd_list_length].root = g_strdup ( root );
528 pd->entry_list[pd->cmd_list_length].path = g_strdup ( path );
529 pd->entry_list[pd->cmd_list_length].desktop_id = g_strdup (
id );
530 pd->entry_list[pd->cmd_list_length].app_id = g_strndup ( basename, strlen ( basename ) - strlen (
".desktop" ) );
531 gchar *n = g_key_file_get_locale_string ( kf, DRUN_GROUP_NAME,
"Name", NULL, NULL );
533 if ( action != DRUN_GROUP_NAME ) {
534 gchar *na = g_key_file_get_locale_string ( kf, action,
"Name", NULL, NULL );
535 gchar *l = g_strdup_printf (
"%s - %s", n, na );
539 pd->entry_list[pd->cmd_list_length].name = n;
540 pd->entry_list[pd->cmd_list_length].action = DRUN_GROUP_NAME;
541 gchar *gn = g_key_file_get_locale_string ( kf, DRUN_GROUP_NAME,
"GenericName", NULL, NULL );
542 pd->entry_list[pd->cmd_list_length].generic_name = gn;
544 if ( matching_entry_fields[DRUN_MATCH_FIELD_KEYWORDS].enabled ) {
545 pd->entry_list[pd->cmd_list_length].keywords = g_key_file_get_locale_string_list ( kf, DRUN_GROUP_NAME,
"Keywords", NULL, NULL, NULL );
548 pd->entry_list[pd->cmd_list_length].keywords = NULL;
551 if ( matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled ) {
553 pd->entry_list[pd->cmd_list_length].categories = categories;
557 pd->entry_list[pd->cmd_list_length].categories = g_key_file_get_locale_string_list ( kf, DRUN_GROUP_NAME,
"Categories", NULL, NULL, NULL );
561 pd->entry_list[pd->cmd_list_length].categories = NULL;
563 g_strfreev ( categories );
565 pd->entry_list[pd->cmd_list_length].type = desktop_entry_type;
566 if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION ) {
567 pd->entry_list[pd->cmd_list_length].exec = g_key_file_get_string ( kf, action,
"Exec", NULL );
570 pd->entry_list[pd->cmd_list_length].exec = NULL;
573 if ( matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled ) {
574 pd->entry_list[pd->cmd_list_length].comment = g_key_file_get_locale_string ( kf,
575 DRUN_GROUP_NAME,
"Comment", NULL, NULL );
578 pd->entry_list[pd->cmd_list_length].comment = NULL;
581 pd->entry_list[pd->cmd_list_length].icon_name = g_key_file_get_locale_string ( kf, DRUN_GROUP_NAME,
"Icon", NULL, NULL );
584 pd->entry_list[pd->cmd_list_length].icon_name = NULL;
586 pd->entry_list[pd->cmd_list_length].icon = NULL;
589 pd->entry_list[pd->cmd_list_length].key_file = kf;
591 g_hash_table_add ( pd->disabled_entries, g_strdup (
id ) );
592 g_debug (
"[%s] Using file %s.",
id, path );
593 ( pd->cmd_list_length )++;
595 if ( !parse_action ) {
596 gsize actions_length = 0;
597 char **actions = g_key_file_get_string_list ( kf, DRUN_GROUP_NAME,
"Actions", &actions_length, NULL );
598 for ( gsize iter = 0; iter < actions_length; iter++ ) {
599 char *new_action = g_strdup_printf (
"Desktop Action %s", actions[iter] );
600 read_desktop_file ( pd, root, path, basename, new_action );
601 g_free ( new_action );
603 g_strfreev ( actions );
611 static void walk_dir ( DRunModePrivateData *pd,
const char *root,
const char *dirname )
615 g_debug (
"Checking directory %s for desktop files.", dirname );
616 dir = opendir ( dirname );
622 gchar *filename = NULL;
624 while ( ( file = readdir ( dir ) ) != NULL ) {
625 if ( file->d_name[0] ==
'.' ) {
628 switch ( file->d_type )
634 filename = g_build_filename ( dirname, file->d_name, NULL );
642 if ( file->d_type == DT_LNK || file->d_type == DT_UNKNOWN ) {
643 file->d_type = DT_UNKNOWN;
644 if ( stat ( filename, &st ) == 0 ) {
645 if ( S_ISDIR ( st.st_mode ) ) {
646 file->d_type = DT_DIR;
648 else if ( S_ISREG ( st.st_mode ) ) {
649 file->d_type = DT_REG;
654 switch ( file->d_type )
658 if ( g_str_has_suffix ( file->d_name,
".desktop" ) ) {
659 read_desktop_file ( pd, root, filename, file->d_name, DRUN_GROUP_NAME );
663 walk_dir ( pd, root, filename );
677 static void delete_entry_history (
const DRunModeEntry *entry )
679 char *path = g_build_filename (
cache_dir, DRUN_CACHE_FILE, NULL );
684 static void get_apps_history ( DRunModePrivateData *pd )
686 TICK_N (
"Start drun history" );
687 unsigned int length = 0;
688 gchar *path = g_build_filename (
cache_dir, DRUN_CACHE_FILE, NULL );
690 for (
unsigned int index = 0; index < length; index++ ) {
691 for (
size_t i = 0; i < pd->cmd_list_length; i++ ) {
692 if ( g_strcmp0 ( pd->entry_list[i].desktop_id, retv[index] ) == 0 ) {
693 unsigned int sort_index = length - index;
694 if ( G_LIKELY ( sort_index < INT_MAX ) ) {
695 pd->entry_list[i].sort_index = sort_index;
699 pd->entry_list[i].sort_index = INT_MAX;
706 TICK_N (
"Stop drun history" );
709 static gint drun_int_sort_list ( gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer user_data )
711 DRunModeEntry *da = (DRunModeEntry *) a;
712 DRunModeEntry *db = (DRunModeEntry *) b;
714 if ( da->sort_index < 0 && db->sort_index < 0 ) {
715 return g_utf8_collate ( da->name, db->name );
718 return db->sort_index - da->sort_index;
726 #define CACHE_VERSION 2
727 static void drun_write_str ( FILE *fd,
const char *str )
729 size_t l = ( str == NULL ? 0 : strlen ( str ) );
730 fwrite ( &l,
sizeof ( l ), 1, fd );
734 fwrite ( str, 1, l + 1, fd );
737 static void drun_write_integer ( FILE *fd, int32_t val )
739 fwrite ( &val,
sizeof ( val ), 1, fd );
741 static void drun_read_integer ( FILE *fd, int32_t *type )
743 if ( fread ( type,
sizeof ( int32_t ), 1, fd ) != 1 ) {
744 g_warning (
"Failed to read entry, cache corrupt?" );
748 static void drun_read_string ( FILE *fd,
char **str )
752 if ( fread ( &l,
sizeof ( l ), 1, fd ) != 1 ) {
753 g_warning (
"Failed to read entry, cache corrupt?" );
760 ( *str ) = g_malloc ( l );
761 if ( fread ( ( *str ), 1, l, fd ) != l ) {
762 g_warning (
"Failed to read entry, cache corrupt?" );
766 static void drun_write_strv ( FILE *fd,
char **str )
768 guint vl = ( str == NULL ? 0 : g_strv_length ( str ) );
769 fwrite ( &vl,
sizeof ( vl ), 1, fd );
770 for ( guint index = 0; index < vl; index++ ) {
771 drun_write_str ( fd, str[index] );
774 static void drun_read_stringv ( FILE *fd,
char ***str )
778 if ( fread ( &vl,
sizeof ( vl ), 1, fd ) != 1 ) {
779 g_warning (
"Failed to read entry, cache corrupt?" );
784 ( *str ) = g_malloc0 ( ( vl + 1 ) *
sizeof ( **str ) );
785 for ( guint index = 0; index < vl; index++ ) {
786 drun_read_string ( fd, &( ( *str )[index] ) );
791 static void write_cache ( DRunModePrivateData *pd,
const char *cache_file )
796 TICK_N (
"DRUN Write CACHE: start" );
798 FILE *fd = fopen ( cache_file,
"w" );
800 g_warning (
"Failed to write to cache file" );
803 uint8_t version = CACHE_VERSION;
804 fwrite ( &version,
sizeof ( version ), 1, fd );
806 fwrite ( &( pd->cmd_list_length ), sizeof ( pd->cmd_list_length ), 1, fd );
807 for (
unsigned int index = 0; index < pd->cmd_list_length; index++ ) {
808 DRunModeEntry *entry = &( pd->entry_list[index] );
810 drun_write_str ( fd, entry->action );
811 drun_write_str ( fd, entry->root );
812 drun_write_str ( fd, entry->path );
813 drun_write_str ( fd, entry->app_id );
814 drun_write_str ( fd, entry->desktop_id );
815 drun_write_str ( fd, entry->icon_name );
816 drun_write_str ( fd, entry->exec );
817 drun_write_str ( fd, entry->name );
818 drun_write_str ( fd, entry->generic_name );
820 drun_write_strv ( fd, entry->categories );
821 drun_write_strv ( fd, entry->keywords );
823 drun_write_str ( fd, entry->comment );
824 drun_write_integer ( fd, (int32_t) entry->type );
828 TICK_N (
"DRUN Write CACHE: end" );
834 static gboolean drun_read_cache ( DRunModePrivateData *pd,
const char *cache_file )
843 TICK_N (
"DRUN Read CACHE: start" );
844 FILE *fd = fopen ( cache_file,
"r" );
846 TICK_N (
"DRUN Read CACHE: stop" );
853 if ( fread ( &version,
sizeof ( version ), 1, fd ) != 1 ) {
855 g_warning (
"Cache corrupt, ignoring." );
856 TICK_N (
"DRUN Read CACHE: stop" );
860 if ( version != CACHE_VERSION ) {
862 g_warning (
"Cache file wrong version, ignoring." );
863 TICK_N (
"DRUN Read CACHE: stop" );
867 if ( fread ( &( pd->cmd_list_length ), sizeof ( pd->cmd_list_length ), 1, fd ) != 1 ) {
869 g_warning (
"Cache corrupt, ignoring." );
870 TICK_N (
"DRUN Read CACHE: stop" );
874 pd->cmd_list_length_actual = pd->cmd_list_length;
876 pd->entry_list = g_malloc0 ( pd->cmd_list_length_actual * sizeof ( *( pd->entry_list ) ) );
878 for (
unsigned int index = 0; index < pd->cmd_list_length; index++ ) {
879 DRunModeEntry *entry = &( pd->entry_list[index] );
881 drun_read_string ( fd, &( entry->action ) );
882 drun_read_string ( fd, &( entry->root ) );
883 drun_read_string ( fd, &( entry->path ) );
884 drun_read_string ( fd, &( entry->app_id ) );
885 drun_read_string ( fd, &( entry->desktop_id ) );
886 drun_read_string ( fd, &( entry->icon_name ) );
887 drun_read_string ( fd, &( entry->exec ) );
888 drun_read_string ( fd, &( entry->name ) );
889 drun_read_string ( fd, &( entry->generic_name ) );
891 drun_read_stringv ( fd, &( entry->categories ) );
892 drun_read_stringv ( fd, &( entry->keywords ) );
894 drun_read_string ( fd, &( entry->comment ) );
896 drun_read_integer ( fd, &( type ) );
901 TICK_N (
"DRUN Read CACHE: stop" );
905 static void get_apps ( DRunModePrivateData *pd )
907 char *cache_file = g_build_filename (
cache_dir, DRUN_DESKTOP_CACHE_FILE, NULL );
908 TICK_N (
"Get Desktop apps (start)" );
909 if ( drun_read_cache ( pd, cache_file ) ) {
912 dir = g_build_filename ( g_get_user_data_dir (),
"applications", NULL );
913 walk_dir ( pd, dir, dir );
915 TICK_N (
"Get Desktop apps (user dir)" );
917 const gchar *
const * sys = g_get_system_data_dirs ();
918 for (
const gchar *
const *iter = sys; *iter != NULL; ++iter ) {
919 gboolean unique = TRUE;
921 for (
const gchar *
const *iterd = sys; iterd != iter; ++iterd ) {
922 if ( g_strcmp0 ( *iter, *iterd ) == 0 ) {
927 if ( unique && ( **iter ) !=
'\0' ) {
928 dir = g_build_filename ( *iter,
"applications", NULL );
929 walk_dir ( pd, dir, dir );
933 TICK_N (
"Get Desktop apps (system dirs)" );
934 get_apps_history ( pd );
936 g_qsort_with_data ( pd->entry_list, pd->cmd_list_length, sizeof ( DRunModeEntry ), drun_int_sort_list, NULL );
938 TICK_N (
"Sorting done." );
940 write_cache ( pd, cache_file );
942 g_free ( cache_file );
945 static void drun_mode_parse_entry_fields ()
950 const char *
const sep =
",#";
952 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
953 matching_entry_fields[i].enabled = FALSE;
955 for (
char *token = strtok_r ( switcher_str, sep, &savept ); token != NULL;
956 token = strtok_r ( NULL, sep, &savept ) ) {
957 if ( strcmp ( token,
"all" ) == 0 ) {
958 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
959 matching_entry_fields[i].enabled = TRUE;
964 gboolean matched = FALSE;
965 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
966 const char * entry_name = matching_entry_fields[i].entry_field_name;
967 if ( g_ascii_strcasecmp ( token, entry_name ) == 0 ) {
968 matching_entry_fields[i].enabled = TRUE;
973 g_warning (
"Invalid entry name :%s", token );
978 g_free ( switcher_str );
981 static int drun_mode_init (
Mode *sw )
986 DRunModePrivateData *pd = g_malloc0 (
sizeof ( *pd ) );
987 pd->disabled_entries = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL );
990 const char *current_desktop = g_getenv (
"XDG_CURRENT_DESKTOP" );
991 pd->current_desktop_list = current_desktop ? g_strsplit ( current_desktop,
":", 0 ) : NULL;
997 drun_mode_parse_entry_fields ();
1001 static void drun_entry_clear ( DRunModeEntry *e )
1005 g_free ( e->app_id );
1006 g_free ( e->desktop_id );
1007 if ( e->icon != NULL ) {
1008 cairo_surface_destroy ( e->icon );
1010 g_free ( e->icon_name );
1013 g_free ( e->generic_name );
1014 g_free ( e->comment );
1015 if ( e->action != DRUN_GROUP_NAME ) {
1016 g_free ( e->action );
1018 g_strfreev ( e->categories );
1019 g_strfreev ( e->keywords );
1020 if ( e->key_file ) {
1021 g_key_file_free ( e->key_file );
1025 static ModeMode drun_mode_result (
Mode *sw,
int mretv,
char **input,
unsigned int selected_line )
1031 switch ( rmpd->entry_list[selected_line].type )
1033 case DRUN_DESKTOP_ENTRY_TYPE_APPLICATION:
1034 exec_cmd_entry ( &( rmpd->entry_list[selected_line] ) );
1036 case DRUN_DESKTOP_ENTRY_TYPE_LINK:
1037 launch_link_entry ( &( rmpd->entry_list[selected_line] ) );
1040 g_assert_not_reached ();
1043 else if ( ( mretv &
MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] !=
'\0' ) {
1051 else if ( ( mretv &
MENU_ENTRY_DELETE ) && selected_line < rmpd->cmd_list_length ) {
1053 if ( rmpd->entry_list[selected_line].sort_index >= 0 ) {
1054 delete_entry_history ( &( rmpd->entry_list[selected_line] ) );
1055 drun_entry_clear ( &( rmpd->entry_list[selected_line] ) );
1056 memmove ( &( rmpd->entry_list[selected_line] ), &rmpd->entry_list[selected_line + 1],
1057 sizeof ( DRunModeEntry ) * ( rmpd->cmd_list_length - selected_line - 1 ) );
1058 rmpd->cmd_list_length--;
1064 static void drun_mode_destroy (
Mode *sw )
1067 if ( rmpd != NULL ) {
1068 for (
size_t i = 0; i < rmpd->cmd_list_length; i++ ) {
1069 drun_entry_clear ( &( rmpd->entry_list[i] ) );
1071 g_hash_table_destroy ( rmpd->disabled_entries );
1072 g_free ( rmpd->entry_list );
1074 g_strfreev ( rmpd->current_desktop_list );
1075 g_strfreev ( rmpd->show_categories );
1081 static char *
_get_display_value (
const Mode *sw,
unsigned int selected_line,
int *state, G_GNUC_UNUSED GList **list,
int get_entry )
1088 if ( pd->entry_list == NULL ) {
1090 return g_strdup (
"Failed" );
1093 DRunModeEntry *dr = &( pd->entry_list[selected_line] );
1095 if ( dr->categories ) {
1096 char *tcats = g_strjoinv (
",", dr->categories );
1098 cats = g_markup_escape_text ( tcats, -1 );
1102 gchar *keywords = NULL;
1103 if ( dr->keywords ) {
1104 char *tkeyw = g_strjoinv (
",", dr->keywords );
1106 keywords = g_markup_escape_text ( tkeyw, -1 );
1114 if ( dr->generic_name ) {
1115 egn = g_markup_escape_text ( dr->generic_name, -1 );
1118 en = g_markup_escape_text ( dr->name, -1 );
1120 if ( dr->comment ) {
1121 ec = g_markup_escape_text ( dr->comment, -1 );
1129 "{categories}", cats,
1130 "{keywords}", keywords,
1139 static cairo_surface_t *
_get_icon (
const Mode *sw,
unsigned int selected_line,
int height )
1142 g_return_val_if_fail ( pd->entry_list != NULL, NULL );
1143 DRunModeEntry *dr = &( pd->entry_list[selected_line] );
1144 if ( dr->icon_name == NULL ) {
1147 if ( dr->icon_fetch_uid > 0 ) {
1154 static char *drun_get_completion (
const Mode *sw,
unsigned int index )
1158 DRunModeEntry *dr = &( pd->entry_list[index] );
1159 if ( dr->generic_name == NULL ) {
1160 return g_strdup ( dr->name );
1163 return g_strdup_printf (
"%s", dr->name );
1167 static int drun_token_match (
const Mode *data,
rofi_int_matcher **tokens,
unsigned int index )
1172 for (
int j = 0; match && tokens != NULL && tokens[j] != NULL; j++ ) {
1176 if ( matching_entry_fields[DRUN_MATCH_FIELD_NAME].enabled ) {
1177 if ( rmpd->entry_list[index].name ) {
1181 if ( matching_entry_fields[DRUN_MATCH_FIELD_GENERIC].enabled ) {
1183 if ( test == tokens[j]->invert && rmpd->entry_list[index].generic_name ) {
1187 if ( matching_entry_fields[DRUN_MATCH_FIELD_EXEC].enabled ) {
1189 if ( test == tokens[j]->invert && rmpd->entry_list[index].exec ) {
1193 if ( matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled ) {
1195 if ( test == tokens[j]->invert ) {
1196 gchar **list = rmpd->entry_list[index].categories;
1197 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter]; iter++ ) {
1202 if ( matching_entry_fields[DRUN_MATCH_FIELD_KEYWORDS].enabled ) {
1204 if ( test == tokens[j]->invert ) {
1205 gchar **list = rmpd->entry_list[index].keywords;
1206 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter]; iter++ ) {
1211 if ( matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled ) {
1213 if ( test == tokens[j]->invert && rmpd->entry_list[index].comment ) {
1226 static unsigned int drun_mode_get_num_entries (
const Mode *sw )
1229 return pd->cmd_list_length;
1235 .cfg_name_key =
"display-drun",
1236 ._init = drun_mode_init,
1237 ._get_num_entries = drun_mode_get_num_entries,
1238 ._result = drun_mode_result,
1239 ._destroy = drun_mode_destroy,
1240 ._token_match = drun_token_match,
1241 ._get_completion = drun_get_completion,
1244 ._preprocess_input = NULL,
1245 .private_data = NULL,
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, 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]
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_set_private_data(Mode *mode, void *pd)
void * mode_get_private_data(const Mode *mode)
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