* subr.el (listify-key-sequence-1): Use normal syntax since those
[bpt/emacs.git] / lisp / net / dbus.el
CommitLineData
3a993e3d
MA
1;;; dbus.el --- Elisp bindings for D-Bus.
2
d665fff0 3;; Copyright (C) 2007, 2008, 2009 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
31;;; Code:
32
7bb7efbd 33;; D-Bus support in the Emacs core can be disabled with configuration
6981d00a
MA
34;; option "--without-dbus". Declare used subroutines and variables.
35(declare-function dbus-call-method "dbusbind.c")
52a39a64 36(declare-function dbus-call-method-asynchronously "dbusbind.c")
ba6f7d86 37(declare-function dbus-init-bus "dbusbind.c")
5e895c06
MA
38(declare-function dbus-method-return-internal "dbusbind.c")
39(declare-function dbus-method-error-internal "dbusbind.c")
6981d00a
MA
40(declare-function dbus-register-signal "dbusbind.c")
41(defvar dbus-debug)
42(defvar dbus-registered-functions-table)
43
ba6f7d86
MA
44;; Initialize :system and :session buses. This adds their file
45;; descriptors to input_wait_mask, in order to detect incoming
46;; messages immediately.
47;; We must avoid to call the function twice for a bus, because the
48;; DBusWatch will be removed then.
49(when (and (featurep 'dbusbind) (not (featurep 'dbus)))
50 (dbus-init-bus :system)
51 (dbus-init-bus :session))
52
6981d00a
MA
53;; Pacify byte compiler.
54(eval-when-compile
55 (require 'cl))
7bb7efbd 56
3a993e3d
MA
57(require 'xml)
58
59(defconst dbus-service-dbus "org.freedesktop.DBus"
60 "The bus name used to talk to the bus itself.")
61
62(defconst dbus-path-dbus "/org/freedesktop/DBus"
63 "The object path used to talk to the bus itself.")
64
65(defconst dbus-interface-dbus "org.freedesktop.DBus"
66 "The interface exported by the object with `dbus-service-dbus' and `dbus-path-dbus'.")
67
4ba11bcb
MA
68(defconst dbus-interface-peer (concat dbus-interface-dbus ".Peer")
69 "The interface for peer objects.")
70
71(defconst dbus-interface-introspectable
72 (concat dbus-interface-dbus ".Introspectable")
3a993e3d
MA
73 "The interface supported by introspectable objects.")
74
f636d3ca
MA
75(defconst dbus-interface-properties (concat dbus-interface-dbus ".Properties")
76 "The interface for property objects.")
77
65b7cb2c
MA
78(defconst dbus-service-emacs "org.gnu.Emacs"
79 "The well known service name of Emacs.")
80
81(defconst dbus-path-emacs "/org/gnu/Emacs"
82 "The object path head used by Emacs.")
83
98c38bfc
MA
84(defconst dbus-message-type-invalid 0
85 "This value is never a valid message type.")
86
87(defconst dbus-message-type-method-call 1
88 "Message type of a method call message.")
89
90(defconst dbus-message-type-method-return 2
91 "Message type of a method return message.")
92
93(defconst dbus-message-type-error 3
94 "Message type of an error reply message.")
95
96(defconst dbus-message-type-signal 4
97 "Message type of a signal message.")
98
246a286b
MA
99(defmacro dbus-ignore-errors (&rest body)
100 "Execute BODY; signal D-Bus error when `dbus-debug' is non-nil.
101Otherwise, return result of last form in BODY, or all other errors."
102 `(condition-case err
103 (progn ,@body)
104 (dbus-error (when dbus-debug (signal (car err) (cdr err))))))
105
106(put 'dbus-ignore-errors 'lisp-indent-function 0)
98c38bfc 107(put 'dbus-ignore-errors 'edebug-form-spec '(form body))
246a286b
MA
108(font-lock-add-keywords 'emacs-lisp-mode '("\\<dbus-ignore-errors\\>"))
109
e12c189f
MA
110(defvar dbus-event-error-hooks nil
111 "Functions to be called when a D-Bus error happens in the event handler.
f213fc09 112Every function must accept two arguments, the event and the error variable
e12c189f
MA
113catched in `condition-case' by `dbus-error'.")
114
5363d8ea
MA
115\f
116;;; Hash table of registered functions.
117
79945ac1
MA
118;; We create it here. So we have a simple test in dbusbind.c, whether
119;; the Lisp code has been loaded.
120(setq dbus-registered-functions-table (make-hash-table :test 'equal))
5363d8ea 121
98c38bfc
MA
122(defvar dbus-return-values-table (make-hash-table :test 'equal)
123 "Hash table for temporary storing arguments of reply messages.
124A key in this hash table is a list (BUS SERIAL). BUS is either the
125symbol `:system' or the symbol `:session'. SERIAL is the serial number
126of the reply message. See `dbus-call-method-non-blocking-handler' and
127`dbus-call-method-non-blocking'.")
128
ef6ce14c 129(defun dbus-list-hash-table ()
e49d337b 130 "Returns all registered member registrations to D-Bus.
ef6ce14c
MA
131The return value is a list, with elements of kind (KEY . VALUE).
132See `dbus-registered-functions-table' for a description of the
133hash table."
134 (let (result)
135 (maphash
136 '(lambda (key value) (add-to-list 'result (cons key value) 'append))
137 dbus-registered-functions-table)
138 result))
139
246a286b
MA
140(defun dbus-unregister-object (object)
141 "Unregister OBJECT from D-Bus.
142OBJECT must be the result of a preceding `dbus-register-method'
f636d3ca
MA
143or `dbus-register-signal' call. It returns `t' if OBJECT has
144been unregistered, `nil' otherwise."
246a286b
MA
145 ;; Check parameter.
146 (unless (and (consp object) (not (null (car object))) (consp (cdr object)))
147 (signal 'wrong-type-argument (list 'D-Bus object)))
148
149 ;; Find the corresponding entry in the hash table.
150 (let* ((key (car object))
151 (value (gethash key dbus-registered-functions-table)))
152 ;; Loop over the registered functions.
153 (while (consp value)
154 ;; (car value) has the structure (UNAME SERVICE PATH HANDLER).
155 ;; (cdr object) has the structure ((SERVICE PATH HANDLER) ...).
156 (if (not (equal (cdr (car value)) (car (cdr object))))
157 (setq value (cdr value))
158 ;; Compute new hash value. If it is empty, remove it from
159 ;; hash table.
160 (unless
161 (puthash
162 key
163 (delete (car value) (gethash key dbus-registered-functions-table))
164 dbus-registered-functions-table)
165 (remhash key dbus-registered-functions-table))
166 (setq value t)))
167 value))
168
98c38bfc
MA
169(defun dbus-call-method-non-blocking-handler (&rest args)
170 "Handler for reply messages of asynchronous D-Bus message calls.
171It calls the function stored in `dbus-registered-functions-table'.
172The result will be made available in `dbus-return-values-table'."
173 (puthash (list (dbus-event-bus-name last-input-event)
174 (dbus-event-serial-number last-input-event))
175 (if (= (length args) 1) (car args) args)
176 dbus-return-values-table))
177
178(defun dbus-call-method-non-blocking
179 (bus service path interface method &rest args)
180 "Call METHOD on the D-Bus BUS, but don't block the event queue.
181This is necessary for communicating to registered D-Bus methods,
182which are running in the same Emacs process.
183
184The arguments are the same as in `dbus-call-method'.
185
186usage: (dbus-call-method-non-blocking
187 BUS SERVICE PATH INTERFACE METHOD
188 &optional :timeout TIMEOUT &rest ARGS)"
189
190 (let ((key
191 (apply
192 'dbus-call-method-asynchronously
193 bus service path interface method
194 'dbus-call-method-non-blocking-handler args)))
195 ;; Wait until `dbus-call-method-non-blocking-handler' has put the
196 ;; result into `dbus-return-values-table'.
3dec5c36 197 (while (eq (gethash key dbus-return-values-table :ignore) :ignore)
98c38bfc
MA
198 (read-event nil nil 0.1))
199
200 ;; Cleanup `dbus-return-values-table'. Return the result.
201 (prog1
202 (gethash key dbus-return-values-table nil)
203 (remhash key dbus-return-values-table))))
204
0e0c4247 205(defun dbus-name-owner-changed-handler (&rest args)
e49d337b 206 "Reapplies all member registrations to D-Bus.
ef6ce14c
MA
207This handler is applied when a \"NameOwnerChanged\" signal has
208arrived. SERVICE is the object name for which the name owner has
209been changed. OLD-OWNER is the previous owner of SERVICE, or the
210empty string if SERVICE was not owned yet. NEW-OWNER is the new
0e0c4247
MA
211owner of SERVICE, or the empty string if SERVICE looses any name owner.
212
213usage: (dbus-name-owner-changed-handler service old-owner new-owner)"
ef6ce14c 214 (save-match-data
0e0c4247
MA
215 ;; Check the arguments. We should silently ignore it when they
216 ;; are wrong.
217 (if (and (= (length args) 3)
218 (stringp (car args))
219 (stringp (cadr args))
220 (stringp (caddr args)))
221 (let ((service (car args))
222 (old-owner (cadr args))
223 (new-owner (caddr args)))
224 ;; Check whether SERVICE is a known name.
225 (when (not (string-match "^:" service))
226 (maphash
227 '(lambda (key value)
228 (dolist (elt value)
e49d337b 229 ;; key has the structure (BUS INTERFACE MEMBER).
0e0c4247
MA
230 ;; elt has the structure (UNAME SERVICE PATH HANDLER).
231 (when (string-equal old-owner (car elt))
232 ;; Remove old key, and add new entry with changed name.
7d1112ae 233 (dbus-unregister-object (list key (cdr elt)))
0e0c4247
MA
234 ;; Maybe we could arrange the lists a little bit better
235 ;; that we don't need to extract every single element?
236 (dbus-register-signal
237 ;; BUS SERVICE PATH
238 (nth 0 key) (nth 1 elt) (nth 2 elt)
e49d337b 239 ;; INTERFACE MEMBER HANDLER
0e0c4247
MA
240 (nth 1 key) (nth 2 key) (nth 3 elt)))))
241 (copy-hash-table dbus-registered-functions-table))))
242 ;; The error is reported only in debug mode.
243 (when dbus-debug
244 (signal
245 'dbus-error
246 (cons
247 (format "Wrong arguments of %s.NameOwnerChanged" dbus-interface-dbus)
248 args))))))
ef6ce14c
MA
249
250;; Register the handler.
98c38bfc 251(when nil ;ignore-errors
246a286b
MA
252 (dbus-register-signal
253 :system dbus-service-dbus dbus-path-dbus dbus-interface-dbus
254 "NameOwnerChanged" 'dbus-name-owner-changed-handler)
255 (dbus-register-signal
256 :session dbus-service-dbus dbus-path-dbus dbus-interface-dbus
257 "NameOwnerChanged" 'dbus-name-owner-changed-handler))
ef6ce14c 258
5363d8ea 259\f
82697a45
MA
260;;; D-Bus type conversion.
261
262(defun dbus-string-to-byte-array (string)
263 "Transforms STRING to list (:array :byte c1 :byte c2 ...).
264STRING shall be UTF8 coded."
d665fff0
MA
265 (if (zerop (length string))
266 '(:array :signature "y")
267 (let (result)
268 (dolist (elt (string-to-list string) (append '(:array) result))
269 (setq result (append result (list :byte elt)))))))
82697a45
MA
270
271(defun dbus-byte-array-to-string (byte-array)
272 "Transforms BYTE-ARRAY into UTF8 coded string.
273BYTE-ARRAY must be a list of structure (c1 c2 ...)."
274 (apply 'string byte-array))
275
276(defun dbus-escape-as-identifier (string)
277 "Escape an arbitrary STRING so it follows the rules for a C identifier.
278The escaped string can be used as object path component, interface element
279component, bus name component or member name in D-Bus.
280
281The escaping consists of replacing all non-alphanumerics, and the
282first character if it's a digit, with an underscore and two
283lower-case hex digits:
284
285 \"0123abc_xyz\\x01\\xff\" -> \"_30123abc_5fxyz_01_ff\"
286
287i.e. similar to URI encoding, but with \"_\" taking the role of \"%\",
288and a smaller allowed set. As a special case, \"\" is escaped to
289\"_\".
290
291Returns the escaped string. Algorithm taken from
292telepathy-glib's `tp-escape-as-identifier'."
293 (if (zerop (length string))
294 "_"
295 (replace-regexp-in-string
296 "^[0-9]\\|[^A-Za-z0-9]"
297 (lambda (x) (format "_%2x" (aref x 0)))
298 string)))
299
300(defun dbus-unescape-from-identifier (string)
301 "Retrieve the original string from the encoded STRING.
302STRING must have been coded with `dbus-escape-as-identifier'"
303 (if (string-equal string "_")
304 ""
305 (replace-regexp-in-string
306 "_.."
307 (lambda (x) (format "%c" (string-to-number (substring x 1) 16)))
308 string)))
309
310\f
5363d8ea
MA
311;;; D-Bus events.
312
3a993e3d
MA
313(defun dbus-check-event (event)
314 "Checks whether EVENT is a well formed D-Bus event.
315EVENT is a list which starts with symbol `dbus-event':
316
98c38bfc 317 (dbus-event BUS TYPE SERIAL SERVICE PATH INTERFACE MEMBER HANDLER &rest ARGS)
3a993e3d 318
e49d337b 319BUS identifies the D-Bus the message is coming from. It is
98c38bfc
MA
320either the symbol `:system' or the symbol `:session'. TYPE is
321the D-Bus message type which has caused the event, SERIAL is the
322serial number of the received D-Bus message. SERVICE and PATH
323are the unique name and the object path of the D-Bus object
324emitting the message. INTERFACE and MEMBER denote the message
325which has been sent. HANDLER is the function which has been
326registered for this message. ARGS are the arguments passed to
327HANDLER, when it is called during event handling in
328`dbus-handle-event'.
3a993e3d
MA
329
330This function raises a `dbus-error' signal in case the event is
331not well formed."
332 (when dbus-debug (message "DBus-Event %s" event))
333 (unless (and (listp event)
334 (eq (car event) 'dbus-event)
5363d8ea 335 ;; Bus symbol.
ef6ce14c 336 (symbolp (nth 1 event))
98c38bfc
MA
337 ;; Type.
338 (and (natnump (nth 2 event))
339 (< dbus-message-type-invalid (nth 2 event)))
e49d337b 340 ;; Serial.
98c38bfc 341 (natnump (nth 3 event))
5363d8ea 342 ;; Service.
98c38bfc 343 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 344 (= dbus-message-type-error (nth 2 event))
98c38bfc 345 (stringp (nth 4 event)))
e49d337b 346 ;; Object path.
98c38bfc 347 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 348 (= dbus-message-type-error (nth 2 event))
98c38bfc 349 (stringp (nth 5 event)))
e49d337b 350 ;; Interface.
98c38bfc 351 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 352 (= dbus-message-type-error (nth 2 event))
98c38bfc 353 (stringp (nth 6 event)))
e49d337b 354 ;; Member.
98c38bfc 355 (or (= dbus-message-type-method-return (nth 2 event))
ba0b66b0 356 (= dbus-message-type-error (nth 2 event))
98c38bfc 357 (stringp (nth 7 event)))
ef6ce14c 358 ;; Handler.
98c38bfc 359 (functionp (nth 8 event)))
3a993e3d
MA
360 (signal 'dbus-error (list "Not a valid D-Bus event" event))))
361
362;;;###autoload
363(defun dbus-handle-event (event)
364 "Handle events from the D-Bus.
5363d8ea 365EVENT is a D-Bus event, see `dbus-check-event'. HANDLER, being
98c38bfc
MA
366part of the event, is called with arguments ARGS.
367If the HANDLER returns an `dbus-error', it is propagated as return message."
3a993e3d 368 (interactive "e")
98c38bfc
MA
369 (condition-case err
370 (let (result)
ba0b66b0 371 ;; We ignore not well-formed events.
98c38bfc 372 (dbus-check-event event)
ba0b66b0
MA
373 ;; Error messages must be propagated.
374 (when (= dbus-message-type-error (nth 2 event))
375 (signal 'dbus-error (nthcdr 9 event)))
376 ;; Apply the handler.
98c38bfc
MA
377 (setq result (apply (nth 8 event) (nthcdr 9 event)))
378 ;; Return a message when it is a message call.
379 (when (= dbus-message-type-method-call (nth 2 event))
380 (dbus-ignore-errors
3dec5c36
MA
381 (if (eq result :ignore)
382 (dbus-method-return-internal
383 (nth 1 event) (nth 3 event) (nth 4 event))
384 (apply 'dbus-method-return-internal
385 (nth 1 event) (nth 3 event) (nth 4 event)
386 (if (consp result) result (list result)))))))
98c38bfc
MA
387 ;; Error handling.
388 (dbus-error
389 ;; Return an error message when it is a message call.
390 (when (= dbus-message-type-method-call (nth 2 event))
391 (dbus-ignore-errors
392 (dbus-method-error-internal
393 (nth 1 event) (nth 3 event) (nth 4 event) (cadr err))))
ba0b66b0 394 ;; Propagate D-Bus error messages.
f213fc09 395 (run-hook-with-args 'dbus-event-error-hooks event err)
ba0b66b0
MA
396 (when (or dbus-debug (= dbus-message-type-error (nth 2 event)))
397 (signal (car err) (cdr err))))))
3a993e3d
MA
398
399(defun dbus-event-bus-name (event)
400 "Return the bus name the event is coming from.
401The result is either the symbol `:system' or the symbol `:session'.
402EVENT is a D-Bus event, see `dbus-check-event'. This function
403raises a `dbus-error' signal in case the event is not well
404formed."
405 (dbus-check-event event)
ef6ce14c 406 (nth 1 event))
3a993e3d 407
98c38bfc
MA
408(defun dbus-event-message-type (event)
409 "Return the message type of the corresponding D-Bus message.
410The result is a number. EVENT is a D-Bus event, see
411`dbus-check-event'. This function raises a `dbus-error' signal
412in case the event is not well formed."
413 (dbus-check-event event)
414 (nth 2 event))
415
e49d337b
MA
416(defun dbus-event-serial-number (event)
417 "Return the serial number of the corresponding D-Bus message.
98c38bfc
MA
418The result is a number. The serial number is needed for
419generating a reply message. EVENT is a D-Bus event, see
420`dbus-check-event'. This function raises a `dbus-error' signal
421in case the event is not well formed."
e49d337b 422 (dbus-check-event event)
98c38bfc 423 (nth 3 event))
e49d337b 424
3a993e3d 425(defun dbus-event-service-name (event)
5363d8ea 426 "Return the name of the D-Bus object the event is coming from.
3a993e3d
MA
427The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
428This function raises a `dbus-error' signal in case the event is
429not well formed."
430 (dbus-check-event event)
98c38bfc 431 (nth 4 event))
3a993e3d
MA
432
433(defun dbus-event-path-name (event)
434 "Return the object path of the D-Bus object the event is coming from.
435The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
436This function raises a `dbus-error' signal in case the event is
437not well formed."
438 (dbus-check-event event)
98c38bfc 439 (nth 5 event))
3a993e3d
MA
440
441(defun dbus-event-interface-name (event)
442 "Return the interface name of the D-Bus object the event is coming from.
443The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
444This function raises a `dbus-error' signal in case the event is
445not well formed."
446 (dbus-check-event event)
98c38bfc 447 (nth 6 event))
3a993e3d
MA
448
449(defun dbus-event-member-name (event)
450 "Return the member name the event is coming from.
451It is either a signal name or a method name. The result is is a
452string. EVENT is a D-Bus event, see `dbus-check-event'. This
453function raises a `dbus-error' signal in case the event is not
454well formed."
455 (dbus-check-event event)
98c38bfc 456 (nth 7 event))
5363d8ea
MA
457
458\f
459;;; D-Bus registered names.
3a993e3d
MA
460
461(defun dbus-list-activatable-names ()
462 "Return the D-Bus service names which can be activated as list.
f636d3ca 463The result is a list of strings, which is `nil' when there are no
3a993e3d 464activatable service names at all."
246a286b
MA
465 (dbus-ignore-errors
466 (dbus-call-method
467 :system dbus-service-dbus
468 dbus-path-dbus dbus-interface-dbus "ListActivatableNames")))
3a993e3d
MA
469
470(defun dbus-list-names (bus)
471 "Return the service names registered at D-Bus BUS.
f636d3ca
MA
472The result is a list of strings, which is `nil' when there are no
473registered service names at all. Well known names are strings
474like \"org.freedesktop.DBus\". Names starting with \":\" are
475unique names for services."
246a286b
MA
476 (dbus-ignore-errors
477 (dbus-call-method
478 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus "ListNames")))
3a993e3d
MA
479
480(defun dbus-list-known-names (bus)
481 "Retrieve all services which correspond to a known name in BUS.
482A service has a known name if it doesn't start with \":\"."
483 (let (result)
484 (dolist (name (dbus-list-names bus) result)
485 (unless (string-equal ":" (substring name 0 1))
486 (add-to-list 'result name 'append)))))
487
488(defun dbus-list-queued-owners (bus service)
f636d3ca
MA
489 "Return the unique names registered at D-Bus BUS and queued for SERVICE.
490The result is a list of strings, or `nil' when there are no
491queued name owners service names at all."
246a286b
MA
492 (dbus-ignore-errors
493 (dbus-call-method
494 bus dbus-service-dbus dbus-path-dbus
495 dbus-interface-dbus "ListQueuedOwners" service)))
3a993e3d
MA
496
497(defun dbus-get-name-owner (bus service)
498 "Return the name owner of SERVICE registered at D-Bus BUS.
f636d3ca 499The result is either a string, or `nil' if there is no name owner."
246a286b
MA
500 (dbus-ignore-errors
501 (dbus-call-method
502 bus dbus-service-dbus dbus-path-dbus
503 dbus-interface-dbus "GetNameOwner" service)))
3a993e3d 504
4ba11bcb
MA
505(defun dbus-ping (bus service)
506 "Check whether SERVICE is registered for D-Bus BUS."
507 ;; "Ping" raises a D-Bus error if SERVICE does not exist.
508 ;; Otherwise, it returns silently with `nil'.
509 (condition-case nil
510 (not
511 (dbus-call-method bus service dbus-path-dbus dbus-interface-peer "Ping"))
512 (dbus-error nil)))
513
f636d3ca
MA
514\f
515;;; D-Bus introspection.
3a993e3d 516
f636d3ca
MA
517(defun dbus-introspect (bus service path)
518 "This function returns all interfaces and sub-nodes of SERVICE,
519registered at object path PATH at bus BUS.
520
521BUS must be either the symbol `:system' or the symbol `:session'.
522SERVICE must be a known service name, and PATH must be a valid
523object path. The last two parameters are strings. The result,
524the introspection data, is a string in XML format."
736215fd
MA
525 ;; We don't want to raise errors. `dbus-call-method-non-blocking'
526 ;; is used, because the handler can be registered in our Emacs
527 ;; instance; caller an callee would block each other.
246a286b 528 (dbus-ignore-errors
736215fd 529 (dbus-call-method-non-blocking
246a286b 530 bus service path dbus-interface-introspectable "Introspect")))
3a993e3d 531
f636d3ca
MA
532(defun dbus-introspect-xml (bus service path)
533 "Return the introspection data of SERVICE in D-Bus BUS at object path PATH.
534The data are a parsed list. The root object is a \"node\",
535representing the object path PATH. The root object can contain
536\"interface\" and further \"node\" objects."
537 ;; We don't want to raise errors.
538 (xml-node-name
539 (ignore-errors
540 (with-temp-buffer
541 (insert (dbus-introspect bus service path))
542 (xml-parse-region (point-min) (point-max))))))
543
544(defun dbus-introspect-get-attribute (object attribute)
545 "Return the ATTRIBUTE value of D-Bus introspection OBJECT.
546ATTRIBUTE must be a string according to the attribute names in
547the D-Bus specification."
548 (xml-get-attribute-or-nil object (intern attribute)))
549
550(defun dbus-introspect-get-node-names (bus service path)
551 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
552It returns a list of strings. The node names stand for further
553object paths of the D-Bus service."
554 (let ((object (dbus-introspect-xml bus service path))
555 result)
556 (dolist (elt (xml-get-children object 'node) result)
557 (add-to-list
558 'result (dbus-introspect-get-attribute elt "name") 'append))))
559
560(defun dbus-introspect-get-all-nodes (bus service path)
561 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
562It returns a list of strings, which are further object paths of SERVICE."
563 (let ((result (list path)))
564 (dolist (elt
565 (dbus-introspect-get-node-names bus service path)
566 result)
567 (setq elt (expand-file-name elt path))
568 (setq result
569 (append result (dbus-introspect-get-all-nodes bus service elt))))))
570
571(defun dbus-introspect-get-interface-names (bus service path)
572 "Return all interface names of SERVICE in D-Bus BUS at object path PATH.
573It returns a list of strings.
574
575There will be always the default interface
576\"org.freedesktop.DBus.Introspectable\". Another default
577interface is \"org.freedesktop.DBus.Properties\". If present,
578\"interface\" objects can also have \"property\" objects as
579children, beside \"method\" and \"signal\" objects."
580 (let ((object (dbus-introspect-xml bus service path))
581 result)
582 (dolist (elt (xml-get-children object 'interface) result)
583 (add-to-list
584 'result (dbus-introspect-get-attribute elt "name") 'append))))
585
586(defun dbus-introspect-get-interface (bus service path interface)
587 "Return the INTERFACE of SERVICE in D-Bus BUS at object path PATH.
588The return value is an XML object. INTERFACE must be a string,
589element of the list returned by
590`dbus-introspect-get-interface-names'. The resulting
591\"interface\" object can contain \"method\", \"signal\",
592\"property\" and \"annotation\" children."
593 (let ((elt (xml-get-children
594 (dbus-introspect-xml bus service path) 'interface)))
595 (while (and elt
596 (not (string-equal
597 interface
598 (dbus-introspect-get-attribute (car elt) "name"))))
599 (setq elt (cdr elt)))
600 (car elt)))
601
602(defun dbus-introspect-get-method-names (bus service path interface)
603 "Return a list of strings of all method names of INTERFACE.
604SERVICE is a service of D-Bus BUS at object path PATH."
605 (let ((object (dbus-introspect-get-interface bus service path interface))
606 result)
607 (dolist (elt (xml-get-children object 'method) result)
608 (add-to-list
609 'result (dbus-introspect-get-attribute elt "name") 'append))))
610
611(defun dbus-introspect-get-method (bus service path interface method)
612 "Return method METHOD of interface INTERFACE as XML object.
613It must be located at SERVICE in D-Bus BUS at object path PATH.
614METHOD must be a string, element of the list returned by
615`dbus-introspect-get-method-names'. The resulting \"method\"
616object can contain \"arg\" and \"annotation\" children."
617 (let ((elt (xml-get-children
618 (dbus-introspect-get-interface bus service path interface)
619 'method)))
620 (while (and elt
621 (not (string-equal
622 method (dbus-introspect-get-attribute (car elt) "name"))))
623 (setq elt (cdr elt)))
624 (car elt)))
625
626(defun dbus-introspect-get-signal-names (bus service path interface)
627 "Return a list of strings of all signal names of INTERFACE.
628SERVICE is a service of D-Bus BUS at object path PATH."
629 (let ((object (dbus-introspect-get-interface bus service path interface))
630 result)
631 (dolist (elt (xml-get-children object 'signal) result)
632 (add-to-list
633 'result (dbus-introspect-get-attribute elt "name") 'append))))
634
635(defun dbus-introspect-get-signal (bus service path interface signal)
636 "Return signal SIGNAL of interface INTERFACE as XML object.
637It must be located at SERVICE in D-Bus BUS at object path PATH.
638SIGNAL must be a string, element of the list returned by
639`dbus-introspect-get-signal-names'. The resulting \"signal\"
640object can contain \"arg\" and \"annotation\" children."
641 (let ((elt (xml-get-children
642 (dbus-introspect-get-interface bus service path interface)
643 'signal)))
644 (while (and elt
645 (not (string-equal
646 signal (dbus-introspect-get-attribute (car elt) "name"))))
647 (setq elt (cdr elt)))
648 (car elt)))
649
650(defun dbus-introspect-get-property-names (bus service path interface)
651 "Return a list of strings of all property names of INTERFACE.
652SERVICE is a service of D-Bus BUS at object path PATH."
653 (let ((object (dbus-introspect-get-interface bus service path interface))
654 result)
655 (dolist (elt (xml-get-children object 'property) result)
656 (add-to-list
657 'result (dbus-introspect-get-attribute elt "name") 'append))))
658
659(defun dbus-introspect-get-property (bus service path interface property)
660 "This function returns PROPERTY of INTERFACE as XML object.
661It must be located at SERVICE in D-Bus BUS at object path PATH.
662PROPERTY must be a string, element of the list returned by
663`dbus-introspect-get-property-names'. The resulting PROPERTY
664object can contain \"annotation\" children."
665 (let ((elt (xml-get-children
666 (dbus-introspect-get-interface bus service path interface)
667 'property)))
668 (while (and elt
669 (not (string-equal
670 property
671 (dbus-introspect-get-attribute (car elt) "name"))))
672 (setq elt (cdr elt)))
673 (car elt)))
674
675(defun dbus-introspect-get-annotation-names
676 (bus service path interface &optional name)
677 "Return all annotation names as list of strings.
678If NAME is `nil', the annotations are children of INTERFACE,
679otherwise NAME must be a \"method\", \"signal\", or \"property\"
680object, where the annotations belong to."
681 (let ((object
682 (if name
683 (or (dbus-introspect-get-method bus service path interface name)
684 (dbus-introspect-get-signal bus service path interface name)
685 (dbus-introspect-get-property bus service path interface name))
686 (dbus-introspect-get-interface bus service path interface)))
687 result)
688 (dolist (elt (xml-get-children object 'annotation) result)
689 (add-to-list
690 'result (dbus-introspect-get-attribute elt "name") 'append))))
691
692(defun dbus-introspect-get-annotation
693 (bus service path interface name annotation)
694 "Return ANNOTATION as XML object.
695If NAME is `nil', ANNOTATION is a child of INTERFACE, otherwise
696NAME must be the name of a \"method\", \"signal\", or
697\"property\" object, where the ANNOTATION belongs to."
698 (let ((elt (xml-get-children
699 (if name
700 (or (dbus-introspect-get-method
701 bus service path interface name)
702 (dbus-introspect-get-signal
703 bus service path interface name)
704 (dbus-introspect-get-property
705 bus service path interface name))
706 (dbus-introspect-get-interface bus service path interface))
707 'annotation)))
708 (while (and elt
709 (not (string-equal
710 annotation
711 (dbus-introspect-get-attribute (car elt) "name"))))
712 (setq elt (cdr elt)))
713 (car elt)))
714
715(defun dbus-introspect-get-argument-names (bus service path interface name)
716 "Return a list of all argument names as list of strings.
717NAME must be a \"method\" or \"signal\" object.
718
719Argument names are optional, the function can return `nil'
720therefore, even if the method or signal has arguments."
721 (let ((object
722 (or (dbus-introspect-get-method bus service path interface name)
723 (dbus-introspect-get-signal bus service path interface name)))
724 result)
725 (dolist (elt (xml-get-children object 'arg) result)
726 (add-to-list
727 'result (dbus-introspect-get-attribute elt "name") 'append))))
728
729(defun dbus-introspect-get-argument (bus service path interface name arg)
730 "Return argument ARG as XML object.
731NAME must be a \"method\" or \"signal\" object. ARG must be a
732string, element of the list returned by `dbus-introspect-get-argument-names'."
733 (let ((elt (xml-get-children
734 (or (dbus-introspect-get-method bus service path interface name)
735 (dbus-introspect-get-signal bus service path interface name))
736 'arg)))
737 (while (and elt
738 (not (string-equal
739 arg (dbus-introspect-get-attribute (car elt) "name"))))
740 (setq elt (cdr elt)))
741 (car elt)))
742
743(defun dbus-introspect-get-signature
744 (bus service path interface name &optional direction)
745 "Return signature of a `method' or `signal', represented by NAME, as string.
746If NAME is a `method', DIRECTION can be either \"in\" or \"out\".
747If DIRECTION is `nil', \"in\" is assumed.
748
749If NAME is a `signal', and DIRECTION is non-`nil', DIRECTION must
750be \"out\"."
751 ;; For methods, we use "in" as default direction.
752 (let ((object (or (dbus-introspect-get-method
753 bus service path interface name)
754 (dbus-introspect-get-signal
755 bus service path interface name))))
756 (when (and (string-equal
757 "method" (dbus-introspect-get-attribute object "name"))
758 (not (stringp direction)))
759 (setq direction "in"))
760 ;; In signals, no direction is given.
761 (when (string-equal "signal" (dbus-introspect-get-attribute object "name"))
762 (setq direction nil))
763 ;; Collect the signatures.
764 (mapconcat
765 '(lambda (x)
766 (let ((arg (dbus-introspect-get-argument
767 bus service path interface name x)))
768 (if (or (not (stringp direction))
769 (string-equal
770 direction
771 (dbus-introspect-get-attribute arg "direction")))
772 (dbus-introspect-get-attribute arg "type")
773 "")))
774 (dbus-introspect-get-argument-names bus service path interface name)
775 "")))
3a993e3d 776
f636d3ca
MA
777\f
778;;; D-Bus properties.
3a993e3d 779
f636d3ca
MA
780(defun dbus-get-property (bus service path interface property)
781 "Return the value of PROPERTY of INTERFACE.
782It will be checked at BUS, SERVICE, PATH. The result can be any
783valid D-Bus value, or `nil' if there is no PROPERTY."
246a286b 784 (dbus-ignore-errors
f636d3ca
MA
785 ;; We must check, whether the "org.freedesktop.DBus.Properties"
786 ;; interface is supported; otherwise the call blocks.
787 (when
788 (member
789 "Get"
790 (dbus-introspect-get-method-names
791 bus service path "org.freedesktop.DBus.Properties"))
792 ;; "Get" returns a variant, so we must use the car.
793 (car
794 (dbus-call-method
795 bus service path dbus-interface-properties
796 "Get" interface property)))))
797
798(defun dbus-set-property (bus service path interface property value)
799 "Set value of PROPERTY of INTERFACE to VALUE.
800It will be checked at BUS, SERVICE, PATH. When the value has
801been set successful, the result is VALUE. Otherwise, `nil' is
802returned."
803 (dbus-ignore-errors
804 (when
805 (and
806 ;; We must check, whether the
807 ;; "org.freedesktop.DBus.Properties" interface is supported;
808 ;; otherwise the call blocks.
809 (member
810 "Set"
811 (dbus-introspect-get-method-names
812 bus service path "org.freedesktop.DBus.Properties"))
813 ;; PROPERTY must be writable.
814 (string-equal
815 "readwrite"
816 (dbus-introspect-get-attribute
ba0b66b0 817 (dbus-introspect-get-property bus service path interface property)
98c38bfc 818 "access")))
f636d3ca
MA
819 ;; "Set" requires a variant.
820 (dbus-call-method
821 bus service path dbus-interface-properties
822 "Set" interface property (list :variant value))
823 ;; Return VALUE.
824 (dbus-get-property bus service path interface property))))
825
826(defun dbus-get-all-properties (bus service path interface)
827 "Return all properties of INTERFACE at BUS, SERVICE, PATH.
828The result is a list of entries. Every entry is a cons of the
829name of the property, and its value. If there are no properties,
830`nil' is returned."
831 ;; "org.freedesktop.DBus.Properties.GetAll" is not supported at
832 ;; all interfaces. Therefore, we do it ourselves.
833 (dbus-ignore-errors
834 (let (result)
835 (dolist (property
836 (dbus-introspect-get-property-names
837 bus service path interface)
838 result)
839 (add-to-list
840 'result
841 (cons property (dbus-get-property bus service path interface property))
842 'append)))))
3a993e3d
MA
843
844(provide 'dbus)
845
79f10da0 846;; arch-tag: a47caf84-9162-4811-90cc-5d388e37b9bd
3a993e3d 847;;; dbus.el ends here