1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-24 05:22:04 -08:00

Call all registered D-Bus signal handlers

* doc/misc/dbus.texi (Signals): All registered signal handlers are
called.
(Synchronous Methods, Signals, Monitoring Messages): Add function
result in examples.

* src/dbusbind.c (xd_store_event): New function.
(xd_read_message_1): Use it.  Call all registered handlers per
signal.  (Bug#80168)

* test/lisp/net/dbus-tests.el (dbus--test-signal-handler): Adapt defun.
(dbus--test-signal-handler1, dbus--test-signal-handler2): New defuns.
(dbus-test05-register-signal-several-handlers): New test.
(dbus-test04-register-method)
(dbus-test04-call-method-authorizable)
(dbus-test05-register-signal)
(dbus-test05-register-signal-with-nils)
(dbus-test06-register-property-emits-signal): Adapt tests.
This commit is contained in:
Michael Albinus 2026-01-14 10:41:41 +01:00
parent 986aaf06cd
commit 5a1ced4b24
3 changed files with 152 additions and 83 deletions

View file

@ -1240,6 +1240,8 @@ running):
"org.freedesktop.systemd1.Manager" "RestartUnit"
:authorizable t
"bluetooth.service" "replace")
@result{} "/org/freedesktop/systemd1/job/17508"
@end lisp
The remaining arguments @var{args} are passed to @var{method} as
@ -1752,6 +1754,8 @@ arguments. They are converted into D-Bus types as described in
:session nil dbus-path-emacs
(concat dbus-interface-emacs ".FileManager") "FileModified"
"/home/albinus/.emacs")
@result{} nil
@end lisp
@end defun
@ -1779,7 +1783,10 @@ argument.
@var{handler} is a Lisp function to be called when the @var{signal} is
received. It must accept as arguments the output parameters
@var{signal} is sending.
@var{signal} is sending.@footnote{It is possible to register different
handlers for the same signal. All registered handlers will be called
when the signal arrives. This is useful for example if different Lisp
packages are interested in the same signal.}
The remaining arguments @var{args} can be keywords or keyword string
pairs.@footnote{For backward compatibility, the arguments @var{args}
@ -2178,12 +2185,16 @@ The following form shows all D-Bus events on the session bus in buffer
@lisp
(dbus-register-monitor :session)
@result{} ((:monitor :session-private) (nil nil dbus-monitor-handler))
@end lisp
And this form restricts the monitoring on D-Bus errors:
@lisp
(dbus-register-monitor :session nil :type "error")
@result{} ((:monitor :session-private) (nil nil dbus-monitor-handler))
@end lisp
@end defun

View file

@ -1617,14 +1617,32 @@ usage: (dbus-message-internal &rest REST) */)
return result;
}
/* Construct a D-Bus event, and store it into the input event queue. */
static void
xd_store_event (Lisp_Object handler, Lisp_Object handler_args,
Lisp_Object event_args)
{
struct input_event event;
EVENT_INIT (event);
event.kind = DBUS_EVENT;
event.frame_or_window = Qnil;
/* Handler and handler args. */
event.arg = Fcons (handler, handler_args);
/* Event args. */
event.arg = CALLN (Fappend, event_args, event.arg);
/* Store it into the input event queue. */
kbd_buffer_store_event (&event);
XD_DEBUG_MESSAGE ("Event stored: %s", XD_OBJECT_TO_STRING (event.arg));
}
/* Read one queued incoming message of the D-Bus BUS.
BUS is either a Lisp symbol, :system, :session, :system-private or
:session-private, or a string denoting the bus address. */
static void
xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
{
Lisp_Object args, key, value;
struct input_event event;
Lisp_Object args, event_args, key, value;
DBusMessage *dmessage;
DBusMessageIter iter;
int dtype;
@ -1676,6 +1694,27 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
mtype == DBUS_MESSAGE_TYPE_ERROR ? error_name : member,
XD_OBJECT_TO_STRING (args));
/* Add type, serial, uname, destination, path, interface and member
or error_name to the event_args. */
event_args
= Fcons (mtype == DBUS_MESSAGE_TYPE_ERROR
? error_name == NULL ? Qnil : build_string (error_name)
: member == NULL ? Qnil : build_string (member),
Qnil);
event_args = Fcons ((interface == NULL ? Qnil : build_string (interface)),
event_args);
event_args = Fcons ((path == NULL ? Qnil : build_string (path)),
event_args);
event_args = Fcons ((destination == NULL ? Qnil : build_string (destination)),
event_args);
event_args = Fcons ((uname == NULL ? Qnil : build_string (uname)),
event_args);
event_args = Fcons (INT_TO_INTEGER (serial), event_args);
event_args = Fcons (make_fixnum (mtype), event_args);
/* Add the bus symbol to the event. */
event_args = Fcons (bus, event_args);
if (mtype == DBUS_MESSAGE_TYPE_INVALID)
goto cleanup;
@ -1693,12 +1732,8 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
/* Remove the entry. */
Fremhash (key, Vdbus_registered_objects_table);
/* Construct an event. */
EVENT_INIT (event);
event.kind = DBUS_EVENT;
event.frame_or_window = Qnil;
/* Handler. */
event.arg = Fcons (value, args);
/* Store the event. */
xd_store_event (value, args, event_args);
}
else /* DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_SIGNAL. */
@ -1729,6 +1764,7 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
Fgethash (key, Vdbus_registered_objects_table, Qnil));
}
Lisp_Object called_handlers = Qnil;
/* Loop over the registered functions. Construct an event. */
for (; !NILP (value); value = CDR_SAFE (value))
{
@ -1747,45 +1783,15 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
Lisp_Object handler = CAR_SAFE (CDR_SAFE (key_path_etc));
if (NILP (handler))
continue;
if (!NILP (memq_no_quit (handler, called_handlers)))
continue;
called_handlers = Fcons (handler, called_handlers);
/* Construct an event and exit the loop. */
EVENT_INIT (event);
event.kind = DBUS_EVENT;
event.frame_or_window = Qnil;
event.arg = Fcons (handler, args);
break;
/* Store the event. */
xd_store_event (handler, args, event_args);
}
if (NILP (value))
goto monitor;
}
/* Add type, serial, uname, destination, path, interface and member
or error_name to the event. */
event.arg
= Fcons (mtype == DBUS_MESSAGE_TYPE_ERROR
? error_name == NULL ? Qnil : build_string (error_name)
: member == NULL ? Qnil : build_string (member),
event.arg);
event.arg = Fcons ((interface == NULL ? Qnil : build_string (interface)),
event.arg);
event.arg = Fcons ((path == NULL ? Qnil : build_string (path)),
event.arg);
event.arg = Fcons ((destination == NULL ? Qnil : build_string (destination)),
event.arg);
event.arg = Fcons ((uname == NULL ? Qnil : build_string (uname)),
event.arg);
event.arg = Fcons (INT_TO_INTEGER (serial), event.arg);
event.arg = Fcons (make_fixnum (mtype), event.arg);
/* Add the bus symbol to the event. */
event.arg = Fcons (bus, event.arg);
/* Store it into the input event queue. */
kbd_buffer_store_event (&event);
XD_DEBUG_MESSAGE ("Event stored: %s", XD_OBJECT_TO_STRING (event.arg));
/* Monitor. */
monitor:
/* Search for a registered function of the message. */
@ -1796,39 +1802,9 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
if (NILP (value))
goto cleanup;
/* Construct an event. */
EVENT_INIT (event);
event.kind = DBUS_EVENT;
event.frame_or_window = Qnil;
/* Add type, serial, uname, destination, path, interface, member
or error_name and handler to the event. */
event.arg
= Fcons (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (CAR_SAFE (value))))),
args);
event.arg
= Fcons (mtype == DBUS_MESSAGE_TYPE_ERROR
? error_name == NULL ? Qnil : build_string (error_name)
: member == NULL ? Qnil : build_string (member),
event.arg);
event.arg = Fcons ((interface == NULL ? Qnil : build_string (interface)),
event.arg);
event.arg = Fcons ((path == NULL ? Qnil : build_string (path)),
event.arg);
event.arg = Fcons ((destination == NULL ? Qnil : build_string (destination)),
event.arg);
event.arg = Fcons ((uname == NULL ? Qnil : build_string (uname)),
event.arg);
event.arg = Fcons (INT_TO_INTEGER (serial), event.arg);
event.arg = Fcons (make_fixnum (mtype), event.arg);
/* Add the bus symbol to the event. */
event.arg = Fcons (bus, event.arg);
/* Store it into the input event queue. */
kbd_buffer_store_event (&event);
XD_DEBUG_MESSAGE ("Monitor event stored: %s", XD_OBJECT_TO_STRING (event.arg));
/* Store the event. */
xd_store_event (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (CAR_SAFE (value))))),
args, event_args);
/* Cleanup. */
cleanup:

View file

@ -607,6 +607,7 @@ This includes initialization and closing the bus."
(let ((method1 "Method1")
(method2 "Method2")
(handler #'dbus--test-method-handler)
dbus-debug ; There would be errors otherwise.
registered)
;; The service is not registered yet.
@ -759,6 +760,7 @@ Returns the respective error."
(unwind-protect
(let ((method "Method")
(handler #'dbus--test-method-authorizable-handler)
dbus-debug ; There would be errors otherwise.
registered)
;; Register.
@ -850,7 +852,7 @@ Returns the respective error."
(dbus-event-path-name dbus--test-event-expected))
(equal (dbus-event-member-name last-input-event)
(dbus-event-member-name dbus--test-event-expected))))
(setq dbus--test-signal-received args)))))
(push args dbus--test-signal-received)))))
(defun dbus--test-timeout-handler (&rest _ignore)
"Timeout handler, reporting a failed test."
@ -885,7 +887,7 @@ Returns the respective error."
(with-timeout (1 (dbus--test-timeout-handler))
(while (null dbus--test-signal-received)
(read-event nil nil 0.1)))
(should (equal dbus--test-signal-received '("foo")))
(should (equal dbus--test-signal-received '(("foo"))))
;; Send two arguments, compound types.
(setq dbus--test-signal-received nil)
@ -896,7 +898,7 @@ Returns the respective error."
(with-timeout (1 (dbus--test-timeout-handler))
(while (null dbus--test-signal-received)
(read-event nil nil 0.1)))
(should (equal dbus--test-signal-received '((1 2 3) ("bar"))))
(should (equal dbus--test-signal-received '(((1 2 3) ("bar")))))
;; Unregister signal.
(should (dbus-unregister-object registered))
@ -905,6 +907,86 @@ Returns the respective error."
;; Cleanup.
(dbus-unregister-service :session dbus--test-service)))
(defun dbus--test-signal-handler1 (&rest args)
"Signal handler for `dbus-test05-register-signal-several-handlers'."
;; (message "dbus--test-signal-handler1 %S" last-input-event)
(dbus--test-signal-handler (cons "dbus--test-signal-handler1" args)))
(defun dbus--test-signal-handler2 (&rest args)
"Signal handler for `dbus-test05-register-signal-several-handlers'."
;; (message "dbus--test-signal-handler2 %S" last-input-event)
(dbus--test-signal-handler (cons "dbus--test-signal-handler2" args)))
(ert-deftest dbus-test05-register-signal-several-handlers ()
"Check signal registration for an own service.
It shall call several handlers per received signal."
(skip-unless dbus--test-enabled-session-bus)
(dbus-ignore-errors (dbus-unregister-service :session dbus--test-service))
(unwind-protect
(let ((member "Member")
(handler1 #'dbus--test-signal-handler1)
(handler2 #'dbus--test-signal-handler2)
registered1 registered2)
;; Register signal handlers.
(should
(equal
(setq
registered1
(dbus-register-signal
:session dbus--test-service dbus--test-path
dbus--test-interface member handler1))
`((:signal :session ,dbus--test-interface ,member)
(,dbus--test-service ,dbus--test-path ,handler1))))
(should
(equal
(setq
registered2
(dbus-register-signal
:session dbus--test-service dbus--test-path
dbus--test-interface member handler2))
`((:signal :session ,dbus--test-interface ,member)
(,dbus--test-service ,dbus--test-path ,handler2))))
;; Send one argument, basic type.
(setq dbus--test-signal-received nil)
(dbus-send-signal
:session dbus--test-service dbus--test-path
dbus--test-interface member "foo")
(with-timeout (1 (dbus--test-timeout-handler))
(while (length< dbus--test-signal-received 2)
(read-event nil nil 0.1)))
(should
(member
'(("dbus--test-signal-handler1" "foo")) dbus--test-signal-received))
(should
(member
'(("dbus--test-signal-handler2" "foo")) dbus--test-signal-received))
;; Unregister one signal.
(should (dbus-unregister-object registered1))
(should-not (dbus-unregister-object registered1))
;; Send one argument, basic type.
(setq dbus--test-signal-received nil)
(dbus-send-signal
:session dbus--test-service dbus--test-path
dbus--test-interface member "foo")
(with-timeout (1 (dbus--test-timeout-handler))
(while (null dbus--test-signal-received)
(read-event nil nil 0.1)))
(should
(equal
dbus--test-signal-received '((("dbus--test-signal-handler2" "foo")))))
;; Unregister the other signal.
(should (dbus-unregister-object registered2))
(should-not (dbus-unregister-object registered2)))
;; Cleanup.
(dbus-unregister-service :session dbus--test-service)))
(ert-deftest dbus-test05-register-signal-with-nils ()
"Check signal registration for an own service.
SERVICE, PATH, INTERFACE and SIGNAL are nil. This is interpreted as a
@ -956,7 +1038,7 @@ wildcard for the respective argument."
(with-timeout (1 (dbus--test-timeout-handler))
(while (null dbus--test-signal-received)
(read-event nil nil 0.1)))
(should (equal dbus--test-signal-received '("foo")))
(should (equal dbus--test-signal-received '(("foo"))))
;; Unregister signal.
(should (dbus-unregister-object registered))
@ -1317,7 +1399,7 @@ wildcard for the respective argument."
;; "invalidated_properties" (an array of strings).
(should
(equal dbus--test-signal-received
`(,dbus--test-interface ((,property ("foo"))) ())))
`((,dbus--test-interface ((,property ("foo"))) ()))))
(should
(equal
@ -1341,7 +1423,7 @@ wildcard for the respective argument."
(should
(equal
dbus--test-signal-received
`(,dbus--test-interface ((,property ((1 2 3)))) ())))
`((,dbus--test-interface ((,property ((1 2 3)))) ()))))
(should
(equal