diff --git a/lib/AppSystem.vala b/lib/AppSystem.vala index 65b7cbd4a..d2510904f 100644 --- a/lib/AppSystem.vala +++ b/lib/AppSystem.vala @@ -14,12 +14,64 @@ public class Gala.AppSystem : GLib.Object { private GLib.HashTable id_to_app; private GLib.HashTable startup_wm_class_to_id; private Gala.AppCache app_cache; + private string[] all_desktop_files = {}; + private GLib.FileMonitor[]? directory_monitors; construct { id_to_app = new GLib.HashTable (str_hash, str_equal); startup_wm_class_to_id = new GLib.HashTable (str_hash, str_equal); running_apps = new GLib.HashTable (null, null); app_cache = new AppCache (); + + update_desktop_files (); + } + + private void update_desktop_files () { + var data_dirs = Environment.get_system_data_dirs (); + data_dirs += Environment.get_user_data_dir (); + + var create_monitors = directory_monitors == null; + if (create_monitors) { + directory_monitors = {}; + } + + foreach (unowned string data_dir in data_dirs) { + var app_dir = Path.build_filename (data_dir, "applications"); + if (FileUtils.test (app_dir, FileTest.EXISTS)) { + try { + foreach (var name in enumerate_children (app_dir)) { + if (!name.contains ("~") && name.has_suffix (".desktop")) { + all_desktop_files += name; + } + } + + if (!create_monitors) { + continue; + } + + var monitor = File.new_for_path (app_dir).monitor (GLib.FileMonitorFlags.NONE, null); + monitor.changed.connect ((file, other_file, event_type) => { + if (event_type == GLib.FileMonitorEvent.CHANGES_DONE_HINT) { + update_desktop_files (); + } + }); + directory_monitors += monitor; + } catch (Error e) { + debug ("Error inside %s: %s", app_dir, e.message); + } + } + } + } + + private string[] enumerate_children (string dir) throws Error { + string[] result = {}; + FileInfo? file_info; + var enumerator = File.new_for_path (dir).enumerate_children (FileAttribute.STANDARD_NAME, 0); + while ((file_info = enumerator.next_file ()) != null) { + result += file_info.get_name (); + } + + return result; } public unowned Gala.App? lookup_app (string id) { @@ -105,6 +157,20 @@ public class Gala.AppSystem : GLib.Object { return lookup_heuristic_basename (desktop_file); } + public unowned Gala.App? guess_app_by_id (string _id) { + var id = _id.ascii_down (); + unowned Gala.App? result = null; + + foreach (var name in all_desktop_files) { + // Try to find desktop file based on the application name + if (name.contains (id) && (result = lookup_app (name)) != null) { + return result; + } + } + + return null; + } + public void notify_app_state_changed (Gala.App app) { if (app.state == Gala.AppState.RUNNING) { running_apps.insert (app, app); diff --git a/src/WindowTracker.vala b/src/WindowTracker.vala index 29f68aee6..4912130b5 100644 --- a/src/WindowTracker.vala +++ b/src/WindowTracker.vala @@ -199,6 +199,34 @@ public class Gala.WindowTracker : GLib.Object { return null; } + private unowned Gala.App? get_app_by_guessing (Meta.Window window) { + unowned var app_system = Gala.AppSystem.get_default (); + unowned Gala.App? result = null; + + unowned string? id = window.get_gtk_application_id (); + // if id contains '.' it's probably a valid app id but it doesn't provide the desktop file + if (id != null && !id.contains (".") && (result = app_system.guess_app_by_id (id)) != null) { + return result; + } + + id = window.get_sandboxed_app_id (); + if (id != null && !id.contains (".") && (result = app_system.guess_app_by_id (id)) != null) { + return result; + } + + id = window.get_wm_class (); + if (id != null && !id.contains (".") && (result = app_system.guess_app_by_id (id)) != null) { + return result; + } + + id = window.get_wm_class_instance (); + if (id != null && !id.contains (".") && (result = app_system.guess_app_by_id (id)) != null) { + return result; + } + + return null; + } + public Gala.App get_app_for_window (Meta.Window window) { unowned Meta.Window? transient_for = window.get_transient_for (); if (transient_for != null) { @@ -279,9 +307,15 @@ public class Gala.WindowTracker : GLib.Object { return result; } + /* Try to carefully guess .desktop file name based on application ids and wm class + */ + result = get_app_by_guessing (window); + if (result != null) { + return result; + } + /* Our last resort - we create a fake app from the window */ return new Gala.App.for_window (window); - } private void tracked_window_changed (Meta.Window window) {