-
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
8 changed files
with
275 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
/* | ||
* SPDX-License-Identifier: GPL-3.0+ | ||
* SPDX-FileCopyrightText: 2023 elementary, Inc. (https://elementary.io) | ||
* 2015 Rico Tzschichholz | ||
*/ | ||
|
||
/** | ||
* The interface to provide the LauncherEntry handling. | ||
*/ | ||
public interface Slingshot.UnityClient : Object { | ||
/** | ||
* The LauncherEntry corresponding to the sender_name requested an update | ||
* | ||
* @param sender_name the dbusname | ||
* @param parameters the data in a standardize format '(sa{sv})' from libunity | ||
* @param is_retry whether this data was already processed before and decided to give is another run | ||
*/ | ||
public abstract void update_launcher_entry (string sender_name, Variant parameters, bool is_retry = false); | ||
|
||
/** | ||
* The LauncherEntry corresponding to the sender_name vanished | ||
* | ||
* @param sender_name the dbusname | ||
*/ | ||
public abstract void remove_launcher_entry (string sender_name); | ||
} | ||
|
||
/** | ||
* Handle the LauncherEntry DBus interface implemented by applications. | ||
*/ | ||
public class Slingshot.Unity : Object { | ||
private class LauncherEntry { | ||
public uint fast_count = 0U; | ||
public int64 last_update = 0LL; | ||
public string? sender_name; | ||
public Variant? parameters; | ||
public uint timer_id = 0U; | ||
public bool warned = false; | ||
} | ||
|
||
private const string DBUS_NAME = "com.canonical.Unity"; | ||
private const string INTERFACE_NAME = "com.canonical.Unity.LauncherEntry"; | ||
private const string SIGNAL_NAME = "Update"; | ||
|
||
private const uint UNITY_UPDATE_THRESHOLD_DURATION = 32; | ||
private const uint UNITY_UPDATE_THRESHOLD_FAST_COUNT = 3; | ||
|
||
private static Unity? instance = null; | ||
private static DBusConnection connection = null; | ||
private static uint unity_bus_id = 0U; | ||
private static VariantType payload_variant_type; | ||
|
||
public static unowned Unity get_default () { | ||
if (instance == null) | ||
instance = new Unity (); | ||
|
||
return instance; | ||
} | ||
|
||
static construct { | ||
acquire_unity_dbus (); | ||
payload_variant_type = new VariantType ("(sa{sv})"); | ||
} | ||
|
||
/** | ||
* Connect DBus connection and try to aquire unity busname | ||
*/ | ||
private static void acquire_unity_dbus () { | ||
// Initialize Unity DBus | ||
try { | ||
if (connection == null) { | ||
connection = Bus.get_sync (BusType.SESSION, null); | ||
} | ||
} catch (Error e) { | ||
warning (e.message); | ||
return; | ||
} | ||
|
||
if (unity_bus_id == 0U) { | ||
// Acquire Unity bus-name to activate libunity clients since normally there shouldn't be a running Unity | ||
unity_bus_id = Bus.own_name (BusType.SESSION, DBUS_NAME, BusNameOwnerFlags.ALLOW_REPLACEMENT, | ||
(BusAcquiredCallback) handle_bus_acquired, (BusNameAcquiredCallback) handle_name_acquired, | ||
(BusNameLostCallback) handle_name_lost); | ||
} | ||
} | ||
|
||
private static void handle_bus_acquired (DBusConnection conn, string name) { | ||
// Nothing here since we just want to provide this bus without any functionality | ||
} | ||
|
||
private static void handle_name_acquired (DBusConnection conn, string name) { | ||
debug ("%s acquired", name); | ||
} | ||
|
||
private static void handle_name_lost (DBusConnection conn, string name) { | ||
if (conn == null) { | ||
warning ("%s failed", name); | ||
} else { | ||
debug ("%s lost", name); | ||
} | ||
} | ||
|
||
private Gee.HashSet<UnityClient> clients; | ||
|
||
private uint launcher_entry_dbus_signal_id = 0U; | ||
private uint dbus_name_owner_changed_signal_id = 0U; | ||
private Gee.HashMap<string, LauncherEntry> launcher_entries; | ||
private uint launcher_entries_timer_id = 0U; | ||
|
||
construct { | ||
clients = new Gee.HashSet<UnityClient> (); | ||
launcher_entries = new Gee.HashMap<string, LauncherEntry> (); | ||
|
||
acquire_unity_dbus (); | ||
|
||
if (connection != null) { | ||
launcher_entry_dbus_signal_id = connection.signal_subscribe ( | ||
null, INTERFACE_NAME, null, null, null, DBusSignalFlags.NONE, (DBusSignalCallback) handle_entry_signal | ||
); | ||
|
||
dbus_name_owner_changed_signal_id = connection.signal_subscribe ( | ||
"org.freedesktop.DBus", "org.freedesktop.DBus", "NameOwnerChanged", | ||
"/org/freedesktop/DBus", null, DBusSignalFlags.NONE, (DBusSignalCallback) handle_name_owner_changed | ||
); | ||
} | ||
} | ||
|
||
~Unity () { | ||
if (launcher_entries_timer_id > 0U) { | ||
Source.remove (launcher_entries_timer_id); | ||
} | ||
|
||
clients = null; | ||
launcher_entries = null; | ||
|
||
if (unity_bus_id > 0U) { | ||
Bus.unown_name (unity_bus_id); | ||
} | ||
|
||
if (connection != null) { | ||
if (launcher_entry_dbus_signal_id > 0U) { | ||
connection.signal_unsubscribe (launcher_entry_dbus_signal_id); | ||
} | ||
|
||
if (dbus_name_owner_changed_signal_id > 0U) { | ||
connection.signal_unsubscribe (dbus_name_owner_changed_signal_id); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Add a client which will receive all update requests of running LauncherEntry applications. | ||
* | ||
* @param client the client to add | ||
*/ | ||
public void add_client (UnityClient client) { | ||
clients.add (client); | ||
} | ||
|
||
/** | ||
* Remove a client. | ||
* | ||
* @param client the client to remove | ||
*/ | ||
public void remove_client (UnityClient client) { | ||
clients.remove (client); | ||
} | ||
|
||
[CCode (instance_pos = -1)] | ||
private void handle_entry_signal ( | ||
DBusConnection connection, string sender_name, string object_path, | ||
string interface_name, string signal_name, Variant parameters | ||
) { | ||
if (parameters == null || signal_name == null || sender_name == null) { | ||
return; | ||
} | ||
|
||
if (signal_name == SIGNAL_NAME) { | ||
handle_update_request (sender_name, parameters); | ||
} | ||
} | ||
|
||
[CCode (instance_pos = -1)] | ||
private void handle_name_owner_changed ( | ||
DBusConnection connection, string sender_name, string object_path, | ||
string interface_name, string signal_name, Variant parameters | ||
) { | ||
string name, before, after; | ||
parameters.get ("(sss)", out name, out before, out after); | ||
|
||
if (after != null && after != "") { | ||
return; | ||
} | ||
|
||
clients.foreach ((client) => { | ||
client.remove_launcher_entry (name); | ||
return true; | ||
}); | ||
} | ||
|
||
private void handle_update_request (string sender_name, Variant parameters) { | ||
var current_time = GLib.get_monotonic_time (); | ||
LauncherEntry? entry; | ||
if ((entry = launcher_entries.get (sender_name)) != null) { | ||
entry.parameters = parameters; | ||
if (current_time - entry.last_update < UNITY_UPDATE_THRESHOLD_DURATION * 1000 | ||
&& entry.fast_count > UNITY_UPDATE_THRESHOLD_FAST_COUNT) { | ||
if (entry.timer_id <= 0U) { | ||
if (!entry.warned) { | ||
warning ("LauncherEntry '%s' is behaving badly, skipping requests", sender_name); | ||
entry.warned = true; | ||
} | ||
|
||
entry.timer_id = Timeout.add (UNITY_UPDATE_THRESHOLD_DURATION, () => { | ||
entry.timer_id = 0U; | ||
entry.last_update = GLib.get_monotonic_time (); | ||
perform_update (entry.sender_name, entry.parameters); | ||
return false; | ||
}); | ||
} | ||
} else { | ||
entry.fast_count++; | ||
entry.last_update = current_time; | ||
perform_update (entry.sender_name, entry.parameters); | ||
} | ||
} else { | ||
entry = new LauncherEntry (); | ||
entry.fast_count++; | ||
entry.last_update = current_time; | ||
entry.sender_name = sender_name; | ||
entry.parameters = parameters; | ||
launcher_entries.set (sender_name, entry); | ||
perform_update (sender_name, parameters); | ||
} | ||
|
||
if (launcher_entries_timer_id <= 0U) { | ||
launcher_entries_timer_id = Timeout.add (60 * 1000, (SourceFunc) clean_up_launcher_entries); | ||
} | ||
} | ||
|
||
private bool clean_up_launcher_entries () { | ||
var current_time = GLib.get_monotonic_time (); | ||
|
||
var launcher_entries_it = launcher_entries.map_iterator (); | ||
while (launcher_entries_it.next ()) { | ||
var entry = launcher_entries_it.get_value (); | ||
if (current_time - entry.last_update > 10 * UNITY_UPDATE_THRESHOLD_DURATION * 1000) | ||
launcher_entries_it.unset (); | ||
} | ||
|
||
var keep_running = (launcher_entries.size > 0); | ||
if (!keep_running) { | ||
launcher_entries_timer_id = 0U; | ||
} | ||
|
||
return keep_running; | ||
} | ||
|
||
private void perform_update (string sender_name, Variant parameters) { | ||
if (!parameters.is_of_type (payload_variant_type)) { | ||
warning ("Illegal payload signature '%s' from %s. expected '(sa{sv})'", parameters.get_type_string (), sender_name); | ||
return; | ||
} | ||
|
||
clients.foreach ((client) => { | ||
client.update_launcher_entry (sender_name, parameters); | ||
return true; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.