Skip to content

Commit

Permalink
Unity badges without Plank (#601)
Browse files Browse the repository at this point in the history
Co-authored-by: Leo <[email protected]>
  • Loading branch information
danirabbit and lenemter authored Aug 14, 2024
1 parent ff184bc commit 3dc8702
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 43 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ You'll need the following dependencies:
* libgtk-3-dev
* libhandy-1-dev >= 0.83.0
* libjson-glib-dev
* libplank-dev
* libsoup2.4-dev
* libswitchboard-3-dev
* libunity-dev
Expand Down
5 changes: 0 additions & 5 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ libhandy_dep = dependency('libhandy-1', version: '>= 0.83.0')
wingpanel_dep = dependency('wingpanel', version: '>=2.1.0')
posix_dep = meson.get_compiler('vala').find_library('posix')

plank_dep = dependency('plank', required: false)
if plank_dep.version().version_compare('>=0.10.9')
add_project_arguments('--define=HAS_PLANK', language: 'vala')
endif

zeitgeist_dep = []

if get_option('with-zeitgeist')
Expand Down
4 changes: 0 additions & 4 deletions src/Backend/App.vala
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ public class Slingshot.Backend.App : Object {
public bool prefers_default_gpu { get; private set; default = false; }
public AppType app_type { get; private set; default = AppType.APP; }

#if HAS_PLANK
private string? unity_sender_name = null;
public bool count_visible { get; private set; default = false; }
public int64 current_count { get; private set; default = 0; }
#endif

public Synapse.Match? match { get; private set; default = null; }
public Synapse.Match? target { get; private set; default = null; }
Expand Down Expand Up @@ -165,7 +163,6 @@ public class Slingshot.Backend.App : Object {
return true;
}

#if HAS_PLANK
public void perform_unity_update (string sender_name, VariantIter prop_iter) {
unity_sender_name = sender_name;

Expand All @@ -187,5 +184,4 @@ public class Slingshot.Backend.App : Object {
current_count = 0;
}
}
#endif
}
270 changes: 270 additions & 0 deletions src/Backend/Unity.vala
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;
});
}
}
11 changes: 2 additions & 9 deletions src/Indicator.vala
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if HAS_PLANK
[CCode (cname = "PKGDATADIR")]
private extern const string PKGDATADIR;
#endif

public class Slingshot.Indicator : Wingpanel.Indicator {
private const string KEYBINDING_SCHEMA = "org.pantheon.desktop.gala.keybindings";
private const string GALA_BEHAVIOR_SCHEMA = "org.pantheon.desktop.gala.behavior";
Expand Down Expand Up @@ -62,10 +57,8 @@ public class Slingshot.Indicator : Wingpanel.Indicator {
if (view == null) {
view = new SlingshotView ();

#if HAS_PLANK
unowned Plank.Unity client = Plank.Unity.get_default ();
client.add_client (view);
#endif
unowned var unity_client = Unity.get_default ();
unity_client.add_client (view);

view.close_indicator.connect (on_close_indicator);

Expand Down
8 changes: 1 addition & 7 deletions src/SlingshotView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if HAS_PLANK
public class Slingshot.SlingshotView : Gtk.Grid, Plank.UnityClient {
#else
public class Slingshot.SlingshotView : Gtk.Grid {
#endif
public class Slingshot.SlingshotView : Gtk.Grid, UnityClient {
public signal void close_indicator ();

public Backend.AppSystem app_system;
Expand Down Expand Up @@ -168,7 +164,6 @@ public class Slingshot.SlingshotView : Gtk.Grid {
});
}

#if HAS_PLANK
public void update_launcher_entry (string sender_name, GLib.Variant parameters, bool is_retry = false) {
if (!is_retry) {
// Wait to let further update requests come in to catch the case where one application
Expand Down Expand Up @@ -197,7 +192,6 @@ public class Slingshot.SlingshotView : Gtk.Grid {
app.remove_launcher_entry (sender_name);
}
}
#endif

private void search_entry_activated () {
if (modality == Modality.SEARCH_VIEW) {
Expand Down
Loading

0 comments on commit 3dc8702

Please sign in to comment.