Add DBus interface to control dunst
This commit is contained in:
parent
0d038021a6
commit
55f4971a92
233
src/dbus.c
233
src/dbus.c
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "menu.h"
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "queues.h"
|
#include "queues.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
@ -17,6 +18,12 @@
|
|||||||
#define FDN_IFAC "org.freedesktop.Notifications"
|
#define FDN_IFAC "org.freedesktop.Notifications"
|
||||||
#define FDN_NAME "org.freedesktop.Notifications"
|
#define FDN_NAME "org.freedesktop.Notifications"
|
||||||
|
|
||||||
|
#define DUNST_PATH "/org/freedesktop/Notifications"
|
||||||
|
#define DUNST_IFAC "org.dunstproject.cmd0"
|
||||||
|
#define DUNST_NAME "org.freedesktop.Notifications"
|
||||||
|
|
||||||
|
#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties"
|
||||||
|
|
||||||
GDBusConnection *dbus_conn;
|
GDBusConnection *dbus_conn;
|
||||||
|
|
||||||
static GDBusNodeInfo *introspection_data = NULL;
|
static GDBusNodeInfo *introspection_data = NULL;
|
||||||
@ -62,6 +69,18 @@ static const char *introspection_xml =
|
|||||||
" <arg name=\"id\" type=\"u\"/>"
|
" <arg name=\"id\" type=\"u\"/>"
|
||||||
" <arg name=\"action_key\" type=\"s\"/>"
|
" <arg name=\"action_key\" type=\"s\"/>"
|
||||||
" </signal>"
|
" </signal>"
|
||||||
|
" </interface>"
|
||||||
|
" <interface name=\""DUNST_IFAC"\">"
|
||||||
|
|
||||||
|
" <method name=\"ContextMenuCall\" />"
|
||||||
|
" <method name=\"NotificationCloseLast\" />"
|
||||||
|
" <method name=\"NotificationCloseAll\" />"
|
||||||
|
" <method name=\"NotificationShow\" />"
|
||||||
|
|
||||||
|
" <property name=\"running\" type=\"b\" access=\"readwrite\">"
|
||||||
|
" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
|
||||||
|
" </property>"
|
||||||
|
|
||||||
" </interface>"
|
" </interface>"
|
||||||
"</node>";
|
"</node>";
|
||||||
|
|
||||||
@ -80,6 +99,7 @@ struct dbus_method {
|
|||||||
GDBusMethodInvocation *invocation);
|
GDBusMethodInvocation *invocation);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: call it interface methods
|
||||||
#define DBUS_METHOD(name) static void dbus_cb_##name( \
|
#define DBUS_METHOD(name) static void dbus_cb_##name( \
|
||||||
GDBusConnection *connection, \
|
GDBusConnection *connection, \
|
||||||
const gchar *sender, \
|
const gchar *sender, \
|
||||||
@ -98,7 +118,6 @@ DBUS_METHOD(Notify);
|
|||||||
DBUS_METHOD(CloseNotification);
|
DBUS_METHOD(CloseNotification);
|
||||||
DBUS_METHOD(GetCapabilities);
|
DBUS_METHOD(GetCapabilities);
|
||||||
DBUS_METHOD(GetServerInformation);
|
DBUS_METHOD(GetServerInformation);
|
||||||
|
|
||||||
static struct dbus_method methods_fdn[] = {
|
static struct dbus_method methods_fdn[] = {
|
||||||
{"CloseNotification", dbus_cb_CloseNotification},
|
{"CloseNotification", dbus_cb_CloseNotification},
|
||||||
{"GetCapabilities", dbus_cb_GetCapabilities},
|
{"GetCapabilities", dbus_cb_GetCapabilities},
|
||||||
@ -106,7 +125,7 @@ static struct dbus_method methods_fdn[] = {
|
|||||||
{"Notify", dbus_cb_Notify},
|
{"Notify", dbus_cb_Notify},
|
||||||
};
|
};
|
||||||
|
|
||||||
void handle_method_call(GDBusConnection *connection,
|
void dbus_cb_fdn_methods(GDBusConnection *connection,
|
||||||
const gchar *sender,
|
const gchar *sender,
|
||||||
const gchar *object_path,
|
const gchar *object_path,
|
||||||
const gchar *interface_name,
|
const gchar *interface_name,
|
||||||
@ -115,9 +134,9 @@ void handle_method_call(GDBusConnection *connection,
|
|||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
struct dbus_method *m = bsearch(
|
|
||||||
method_name,
|
struct dbus_method *m = bsearch(method_name,
|
||||||
&methods_fdn,
|
methods_fdn,
|
||||||
G_N_ELEMENTS(methods_fdn),
|
G_N_ELEMENTS(methods_fdn),
|
||||||
sizeof(struct dbus_method),
|
sizeof(struct dbus_method),
|
||||||
cmp_methods);
|
cmp_methods);
|
||||||
@ -131,6 +150,98 @@ void handle_method_call(GDBusConnection *connection,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBUS_METHOD(dunst_ContextMenuCall);
|
||||||
|
DBUS_METHOD(dunst_NotificationCloseAll);
|
||||||
|
DBUS_METHOD(dunst_NotificationCloseLast);
|
||||||
|
DBUS_METHOD(dunst_NotificationShow);
|
||||||
|
static struct dbus_method methods_dunst[] = {
|
||||||
|
{"ContextMenuCall", dbus_cb_dunst_ContextMenuCall},
|
||||||
|
{"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll},
|
||||||
|
{"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast},
|
||||||
|
{"NotificationShow", dbus_cb_dunst_NotificationShow},
|
||||||
|
};
|
||||||
|
|
||||||
|
void dbus_cb_dunst_methods(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
const gchar *object_path,
|
||||||
|
const gchar *interface_name,
|
||||||
|
const gchar *method_name,
|
||||||
|
GVariant *parameters,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct dbus_method *m = bsearch(method_name,
|
||||||
|
methods_dunst,
|
||||||
|
G_N_ELEMENTS(methods_dunst),
|
||||||
|
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,
|
||||||
|
sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
GVariant *parameters,
|
||||||
|
GDBusMethodInvocation *invocation)
|
||||||
|
{
|
||||||
|
LOG_D("CMD: Calling context menu");
|
||||||
|
context_menu();
|
||||||
|
|
||||||
|
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||||
|
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
GVariant *parameters,
|
||||||
|
GDBusMethodInvocation *invocation)
|
||||||
|
{
|
||||||
|
LOG_D("CMD: Pushing all to history");
|
||||||
|
queues_history_push_all();
|
||||||
|
wake_up();
|
||||||
|
|
||||||
|
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||||
|
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
GVariant *parameters,
|
||||||
|
GDBusMethodInvocation *invocation)
|
||||||
|
{
|
||||||
|
LOG_D("CMD: Closing last notification");
|
||||||
|
const GList *list = queues_get_displayed();
|
||||||
|
if (list && list->data) {
|
||||||
|
struct notification *n = list->data;
|
||||||
|
queues_notification_close_id(n->id, REASON_USER);
|
||||||
|
wake_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||||
|
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
GVariant *parameters,
|
||||||
|
GDBusMethodInvocation *invocation)
|
||||||
|
{
|
||||||
|
LOG_D("CMD: Showing last notification from history");
|
||||||
|
queues_history_pop();
|
||||||
|
wake_up();
|
||||||
|
|
||||||
|
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||||
|
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dbus_cb_GetCapabilities(
|
static void dbus_cb_GetCapabilities(
|
||||||
GDBusConnection *connection,
|
GDBusConnection *connection,
|
||||||
const gchar *sender,
|
const gchar *sender,
|
||||||
@ -347,11 +458,9 @@ static void dbus_cb_GetServerInformation(
|
|||||||
GVariant *parameters,
|
GVariant *parameters,
|
||||||
GDBusMethodInvocation *invocation)
|
GDBusMethodInvocation *invocation)
|
||||||
{
|
{
|
||||||
GVariant *value;
|
GVariant *answer = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
|
||||||
|
|
||||||
value = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
|
|
||||||
g_dbus_method_invocation_return_value(invocation, value);
|
|
||||||
|
|
||||||
|
g_dbus_method_invocation_return_value(invocation, answer);
|
||||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,28 +529,112 @@ void signal_action_invoked(const struct notification *n, const char *identifier)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const GDBusInterfaceVTable interface_vtable = {
|
//FIXME: Is this necessary or alternative question: Is this implemented correctl?
|
||||||
handle_method_call
|
// This had been an old relict from the manual times, when I haven't used the
|
||||||
|
// interface vtable of GLib
|
||||||
|
void dbus_signal_status_changed(struct dunst_status status)
|
||||||
|
{
|
||||||
|
// We might have not a working connection yet, so just ignore it.
|
||||||
|
if (!dbus_conn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//TODO: I'm pretty sure this is the right format string, but I don't know how to verify it
|
||||||
|
GVariantBuilder builder;
|
||||||
|
g_variant_builder_init(&builder, G_VARIANT_TYPE("(sa{sv}as)"));
|
||||||
|
g_variant_builder_add(&builder, "s", DUNST_IFAC);
|
||||||
|
|
||||||
|
g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||||
|
g_variant_builder_add(&builder, "{sv}", "running", g_variant_new_boolean(status.running));
|
||||||
|
g_variant_builder_close(&builder);
|
||||||
|
|
||||||
|
g_variant_builder_open(&builder, G_VARIANT_TYPE ("as"));
|
||||||
|
g_variant_builder_add(&builder, "s", "unrelated");
|
||||||
|
g_variant_builder_close(&builder);
|
||||||
|
|
||||||
|
GError *err = NULL;
|
||||||
|
g_dbus_connection_emit_signal(dbus_conn,
|
||||||
|
NULL,
|
||||||
|
DUNST_PATH,
|
||||||
|
PROPERTIES_IFAC,
|
||||||
|
"PropertiesChanged",
|
||||||
|
g_variant_builder_end(&builder),
|
||||||
|
&err);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
LOG_W("Unable send signal 'PropertiesChanged': %s", err->message);
|
||||||
|
g_error_free(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
const gchar *object_path,
|
||||||
|
const gchar *interface_name,
|
||||||
|
const gchar *property_name,
|
||||||
|
GError **error,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
struct dunst_status status = dunst_status_get();
|
||||||
|
|
||||||
|
if (STR_EQ(property_name, "running"))
|
||||||
|
return g_variant_new_boolean(status.running);
|
||||||
|
else
|
||||||
|
//TODO: is NULL as return value allowed?
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection,
|
||||||
|
const gchar *sender,
|
||||||
|
const gchar *object_path,
|
||||||
|
const gchar *interface_name,
|
||||||
|
const gchar *property_name,
|
||||||
|
GVariant *value,
|
||||||
|
GError **error,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
if (STR_EQ(property_name, "running"))
|
||||||
|
dunst_status(S_RUNNING, g_variant_get_boolean(value));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const GDBusInterfaceVTable interface_vtable_fdn = {
|
||||||
|
dbus_cb_fdn_methods
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GDBusInterfaceVTable interface_vtable_dunst = {
|
||||||
|
dbus_cb_dunst_methods,
|
||||||
|
dbus_cb_dunst_Properties_Get,
|
||||||
|
dbus_cb_dunst_Properties_Set,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dbus_cb_bus_acquired(GDBusConnection *connection,
|
static void dbus_cb_bus_acquired(GDBusConnection *connection,
|
||||||
const gchar *name,
|
const gchar *name,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
guint registration_id;
|
// TODO: deduplicate the code
|
||||||
|
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
|
if(!g_dbus_connection_register_object(
|
||||||
registration_id = g_dbus_connection_register_object(connection,
|
connection,
|
||||||
FDN_PATH,
|
FDN_PATH,
|
||||||
introspection_data->interfaces[0],
|
introspection_data->interfaces[0],
|
||||||
&interface_vtable,
|
&interface_vtable_fdn,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
&err);
|
&err)) {
|
||||||
|
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message);
|
||||||
|
}
|
||||||
|
|
||||||
if (registration_id == 0) {
|
if(!g_dbus_connection_register_object(
|
||||||
DIE("Unable to register dbus connection: %s", err->message);
|
connection,
|
||||||
|
FDN_PATH,
|
||||||
|
introspection_data->interfaces[1],
|
||||||
|
&interface_vtable_dunst,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&err)) {
|
||||||
|
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,6 +642,8 @@ static void dbus_cb_name_acquired(GDBusConnection *connection,
|
|||||||
const gchar *name,
|
const gchar *name,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
// If we're not able to get org.fd.N bus, we've still got a problem
|
||||||
|
if (STR_EQ(name, FDN_NAME))
|
||||||
dbus_conn = connection;
|
dbus_conn = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#ifndef DUNST_DBUS_H
|
#ifndef DUNST_DBUS_H
|
||||||
#define DUNST_DBUS_H
|
#define DUNST_DBUS_H
|
||||||
|
|
||||||
|
#include "dunst.h"
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
|
|
||||||
/// The reasons according to the notification spec
|
/// The reasons according to the notification spec
|
||||||
@ -20,5 +21,12 @@ void dbus_teardown(int id);
|
|||||||
void signal_notification_closed(struct notification *n, enum reason reason);
|
void signal_notification_closed(struct notification *n, enum reason reason);
|
||||||
void signal_action_invoked(const struct notification *n, const char *identifier);
|
void signal_action_invoked(const struct notification *n, const char *identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal a changed status via DBus
|
||||||
|
*
|
||||||
|
* @param status The current status of dunst
|
||||||
|
* */
|
||||||
|
void dbus_signal_status_changed(struct dunst_status status);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
@ -31,20 +31,26 @@ static struct dunst_status status;
|
|||||||
void dunst_status(const enum dunst_status_field field,
|
void dunst_status(const enum dunst_status_field field,
|
||||||
bool value)
|
bool value)
|
||||||
{
|
{
|
||||||
|
bool changed;
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case S_FULLSCREEN:
|
case S_FULLSCREEN:
|
||||||
|
changed = status.fullscreen != value;
|
||||||
status.fullscreen = value;
|
status.fullscreen = value;
|
||||||
break;
|
break;
|
||||||
case S_IDLE:
|
case S_IDLE:
|
||||||
|
changed = status.idle != value;
|
||||||
status.idle = value;
|
status.idle = value;
|
||||||
break;
|
break;
|
||||||
case S_RUNNING:
|
case S_RUNNING:
|
||||||
|
changed = status.running != value;
|
||||||
status.running = value;
|
status.running = value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__);
|
LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (changed)
|
||||||
|
dbus_signal_status_changed(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see dunst.h */
|
/* see dunst.h */
|
||||||
|
@ -799,6 +799,12 @@ TEST assert_methodlists_sorted(void)
|
|||||||
methods_fdn[i+1].method_name));
|
methods_fdn[i+1].method_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_dunst); i++) {
|
||||||
|
ASSERT(0 > strcmp(
|
||||||
|
methods_dunst[i].method_name,
|
||||||
|
methods_dunst[i+1].method_name));
|
||||||
|
}
|
||||||
|
|
||||||
PASS();
|
PASS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
|
#define dbus_signal_status_changed(status) signal_sent_stub(status)
|
||||||
#include "../src/dunst.c"
|
#include "../src/dunst.c"
|
||||||
#include "greatest.h"
|
#include "greatest.h"
|
||||||
|
|
||||||
|
static bool signal_sent = false;
|
||||||
|
|
||||||
|
void signal_sent_stub(struct dunst_status status)
|
||||||
|
{
|
||||||
|
signal_sent = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TEST test_dunst_status(void)
|
TEST test_dunst_status(void)
|
||||||
{
|
{
|
||||||
status = (struct dunst_status) {false, false, false};
|
status = (struct dunst_status) {false, false, false};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user