diff options
author | Calvin Lee <cyrus296@gmail.com> | 2017-06-07 16:45:28 -0700 |
---|---|---|
committer | Calvin Lee <cyrus296@gmail.com> | 2017-06-07 17:49:16 -0700 |
commit | 843ad38b3c427adb0bf319e9613d9813c8d9246c (patch) | |
tree | e02a5b06e2b6923371fd53724791c147c18a1fa4 /swaybar/tray/dbus.c | |
parent | fd47a30e75cccfc3789eafa4bfabd66f4696099b (diff) |
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
Diffstat (limited to 'swaybar/tray/dbus.c')
-rw-r--r-- | swaybar/tray/dbus.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c new file mode 100644 index 00000000..333d398e --- /dev/null +++ b/swaybar/tray/dbus.c @@ -0,0 +1,189 @@ +#define _XOPEN_SOURCE 500 +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <poll.h> +#include <signal.h> +#include <time.h> +#include <dbus/dbus.h> +#include "swaybar/tray/dbus.h" +#include "swaybar/event_loop.h" +#include "log.h" + +DBusConnection *conn = NULL; + +static void dispatch_watch(int fd, short mask, void *data) { + sway_log(L_DEBUG, "Dispatching watch"); + DBusWatch *watch = data; + + if (!dbus_watch_get_enabled(watch)) { + return; + } + + uint32_t flags = 0; + + if (mask & POLLIN) { + flags |= DBUS_WATCH_READABLE; + } if (mask & POLLOUT) { + flags |= DBUS_WATCH_WRITABLE; + } if (mask & POLLHUP) { + flags |= DBUS_WATCH_HANGUP; + } if (mask & POLLERR) { + flags |= DBUS_WATCH_ERROR; + } + + dbus_watch_handle(watch, flags); +} + +static dbus_bool_t add_watch(DBusWatch *watch, void *_data) { + if (!dbus_watch_get_enabled(watch)) { + // Watch should not be polled + return TRUE; + } + + short mask = 0; + uint32_t flags = dbus_watch_get_flags(watch); + + if (flags & DBUS_WATCH_READABLE) { + mask |= POLLIN; + } if (flags & DBUS_WATCH_WRITABLE) { + mask |= POLLOUT; + } + + int fd = dbus_watch_get_unix_fd(watch); + + sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd); + add_event(fd, mask, dispatch_watch, watch); + + return TRUE; +} + +static void remove_watch(DBusWatch *watch, void *_data) { + int fd = dbus_watch_get_unix_fd(watch); + + remove_event(fd); +} + +static void dispatch_timeout(timer_t timer, void *data) { + sway_log(L_DEBUG, "Dispatching DBus timeout"); + DBusTimeout *timeout = data; + + if (dbus_timeout_get_enabled(timeout)) { + dbus_timeout_handle(timeout); + } +} + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) { + if (!dbus_timeout_get_enabled(timeout)) { + return TRUE; + } + + timer_t *timer = malloc(sizeof(timer_t)); + if (!timer) { + sway_log(L_ERROR, "Cannot allocate memory"); + return FALSE; + } + struct sigevent ev = { + .sigev_notify = SIGEV_NONE, + }; + + if (timer_create(CLOCK_MONOTONIC, &ev, timer)) { + sway_log(L_ERROR, "Could not create DBus timer"); + return FALSE; + } + + int interval = dbus_timeout_get_interval(timeout); + int interval_sec = interval / 1000; + int interval_msec = (interval_sec * 1000) - interval; + + struct timespec period = { + (time_t) interval_sec, + ((long) interval_msec) * 1000 * 1000, + }; + struct itimerspec time = { + period, + period, + }; + + timer_settime(*timer, 0, &time, NULL); + + dbus_timeout_set_data(timeout, timer, free); + + sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec); + add_timer(*timer, dispatch_timeout, timeout); + + return TRUE; +} +static void remove_timeout(DBusTimeout *timeout, void *_data) { + timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout); + sway_log(L_DEBUG, "Removing DBus timeout."); + + if (timer) { + remove_timer(*timer); + } +} + +static bool should_dispatch = true; + +static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status, + void *_data) { + if (new_status == DBUS_DISPATCH_DATA_REMAINS) { + should_dispatch = true; + } +} + +/* Public functions below */ + +void dispatch_dbus() { + if (!should_dispatch) { + return; + } + + DBusDispatchStatus status; + + do { + status = dbus_connection_dispatch(conn); + } while (status == DBUS_DISPATCH_DATA_REMAINS); + + if (status != DBUS_DISPATCH_COMPLETE) { + sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status); + } + + should_dispatch = false; +} + +int dbus_init() { + DBusError error; + dbus_error_init(&error); + + conn = dbus_bus_get(DBUS_BUS_SESSION, &error); + dbus_connection_set_exit_on_disconnect(conn, FALSE); + if (dbus_error_is_set(&error)) { + sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message); + conn = NULL; + return -1; + } + + sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn)); + + // Will be called if dispatch status changes + dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); + + if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, + NULL, NULL, NULL)) { + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); + sway_log(L_ERROR, "Failed to activate DBUS watch functions"); + return -1; + } + + if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, + NULL, NULL, NULL)) { + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); + dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); + sway_log(L_ERROR, "Failed to activate DBUS timeout functions"); + return -1; + } + + return 0; +} |