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:
parent
986aaf06cd
commit
5a1ced4b24
3 changed files with 152 additions and 83 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
128
src/dbusbind.c
128
src/dbusbind.c
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue