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:
Calvin Lee 2017-06-07 16:45:28 -07:00
parent fd47a30e75
commit 843ad38b3c
35 changed files with 2714 additions and 58 deletions

View file

@ -21,6 +21,9 @@ struct output {
struct window *window;
struct registry *registry;
list_t *workspaces;
#ifdef ENABLE_TRAY
list_t *items;
#endif
char *name;
int idx;
bool focused;
@ -37,6 +40,9 @@ struct workspace {
/** Global bar state */
extern struct bar swaybar;
/** True if sway needs to render */
extern bool dirty;
/**
* Setup bar.
*/

View file

@ -33,6 +33,17 @@ struct config {
bool all_outputs;
list_t *outputs;
#ifdef ENABLE_TRAY
// Tray
char *tray_output;
char *icon_theme;
uint32_t tray_padding;
uint32_t activate_button;
uint32_t context_button;
uint32_t secondary_button;
#endif
int height;
struct {

View file

@ -0,0 +1,26 @@
#ifndef _SWAYBAR_EVENT_LOOP_H
#define _SWAYBAR_EVENT_LOOP_H
#include <stdbool.h>
#include <time.h>
void add_event(int fd, short mask,
void(*cb)(int fd, short mask, void *data),
void *data);
// Not guaranteed to notify cb immediately
void add_timer(timer_t timer,
void(*cb)(timer_t timer, void *data),
void *data);
// Returns false if nothing exists, true otherwise
bool remove_event(int fd);
// Returns false if nothing exists, true otherwise
bool remove_timer(timer_t timer);
// Blocks and returns after sending callbacks
void event_loop_poll();
void init_event_loop();
#endif /*_SWAYBAR_EVENT_LOOP_H */

View file

@ -0,0 +1,18 @@
#ifndef _SWAYBAR_DBUS_H
#define _SWAYBAR_DBUS_H
#include <stdbool.h>
#include <dbus/dbus.h>
extern DBusConnection *conn;
/**
* Should be called in main loop to dispatch events
*/
void dispatch_dbus();
/**
* Initializes async dbus communication
*/
int dbus_init();
#endif /* _SWAYBAR_DBUS_H */

View file

@ -0,0 +1,16 @@
#ifndef _SWAYBAR_ICON_H
#define _SWAYBAR_ICON_H
#include <stdint.h>
#include <stdbool.h>
#include <client/cairo.h>
/**
* Returns the image found by `name` that is closest to `size`
*/
cairo_surface_t *find_icon(const char *name, int size);
/* Struct used internally only */
struct subdir;
#endif /* _SWAYBAR_ICON_H */

View file

@ -0,0 +1,81 @@
#ifndef _SWAYBAR_SNI_H
#define _SWAYBAR_SNI_H
#include <stdbool.h>
#include <client/cairo.h>
struct StatusNotifierItem {
/* Name registered to sni watcher */
char *name;
/* Unique bus name, needed for determining signal origins */
char *unique_name;
bool kde_special_snowflake;
cairo_surface_t *image;
bool dirty;
};
/* Each output holds an sni_icon_ref of each item to render */
struct sni_icon_ref {
cairo_surface_t *icon;
struct StatusNotifierItem *ref;
};
struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
int height);
void sni_icon_ref_free(struct sni_icon_ref *sni_ref);
/**
* Will return a new item and get its icon. (see warning below)
*/
struct StatusNotifierItem *sni_create(const char *name);
/**
* `item` must be a struct StatusNotifierItem *
* `str` must be a NUL terminated char *
*
* Returns 0 if `item` has a name of `str`
*/
int sni_str_cmp(const void *item, const void *str);
/**
* Returns 0 if `item` has a unique name of `str` or if
* `item->unique_name == NULL`
*/
int sni_uniq_cmp(const void *item, const void *str);
/**
* Gets an icon for the given item if found.
*
* XXX
* This function keeps a reference to the item until it gets responses, make
* sure that the reference and item are valid during this time.
*/
void get_icon(struct StatusNotifierItem *item);
/**
* Calls the "activate" method on the given StatusNotifierItem
*
* x and y should be where the item was clicked
*/
void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
/**
* Asks the item to draw a context menu at the given x and y coords
*/
void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
/**
* Calls the "secondary activate" method on the given StatusNotifierItem
*
* x and y should be where the item was clicked
*/
void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y);
/**
* Deconstructs `item`
*/
void sni_free(struct StatusNotifierItem *item);
#endif /* _SWAYBAR_SNI_H */

View file

@ -0,0 +1,10 @@
#ifndef _SWAYBAR_SNI_WATCHER_H
#define _SWAYBAR_SNI_WATCHER_H
/**
* Starts the sni_watcher, the watcher is practically a black box and should
* only be accessed though functions described in its spec
*/
int init_sni_watcher();
#endif /* _SWAYBAR_SNI_WATCHER_H */

View file

@ -0,0 +1,26 @@
#ifndef _SWAYBAR_TRAY_H
#define _SWAYBAR_TRAY_H
#include <stdint.h>
#include <stdbool.h>
#include "swaybar/tray/dbus.h"
#include "swaybar/tray/sni.h"
#include "list.h"
extern struct tray *tray;
struct tray {
list_t *items;
};
/**
* Initializes the tray host with D-Bus
*/
int init_tray();
/**
* Returns an item if `x` and `y` collide with it and NULL otherwise
*/
struct StatusNotifierItem *collides_with_sni(int x, int y);
#endif /* _SWAYBAR_TRAY_H */