summaryrefslogtreecommitdiff
path: root/libmdock/matcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmdock/matcher.c')
-rw-r--r--libmdock/matcher.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/libmdock/matcher.c b/libmdock/matcher.c
new file mode 100644
index 0000000..25e2b2f
--- /dev/null
+++ b/libmdock/matcher.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2015 Javier S. Pedro <dev.git@javispedro.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <ftw.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#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 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)) {
+ add_desktop_item_by_exec(executable, info, 4000);
+ } else {
+ gchar *execpath = g_find_program_in_path(executable);
+ if (execpath) {
+ add_desktop_item_by_exec(execpath, info, 0);
+ } 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]) {
+ add_desktop_item_by_wmclass(wmclass, info, 128000);
+ }
+
+ if (executable && executable[0]) {
+ gchar *basename = g_path_get_basename(executable);
+ add_desktop_item_by_wmclass(basename, info, -32000);
+ g_free(basename);
+ }
+
+ 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 = g_list_first(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();
+ }
+
+ 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_application_to_desktop_file(AppId *appid)
+{
+ if (!init_matcher()) return NULL;
+
+ GSList *list = NULL;
+
+ if (appid->executable) {
+ list = g_slist_concat(list, matches_by_executable(appid->executable));
+ }
+ if (appid->wm_class_name) {
+ list = g_slist_concat(list, matches_by_wmclass(appid->wm_class_name));
+ }
+
+ if (list) {
+ list = g_slist_sort(list, desktop_item_compare);
+ DesktopItem *best = list->data;
+ g_slist_free(list);
+ return best->desktop_file;
+ } else {
+ return NULL;
+ }
+}