nt/config.nt: Sync with autogen/config.in.
[bpt/emacs.git] / lisp / net / dbus.el
CommitLineData
3a993e3d
MA
1;;; dbus.el --- Elisp bindings for D-Bus.
2
acaf905b 3;; Copyright (C) 2007-2012 Free Software Foundation, Inc.
3a993e3d
MA
4
5;; Author: Michael Albinus <michael.albinus@gmx.de>
6;; Keywords: comm, hardware
7
8;; This file is part of GNU Emacs.
9
874a927a 10;; GNU Emacs is free software: you can redistribute it and/or modify
3a993e3d 11;; it under the terms of the GNU General Public License as published by
874a927a
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
3a993e3d
MA
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
874a927a 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
3a993e3d
MA
22
23;;; Commentary:
24
25;; This package provides language bindings for the D-Bus API. D-Bus
26;; is a message bus system, a simple way for applications to talk to
27;; one another. See <http://dbus.freedesktop.org/> for details.
28
29;; Low-level language bindings are implemented in src/dbusbind.c.
30
dcbf5805
MA
31;; D-Bus support in the Emacs core can be disabled with configuration
32;; option "--without-dbus".
33
3a993e3d
MA
34;;; Code:
35
dcbf5805
MA
36;; Declare used subroutines and variables.
37(declare-function dbus-message-internal "dbusbind.c")
ba6f7d86 38(declare-function dbus-init-bus "dbusbind.c")
dcbf5805
MA
39(defvar dbus-message-type-invalid)
40(defvar dbus-message-type-method-call)
41(defvar dbus-message-type-method-return)
42(defvar dbus-message-type-error)
43(defvar dbus-message-type-signal)
6981d00a 44(defvar dbus-debug)
b172ed20 45(defvar dbus-registered-objects-table)
6981d00a
MA
46
47;; Pacify byte compiler.
48(eval-when-compile
49 (require 'cl))
7bb7efbd 50
3a993e3d
MA
51(require 'xml)
52
53(defconst dbus-service-dbus "org.freedesktop.DBus"
54 "The bus name used to talk to the bus itself.")
55
56(defconst dbus-path-dbus "/org/freedesktop/DBus"
57 "The object path used to talk to the bus itself.")
58
dcbf5805
MA
59;; Default D-Bus interfaces.
60
3a993e3d 61(defconst dbus-interface-dbus "org.freedesktop.DBus"
dcbf5805 62 "The interface exported by the service `dbus-service-dbus'.")
3a993e3d 63
4ba11bcb 64(defconst dbus-interface-peer (concat dbus-interface-dbus ".Peer")
dcbf5805
MA
65 "The interface for peer objects.
66See URL `http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer'.")
67
68;; <interface name="org.freedesktop.DBus.Peer">
69;; <method name="Ping">
70;; </method>
71;; <method name="GetMachineId">
72;; <arg name="machine_uuid" type="s" direction="out"/>
73;; </method>
74;; </interface>
4ba11bcb
MA
75
76(defconst dbus-interface-introspectable
77 (concat dbus-interface-dbus ".Introspectable")
dcbf5805
MA
78 "The interface supported by introspectable objects.
79See URL `http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable'.")
3a993e3d 80
dcbf5805
MA
81;; <interface name="org.freedesktop.DBus.Introspectable">
82;; <method name="Introspect">
83;; <arg name="data" type="s" direction="out"/>
84;; </method>
85;; </interface>
f636d3ca 86
dcbf5805
MA
87(defconst dbus-interface-properties (concat dbus-interface-dbus ".Properties")
88 "The interface for property objects.
89See URL `http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties'.")
90
91;; <interface name="org.freedesktop.DBus.Properties">
92;; <method name="Get">
93;; <arg name="interface" type="s" direction="in"/>
94;; <arg name="propname" type="s" direction="in"/>
95;; <arg name="value" type="v" direction="out"/>
96;; </method>
97;; <method name="Set">
98;; <arg name="interface" type="s" direction="in"/>
99;; <arg name="propname" type="s" direction="in"/>
100;; <arg name="value" type="v" direction="in"/>
101;; </method>
102;; <method name="GetAll">
103;; <arg name="interface" type="s" direction="in"/>
104;; <arg name="props" type="a{sv}" direction="out"/>
105;; </method>
106;; <signal name="PropertiesChanged">
107;; <arg name="interface" type="s"/>
108;; <arg name="changed_properties" type="a{sv}"/>
109;; <arg name="invalidated_properties" type="as"/>
110;; </signal>
111;; </interface>
112
113(defconst dbus-interface-objectmanager
114 (concat dbus-interface-dbus ".ObjectManager")
115 "The object manager interface.
116See URL `http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager'.")
117
118;; <interface name="org.freedesktop.DBus.ObjectManager">
119;; <method name="GetManagedObjects">
120;; <arg name="object_paths_interfaces_and_properties"
121;; type="a{oa{sa{sv}}}" direction="out"/>
122;; </method>
123;; <signal name="InterfacesAdded">
124;; <arg name="object_path" type="o"/>
125;; <arg name="interfaces_and_properties" type="a{sa{sv}}"/>
126;; </signal>
127;; <signal name="InterfacesRemoved">
128;; <arg name="object_path" type="o"/>
129;; <arg name="interfaces" type="as"/>
130;; </signal>
131;; </interface>
132
133;; Emacs defaults.
65b7cb2c
MA
134(defconst dbus-service-emacs "org.gnu.Emacs"
135 "The well known service name of Emacs.")
136
137(defconst dbus-path-emacs "/org/gnu/Emacs"
dcbf5805
MA
138 "The object path namespace used by Emacs.
139All object paths provided by the service `dbus-service-emacs'
140shall be subdirectories of this path.")
65b7cb2c 141
dcbf5805
MA
142(defconst dbus-interface-emacs "org.gnu.Emacs"
143 "The interface namespace used by Emacs.")
98c38bfc 144
dcbf5805 145;; D-Bus constants.
98c38bfc 146
246a286b
MA
147(defmacro dbus-ignore-errors (&rest body)
148 "Execute BODY; signal D-Bus error when `dbus-debug' is non-nil.
149Otherwise, return result of last form in BODY, or all other errors."
f291fe60 150 (declare (indent 0) (debug t))
246a286b
MA
151 `(condition-case err
152 (progn ,@body)
153 (dbus-error (when dbus-debug (signal (car err) (cdr err))))))
246a286b
MA
154(font-lock-add-keywords 'emacs-lisp-mode '("\\<dbus-ignore-errors\\>"))
155
e12c189f
MA
156(defvar dbus-event-error-hooks nil
157 "Functions to be called when a D-Bus error happens in the event handler.
f213fc09 158Every function must accept two arguments, the event and the error variable
333f9019 159caught in `condition-case' by `dbus-error'.")
e12c189f 160
5363d8ea 161\f
dcbf5805 162;;; Basic D-Bus message functions.
5363d8ea 163
98c38bfc
MA
164(defvar dbus-return-values-table (make-hash-table :test 'equal)
165 "Hash table for temporary storing arguments of reply messages.
dcbf5805
MA
166A key in this hash table is a list (:serial BUS SERIAL), like in
167`dbus-registered-objects-table'. BUS is either a Lisp symbol,
168`:system' or `:session', or a string denoting the bus address.
169SERIAL is the serial number of the reply message.")
170
171(defun dbus-call-method-handler (&rest args)
172 "Handler for reply messages of asynchronous D-Bus message calls.
173It calls the function stored in `dbus-registered-objects-table'.
174The result will be made available in `dbus-return-values-table'."
175 (puthash (list :serial
176 (dbus-event-bus-name last-input-event)
177 (dbus-event-serial-number last-input-event))
178 (if (= (length args) 1) (car args) args)
179 dbus-return-values-table))
180
181(defun dbus-call-method (bus service path interface method &rest args)
182 "Call METHOD on the D-Bus BUS.
183
184BUS is either a Lisp symbol, `:system' or `:session', or a string
185denoting the bus address.
186
187SERVICE is the D-Bus service name to be used. PATH is the D-Bus
188object path SERVICE is registered at. INTERFACE is an interface
189offered by SERVICE. It must provide METHOD.
190
191If the parameter `:timeout' is given, the following integer TIMEOUT
192specifies the maximum number of milliseconds the method call must
193return. The default value is 25,000. If the method call doesn't
194return in time, a D-Bus error is raised.
195
196All other arguments ARGS are passed to METHOD as arguments. They are
197converted into D-Bus types via the following rules:
198
199 t and nil => DBUS_TYPE_BOOLEAN
200 number => DBUS_TYPE_UINT32
201 integer => DBUS_TYPE_INT32
202 float => DBUS_TYPE_DOUBLE
203 string => DBUS_TYPE_STRING
204 list => DBUS_TYPE_ARRAY
205
206All arguments can be preceded by a type symbol. For details about
207type symbols, see Info node `(dbus)Type Conversion'.
208
209`dbus-call-method' returns the resulting values of METHOD as a list of
210Lisp objects. The type conversion happens the other direction as for
211input arguments. It follows the mapping rules:
212
213 DBUS_TYPE_BOOLEAN => t or nil
214 DBUS_TYPE_BYTE => number
215 DBUS_TYPE_UINT16 => number
216 DBUS_TYPE_INT16 => integer
217 DBUS_TYPE_UINT32 => number or float
218 DBUS_TYPE_UNIX_FD => number or float
219 DBUS_TYPE_INT32 => integer or float
220 DBUS_TYPE_UINT64 => number or float
221 DBUS_TYPE_INT64 => integer or float
222 DBUS_TYPE_DOUBLE => float
223 DBUS_TYPE_STRING => string
224 DBUS_TYPE_OBJECT_PATH => string
225 DBUS_TYPE_SIGNATURE => string
226 DBUS_TYPE_ARRAY => list
227 DBUS_TYPE_VARIANT => list
228 DBUS_TYPE_STRUCT => list
229 DBUS_TYPE_DICT_ENTRY => list
230
231Example:
232
233\(dbus-call-method
234 :session \"org.gnome.seahorse\" \"/org/gnome/seahorse/keys/openpgp\"
235 \"org.gnome.seahorse.Keys\" \"GetKeyField\"
236 \"openpgp:657984B8C7A966DD\" \"simple-name\")
237
238 => (t (\"Philip R. Zimmermann\"))
239
240If the result of the METHOD call is just one value, the converted Lisp
241object is returned instead of a list containing this single Lisp object.
242
243\(dbus-call-method
244 :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
245 \"org.freedesktop.Hal.Device\" \"GetPropertyString\"
246 \"system.kernel.machine\")
247
248 => \"i686\""
249
250 (or (memq bus '(:system :session)) (stringp bus)
251 (signal 'wrong-type-argument (list 'keywordp bus)))
252 (or (stringp service)
253 (signal 'wrong-type-argument (list 'stringp service)))
254 (or (stringp path)
255 (signal 'wrong-type-argument (list 'stringp path)))
256 (or (stringp interface)
257 (signal 'wrong-type-argument (list 'stringp interface)))
258 (or (stringp method)
259 (signal 'wrong-type-argument (list 'stringp method)))
260
261 (let ((timeout (plist-get args :timeout))
262 (key
263 (apply
264 'dbus-message-internal dbus-message-type-method-call
265 bus service path interface method 'dbus-call-method-handler args)))
205a7391 266
dcbf5805
MA
267 ;; Wait until `dbus-call-method-handler' has put the result into
268 ;; `dbus-return-values-table'. If no timeout is given, use the
205a7391 269 ;; default 25". Events which are not from D-Bus must be restored.
dcbf5805
MA
270 (with-timeout ((if timeout (/ timeout 1000.0) 25))
271 (while (eq (gethash key dbus-return-values-table :ignore) :ignore)
682cefaf 272 (let ((event (let (unread-command-events) (read-event nil nil 0.1))))
205a7391
MA
273 (when (and event (not (ignore-errors (dbus-check-event event))))
274 (setq unread-command-events
275 (append unread-command-events (list event)))))))
dcbf5805
MA
276
277 ;; Cleanup `dbus-return-values-table'. Return the result.
278 (prog1
279 (gethash key dbus-return-values-table)
280 (remhash key dbus-return-values-table))))
281
282;; `dbus-call-method' works non-blocking now.
283(defalias 'dbus-call-method-non-blocking 'dbus-call-method)
284(make-obsolete 'dbus-call-method-non-blocking 'dbus-call-method "24.2")
285
286(defun dbus-call-method-asynchronously
287 (bus service path interface method handler &rest args)
288 "Call METHOD on the D-Bus BUS asynchronously.
289
290BUS is either a Lisp symbol, `:system' or `:session', or a string
291denoting the bus address.
292
293SERVICE is the D-Bus service name to be used. PATH is the D-Bus
294object path SERVICE is registered at. INTERFACE is an interface
295offered by SERVICE. It must provide METHOD.
296
297HANDLER is a Lisp function, which is called when the corresponding
298return message has arrived. If HANDLER is nil, no return message
299will be expected.
300
301If the parameter `:timeout' is given, the following integer TIMEOUT
302specifies the maximum number of milliseconds the method call must
303return. The default value is 25,000. If the method call doesn't
304return in time, a D-Bus error is raised.
305
306All other arguments ARGS are passed to METHOD as arguments. They are
307converted into D-Bus types via the following rules:
308
309 t and nil => DBUS_TYPE_BOOLEAN
310 number => DBUS_TYPE_UINT32
311 integer => DBUS_TYPE_INT32
312 float => DBUS_TYPE_DOUBLE
313 string => DBUS_TYPE_STRING
314 list => DBUS_TYPE_ARRAY
315
316All arguments can be preceded by a type symbol. For details about
317type symbols, see Info node `(dbus)Type Conversion'.
318
319If HANDLER is a Lisp function, the function returns a key into the
320hash table `dbus-registered-objects-table'. The corresponding entry
321in the hash table is removed, when the return message has been arrived,
322and HANDLER is called.
323
324Example:
325
326\(dbus-call-method-asynchronously
327 :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
328 \"org.freedesktop.Hal.Device\" \"GetPropertyString\" 'message
329 \"system.kernel.machine\")
330
331 => \(:serial :system 2)
332
333 -| i686"
334
335 (or (memq bus '(:system :session)) (stringp bus)
336 (signal 'wrong-type-argument (list 'keywordp bus)))
337 (or (stringp service)
338 (signal 'wrong-type-argument (list 'stringp service)))
339 (or (stringp path)
340 (signal 'wrong-type-argument (list 'stringp path)))
341 (or (stringp interface)
342 (signal 'wrong-type-argument (list 'stringp interface)))
343 (or (stringp method)
344 (signal 'wrong-type-argument (list 'stringp method)))
345 (or (null handler) (functionp handler)
346 (signal 'wrong-type-argument (list 'functionp handler)))
347
348 (apply 'dbus-message-internal dbus-message-type-method-call
349 bus service path interface method handler args))
350
351(defun dbus-send-signal (bus service path interface signal &rest args)
352 "Send signal SIGNAL on the D-Bus BUS.
353
354BUS is either a Lisp symbol, `:system' or `:session', or a string
355denoting the bus address. The signal is sent from the D-Bus object
356Emacs is registered at BUS.
357
358SERVICE is the D-Bus name SIGNAL is sent to. It can be either a known
359name or a unique name. If SERVICE is nil, the signal is sent as
360broadcast message. PATH is the D-Bus object path SIGNAL is sent from.
361INTERFACE is an interface available at PATH. It must provide signal
362SIGNAL.
363
364All other arguments ARGS are passed to SIGNAL as arguments. They are
365converted into D-Bus types via the following rules:
366
367 t and nil => DBUS_TYPE_BOOLEAN
368 number => DBUS_TYPE_UINT32
369 integer => DBUS_TYPE_INT32
370 float => DBUS_TYPE_DOUBLE
371 string => DBUS_TYPE_STRING
372 list => DBUS_TYPE_ARRAY
373
374All arguments can be preceded by a type symbol. For details about
375type symbols, see Info node `(dbus)Type Conversion'.
376
377Example:
378
379\(dbus-send-signal
380 :session nil \"/org/gnu/Emacs\" \"org.gnu.Emacs.FileManager\"
381 \"FileModified\" \"/home/albinus/.emacs\")"
382
383 (or (memq bus '(:system :session)) (stringp bus)
384 (signal 'wrong-type-argument (list 'keywordp bus)))
385 (or (null service) (stringp service)
386 (signal 'wrong-type-argument (list 'stringp service)))
387 (or (stringp path)
388 (signal 'wrong-type-argument (list 'stringp path)))
389 (or (stringp interface)
390 (signal 'wrong-type-argument (list 'stringp interface)))
391 (or (stringp signal)
392 (signal 'wrong-type-argument (list 'stringp signal)))
393
394 (apply 'dbus-message-internal dbus-message-type-signal
395 bus service path interface signal args))
396
397(defun dbus-method-return-internal (bus service serial &rest args)
398 "Return for message SERIAL on the D-Bus BUS.
399This is an internal function, it shall not be used outside dbus.el."
400
401 (or (memq bus '(:system :session)) (stringp bus)
402 (signal 'wrong-type-argument (list 'keywordp bus)))
403 (or (stringp service)
404 (signal 'wrong-type-argument (list 'stringp service)))
405 (or (natnump serial)
406 (signal 'wrong-type-argument (list 'natnump serial)))
407
408 (apply 'dbus-message-internal dbus-message-type-method-return
409 bus service serial args))
410
411(defun dbus-method-error-internal (bus service serial &rest args)
412 "Return error message for message SERIAL on the D-Bus BUS.
413This is an internal function, it shall not be used outside dbus.el."
414
415 (or (memq bus '(:system :session)) (stringp bus)
416 (signal 'wrong-type-argument (list 'keywordp bus)))
417 (or (stringp service)
418 (signal 'wrong-type-argument (list 'stringp service)))
419 (or (natnump serial)
420 (signal 'wrong-type-argument (list 'natnump serial)))
421
422 (apply 'dbus-message-internal dbus-message-type-error
423 bus service serial args))
424
425\f
426;;; Hash table of registered functions.
98c38bfc 427
ef6ce14c 428(defun dbus-list-hash-table ()
e49d337b 429 "Returns all registered member registrations to D-Bus.
ef6ce14c 430The return value is a list, with elements of kind (KEY . VALUE).
b172ed20 431See `dbus-registered-objects-table' for a description of the
ef6ce14c
MA
432hash table."
433 (let (result)
434 (maphash
4f91a816 435 (lambda (key value) (add-to-list 'result (cons key value) 'append))
b172ed20 436 dbus-registered-objects-table)
ef6ce14c
MA
437 result))
438
dcbf5805
MA
439(defun dbus-setenv (bus variable value)
440 "Set the value of the BUS environment variable named VARIABLE to VALUE.
b172ed20 441
dcbf5805
MA
442BUS is either a Lisp symbol, `:system' or `:session', or a string
443denoting the bus address. Both VARIABLE and VALUE should be strings.
246a286b 444
dcbf5805
MA
445Normally, services inherit the environment of the BUS daemon. This
446function adds to or modifies that environment when activating services.
b172ed20 447
dcbf5805
MA
448Some bus instances, such as `:system', may disable setting the environment."
449 (dbus-call-method
450 bus dbus-service-dbus dbus-path-dbus
451 dbus-interface-dbus "UpdateActivationEnvironment"
452 `(:array (:dict-entry ,variable ,value))))
453
454(defun dbus-register-service (bus service &rest flags)
455 "Register known name SERVICE on the D-Bus BUS.
456
457BUS is either a Lisp symbol, `:system' or `:session', or a string
458denoting the bus address.
459
460SERVICE is the D-Bus service name that should be registered. It must
461be a known name.
462
463FLAGS are keywords, which control how the service name is registered.
464The following keywords are recognized:
465
466`:allow-replacement': Allow another service to become the primary
467owner if requested.
468
469`:replace-existing': Request to replace the current primary owner.
470
471`:do-not-queue': If we can not become the primary owner do not place
472us in the queue.
473
474The function returns a keyword, indicating the result of the
475operation. One of the following keywords is returned:
476
477`:primary-owner': Service has become the primary owner of the
478requested name.
479
480`:in-queue': Service could not become the primary owner and has been
481placed in the queue.
482
483`:exists': Service is already in the queue.
484
485`:already-owner': Service is already the primary owner."
486
487 ;; Add ObjectManager handler.
488 (dbus-register-method
489 bus service nil dbus-interface-objectmanager "GetManagedObjects"
490 'dbus-managed-objects-handler 'dont-register)
491
492 (let ((arg 0)
493 reply)
494 (dolist (flag flags)
495 (setq arg
496 (+ arg
497 (case flag
498 (:allow-replacement 1)
499 (:replace-existing 2)
500 (:do-not-queue 4)
501 (t (signal 'wrong-type-argument (list flag)))))))
502 (setq reply (dbus-call-method
503 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
504 "RequestName" service arg))
505 (case reply
506 (1 :primary-owner)
507 (2 :in-queue)
508 (3 :exists)
509 (4 :already-owner)
510 (t (signal 'dbus-error (list "Could not register service" service))))))
246a286b 511
c0a39702
MA
512(defun dbus-unregister-service (bus service)
513 "Unregister all objects related to SERVICE from D-Bus BUS.
e73f184c 514BUS is either a Lisp symbol, `:system' or `:session', or a string
5c0b4070
MA
515denoting the bus address. SERVICE must be a known service name.
516
517The function returns a keyword, indicating the result of the
518operation. One of the following keywords is returned:
519
520`:released': Service has become the primary owner of the name.
521
522`:non-existent': Service name does not exist on this bus.
523
524`:not-owner': We are neither the primary owner nor waiting in the
525queue of this service."
526
c0a39702
MA
527 (maphash
528 (lambda (key value)
529 (dolist (elt value)
530 (ignore-errors
dcbf5805 531 (when (and (equal bus (cadr key)) (string-equal service (cadr elt)))
c0a39702
MA
532 (unless
533 (puthash key (delete elt value) dbus-registered-objects-table)
534 (remhash key dbus-registered-objects-table))))))
535 dbus-registered-objects-table)
0a203b61
MA
536 (let ((reply (dbus-call-method
537 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
538 "ReleaseName" service)))
539 (case reply
540 (1 :released)
541 (2 :non-existent)
542 (3 :not-owner)
5c0b4070 543 (t (signal 'dbus-error (list "Could not unregister service" service))))))
c0a39702 544
dcbf5805
MA
545(defun dbus-register-signal
546 (bus service path interface signal handler &rest args)
547 "Register for a signal on the D-Bus BUS.
98c38bfc 548
dcbf5805
MA
549BUS is either a Lisp symbol, `:system' or `:session', or a string
550denoting the bus address.
98c38bfc 551
dcbf5805
MA
552SERVICE is the D-Bus service name used by the sending D-Bus object.
553It can be either a known name or the unique name of the D-Bus object
554sending the signal.
555
556PATH is the D-Bus object path SERVICE is registered. INTERFACE
557is an interface offered by SERVICE. It must provide SIGNAL.
558HANDLER is a Lisp function to be called when the signal is
559received. It must accept as arguments the values SIGNAL is
560sending.
561
562SERVICE, PATH, INTERFACE and SIGNAL can be nil. This is
563interpreted as a wildcard for the respective argument.
564
565The remaining arguments ARGS can be keywords or keyword string pairs.
566The meaning is as follows:
567
568`:argN' STRING:
569`:pathN' STRING: This stands for the Nth argument of the
570signal. `:pathN' arguments can be used for object path wildcard
0ba2624f 571matches as specified by D-Bus, while an `:argN' argument
dcbf5805
MA
572requires an exact match.
573
574`:arg-namespace' STRING: Register for the signals, which first
575argument defines the service or interface namespace STRING.
576
577`:path-namespace' STRING: Register for the object path namespace
578STRING. All signals sent from an object path, which has STRING as
579the preceding string, are matched. This requires PATH to be nil.
580
581`:eavesdrop': Register for unicast signals which are not directed
582to the D-Bus object Emacs is registered at D-Bus BUS, if the
583security policy of BUS allows this.
584
585Example:
586
587\(defun my-signal-handler (device)
588 (message \"Device %s added\" device))
589
590\(dbus-register-signal
591 :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/Manager\"
592 \"org.freedesktop.Hal.Manager\" \"DeviceAdded\" 'my-signal-handler)
593
594 => \(\(:signal :system \"org.freedesktop.Hal.Manager\" \"DeviceAdded\")
595 \(\"org.freedesktop.Hal\" \"/org/freedesktop/Hal/Manager\" my-signal-handler))
596
597`dbus-register-signal' returns an object, which can be used in
598`dbus-unregister-object' for removing the registration."
599
600 (let ((counter 0)
601 (rule "type='signal'")
602 uname key key1 value)
603
604 ;; Retrieve unique name of service. If service is a known name,
605 ;; we will register for the corresponding unique name, if any.
606 ;; Signals are sent always with the unique name as sender. Note:
607 ;; the unique name of `dbus-service-dbus' is that string itself.
608 (if (and (stringp service)
609 (not (zerop (length service)))
610 (not (string-equal service dbus-service-dbus))
611 (not (string-match "^:" service)))
612 (setq uname (dbus-get-name-owner bus service))
613 (setq uname service))
614
615 (setq rule (concat rule
616 (when uname (format ",sender='%s'" uname))
617 (when interface (format ",interface='%s'" interface))
618 (when signal (format ",member='%s'" signal))
619 (when path (format ",path='%s'" path))))
620
621 ;; Add arguments to the rule.
622 (if (or (stringp (car args)) (null (car args)))
623 ;; As backward compatibility option, we allow just strings.
624 (dolist (arg args)
625 (if (stringp arg)
626 (setq rule (concat rule (format ",arg%d='%s'" counter arg)))
627 (if arg (signal 'wrong-type-argument (list "Wrong argument" arg))))
628 (setq counter (1+ counter)))
629
630 ;; Parse keywords.
631 (while args
632 (setq
633 key (car args)
634 rule (concat
635 rule
636 (cond
637 ;; `:arg0' .. `:arg63', `:path0' .. `:path63'.
638 ((and (keywordp key)
639 (string-match
640 "^:\\(arg\\|path\\)\\([[:digit:]]+\\)$"
641 (symbol-name key)))
642 (setq counter (match-string 2 (symbol-name key))
643 args (cdr args)
644 value (car args))
645 (unless (and (<= counter 63) (stringp value))
646 (signal 'wrong-type-argument
647 (list "Wrong argument" key value)))
648 (format
649 ",arg%s%s='%s'"
650 counter
651 (if (string-equal (match-string 1 (symbol-name key)) "path")
652 "path" "")
653 value))
654 ;; `:arg-namespace', `:path-namespace'.
655 ((and (keywordp key)
656 (string-match
657 "^:\\(arg\\|path\\)-namespace$" (symbol-name key)))
658 (setq args (cdr args)
659 value (car args))
660 (unless (stringp value)
661 (signal 'wrong-type-argument
662 (list "Wrong argument" key value)))
663 (format
664 ",%s='%s'"
665 (if (string-equal (match-string 1 (symbol-name key)) "path")
666 "path_namespace" "arg0namespace")
667 value))
668 ;; `:eavesdrop'.
669 ((eq key :eavesdrop)
670 ",eavesdrop='true'")
671 (t (signal 'wrong-type-argument (list "Wrong argument" key)))))
672 args (cdr args))))
673
674 ;; Add the rule to the bus.
675 (condition-case err
676 (dbus-call-method
677 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
678 "AddMatch" rule)
679 (dbus-error
680 (if (not (string-match "eavesdrop" rule))
681 (signal (car err) (cdr err))
682 ;; The D-Bus spec says we shall fall back to a rule without eavesdrop.
683 (when dbus-debug (message "Removing eavesdrop from rule %s" rule))
684 (setq rule (replace-regexp-in-string ",eavesdrop='true'" "" rule))
685 (dbus-call-method
686 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
687 "AddMatch" rule))))
98c38bfc 688
dcbf5805 689 (when dbus-debug (message "Matching rule \"%s\" created" rule))
98c38bfc 690
dcbf5805
MA
691 ;; Create a hash table entry.
692 (setq key (list :signal bus interface signal)
693 key1 (list uname service path handler rule)
694 value (gethash key dbus-registered-objects-table))
695 (unless (member key1 value)
696 (puthash key (cons key1 value) dbus-registered-objects-table))
98c38bfc 697
dcbf5805
MA
698 ;; Return the object.
699 (list key (list service path handler))))
98c38bfc 700
dcbf5805
MA
701(defun dbus-register-method
702 (bus service path interface method handler &optional dont-register-service)
703 "Register for method METHOD on the D-Bus BUS.
704
705BUS is either a Lisp symbol, `:system' or `:session', or a string
706denoting the bus address.
707
708SERVICE is the D-Bus service name of the D-Bus object METHOD is
709registered for. It must be a known name (See discussion of
710DONT-REGISTER-SERVICE below).
711
712PATH is the D-Bus object path SERVICE is registered (See discussion of
713DONT-REGISTER-SERVICE below). INTERFACE is the interface offered by
714SERVICE. It must provide METHOD.
715
716HANDLER is a Lisp function to be called when a method call is
717received. It must accept the input arguments of METHOD. The return
718value of HANDLER is used for composing the returning D-Bus message.
719In case HANDLER shall return a reply message with an empty argument
720list, HANDLER must return the symbol `:ignore'.
721
722When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is not
723registered. This means that other D-Bus clients have no way of
724noticing the newly registered method. When interfaces are constructed
725incrementally by adding single methods or properties at a time,
726DONT-REGISTER-SERVICE can be used to prevent other clients from
727discovering the still incomplete interface."
728
729 ;; Register SERVICE.
730 (unless (or dont-register-service
731 (member service (dbus-list-names bus)))
732 (dbus-register-service bus service))
733
734 ;; Create a hash table entry. We use nil for the unique name,
735 ;; because the method might be called from anybody.
736 (let* ((key (list :method bus interface method))
737 (key1 (list nil service path handler))
738 (value (gethash key dbus-registered-objects-table)))
739
740 (unless (member key1 value)
741 (puthash key (cons key1 value) dbus-registered-objects-table))
742
743 ;; Return the object.
744 (list key (list service path handler))))
745
746(defun dbus-unregister-object (object)
747 "Unregister OBJECT from D-Bus.
748OBJECT must be the result of a preceding `dbus-register-method',
749`dbus-register-property' or `dbus-register-signal' call. It
750returns `t' if OBJECT has been unregistered, `nil' otherwise.
751
752When OBJECT identifies the last method or property, which is
753registered for the respective service, Emacs releases its
754association to the service from D-Bus."
755 ;; Check parameter.
756 (unless (and (consp object) (not (null (car object))) (consp (cdr object)))
757 (signal 'wrong-type-argument (list 'D-Bus object)))
758
759 ;; Find the corresponding entry in the hash table.
760 (let* ((key (car object))
761 (type (car key))
762 (bus (cadr key))
763 (value (cadr object))
764 (service (car value))
765 (entry (gethash key dbus-registered-objects-table))
766 ret)
767 ;; key has the structure (TYPE BUS INTERFACE MEMBER).
768 ;; value has the structure (SERVICE PATH [HANDLER]).
769 ;; entry has the structure ((UNAME SERVICE PATH MEMBER [RULE]) ...).
770 ;; MEMBER is either a string (the handler), or a cons cell (a
771 ;; property value). UNAME and property values are not taken into
772 ;; account for comparison.
773
774 ;; Loop over the registered functions.
775 (dolist (elt entry)
776 (when (equal
777 value
778 (butlast (cdr elt) (- (length (cdr elt)) (length value))))
779 (setq ret t)
780 ;; Compute new hash value. If it is empty, remove it from the
781 ;; hash table.
782 (unless (puthash key (delete elt entry) dbus-registered-objects-table)
783 (remhash key dbus-registered-objects-table))
784 ;; Remove match rule of signals.
785 (when (eq type :signal)
786 (dbus-call-method
787 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
788 "RemoveMatch" (nth 4 elt)))))
789
790 ;; Check, whether there is still a registered function or property
791 ;; for the given service. If not, unregister the service from the
792 ;; bus.
793 (when (and service (memq type '(:method :property))
794 (not (catch :found
795 (progn
796 (maphash
797 (lambda (k v)
798 (dolist (e v)
799 (ignore-errors
800 (and
801 ;; Bus.
802 (equal bus (cadr k))
803 ;; Service.
804 (string-equal service (cadr e))
805 ;; Non-empty object path.
806 (caddr e)
807 (throw :found t)))))
808 dbus-registered-objects-table)
809 nil))))
810 (dbus-unregister-service bus service))
811 ;; Return.
812 ret))
ef6ce14c 813
5363d8ea 814\f
82697a45
MA
815;;; D-Bus type conversion.
816
817(defun dbus-string-to-byte-array (string)
818 "Transforms STRING to list (:array :byte c1 :byte c2 ...).
819STRING shall be UTF8 coded."
d665fff0
MA
820 (if (zerop (length string))
821 '(:array :signature "y")
822 (let (result)
823 (dolist (elt (string-to-list string) (append '(:array) result))
824 (setq result (append result (list :byte elt)))))))
82697a45
MA
825
826(defun dbus-byte-array-to-string (byte-array)
827 "Transforms BYTE-ARRAY into UTF8 coded string.
828BYTE-ARRAY must be a list of structure (c1 c2 ...)."
829 (apply 'string byte-array))
830
831(defun dbus-escape-as-identifier (string)
832 "Escape an arbitrary STRING so it follows the rules for a C identifier.
833The escaped string can be used as object path component, interface element
834component, bus name component or member name in D-Bus.
835
836The escaping consists of replacing all non-alphanumerics, and the
837first character if it's a digit, with an underscore and two
838lower-case hex digits:
839
840 \"0123abc_xyz\\x01\\xff\" -> \"_30123abc_5fxyz_01_ff\"
841
842i.e. similar to URI encoding, but with \"_\" taking the role of \"%\",
843and a smaller allowed set. As a special case, \"\" is escaped to
844\"_\".
845
846Returns the escaped string. Algorithm taken from
847telepathy-glib's `tp-escape-as-identifier'."
848 (if (zerop (length string))
849 "_"
850 (replace-regexp-in-string
851 "^[0-9]\\|[^A-Za-z0-9]"
852 (lambda (x) (format "_%2x" (aref x 0)))
853 string)))
854
855(defun dbus-unescape-from-identifier (string)
856 "Retrieve the original string from the encoded STRING.
857STRING must have been coded with `dbus-escape-as-identifier'"
858 (if (string-equal string "_")
859 ""
860 (replace-regexp-in-string
861 "_.."
862 (lambda (x) (format "%c" (string-to-number (substring x 1) 16)))
863 string)))
864
865\f
5363d8ea
MA
866;;; D-Bus events.
867
3a993e3d
MA
868(defun dbus-check-event (event)
869 "Checks whether EVENT is a well formed D-Bus event.
870EVENT is a list which starts with symbol `dbus-event':
871
98c38bfc 872 (dbus-event BUS TYPE SERIAL SERVICE PATH INTERFACE MEMBER HANDLER &rest ARGS)
3a993e3d 873
e49d337b 874BUS identifies the D-Bus the message is coming from. It is
e73f184c
MA
875either a Lisp symbol, `:system' or `:session', or a string
876denoting the bus address. TYPE is the D-Bus message type which
877has caused the event, SERIAL is the serial number of the received
878D-Bus message. SERVICE and PATH are the unique name and the
879object path of the D-Bus object emitting the message. INTERFACE
880and MEMBER denote the message which has been sent. HANDLER is
881the function which has been registered for this message. ARGS
882are the arguments passed to HANDLER, when it is called during
883event handling in `dbus-handle-event'.
3a993e3d
MA
884
885This function raises a `dbus-error' signal in case the event is
886not well formed."
887 (when dbus-debug (message "DBus-Event %s" event))
888 (unless (and (listp event)
889 (eq (car event) 'dbus-event)
5363d8ea 890 ;; Bus symbol.
e73f184c
MA
891 (or (symbolp (nth 1 event))
892 (stringp (nth 1 event)))
98c38bfc
MA
893 ;; Type.
894 (and (natnump (nth 2 event))
895 (< dbus-message-type-invalid (nth 2 event)))
e49d337b 896 ;; Serial.
98c38bfc 897 (natnump (nth 3 event))
5363d8ea 898 ;; Service.
98c38bfc 899 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 900 (= dbus-message-type-error (nth 2 event))
98c38bfc 901 (stringp (nth 4 event)))
e49d337b 902 ;; Object path.
98c38bfc 903 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 904 (= dbus-message-type-error (nth 2 event))
98c38bfc 905 (stringp (nth 5 event)))
e49d337b 906 ;; Interface.
98c38bfc 907 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 908 (= dbus-message-type-error (nth 2 event))
98c38bfc 909 (stringp (nth 6 event)))
e49d337b 910 ;; Member.
98c38bfc 911 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 912 (= dbus-message-type-error (nth 2 event))
98c38bfc 913 (stringp (nth 7 event)))
ef6ce14c 914 ;; Handler.
98c38bfc 915 (functionp (nth 8 event)))
3a993e3d
MA
916 (signal 'dbus-error (list "Not a valid D-Bus event" event))))
917
918;;;###autoload
919(defun dbus-handle-event (event)
920 "Handle events from the D-Bus.
5363d8ea 921EVENT is a D-Bus event, see `dbus-check-event'. HANDLER, being
98c38bfc 922part of the event, is called with arguments ARGS.
35b148ee 923If the HANDLER returns a `dbus-error', it is propagated as return message."
3a993e3d 924 (interactive "e")
98c38bfc
MA
925 (condition-case err
926 (let (result)
ba0b66b0 927 ;; We ignore not well-formed events.
98c38bfc 928 (dbus-check-event event)
ba0b66b0
MA
929 ;; Error messages must be propagated.
930 (when (= dbus-message-type-error (nth 2 event))
931 (signal 'dbus-error (nthcdr 9 event)))
932 ;; Apply the handler.
98c38bfc
MA
933 (setq result (apply (nth 8 event) (nthcdr 9 event)))
934 ;; Return a message when it is a message call.
935 (when (= dbus-message-type-method-call (nth 2 event))
936 (dbus-ignore-errors
3dec5c36
MA
937 (if (eq result :ignore)
938 (dbus-method-return-internal
dcbf5805 939 (nth 1 event) (nth 4 event) (nth 3 event))
3dec5c36 940 (apply 'dbus-method-return-internal
dcbf5805 941 (nth 1 event) (nth 4 event) (nth 3 event)
3dec5c36 942 (if (consp result) result (list result)))))))
98c38bfc
MA
943 ;; Error handling.
944 (dbus-error
945 ;; Return an error message when it is a message call.
946 (when (= dbus-message-type-method-call (nth 2 event))
947 (dbus-ignore-errors
948 (dbus-method-error-internal
dcbf5805 949 (nth 1 event) (nth 4 event) (nth 3 event) (cadr err))))
ba0b66b0 950 ;; Propagate D-Bus error messages.
f213fc09 951 (run-hook-with-args 'dbus-event-error-hooks event err)
ba0b66b0
MA
952 (when (or dbus-debug (= dbus-message-type-error (nth 2 event)))
953 (signal (car err) (cdr err))))))
3a993e3d
MA
954
955(defun dbus-event-bus-name (event)
956 "Return the bus name the event is coming from.
e73f184c
MA
957The result is either a Lisp symbol, `:system' or `:session', or a
958string denoting the bus address. EVENT is a D-Bus event, see
959`dbus-check-event'. This function raises a `dbus-error' signal
960in case the event is not well formed."
3a993e3d 961 (dbus-check-event event)
ef6ce14c 962 (nth 1 event))
3a993e3d 963
98c38bfc
MA
964(defun dbus-event-message-type (event)
965 "Return the message type of the corresponding D-Bus message.
966The result is a number. EVENT is a D-Bus event, see
967`dbus-check-event'. This function raises a `dbus-error' signal
968in case the event is not well formed."
969 (dbus-check-event event)
970 (nth 2 event))
971
e49d337b
MA
972(defun dbus-event-serial-number (event)
973 "Return the serial number of the corresponding D-Bus message.
98c38bfc
MA
974The result is a number. The serial number is needed for
975generating a reply message. EVENT is a D-Bus event, see
976`dbus-check-event'. This function raises a `dbus-error' signal
977in case the event is not well formed."
e49d337b 978 (dbus-check-event event)
98c38bfc 979 (nth 3 event))
e49d337b 980
3a993e3d 981(defun dbus-event-service-name (event)
5363d8ea 982 "Return the name of the D-Bus object the event is coming from.
3a993e3d
MA
983The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
984This function raises a `dbus-error' signal in case the event is
985not well formed."
986 (dbus-check-event event)
98c38bfc 987 (nth 4 event))
3a993e3d
MA
988
989(defun dbus-event-path-name (event)
990 "Return the object path of the D-Bus object the event is coming from.
991The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
992This function raises a `dbus-error' signal in case the event is
993not well formed."
994 (dbus-check-event event)
98c38bfc 995 (nth 5 event))
3a993e3d
MA
996
997(defun dbus-event-interface-name (event)
998 "Return the interface name of the D-Bus object the event is coming from.
999The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
1000This function raises a `dbus-error' signal in case the event is
1001not well formed."
1002 (dbus-check-event event)
98c38bfc 1003 (nth 6 event))
3a993e3d
MA
1004
1005(defun dbus-event-member-name (event)
1006 "Return the member name the event is coming from.
58179cce 1007It is either a signal name or a method name. The result is a
3a993e3d
MA
1008string. EVENT is a D-Bus event, see `dbus-check-event'. This
1009function raises a `dbus-error' signal in case the event is not
1010well formed."
1011 (dbus-check-event event)
98c38bfc 1012 (nth 7 event))
5363d8ea
MA
1013
1014\f
1015;;; D-Bus registered names.
3a993e3d 1016
07e52e08 1017(defun dbus-list-activatable-names (&optional bus)
3a993e3d 1018 "Return the D-Bus service names which can be activated as list.
07e52e08
MA
1019If BUS is left nil, `:system' is assumed. The result is a list
1020of strings, which is `nil' when there are no activatable service
1021names at all."
246a286b
MA
1022 (dbus-ignore-errors
1023 (dbus-call-method
07e52e08 1024 (or bus :system) dbus-service-dbus
246a286b 1025 dbus-path-dbus dbus-interface-dbus "ListActivatableNames")))
3a993e3d
MA
1026
1027(defun dbus-list-names (bus)
1028 "Return the service names registered at D-Bus BUS.
f636d3ca
MA
1029The result is a list of strings, which is `nil' when there are no
1030registered service names at all. Well known names are strings
1031like \"org.freedesktop.DBus\". Names starting with \":\" are
1032unique names for services."
246a286b
MA
1033 (dbus-ignore-errors
1034 (dbus-call-method
1035 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus "ListNames")))
3a993e3d
MA
1036
1037(defun dbus-list-known-names (bus)
1038 "Retrieve all services which correspond to a known name in BUS.
1039A service has a known name if it doesn't start with \":\"."
1040 (let (result)
1041 (dolist (name (dbus-list-names bus) result)
1042 (unless (string-equal ":" (substring name 0 1))
1043 (add-to-list 'result name 'append)))))
1044
1045(defun dbus-list-queued-owners (bus service)
f636d3ca
MA
1046 "Return the unique names registered at D-Bus BUS and queued for SERVICE.
1047The result is a list of strings, or `nil' when there are no
1048queued name owners service names at all."
246a286b
MA
1049 (dbus-ignore-errors
1050 (dbus-call-method
1051 bus dbus-service-dbus dbus-path-dbus
1052 dbus-interface-dbus "ListQueuedOwners" service)))
3a993e3d
MA
1053
1054(defun dbus-get-name-owner (bus service)
1055 "Return the name owner of SERVICE registered at D-Bus BUS.
f636d3ca 1056The result is either a string, or `nil' if there is no name owner."
246a286b
MA
1057 (dbus-ignore-errors
1058 (dbus-call-method
1059 bus dbus-service-dbus dbus-path-dbus
1060 dbus-interface-dbus "GetNameOwner" service)))
3a993e3d 1061
93fb0645
MA
1062(defun dbus-ping (bus service &optional timeout)
1063 "Check whether SERVICE is registered for D-Bus BUS.
1064TIMEOUT, a nonnegative integer, specifies the maximum number of
1065milliseconds `dbus-ping' must return. The default value is 25,000.
1066
1067Note, that this autoloads SERVICE if it is not running yet. If
1068it shall be checked whether SERVICE is already running, one shall
1069apply
1070
1071 \(member service \(dbus-list-known-names bus))"
4ba11bcb
MA
1072 ;; "Ping" raises a D-Bus error if SERVICE does not exist.
1073 ;; Otherwise, it returns silently with `nil'.
1074 (condition-case nil
1075 (not
93fb0645
MA
1076 (if (natnump timeout)
1077 (dbus-call-method
1078 bus service dbus-path-dbus dbus-interface-peer
1079 "Ping" :timeout timeout)
1080 (dbus-call-method
1081 bus service dbus-path-dbus dbus-interface-peer "Ping")))
4ba11bcb
MA
1082 (dbus-error nil)))
1083
f636d3ca
MA
1084\f
1085;;; D-Bus introspection.
3a993e3d 1086
f636d3ca 1087(defun dbus-introspect (bus service path)
35b148ee 1088 "Return all interfaces and sub-nodes of SERVICE,
f636d3ca
MA
1089registered at object path PATH at bus BUS.
1090
e73f184c
MA
1091BUS is either a Lisp symbol, `:system' or `:session', or a string
1092denoting the bus address. SERVICE must be a known service name,
1093and PATH must be a valid object path. The last two parameters
1094are strings. The result, the introspection data, is a string in
1095XML format."
205a7391 1096 ;; We don't want to raise errors.
246a286b 1097 (dbus-ignore-errors
dcbf5805
MA
1098 (dbus-call-method
1099 bus service path dbus-interface-introspectable "Introspect"
1100 :timeout 1000)))
3a993e3d 1101
f636d3ca
MA
1102(defun dbus-introspect-xml (bus service path)
1103 "Return the introspection data of SERVICE in D-Bus BUS at object path PATH.
1104The data are a parsed list. The root object is a \"node\",
1105representing the object path PATH. The root object can contain
1106\"interface\" and further \"node\" objects."
1107 ;; We don't want to raise errors.
1108 (xml-node-name
1109 (ignore-errors
1110 (with-temp-buffer
1111 (insert (dbus-introspect bus service path))
1112 (xml-parse-region (point-min) (point-max))))))
1113
1114(defun dbus-introspect-get-attribute (object attribute)
1115 "Return the ATTRIBUTE value of D-Bus introspection OBJECT.
1116ATTRIBUTE must be a string according to the attribute names in
1117the D-Bus specification."
1118 (xml-get-attribute-or-nil object (intern attribute)))
1119
1120(defun dbus-introspect-get-node-names (bus service path)
1121 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
1122It returns a list of strings. The node names stand for further
1123object paths of the D-Bus service."
1124 (let ((object (dbus-introspect-xml bus service path))
1125 result)
1126 (dolist (elt (xml-get-children object 'node) result)
1127 (add-to-list
1128 'result (dbus-introspect-get-attribute elt "name") 'append))))
1129
1130(defun dbus-introspect-get-all-nodes (bus service path)
1131 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
1132It returns a list of strings, which are further object paths of SERVICE."
1133 (let ((result (list path)))
1134 (dolist (elt
1135 (dbus-introspect-get-node-names bus service path)
1136 result)
1137 (setq elt (expand-file-name elt path))
1138 (setq result
1139 (append result (dbus-introspect-get-all-nodes bus service elt))))))
1140
1141(defun dbus-introspect-get-interface-names (bus service path)
1142 "Return all interface names of SERVICE in D-Bus BUS at object path PATH.
1143It returns a list of strings.
1144
1145There will be always the default interface
1146\"org.freedesktop.DBus.Introspectable\". Another default
1147interface is \"org.freedesktop.DBus.Properties\". If present,
1148\"interface\" objects can also have \"property\" objects as
1149children, beside \"method\" and \"signal\" objects."
1150 (let ((object (dbus-introspect-xml bus service path))
1151 result)
1152 (dolist (elt (xml-get-children object 'interface) result)
1153 (add-to-list
1154 'result (dbus-introspect-get-attribute elt "name") 'append))))
1155
1156(defun dbus-introspect-get-interface (bus service path interface)
1157 "Return the INTERFACE of SERVICE in D-Bus BUS at object path PATH.
1158The return value is an XML object. INTERFACE must be a string,
35b148ee
JB
1159element of the list returned by `dbus-introspect-get-interface-names'.
1160The resulting \"interface\" object can contain \"method\", \"signal\",
f636d3ca
MA
1161\"property\" and \"annotation\" children."
1162 (let ((elt (xml-get-children
1163 (dbus-introspect-xml bus service path) 'interface)))
1164 (while (and elt
1165 (not (string-equal
1166 interface
1167 (dbus-introspect-get-attribute (car elt) "name"))))
1168 (setq elt (cdr elt)))
1169 (car elt)))
1170
1171(defun dbus-introspect-get-method-names (bus service path interface)
1172 "Return a list of strings of all method names of INTERFACE.
1173SERVICE is a service of D-Bus BUS at object path PATH."
1174 (let ((object (dbus-introspect-get-interface bus service path interface))
1175 result)
1176 (dolist (elt (xml-get-children object 'method) result)
1177 (add-to-list
1178 'result (dbus-introspect-get-attribute elt "name") 'append))))
1179
1180(defun dbus-introspect-get-method (bus service path interface method)
1181 "Return method METHOD of interface INTERFACE as XML object.
1182It must be located at SERVICE in D-Bus BUS at object path PATH.
1183METHOD must be a string, element of the list returned by
1184`dbus-introspect-get-method-names'. The resulting \"method\"
1185object can contain \"arg\" and \"annotation\" children."
1186 (let ((elt (xml-get-children
1187 (dbus-introspect-get-interface bus service path interface)
1188 'method)))
1189 (while (and elt
1190 (not (string-equal
1191 method (dbus-introspect-get-attribute (car elt) "name"))))
1192 (setq elt (cdr elt)))
1193 (car elt)))
1194
1195(defun dbus-introspect-get-signal-names (bus service path interface)
1196 "Return a list of strings of all signal names of INTERFACE.
1197SERVICE is a service of D-Bus BUS at object path PATH."
1198 (let ((object (dbus-introspect-get-interface bus service path interface))
1199 result)
1200 (dolist (elt (xml-get-children object 'signal) result)
1201 (add-to-list
1202 'result (dbus-introspect-get-attribute elt "name") 'append))))
1203
1204(defun dbus-introspect-get-signal (bus service path interface signal)
1205 "Return signal SIGNAL of interface INTERFACE as XML object.
1206It must be located at SERVICE in D-Bus BUS at object path PATH.
1207SIGNAL must be a string, element of the list returned by
1208`dbus-introspect-get-signal-names'. The resulting \"signal\"
1209object can contain \"arg\" and \"annotation\" children."
1210 (let ((elt (xml-get-children
1211 (dbus-introspect-get-interface bus service path interface)
1212 'signal)))
1213 (while (and elt
1214 (not (string-equal
1215 signal (dbus-introspect-get-attribute (car elt) "name"))))
1216 (setq elt (cdr elt)))
1217 (car elt)))
1218
1219(defun dbus-introspect-get-property-names (bus service path interface)
1220 "Return a list of strings of all property names of INTERFACE.
1221SERVICE is a service of D-Bus BUS at object path PATH."
1222 (let ((object (dbus-introspect-get-interface bus service path interface))
1223 result)
1224 (dolist (elt (xml-get-children object 'property) result)
1225 (add-to-list
1226 'result (dbus-introspect-get-attribute elt "name") 'append))))
1227
1228(defun dbus-introspect-get-property (bus service path interface property)
1229 "This function returns PROPERTY of INTERFACE as XML object.
1230It must be located at SERVICE in D-Bus BUS at object path PATH.
1231PROPERTY must be a string, element of the list returned by
1232`dbus-introspect-get-property-names'. The resulting PROPERTY
1233object can contain \"annotation\" children."
1234 (let ((elt (xml-get-children
1235 (dbus-introspect-get-interface bus service path interface)
1236 'property)))
1237 (while (and elt
1238 (not (string-equal
1239 property
1240 (dbus-introspect-get-attribute (car elt) "name"))))
1241 (setq elt (cdr elt)))
1242 (car elt)))
1243
1244(defun dbus-introspect-get-annotation-names
1245 (bus service path interface &optional name)
1246 "Return all annotation names as list of strings.
1247If NAME is `nil', the annotations are children of INTERFACE,
1248otherwise NAME must be a \"method\", \"signal\", or \"property\"
1249object, where the annotations belong to."
1250 (let ((object
1251 (if name
1252 (or (dbus-introspect-get-method bus service path interface name)
1253 (dbus-introspect-get-signal bus service path interface name)
1254 (dbus-introspect-get-property bus service path interface name))
1255 (dbus-introspect-get-interface bus service path interface)))
1256 result)
1257 (dolist (elt (xml-get-children object 'annotation) result)
1258 (add-to-list
1259 'result (dbus-introspect-get-attribute elt "name") 'append))))
1260
1261(defun dbus-introspect-get-annotation
1262 (bus service path interface name annotation)
1263 "Return ANNOTATION as XML object.
1264If NAME is `nil', ANNOTATION is a child of INTERFACE, otherwise
1265NAME must be the name of a \"method\", \"signal\", or
1266\"property\" object, where the ANNOTATION belongs to."
1267 (let ((elt (xml-get-children
1268 (if name
1269 (or (dbus-introspect-get-method
1270 bus service path interface name)
1271 (dbus-introspect-get-signal
1272 bus service path interface name)
1273 (dbus-introspect-get-property
1274 bus service path interface name))
1275 (dbus-introspect-get-interface bus service path interface))
1276 'annotation)))
1277 (while (and elt
1278 (not (string-equal
1279 annotation
1280 (dbus-introspect-get-attribute (car elt) "name"))))
1281 (setq elt (cdr elt)))
1282 (car elt)))
1283
1284(defun dbus-introspect-get-argument-names (bus service path interface name)
1285 "Return a list of all argument names as list of strings.
1286NAME must be a \"method\" or \"signal\" object.
1287
1288Argument names are optional, the function can return `nil'
1289therefore, even if the method or signal has arguments."
1290 (let ((object
1291 (or (dbus-introspect-get-method bus service path interface name)
1292 (dbus-introspect-get-signal bus service path interface name)))
1293 result)
1294 (dolist (elt (xml-get-children object 'arg) result)
1295 (add-to-list
1296 'result (dbus-introspect-get-attribute elt "name") 'append))))
1297
1298(defun dbus-introspect-get-argument (bus service path interface name arg)
1299 "Return argument ARG as XML object.
35b148ee
JB
1300NAME must be a \"method\" or \"signal\" object. ARG must be a string,
1301element of the list returned by `dbus-introspect-get-argument-names'."
f636d3ca
MA
1302 (let ((elt (xml-get-children
1303 (or (dbus-introspect-get-method bus service path interface name)
1304 (dbus-introspect-get-signal bus service path interface name))
1305 'arg)))
1306 (while (and elt
1307 (not (string-equal
1308 arg (dbus-introspect-get-attribute (car elt) "name"))))
1309 (setq elt (cdr elt)))
1310 (car elt)))
1311
1312(defun dbus-introspect-get-signature
1313 (bus service path interface name &optional direction)
1314 "Return signature of a `method' or `signal', represented by NAME, as string.
1315If NAME is a `method', DIRECTION can be either \"in\" or \"out\".
1316If DIRECTION is `nil', \"in\" is assumed.
1317
1318If NAME is a `signal', and DIRECTION is non-`nil', DIRECTION must
1319be \"out\"."
1320 ;; For methods, we use "in" as default direction.
1321 (let ((object (or (dbus-introspect-get-method
1322 bus service path interface name)
1323 (dbus-introspect-get-signal
1324 bus service path interface name))))
1325 (when (and (string-equal
1326 "method" (dbus-introspect-get-attribute object "name"))
1327 (not (stringp direction)))
1328 (setq direction "in"))
1329 ;; In signals, no direction is given.
1330 (when (string-equal "signal" (dbus-introspect-get-attribute object "name"))
1331 (setq direction nil))
1332 ;; Collect the signatures.
1333 (mapconcat
4f91a816
SM
1334 (lambda (x)
1335 (let ((arg (dbus-introspect-get-argument
1336 bus service path interface name x)))
1337 (if (or (not (stringp direction))
1338 (string-equal
1339 direction
1340 (dbus-introspect-get-attribute arg "direction")))
1341 (dbus-introspect-get-attribute arg "type")
1342 "")))
f636d3ca
MA
1343 (dbus-introspect-get-argument-names bus service path interface name)
1344 "")))
3a993e3d 1345
f636d3ca
MA
1346\f
1347;;; D-Bus properties.
3a993e3d 1348
f636d3ca
MA
1349(defun dbus-get-property (bus service path interface property)
1350 "Return the value of PROPERTY of INTERFACE.
1351It will be checked at BUS, SERVICE, PATH. The result can be any
1352valid D-Bus value, or `nil' if there is no PROPERTY."
246a286b 1353 (dbus-ignore-errors
dcbf5805
MA
1354 ;; "Get" returns a variant, so we must use the `car'.
1355 (car
1356 (dbus-call-method
1357 bus service path dbus-interface-properties
1358 "Get" :timeout 500 interface property))))
f636d3ca
MA
1359
1360(defun dbus-set-property (bus service path interface property value)
1361 "Set value of PROPERTY of INTERFACE to VALUE.
1362It will be checked at BUS, SERVICE, PATH. When the value has
1363been set successful, the result is VALUE. Otherwise, `nil' is
1364returned."
1365 (dbus-ignore-errors
dcbf5805
MA
1366 ;; "Set" requires a variant.
1367 (dbus-call-method
1368 bus service path dbus-interface-properties
1369 "Set" :timeout 500 interface property (list :variant value))
1370 ;; Return VALUE.
1371 (dbus-get-property bus service path interface property)))
f636d3ca
MA
1372
1373(defun dbus-get-all-properties (bus service path interface)
1374 "Return all properties of INTERFACE at BUS, SERVICE, PATH.
1375The result is a list of entries. Every entry is a cons of the
1376name of the property, and its value. If there are no properties,
1377`nil' is returned."
f636d3ca 1378 (dbus-ignore-errors
b172ed20 1379 ;; "GetAll" returns "a{sv}".
f636d3ca 1380 (let (result)
b172ed20 1381 (dolist (dict
dcbf5805 1382 (dbus-call-method
b172ed20
MA
1383 bus service path dbus-interface-properties
1384 "GetAll" :timeout 500 interface)
f636d3ca 1385 result)
b172ed20
MA
1386 (add-to-list 'result (cons (car dict) (caadr dict)) 'append)))))
1387
1388(defun dbus-register-property
6388924a
MA
1389 (bus service path interface property access value
1390 &optional emits-signal dont-register-service)
b172ed20
MA
1391 "Register property PROPERTY on the D-Bus BUS.
1392
e73f184c
MA
1393BUS is either a Lisp symbol, `:system' or `:session', or a string
1394denoting the bus address.
b172ed20
MA
1395
1396SERVICE is the D-Bus service name of the D-Bus. It must be a
6388924a
MA
1397known name (See discussion of DONT-REGISTER-SERVICE below).
1398
1399PATH is the D-Bus object path SERVICE is registered (See
1400discussion of DONT-REGISTER-SERVICE below). INTERFACE is the
1401name of the interface used at PATH, PROPERTY is the name of the
1402property of INTERFACE. ACCESS indicates, whether the property
1403can be changed by other services via D-Bus. It must be either
1404the symbol `:read' or `:readwrite'. VALUE is the initial value
1405of the property, it can be of any valid type (see
b172ed20
MA
1406`dbus-call-method' for details).
1407
1408If PROPERTY already exists on PATH, it will be overwritten. For
1409properties with access type `:read' this is the only way to
1410change their values. Properties with access type `:readwrite'
1411can be changed by `dbus-set-property'.
1412
1413The interface \"org.freedesktop.DBus.Properties\" is added to
1414PATH, including a default handler for the \"Get\", \"GetAll\" and
b1ce08da
MA
1415\"Set\" methods of this interface. When EMITS-SIGNAL is non-nil,
1416the signal \"PropertiesChanged\" is sent when the property is
6388924a
MA
1417changed by `dbus-set-property'.
1418
1419When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is
1420not registered. This means that other D-Bus clients have no way
1421of noticing the newly registered property. When interfaces are
1422constructed incrementally by adding single methods or properties
1423at a time, DONT-REGISTER-SERVICE can be used to prevent other
1424clients from discovering the still incomplete interface."
b172ed20 1425 (unless (member access '(:read :readwrite))
dcbf5805 1426 (signal 'wrong-type-argument (list "Access type invalid" access)))
b172ed20 1427
0a203b61 1428 ;; Add handlers for the three property-related methods.
b172ed20 1429 (dbus-register-method
0a203b61 1430 bus service path dbus-interface-properties "Get"
1a27c64e 1431 'dbus-property-handler 'dont-register)
b172ed20 1432 (dbus-register-method
1a27c64e
MA
1433 bus service path dbus-interface-properties "GetAll"
1434 'dbus-property-handler 'dont-register)
b172ed20 1435 (dbus-register-method
1a27c64e
MA
1436 bus service path dbus-interface-properties "Set"
1437 'dbus-property-handler 'dont-register)
0a203b61 1438
dcbf5805
MA
1439 ;; Register SERVICE.
1440 (unless (or dont-register-service (member service (dbus-list-names bus)))
0a203b61 1441 (dbus-register-service bus service))
b172ed20 1442
b1ce08da
MA
1443 ;; Send the PropertiesChanged signal.
1444 (when emits-signal
1445 (dbus-send-signal
1446 bus service path dbus-interface-properties "PropertiesChanged"
dcbf5805 1447 `((:dict-entry ,property (:variant ,value)))
b1ce08da
MA
1448 '(:array)))
1449
b172ed20
MA
1450 ;; Create a hash table entry. We use nil for the unique name,
1451 ;; because the property might be accessed from anybody.
dcbf5805 1452 (let ((key (list :property bus interface property))
b1ce08da
MA
1453 (val
1454 (list
1455 (list
1456 nil service path
1457 (cons
1458 (if emits-signal (list access :emits-signal) (list access))
1459 value)))))
b172ed20
MA
1460 (puthash key val dbus-registered-objects-table)
1461
1462 ;; Return the object.
1463 (list key (list service path))))
1464
1465(defun dbus-property-handler (&rest args)
35b148ee 1466 "Default handler for the \"org.freedesktop.DBus.Properties\" interface.
dcbf5805 1467It will be registered for all objects created by `dbus-register-property'."
b172ed20 1468 (let ((bus (dbus-event-bus-name last-input-event))
b1ce08da 1469 (service (dbus-event-service-name last-input-event))
b172ed20
MA
1470 (path (dbus-event-path-name last-input-event))
1471 (method (dbus-event-member-name last-input-event))
1472 (interface (car args))
1473 (property (cadr args)))
1474 (cond
1475 ;; "Get" returns a variant.
1476 ((string-equal method "Get")
dcbf5805 1477 (let ((entry (gethash (list :property bus interface property)
b1ce08da
MA
1478 dbus-registered-objects-table)))
1479 (when (string-equal path (nth 2 (car entry)))
dcbf5805 1480 `((:variant ,(cdar (last (car entry))))))))
b172ed20
MA
1481
1482 ;; "Set" expects a variant.
1483 ((string-equal method "Set")
b1ce08da 1484 (let* ((value (caar (cddr args)))
dcbf5805 1485 (entry (gethash (list :property bus interface property)
b1ce08da
MA
1486 dbus-registered-objects-table))
1487 ;; The value of the hash table is a list; in case of
1488 ;; properties it contains just one element (UNAME SERVICE
1489 ;; PATH OBJECT). OBJECT is a cons cell of a list, which
1490 ;; contains a list of annotations (like :read,
1491 ;; :read-write, :emits-signal), and the value of the
1492 ;; property.
1493 (object (car (last (car entry)))))
1494 (unless (consp object)
b172ed20
MA
1495 (signal 'dbus-error
1496 (list "Property not registered at path" property path)))
b1ce08da 1497 (unless (member :readwrite (car object))
b172ed20
MA
1498 (signal 'dbus-error
1499 (list "Property not writable at path" property path)))
dcbf5805 1500 (puthash (list :property bus interface property)
b1ce08da
MA
1501 (list (append (butlast (car entry))
1502 (list (cons (car object) value))))
b172ed20 1503 dbus-registered-objects-table)
b1ce08da
MA
1504 ;; Send the "PropertiesChanged" signal.
1505 (when (member :emits-signal (car object))
1506 (dbus-send-signal
1507 bus service path dbus-interface-properties "PropertiesChanged"
dcbf5805 1508 `((:dict-entry ,property (:variant ,value)))
b1ce08da
MA
1509 '(:array)))
1510 ;; Return empty reply.
b172ed20
MA
1511 :ignore))
1512
1513 ;; "GetAll" returns "a{sv}".
1514 ((string-equal method "GetAll")
1515 (let (result)
1516 (maphash
1517 (lambda (key val)
dcbf5805 1518 (when (and (equal (butlast key) (list :property bus interface))
b172ed20 1519 (string-equal path (nth 2 (car val)))
31bb373f 1520 (not (functionp (car (last (car val))))))
b172ed20
MA
1521 (add-to-list
1522 'result
1523 (list :dict-entry
1524 (car (last key))
1525 (list :variant (cdar (last (car val))))))))
1526 dbus-registered-objects-table)
052e28ac
MA
1527 ;; Return the result, or an empty array.
1528 (list :array (or result '(:signature "{sv}"))))))))
b172ed20 1529
dcbf5805
MA
1530\f
1531;;; D-Bus object manager.
1532
1533(defun dbus-get-all-managed-objects (bus service path)
1534 "Return all objects at BUS, SERVICE, PATH, and the children of PATH.
1535The result is a list of objects. Every object is a cons of an
1536existing path name, and the list of available interface objects.
1537An interface object is another cons, which car is the interface
1538name, and the cdr is the list of properties as returned by
205a7391 1539`dbus-get-all-properties' for that path and interface. Example:
dcbf5805
MA
1540
1541\(dbus-get-all-managed-objects :session \"org.gnome.SettingsDaemon\" \"/\")
1542
1543 => \(\(\"/org/gnome/SettingsDaemon/MediaKeys\"
1544 \(\"org.gnome.SettingsDaemon.MediaKeys\")
1545 \(\"org.freedesktop.DBus.Peer\")
1546 \(\"org.freedesktop.DBus.Introspectable\")
1547 \(\"org.freedesktop.DBus.Properties\")
1548 \(\"org.freedesktop.DBus.ObjectManager\"))
1549 \(\"/org/gnome/SettingsDaemon/Power\"
1550 \(\"org.gnome.SettingsDaemon.Power.Keyboard\")
1551 \(\"org.gnome.SettingsDaemon.Power.Screen\")
1552 \(\"org.gnome.SettingsDaemon.Power\"
1553 \(\"Icon\" . \". GThemedIcon battery-full-charged-symbolic \")
1554 \(\"Tooltip\" . \"Laptop battery is charged\"))
1555 \(\"org.freedesktop.DBus.Peer\")
1556 \(\"org.freedesktop.DBus.Introspectable\")
1557 \(\"org.freedesktop.DBus.Properties\")
1558 \(\"org.freedesktop.DBus.ObjectManager\"))
1559 ...)
1560
1561If possible, \"org.freedesktop.DBus.ObjectManager.GetManagedObjects\"
1562is used for retrieving the information. Otherwise, the information
1563is collected via \"org.freedesktop.DBus.Introspectable.Introspect\"
1564and \"org.freedesktop.DBus.Properties.GetAll\", which is slow."
1565 (let ((result
1566 ;; Direct call. Fails, if the target does not support the
1567 ;; object manager interface.
1568 (dbus-ignore-errors
1569 (dbus-call-method
1570 bus service path dbus-interface-objectmanager
1571 "GetManagedObjects" :timeout 1000))))
1572
1573 (if result
1574 ;; Massage the returned structure.
1575 (dolist (entry result result)
1576 ;; "a{oa{sa{sv}}}".
1577 (dolist (entry1 (cdr entry))
1578 ;; "a{sa{sv}}".
1579 (dolist (entry2 entry1)
1580 ;; "a{sv}".
1581 (if (cadr entry2)
1582 ;; "sv".
1583 (dolist (entry3 (cadr entry2))
1584 (setcdr entry3 (caadr entry3)))
1585 (setcdr entry2 nil)))))
1586
1587 ;; Fallback: collect the information. Slooow!
1588 (dolist (object
1589 (dbus-introspect-get-all-nodes bus service path)
1590 result)
1591 (let (result1)
1592 (dolist
1593 (interface
1594 (dbus-introspect-get-interface-names bus service object)
1595 result1)
1596 (add-to-list
1597 'result1
1598 (cons interface
1599 (dbus-get-all-properties bus service object interface))))
1600 (when result1
1601 (add-to-list 'result (cons object result1))))))))
1602
1603(defun dbus-managed-objects-handler ()
1604 "Default handler for the \"org.freedesktop.DBus.ObjectManager\" interface.
1605It will be registered for all objects created by `dbus-register-method'."
1606 (let* ((last-input-event last-input-event)
1607 (bus (dbus-event-bus-name last-input-event))
1608 (service (dbus-event-service-name last-input-event))
1609 (path (dbus-event-path-name last-input-event)))
1610 ;; "GetManagedObjects" returns "a{oa{sa{sv}}}".
1611 (let (interfaces result)
1612
1613 ;; Check for object path wildcard interfaces.
1614 (maphash
1615 (lambda (key val)
1616 (when (and (equal (butlast key 2) (list :method bus))
1617 (null (nth 2 (car-safe val))))
1618 (add-to-list 'interfaces (nth 2 key))))
1619 dbus-registered-objects-table)
1620
1621 ;; Check all registered object paths.
1622 (maphash
1623 (lambda (key val)
1624 (let ((object (or (nth 2 (car-safe val)) ""))
1625 (interface (nth 2 key)))
1626 (when (and (equal (butlast key 2) (list :method bus))
1627 (string-prefix-p path object))
1628 (dolist (interface (cons (nth 2 key) interfaces))
1629 (unless (assoc object result)
1630 (add-to-list 'result (list object)))
1631 (unless (assoc interface (cdr (assoc object result)))
1632 (setcdr
1633 (assoc object result)
1634 (append
1635 (list (cons
1636 interface
1637 ;; We simulate "org.freedesktop.DBus.Properties.GetAll"
1638 ;; by using an appropriate D-Bus event.
1639 (let ((last-input-event
1640 (append
1641 (butlast last-input-event 4)
1642 (list object dbus-interface-properties
1643 "GetAll" 'dbus-property-handler))))
1644 (dbus-property-handler interface))))
1645 (cdr (assoc object result)))))))))
1646 dbus-registered-objects-table)
1647
1648 ;; Return the result, or an empty array.
1649 (list
1650 :array
1651 (or
1652 (mapcar
1653 (lambda (x)
1654 (list
1655 :dict-entry :object-path (car x)
1656 (cons :array (mapcar (lambda (y) (cons :dict-entry y)) (cdr x)))))
1657 result)
1658 '(:signature "{oa{sa{sv}}}"))))))
1659
b172ed20 1660 \f
dcbf5805 1661;; Initialize `:system' and `:session' buses. This adds their file
720c7cd6
MA
1662;; descriptors to input_wait_mask, in order to detect incoming
1663;; messages immediately.
9e846523
MA
1664(when (featurep 'dbusbind)
1665 (dbus-ignore-errors
dcbf5805
MA
1666 (dbus-init-bus :system))
1667 (dbus-ignore-errors
9e846523 1668 (dbus-init-bus :session)))
720c7cd6 1669
3a993e3d
MA
1670(provide 'dbus)
1671
dcbf5805
MA
1672;;; TODO:
1673
1674;; * Implement org.freedesktop.DBus.ObjectManager.InterfacesAdded and
1675;; org.freedesktop.DBus.ObjectManager.InterfacesRemoved.
1676
3a993e3d 1677;;; dbus.el ends here