mirror of
https://github.com/swaywm/sway.git
synced 2025-10-31 22:25:26 -04:00
Implement Tray Icons
This commit implements the StatusNotifierItem protocol, and enables swaybar to show tray icons. It also uses `xembedsniproxy` in order to communicate with xembed applications. The tray is completely optional, and can be disabled on compile time with the `enable-tray` option. Or on runtime with the bar config option `tray_output none`. Overview of changes: In swaybar very little is changed outside the tray subfolder except that all events are now polled in `event_loop.c`, this creates no functional difference. Six bar configuration options were added, these are detailed in sway-bar(5) The tray subfolder is where all protocol implementation takes place and is organised as follows: tray/sni_watcher.c: This file contains the StatusNotifierWatcher. It keeps track of items and hosts and reports when they come or go. tray/tray.c This file contains the StatusNotifierHost. It keeps track of sway's version of the items and represents the tray itself. tray/sni.c This file contains the StatusNotifierItem struct and all communication with individual items. tray/icon.c This file implements the icon theme protocol. It allows for finding icons by name, rather than by pixmap. tray/dbus.c This file allows for asynchronous DBus communication. See #986 #343
This commit is contained in:
parent
fd47a30e75
commit
843ad38b3c
35 changed files with 2714 additions and 58 deletions
487
swaybar/tray/sni_watcher.c
Normal file
487
swaybar/tray/sni_watcher.c
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include "swaybar/tray/dbus.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
static list_t *items = NULL;
|
||||
static list_t *hosts = NULL;
|
||||
|
||||
/**
|
||||
* Describes the function of the StatusNotifierWatcher
|
||||
* See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
|
||||
*
|
||||
* We also implement KDE's special snowflake protocol, it's like this but with
|
||||
* all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
|
||||
*/
|
||||
static const char *interface_xml =
|
||||
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
|
||||
"'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
|
||||
"<node>"
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>"
|
||||
" <method name='Introspect'>"
|
||||
" <arg name='xml_data' direction='out' type='s'/>"
|
||||
" </method>"
|
||||
" </interface>"
|
||||
" <interface name='org.freedesktop.DBus.Properties'>"
|
||||
" <method name='Get'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='propname' direction='in' type='s'/>"
|
||||
" <arg name='value' direction='out' type='v'/>"
|
||||
" </method>"
|
||||
" <method name='Set'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='propname' direction='in' type='s'/>"
|
||||
" <arg name='value' direction='in' type='v'/>"
|
||||
" </method>"
|
||||
" <method name='GetAll'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='props' direction='out' type='a{sv}'/>"
|
||||
" </method>"
|
||||
" </interface>"
|
||||
" <interface name='org.freedesktop.StatusNotifierWatcher'>"
|
||||
" <method name='RegisterStatusNotifierItem'>"
|
||||
" <arg type='s' name='service' direction='in'/>"
|
||||
" </method>"
|
||||
" <method name='RegisterStatusNotifierHost'>"
|
||||
" <arg type='s' name='service' direction='in'/>"
|
||||
" </method>"
|
||||
" <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
|
||||
" <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
|
||||
" <property name='ProtocolVersion' type='i' access='read'/>"
|
||||
" <signal name='StatusNotifierItemRegistered'>"
|
||||
" <arg type='s' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" <signal name='StatusNotifierItemUnregistered'>"
|
||||
" <arg type='s' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" <signal name='StatusNotifierHostRegistered'>"
|
||||
" <arg type='' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
|
||||
static void host_registered_signal(DBusConnection *connection) {
|
||||
// Send one signal for each protocol
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierHostRegistered");
|
||||
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierHostRegistered");
|
||||
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
static void item_registered_signal(DBusConnection *connection, const char *name) {
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemRegistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierItemRegistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
static void item_unregistered_signal(DBusConnection *connection, const char *name) {
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemUnregistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierItemUnregistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
|
||||
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
|
||||
DBusMessage *reply;
|
||||
|
||||
reply = dbus_message_new_method_return(request);
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &interface_xml,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static void register_item(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *name;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
|
||||
}
|
||||
|
||||
name = strdup(name);
|
||||
sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
|
||||
|
||||
// Don't add duplicate or not real item
|
||||
if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
||||
return;
|
||||
}
|
||||
if (!dbus_bus_name_has_owner(connection, name, &error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(items, name);
|
||||
item_registered_signal(connection, name);
|
||||
|
||||
// It's silly, but xembedsniproxy wants a reply for this function
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static void register_host(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *name;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
|
||||
|
||||
// Don't add duplicate or not real host
|
||||
if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
||||
return;
|
||||
}
|
||||
if (!dbus_bus_name_has_owner(connection, name, &error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(hosts, strdup(name));
|
||||
host_registered_signal(connection);
|
||||
}
|
||||
|
||||
static void get_property(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *interface;
|
||||
char *property;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
|
||||
sway_log(L_INFO, "Replying with items\n");
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
DBusMessageIter subsub;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"as", &sub);
|
||||
dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
|
||||
"s", &subsub);
|
||||
|
||||
for (int i = 0; i < items->length; ++i) {
|
||||
dbus_message_iter_append_basic(&subsub,
|
||||
DBUS_TYPE_STRING, &items->items[i]);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&sub, &subsub);
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
} else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
|
||||
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"b", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_BOOLEAN, ®istered);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
} else if (strcmp(property, "ProtocolVersion") == 0) {
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
const int version = 0;
|
||||
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"i", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_INT32, &version);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_property(DBusConnection *connection, DBusMessage *message) {
|
||||
// All properties are read only and we don't allow new properties
|
||||
return;
|
||||
}
|
||||
|
||||
static void get_all(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter; /* a{v} */
|
||||
DBusMessageIter arr;
|
||||
DBusMessageIter dict;
|
||||
DBusMessageIter sub;
|
||||
DBusMessageIter subsub;
|
||||
int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
|
||||
const int version = 0;
|
||||
const char *prop;
|
||||
|
||||
// Could clean this up with a function for each prop
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
"{sv}", &arr);
|
||||
|
||||
prop = "RegisteredStatusNotifierItems";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"as", &sub);
|
||||
dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
|
||||
"s", &subsub);
|
||||
for (int i = 0; i < items->length; ++i) {
|
||||
dbus_message_iter_append_basic(&subsub,
|
||||
DBUS_TYPE_STRING, &items->items[i]);
|
||||
}
|
||||
dbus_message_iter_close_container(&sub, &subsub);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
prop = "IsStatusNotifierHostRegistered";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"b", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_BOOLEAN, ®istered);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
prop = "ProtocolVersion";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"i", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_INT32, &version);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &arr);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
DBusMessage *message, void *data) {
|
||||
const char *interface_name = dbus_message_get_interface(message);
|
||||
const char *member_name = dbus_message_get_member(message);
|
||||
|
||||
// In order of the xml above
|
||||
if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
|
||||
strcmp(member_name, "Introspect") == 0) {
|
||||
// We don't have an introspect for KDE
|
||||
respond_to_introspect(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
|
||||
if (strcmp(member_name, "Get") == 0) {
|
||||
get_property(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "Set") == 0) {
|
||||
set_property(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "GetAll") == 0) {
|
||||
get_all(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
} else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
|
||||
strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
|
||||
if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
|
||||
register_item(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
|
||||
register_host(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||
DBusMessage *message, void *_data) {
|
||||
if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
|
||||
// Only eat the message if it is name that we are watching
|
||||
const char *name;
|
||||
const char *old_owner;
|
||||
const char *new_owner;
|
||||
int index;
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_STRING, &old_owner,
|
||||
DBUS_TYPE_STRING, &new_owner,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error getting LostName args");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if (strcmp(new_owner, "") != 0) {
|
||||
// Name is not lost
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
|
||||
sway_log(L_INFO, "Status Notifier Item lost %s", name);
|
||||
free(items->items[index]);
|
||||
list_del(items, index);
|
||||
item_unregistered_signal(connection, name);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
|
||||
sway_log(L_INFO, "Status Notifier Host lost %s", name);
|
||||
free(hosts->items[index]);
|
||||
list_del(hosts, index);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static const DBusObjectPathVTable vtable = {
|
||||
.message_function = message_handler,
|
||||
.unregister_function = NULL,
|
||||
};
|
||||
|
||||
int init_sni_watcher() {
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
if (!conn) {
|
||||
sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
|
||||
return -1;
|
||||
}
|
||||
|
||||
items = create_list();
|
||||
hosts = create_list();
|
||||
|
||||
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
||||
&error);
|
||||
if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
sway_log(L_DEBUG, "Got watcher name");
|
||||
} else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
|
||||
sway_log(L_INFO, "Could not get watcher name, it may start later");
|
||||
}
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
||||
&error);
|
||||
if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
sway_log(L_DEBUG, "Got kde watcher name");
|
||||
} else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
|
||||
sway_log(L_INFO, "Could not get kde watcher name, it may start later");
|
||||
}
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_connection_try_register_object_path(conn,
|
||||
"/StatusNotifierWatcher",
|
||||
&vtable, NULL, &error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
sender='org.freedesktop.DBus',\
|
||||
interface='org.freedesktop.DBus',\
|
||||
member='NameOwnerChanged'",
|
||||
&error);
|
||||
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
|
||||
}
|
||||
|
||||
dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue