mirror of
https://github.com/swaywm/sway.git
synced 2026-05-04 06:46:25 -04:00
Port swaybar tray files to 0.15 and fix compiling
This commit is contained in:
parent
d3eaf6468c
commit
a20f21281f
15 changed files with 2354 additions and 34 deletions
516
swaybar/tray/tray.c
Normal file
516
swaybar/tray/tray.c
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
#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"
|
||||
#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();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue