swaybar: Emit property changes for SNI watcher

Emit property change signals for the IsStatusNotifierHostRegistered and
RegisteredStatusNotifierItems properties in StatusNotifierWatcher,
so code relying on the PropertiesChanged signal, instead of signals
such as StatusNotifierHostRegistered, can work properly.

A library that is affected by this is the libappindicator-gtk3* library
and it can cause tray icons to be missing after starting swaybar due to
a race condition, as follows:
* An application using libappindicator-gtk3 starts, e.g. nm-applet.
* Some time later, swaybar starts.
* swaybar creates the StatusNotifierWatcher.
* libappindicator-gtk3 observes the new watcher, but it sees that
  IsStatusNotifierHostRegistered=false, so it falls back to the
  Freedesktop System tray protocol.
* swaybar creates the StatusNotifierHost.
  At this point, libappindicator-gtk3 should "un-fallback" back to SNI.
  However, since swaybar does not emit the PropertiesChange signal on
  IsStatusNotifierHostRegistered, libappindicator-gtk3 doesn't get
  notified, and stays in fallback state forever.
* As a result, nm-applet will not show in the swaybar tray.

This race can be made reliable by inserting a 1-second long sleep here:
03483ff370/swaybar/tray/tray.c (L57)

(*) Note that the libappindicator-gtk3 library has been mostly replaced
    by libayatana-appindicator, which is not affected by this.
    The affected version is still used by Arch Linux, source code at:
    https://bazaar.launchpad.net/~indicator-applet-developers/libappindicator/trunk/files/298
This commit is contained in:
Joan Bruguera Micó 2024-11-10 15:24:15 +00:00 committed by Simon Ser
parent 03483ff370
commit f23d100747

View file

@ -38,6 +38,8 @@ static int handle_lost_service(sd_bus_message *msg,
list_del(watcher->items, idx--); list_del(watcher->items, idx--);
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierItemUnregistered", "s", id); "StatusNotifierItemUnregistered", "s", id);
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"RegisteredStatusNotifierItems", NULL);
free(id); free(id);
if (using_standard_protocol(watcher)) { if (using_standard_protocol(watcher)) {
break; break;
@ -50,6 +52,10 @@ static int handle_lost_service(sd_bus_message *msg,
sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service);
free(watcher->hosts->items[idx]); free(watcher->hosts->items[idx]);
list_del(watcher->hosts, idx); list_del(watcher->hosts, idx);
if (watcher->hosts->length == 0) {
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"IsStatusNotifierHostRegistered", NULL);
}
} }
} }
@ -82,6 +88,8 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
if (list_seq_find(watcher->items, cmp_id, id) == -1) { if (list_seq_find(watcher->items, cmp_id, id) == -1) {
sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id); sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id);
list_add(watcher->items, id); list_add(watcher->items, id);
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"RegisteredStatusNotifierItems", NULL);
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierItemRegistered", "s", id); "StatusNotifierItemRegistered", "s", id);
} else { } else {
@ -104,6 +112,10 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { if (list_seq_find(watcher->hosts, cmp_id, service) == -1) {
sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service);
list_add(watcher->hosts, strdup(service)); list_add(watcher->hosts, strdup(service));
if (watcher->hosts->length == 1) {
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"IsStatusNotifierHostRegistered", NULL);
}
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierHostRegistered", ""); "StatusNotifierHostRegistered", "");
} else { } else {