mirror of
https://github.com/swaywm/sway.git
synced 2026-03-31 07:11:24 -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 output {
|
||||||
struct window *window;
|
struct window *window;
|
||||||
struct registry *registry;
|
struct registry *registry;
|
||||||
|
struct output_state *state;
|
||||||
list_t *workspaces;
|
list_t *workspaces;
|
||||||
#ifdef ENABLE_TRAY
|
#ifdef ENABLE_TRAY
|
||||||
list_t *items;
|
list_t *items;
|
||||||
|
|
@ -28,6 +29,9 @@ struct output {
|
||||||
char *name;
|
char *name;
|
||||||
int idx;
|
int idx;
|
||||||
bool focused;
|
bool focused;
|
||||||
|
#ifdef ENABLE_TRAY
|
||||||
|
bool active;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct workspace {
|
struct workspace {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,37 @@
|
||||||
#include <dbus/dbus.h>
|
#include <dbus/dbus.h>
|
||||||
extern DBusConnection *conn;
|
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
|
* Should be called in main loop to dispatch events
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ struct StatusNotifierItem {
|
||||||
char *name;
|
char *name;
|
||||||
/* Unique bus name, needed for determining signal origins */
|
/* Unique bus name, needed for determining signal origins */
|
||||||
char *unique_name;
|
char *unique_name;
|
||||||
|
/* Object path, useful for items not registerd by well known name */
|
||||||
|
char *object_path;
|
||||||
bool kde_special_snowflake;
|
bool kde_special_snowflake;
|
||||||
|
|
||||||
cairo_surface_t *image;
|
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.
|
* May return `NULL` if `name` is not valid.
|
||||||
*/
|
*/
|
||||||
struct StatusNotifierItem *sni_create(const char *name);
|
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 *
|
* `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);
|
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.
|
* 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 */
|
||||||
set_window_height(bar_output->window, bar->config->height);
|
set_window_height(bar_output->window, bar->config->height);
|
||||||
|
|
||||||
|
bar_output->state = output;
|
||||||
}
|
}
|
||||||
/* spawn status command */
|
/* spawn status command */
|
||||||
spawn_status_cmd_proc(bar);
|
spawn_status_cmd_proc(bar);
|
||||||
|
|
@ -296,6 +298,11 @@ void bar_run(struct bar *bar) {
|
||||||
render(output, bar->config, bar->status);
|
render(output, bar->config, bar->status);
|
||||||
window_render(output->window);
|
window_render(output->window);
|
||||||
wl_display_flush(output->registry->display);
|
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
|
#define _XOPEN_SOURCE 700
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.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() {
|
void dispatch_dbus() {
|
||||||
if (!should_dispatch || !conn) {
|
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.
|
* Returns the directory of a given theme if it exists.
|
||||||
* The returned pointer must be freed.
|
* 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 ((basedir = getenv("XDG_DATA_DIRS"))) {
|
||||||
if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
|
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");
|
sway_log(L_ERROR, "Path too long to render");
|
||||||
// ditto
|
// ditto
|
||||||
|
free(basedir);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isdir(icon_dir)) {
|
if (isdir(icon_dir)) {
|
||||||
|
free(basedir);
|
||||||
return icon_dir;
|
return icon_dir;
|
||||||
}
|
}
|
||||||
|
token = strtok(NULL, ":");
|
||||||
|
}
|
||||||
|
free(basedir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spec says use "/usr/share/pixmaps/", but I see everything in
|
// 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_cat(dirs, inherits);
|
||||||
list_free(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");
|
dir = find_theme_dir("hicolor");
|
||||||
if (dir) {
|
if (dir) {
|
||||||
list_add(dirs, dir);
|
list_add(dirs, dir);
|
||||||
|
|
@ -290,6 +323,24 @@ fail:
|
||||||
return dirs;
|
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 */
|
/* Returns the file of an icon given its name and size */
|
||||||
static char *find_icon_file(const char *name, int size) {
|
static char *find_icon_file(const char *name, int size) {
|
||||||
int namelen = strlen(name);
|
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) {
|
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) {
|
if (image_path == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
#include "client/cairo.h"
|
#include "client/cairo.h"
|
||||||
#include "log.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.
|
// Not sure what this is but cairo needs it.
|
||||||
static const cairo_user_data_key_t cairo_user_data_key;
|
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 */
|
/* 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;
|
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 d_struct; /* (iiay) */
|
||||||
DBusMessageIter icon; /* ay */
|
DBusMessageIter struct_items;
|
||||||
|
DBusMessageIter icon;
|
||||||
|
|
||||||
dbus_message_iter_init(reply, &iter);
|
if (dbus_message_iter_get_element_count(iter) == 0) {
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// Can't recurse if there are no items
|
// Can't recurse if there are no items
|
||||||
sway_log(L_INFO, "Item has no icon");
|
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;
|
int width;
|
||||||
dbus_message_iter_get_basic(&d_struct, &width);
|
dbus_message_iter_get_basic(&struct_items, &width);
|
||||||
dbus_message_iter_next(&d_struct);
|
dbus_message_iter_next(&struct_items);
|
||||||
|
|
||||||
int height;
|
int height;
|
||||||
dbus_message_iter_get_basic(&d_struct, &height);
|
dbus_message_iter_get_basic(&struct_items, &height);
|
||||||
dbus_message_iter_next(&d_struct);
|
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) {
|
if (!len) {
|
||||||
sway_log(L_ERROR, "No icon data");
|
sway_log(L_ERROR, "No icon data");
|
||||||
goto bail;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also implies len % 4 == 0, useful below
|
// Also implies len % 4 == 0, useful below
|
||||||
if (len != width * height * 4) {
|
if (len != width * height * 4) {
|
||||||
sway_log(L_ERROR, "Incorrect array size passed");
|
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);
|
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||||
// FIXME support a variable stride
|
// FIXME support a variable stride
|
||||||
// (works on my machine though for all tested widths)
|
// (works on my machine though for all tested widths)
|
||||||
if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
|
if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
|
||||||
goto bail;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data is by reference, no need to free
|
// 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);
|
uint8_t *image_data = malloc(stride * height);
|
||||||
if (!image_data) {
|
if (!image_data) {
|
||||||
sway_log(L_ERROR, "Could not allocate memory for icon");
|
sway_log(L_ERROR, "Could not allocate memory for icon");
|
||||||
goto bail;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform from network byte order to host byte order
|
// Transform from network byte order to host byte order
|
||||||
|
|
@ -159,101 +125,29 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
|
||||||
item->dirty = true;
|
item->dirty = true;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
dbus_pending_call_unref(pending);
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
sway_log(L_ERROR, "Could not create image surface");
|
sway_log(L_ERROR, "Could not create image surface");
|
||||||
free(image_data);
|
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");
|
sway_log(L_ERROR, "Could not get icon from item");
|
||||||
return;
|
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,
|
/* Get an icon by its name */
|
||||||
DBUS_TYPE_STRING, &iface,
|
static void reply_icon_name(DBusMessageIter *iter, void *_data, enum property_status status) {
|
||||||
DBUS_TYPE_STRING, &prop,
|
struct StatusNotifierItem *item = _data;
|
||||||
DBUS_TYPE_INVALID);
|
|
||||||
|
|
||||||
bool status =
|
if (status != PROP_EXISTS) {
|
||||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
dbus_get_prop_async(item->name, item->object_path,
|
||||||
|
(item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
|
||||||
dbus_message_unref(message);
|
"IconPixmap", "a(iiay)", reply_icon, item);
|
||||||
|
|
||||||
if (!(pending || status)) {
|
|
||||||
sway_log(L_ERROR, "Could not get item icon");
|
|
||||||
return;
|
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;
|
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);
|
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;
|
item->dirty = true;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
dbus_pending_call_unref(pending);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bail:
|
|
||||||
if (reply) {
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
}
|
|
||||||
dbus_pending_call_unref(pending);
|
|
||||||
// Now try the pixmap
|
// Now try the pixmap
|
||||||
send_icon_msg(item);
|
dbus_get_prop_async(item->name, item->object_path,
|
||||||
return;
|
(item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
|
||||||
}
|
"IconPixmap", "a(iiay)", reply_icon, item);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_icon(struct StatusNotifierItem *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) {
|
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");
|
: "org.freedesktop.StatusNotifierItem");
|
||||||
DBusMessage *message = dbus_message_new_method_call(
|
DBusMessage *message = dbus_message_new_method_call(
|
||||||
item->name,
|
item->name,
|
||||||
"/StatusNotifierItem",
|
item->object_path,
|
||||||
iface,
|
iface,
|
||||||
"Activate");
|
"Activate");
|
||||||
|
|
||||||
|
|
@ -342,9 +200,10 @@ void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||||
const char *iface =
|
const char *iface =
|
||||||
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
||||||
: "org.freedesktop.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(
|
DBusMessage *message = dbus_message_new_method_call(
|
||||||
item->name,
|
item->name,
|
||||||
"/StatusNotifierItem",
|
item->object_path,
|
||||||
iface,
|
iface,
|
||||||
"ContextMenu");
|
"ContextMenu");
|
||||||
|
|
||||||
|
|
@ -363,7 +222,7 @@ void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||||
: "org.freedesktop.StatusNotifierItem");
|
: "org.freedesktop.StatusNotifierItem");
|
||||||
DBusMessage *message = dbus_message_new_method_call(
|
DBusMessage *message = dbus_message_new_method_call(
|
||||||
item->name,
|
item->name,
|
||||||
"/StatusNotifierItem",
|
item->object_path,
|
||||||
iface,
|
iface,
|
||||||
"SecondaryActivate");
|
"SecondaryActivate");
|
||||||
|
|
||||||
|
|
@ -426,6 +285,8 @@ struct StatusNotifierItem *sni_create(const char *name) {
|
||||||
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
|
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
|
||||||
item->name = strdup(name);
|
item->name = strdup(name);
|
||||||
item->unique_name = NULL;
|
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->image = NULL;
|
||||||
item->dirty = false;
|
item->dirty = false;
|
||||||
|
|
||||||
|
|
@ -449,6 +310,21 @@ struct StatusNotifierItem *sni_create(const char *name) {
|
||||||
|
|
||||||
return item;
|
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` */
|
/* Return 0 if `item` has a name of `str` */
|
||||||
int sni_str_cmp(const void *_item, const void *_str) {
|
int sni_str_cmp(const void *_item, const void *_str) {
|
||||||
const struct StatusNotifierItem *item = _item;
|
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);
|
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) {
|
void sni_free(struct StatusNotifierItem *item) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
free(item->name);
|
free(item->name);
|
||||||
if (item->unique_name) {
|
|
||||||
free(item->unique_name);
|
free(item->unique_name);
|
||||||
}
|
free(item->object_path);
|
||||||
if (item->image) {
|
if (item->image) {
|
||||||
cairo_surface_destroy(item->image);
|
cairo_surface_destroy(item->image);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
static list_t *items = NULL;
|
static list_t *items = NULL;
|
||||||
static list_t *hosts = NULL;
|
static list_t *hosts = NULL;
|
||||||
|
static list_t *object_path_items = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the function of the StatusNotifierWatcher
|
* 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
|
* 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.
|
* 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 =
|
static const char *interface_xml =
|
||||||
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
|
"<!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'/>"
|
" <arg type='' name='service' direction='out'/>"
|
||||||
" </signal>"
|
" </signal>"
|
||||||
" </interface>"
|
" </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>";
|
"</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) {
|
static void host_registered_signal(DBusConnection *connection) {
|
||||||
// Send one signal for each protocol
|
// Send one signal for each protocol
|
||||||
DBusMessage *signal = dbus_message_new_signal(
|
DBusMessage *signal = dbus_message_new_signal(
|
||||||
|
|
@ -128,6 +184,19 @@ static void item_unregistered_signal(DBusConnection *connection, const char *nam
|
||||||
dbus_message_unref(signal);
|
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) {
|
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
|
||||||
DBusMessage *reply;
|
DBusMessage *reply;
|
||||||
|
|
||||||
|
|
@ -141,22 +210,38 @@ static void respond_to_introspect(DBusConnection *connection, DBusMessage *reque
|
||||||
|
|
||||||
static void register_item(DBusConnection *connection, DBusMessage *message) {
|
static void register_item(DBusConnection *connection, DBusMessage *message) {
|
||||||
DBusError error;
|
DBusError error;
|
||||||
|
DBusMessage *reply;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
dbus_error_init(&error);
|
dbus_error_init(&error);
|
||||||
if (!dbus_message_get_args(message, &error,
|
if (!dbus_message_get_args(message, &error,
|
||||||
DBUS_TYPE_STRING, &name,
|
DBUS_TYPE_STRING, &name,
|
||||||
DBUS_TYPE_INVALID)) {
|
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
|
// Don't add duplicate or not real item
|
||||||
if (!dbus_validate_bus_name(name, NULL)) {
|
if (!dbus_validate_bus_name(name, NULL)) {
|
||||||
|
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.");
|
sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -167,9 +252,11 @@ static void register_item(DBusConnection *connection, DBusMessage *message) {
|
||||||
|
|
||||||
list_add(items, strdup(name));
|
list_add(items, strdup(name));
|
||||||
item_registered_signal(connection, name);
|
item_registered_signal(connection, name);
|
||||||
|
}
|
||||||
|
|
||||||
// It's silly, but xembedsniproxy wants a reply for this function
|
send_reply:
|
||||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
// 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_connection_send(connection, reply, NULL);
|
||||||
dbus_message_unref(reply);
|
dbus_message_unref(reply);
|
||||||
}
|
}
|
||||||
|
|
@ -182,10 +269,10 @@ static void register_host(DBusConnection *connection, DBusMessage *message) {
|
||||||
if (!dbus_message_get_args(message, &error,
|
if (!dbus_message_get_args(message, &error,
|
||||||
DBUS_TYPE_STRING, &name,
|
DBUS_TYPE_STRING, &name,
|
||||||
DBUS_TYPE_INVALID)) {
|
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
|
// Don't add duplicate or not real host
|
||||||
if (!dbus_validate_bus_name(name, NULL)) {
|
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, &interface,
|
||||||
DBUS_TYPE_STRING, &property,
|
DBUS_TYPE_STRING, &property,
|
||||||
DBUS_TYPE_INVALID)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
|
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
|
||||||
sway_log(L_INFO, "Replying with items\n");
|
sway_log(L_INFO, "Replying with items");
|
||||||
DBusMessage *reply;
|
DBusMessage *reply;
|
||||||
reply = dbus_message_new_method_return(message);
|
reply = dbus_message_new_method_return(message);
|
||||||
DBusMessageIter iter;
|
DBusMessageIter iter;
|
||||||
|
|
@ -279,6 +366,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
|
||||||
DBUS_TYPE_INT32, &version);
|
DBUS_TYPE_INT32, &version);
|
||||||
|
|
||||||
dbus_message_iter_close_container(&iter, &sub);
|
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_connection_send(connection, reply, NULL);
|
||||||
dbus_message_unref(reply);
|
dbus_message_unref(reply);
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +411,8 @@ static void set_property(DBusConnection *connection, DBusMessage *message) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO clean me up please or get rid of me
|
||||||
|
// also add LessSuckyStatusNotifierWatcher props
|
||||||
static void get_all(DBusConnection *connection, DBusMessage *message) {
|
static void get_all(DBusConnection *connection, DBusMessage *message) {
|
||||||
DBusMessage *reply;
|
DBusMessage *reply;
|
||||||
reply = dbus_message_new_method_return(message);
|
reply = dbus_message_new_method_return(message);
|
||||||
|
|
@ -400,6 +524,8 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||||
const char *old_owner;
|
const char *old_owner;
|
||||||
const char *new_owner;
|
const char *new_owner;
|
||||||
int index;
|
int index;
|
||||||
|
bool found_obj_path_item = false;
|
||||||
|
|
||||||
if (!dbus_message_get_args(message, NULL,
|
if (!dbus_message_get_args(message, NULL,
|
||||||
DBUS_TYPE_STRING, &name,
|
DBUS_TYPE_STRING, &name,
|
||||||
DBUS_TYPE_STRING, &old_owner,
|
DBUS_TYPE_STRING, &old_owner,
|
||||||
|
|
@ -427,6 +553,17 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||||
|
|
||||||
return DBUS_HANDLER_RESULT_HANDLED;
|
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;
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
@ -446,6 +583,7 @@ int init_sni_watcher() {
|
||||||
|
|
||||||
items = create_list();
|
items = create_list();
|
||||||
hosts = create_list();
|
hosts = create_list();
|
||||||
|
object_path_items = create_list();
|
||||||
|
|
||||||
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
|
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
|
||||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
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");
|
sway_log(L_INFO, "Could not get watcher name, it may start later");
|
||||||
}
|
}
|
||||||
if (dbus_error_is_set(&error)) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,7 +607,7 @@ int init_sni_watcher() {
|
||||||
sway_log(L_INFO, "Could not get kde watcher name, it may start later");
|
sway_log(L_INFO, "Could not get kde watcher name, it may start later");
|
||||||
}
|
}
|
||||||
if (dbus_error_is_set(&error)) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,7 +615,7 @@ int init_sni_watcher() {
|
||||||
"/StatusNotifierWatcher",
|
"/StatusNotifierWatcher",
|
||||||
&vtable, NULL, &error);
|
&vtable, NULL, &error);
|
||||||
if (dbus_error_is_set(&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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,56 +38,21 @@ static void register_host(char *name) {
|
||||||
dbus_message_unref(message);
|
dbus_message_unref(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_items_reply(DBusPendingCall *pending, void *_data) {
|
static void get_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
|
||||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
if (status != PROP_EXISTS) {
|
||||||
|
return;
|
||||||
if (!reply) {
|
|
||||||
sway_log(L_ERROR, "Got no items reply from sni watcher");
|
|
||||||
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;
|
|
||||||
DBusMessageIter array;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// O(n) function, could be faster dynamically reading values
|
||||||
int len = dbus_message_iter_get_element_count(&variant);
|
int len = dbus_message_iter_get_element_count(iter);
|
||||||
|
|
||||||
dbus_message_iter_recurse(&variant, &array);
|
dbus_message_iter_recurse(iter, &array);
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
const char *name;
|
const char *name;
|
||||||
dbus_message_iter_get_basic(&array, &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);
|
struct StatusNotifierItem *item = sni_create(name);
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
|
|
@ -96,37 +61,58 @@ static void get_items_reply(DBusPendingCall *pending, void *_data) {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bail:
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
dbus_pending_call_unref(pending);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
static void get_items() {
|
static void get_obj_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
|
||||||
DBusPendingCall *pending;
|
if (status != PROP_EXISTS) {
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
DBusMessageIter array;
|
||||||
|
DBusMessageIter dstruct;
|
||||||
|
|
||||||
dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
|
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();
|
||||||
|
|
||||||
|
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,
|
static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||||
|
|
@ -162,11 +148,14 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
int index;
|
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]);
|
sni_free(tray->items->items[index]);
|
||||||
list_del(tray->items, index);
|
list_del(tray->items, index);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
} else {
|
}
|
||||||
|
if (found_item == false) {
|
||||||
// If it's not in our list, then our list is incorrect.
|
// If it's not in our list, then our list is incorrect.
|
||||||
// Fetch all items again
|
// Fetch all items again
|
||||||
sway_log(L_INFO, "Host item list incorrect, refreshing");
|
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,
|
"NewIcon") || dbus_message_is_signal(message,
|
||||||
"org.kde.StatusNotifierItem", "NewIcon")) {
|
"org.kde.StatusNotifierItem", "NewIcon")) {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
const char *obj_path;
|
||||||
int index;
|
int index;
|
||||||
struct StatusNotifierItem *item;
|
struct StatusNotifierItem *item;
|
||||||
|
|
||||||
name = dbus_message_get_sender(message);
|
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];
|
item = tray->items->items[index];
|
||||||
sway_log(L_INFO, "NewIcon signal from item %s", item->name);
|
sway_log(L_INFO, "NewIcon signal from item %s", item->name);
|
||||||
get_icon(item);
|
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_HANDLED;
|
||||||
}
|
}
|
||||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
|
@ -234,6 +258,9 @@ static int init_host() {
|
||||||
|
|
||||||
register_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();
|
get_items();
|
||||||
|
|
||||||
// Perhaps use addmatch helper functions like wlc does?
|
// 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);
|
sway_log(L_ERROR, "dbus_err: %s", error.message);
|
||||||
return -1;
|
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
|
// SNI matches
|
||||||
dbus_bus_add_match(conn,
|
dbus_bus_add_match(conn,
|
||||||
|
|
@ -287,9 +323,13 @@ err:
|
||||||
return -1;
|
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) {
|
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;
|
struct window *window = output->window;
|
||||||
uint32_t tray_padding = swaybar.config->tray_padding;
|
uint32_t tray_padding = swaybar.config->tray_padding;
|
||||||
int tray_width = window->width * window->scale;
|
int tray_width = window->width * window->scale;
|
||||||
|
|
@ -332,6 +372,24 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
||||||
return tray_width;
|
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) {
|
for (int i = 0; i < tray->items->length; ++i) {
|
||||||
struct StatusNotifierItem *item =
|
struct StatusNotifierItem *item =
|
||||||
tray->items->items[i];
|
tray->items->items[i];
|
||||||
|
|
@ -358,6 +416,7 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
||||||
list_add(output->items, render_item);
|
list_add(output->items, render_item);
|
||||||
} else if (item->dirty) {
|
} else if (item->dirty) {
|
||||||
// item needs re-render
|
// item needs re-render
|
||||||
|
sway_log(L_DEBUG, "Redrawing item %d for output %d", i, output->idx);
|
||||||
sni_icon_ref_free(render_item);
|
sni_icon_ref_free(render_item);
|
||||||
output->items->items[j] = render_item =
|
output->items->items[j] = render_item =
|
||||||
sni_icon_ref_create(item, item_size);
|
sni_icon_ref_create(item, item_size);
|
||||||
|
|
@ -373,8 +432,10 @@ uint32_t tray_render(struct output *output, struct config *config) {
|
||||||
cairo_fill(cairo);
|
cairo_fill(cairo);
|
||||||
cairo_set_operator(cairo, op);
|
cairo_set_operator(cairo, op);
|
||||||
|
|
||||||
|
if (clean_item) {
|
||||||
item->dirty = false;
|
item->dirty = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (tray_width != window->width * window->scale) {
|
if (tray_width != window->width * window->scale) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue