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
	 Benedikt Heine
						Benedikt Heine