/* * Copyright 2015 Javier S. Pedro * * This file is part of MDock. * * MDock is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MDock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MDock. If not, see . */ #include #include #include #include #include #include #include "matcher.h" static gboolean init_done = FALSE; static GAppInfoMonitor *appinfo_monitor; static guint refresh_timeout; static GHashTable *info_by_exec; static GHashTable *info_by_wmclass; typedef struct _DesktopItem { gint priority; gchar *desktop_file; } DesktopItem; typedef struct _DesktopItemList { GSList *list; } DesktopItemList; static DesktopItem * desktop_item_create() { return g_slice_new(DesktopItem); } static void desktop_item_destroy(DesktopItem *item) { g_free(item->desktop_file); g_slice_free(DesktopItem, item); } static gint desktop_item_compare(gconstpointer ap, gconstpointer bp) { const DesktopItem *a = ap, *b = bp; return b->priority - a->priority; } static void desktop_item_list_destroy(DesktopItemList *l) { g_slist_free_full(l->list, (GDestroyNotify)desktop_item_destroy); } static gint compute_priority(GDesktopAppInfo *info, gint base_priority) { gint prio = base_priority; if (g_app_info_should_show(G_APP_INFO(info))) { prio += 32000; } const gchar *cmdline = g_app_info_get_commandline(G_APP_INFO(info)); prio -= strlen(cmdline); return prio; } static void add_desktop_item_by_exec(const gchar *exec, GDesktopAppInfo *info, gint base_priority) { DesktopItem *item = desktop_item_create(); item->priority = compute_priority(info, base_priority); item->desktop_file = g_strdup(g_app_info_get_id(G_APP_INFO(info))); DesktopItemList *elist = g_hash_table_lookup(info_by_exec, exec); if (!elist) { elist = g_slice_new0(DesktopItemList); g_hash_table_insert(info_by_exec, g_strdup(exec), elist); } elist->list = g_slist_append(elist->list, item); } static void add_desktop_item_by_wmclass(const gchar *wmclass, GDesktopAppInfo *info, gint base_priority) { DesktopItem *item = desktop_item_create(); item->priority = compute_priority(info, base_priority); item->desktop_file = g_strdup(g_app_info_get_id(G_APP_INFO(info))); DesktopItemList *elist = g_hash_table_lookup(info_by_wmclass, wmclass); if (!elist) { elist = g_slice_new0(DesktopItemList); g_hash_table_insert(info_by_wmclass, g_strdup(wmclass), elist); } elist->list = g_slist_append(elist->list, item); } static gchar *resolve_path(const gchar *path) { char *buf = realpath(path, NULL); if (buf) { gchar *str = g_strdup(buf); free(buf); return str; } else { return NULL; } } static void add_desktop_info(GDesktopAppInfo *info) { const char *quoted_executable = g_app_info_get_executable(G_APP_INFO(info)); gchar *executable = NULL; if (quoted_executable) { executable = g_shell_unquote(quoted_executable, NULL); } if (executable && executable[0]) { if (g_path_is_absolute(executable)) { gchar *resolved_exec = resolve_path(executable); add_desktop_item_by_exec(resolved_exec, info, 4000); g_free(resolved_exec); } else { gchar *execpath = g_find_program_in_path(executable); if (execpath) { gchar *resolved_exec = resolve_path(execpath); if (resolved_exec) { add_desktop_item_by_exec(resolved_exec, info, 0); } else { g_warning("Could not realpath executable: '%s'", execpath); } g_free(resolved_exec); } else { g_warning("Could not find executable in path: '%s'", executable); } g_free(execpath); } } const gchar *wmclass = g_desktop_app_info_get_startup_wm_class(info); if (wmclass && wmclass[0]) { gchar *wmclass_lower = g_ascii_strdown(wmclass, -1); add_desktop_item_by_wmclass(wmclass_lower, info, 128000); g_free(wmclass_lower); } if (executable && executable[0]) { gchar *basename = g_path_get_basename(executable); gchar *basename_lower = g_ascii_strdown(basename, -1); add_desktop_item_by_wmclass(basename_lower, info, -32000); g_free(basename); g_free(basename_lower); } g_free(executable); } static void refresh_appinfo() { g_debug("Refreshing application info..."); g_hash_table_remove_all(info_by_exec); g_hash_table_remove_all(info_by_wmclass); GList *list = g_app_info_get_all(); for (GList *l = list; l; l = g_list_next(l)) { add_desktop_info(G_DESKTOP_APP_INFO(l->data)); } g_list_free_full(list, g_object_unref); } static gboolean appinfo_timeout(gpointer user_data) { refresh_appinfo(); refresh_timeout = 0; return G_SOURCE_REMOVE; } static void appinfo_changed(GAppInfoMonitor *monitor, gpointer user_data) { g_debug("Appinfo changed"); if (refresh_timeout == 0) { refresh_timeout = g_timeout_add_seconds(10, appinfo_timeout, 0); } } static gboolean init_matcher(void) { if (!init_done) { refresh_timeout = 0; appinfo_monitor = g_app_info_monitor_get(); g_signal_connect(appinfo_monitor, "changed", G_CALLBACK(appinfo_changed), 0); info_by_exec = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)desktop_item_list_destroy); info_by_wmclass = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)desktop_item_list_destroy); refresh_appinfo(); init_done = TRUE; } return TRUE; } static GSList * matches_by_executable(const gchar *exec) { DesktopItemList *elist = g_hash_table_lookup(info_by_exec, exec); if (elist) { return g_slist_copy(elist->list); } else { return NULL; } } static GSList * matches_by_wmclass(const gchar *wmclass) { DesktopItemList *elist = g_hash_table_lookup(info_by_wmclass, wmclass); if (elist) { return g_slist_copy(elist->list); } else { return NULL; } } const gchar * match_appid_to_desktopid(AppId *appid) { if (!init_matcher()) return NULL; if (!app_id_is_local_user(appid)) return NULL; GSList *list = NULL; if (appid->executable) { list = g_slist_concat(list, matches_by_executable(appid->executable)); } if (appid->wm_class_class) { list = g_slist_concat(list, matches_by_wmclass(appid->wm_class_class)); } if (appid->wm_class_name && (g_strcmp0(appid->wm_class_class, appid->wm_class_name) != 0)) { list = g_slist_concat(list, matches_by_wmclass(appid->wm_class_name)); } if (list) { list = g_slist_sort(list, desktop_item_compare); #if 0 g_debug("for appid with wmclass %s", appid->wm_class_class); for (GSList *l = list; l; l = l->next) { DesktopItem *item = l->data; g_debug(" match %d %s", item->priority, item->desktop_file); } #endif DesktopItem *best = list->data; g_slist_free(list); return best->desktop_file; } else { return NULL; } }