mirror of
https://github.com/swaywm/sway.git
synced 2026-03-30 11:10:59 -04:00
Merge pull request #1431 from 4e554c4c/sni_sucks
Support libappindicator
This commit is contained in:
commit
1e87c90923
9 changed files with 601 additions and 299 deletions
|
|
@ -21,6 +21,7 @@ struct bar {
|
|||
struct output {
|
||||
struct window *window;
|
||||
struct registry *registry;
|
||||
struct output_state *state;
|
||||
list_t *workspaces;
|
||||
#ifdef ENABLE_TRAY
|
||||
list_t *items;
|
||||
|
|
@ -28,6 +29,9 @@ struct output {
|
|||
char *name;
|
||||
int idx;
|
||||
bool focused;
|
||||
#ifdef ENABLE_TRAY
|
||||
bool active;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct workspace {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,37 @@
|
|||
#include <dbus/dbus.h>
|
||||
extern DBusConnection *conn;
|
||||
|
||||
enum property_status {
|
||||
PROP_EXISTS, /* Will give iter */
|
||||
PROP_ERROR, /* Will not give iter */
|
||||
PROP_BAD_DATA, /* Will not give iter */
|
||||
PROP_WRONG_SIG, /* Will give iter, please be careful */
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the signature of the given iter against `sig`. Prefer to
|
||||
* `dbus_message_iter_get_signature` as this one frees the intermediate string.
|
||||
*/
|
||||
bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig);
|
||||
|
||||
/**
|
||||
* Fetches the property and calls `callback` with a message iter pointing it.
|
||||
* Performs error handling and signature checking.
|
||||
*
|
||||
* Returns: true if message is successfully sent and false otherwise. If there
|
||||
* is an error getting a property, `callback` will still be run, but with
|
||||
* `status` set to the error.
|
||||
*
|
||||
* NOTE: `expected_signature` must remain valid until the message reply is
|
||||
* received, please only use 'static signatures.
|
||||
*/
|
||||
bool dbus_get_prop_async(const char *destination,
|
||||
const char *path,
|
||||
const char *iface,
|
||||
const char *prop,
|
||||
const char *expected_signature,
|
||||
void(*callback)(DBusMessageIter *iter, void *data, enum property_status status),
|
||||
void *data);
|
||||
/**
|
||||
* Should be called in main loop to dispatch events
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ struct StatusNotifierItem {
|
|||
char *name;
|
||||
/* Unique bus name, needed for determining signal origins */
|
||||
char *unique_name;
|
||||
/* Object path, useful for items not registerd by well known name */
|
||||
char *object_path;
|
||||
bool kde_special_snowflake;
|
||||
|
||||
cairo_surface_t *image;
|
||||
|
|
@ -31,6 +33,12 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref);
|
|||
* May return `NULL` if `name` is not valid.
|
||||
*/
|
||||
struct StatusNotifierItem *sni_create(const char *name);
|
||||
/**
|
||||
* Same as sni_create, but takes an object path and unique name instead of
|
||||
* well-known name.
|
||||
*/
|
||||
struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
|
||||
const char *object_path);
|
||||
|
||||
/**
|
||||
* `item` must be a struct StatusNotifierItem *
|
||||
|
|
@ -46,6 +54,17 @@ int sni_str_cmp(const void *item, const void *str);
|
|||
*/
|
||||
int sni_uniq_cmp(const void *item, const void *str);
|
||||
|
||||
|
||||
struct ObjName {
|
||||
const void *obj_path;
|
||||
const void *name;
|
||||
};
|
||||
/**
|
||||
* Returns 0 if `item` has a name of `obj_name->name` and object path of
|
||||
* `obj_name->obj_path`.
|
||||
*/
|
||||
int sni_obj_name_cmp(const void *item, const void *obj_name);
|
||||
|
||||
/**
|
||||
* Gets an icon for the given item if found.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -247,6 +247,8 @@ void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) {
|
|||
|
||||
/* set window height */
|
||||
set_window_height(bar_output->window, bar->config->height);
|
||||
|
||||
bar_output->state = output;
|
||||
}
|
||||
/* spawn status command */
|
||||
spawn_status_cmd_proc(bar);
|
||||
|
|
@ -296,6 +298,11 @@ void bar_run(struct bar *bar) {
|
|||
render(output, bar->config, bar->status);
|
||||
window_render(output->window);
|
||||
wl_display_flush(output->registry->display);
|
||||
#ifdef ENABLE_TRAY
|
||||
output->active = true;
|
||||
} else {
|
||||
output->active = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -135,7 +136,106 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s
|
|||
}
|
||||
}
|
||||
|
||||
/* Public functions below */
|
||||
struct async_prop_data {
|
||||
char const *sig;
|
||||
void(*callback)(DBusMessageIter *, void *, enum property_status);
|
||||
void *usr_data;
|
||||
};
|
||||
|
||||
static void get_prop_callback(DBusPendingCall *pending, void *_data) {
|
||||
struct async_prop_data *data = _data;
|
||||
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_INFO, "Got no icon name reply from item");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_INFO, "Failure to get property: %s", msg);
|
||||
data->callback(NULL, data->usr_data, PROP_ERROR);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant;
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Property relpy type incorrect");
|
||||
data->callback(NULL, data->usr_data, PROP_BAD_DATA);
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
|
||||
if (!dbus_message_iter_check_signature(&variant, data->sig)) {
|
||||
sway_log(L_INFO, "Property returned has incorrect signatue.");
|
||||
data->callback(&variant, data->usr_data, PROP_WRONG_SIG);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
data->callback(&variant, data->usr_data, PROP_EXISTS);
|
||||
|
||||
bail:
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
dbus_pending_call_unref(pending);
|
||||
}
|
||||
|
||||
/* Public functions below -- see header for docs*/
|
||||
|
||||
bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig) {
|
||||
char *msg_sig = dbus_message_iter_get_signature(iter);
|
||||
int result = strcmp(msg_sig, sig);
|
||||
dbus_free(msg_sig);
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
bool dbus_get_prop_async(const char *destination,
|
||||
const char *path, const char *iface,
|
||||
const char *prop, const char *expected_signature,
|
||||
void(*callback)(DBusMessageIter *, void *, enum property_status),
|
||||
void *usr_data) {
|
||||
struct async_prop_data *data = malloc(sizeof(struct async_prop_data));
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
destination, path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get property");
|
||||
return false;
|
||||
}
|
||||
|
||||
data->sig = expected_signature;
|
||||
data->callback = callback;
|
||||
data->usr_data = usr_data;
|
||||
dbus_pending_call_set_notify(pending, get_prop_callback, data, free);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dispatch_dbus() {
|
||||
if (!should_dispatch || !conn) {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,17 @@ static bool isdir(const char *path) {
|
|||
|
||||
}
|
||||
|
||||
static bool isfile(const char *path) {
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) != -1) {
|
||||
if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory of a given theme if it exists.
|
||||
* The returned pointer must be freed.
|
||||
|
|
@ -111,15 +122,28 @@ static char *find_theme_dir(const char *theme) {
|
|||
}
|
||||
|
||||
if ((basedir = getenv("XDG_DATA_DIRS"))) {
|
||||
if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
// ditto
|
||||
if (!(basedir = strdup(basedir))) {
|
||||
sway_log_errno(L_ERROR, "Path too long to render");
|
||||
goto fail;
|
||||
}
|
||||
char *token = strtok(basedir, ":");
|
||||
while (token) {
|
||||
// By peeking at the spec, there should be a slash at
|
||||
// the end of the data dir.
|
||||
if (snprintf(icon_dir, 1024, "%sicons/%s", token, theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
// ditto
|
||||
free(basedir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (isdir(icon_dir)) {
|
||||
return icon_dir;
|
||||
if (isdir(icon_dir)) {
|
||||
free(basedir);
|
||||
return icon_dir;
|
||||
}
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
free(basedir);
|
||||
}
|
||||
|
||||
// Spec says use "/usr/share/pixmaps/", but I see everything in
|
||||
|
|
@ -162,6 +186,15 @@ static list_t *find_all_theme_dirs(const char *theme) {
|
|||
list_cat(dirs, inherits);
|
||||
list_free(inherits);
|
||||
}
|
||||
// 'default' usually inherits the default theme. I don't believe it has
|
||||
// any icons, but look for them anyway
|
||||
dir = find_theme_dir("default");
|
||||
if (dir) {
|
||||
list_add(dirs, dir);
|
||||
list_t *inherits = find_inherits(dir);
|
||||
list_cat(dirs, inherits);
|
||||
list_free(inherits);
|
||||
}
|
||||
dir = find_theme_dir("hicolor");
|
||||
if (dir) {
|
||||
list_add(dirs, dir);
|
||||
|
|
@ -290,6 +323,24 @@ fail:
|
|||
return dirs;
|
||||
}
|
||||
|
||||
/* Returns true if full path and file exists */
|
||||
static bool is_valid_path(const char *file) {
|
||||
if (strstr(file, "/") == NULL || !isfile(file)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef WITH_GDK_PIXBUF
|
||||
if (strstr(file, ".png") == NULL &&
|
||||
strstr(file, ".xpm") == NULL &&
|
||||
strstr(file, ".svg") == NULL) {
|
||||
#else
|
||||
if (strstr(file, ".png") == NULL) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns the file of an icon given its name and size */
|
||||
static char *find_icon_file(const char *name, int size) {
|
||||
int namelen = strlen(name);
|
||||
|
|
@ -372,7 +423,12 @@ static char *find_icon_file(const char *name, int size) {
|
|||
}
|
||||
|
||||
cairo_surface_t *find_icon(const char *name, int size) {
|
||||
char *image_path = find_icon_file(name, size);
|
||||
char *image_path;
|
||||
if (is_valid_path(name)) {
|
||||
image_path = strdup(name);
|
||||
} else {
|
||||
image_path = find_icon_file(name, size);
|
||||
}
|
||||
if (image_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
#include "client/cairo.h"
|
||||
#include "log.h"
|
||||
|
||||
static const char *KDE_IFACE = "org.kde.StatusNotifierItem";
|
||||
static const char *FD_IFACE = "org.freedesktop.StatusNotifierItem";
|
||||
|
||||
// Not sure what this is but cairo needs it.
|
||||
static const cairo_user_data_key_t cairo_user_data_key;
|
||||
|
||||
|
|
@ -38,90 +41,53 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
|
|||
}
|
||||
|
||||
/* Gets the pixmap of an icon */
|
||||
static void reply_icon(DBusPendingCall *pending, void *_data) {
|
||||
static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data, enum property_status status) {
|
||||
if (status != PROP_EXISTS) {
|
||||
return;
|
||||
}
|
||||
struct StatusNotifierItem *item = _data;
|
||||
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_ERROR, "Did not get reply");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_ERROR, "Message is error: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant; /* v[a(iiay)] */
|
||||
DBusMessageIter array; /* a(iiay) */
|
||||
DBusMessageIter d_struct; /* (iiay) */
|
||||
DBusMessageIter icon; /* ay */
|
||||
DBusMessageIter struct_items;
|
||||
DBusMessageIter icon;
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
|
||||
// Each if here checks the types above before recursing
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
|
||||
if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&variant));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_element_count(&variant) == 0) {
|
||||
if (dbus_message_iter_get_element_count(iter) == 0) {
|
||||
// Can't recurse if there are no items
|
||||
sway_log(L_INFO, "Item has no icon");
|
||||
goto bail;
|
||||
return;
|
||||
}
|
||||
dbus_message_iter_recurse(&variant, &array);
|
||||
|
||||
dbus_message_iter_recurse(&array, &d_struct);
|
||||
dbus_message_iter_recurse(iter, &d_struct);
|
||||
dbus_message_iter_recurse(&d_struct, &struct_items);
|
||||
|
||||
int width;
|
||||
dbus_message_iter_get_basic(&d_struct, &width);
|
||||
dbus_message_iter_next(&d_struct);
|
||||
dbus_message_iter_get_basic(&struct_items, &width);
|
||||
dbus_message_iter_next(&struct_items);
|
||||
|
||||
int height;
|
||||
dbus_message_iter_get_basic(&d_struct, &height);
|
||||
dbus_message_iter_next(&d_struct);
|
||||
dbus_message_iter_get_basic(&struct_items, &height);
|
||||
dbus_message_iter_next(&struct_items);
|
||||
|
||||
int len = dbus_message_iter_get_element_count(&d_struct);
|
||||
int len = dbus_message_iter_get_element_count(&struct_items);
|
||||
|
||||
if (!len) {
|
||||
sway_log(L_ERROR, "No icon data");
|
||||
goto bail;
|
||||
return;
|
||||
}
|
||||
|
||||
// Also implies len % 4 == 0, useful below
|
||||
if (len != width * height * 4) {
|
||||
sway_log(L_ERROR, "Incorrect array size passed");
|
||||
goto bail;
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&d_struct, &icon);
|
||||
dbus_message_iter_recurse(&struct_items, &icon);
|
||||
|
||||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||
// FIXME support a variable stride
|
||||
// (works on my machine though for all tested widths)
|
||||
if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
|
||||
goto bail;
|
||||
return;
|
||||
}
|
||||
|
||||
// Data is by reference, no need to free
|
||||
|
|
@ -131,7 +97,7 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
|
|||
uint8_t *image_data = malloc(stride * height);
|
||||
if (!image_data) {
|
||||
sway_log(L_ERROR, "Could not allocate memory for icon");
|
||||
goto bail;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transform from network byte order to host byte order
|
||||
|
|
@ -159,101 +125,29 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
|
|||
item->dirty = true;
|
||||
dirty = true;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
} else {
|
||||
sway_log(L_ERROR, "Could not create image surface");
|
||||
free(image_data);
|
||||
}
|
||||
|
||||
bail:
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
dbus_pending_call_unref(pending);
|
||||
sway_log(L_ERROR, "Could not get icon from item");
|
||||
return;
|
||||
}
|
||||
static void send_icon_msg(struct StatusNotifierItem *item) {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
const char *iface;
|
||||
if (item->kde_special_snowflake) {
|
||||
iface = "org.kde.StatusNotifierItem";
|
||||
} else {
|
||||
iface = "org.freedesktop.StatusNotifierItem";
|
||||
}
|
||||
const char *prop = "IconPixmap";
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
/* Get an icon by its name */
|
||||
static void reply_icon_name(DBusMessageIter *iter, void *_data, enum property_status status) {
|
||||
struct StatusNotifierItem *item = _data;
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get item icon");
|
||||
if (status != PROP_EXISTS) {
|
||||
dbus_get_prop_async(item->name, item->object_path,
|
||||
(item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
|
||||
"IconPixmap", "a(iiay)", reply_icon, item);
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
|
||||
}
|
||||
|
||||
/* Get an icon by its name */
|
||||
static void reply_icon_name(DBusPendingCall *pending, void *_data) {
|
||||
struct StatusNotifierItem *item = _data;
|
||||
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_INFO, "Got no icon name reply from item");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_INFO, "Could not get icon name: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter; /* v[s] */
|
||||
DBusMessageIter variant; /* s */
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
char *icon_name;
|
||||
dbus_message_iter_get_basic(&variant, &icon_name);
|
||||
dbus_message_iter_get_basic(iter, &icon_name);
|
||||
|
||||
cairo_surface_t *image = find_icon(icon_name, 256);
|
||||
|
||||
|
|
@ -267,55 +161,19 @@ static void reply_icon_name(DBusPendingCall *pending, void *_data) {
|
|||
item->dirty = true;
|
||||
dirty = true;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
}
|
||||
|
||||
bail:
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
dbus_pending_call_unref(pending);
|
||||
// Now try the pixmap
|
||||
send_icon_msg(item);
|
||||
return;
|
||||
}
|
||||
static void send_icon_name_msg(struct StatusNotifierItem *item) {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
const char *iface;
|
||||
if (item->kde_special_snowflake) {
|
||||
iface = "org.kde.StatusNotifierItem";
|
||||
} else {
|
||||
iface = "org.freedesktop.StatusNotifierItem";
|
||||
}
|
||||
const char *prop = "IconName";
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get item icon name");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
|
||||
dbus_get_prop_async(item->name, item->object_path,
|
||||
(item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
|
||||
"IconPixmap", "a(iiay)", reply_icon, item);
|
||||
}
|
||||
|
||||
void get_icon(struct StatusNotifierItem *item) {
|
||||
send_icon_name_msg(item);
|
||||
dbus_get_prop_async(item->name, item->object_path,
|
||||
(item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
|
||||
"IconName", "s", reply_icon_name, item);
|
||||
}
|
||||
|
||||
void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||
|
|
@ -324,7 +182,7 @@ void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
|||
: "org.freedesktop.StatusNotifierItem");
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
item->object_path,
|
||||
iface,
|
||||
"Activate");
|
||||
|
||||
|
|
@ -342,9 +200,10 @@ void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
|||
const char *iface =
|
||||
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
||||
: "org.freedesktop.StatusNotifierItem");
|
||||
sway_log(L_INFO, "Activating context menu for item: (%s,%s)", item->name, item->object_path);
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
item->object_path,
|
||||
iface,
|
||||
"ContextMenu");
|
||||
|
||||
|
|
@ -363,7 +222,7 @@ void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
|||
: "org.freedesktop.StatusNotifierItem");
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
item->object_path,
|
||||
iface,
|
||||
"SecondaryActivate");
|
||||
|
||||
|
|
@ -426,6 +285,8 @@ struct StatusNotifierItem *sni_create(const char *name) {
|
|||
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
|
||||
item->name = strdup(name);
|
||||
item->unique_name = NULL;
|
||||
// TODO use static str if the default path instead of all these god-damn strdups
|
||||
item->object_path = strdup("/StatusNotifierItem");
|
||||
item->image = NULL;
|
||||
item->dirty = false;
|
||||
|
||||
|
|
@ -449,6 +310,21 @@ struct StatusNotifierItem *sni_create(const char *name) {
|
|||
|
||||
return item;
|
||||
}
|
||||
struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
|
||||
const char *object_path) {
|
||||
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
|
||||
// XXX strdup-ing twice to avoid a double-free; see above todo
|
||||
item->name = strdup(unique_name);
|
||||
item->unique_name = strdup(unique_name);
|
||||
item->object_path = strdup(object_path);
|
||||
item->image = NULL;
|
||||
item->dirty = false;
|
||||
// If they're registering by obj-path they're a special snowflake
|
||||
item->kde_special_snowflake = true;
|
||||
|
||||
get_icon(item);
|
||||
return item;
|
||||
}
|
||||
/* Return 0 if `item` has a name of `str` */
|
||||
int sni_str_cmp(const void *_item, const void *_str) {
|
||||
const struct StatusNotifierItem *item = _item;
|
||||
|
|
@ -466,14 +342,24 @@ int sni_uniq_cmp(const void *_item, const void *_str) {
|
|||
}
|
||||
return strcmp(item->unique_name, str);
|
||||
}
|
||||
int sni_obj_name_cmp(const void *_item, const void *_obj_name) {
|
||||
const struct StatusNotifierItem *item = _item;
|
||||
const struct ObjName *obj_name = _obj_name;
|
||||
|
||||
if (strcmp(item->unique_name, obj_name->name) == 0 &&
|
||||
strcmp(item->object_path, obj_name->obj_path) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void sni_free(struct StatusNotifierItem *item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
free(item->name);
|
||||
if (item->unique_name) {
|
||||
free(item->unique_name);
|
||||
}
|
||||
free(item->unique_name);
|
||||
free(item->object_path);
|
||||
if (item->image) {
|
||||
cairo_surface_destroy(item->image);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
static list_t *items = NULL;
|
||||
static list_t *hosts = NULL;
|
||||
static list_t *object_path_items = NULL;
|
||||
|
||||
/**
|
||||
* Describes the function of the StatusNotifierWatcher
|
||||
|
|
@ -18,6 +19,10 @@ static list_t *hosts = NULL;
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* We _also_ support registering items by object path (even though this is a
|
||||
* huge pain in the ass). Hosts that would like to subscribe to these items have
|
||||
* to go through the `org.swaywm.LessSuckyStatusNotifierWatcher` interface.
|
||||
*/
|
||||
static const char *interface_xml =
|
||||
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
|
||||
|
|
@ -64,8 +69,59 @@ static const char *interface_xml =
|
|||
" <arg type='' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
" <interface name='org.swaywm.LessSuckyStatusNotifierWatcher'>"
|
||||
" <property name='RegisteredObjectPathItems' type='a(os)' access='read'/>"
|
||||
" <signal name='ObjPathItemRegistered'>"
|
||||
" <arg type='os' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
|
||||
struct ObjPathItem {
|
||||
char *obj_path;
|
||||
char *unique_name;
|
||||
};
|
||||
|
||||
static void free_obj_path_item(struct ObjPathItem *item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
free(item->unique_name);
|
||||
free(item->obj_path);
|
||||
free(item);
|
||||
}
|
||||
static struct ObjPathItem *create_obj_path_item(const char *unique_name, const char *obj_path) {
|
||||
struct ObjPathItem *item = malloc(sizeof(struct ObjPathItem));
|
||||
if (!item) {
|
||||
return NULL;
|
||||
}
|
||||
item->unique_name = strdup(unique_name);
|
||||
item->obj_path = strdup(obj_path);
|
||||
if (!item->unique_name || !item->obj_path) {
|
||||
free_obj_path_item(item);
|
||||
return NULL;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
/**
|
||||
* NOTE: This compare function does have ordering, this is because it has to
|
||||
* comapre two strings.
|
||||
*/
|
||||
static int obj_path_item_cmp(const void *_item1, const void *_item2) {
|
||||
const struct ObjPathItem *item1 = _item1;
|
||||
const struct ObjPathItem *item2 = _item2;
|
||||
if (strcmp(item1->unique_name,item2->unique_name) == 0 &&
|
||||
strcmp(item1->obj_path,item2->obj_path) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int obj_path_unique_name_cmp(const void *_item, const void *_unique_name) {
|
||||
const struct ObjPathItem *item = _item;
|
||||
const char *unique_name = _unique_name;
|
||||
return strcmp(item->unique_name, unique_name);
|
||||
}
|
||||
|
||||
static void host_registered_signal(DBusConnection *connection) {
|
||||
// Send one signal for each protocol
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
|
|
@ -128,6 +184,19 @@ static void item_unregistered_signal(DBusConnection *connection, const char *nam
|
|||
dbus_message_unref(signal);
|
||||
}
|
||||
|
||||
static void obj_path_item_registered_signal(DBusConnection *connection, const struct ObjPathItem *item) {
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.swaywm.LessSuckyStatusNotifierWatcher",
|
||||
"ObjPathItemRegistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_OBJECT_PATH, &item->obj_path,
|
||||
DBUS_TYPE_STRING, &item->unique_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;
|
||||
|
||||
|
|
@ -141,35 +210,53 @@ static void respond_to_introspect(DBusConnection *connection, DBusMessage *reque
|
|||
|
||||
static void register_item(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
DBusMessage *reply;
|
||||
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_ERROR, "Error parsing method args: %s", error.message);
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
|
||||
sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"", name);
|
||||
|
||||
// Don't add duplicate or not real item
|
||||
if (!dbus_validate_bus_name(name, NULL)) {
|
||||
sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
|
||||
return;
|
||||
if (dbus_validate_path(name, NULL)) {
|
||||
// Item is registered by object path
|
||||
struct ObjPathItem *item =
|
||||
create_obj_path_item(dbus_message_get_sender(message), name);
|
||||
|
||||
// Add ObjPathItem
|
||||
if (list_seq_find(object_path_items, obj_path_item_cmp, item) != -1) {
|
||||
free_obj_path_item(item);
|
||||
return;
|
||||
}
|
||||
list_add(object_path_items, item);
|
||||
obj_path_item_registered_signal(connection, item);
|
||||
goto send_reply;
|
||||
} else {
|
||||
sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
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, strdup(name));
|
||||
item_registered_signal(connection, name);
|
||||
}
|
||||
|
||||
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, strdup(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);
|
||||
send_reply:
|
||||
// It's silly, but clients want a reply for this function
|
||||
reply = dbus_message_new_method_return(message);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
|
@ -182,10 +269,10 @@ static void register_host(DBusConnection *connection, DBusMessage *message) {
|
|||
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_ERROR, "Error parsing method args: %s", error.message);
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
|
||||
sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"", name);
|
||||
|
||||
// Don't add duplicate or not real host
|
||||
if (!dbus_validate_bus_name(name, NULL)) {
|
||||
|
|
@ -215,12 +302,12 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
|
|||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
|
||||
sway_log(L_ERROR, "Error parsing prop args: %s", error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
|
||||
sway_log(L_INFO, "Replying with items\n");
|
||||
sway_log(L_INFO, "Replying with items");
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter;
|
||||
|
|
@ -279,6 +366,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
|
|||
DBUS_TYPE_INT32, &version);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
} else if (strcmp(property, "RegisteredObjectPathItems") == 0) {
|
||||
sway_log(L_INFO, "Replying with ObjPathItems");
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant;
|
||||
DBusMessageIter array;
|
||||
DBusMessageIter dstruct;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"a(os)", &variant);
|
||||
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
|
||||
"(os)", &array);
|
||||
|
||||
for (int i = 0; i < object_path_items->length; ++i) {
|
||||
struct ObjPathItem *item = object_path_items->items[i];
|
||||
|
||||
dbus_message_iter_open_container(&array,
|
||||
DBUS_TYPE_STRUCT, NULL, &dstruct);
|
||||
|
||||
dbus_message_iter_append_basic(&dstruct,
|
||||
DBUS_TYPE_OBJECT_PATH, &item->obj_path);
|
||||
dbus_message_iter_append_basic(&dstruct,
|
||||
DBUS_TYPE_STRING, &item->unique_name);
|
||||
|
||||
dbus_message_iter_close_container(&array, &dstruct);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&variant, &array);
|
||||
dbus_message_iter_close_container(&iter, &variant);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
|
@ -289,6 +411,8 @@ static void set_property(DBusConnection *connection, DBusMessage *message) {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO clean me up please or get rid of me
|
||||
// also add LessSuckyStatusNotifierWatcher props
|
||||
static void get_all(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
|
@ -400,6 +524,8 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
|||
const char *old_owner;
|
||||
const char *new_owner;
|
||||
int index;
|
||||
bool found_obj_path_item = false;
|
||||
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_STRING, &old_owner,
|
||||
|
|
@ -427,6 +553,17 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
|||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
while ((index = list_seq_find(object_path_items, obj_path_unique_name_cmp, name)) != -1) {
|
||||
found_obj_path_item = true;
|
||||
struct ObjPathItem *item = object_path_items->items[index];
|
||||
sway_log(L_INFO, "ObjPathItem lost %s", item->obj_path);
|
||||
list_del(object_path_items, index);
|
||||
free_obj_path_item(item);
|
||||
}
|
||||
if (found_obj_path_item) {
|
||||
item_unregistered_signal(connection, name);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
|
@ -446,6 +583,7 @@ int init_sni_watcher() {
|
|||
|
||||
items = create_list();
|
||||
hosts = create_list();
|
||||
object_path_items = create_list();
|
||||
|
||||
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
||||
|
|
@ -456,7 +594,7 @@ int init_sni_watcher() {
|
|||
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);
|
||||
sway_log(L_ERROR, "dbus err getting watcher name: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +607,7 @@ int init_sni_watcher() {
|
|||
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);
|
||||
sway_log(L_ERROR, "dbus err getting kde watcher name: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -477,7 +615,7 @@ int init_sni_watcher() {
|
|||
"/StatusNotifierWatcher",
|
||||
&vtable, NULL, &error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s\n", error.message);
|
||||
sway_log(L_ERROR, "dbus_err: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,95 +38,81 @@ static void register_host(char *name) {
|
|||
dbus_message_unref(message);
|
||||
}
|
||||
|
||||
static void get_items_reply(DBusPendingCall *pending, void *_data) {
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_ERROR, "Got no items reply from sni watcher");
|
||||
goto bail;
|
||||
static void get_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
|
||||
if (status != PROP_EXISTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_ERROR, "Message is error: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant;
|
||||
DBusMessageIter array;
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
|
||||
sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
|
||||
goto bail;
|
||||
}
|
||||
// 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);
|
||||
|
||||
if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
|
||||
struct StatusNotifierItem *item = sni_create(name);
|
||||
|
||||
if (item) {
|
||||
sway_log(L_DEBUG, "Item registered with host: %s", name);
|
||||
list_add(tray->items, item);
|
||||
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);
|
||||
|
||||
struct ObjName obj_name = {
|
||||
object_path,
|
||||
unique_name,
|
||||
};
|
||||
if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
|
||||
struct StatusNotifierItem *item =
|
||||
sni_create_from_obj_path(unique_name, object_path);
|
||||
|
||||
if (item) {
|
||||
sway_log(L_DEBUG, "Item registered with host: %s", unique_name);
|
||||
list_add(tray->items, item);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_items() {
|
||||
// Clear list
|
||||
list_foreach(tray->items, (void (*)(void *))sni_free);
|
||||
list_free(tray->items);
|
||||
tray->items = create_list();
|
||||
|
||||
// O(n) function, could be faster dynamically reading values
|
||||
int len = dbus_message_iter_get_element_count(&variant);
|
||||
dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
|
||||
"/StatusNotifierWatcher","org.freedesktop.StatusNotifierWatcher",
|
||||
"RegisteredStatusNotifierItems", "as", get_items_reply, NULL);
|
||||
|
||||
dbus_message_iter_recurse(&variant, &array);
|
||||
for (int i = 0; i < len; i++) {
|
||||
const char *name;
|
||||
dbus_message_iter_get_basic(&array, &name);
|
||||
|
||||
struct StatusNotifierItem *item = sni_create(name);
|
||||
|
||||
if (item) {
|
||||
sway_log(L_DEBUG, "Item registered with host: %s", name);
|
||||
list_add(tray->items, item);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
}
|
||||
static void get_items() {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
|
||||
const char *iface = "org.freedesktop.StatusNotifierWatcher";
|
||||
const char *prop = "RegisteredStatusNotifierItems";
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get items");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_items_reply, NULL, 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,
|
||||
|
|
@ -162,11 +148,14 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
|||
}
|
||||
|
||||
int index;
|
||||
if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
|
||||
bool found_item = false;
|
||||
while ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
|
||||
found_item = true;
|
||||
sni_free(tray->items->items[index]);
|
||||
list_del(tray->items, index);
|
||||
dirty = true;
|
||||
} else {
|
||||
}
|
||||
if (found_item == false) {
|
||||
// If it's not in our list, then our list is incorrect.
|
||||
// Fetch all items again
|
||||
sway_log(L_INFO, "Host item list incorrect, refreshing");
|
||||
|
|
@ -178,16 +167,51 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
|||
"NewIcon") || dbus_message_is_signal(message,
|
||||
"org.kde.StatusNotifierItem", "NewIcon")) {
|
||||
const char *name;
|
||||
const char *obj_path;
|
||||
int index;
|
||||
struct StatusNotifierItem *item;
|
||||
|
||||
name = dbus_message_get_sender(message);
|
||||
if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
|
||||
obj_path = dbus_message_get_path(message);
|
||||
struct ObjName obj_name = {
|
||||
obj_path,
|
||||
name,
|
||||
};
|
||||
if ((index = list_seq_find(tray->items, sni_obj_name_cmp, &obj_name)) != -1) {
|
||||
item = tray->items->items[index];
|
||||
sway_log(L_INFO, "NewIcon signal from item %s", item->name);
|
||||
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)) {
|
||||
sway_log(L_ERROR, "Error getting ObjPathItemRegistered args");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
struct ObjName obj_name = {
|
||||
object_path,
|
||||
unique_name,
|
||||
};
|
||||
if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
|
||||
struct StatusNotifierItem *item =
|
||||
sni_create_from_obj_path(unique_name,
|
||||
object_path);
|
||||
|
||||
if (item) {
|
||||
list_add(tray->items, item);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
|
@ -234,6 +258,9 @@ static int init_host() {
|
|||
|
||||
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?
|
||||
|
|
@ -255,6 +282,15 @@ static int init_host() {
|
|||
sway_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)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SNI matches
|
||||
dbus_bus_add_match(conn,
|
||||
|
|
@ -287,9 +323,13 @@ err:
|
|||
return -1;
|
||||
}
|
||||
|
||||
void tray_mouse_event(struct output *output, int x, int y,
|
||||
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;
|
||||
|
|
@ -332,6 +372,24 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < tray->items->length; ++i) {
|
||||
struct StatusNotifierItem *item =
|
||||
tray->items->items[i];
|
||||
|
|
@ -358,6 +416,7 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
|||
list_add(output->items, render_item);
|
||||
} else if (item->dirty) {
|
||||
// item needs re-render
|
||||
sway_log(L_DEBUG, "Redrawing item %d for output %d", i, output->idx);
|
||||
sni_icon_ref_free(render_item);
|
||||
output->items->items[j] = render_item =
|
||||
sni_icon_ref_create(item, item_size);
|
||||
|
|
@ -373,7 +432,9 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
|||
cairo_fill(cairo);
|
||||
cairo_set_operator(cairo, op);
|
||||
|
||||
item->dirty = false;
|
||||
if (clean_item) {
|
||||
item->dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue