mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-25 13:55:34 -05:00
Follow Type=Link standard desktop entries with drun (#1168)
* [DRun] Introduce data structure changes for Link desktop entries From the [freedesktop spec][1]: > This specification defines 3 types of desktop entries: > Application (type 1), Link (type 2) and Directory (type 3). To allow > the addition of new types in the future, implementations should > ignore desktop entries with an unknown type. This commit adds an enum to capture these types, and adds `type` to DRunModeEntry. [1]: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html part of #1166 * [DRun] Sanity check Link entries and capture the URL Note that we're introducing some logic that will differ depending on the Desktop entry type (Application or Link). The logic is: - if entry is Application type, - then Exec is required - and the value is saved in .exec - and drun_mode_result calls exec_cmd_entry - if entry is Link type, - then URL is required (but is not saved in the DRunModeEntry) - and drun_mode_result calls new function launch_link_entry part of #1166 * [DRun] Launch desktop links via xdg-open Note that this introduces a new dependency on xdg-open, which may not be installed. In that case, rofi will display an error dialog with something like: "Failed to execute child process xdg-open (No such file or directory)" which hopefully is explanatory enough for folks. part of #1166 * Make drun options comments consistent and add a bit of whitespace * [DRun] new config option drun-url-launcher for opening links In previous commit, this was a hard-coded string. part of #1166
This commit is contained in:
parent
120ce36055
commit
5bec191d2e
4 changed files with 102 additions and 14 deletions
|
@ -116,14 +116,18 @@ Settings config = {
|
|||
.tokenize = TRUE,
|
||||
.matching = "normal",
|
||||
.matching_method = MM_NORMAL,
|
||||
/** Desktop entry fields to match*/
|
||||
|
||||
/** Desktop entries to match in drun */
|
||||
.drun_match_fields = "name,generic,exec,categories,keywords",
|
||||
/** Only show entries in this category */
|
||||
.drun_categories = NULL,
|
||||
/** Desktop format display */
|
||||
.drun_display_format = "{name} [<span weight='light' size='small'><i>({generic})</i></span>]",
|
||||
/** Desktop entry show actions */
|
||||
.drun_show_actions = FALSE,
|
||||
/** Desktop entry show actions */
|
||||
/** Desktop format display */
|
||||
.drun_display_format = "{name} [<span weight='light' size='small'><i>({generic})</i></span>]",
|
||||
/** Desktop Link launch command */
|
||||
.drun_url_launcher = "xdg-open",
|
||||
|
||||
/** Window fields to match in window mode*/
|
||||
.window_match_fields = "all",
|
||||
/** Monitor */
|
||||
|
@ -160,8 +164,11 @@ Settings config = {
|
|||
|
||||
.cache_dir = NULL,
|
||||
.window_thumbnail = FALSE,
|
||||
|
||||
/** drun cache */
|
||||
.drun_use_desktop_cache = FALSE,
|
||||
.drun_reload_desktop_cache = FALSE,
|
||||
|
||||
/** Benchmarks */
|
||||
.benchmark_ui = FALSE
|
||||
};
|
||||
|
|
|
@ -119,14 +119,18 @@ typedef struct
|
|||
SortingMethod sorting_method_enum;
|
||||
/** Sorting method. */
|
||||
char * sorting_method;
|
||||
|
||||
/** Desktop entries to match in drun */
|
||||
char * drun_match_fields;
|
||||
/** Only show entries in this category */
|
||||
char * drun_categories;
|
||||
/** Desktop entry show actions */
|
||||
unsigned int drun_show_actions;
|
||||
/** Desktop entry show */
|
||||
/** Desktop format display */
|
||||
char * drun_display_format;
|
||||
/** Desktop Link launch command */
|
||||
char * drun_url_launcher;
|
||||
|
||||
/** Search case sensitivity */
|
||||
unsigned int case_sensitive;
|
||||
/** Cycle through in the element list */
|
||||
|
|
|
@ -61,6 +61,15 @@
|
|||
char *DRUN_GROUP_NAME = "Desktop Entry";
|
||||
|
||||
typedef struct _DRunModePrivateData DRunModePrivateData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED = 0,
|
||||
DRUN_DESKTOP_ENTRY_TYPE_APPLICATION,
|
||||
DRUN_DESKTOP_ENTRY_TYPE_LINK,
|
||||
DRUN_DESKTOP_ENTRY_TYPE_DIRECTORY,
|
||||
} DRunDesktopEntryType;
|
||||
|
||||
/**
|
||||
* Store extra information about the entry.
|
||||
* Currently the executable and if it should run in terminal.
|
||||
|
@ -86,7 +95,7 @@ typedef struct
|
|||
int icon_size;
|
||||
/* Surface holding the icon. */
|
||||
cairo_surface_t *icon;
|
||||
/* Executable */
|
||||
/* Executable - for Application entries only */
|
||||
char *exec;
|
||||
/* Name of the Entry */
|
||||
char *name;
|
||||
|
@ -104,6 +113,8 @@ typedef struct
|
|||
gint sort_index;
|
||||
|
||||
uint32_t icon_fetch_uid;
|
||||
|
||||
DRunDesktopEntryType type;
|
||||
} DRunModeEntry;
|
||||
|
||||
typedef struct
|
||||
|
@ -209,6 +220,43 @@ static gboolean drun_helper_eval_cb ( const GMatchInfo *info, GString *res, gpoi
|
|||
// Continue replacement.
|
||||
return FALSE;
|
||||
}
|
||||
static void launch_link_entry ( DRunModeEntry *e )
|
||||
{
|
||||
if ( e->key_file == NULL ) {
|
||||
GKeyFile *kf = g_key_file_new ();
|
||||
GError *error = NULL;
|
||||
gboolean res = g_key_file_load_from_file ( kf, e->path, 0, &error );
|
||||
if ( res ) {
|
||||
e->key_file = kf;
|
||||
}
|
||||
else {
|
||||
g_warning ( "[%s] [%s] Failed to parse desktop file because: %s.", e->app_id, e->path, error->message );
|
||||
g_error_free ( error );
|
||||
g_key_file_free ( kf );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gchar *url = g_key_file_get_string ( e->key_file, e->action, "URL", NULL );
|
||||
if ( url == NULL || strlen ( url ) == 0 ) {
|
||||
g_warning ( "[%s] [%s] No URL found.", e->app_id, e->path );
|
||||
g_free ( url );
|
||||
return ;
|
||||
}
|
||||
|
||||
gsize command_len = strlen( config.drun_url_launcher ) + strlen( url ) + 2; // space + terminator = 2
|
||||
gchar *command = g_newa ( gchar, command_len );
|
||||
g_snprintf( command, command_len, "%s %s", config.drun_url_launcher, url );
|
||||
g_free ( url );
|
||||
|
||||
g_debug ( "Link launch command: |%s|", command );
|
||||
if ( helper_execute_command ( NULL, command, FALSE, NULL ) ) {
|
||||
char *path = g_build_filename ( cache_dir, DRUN_CACHE_FILE, NULL );
|
||||
// Store it based on the unique identifiers (desktop_id).
|
||||
history_set ( path, e->desktop_id );
|
||||
g_free ( path );
|
||||
}
|
||||
}
|
||||
static void exec_cmd_entry ( DRunModeEntry *e )
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
@ -300,6 +348,7 @@ static gboolean rofi_strv_contains ( const char * const *categories, const char
|
|||
*/
|
||||
static void read_desktop_file ( DRunModePrivateData *pd, const char *root, const char *path, const gchar *basename, const char *action )
|
||||
{
|
||||
DRunDesktopEntryType desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_UNDETERMINED;
|
||||
int parse_action = ( config.drun_show_actions && action != DRUN_GROUP_NAME );
|
||||
// Create ID on stack.
|
||||
// We know strlen (path ) > strlen(root)+1
|
||||
|
@ -342,8 +391,12 @@ static void read_desktop_file ( DRunModePrivateData *pd, const char *root, const
|
|||
g_key_file_free ( kf );
|
||||
return;
|
||||
}
|
||||
if ( g_strcmp0 ( key, "Application" ) ) {
|
||||
g_debug ( "[%s] [%s] Skipping desktop file: Not of type application (%s)", id, path, key );
|
||||
if ( !g_strcmp0 ( key, "Application" ) ) {
|
||||
desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_APPLICATION;
|
||||
} else if ( !g_strcmp0 ( key, "Link" ) ) {
|
||||
desktop_entry_type = DRUN_DESKTOP_ENTRY_TYPE_LINK;
|
||||
} else {
|
||||
g_debug ( "[%s] [%s] Skipping desktop file: Not of type Application or Link (%s)", id, path, key );
|
||||
g_free ( key );
|
||||
g_key_file_free ( kf );
|
||||
return;
|
||||
|
@ -407,9 +460,17 @@ static void read_desktop_file ( DRunModePrivateData *pd, const char *root, const
|
|||
g_hash_table_add ( pd->disabled_entries, g_strdup ( id ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// We need Exec, don't support DBusActivatable
|
||||
if ( !g_key_file_has_key ( kf, DRUN_GROUP_NAME, "Exec", NULL ) ) {
|
||||
g_debug ( "[%s] [%s] Unsupported desktop file: no 'Exec' key present.", id, path );
|
||||
if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION
|
||||
&& !g_key_file_has_key ( kf, DRUN_GROUP_NAME, "Exec", NULL ) ) {
|
||||
g_debug ( "[%s] [%s] Unsupported desktop file: no 'Exec' key present for type Application.", id, path );
|
||||
g_key_file_free ( kf );
|
||||
return;
|
||||
}
|
||||
if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_LINK
|
||||
&& !g_key_file_has_key ( kf, DRUN_GROUP_NAME, "URL", NULL ) ) {
|
||||
g_debug ( "[%s] [%s] Unsupported desktop file: no 'URL' key present for type Link.", id, path );
|
||||
g_key_file_free ( kf );
|
||||
return;
|
||||
}
|
||||
|
@ -499,7 +560,12 @@ static void read_desktop_file ( DRunModePrivateData *pd, const char *root, const
|
|||
}
|
||||
g_strfreev ( categories );
|
||||
|
||||
pd->entry_list[pd->cmd_list_length].exec = g_key_file_get_string ( kf, action, "Exec", NULL );
|
||||
pd->entry_list[pd->cmd_list_length].type = desktop_entry_type;
|
||||
if ( desktop_entry_type == DRUN_DESKTOP_ENTRY_TYPE_APPLICATION ) {
|
||||
pd->entry_list[pd->cmd_list_length].exec = g_key_file_get_string ( kf, action, "Exec", NULL );
|
||||
} else {
|
||||
pd->entry_list[pd->cmd_list_length].exec = NULL;
|
||||
}
|
||||
|
||||
if ( matching_entry_fields[DRUN_MATCH_FIELD_COMMENT].enabled ) {
|
||||
pd->entry_list[pd->cmd_list_length].comment = g_key_file_get_locale_string ( kf,
|
||||
|
@ -953,7 +1019,16 @@ static ModeMode drun_mode_result ( Mode *sw, int mretv, char **input, unsigned i
|
|||
retv = ( mretv & MENU_LOWER_MASK );
|
||||
}
|
||||
else if ( ( mretv & MENU_OK ) ) {
|
||||
exec_cmd_entry ( &( rmpd->entry_list[selected_line] ) );
|
||||
switch ( rmpd->entry_list[selected_line].type ) {
|
||||
case DRUN_DESKTOP_ENTRY_TYPE_APPLICATION:
|
||||
exec_cmd_entry ( &( rmpd->entry_list[selected_line] ) );
|
||||
break;
|
||||
case DRUN_DESKTOP_ENTRY_TYPE_LINK:
|
||||
launch_link_entry ( &( rmpd->entry_list[selected_line] ) );
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
|
||||
retv = RELOAD_DIALOG;
|
||||
|
@ -1096,7 +1171,7 @@ static int drun_token_match ( const Mode *data, rofi_int_matcher **tokens, unsig
|
|||
}
|
||||
if ( matching_entry_fields[DRUN_MATCH_FIELD_EXEC].enabled ) {
|
||||
// Match executable name.
|
||||
if ( test == tokens[j]->invert ) {
|
||||
if ( test == tokens[j]->invert && rmpd->entry_list[index].exec ) {
|
||||
test = helper_token_match ( ftokens, rmpd->entry_list[index].exec );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,13 +135,15 @@ static XrmOption xrmOptions[] = {
|
|||
|
||||
{ xrm_String, "drun-match-fields", { .str = &config.drun_match_fields }, NULL,
|
||||
"Desktop entry fields to match in drun", CONFIG_DEFAULT },
|
||||
|
||||
{ xrm_String, "drun-categories", { .str = &config.drun_categories }, NULL,
|
||||
"Only show Desktop entry from these categories", CONFIG_DEFAULT },
|
||||
{ xrm_Boolean, "drun-show-actions", { .num = &config.drun_show_actions }, NULL,
|
||||
"Desktop entry show actions.", CONFIG_DEFAULT },
|
||||
{ xrm_String, "drun-display-format", { .str = &config.drun_display_format }, NULL,
|
||||
"DRUN format string. (Supports: generic,name,comment,exec,categories)", CONFIG_DEFAULT },
|
||||
{ xrm_String, "drun-url-launcher", { .str = &config.drun_url_launcher }, NULL,
|
||||
"Command to open an Desktop Entry that is a Link.", CONFIG_DEFAULT },
|
||||
|
||||
{ xrm_Boolean, "disable-history", { .num = &config.disable_history }, NULL,
|
||||
"Disable history in run/ssh", CONFIG_DEFAULT },
|
||||
{ xrm_String, "ignored-prefixes", { .str = &config.ignored_prefixes }, NULL,
|
||||
|
|
Loading…
Reference in a new issue