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