1 ;;; dbus.el --- Elisp bindings for D-Bus.
3 ;; Copyright (C) 2007-2011 Free Software Foundation, Inc.
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
6 ;; Keywords: comm, hardware
8 ;; This file is part of GNU Emacs.
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.
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.
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/>.
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.
29 ;; Low-level language bindings are implemented in src/dbusbind.c.
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-init-bus
"dbusbind.c")
38 (declare-function dbus-method-return-internal
"dbusbind.c")
39 (declare-function dbus-method-error-internal
"dbusbind.c")
40 (declare-function dbus-register-signal
"dbusbind.c")
41 (declare-function dbus-register-method
"dbusbind.c")
42 (declare-function dbus-send-signal
"dbusbind.c")
44 (defvar dbus-registered-objects-table
)
46 ;; Pacify byte compiler.
52 (defconst dbus-service-dbus
"org.freedesktop.DBus"
53 "The bus name used to talk to the bus itself.")
55 (defconst dbus-path-dbus
"/org/freedesktop/DBus"
56 "The object path used to talk to the bus itself.")
58 (defconst dbus-interface-dbus
"org.freedesktop.DBus"
59 "The interface exported by the object with `dbus-service-dbus' and `dbus-path-dbus'.")
61 (defconst dbus-interface-peer
(concat dbus-interface-dbus
".Peer")
62 "The interface for peer objects.")
64 (defconst dbus-interface-introspectable
65 (concat dbus-interface-dbus
".Introspectable")
66 "The interface supported by introspectable objects.")
68 (defconst dbus-interface-properties
(concat dbus-interface-dbus
".Properties")
69 "The interface for property objects.")
71 (defconst dbus-service-emacs
"org.gnu.Emacs"
72 "The well known service name of Emacs.")
74 (defconst dbus-path-emacs
"/org/gnu/Emacs"
75 "The object path head used by Emacs.")
77 (defconst dbus-message-type-invalid
0
78 "This value is never a valid message type.")
80 (defconst dbus-message-type-method-call
1
81 "Message type of a method call message.")
83 (defconst dbus-message-type-method-return
2
84 "Message type of a method return message.")
86 (defconst dbus-message-type-error
3
87 "Message type of an error reply message.")
89 (defconst dbus-message-type-signal
4
90 "Message type of a signal message.")
92 (defmacro dbus-ignore-errors
(&rest body
)
93 "Execute BODY; signal D-Bus error when `dbus-debug' is non-nil.
94 Otherwise, return result of last form in BODY, or all other errors."
95 (declare (indent 0) (debug t
))
98 (dbus-error (when dbus-debug
(signal (car err
) (cdr err
))))))
99 (font-lock-add-keywords 'emacs-lisp-mode
'("\\<dbus-ignore-errors\\>"))
101 (defvar dbus-event-error-hooks nil
102 "Functions to be called when a D-Bus error happens in the event handler.
103 Every function must accept two arguments, the event and the error variable
104 catched in `condition-case' by `dbus-error'.")
107 ;;; Hash table of registered functions.
109 (defvar dbus-return-values-table
(make-hash-table :test
'equal
)
110 "Hash table for temporary storing arguments of reply messages.
111 A key in this hash table is a list (BUS SERIAL). BUS is either a
112 Lisp symbol, `:system' or `:session', or a string denoting the
113 bus address. SERIAL is the serial number of the reply message.
114 See `dbus-call-method-non-blocking-handler' and
115 `dbus-call-method-non-blocking'.")
117 (defun dbus-list-hash-table ()
118 "Returns all registered member registrations to D-Bus.
119 The return value is a list, with elements of kind (KEY . VALUE).
120 See `dbus-registered-objects-table' for a description of the
124 (lambda (key value
) (add-to-list 'result
(cons key value
) 'append
))
125 dbus-registered-objects-table
)
128 (defun dbus-unregister-object (object)
129 "Unregister OBJECT from D-Bus.
130 OBJECT must be the result of a preceding `dbus-register-method',
131 `dbus-register-property' or `dbus-register-signal' call. It
132 returns `t' if OBJECT has been unregistered, `nil' otherwise.
134 When OBJECT identifies the last method or property, which is
135 registered for the respective service, Emacs releases its
136 association to the service from D-Bus."
138 (unless (and (consp object
) (not (null (car object
))) (consp (cdr object
)))
139 (signal 'wrong-type-argument
(list 'D-Bus object
)))
141 ;; Find the corresponding entry in the hash table.
142 (let* ((key (car object
))
144 (entry (gethash key dbus-registered-objects-table
))
146 ;; entry has the structure ((UNAME SERVICE PATH MEMBER [RULE]) ...).
147 ;; value has the structure ((SERVICE PATH [HANDLER]) ...).
148 ;; MEMBER is either a string (the handler), or a cons cell (a
149 ;; property value). UNAME and property values are not taken into
150 ;; account for comparision.
152 ;; Loop over the registered functions.
156 (butlast (cdr elt
) (- (length (cdr elt
)) (length (car value
)))))
158 ;; Compute new hash value. If it is empty, remove it from the
160 (unless (puthash key
(delete elt entry
) dbus-registered-objects-table
)
161 (remhash key dbus-registered-objects-table
))
162 ;; Remove match rule of signals.
163 (let ((rule (nth 4 elt
)))
166 (car key
) dbus-service-dbus dbus-path-dbus dbus-interface-dbus
167 "RemoveMatch" rule
)))))
168 ;; Check, whether there is still a registered function or property
169 ;; for the given service. If not, unregister the service from the
172 (let ((service (cadr elt
))
180 (when (and (equal bus
(car k
)) (string-equal service
(cadr e
)))
182 dbus-registered-objects-table
)
185 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
186 "ReleaseName" service
)))))
190 (defun dbus-unregister-service (bus service
)
191 "Unregister all objects related to SERVICE from D-Bus BUS.
192 BUS is either a Lisp symbol, `:system' or `:session', or a string
193 denoting the bus address. SERVICE must be a known service name.
195 The function returns a keyword, indicating the result of the
196 operation. One of the following keywords is returned:
198 `:released': Service has become the primary owner of the name.
200 `:non-existent': Service name does not exist on this bus.
202 `:not-owner': We are neither the primary owner nor waiting in the
203 queue of this service."
209 (when (and (equal bus
(car key
)) (string-equal service
(cadr elt
)))
211 (puthash key
(delete elt value
) dbus-registered-objects-table
)
212 (remhash key dbus-registered-objects-table
))))))
213 dbus-registered-objects-table
)
214 (let ((reply (dbus-call-method
215 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
216 "ReleaseName" service
)))
221 (t (signal 'dbus-error
(list "Could not unregister service" service
))))))
223 (defun dbus-call-method-non-blocking-handler (&rest args
)
224 "Handler for reply messages of asynchronous D-Bus message calls.
225 It calls the function stored in `dbus-registered-objects-table'.
226 The result will be made available in `dbus-return-values-table'."
227 (puthash (list (dbus-event-bus-name last-input-event
)
228 (dbus-event-serial-number last-input-event
))
229 (if (= (length args
) 1) (car args
) args
)
230 dbus-return-values-table
))
232 (defun dbus-call-method-non-blocking
233 (bus service path interface method
&rest args
)
234 "Call METHOD on the D-Bus BUS, but don't block the event queue.
235 This is necessary for communicating to registered D-Bus methods,
236 which are running in the same Emacs process.
238 The arguments are the same as in `dbus-call-method'.
240 usage: (dbus-call-method-non-blocking
241 BUS SERVICE PATH INTERFACE METHOD
242 &optional :timeout TIMEOUT &rest ARGS)"
246 'dbus-call-method-asynchronously
247 bus service path interface method
248 'dbus-call-method-non-blocking-handler args
)))
249 ;; Wait until `dbus-call-method-non-blocking-handler' has put the
250 ;; result into `dbus-return-values-table'.
251 (while (eq (gethash key dbus-return-values-table
:ignore
) :ignore
)
252 (read-event nil nil
0.1))
254 ;; Cleanup `dbus-return-values-table'. Return the result.
256 (gethash key dbus-return-values-table nil
)
257 (remhash key dbus-return-values-table
))))
259 (defun dbus-name-owner-changed-handler (&rest args
)
260 "Reapplies all member registrations to D-Bus.
261 This handler is applied when a \"NameOwnerChanged\" signal has
262 arrived. SERVICE is the object name for which the name owner has
263 been changed. OLD-OWNER is the previous owner of SERVICE, or the
264 empty string if SERVICE was not owned yet. NEW-OWNER is the new
265 owner of SERVICE, or the empty string if SERVICE loses any name owner.
267 usage: (dbus-name-owner-changed-handler service old-owner new-owner)"
269 ;; Check the arguments. We should silently ignore it when they
271 (if (and (= (length args
) 3)
273 (stringp (cadr args
))
274 (stringp (caddr args
)))
275 (let ((service (car args
))
276 (old-owner (cadr args
))
277 (new-owner (caddr args
)))
278 ;; Check whether SERVICE is a known name.
279 (when (not (string-match "^:" service
))
283 ;; key has the structure (BUS INTERFACE MEMBER).
284 ;; elt has the structure (UNAME SERVICE PATH HANDLER).
285 (when (string-equal old-owner
(car elt
))
286 ;; Remove old key, and add new entry with changed name.
287 (dbus-unregister-object (list key
(cdr elt
)))
288 ;; Maybe we could arrange the lists a little bit better
289 ;; that we don't need to extract every single element?
290 (dbus-register-signal
292 (nth 0 key
) (nth 1 elt
) (nth 2 elt
)
293 ;; INTERFACE MEMBER HANDLER
294 (nth 1 key
) (nth 2 key
) (nth 3 elt
)))))
295 (copy-hash-table dbus-registered-objects-table
))))
296 ;; The error is reported only in debug mode.
301 (format "Wrong arguments of %s.NameOwnerChanged" dbus-interface-dbus
)
304 ;; Register the handler.
305 (when nil
;ignore-errors
306 (dbus-register-signal
307 :system dbus-service-dbus dbus-path-dbus dbus-interface-dbus
308 "NameOwnerChanged" 'dbus-name-owner-changed-handler
)
309 (dbus-register-signal
310 :session dbus-service-dbus dbus-path-dbus dbus-interface-dbus
311 "NameOwnerChanged" 'dbus-name-owner-changed-handler
))
314 ;;; D-Bus type conversion.
316 (defun dbus-string-to-byte-array (string)
317 "Transforms STRING to list (:array :byte c1 :byte c2 ...).
318 STRING shall be UTF8 coded."
319 (if (zerop (length string
))
320 '(:array
:signature
"y")
322 (dolist (elt (string-to-list string
) (append '(:array
) result
))
323 (setq result
(append result
(list :byte elt
)))))))
325 (defun dbus-byte-array-to-string (byte-array)
326 "Transforms BYTE-ARRAY into UTF8 coded string.
327 BYTE-ARRAY must be a list of structure (c1 c2 ...)."
328 (apply 'string byte-array
))
330 (defun dbus-escape-as-identifier (string)
331 "Escape an arbitrary STRING so it follows the rules for a C identifier.
332 The escaped string can be used as object path component, interface element
333 component, bus name component or member name in D-Bus.
335 The escaping consists of replacing all non-alphanumerics, and the
336 first character if it's a digit, with an underscore and two
337 lower-case hex digits:
339 \"0123abc_xyz\\x01\\xff\" -> \"_30123abc_5fxyz_01_ff\"
341 i.e. similar to URI encoding, but with \"_\" taking the role of \"%\",
342 and a smaller allowed set. As a special case, \"\" is escaped to
345 Returns the escaped string. Algorithm taken from
346 telepathy-glib's `tp-escape-as-identifier'."
347 (if (zerop (length string
))
349 (replace-regexp-in-string
350 "^[0-9]\\|[^A-Za-z0-9]"
351 (lambda (x) (format "_%2x" (aref x
0)))
354 (defun dbus-unescape-from-identifier (string)
355 "Retrieve the original string from the encoded STRING.
356 STRING must have been coded with `dbus-escape-as-identifier'"
357 (if (string-equal string
"_")
359 (replace-regexp-in-string
361 (lambda (x) (format "%c" (string-to-number (substring x
1) 16)))
367 (defun dbus-check-event (event)
368 "Checks whether EVENT is a well formed D-Bus event.
369 EVENT is a list which starts with symbol `dbus-event':
371 (dbus-event BUS TYPE SERIAL SERVICE PATH INTERFACE MEMBER HANDLER &rest ARGS)
373 BUS identifies the D-Bus the message is coming from. It is
374 either a Lisp symbol, `:system' or `:session', or a string
375 denoting the bus address. TYPE is the D-Bus message type which
376 has caused the event, SERIAL is the serial number of the received
377 D-Bus message. SERVICE and PATH are the unique name and the
378 object path of the D-Bus object emitting the message. INTERFACE
379 and MEMBER denote the message which has been sent. HANDLER is
380 the function which has been registered for this message. ARGS
381 are the arguments passed to HANDLER, when it is called during
382 event handling in `dbus-handle-event'.
384 This function raises a `dbus-error' signal in case the event is
386 (when dbus-debug
(message "DBus-Event %s" event
))
387 (unless (and (listp event
)
388 (eq (car event
) 'dbus-event
)
390 (or (symbolp (nth 1 event
))
391 (stringp (nth 1 event
)))
393 (and (natnump (nth 2 event
))
394 (< dbus-message-type-invalid
(nth 2 event
)))
396 (natnump (nth 3 event
))
398 (or (= dbus-message-type-method-return
(nth 2 event
))
399 (= dbus-message-type-error
(nth 2 event
))
400 (stringp (nth 4 event
)))
402 (or (= dbus-message-type-method-return
(nth 2 event
))
403 (= dbus-message-type-error
(nth 2 event
))
404 (stringp (nth 5 event
)))
406 (or (= dbus-message-type-method-return
(nth 2 event
))
407 (= dbus-message-type-error
(nth 2 event
))
408 (stringp (nth 6 event
)))
410 (or (= dbus-message-type-method-return
(nth 2 event
))
411 (= dbus-message-type-error
(nth 2 event
))
412 (stringp (nth 7 event
)))
414 (functionp (nth 8 event
)))
415 (signal 'dbus-error
(list "Not a valid D-Bus event" event
))))
418 (defun dbus-handle-event (event)
419 "Handle events from the D-Bus.
420 EVENT is a D-Bus event, see `dbus-check-event'. HANDLER, being
421 part of the event, is called with arguments ARGS.
422 If the HANDLER returns a `dbus-error', it is propagated as return message."
426 ;; We ignore not well-formed events.
427 (dbus-check-event event
)
428 ;; Error messages must be propagated.
429 (when (= dbus-message-type-error
(nth 2 event
))
430 (signal 'dbus-error
(nthcdr 9 event
)))
431 ;; Apply the handler.
432 (setq result
(apply (nth 8 event
) (nthcdr 9 event
)))
433 ;; Return a message when it is a message call.
434 (when (= dbus-message-type-method-call
(nth 2 event
))
436 (if (eq result
:ignore
)
437 (dbus-method-return-internal
438 (nth 1 event
) (nth 3 event
) (nth 4 event
))
439 (apply 'dbus-method-return-internal
440 (nth 1 event
) (nth 3 event
) (nth 4 event
)
441 (if (consp result
) result
(list result
)))))))
444 ;; Return an error message when it is a message call.
445 (when (= dbus-message-type-method-call
(nth 2 event
))
447 (dbus-method-error-internal
448 (nth 1 event
) (nth 3 event
) (nth 4 event
) (cadr err
))))
449 ;; Propagate D-Bus error messages.
450 (run-hook-with-args 'dbus-event-error-hooks event err
)
451 (when (or dbus-debug
(= dbus-message-type-error
(nth 2 event
)))
452 (signal (car err
) (cdr err
))))))
454 (defun dbus-event-bus-name (event)
455 "Return the bus name the event is coming from.
456 The result is either a Lisp symbol, `:system' or `:session', or a
457 string denoting the bus address. EVENT is a D-Bus event, see
458 `dbus-check-event'. This function raises a `dbus-error' signal
459 in case the event is not well formed."
460 (dbus-check-event event
)
463 (defun dbus-event-message-type (event)
464 "Return the message type of the corresponding D-Bus message.
465 The result is a number. EVENT is a D-Bus event, see
466 `dbus-check-event'. This function raises a `dbus-error' signal
467 in case the event is not well formed."
468 (dbus-check-event event
)
471 (defun dbus-event-serial-number (event)
472 "Return the serial number of the corresponding D-Bus message.
473 The result is a number. The serial number is needed for
474 generating a reply message. EVENT is a D-Bus event, see
475 `dbus-check-event'. This function raises a `dbus-error' signal
476 in case the event is not well formed."
477 (dbus-check-event event
)
480 (defun dbus-event-service-name (event)
481 "Return the name of the D-Bus object the event is coming from.
482 The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
483 This function raises a `dbus-error' signal in case the event is
485 (dbus-check-event event
)
488 (defun dbus-event-path-name (event)
489 "Return the object path of the D-Bus object the event is coming from.
490 The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
491 This function raises a `dbus-error' signal in case the event is
493 (dbus-check-event event
)
496 (defun dbus-event-interface-name (event)
497 "Return the interface name of the D-Bus object the event is coming from.
498 The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
499 This function raises a `dbus-error' signal in case the event is
501 (dbus-check-event event
)
504 (defun dbus-event-member-name (event)
505 "Return the member name the event is coming from.
506 It is either a signal name or a method name. The result is is a
507 string. EVENT is a D-Bus event, see `dbus-check-event'. This
508 function raises a `dbus-error' signal in case the event is not
510 (dbus-check-event event
)
514 ;;; D-Bus registered names.
516 (defun dbus-list-activatable-names (&optional bus
)
517 "Return the D-Bus service names which can be activated as list.
518 If BUS is left nil, `:system' is assumed. The result is a list
519 of strings, which is `nil' when there are no activatable service
523 (or bus
:system
) dbus-service-dbus
524 dbus-path-dbus dbus-interface-dbus
"ListActivatableNames")))
526 (defun dbus-list-names (bus)
527 "Return the service names registered at D-Bus BUS.
528 The result is a list of strings, which is `nil' when there are no
529 registered service names at all. Well known names are strings
530 like \"org.freedesktop.DBus\". Names starting with \":\" are
531 unique names for services."
534 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
"ListNames")))
536 (defun dbus-list-known-names (bus)
537 "Retrieve all services which correspond to a known name in BUS.
538 A service has a known name if it doesn't start with \":\"."
540 (dolist (name (dbus-list-names bus
) result
)
541 (unless (string-equal ":" (substring name
0 1))
542 (add-to-list 'result name
'append
)))))
544 (defun dbus-list-queued-owners (bus service
)
545 "Return the unique names registered at D-Bus BUS and queued for SERVICE.
546 The result is a list of strings, or `nil' when there are no
547 queued name owners service names at all."
550 bus dbus-service-dbus dbus-path-dbus
551 dbus-interface-dbus
"ListQueuedOwners" service
)))
553 (defun dbus-get-name-owner (bus service
)
554 "Return the name owner of SERVICE registered at D-Bus BUS.
555 The result is either a string, or `nil' if there is no name owner."
558 bus dbus-service-dbus dbus-path-dbus
559 dbus-interface-dbus
"GetNameOwner" service
)))
561 (defun dbus-ping (bus service
&optional timeout
)
562 "Check whether SERVICE is registered for D-Bus BUS.
563 TIMEOUT, a nonnegative integer, specifies the maximum number of
564 milliseconds `dbus-ping' must return. The default value is 25,000.
566 Note, that this autoloads SERVICE if it is not running yet. If
567 it shall be checked whether SERVICE is already running, one shall
570 \(member service \(dbus-list-known-names bus))"
571 ;; "Ping" raises a D-Bus error if SERVICE does not exist.
572 ;; Otherwise, it returns silently with `nil'.
575 (if (natnump timeout
)
577 bus service dbus-path-dbus dbus-interface-peer
578 "Ping" :timeout timeout
)
580 bus service dbus-path-dbus dbus-interface-peer
"Ping")))
584 ;;; D-Bus introspection.
586 (defun dbus-introspect (bus service path
)
587 "Return all interfaces and sub-nodes of SERVICE,
588 registered at object path PATH at bus BUS.
590 BUS is either a Lisp symbol, `:system' or `:session', or a string
591 denoting the bus address. SERVICE must be a known service name,
592 and PATH must be a valid object path. The last two parameters
593 are strings. The result, the introspection data, is a string in
595 ;; We don't want to raise errors. `dbus-call-method-non-blocking'
596 ;; is used, because the handler can be registered in our Emacs
597 ;; instance; caller an callee would block each other.
600 (if noninteractive
'dbus-call-method
'dbus-call-method-non-blocking
)
601 bus service path dbus-interface-introspectable
"Introspect")))
603 (defun dbus-introspect-xml (bus service path
)
604 "Return the introspection data of SERVICE in D-Bus BUS at object path PATH.
605 The data are a parsed list. The root object is a \"node\",
606 representing the object path PATH. The root object can contain
607 \"interface\" and further \"node\" objects."
608 ;; We don't want to raise errors.
612 (insert (dbus-introspect bus service path
))
613 (xml-parse-region (point-min) (point-max))))))
615 (defun dbus-introspect-get-attribute (object attribute
)
616 "Return the ATTRIBUTE value of D-Bus introspection OBJECT.
617 ATTRIBUTE must be a string according to the attribute names in
618 the D-Bus specification."
619 (xml-get-attribute-or-nil object
(intern attribute
)))
621 (defun dbus-introspect-get-node-names (bus service path
)
622 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
623 It returns a list of strings. The node names stand for further
624 object paths of the D-Bus service."
625 (let ((object (dbus-introspect-xml bus service path
))
627 (dolist (elt (xml-get-children object
'node
) result
)
629 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
631 (defun dbus-introspect-get-all-nodes (bus service path
)
632 "Return all node names of SERVICE in D-Bus BUS at object path PATH.
633 It returns a list of strings, which are further object paths of SERVICE."
634 (let ((result (list path
)))
636 (dbus-introspect-get-node-names bus service path
)
638 (setq elt
(expand-file-name elt path
))
640 (append result
(dbus-introspect-get-all-nodes bus service elt
))))))
642 (defun dbus-introspect-get-interface-names (bus service path
)
643 "Return all interface names of SERVICE in D-Bus BUS at object path PATH.
644 It returns a list of strings.
646 There will be always the default interface
647 \"org.freedesktop.DBus.Introspectable\". Another default
648 interface is \"org.freedesktop.DBus.Properties\". If present,
649 \"interface\" objects can also have \"property\" objects as
650 children, beside \"method\" and \"signal\" objects."
651 (let ((object (dbus-introspect-xml bus service path
))
653 (dolist (elt (xml-get-children object
'interface
) result
)
655 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
657 (defun dbus-introspect-get-interface (bus service path interface
)
658 "Return the INTERFACE of SERVICE in D-Bus BUS at object path PATH.
659 The return value is an XML object. INTERFACE must be a string,
660 element of the list returned by `dbus-introspect-get-interface-names'.
661 The resulting \"interface\" object can contain \"method\", \"signal\",
662 \"property\" and \"annotation\" children."
663 (let ((elt (xml-get-children
664 (dbus-introspect-xml bus service path
) 'interface
)))
668 (dbus-introspect-get-attribute (car elt
) "name"))))
669 (setq elt
(cdr elt
)))
672 (defun dbus-introspect-get-method-names (bus service path interface
)
673 "Return a list of strings of all method names of INTERFACE.
674 SERVICE is a service of D-Bus BUS at object path PATH."
675 (let ((object (dbus-introspect-get-interface bus service path interface
))
677 (dolist (elt (xml-get-children object
'method
) result
)
679 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
681 (defun dbus-introspect-get-method (bus service path interface method
)
682 "Return method METHOD of interface INTERFACE as XML object.
683 It must be located at SERVICE in D-Bus BUS at object path PATH.
684 METHOD must be a string, element of the list returned by
685 `dbus-introspect-get-method-names'. The resulting \"method\"
686 object can contain \"arg\" and \"annotation\" children."
687 (let ((elt (xml-get-children
688 (dbus-introspect-get-interface bus service path interface
)
692 method
(dbus-introspect-get-attribute (car elt
) "name"))))
693 (setq elt
(cdr elt
)))
696 (defun dbus-introspect-get-signal-names (bus service path interface
)
697 "Return a list of strings of all signal names of INTERFACE.
698 SERVICE is a service of D-Bus BUS at object path PATH."
699 (let ((object (dbus-introspect-get-interface bus service path interface
))
701 (dolist (elt (xml-get-children object
'signal
) result
)
703 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
705 (defun dbus-introspect-get-signal (bus service path interface signal
)
706 "Return signal SIGNAL of interface INTERFACE as XML object.
707 It must be located at SERVICE in D-Bus BUS at object path PATH.
708 SIGNAL must be a string, element of the list returned by
709 `dbus-introspect-get-signal-names'. The resulting \"signal\"
710 object can contain \"arg\" and \"annotation\" children."
711 (let ((elt (xml-get-children
712 (dbus-introspect-get-interface bus service path interface
)
716 signal
(dbus-introspect-get-attribute (car elt
) "name"))))
717 (setq elt
(cdr elt
)))
720 (defun dbus-introspect-get-property-names (bus service path interface
)
721 "Return a list of strings of all property names of INTERFACE.
722 SERVICE is a service of D-Bus BUS at object path PATH."
723 (let ((object (dbus-introspect-get-interface bus service path interface
))
725 (dolist (elt (xml-get-children object
'property
) result
)
727 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
729 (defun dbus-introspect-get-property (bus service path interface property
)
730 "This function returns PROPERTY of INTERFACE as XML object.
731 It must be located at SERVICE in D-Bus BUS at object path PATH.
732 PROPERTY must be a string, element of the list returned by
733 `dbus-introspect-get-property-names'. The resulting PROPERTY
734 object can contain \"annotation\" children."
735 (let ((elt (xml-get-children
736 (dbus-introspect-get-interface bus service path interface
)
741 (dbus-introspect-get-attribute (car elt
) "name"))))
742 (setq elt
(cdr elt
)))
745 (defun dbus-introspect-get-annotation-names
746 (bus service path interface
&optional name
)
747 "Return all annotation names as list of strings.
748 If NAME is `nil', the annotations are children of INTERFACE,
749 otherwise NAME must be a \"method\", \"signal\", or \"property\"
750 object, where the annotations belong to."
753 (or (dbus-introspect-get-method bus service path interface name
)
754 (dbus-introspect-get-signal bus service path interface name
)
755 (dbus-introspect-get-property bus service path interface name
))
756 (dbus-introspect-get-interface bus service path interface
)))
758 (dolist (elt (xml-get-children object
'annotation
) result
)
760 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
762 (defun dbus-introspect-get-annotation
763 (bus service path interface name annotation
)
764 "Return ANNOTATION as XML object.
765 If NAME is `nil', ANNOTATION is a child of INTERFACE, otherwise
766 NAME must be the name of a \"method\", \"signal\", or
767 \"property\" object, where the ANNOTATION belongs to."
768 (let ((elt (xml-get-children
770 (or (dbus-introspect-get-method
771 bus service path interface name
)
772 (dbus-introspect-get-signal
773 bus service path interface name
)
774 (dbus-introspect-get-property
775 bus service path interface name
))
776 (dbus-introspect-get-interface bus service path interface
))
781 (dbus-introspect-get-attribute (car elt
) "name"))))
782 (setq elt
(cdr elt
)))
785 (defun dbus-introspect-get-argument-names (bus service path interface name
)
786 "Return a list of all argument names as list of strings.
787 NAME must be a \"method\" or \"signal\" object.
789 Argument names are optional, the function can return `nil'
790 therefore, even if the method or signal has arguments."
792 (or (dbus-introspect-get-method bus service path interface name
)
793 (dbus-introspect-get-signal bus service path interface name
)))
795 (dolist (elt (xml-get-children object
'arg
) result
)
797 'result
(dbus-introspect-get-attribute elt
"name") 'append
))))
799 (defun dbus-introspect-get-argument (bus service path interface name arg
)
800 "Return argument ARG as XML object.
801 NAME must be a \"method\" or \"signal\" object. ARG must be a string,
802 element of the list returned by `dbus-introspect-get-argument-names'."
803 (let ((elt (xml-get-children
804 (or (dbus-introspect-get-method bus service path interface name
)
805 (dbus-introspect-get-signal bus service path interface name
))
809 arg
(dbus-introspect-get-attribute (car elt
) "name"))))
810 (setq elt
(cdr elt
)))
813 (defun dbus-introspect-get-signature
814 (bus service path interface name
&optional direction
)
815 "Return signature of a `method' or `signal', represented by NAME, as string.
816 If NAME is a `method', DIRECTION can be either \"in\" or \"out\".
817 If DIRECTION is `nil', \"in\" is assumed.
819 If NAME is a `signal', and DIRECTION is non-`nil', DIRECTION must
821 ;; For methods, we use "in" as default direction.
822 (let ((object (or (dbus-introspect-get-method
823 bus service path interface name
)
824 (dbus-introspect-get-signal
825 bus service path interface name
))))
826 (when (and (string-equal
827 "method" (dbus-introspect-get-attribute object
"name"))
828 (not (stringp direction
)))
829 (setq direction
"in"))
830 ;; In signals, no direction is given.
831 (when (string-equal "signal" (dbus-introspect-get-attribute object
"name"))
832 (setq direction nil
))
833 ;; Collect the signatures.
836 (let ((arg (dbus-introspect-get-argument
837 bus service path interface name x
)))
838 (if (or (not (stringp direction
))
841 (dbus-introspect-get-attribute arg
"direction")))
842 (dbus-introspect-get-attribute arg
"type")
844 (dbus-introspect-get-argument-names bus service path interface name
)
848 ;;; D-Bus properties.
850 (defun dbus-get-property (bus service path interface property
)
851 "Return the value of PROPERTY of INTERFACE.
852 It will be checked at BUS, SERVICE, PATH. The result can be any
853 valid D-Bus value, or `nil' if there is no PROPERTY."
855 ;; "Get" returns a variant, so we must use the `car'.
858 (if noninteractive
'dbus-call-method
'dbus-call-method-non-blocking
)
859 bus service path dbus-interface-properties
860 "Get" :timeout
500 interface property
))))
862 (defun dbus-set-property (bus service path interface property value
)
863 "Set value of PROPERTY of INTERFACE to VALUE.
864 It will be checked at BUS, SERVICE, PATH. When the value has
865 been set successful, the result is VALUE. Otherwise, `nil' is
868 ;; "Set" requires a variant.
870 (if noninteractive
'dbus-call-method
'dbus-call-method-non-blocking
)
871 bus service path dbus-interface-properties
872 "Set" :timeout
500 interface property
(list :variant value
))
874 (dbus-get-property bus service path interface property
)))
876 (defun dbus-get-all-properties (bus service path interface
)
877 "Return all properties of INTERFACE at BUS, SERVICE, PATH.
878 The result is a list of entries. Every entry is a cons of the
879 name of the property, and its value. If there are no properties,
882 ;; "GetAll" returns "a{sv}".
888 'dbus-call-method-non-blocking
)
889 bus service path dbus-interface-properties
890 "GetAll" :timeout
500 interface
)
892 (add-to-list 'result
(cons (car dict
) (caadr dict
)) 'append
)))))
894 (defun dbus-register-property
895 (bus service path interface property access value
896 &optional emits-signal dont-register-service
)
897 "Register property PROPERTY on the D-Bus BUS.
899 BUS is either a Lisp symbol, `:system' or `:session', or a string
900 denoting the bus address.
902 SERVICE is the D-Bus service name of the D-Bus. It must be a
903 known name (See discussion of DONT-REGISTER-SERVICE below).
905 PATH is the D-Bus object path SERVICE is registered (See
906 discussion of DONT-REGISTER-SERVICE below). INTERFACE is the
907 name of the interface used at PATH, PROPERTY is the name of the
908 property of INTERFACE. ACCESS indicates, whether the property
909 can be changed by other services via D-Bus. It must be either
910 the symbol `:read' or `:readwrite'. VALUE is the initial value
911 of the property, it can be of any valid type (see
912 `dbus-call-method' for details).
914 If PROPERTY already exists on PATH, it will be overwritten. For
915 properties with access type `:read' this is the only way to
916 change their values. Properties with access type `:readwrite'
917 can be changed by `dbus-set-property'.
919 The interface \"org.freedesktop.DBus.Properties\" is added to
920 PATH, including a default handler for the \"Get\", \"GetAll\" and
921 \"Set\" methods of this interface. When EMITS-SIGNAL is non-nil,
922 the signal \"PropertiesChanged\" is sent when the property is
923 changed by `dbus-set-property'.
925 When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is
926 not registered. This means that other D-Bus clients have no way
927 of noticing the newly registered property. When interfaces are
928 constructed incrementally by adding single methods or properties
929 at a time, DONT-REGISTER-SERVICE can be used to prevent other
930 clients from discovering the still incomplete interface."
931 (unless (member access
'(:read
:readwrite
))
932 (signal 'dbus-error
(list "Access type invalid" access
)))
935 (unless (or dont-register-service
936 (member service
(dbus-list-names bus
)))
938 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
939 "RequestName" service
0))
941 ;; Add handlers for the three property-related methods.
942 (dbus-register-method
943 bus service path dbus-interface-properties
"Get"
944 'dbus-property-handler
'dont-register
)
945 (dbus-register-method
946 bus service path dbus-interface-properties
"GetAll"
947 'dbus-property-handler
'dont-register
)
948 (dbus-register-method
949 bus service path dbus-interface-properties
"Set"
950 'dbus-property-handler
'dont-register
)
952 ;; Register the name SERVICE with BUS.
953 (unless dont-register-service
954 (dbus-register-service bus service
))
956 ;; Send the PropertiesChanged signal.
959 bus service path dbus-interface-properties
"PropertiesChanged"
960 (list (list :dict-entry property
(list :variant value
)))
963 ;; Create a hash table entry. We use nil for the unique name,
964 ;; because the property might be accessed from anybody.
965 (let ((key (list bus interface property
))
971 (if emits-signal
(list access
:emits-signal
) (list access
))
973 (puthash key val dbus-registered-objects-table
)
975 ;; Return the object.
976 (list key
(list service path
))))
978 (defun dbus-property-handler (&rest args
)
979 "Default handler for the \"org.freedesktop.DBus.Properties\" interface.
980 It will be registered for all objects created by `dbus-register-object'."
981 (let ((bus (dbus-event-bus-name last-input-event
))
982 (service (dbus-event-service-name last-input-event
))
983 (path (dbus-event-path-name last-input-event
))
984 (method (dbus-event-member-name last-input-event
))
985 (interface (car args
))
986 (property (cadr args
)))
988 ;; "Get" returns a variant.
989 ((string-equal method
"Get")
990 (let ((entry (gethash (list bus interface property
)
991 dbus-registered-objects-table
)))
992 (when (string-equal path
(nth 2 (car entry
)))
993 (list (list :variant
(cdar (last (car entry
))))))))
995 ;; "Set" expects a variant.
996 ((string-equal method
"Set")
997 (let* ((value (caar (cddr args
)))
998 (entry (gethash (list bus interface property
)
999 dbus-registered-objects-table
))
1000 ;; The value of the hash table is a list; in case of
1001 ;; properties it contains just one element (UNAME SERVICE
1002 ;; PATH OBJECT). OBJECT is a cons cell of a list, which
1003 ;; contains a list of annotations (like :read,
1004 ;; :read-write, :emits-signal), and the value of the
1006 (object (car (last (car entry
)))))
1007 (unless (consp object
)
1009 (list "Property not registered at path" property path
)))
1010 (unless (member :readwrite
(car object
))
1012 (list "Property not writable at path" property path
)))
1013 (puthash (list bus interface property
)
1014 (list (append (butlast (car entry
))
1015 (list (cons (car object
) value
))))
1016 dbus-registered-objects-table
)
1017 ;; Send the "PropertiesChanged" signal.
1018 (when (member :emits-signal
(car object
))
1020 bus service path dbus-interface-properties
"PropertiesChanged"
1021 (list (list :dict-entry property
(list :variant value
)))
1023 ;; Return empty reply.
1026 ;; "GetAll" returns "a{sv}".
1027 ((string-equal method
"GetAll")
1031 (when (and (equal (butlast key
) (list bus interface
))
1032 (string-equal path
(nth 2 (car val
)))
1033 (not (functionp (car (last (car val
))))))
1038 (list :variant
(cdar (last (car val
))))))))
1039 dbus-registered-objects-table
)
1043 ;; Initialize :system and :session buses. This adds their file
1044 ;; descriptors to input_wait_mask, in order to detect incoming
1045 ;; messages immediately.
1046 (when (featurep 'dbusbind
)
1048 (dbus-init-bus :system
)
1049 (dbus-init-bus :session
)))
1053 ;;; dbus.el ends here