2018-04-02 19:41:21 -06:00
|
|
|
#define _XOPEN_SOURCE 700
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <dbus/dbus.h>
|
|
|
|
|
#include <wayland-util.h>
|
|
|
|
|
#include "cairo.h"
|
2018-04-02 21:34:45 -04:00
|
|
|
#include "config.h"
|
2018-04-02 19:41:21 -06:00
|
|
|
#include "swaybar/bar.h"
|
|
|
|
|
#include "swaybar/tray/tray.h"
|
|
|
|
|
#include "swaybar/tray/dbus.h"
|
|
|
|
|
#include "swaybar/tray/sni.h"
|
|
|
|
|
#include "swaybar/tray/sni_watcher.h"
|
|
|
|
|
#include "swaybar/bar.h"
|
|
|
|
|
#include "swaybar/config.h"
|
|
|
|
|
#include "list.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
|
|
// TODO TRAY remove globals
|
|
|
|
|
static struct tray tray;
|
|
|
|
|
|
|
|
|
|
static void register_host(char *name) {
|
|
|
|
|
DBusMessage *message;
|
|
|
|
|
|
|
|
|
|
message = dbus_message_new_method_call(
|
|
|
|
|
"org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"/StatusNotifierWatcher",
|
|
|
|
|
"org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"RegisterStatusNotifierHost");
|
|
|
|
|
if (!message) {
|
|
|
|
|
wlr_log(L_ERROR, "Cannot allocate dbus method call");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbus_message_append_args(message,
|
|
|
|
|
DBUS_TYPE_STRING, &name,
|
|
|
|
|
DBUS_TYPE_INVALID);
|
|
|
|
|
|
|
|
|
|
dbus_connection_send(conn, message, NULL);
|
|
|
|
|
|
|
|
|
|
dbus_message_unref(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void get_items_reply(DBusMessageIter *iter, void *_data,
|
|
|
|
|
enum property_status status) {
|
|
|
|
|
if (status != PROP_EXISTS) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
DBusMessageIter array;
|
|
|
|
|
|
|
|
|
|
// O(n) function, could be faster dynamically reading values
|
|
|
|
|
int len = dbus_message_iter_get_element_count(iter);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(iter, &array);
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
const char *name;
|
|
|
|
|
dbus_message_iter_get_basic(&array, &name);
|
|
|
|
|
|
|
|
|
|
bool found = false;;
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, name) == 0) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
struct StatusNotifierItem *item = sni_create(name);
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
wlr_log(L_DEBUG, "Item registered with host: %s",
|
|
|
|
|
name);
|
|
|
|
|
wl_list_insert(&tray.items, &item->link);
|
|
|
|
|
// TODO TRAY render
|
|
|
|
|
//dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void get_obj_items_reply(DBusMessageIter *iter, void *_data,
|
|
|
|
|
enum property_status status) {
|
|
|
|
|
if (status != PROP_EXISTS) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
DBusMessageIter array;
|
|
|
|
|
DBusMessageIter dstruct;
|
|
|
|
|
|
|
|
|
|
int len = dbus_message_iter_get_element_count(iter);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(iter, &array);
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
const char *object_path;
|
|
|
|
|
const char *unique_name;
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&array, &dstruct);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_basic(&dstruct, &object_path);
|
|
|
|
|
dbus_message_iter_next(&dstruct);
|
|
|
|
|
dbus_message_iter_get_basic(&dstruct, &unique_name);
|
|
|
|
|
|
|
|
|
|
bool found = false;;
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, object_path) == 0 &&
|
|
|
|
|
strcmp(item->unique_name, unique_name) == 0) {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
item = sni_create_from_obj_path(unique_name, object_path);
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
wlr_log(L_DEBUG, "Item registered with host: %s",
|
|
|
|
|
unique_name);
|
|
|
|
|
wl_list_insert(tray.items.prev, &item->link);
|
|
|
|
|
// TODO TRAY
|
|
|
|
|
//dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void get_items() {
|
|
|
|
|
// Clear list
|
|
|
|
|
struct StatusNotifierItem *item, *tmp;
|
|
|
|
|
wl_list_for_each_safe(item, tmp, &tray.items, link) {
|
|
|
|
|
wl_list_remove(&item->link);
|
|
|
|
|
sni_free(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"/StatusNotifierWatcher","org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"RegisteredStatusNotifierItems", "as", get_items_reply, NULL);
|
|
|
|
|
|
|
|
|
|
dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"/StatusNotifierWatcher","org.swaywm.LessSuckyStatusNotifierWatcher",
|
|
|
|
|
"RegisteredObjectPathItems", "a(os)", get_obj_items_reply, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult signal_handler(DBusConnection *connection,
|
|
|
|
|
DBusMessage *message, void *_data) {
|
|
|
|
|
if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"StatusNotifierItemRegistered")) {
|
|
|
|
|
const char *name;
|
|
|
|
|
if (!dbus_message_get_args(message, NULL,
|
|
|
|
|
DBUS_TYPE_STRING, &name,
|
|
|
|
|
DBUS_TYPE_INVALID)) {
|
|
|
|
|
wlr_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool found = false;;
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, name) == 0) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
struct StatusNotifierItem *item = sni_create(name);
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
wl_list_insert(&tray.items, &item->link);
|
|
|
|
|
// TODO TRAY render
|
|
|
|
|
//dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
} else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
|
|
|
|
|
"StatusNotifierItemUnregistered")) {
|
|
|
|
|
const char *name;
|
|
|
|
|
if (!dbus_message_get_args(message, NULL,
|
|
|
|
|
DBUS_TYPE_STRING, &name,
|
|
|
|
|
DBUS_TYPE_INVALID)) {
|
|
|
|
|
wlr_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool found = false;;
|
|
|
|
|
struct StatusNotifierItem *item, *tmp;
|
|
|
|
|
wl_list_for_each_safe(item, tmp, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, name) == 0) {
|
|
|
|
|
found = true;
|
|
|
|
|
wl_list_remove(&item->link);
|
|
|
|
|
sni_free(item);
|
|
|
|
|
// TODO TRAY render
|
|
|
|
|
//dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
// If it's not in our list, then our list is incorrect.
|
|
|
|
|
// Fetch all items again
|
|
|
|
|
wlr_log(L_INFO, "Host item list incorrect, refreshing");
|
|
|
|
|
get_items();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
} else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
|
|
|
|
|
"NewIcon") || dbus_message_is_signal(message,
|
|
|
|
|
"org.kde.StatusNotifierItem", "NewIcon")) {
|
|
|
|
|
const char *name;
|
|
|
|
|
const char *obj_path;
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
|
|
|
|
|
name = dbus_message_get_sender(message);
|
|
|
|
|
obj_path = dbus_message_get_path(message);
|
|
|
|
|
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, name) == 0 &&
|
|
|
|
|
strcmp(item->unique_name, obj_path) == 0) {
|
|
|
|
|
get_icon(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
} else if (dbus_message_is_signal(message,
|
|
|
|
|
"org.swaywm.LessSuckyStatusNotifierWatcher",
|
|
|
|
|
"ObjPathItemRegistered")) {
|
|
|
|
|
const char *object_path;
|
|
|
|
|
const char *unique_name;
|
|
|
|
|
if (!dbus_message_get_args(message, NULL,
|
|
|
|
|
DBUS_TYPE_OBJECT_PATH, &object_path,
|
|
|
|
|
DBUS_TYPE_STRING, &unique_name,
|
|
|
|
|
DBUS_TYPE_INVALID)) {
|
|
|
|
|
wlr_log(L_ERROR, "Error getting ObjPathItemRegistered args");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool found = false;;
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (strcmp(item->name, object_path) == 0 &&
|
|
|
|
|
strcmp(item->unique_name, unique_name) == 0) {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
item = sni_create_from_obj_path(unique_name, object_path);
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
wl_list_insert(&tray.items, &item->link);
|
|
|
|
|
// TODO TRAY render
|
|
|
|
|
//dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int init_host() {
|
|
|
|
|
wl_list_init(&tray.items);
|
|
|
|
|
|
|
|
|
|
DBusError error;
|
|
|
|
|
dbus_error_init(&error);
|
|
|
|
|
char *name = NULL;
|
|
|
|
|
if (!conn) {
|
|
|
|
|
wlr_log(L_ERROR, "Connection is null, cannot init SNI host");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
name = calloc(sizeof(char), 256);
|
|
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
|
wlr_log(L_ERROR, "Cannot allocate name");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid_t pid = getpid();
|
|
|
|
|
if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
|
|
|
|
|
>= 256) {
|
|
|
|
|
wlr_log(L_ERROR, "Cannot get host name because string is too short."
|
|
|
|
|
"This should not happen");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We want to be the sole owner of this name
|
|
|
|
|
if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
|
|
|
|
&error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
|
|
|
|
wlr_log(L_ERROR, "Cannot get host name and start the tray");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
wlr_log(L_DEBUG, "Got host name");
|
|
|
|
|
|
|
|
|
|
register_host(name);
|
|
|
|
|
|
|
|
|
|
// Chances are if an item is already running, we'll get it two times.
|
|
|
|
|
// Once from this and another time from queued signals. Still we want
|
|
|
|
|
// to do this to be a complient sni host just in case.
|
|
|
|
|
get_items();
|
|
|
|
|
|
|
|
|
|
// Perhaps use addmatch helper functions like wlc does?
|
|
|
|
|
dbus_bus_add_match(conn,
|
|
|
|
|
"type='signal',\
|
|
|
|
|
sender='org.freedesktop.StatusNotifierWatcher',\
|
|
|
|
|
member='StatusNotifierItemRegistered'",
|
|
|
|
|
&error);
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "dbus_err: %s", error.message);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
dbus_bus_add_match(conn,
|
|
|
|
|
"type='signal',\
|
|
|
|
|
sender='org.freedesktop.StatusNotifierWatcher',\
|
|
|
|
|
member='StatusNotifierItemUnregistered'",
|
|
|
|
|
&error);
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "dbus_err: %s", error.message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
dbus_bus_add_match(conn,
|
|
|
|
|
"type='signal',\
|
|
|
|
|
sender='org.freedesktop.StatusNotifierWatcher',\
|
|
|
|
|
member='ObjPathItemRegistered'",
|
|
|
|
|
&error);
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "dbus_err: %s", error.message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SNI matches
|
|
|
|
|
dbus_bus_add_match(conn,
|
|
|
|
|
"type='signal',\
|
|
|
|
|
interface='org.freedesktop.StatusNotifierItem',\
|
|
|
|
|
member='NewIcon'",
|
|
|
|
|
&error);
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "dbus_err %s", error.message);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
dbus_bus_add_match(conn,
|
|
|
|
|
"type='signal',\
|
|
|
|
|
interface='org.kde.StatusNotifierItem',\
|
|
|
|
|
member='NewIcon'",
|
|
|
|
|
&error);
|
|
|
|
|
if (dbus_error_is_set(&error)) {
|
|
|
|
|
wlr_log(L_ERROR, "dbus_err %s", error.message);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
free(name);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
// TODO better handle errors
|
|
|
|
|
free(name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO tray mouse
|
|
|
|
|
//void tray_mouse_event(struct output *output, int rel_x, int rel_y,
|
|
|
|
|
// uint32_t button, uint32_t state) {
|
|
|
|
|
//
|
|
|
|
|
// int x = rel_x;
|
|
|
|
|
// int y = rel_y + (swaybar.config->position == DESKTOP_SHELL_PANEL_POSITION_TOP
|
|
|
|
|
// ? 0 : (output->state->height - output->window->height));
|
|
|
|
|
//
|
|
|
|
|
// struct window *window = output->window;
|
|
|
|
|
// uint32_t tray_padding = swaybar.config->tray_padding;
|
|
|
|
|
// int tray_width = window->width * window->scale;
|
|
|
|
|
//
|
|
|
|
|
// for (int i = 0; i < output->items->length; ++i) {
|
|
|
|
|
// struct sni_icon_ref *item =
|
|
|
|
|
// output->items->items[i];
|
|
|
|
|
// int icon_width = cairo_image_surface_get_width(item->icon);
|
|
|
|
|
//
|
|
|
|
|
// tray_width -= tray_padding;
|
|
|
|
|
// if (x <= tray_width && x >= tray_width - icon_width) {
|
|
|
|
|
// if (button == swaybar.config->activate_button) {
|
|
|
|
|
// sni_activate(item->ref, x, y);
|
|
|
|
|
// } else if (button == swaybar.config->context_button) {
|
|
|
|
|
// sni_context_menu(item->ref, x, y);
|
|
|
|
|
// } else if (button == swaybar.config->secondary_button) {
|
|
|
|
|
// sni_secondary(item->ref, x, y);
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// tray_width -= icon_width;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output,
|
|
|
|
|
struct swaybar_config *config, struct swaybar_workspace *ws,
|
|
|
|
|
double *pos, uint32_t height) {
|
|
|
|
|
|
|
|
|
|
double original_width = *pos;
|
|
|
|
|
|
|
|
|
|
// Tray icons
|
|
|
|
|
double tray_padding = (double) config->tray_padding;
|
|
|
|
|
// TODO TRAY scaling
|
|
|
|
|
const double item_size = ((double) height) - (2. * tray_padding);
|
|
|
|
|
|
|
|
|
|
if (item_size < 0) {
|
|
|
|
|
// Can't render items if the padding is too large
|
|
|
|
|
return height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO TRAY outputs
|
|
|
|
|
//if (config->tray_output && strcmp(config->tray_output, output->name) != 0) {
|
|
|
|
|
// return tray_width;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//bool clean_item = false;
|
|
|
|
|
//// Clean item if only one output has tray or this is the last output
|
|
|
|
|
//if (swaybar.outputs->length == 1 || config->tray_output ||
|
|
|
|
|
// output->idx == swaybar.outputs->length-1) {
|
|
|
|
|
// clean_item = true;
|
|
|
|
|
//// More trickery is needed in case you plug off secondary outputs on live
|
|
|
|
|
//} else {
|
|
|
|
|
// int active_outputs = 0;
|
|
|
|
|
// for (int i = 0; i < swaybar.outputs->length; i++) {
|
|
|
|
|
// struct output *output = swaybar.outputs->items[i];
|
|
|
|
|
// if (output->active) {
|
|
|
|
|
// active_outputs++;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// if (active_outputs == 1) {
|
|
|
|
|
// clean_item = true;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
struct wl_list *curr_node = output->item_refs.next;
|
|
|
|
|
|
|
|
|
|
struct StatusNotifierItem *item;
|
|
|
|
|
wl_list_for_each(item, &tray.items, link) {
|
|
|
|
|
if (!item->image) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sni_icon_ref *render_item = NULL;
|
|
|
|
|
|
|
|
|
|
while (curr_node != &output->item_refs) {
|
|
|
|
|
struct sni_icon_ref *ref =
|
|
|
|
|
wl_container_of(curr_node, render_item, link);
|
|
|
|
|
struct wl_list *next = curr_node->next;
|
|
|
|
|
|
|
|
|
|
if (ref->ref == item) {
|
|
|
|
|
render_item = ref;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
wl_list_remove(curr_node);
|
|
|
|
|
sni_icon_ref_free(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr_node = next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!render_item) {
|
|
|
|
|
// We ran out of item refs without finding the current
|
|
|
|
|
// item so we need to add it.
|
|
|
|
|
render_item = sni_icon_ref_create(item, item_size);
|
|
|
|
|
|
|
|
|
|
wl_list_insert(curr_node->prev, &render_item->link);
|
|
|
|
|
} else if (item->dirty) {
|
|
|
|
|
// item needs re-render
|
|
|
|
|
wlr_log(L_DEBUG, "Redrawing item %s for output %s",
|
|
|
|
|
item->name, output->name);
|
|
|
|
|
struct sni_icon_ref *new =
|
|
|
|
|
sni_icon_ref_create(item, item_size);
|
|
|
|
|
|
|
|
|
|
// Replace the current node with the new one
|
|
|
|
|
struct wl_list *tmp = curr_node->prev;
|
|
|
|
|
wl_list_remove(curr_node);
|
|
|
|
|
sni_icon_ref_free(render_item);
|
|
|
|
|
wl_list_insert(tmp, &new->link);
|
|
|
|
|
render_item = new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pos -= tray_padding;
|
|
|
|
|
*pos -= item_size;
|
|
|
|
|
|
|
|
|
|
// Now draw it
|
|
|
|
|
cairo_operator_t op = cairo_get_operator(cairo);
|
|
|
|
|
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
|
|
|
|
|
cairo_set_source_surface(cairo, render_item->icon, *pos,
|
|
|
|
|
tray_padding);
|
|
|
|
|
cairo_rectangle(cairo, *pos, tray_padding, item_size,
|
|
|
|
|
item_size);
|
|
|
|
|
cairo_fill(cairo);
|
|
|
|
|
cairo_set_operator(cairo, op);
|
|
|
|
|
|
|
|
|
|
item->dirty = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we drew any items, add another padding
|
|
|
|
|
// TODO TRAY scale
|
|
|
|
|
if (original_width != *pos) {
|
|
|
|
|
*pos -= tray_padding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void init_tray(struct swaybar *bar) {
|
|
|
|
|
tray.bar = bar;
|
|
|
|
|
|
|
|
|
|
// TODO TRAY outputs only init tray if an output exists.
|
|
|
|
|
/* Connect to the D-Bus */
|
|
|
|
|
dbus_init();
|
|
|
|
|
|
|
|
|
|
/* Start the SNI watcher */
|
|
|
|
|
init_sni_watcher();
|
|
|
|
|
|
|
|
|
|
/* Start the SNI host */
|
|
|
|
|
init_host();
|
|
|
|
|
}
|