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