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 1
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_read_string ( FILE *fd,
char **str )
741 if ( fread ( &l,
sizeof ( l ), 1, fd ) != 1 ) {
742 g_warning (
"Failed to read entry, cache corrupt?" );
749 ( *str ) = g_malloc ( l );
750 if ( fread ( ( *str ), 1, l, fd ) != l ) {
751 g_warning (
"Failed to read entry, cache corrupt?" );
755 static void drun_write_strv ( FILE *fd,
char **str )
757 guint vl = ( str == NULL ? 0 : g_strv_length ( str ) );
758 fwrite ( &vl,
sizeof ( vl ), 1, fd );
759 for ( guint index = 0; index < vl; index++ ) {
760 drun_write_str ( fd, str[index] );
763 static void drun_read_stringv ( FILE *fd,
char ***str )
767 if ( fread ( &vl,
sizeof ( vl ), 1, fd ) != 1 ) {
768 g_warning (
"Failed to read entry, cache corrupt?" );
773 ( *str ) = g_malloc0 ( ( vl + 1 ) *
sizeof ( **str ) );
774 for ( guint index = 0; index < vl; index++ ) {
775 drun_read_string ( fd, &( ( *str )[index] ) );
780 static void write_cache ( DRunModePrivateData *pd,
const char *cache_file )
785 TICK_N (
"DRUN Write CACHE: start" );
787 FILE *fd = fopen ( cache_file,
"w" );
789 g_warning (
"Failed to write to cache file" );
792 uint8_t version = CACHE_VERSION;
793 fwrite ( &version,
sizeof ( version ), 1, fd );
795 fwrite ( &( pd->cmd_list_length ), sizeof ( pd->cmd_list_length ), 1, fd );
796 for (
unsigned int index = 0; index < pd->cmd_list_length; index++ ) {
797 DRunModeEntry *entry = &( pd->entry_list[index] );
799 drun_write_str ( fd, entry->action );
800 drun_write_str ( fd, entry->root );
801 drun_write_str ( fd, entry->path );
802 drun_write_str ( fd, entry->app_id );
803 drun_write_str ( fd, entry->desktop_id );
804 drun_write_str ( fd, entry->icon_name );
805 drun_write_str ( fd, entry->exec );
806 drun_write_str ( fd, entry->name );
807 drun_write_str ( fd, entry->generic_name );
809 drun_write_strv ( fd, entry->categories );
810 drun_write_strv ( fd, entry->keywords );
812 drun_write_str ( fd, entry->comment );
816 TICK_N (
"DRUN Write CACHE: end" );
822 static gboolean drun_read_cache ( DRunModePrivateData *pd,
const char *cache_file )
831 TICK_N (
"DRUN Read CACHE: start" );
832 FILE *fd = fopen ( cache_file,
"r" );
834 TICK_N (
"DRUN Read CACHE: stop" );
841 if ( fread ( &version,
sizeof ( version ), 1, fd ) != 1 ) {
843 g_warning (
"Cache corrupt, ignoring." );
844 TICK_N (
"DRUN Read CACHE: stop" );
848 if ( version != CACHE_VERSION ) {
850 g_warning (
"Cache file wrong version, ignoring." );
851 TICK_N (
"DRUN Read CACHE: stop" );
855 if ( fread ( &( pd->cmd_list_length ), sizeof ( pd->cmd_list_length ), 1, fd ) != 1 ) {
857 g_warning (
"Cache corrupt, ignoring." );
858 TICK_N (
"DRUN Read CACHE: stop" );
862 pd->cmd_list_length_actual = pd->cmd_list_length;
864 pd->entry_list = g_malloc0 ( pd->cmd_list_length_actual * sizeof ( *( pd->entry_list ) ) );
866 for (
unsigned int index = 0; index < pd->cmd_list_length; index++ ) {
867 DRunModeEntry *entry = &( pd->entry_list[index] );
869 drun_read_string ( fd, &( entry->action ) );
870 drun_read_string ( fd, &( entry->root ) );
871 drun_read_string ( fd, &( entry->path ) );
872 drun_read_string ( fd, &( entry->app_id ) );
873 drun_read_string ( fd, &( entry->desktop_id ) );
874 drun_read_string ( fd, &( entry->icon_name ) );
875 drun_read_string ( fd, &( entry->exec ) );
876 drun_read_string ( fd, &( entry->name ) );
877 drun_read_string ( fd, &( entry->generic_name ) );
879 drun_read_stringv ( fd, &( entry->categories ) );
880 drun_read_stringv ( fd, &( entry->keywords ) );
882 drun_read_string ( fd, &( entry->comment ) );
886 TICK_N (
"DRUN Read CACHE: stop" );
890 static void get_apps ( DRunModePrivateData *pd )
892 char *cache_file = g_build_filename (
cache_dir, DRUN_DESKTOP_CACHE_FILE, NULL );
893 TICK_N (
"Get Desktop apps (start)" );
894 if ( drun_read_cache ( pd, cache_file ) ) {
897 dir = g_build_filename ( g_get_user_data_dir (),
"applications", NULL );
898 walk_dir ( pd, dir, dir );
900 TICK_N (
"Get Desktop apps (user dir)" );
902 const gchar *
const * sys = g_get_system_data_dirs ();
903 for (
const gchar *
const *iter = sys; *iter != NULL; ++iter ) {
904 gboolean unique = TRUE;
906 for (
const gchar *
const *iterd = sys; iterd != iter; ++iterd ) {
907 if ( g_strcmp0 ( *iter, *iterd ) == 0 ) {
912 if ( unique && ( **iter ) !=
'\0' ) {
913 dir = g_build_filename ( *iter,
"applications", NULL );
914 walk_dir ( pd, dir, dir );
918 TICK_N (
"Get Desktop apps (system dirs)" );
919 get_apps_history ( pd );
921 g_qsort_with_data ( pd->entry_list, pd->cmd_list_length, sizeof ( DRunModeEntry ), drun_int_sort_list, NULL );
923 TICK_N (
"Sorting done." );
925 write_cache ( pd, cache_file );
927 g_free ( cache_file );
930 static void drun_mode_parse_entry_fields ()
935 const char *
const sep =
",#";
937 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
938 matching_entry_fields[i].enabled = FALSE;
940 for (
char *token = strtok_r ( switcher_str, sep, &savept ); token != NULL;
941 token = strtok_r ( NULL, sep, &savept ) ) {
942 if ( strcmp ( token,
"all" ) == 0 ) {
943 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
944 matching_entry_fields[i].enabled = TRUE;
949 gboolean matched = FALSE;
950 for (
unsigned int i = 0; i < DRUN_MATCH_NUM_FIELDS; i++ ) {
951 const char * entry_name = matching_entry_fields[i].entry_field_name;
952 if ( g_ascii_strcasecmp ( token, entry_name ) == 0 ) {
953 matching_entry_fields[i].enabled = TRUE;
958 g_warning (
"Invalid entry name :%s", token );
963 g_free ( switcher_str );
966 static int drun_mode_init (
Mode *sw )
971 DRunModePrivateData *pd = g_malloc0 (
sizeof ( *pd ) );
972 pd->disabled_entries = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL );
975 const char *current_desktop = g_getenv (
"XDG_CURRENT_DESKTOP" );
976 pd->current_desktop_list = current_desktop ? g_strsplit ( current_desktop,
":", 0 ) : NULL;
982 drun_mode_parse_entry_fields ();
986 static void drun_entry_clear ( DRunModeEntry *e )
990 g_free ( e->app_id );
991 g_free ( e->desktop_id );
992 if ( e->icon != NULL ) {
993 cairo_surface_destroy ( e->icon );
995 g_free ( e->icon_name );
998 g_free ( e->generic_name );
999 g_free ( e->comment );
1000 if ( e->action != DRUN_GROUP_NAME ) {
1001 g_free ( e->action );
1003 g_strfreev ( e->categories );
1004 g_strfreev ( e->keywords );
1005 if ( e->key_file ) {
1006 g_key_file_free ( e->key_file );
1010 static ModeMode drun_mode_result (
Mode *sw,
int mretv,
char **input,
unsigned int selected_line )
1024 else if ( ( mretv &
MENU_OK ) ) {
1025 switch ( rmpd->entry_list[selected_line].type )
1027 case DRUN_DESKTOP_ENTRY_TYPE_APPLICATION:
1028 exec_cmd_entry ( &( rmpd->entry_list[selected_line] ) );
1030 case DRUN_DESKTOP_ENTRY_TYPE_LINK:
1031 launch_link_entry ( &( rmpd->entry_list[selected_line] ) );
1034 g_assert_not_reached ();
1037 else if ( ( mretv &
MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] !=
'\0' ) {
1040 else if ( ( mretv &
MENU_ENTRY_DELETE ) && selected_line < rmpd->cmd_list_length ) {
1042 if ( rmpd->entry_list[selected_line].sort_index >= 0 ) {
1043 delete_entry_history ( &( rmpd->entry_list[selected_line] ) );
1044 drun_entry_clear ( &( rmpd->entry_list[selected_line] ) );
1045 memmove ( &( rmpd->entry_list[selected_line] ), &rmpd->entry_list[selected_line + 1],
1046 sizeof ( DRunModeEntry ) * ( rmpd->cmd_list_length - selected_line - 1 ) );
1047 rmpd->cmd_list_length--;
1053 static void drun_mode_destroy (
Mode *sw )
1056 if ( rmpd != NULL ) {
1057 for (
size_t i = 0; i < rmpd->cmd_list_length; i++ ) {
1058 drun_entry_clear ( &( rmpd->entry_list[i] ) );
1060 g_hash_table_destroy ( rmpd->disabled_entries );
1061 g_free ( rmpd->entry_list );
1063 g_strfreev ( rmpd->current_desktop_list );
1064 g_strfreev ( rmpd->show_categories );
1070 static char *
_get_display_value (
const Mode *sw,
unsigned int selected_line,
int *state, G_GNUC_UNUSED GList **list,
int get_entry )
1077 if ( pd->entry_list == NULL ) {
1079 return g_strdup (
"Failed" );
1082 DRunModeEntry *dr = &( pd->entry_list[selected_line] );
1084 if ( dr->categories ) {
1085 char *tcats = g_strjoinv (
",", dr->categories );
1087 cats = g_markup_escape_text ( tcats, -1 );
1091 gchar *keywords = NULL;
1092 if ( dr->keywords ) {
1093 char *tkeyw = g_strjoinv (
",", dr->keywords );
1095 keywords = g_markup_escape_text ( tkeyw, -1 );
1103 if ( dr->generic_name ) {
1104 egn = g_markup_escape_text ( dr->generic_name, -1 );
1107 en = g_markup_escape_text ( dr->name, -1 );
1109 if ( dr->comment ) {
1110 ec = g_markup_escape_text ( dr->comment, -1 );
1118 "{categories}", cats,
1119 "{keywords}", keywords,
1128 static cairo_surface_t *_get_icon (
const Mode *sw,
unsigned int selected_line,
int height )
1131 g_return_val_if_fail ( pd->entry_list != NULL, NULL );
1132 DRunModeEntry *dr = &( pd->entry_list[selected_line] );
1133 if ( dr->icon_name == NULL ) {
1136 if ( dr->icon_fetch_uid > 0 ) {
1143 static char *drun_get_completion (
const Mode *sw,
unsigned int index )
1147 DRunModeEntry *dr = &( pd->entry_list[index] );
1148 if ( dr->generic_name == NULL ) {
1149 return g_strdup ( dr->name );
1152 return g_strdup_printf (
"%s", dr->name );
1156 static int drun_token_match (
const Mode *data,
rofi_int_matcher **tokens,
unsigned int index )
1161 for (
int j = 0; match && tokens != NULL && tokens[j] != NULL; j++ ) {
1165 if ( matching_entry_fields[DRUN_MATCH_FIELD_NAME].enabled ) {
1166 if ( rmpd->entry_list[index].name ) {
1170 if ( matching_entry_fields[DRUN_MATCH_FIELD_GENERIC].enabled ) {
1172 if ( test == tokens[j]->invert && rmpd->entry_list[index].generic_name ) {
1176 if ( matching_entry_fields[DRUN_MATCH_FIELD_EXEC].enabled ) {
1178 if ( test == tokens[j]->invert && rmpd->entry_list[index].exec ) {
1182 if ( matching_entry_fields[DRUN_MATCH_FIELD_CATEGORIES].enabled ) {
1184 if ( test == tokens[j]->invert ) {
1185 gchar **list = rmpd->entry_list[index].categories;
1186 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter]; iter++ ) {
1191 if ( matching_entry_fields[DRUN_MATCH_FIELD_KEYWORDS].enabled ) {
1193 if ( test == tokens[j]->invert ) {
1194 gchar **list = rmpd->entry_list[index].keywords;
1195 for (
int iter = 0; test == tokens[j]->
invert && list && list[iter]; iter++ ) {
1200 if ( matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled ) {
1202 if ( test == tokens[j]->invert && rmpd->entry_list[index].comment ) {
1215 static unsigned int drun_mode_get_num_entries (
const Mode *sw )
1218 return pd->cmd_list_length;
1224 .cfg_name_key =
"display-drun",
1225 ._init = drun_mode_init,
1226 ._get_num_entries = drun_mode_get_num_entries,
1227 ._result = drun_mode_result,
1228 ._destroy = drun_mode_destroy,
1229 ._token_match = drun_token_match,
1230 ._get_completion = drun_get_completion,
1232 ._get_icon = _get_icon,
1233 ._preprocess_input = NULL,
1234 .private_data = NULL,
1238 #endif // ENABLE_DRUN