commit
c1090ad7dc
@ -56,12 +56,6 @@ workflows:
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Ubuntu 14.04
|
||||
distro: ubuntu-trusty
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Ubuntu 16.04
|
||||
distro: ubuntu-xenial
|
||||
|
@ -14,7 +14,7 @@ addons:
|
||||
- libnotify-dev
|
||||
- libgtk-3-dev
|
||||
- valgrind
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: c
|
||||
|
||||
@ -35,5 +35,3 @@ matrix:
|
||||
after_success:
|
||||
- coveralls
|
||||
- compiler: clang
|
||||
after_success:
|
||||
- coveralls --gcov llvm-cov --gcov-options gcov
|
||||
|
@ -23,7 +23,7 @@ LDFLAGS_DEBUG :=
|
||||
|
||||
pkg_config_packs := gio-2.0 \
|
||||
gdk-pixbuf-2.0 \
|
||||
"glib-2.0 >= 2.36" \
|
||||
"glib-2.0 >= 2.44" \
|
||||
pangocairo \
|
||||
x11 \
|
||||
xinerama \
|
||||
|
405
src/dbus.c
405
src/dbus.c
@ -72,24 +72,42 @@ static const char *stack_tag_hints[] = {
|
||||
"x-dunst-stack-tag"
|
||||
};
|
||||
|
||||
static void on_get_capabilities(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation);
|
||||
static void on_notify(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation);
|
||||
static void on_close_notification(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation);
|
||||
static void on_get_server_information(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation);
|
||||
struct dbus_method {
|
||||
const char *method_name;
|
||||
void (*method) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation);
|
||||
};
|
||||
|
||||
#define DBUS_METHOD(name) static void dbus_cb_##name( \
|
||||
GDBusConnection *connection, \
|
||||
const gchar *sender, \
|
||||
GVariant *parameters, \
|
||||
GDBusMethodInvocation *invocation)
|
||||
|
||||
static struct raw_image *get_raw_image_from_data_hint(GVariant *icon_data);
|
||||
|
||||
int cmp_methods(const void *vkey, const void *velem)
|
||||
{
|
||||
const char *key = (const char*)vkey;
|
||||
const struct dbus_method *m = (const struct dbus_method*)velem;
|
||||
|
||||
return strcmp(key, m->method_name);
|
||||
}
|
||||
|
||||
DBUS_METHOD(Notify);
|
||||
DBUS_METHOD(CloseNotification);
|
||||
DBUS_METHOD(GetCapabilities);
|
||||
DBUS_METHOD(GetServerInformation);
|
||||
|
||||
static struct dbus_method methods_fdn[] = {
|
||||
{"CloseNotification", dbus_cb_CloseNotification},
|
||||
{"GetCapabilities", dbus_cb_GetCapabilities},
|
||||
{"GetServerInformation", dbus_cb_GetServerInformation},
|
||||
{"Notify", dbus_cb_Notify},
|
||||
};
|
||||
|
||||
void handle_method_call(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
@ -99,14 +117,15 @@ void handle_method_call(GDBusConnection *connection,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (STR_EQ(method_name, "GetCapabilities")) {
|
||||
on_get_capabilities(connection, sender, parameters, invocation);
|
||||
} else if (STR_EQ(method_name, "Notify")) {
|
||||
on_notify(connection, sender, parameters, invocation);
|
||||
} else if (STR_EQ(method_name, "CloseNotification")) {
|
||||
on_close_notification(connection, sender, parameters, invocation);
|
||||
} else if (STR_EQ(method_name, "GetServerInformation")) {
|
||||
on_get_server_information(connection, sender, parameters, invocation);
|
||||
struct dbus_method *m = bsearch(
|
||||
method_name,
|
||||
&methods_fdn,
|
||||
G_N_ELEMENTS(methods_fdn),
|
||||
sizeof(struct dbus_method),
|
||||
cmp_methods);
|
||||
|
||||
if (m) {
|
||||
m->method(connection, sender, parameters, invocation);
|
||||
} else {
|
||||
LOG_M("Unknown method name: '%s' (sender: '%s').",
|
||||
method_name,
|
||||
@ -114,10 +133,11 @@ void handle_method_call(GDBusConnection *connection,
|
||||
}
|
||||
}
|
||||
|
||||
static void on_get_capabilities(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
static void dbus_cb_GetCapabilities(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
GVariantBuilder *builder;
|
||||
GVariant *value;
|
||||
@ -142,160 +162,151 @@ static void on_get_capabilities(GDBusConnection *connection,
|
||||
|
||||
static struct notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters)
|
||||
{
|
||||
/* Assert that the parameters' type is actually correct. Albeit usually DBus
|
||||
* already rejects ill typed parameters, it may not be always the case. */
|
||||
GVariantType *required_type = g_variant_type_new("(susssasa{sv}i)");
|
||||
if (!g_variant_is_of_type(parameters, required_type)) {
|
||||
g_variant_type_free(required_type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct notification *n = notification_create();
|
||||
|
||||
n->actions = g_malloc0(sizeof(struct actions));
|
||||
n->dbus_client = g_strdup(sender);
|
||||
n->dbus_valid = true;
|
||||
|
||||
{
|
||||
GVariantIter *iter = g_variant_iter_new(parameters);
|
||||
GVariant *content;
|
||||
GVariant *dict_value;
|
||||
int idx = 0;
|
||||
while ((content = g_variant_iter_next_value(iter))) {
|
||||
GVariant *hints;
|
||||
gchar **actions;
|
||||
int timeout;
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
|
||||
n->appname = g_variant_dup_string(content, NULL);
|
||||
break;
|
||||
case 1:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_UINT32))
|
||||
n->id = g_variant_get_uint32(content);
|
||||
break;
|
||||
case 2:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
|
||||
n->icon = g_variant_dup_string(content, NULL);
|
||||
break;
|
||||
case 3:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
|
||||
n->summary = g_variant_dup_string(content, NULL);
|
||||
break;
|
||||
case 4:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
|
||||
n->body = g_variant_dup_string(content, NULL);
|
||||
break;
|
||||
case 5:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY))
|
||||
n->actions->actions = g_variant_dup_strv(content, &(n->actions->count));
|
||||
break;
|
||||
case 6:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_DICTIONARY)) {
|
||||
GVariantIter i;
|
||||
g_variant_iter_init(&i, parameters);
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "urgency", G_VARIANT_TYPE_BYTE);
|
||||
if (dict_value) {
|
||||
n->urgency = g_variant_get_byte(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
g_variant_iter_next(&i, "s", &n->appname);
|
||||
g_variant_iter_next(&i, "u", &n->id);
|
||||
g_variant_iter_next(&i, "s", &n->icon);
|
||||
g_variant_iter_next(&i, "s", &n->summary);
|
||||
g_variant_iter_next(&i, "s", &n->body);
|
||||
g_variant_iter_next(&i, "^a&s", &actions);
|
||||
g_variant_iter_next(&i, "@a{?*}", &hints);
|
||||
g_variant_iter_next(&i, "i", &timeout);
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "fgcolor", G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
n->colors.fg = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "bgcolor", G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
n->colors.bg = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "frcolor", G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
n->colors.frame = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "category", G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
n->category = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "image-path", G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
g_free(n->icon);
|
||||
n->icon = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(content, "image-data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (!dict_value)
|
||||
dict_value = g_variant_lookup_value(content, "image_data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (!dict_value)
|
||||
dict_value = g_variant_lookup_value(content, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (dict_value) {
|
||||
n->raw_icon = get_raw_image_from_data_hint(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
/* Check for transient hints
|
||||
*
|
||||
* According to the spec, the transient hint should be boolean.
|
||||
* But notify-send does not support hints of type 'boolean'.
|
||||
* So let's check for int and boolean until notify-send is fixed.
|
||||
*/
|
||||
if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_BOOLEAN))) {
|
||||
n->transient = g_variant_get_boolean(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
} else if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_UINT32))) {
|
||||
n->transient = g_variant_get_uint32(dict_value) > 0;
|
||||
g_variant_unref(dict_value);
|
||||
} else if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_INT32))) {
|
||||
n->transient = g_variant_get_int32(dict_value) > 0;
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if((dict_value = g_variant_lookup_value(content, "value", G_VARIANT_TYPE_INT32))) {
|
||||
n->progress = g_variant_get_int32(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
} else if((dict_value = g_variant_lookup_value(content, "value", G_VARIANT_TYPE_UINT32))) {
|
||||
n->progress = g_variant_get_uint32(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
/* Check for hints that define the stack_tag
|
||||
*
|
||||
* Only accept to first one we find.
|
||||
*/
|
||||
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
|
||||
dict_value = g_variant_lookup_value(content, stack_tag_hints[i], G_VARIANT_TYPE_STRING);
|
||||
if (dict_value) {
|
||||
n->stack_tag = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (g_variant_is_of_type(content, G_VARIANT_TYPE_INT32))
|
||||
n->timeout = g_variant_get_int32(content) * 1000;
|
||||
break;
|
||||
}
|
||||
g_variant_unref(content);
|
||||
idx++;
|
||||
gsize num = 0;
|
||||
while (actions[num]) {
|
||||
if (actions[num+1]) {
|
||||
g_hash_table_insert(n->actions,
|
||||
g_strdup(actions[num]),
|
||||
g_strdup(actions[num+1]));
|
||||
num+=2;
|
||||
} else {
|
||||
LOG_W("Odd length in actions array. Ignoring element: %s", actions[num]);
|
||||
break;
|
||||
}
|
||||
|
||||
g_variant_iter_free(iter);
|
||||
}
|
||||
|
||||
if (n->actions->count < 1)
|
||||
g_clear_pointer(&n->actions, actions_free);
|
||||
GVariant *dict_value;
|
||||
if ((dict_value = g_variant_lookup_value(hints, "urgency", G_VARIANT_TYPE_BYTE))) {
|
||||
n->urgency = g_variant_get_byte(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) {
|
||||
n->colors.fg = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "bgcolor", G_VARIANT_TYPE_STRING))) {
|
||||
n->colors.bg = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "frcolor", G_VARIANT_TYPE_STRING))) {
|
||||
n->colors.frame = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
|
||||
n->category = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "image-path", G_VARIANT_TYPE_STRING))) {
|
||||
g_free(n->icon);
|
||||
n->icon = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
dict_value = g_variant_lookup_value(hints, "image-data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (!dict_value)
|
||||
dict_value = g_variant_lookup_value(hints, "image_data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (!dict_value)
|
||||
dict_value = g_variant_lookup_value(hints, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
|
||||
if (dict_value) {
|
||||
n->raw_icon = get_raw_image_from_data_hint(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
/* Check for transient hints
|
||||
*
|
||||
* According to the spec, the transient hint should be boolean.
|
||||
* But notify-send does not support hints of type 'boolean'.
|
||||
* So let's check for int and boolean until notify-send is fixed.
|
||||
*/
|
||||
if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_BOOLEAN))) {
|
||||
n->transient = g_variant_get_boolean(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
} else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_UINT32))) {
|
||||
n->transient = g_variant_get_uint32(dict_value) > 0;
|
||||
g_variant_unref(dict_value);
|
||||
} else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_INT32))) {
|
||||
n->transient = g_variant_get_int32(dict_value) > 0;
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_INT32))) {
|
||||
n->progress = g_variant_get_int32(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
} else if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_UINT32))) {
|
||||
n->progress = g_variant_get_uint32(dict_value);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
/* Check for hints that define the stack_tag
|
||||
*
|
||||
* Only accept to first one we find.
|
||||
*/
|
||||
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
|
||||
if ((dict_value = g_variant_lookup_value(hints, stack_tag_hints[i], G_VARIANT_TYPE_STRING))) {
|
||||
n->stack_tag = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout >= 0)
|
||||
n->timeout = timeout * 1000;
|
||||
|
||||
g_variant_unref(hints);
|
||||
g_variant_type_free(required_type);
|
||||
g_free(actions); // the strv is only a shallow copy
|
||||
|
||||
notification_init(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void on_notify(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
static void dbus_cb_Notify(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
struct notification *n = dbus_message_to_notification(sender, parameters);
|
||||
if (!n) {
|
||||
g_dbus_method_invocation_return_dbus_error(
|
||||
invocation,
|
||||
"Cannot decode notification!",
|
||||
"");
|
||||
}
|
||||
|
||||
int id = queues_notification_insert(n);
|
||||
|
||||
GVariant *reply = g_variant_new("(u)", id);
|
||||
@ -311,10 +322,11 @@ static void on_notify(GDBusConnection *connection,
|
||||
wake_up();
|
||||
}
|
||||
|
||||
static void on_close_notification(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
static void dbus_cb_CloseNotification(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
guint32 id;
|
||||
g_variant_get(parameters, "(u)", &id);
|
||||
@ -324,10 +336,11 @@ static void on_close_notification(GDBusConnection *connection,
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void on_get_server_information(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
static void dbus_cb_GetServerInformation(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
GVariant *value;
|
||||
|
||||
@ -404,9 +417,9 @@ static const GDBusInterfaceVTable interface_vtable = {
|
||||
handle_method_call
|
||||
};
|
||||
|
||||
static void on_bus_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
static void dbus_cb_bus_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
guint registration_id;
|
||||
|
||||
@ -425,9 +438,9 @@ static void on_bus_acquired(GDBusConnection *connection,
|
||||
}
|
||||
}
|
||||
|
||||
static void on_name_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
static void dbus_cb_name_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
dbus_conn = connection;
|
||||
}
|
||||
@ -438,7 +451,7 @@ static void on_name_acquired(GDBusConnection *connection,
|
||||
* If name or vendor specified, the name and vendor
|
||||
* will get additionally get via the FDN GetServerInformation method
|
||||
*
|
||||
* @param connection The dbus connection
|
||||
* @param connection The DBus connection
|
||||
* @param pid The place to report the PID to
|
||||
* @param name The place to report the name to, if not required set to NULL
|
||||
* @param vendor The place to report the vendor to, if not required set to NULL
|
||||
@ -446,9 +459,9 @@ static void on_name_acquired(GDBusConnection *connection,
|
||||
* @returns `true` on success, otherwise `false`
|
||||
*/
|
||||
static bool dbus_get_fdn_daemon_info(GDBusConnection *connection,
|
||||
int *pid,
|
||||
char **name,
|
||||
char **vendor)
|
||||
guint *pid,
|
||||
char **name,
|
||||
char **vendor)
|
||||
{
|
||||
g_return_val_if_fail(pid, false);
|
||||
g_return_val_if_fail(connection, false);
|
||||
@ -475,7 +488,7 @@ static bool dbus_get_fdn_daemon_info(GDBusConnection *connection,
|
||||
return false;
|
||||
}
|
||||
|
||||
GVariant *daemoninfo;
|
||||
GVariant *daemoninfo = NULL;
|
||||
if (name || vendor) {
|
||||
daemoninfo = g_dbus_proxy_call_sync(
|
||||
proxy_fdn,
|
||||
@ -529,27 +542,29 @@ static bool dbus_get_fdn_daemon_info(GDBusConnection *connection,
|
||||
return false;
|
||||
}
|
||||
|
||||
g_variant_get(pidinfo, "(u)", &pid);
|
||||
|
||||
g_object_unref(proxy_fdn);
|
||||
g_object_unref(proxy_dbus);
|
||||
g_free(owner);
|
||||
if (daemoninfo)
|
||||
g_variant_unref(daemoninfo);
|
||||
if (pidinfo)
|
||||
g_variant_unref(pidinfo);
|
||||
|
||||
return true;
|
||||
if (pidinfo) {
|
||||
g_variant_get(pidinfo, "(u)", pid);
|
||||
g_variant_unref(pidinfo);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void on_name_lost(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
static void dbus_cb_name_lost(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (connection) {
|
||||
char *name;
|
||||
int pid;
|
||||
unsigned int pid;
|
||||
if (dbus_get_fdn_daemon_info(connection, &pid, &name, NULL)) {
|
||||
DIE("Cannot acquire '"FDN_NAME"': "
|
||||
"Name is acquired by '%s' with PID '%d'.", name, pid);
|
||||
@ -608,9 +623,9 @@ int dbus_init(void)
|
||||
owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
|
||||
FDN_NAME,
|
||||
G_BUS_NAME_OWNER_FLAGS_NONE,
|
||||
on_bus_acquired,
|
||||
on_name_acquired,
|
||||
on_name_lost,
|
||||
dbus_cb_bus_acquired,
|
||||
dbus_cb_name_acquired,
|
||||
dbus_cb_name_lost,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
|
86
src/menu.c
86
src/menu.c
@ -141,6 +141,27 @@ void open_browser(const char *in)
|
||||
g_free(url);
|
||||
}
|
||||
|
||||
char *notification_dmenu_string(struct notification *n)
|
||||
{
|
||||
char *dmenu_str = NULL;
|
||||
|
||||
gpointer p_key;
|
||||
gpointer p_value;
|
||||
GHashTableIter iter;
|
||||
g_hash_table_iter_init(&iter, n->actions);
|
||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
||||
|
||||
char *key = (char*) p_key;
|
||||
char *value = (char*) p_value;
|
||||
|
||||
char *act_str = g_strdup_printf("#%s (%s) [%d,%s]", value, n->summary, n->id, key);
|
||||
dmenu_str = string_append(dmenu_str, act_str, "\n");
|
||||
|
||||
g_free(act_str);
|
||||
}
|
||||
return dmenu_str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify the corresponding client
|
||||
* that an action has been invoked
|
||||
@ -148,39 +169,49 @@ void open_browser(const char *in)
|
||||
void invoke_action(const char *action)
|
||||
{
|
||||
struct notification *invoked = NULL;
|
||||
char *action_identifier = NULL;
|
||||
uint id;
|
||||
|
||||
char *appname_begin = strchr(action, '[');
|
||||
if (!appname_begin) {
|
||||
char *data_start, *data_comma, *data_end;
|
||||
|
||||
/* format: #<human readable> (<summary>)[<id>,<action>] */
|
||||
data_start = strrchr(action, '[');
|
||||
if (!data_start) {
|
||||
LOG_W("Invalid action: '%s'", action);
|
||||
return;
|
||||
}
|
||||
appname_begin++;
|
||||
int appname_len = strlen(appname_begin) - 1; // remove ]
|
||||
int action_len = strlen(action) - appname_len - 3; // remove space, [, ]
|
||||
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
id = strtol(++data_start, &data_comma, 10);
|
||||
if (*data_comma != ',') {
|
||||
LOG_W("Invalid action: '%s'", action);
|
||||
return;
|
||||
}
|
||||
|
||||
data_end = strchr(data_comma+1, ']');
|
||||
if (!data_end) {
|
||||
LOG_W("Invalid action: '%s'", action);
|
||||
return;
|
||||
}
|
||||
|
||||
char *action_key = g_strndup(data_comma+1, data_end-data_comma-1);
|
||||
|
||||
for (const GList *iter = queues_get_displayed();
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
if (g_str_has_prefix(appname_begin, n->appname) && strlen(n->appname) == appname_len) {
|
||||
if (!n->actions)
|
||||
continue;
|
||||
if (n->id != id)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
char *a_identifier = n->actions->actions[i];
|
||||
char *name = n->actions->actions[i + 1];
|
||||
if (g_str_has_prefix(action, name) && strlen(name) == action_len) {
|
||||
invoked = n;
|
||||
action_identifier = a_identifier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (g_hash_table_contains(n->actions, action_key)) {
|
||||
invoked = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invoked && action_identifier) {
|
||||
signal_action_invoked(invoked, action_identifier);
|
||||
if (invoked && action_key) {
|
||||
signal_action_invoked(invoked, action_key);
|
||||
}
|
||||
|
||||
g_free(action_key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,7 +320,7 @@ static gpointer context_menu_thread(gpointer data)
|
||||
|
||||
|
||||
// Reference and lock the notification if we need it
|
||||
if (n->urls || n->actions) {
|
||||
if (n->urls || g_hash_table_size(n->actions)) {
|
||||
notification_ref(n);
|
||||
|
||||
struct notification_lock *nl =
|
||||
@ -302,13 +333,12 @@ static gpointer context_menu_thread(gpointer data)
|
||||
locked_notifications = g_list_prepend(locked_notifications, nl);
|
||||
}
|
||||
|
||||
char *dmenu_str = notification_dmenu_string(n);
|
||||
dmenu_input = string_append(dmenu_input, dmenu_str, "\n");
|
||||
g_free(dmenu_str);
|
||||
|
||||
if (n->urls)
|
||||
dmenu_input = string_append(dmenu_input, n->urls, "\n");
|
||||
|
||||
if (n->actions)
|
||||
dmenu_input =
|
||||
string_append(dmenu_input, n->actions->dmenu_str,
|
||||
"\n");
|
||||
}
|
||||
|
||||
dmenu_output = invoke_dmenu(dmenu_input);
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
static void notification_extract_urls(struct notification *n);
|
||||
static void notification_format_message(struct notification *n);
|
||||
static void notification_dmenu_string(struct notification *n);
|
||||
|
||||
/* see notification.h */
|
||||
const char *enum_to_string_fullscreen(enum behavior_fullscreen in)
|
||||
@ -75,16 +74,16 @@ void notification_print(const struct notification *n)
|
||||
printf("\t}\n");
|
||||
g_free(urls);
|
||||
}
|
||||
|
||||
if (n->actions) {
|
||||
printf("\tactions:\n");
|
||||
printf("\t{\n");
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
printf("\t\t[%s,%s]\n", n->actions->actions[i],
|
||||
n->actions->actions[i + 1]);
|
||||
}
|
||||
if (g_hash_table_size(n->actions) == 0) {
|
||||
printf("\tactions: {}\n");
|
||||
} else {
|
||||
gpointer p_key, p_value;
|
||||
GHashTableIter iter;
|
||||
g_hash_table_iter_init(&iter, n->actions);
|
||||
printf("\tactions: {\n");
|
||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value))
|
||||
printf("\t\t\"%s\": \"%s\"\n", (char*)p_key, (char*)p_value);
|
||||
printf("\t}\n");
|
||||
printf("\tactions_dmenu: %s\n", n->actions->dmenu_str);
|
||||
}
|
||||
printf("\tscript: %s\n", n->script);
|
||||
printf("}\n");
|
||||
@ -189,17 +188,6 @@ int notification_is_duplicate(const struct notification *a, const struct notific
|
||||
&& a->urgency == b->urgency;
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
void actions_free(struct actions *a)
|
||||
{
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
g_strfreev(a->actions);
|
||||
g_free(a->dmenu_str);
|
||||
g_free(a);
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
void rawimage_free(struct raw_image *i)
|
||||
{
|
||||
@ -253,7 +241,7 @@ void notification_unref(struct notification *n)
|
||||
g_free(n->colors.frame);
|
||||
g_free(n->stack_tag);
|
||||
|
||||
actions_free(n->actions);
|
||||
g_hash_table_unref(n->actions);
|
||||
rawimage_free(n->raw_icon);
|
||||
|
||||
notification_private_free(n->priv);
|
||||
@ -318,6 +306,8 @@ struct notification *notification_create(void)
|
||||
|
||||
n->fullscreen = FS_SHOW;
|
||||
|
||||
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -377,7 +367,6 @@ void notification_init(struct notification *n)
|
||||
|
||||
/* UPDATE derived fields */
|
||||
notification_extract_urls(n);
|
||||
notification_dmenu_string(n);
|
||||
notification_format_message(n);
|
||||
}
|
||||
|
||||
@ -508,23 +497,6 @@ static void notification_extract_urls(struct notification *n)
|
||||
g_free(urls_text);
|
||||
}
|
||||
|
||||
static void notification_dmenu_string(struct notification *n)
|
||||
{
|
||||
if (n->actions) {
|
||||
g_clear_pointer(&n->actions->dmenu_str, g_free);
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
char *human_readable = n->actions->actions[i + 1];
|
||||
string_replace_char('[', '(', human_readable); // kill square brackets
|
||||
string_replace_char(']', ')', human_readable);
|
||||
|
||||
char *act_str = g_strdup_printf("#%s [%s]", human_readable, n->appname);
|
||||
if (act_str) {
|
||||
n->actions->dmenu_str = string_append(n->actions->dmenu_str, act_str, "\n");
|
||||
g_free(act_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notification_update_text_to_render(struct notification *n)
|
||||
{
|
||||
@ -536,14 +508,14 @@ void notification_update_text_to_render(struct notification *n)
|
||||
|
||||
/* print dup_count and msg */
|
||||
if ((n->dup_count > 0 && !settings.hide_duplicate_count)
|
||||
&& (n->actions || n->urls) && settings.show_indicators) {
|
||||
&& (g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) {
|
||||
buf = g_strdup_printf("(%d%s%s) %s",
|
||||
n->dup_count,
|
||||
n->actions ? "A" : "",
|
||||
g_hash_table_size(n->actions) ? "A" : "",
|
||||
n->urls ? "U" : "", msg);
|
||||
} else if ((n->actions || n->urls) && settings.show_indicators) {
|
||||
} else if ((g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) {
|
||||
buf = g_strdup_printf("(%s%s) %s",
|
||||
n->actions ? "A" : "",
|
||||
g_hash_table_size(n->actions) ? "A" : "",
|
||||
n->urls ? "U" : "", msg);
|
||||
} else if (n->dup_count > 0 && !settings.hide_duplicate_count) {
|
||||
buf = g_strdup_printf("(%d) %s", n->dup_count, msg);
|
||||
@ -584,16 +556,16 @@ void notification_update_text_to_render(struct notification *n)
|
||||
/* see notification.h */
|
||||
void notification_do_action(const struct notification *n)
|
||||
{
|
||||
if (n->actions) {
|
||||
if (n->actions->count == 2) {
|
||||
signal_action_invoked(n, n->actions->actions[0]);
|
||||
if (g_hash_table_size(n->actions)) {
|
||||
if (g_hash_table_contains(n->actions, "default")) {
|
||||
signal_action_invoked(n, "default");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
if (STR_EQ(n->actions->actions[i], "default")) {
|
||||
signal_action_invoked(n, n->actions->actions[i]);
|
||||
return;
|
||||
}
|
||||
if (g_hash_table_size(n->actions) == 1) {
|
||||
GList *keys = g_hash_table_get_keys(n->actions);
|
||||
signal_action_invoked(n, keys->data);
|
||||
g_list_free(keys);
|
||||
return;
|
||||
}
|
||||
context_menu();
|
||||
|
||||
|
@ -36,12 +36,6 @@ struct raw_image {
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
struct actions {
|
||||
char **actions;
|
||||
char *dmenu_str;
|
||||
gsize count;
|
||||
};
|
||||
|
||||
typedef struct _notification_private NotificationPrivate;
|
||||
|
||||
struct notification_colors {
|
||||
@ -69,7 +63,7 @@ struct notification {
|
||||
gint64 timestamp; /**< arrival time */
|
||||
gint64 timeout; /**< time to display */
|
||||
|
||||
struct actions *actions;
|
||||
GHashTable *actions;
|
||||
|
||||
enum markup_mode markup;
|
||||
const char *format;
|
||||
@ -127,13 +121,6 @@ void notification_ref(struct notification *n);
|
||||
*/
|
||||
void notification_init(struct notification *n);
|
||||
|
||||
/**
|
||||
* Free the actions structure
|
||||
*
|
||||
* @param a (nullable): Pointer to #actions
|
||||
*/
|
||||
void actions_free(struct actions *a);
|
||||
|
||||
/**
|
||||
* Free a #raw_image
|
||||
*
|
||||
|
753
test/dbus.c
Normal file
753
test/dbus.c
Normal file
@ -0,0 +1,753 @@
|
||||
#define wake_up wake_up_void
|
||||
#include "../src/dbus.c"
|
||||
#include "greatest.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "queues.h"
|
||||
|
||||
void wake_up_void(void) { }
|
||||
|
||||
struct signal_actioninvoked {
|
||||
guint id;
|
||||
gchar *key;
|
||||
guint subscription_id;
|
||||
GDBusConnection *conn;
|
||||
};
|
||||
|
||||
struct signal_closed {
|
||||
guint32 id;
|
||||
guint32 reason;
|
||||
guint subscription_id;
|
||||
GDBusConnection *conn;
|
||||
};
|
||||
|
||||
void dbus_signal_cb_actioninvoked(GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail(user_data);
|
||||
|
||||
guint32 id;
|
||||
gchar *key;
|
||||
|
||||
struct signal_actioninvoked *sig = (struct signal_actioninvoked*) user_data;
|
||||
|
||||
g_variant_get(parameters, "(us)", &id, &key);
|
||||
|
||||
if (id == sig->id) {
|
||||
sig->id = id;
|
||||
sig->key = key;
|
||||
}
|
||||
}
|
||||
|
||||
void dbus_signal_subscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
|
||||
{
|
||||
assert(actioninvoked);
|
||||
|
||||
actioninvoked->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
actioninvoked->subscription_id =
|
||||
g_dbus_connection_signal_subscribe(
|
||||
actioninvoked->conn,
|
||||
FDN_NAME,
|
||||
FDN_IFAC,
|
||||
"ActionInvoked",
|
||||
FDN_PATH,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
dbus_signal_cb_actioninvoked,
|
||||
actioninvoked,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void dbus_signal_unsubscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
|
||||
{
|
||||
assert(actioninvoked);
|
||||
|
||||
g_dbus_connection_signal_unsubscribe(actioninvoked->conn, actioninvoked->subscription_id);
|
||||
g_object_unref(actioninvoked->conn);
|
||||
|
||||
actioninvoked->conn = NULL;
|
||||
actioninvoked->subscription_id = -1;
|
||||
}
|
||||
|
||||
void dbus_signal_cb_closed(GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail(user_data);
|
||||
|
||||
guint32 id;
|
||||
guint32 reason;
|
||||
|
||||
struct signal_closed *sig = (struct signal_closed*) user_data;
|
||||
g_variant_get(parameters, "(uu)", &id, &reason);
|
||||
|
||||
if (id == sig->id) {
|
||||
sig->id = id;
|
||||
sig->reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
void dbus_signal_subscribe_closed(struct signal_closed *closed)
|
||||
{
|
||||
assert(closed);
|
||||
|
||||
closed->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
closed->subscription_id =
|
||||
g_dbus_connection_signal_subscribe(
|
||||
closed->conn,
|
||||
FDN_NAME,
|
||||
FDN_IFAC,
|
||||
"NotificationClosed",
|
||||
FDN_PATH,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
dbus_signal_cb_closed,
|
||||
closed,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void dbus_signal_unsubscribe_closed(struct signal_closed *closed)
|
||||
{
|
||||
assert(closed);
|
||||
|
||||
g_dbus_connection_signal_unsubscribe(closed->conn, closed->subscription_id);
|
||||
g_object_unref(closed->conn);
|
||||
|
||||
closed->conn = NULL;
|
||||
closed->subscription_id = -1;
|
||||
}
|
||||
|
||||
GVariant *dbus_invoke(const char *method, GVariant *params)
|
||||
{
|
||||
GDBusConnection *connection_client;
|
||||
GVariant *retdata;
|
||||
GError *error = NULL;
|
||||
|
||||
connection_client = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
retdata = g_dbus_connection_call_sync(
|
||||
connection_client,
|
||||
FDN_NAME,
|
||||
FDN_PATH,
|
||||
FDN_IFAC,
|
||||
method,
|
||||
params,
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
if (error) {
|
||||
printf("Error while calling GTestDBus instance: %s\n", error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
g_object_unref(connection_client);
|
||||
|
||||
return retdata;
|
||||
}
|
||||
|
||||
struct dbus_notification {
|
||||
const char* app_name;
|
||||
guint replaces_id;
|
||||
const char* app_icon;
|
||||
const char* summary;
|
||||
const char* body;
|
||||
GHashTable *actions;
|
||||
GHashTable *hints;
|
||||
int expire_timeout;
|
||||
};
|
||||
|
||||
void g_free_variant_value(gpointer tofree)
|
||||
{
|
||||
g_variant_unref((GVariant*) tofree);
|
||||
}
|
||||
|
||||
struct dbus_notification *dbus_notification_new(void)
|
||||
{
|
||||
struct dbus_notification *n = g_malloc0(sizeof(struct dbus_notification));
|
||||
n->expire_timeout = -1;
|
||||
n->replaces_id = 0;
|
||||
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
n->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_variant_value);
|
||||
return n;
|
||||
}
|
||||
|
||||
void dbus_notification_free(struct dbus_notification *n)
|
||||
{
|
||||
g_hash_table_unref(n->hints);
|
||||
g_hash_table_unref(n->actions);
|
||||
g_free(n);
|
||||
}
|
||||
|
||||
bool dbus_notification_fire(struct dbus_notification *n, uint *id)
|
||||
{
|
||||
assert(n);
|
||||
assert(id);
|
||||
GVariantBuilder b;
|
||||
GVariantType *t;
|
||||
|
||||
gpointer p_key;
|
||||
gpointer p_value;
|
||||
GHashTableIter iter;
|
||||
|
||||
t = g_variant_type_new("(susssasa{sv}i)");
|
||||
g_variant_builder_init(&b, t);
|
||||
g_variant_type_free(t);
|
||||
|
||||
g_variant_builder_add(&b, "s", n->app_name);
|
||||
g_variant_builder_add(&b, "u", n->replaces_id);
|
||||
g_variant_builder_add(&b, "s", n->app_icon);
|
||||
g_variant_builder_add(&b, "s", n->summary);
|
||||
g_variant_builder_add(&b, "s", n->body);
|
||||
|
||||
// Add the actions
|
||||
t = g_variant_type_new("as");
|
||||
g_variant_builder_open(&b, t);
|
||||
g_hash_table_iter_init(&iter, n->actions);
|
||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
||||
g_variant_builder_add(&b, "s", (char*)p_key);
|
||||
g_variant_builder_add(&b, "s", (char*)p_value);
|
||||
}
|
||||
// Add an invalid appendix to cover odd numbered action arrays
|
||||
// Shouldn't interfere with normal testing
|
||||
g_variant_builder_add(&b, "s", "invalid appendix");
|
||||
g_variant_builder_close(&b);
|
||||
g_variant_type_free(t);
|
||||
|
||||
// Add the hints
|
||||
t = g_variant_type_new("a{sv}");
|
||||
g_variant_builder_open(&b, t);
|
||||
g_hash_table_iter_init(&iter, n->hints);
|
||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
||||
g_variant_builder_add(&b, "{sv}", (char*)p_key, (GVariant*)p_value);
|
||||
}
|
||||
g_variant_builder_close(&b);
|
||||
g_variant_type_free(t);
|
||||
|
||||
g_variant_builder_add(&b, "i", n->expire_timeout);
|
||||
|
||||
GVariant *reply = dbus_invoke("Notify", g_variant_builder_end(&b));
|
||||
if (reply) {
|
||||
g_variant_get(reply, "(u)", id);
|
||||
g_variant_unref(reply);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/////// TESTS
|
||||
gint owner_id;
|
||||
|
||||
TEST test_dbus_init(void)
|
||||
{
|
||||
owner_id = dbus_init();
|
||||
uint waiting = 0;
|
||||
while (!dbus_conn && waiting < 2000) {
|
||||
usleep(500);
|
||||
waiting++;
|
||||
}
|
||||
ASSERTm("After 1s, there is still no dbus connection available.",
|
||||
dbus_conn);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_dbus_teardown(void)
|
||||
{
|
||||
dbus_teardown(owner_id);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_invalid_notification(void)
|
||||
{
|
||||
GVariant *faulty = g_variant_new_boolean(true);
|
||||
|
||||
ASSERT(NULL == dbus_message_to_notification(":123", faulty));
|
||||
ASSERT(NULL == dbus_invoke("Notify", faulty));
|
||||
|
||||
g_variant_unref(faulty);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_empty_notification(void)
|
||||
{
|
||||
struct dbus_notification *n = dbus_notification_new();
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
dbus_notification_free(n);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_basic_notification(void)
|
||||
{
|
||||
struct dbus_notification *n = dbus_notification_new();
|
||||
gsize len = queues_length_waiting();
|
||||
n->app_name = "dunstteststack";
|
||||
n->app_icon = "NONE";
|
||||
n->summary = "Headline";
|
||||
n->body = "Text";
|
||||
g_hash_table_insert(n->actions, g_strdup("actionid"), g_strdup("Print this text"));
|
||||
g_hash_table_insert(n->hints,
|
||||
g_strdup("x-dunst-stack-tag"),
|
||||
g_variant_ref_sink(g_variant_new_string("volume")));
|
||||
|
||||
n->replaces_id = 10;
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
dbus_notification_free(n);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_dbus_notify_colors(void)
|
||||
{
|
||||
const char *color_frame = "I allow all string values for frame!";
|
||||
const char *color_bg = "I allow all string values for background!";
|
||||
const char *color_fg = "I allow all string values for foreground!";
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_dbus_notify_colors";
|
||||
n_dbus->body = "Summary of it";
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("frcolor"),
|
||||
g_variant_ref_sink(g_variant_new_string(color_frame)));
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("bgcolor"),
|
||||
g_variant_ref_sink(g_variant_new_string(color_bg)));
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("fgcolor"),
|
||||
g_variant_ref_sink(g_variant_new_string(color_fg)));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
ASSERT_STR_EQ(n->colors.frame, color_frame);
|
||||
ASSERT_STR_EQ(n->colors.fg, color_fg);
|
||||
ASSERT_STR_EQ(n->colors.bg, color_bg);
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_hint_transient(void)
|
||||
{
|
||||
static char msg[50];
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_transient";
|
||||
n_dbus->body = "Summary of it";
|
||||
|
||||
bool values[] = { true, true, true, false, false, false, false };
|
||||
GVariant *variants[] = {
|
||||
g_variant_new_boolean(true),
|
||||
g_variant_new_int32(1),
|
||||
g_variant_new_uint32(1),
|
||||
g_variant_new_boolean(false),
|
||||
g_variant_new_int32(0),
|
||||
g_variant_new_uint32(0),
|
||||
g_variant_new_int32(-1),
|
||||
};
|
||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("transient"),
|
||||
g_variant_ref_sink(variants[i]));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
||||
ASSERT_EQm(msg, values[i], n->transient);
|
||||
}
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_hint_progress(void)
|
||||
{
|
||||
static char msg[50];
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_progress";
|
||||
n_dbus->body = "Summary of it";
|
||||
|
||||
int values[] = { 99, 12, 123, 123, -1, -1 };
|
||||
GVariant *variants[] = {
|
||||
g_variant_new_int32(99),
|
||||
g_variant_new_uint32(12),
|
||||
g_variant_new_int32(123), // allow higher than 100
|
||||
g_variant_new_uint32(123),
|
||||
g_variant_new_int32(-192),
|
||||
g_variant_new_uint32(-192),
|
||||
};
|
||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("value"),
|
||||
g_variant_ref_sink(variants[i]));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
||||
ASSERT_EQm(msg, values[i], n->progress);
|
||||
}
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_hint_icons(void)
|
||||
{
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
const char *iconname = "NEWICON";
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_icons";
|
||||
n_dbus->body = "Summary of it";
|
||||
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("image-path"),
|
||||
g_variant_ref_sink(g_variant_new_string(iconname)));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
ASSERT_STR_EQ(iconname, n->icon);
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_hint_category(void)
|
||||
{
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
const char *category = "VOLUME";
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_category";
|
||||
n_dbus->body = "Summary of it";
|
||||
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("category"),
|
||||
g_variant_ref_sink(g_variant_new_string(category)));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
ASSERT_STR_EQ(category, n->category);
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_hint_urgency(void)
|
||||
{
|
||||
static char msg[50];
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
|
||||
gsize len = queues_length_waiting();
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_urgency";
|
||||
n_dbus->body = "Summary of it";
|
||||
|
||||
enum urgency values[] = { URG_MAX, URG_LOW, URG_NORM, URG_CRIT };
|
||||
GVariant *variants[] = {
|
||||
g_variant_new_byte(10),
|
||||
g_variant_new_byte(0),
|
||||
g_variant_new_byte(1),
|
||||
g_variant_new_byte(2),
|
||||
};
|
||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("urgency"),
|
||||
g_variant_ref_sink(variants[i]));
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
|
||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
||||
ASSERT_EQm(msg, values[i], n->urgency);
|
||||
|
||||
queues_notification_close_id(id, REASON_UNDEF);
|
||||
}
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_server_caps(enum markup_mode markup)
|
||||
{
|
||||
GVariant *reply;
|
||||
GVariant *caps = NULL;
|
||||
const char **capsarray;
|
||||
|
||||
settings.markup = markup;
|
||||
|
||||
reply = dbus_invoke("GetCapabilities", NULL);
|
||||
|
||||
caps = g_variant_get_child_value(reply, 0);
|
||||
capsarray = g_variant_get_strv(caps, NULL);
|
||||
|
||||
ASSERT(capsarray);
|
||||
ASSERT(g_strv_contains(capsarray, "actions"));
|
||||
ASSERT(g_strv_contains(capsarray, "body"));
|
||||
ASSERT(g_strv_contains(capsarray, "body-hyperlinks"));
|
||||
ASSERT(g_strv_contains(capsarray, "x-dunst-stack-tag"));
|
||||
|
||||
if (settings.markup != MARKUP_NO)
|
||||
ASSERT(g_strv_contains(capsarray, "body-markup"));
|
||||
else
|
||||
ASSERT_FALSE(g_strv_contains(capsarray, "body-markup"));
|
||||
|
||||
g_free(capsarray);
|
||||
g_variant_unref(caps);
|
||||
g_variant_unref(reply);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_signal_actioninvoked(void)
|
||||
{
|
||||
const struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
struct signal_actioninvoked sig = {0, NULL, -1};
|
||||
|
||||
dbus_signal_subscribe_actioninvoked(&sig);
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE2";
|
||||
n_dbus->summary = "Headline for New";
|
||||
n_dbus->body = "Text";
|
||||
g_hash_table_insert(n_dbus->actions, g_strdup("actionkey"), g_strdup("Print this text"));
|
||||
|
||||
dbus_notification_fire(n_dbus, &sig.id);
|
||||
n = queues_debug_find_notification_by_id(sig.id);
|
||||
|
||||
signal_action_invoked(n, "actionkey");
|
||||
|
||||
uint waiting = 0;
|
||||
while (!sig.key && waiting < 2000) {
|
||||
usleep(500);
|
||||
waiting++;
|
||||
}
|
||||
|
||||
ASSERT_STR_EQ("actionkey", sig.key);
|
||||
|
||||
g_free(sig.key);
|
||||
dbus_notification_free(n_dbus);
|
||||
dbus_signal_unsubscribe_actioninvoked(&sig);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_close_and_signal(void)
|
||||
{
|
||||
GVariant *data, *ret;
|
||||
struct dbus_notification *n;
|
||||
struct signal_closed sig = {0, REASON_MIN-1, -1};
|
||||
|
||||
dbus_signal_subscribe_closed(&sig);
|
||||
|
||||
n = dbus_notification_new();
|
||||
n->app_name = "dunstteststack";
|
||||
n->app_icon = "NONE2";
|
||||
n->summary = "Headline for New";
|
||||
n->body = "Text";
|
||||
|
||||
dbus_notification_fire(n, &sig.id);
|
||||
|
||||
data = g_variant_new("(u)", sig.id);
|
||||
ret = dbus_invoke("CloseNotification", data);
|
||||
|
||||
ASSERT(ret);
|
||||
|
||||
uint waiting = 0;
|
||||
while (sig.reason == REASON_MIN-1 && waiting < 2000) {
|
||||
usleep(500);
|
||||
waiting++;
|
||||
}
|
||||
|
||||
ASSERT(sig.reason != REASON_MIN-1);
|
||||
|
||||
dbus_notification_free(n);
|
||||
dbus_signal_unsubscribe_closed(&sig);
|
||||
g_variant_unref(ret);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_fdn_daemon_info(void)
|
||||
{
|
||||
unsigned int pid_is;
|
||||
pid_t pid_should;
|
||||
char *name, *vendor;
|
||||
GDBusConnection *conn;
|
||||
|
||||
pid_should = getpid();
|
||||
conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
|
||||
ASSERT(dbus_get_fdn_daemon_info(conn, &pid_is, &name, &vendor));
|
||||
|
||||
ASSERT_EQ_FMT(pid_should, pid_is, "%d");
|
||||
ASSERT_STR_EQ("dunst", name);
|
||||
ASSERT_STR_EQ("knopwob", vendor);
|
||||
|
||||
g_free(name);
|
||||
g_free(vendor);
|
||||
|
||||
g_object_unref(conn);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST assert_methodlists_sorted(void)
|
||||
{
|
||||
for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_fdn); i++) {
|
||||
ASSERT(0 > strcmp(
|
||||
methods_fdn[i].method_name,
|
||||
methods_fdn[i+1].method_name));
|
||||
}
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
// TESTS END
|
||||
|
||||
GMainLoop *loop;
|
||||
GThread *thread_tests;
|
||||
|
||||
gpointer run_threaded_tests(gpointer data)
|
||||
{
|
||||
RUN_TEST(test_dbus_init);
|
||||
|
||||
RUN_TEST(test_get_fdn_daemon_info);
|
||||
|
||||
RUN_TEST(test_empty_notification);
|
||||
RUN_TEST(test_basic_notification);
|
||||
RUN_TEST(test_invalid_notification);
|
||||
RUN_TEST(test_hint_transient);
|
||||
RUN_TEST(test_hint_progress);
|
||||
RUN_TEST(test_hint_icons);
|
||||
RUN_TEST(test_hint_category);
|
||||
RUN_TEST(test_hint_urgency);
|
||||
RUN_TEST(test_dbus_notify_colors);
|
||||
RUN_TESTp(test_server_caps, MARKUP_FULL);
|
||||
RUN_TESTp(test_server_caps, MARKUP_STRIP);
|
||||
RUN_TESTp(test_server_caps, MARKUP_NO);
|
||||
RUN_TEST(test_close_and_signal);
|
||||
RUN_TEST(test_signal_actioninvoked);
|
||||
|
||||
RUN_TEST(assert_methodlists_sorted);
|
||||
|
||||
RUN_TEST(test_dbus_teardown);
|
||||
g_main_loop_quit(loop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUITE(suite_dbus)
|
||||
{
|
||||
GTestDBus *dbus_bus;
|
||||
g_test_dbus_unset();
|
||||
queues_init();
|
||||
|
||||
loop = g_main_loop_new(NULL, false);
|
||||
|
||||
dbus_bus = g_test_dbus_new(G_TEST_DBUS_NONE);
|
||||
g_test_dbus_up(dbus_bus);
|
||||
|
||||
thread_tests = g_thread_new("testexecutor", run_threaded_tests, loop);
|
||||
g_main_loop_run(loop);
|
||||
|
||||
queues_teardown();
|
||||
g_test_dbus_down(dbus_bus);
|
||||
g_object_unref(dbus_bus);
|
||||
g_thread_unref(thread_tests);
|
||||
g_main_loop_unref(loop);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
@ -6,6 +6,23 @@
|
||||
#include "greatest.h"
|
||||
#include "queues.h"
|
||||
|
||||
struct notification *queues_debug_find_notification_by_id(int id)
|
||||
{
|
||||
assert(id > 0);
|
||||
|
||||
GQueue *allqueues[] = { displayed, waiting, history };
|
||||
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
|
||||
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *cur = iter->data;
|
||||
if (cur->id == id)
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST test_queue_length(void)
|
||||
{
|
||||
queues_init();
|
||||
|
@ -50,5 +50,8 @@ static inline struct notification *test_notification(const char *name, gint64 ti
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Retrieve a notification by its id. Solely for debugging purposes */
|
||||
struct notification *queues_debug_find_notification_by_id(int id);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
@ -19,6 +19,7 @@ SUITE_EXTERN(suite_queues);
|
||||
SUITE_EXTERN(suite_dunst);
|
||||
SUITE_EXTERN(suite_log);
|
||||
SUITE_EXTERN(suite_menu);
|
||||
SUITE_EXTERN(suite_dbus);
|
||||
|
||||
GREATEST_MAIN_DEFS();
|
||||
|
||||
@ -44,6 +45,7 @@ int main(int argc, char *argv[]) {
|
||||
RUN_SUITE(suite_dunst);
|
||||
RUN_SUITE(suite_log);
|
||||
RUN_SUITE(suite_menu);
|
||||
RUN_SUITE(suite_dbus);
|
||||
GREATEST_MAIN_END();
|
||||
|
||||
base = NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user