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