aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorMichael Albinus2012-04-22 16:11:43 +0200
committerMichael Albinus2012-04-22 16:11:43 +0200
commitdcbf5805ac7ade7fc83f3d209e2d56f029918402 (patch)
treeca2d664f76032c4cd39d798ae659e23a30f0b4f8 /lisp
parentcf20dee0248049a925275f54381cf63bb2017e35 (diff)
downloademacs-dcbf5805ac7ade7fc83f3d209e2d56f029918402.tar.gz
emacs-dcbf5805ac7ade7fc83f3d209e2d56f029918402.zip
Move functions from C to Lisp. Make non-blocking method calls
the default. Implement further D-Bus standard interfaces. * configure.in (dbus_validate_bus_name, dbus_validate_path) (dbus_validate_interface, dbus_validate_member): Check also for these library functions * dbusbind.c (DBUS_NUM_MESSAGE_TYPES): Declare. (QCdbus_request_name_allow_replacement) (QCdbus_request_name_replace_existing) (QCdbus_request_name_do_not_queue) (QCdbus_request_name_reply_primary_owner) (QCdbus_request_name_reply_in_queue) (QCdbus_request_name_reply_exists) (QCdbus_request_name_reply_already_owner): Move to dbus.el. (QCdbus_registered_serial, QCdbus_registered_method) (QCdbus_registered_signal): New Lisp objects. (XD_DEBUG_MESSAGE): Use sizeof. (XD_MESSAGE_TYPE_TO_STRING, XD_OBJECT_TO_STRING) (XD_DBUS_VALIDATE_BUS_ADDRESS, XD_DBUS_VALIDATE_OBJECT) (XD_DBUS_VALIDATE_BUS_NAME, XD_DBUS_VALIDATE_PATH) (XD_DBUS_VALIDATE_INTERFACE, XD_DBUS_VALIDATE_MEMBER): New macros. (XD_CHECK_DBUS_SERIAL): Rename from CHECK_DBUS_SERIAL_GET_SERIAL. (xd_signature, xd_append_arg): Allow float for integer types. (xd_get_connection_references): New function. (xd_get_connection_address): Rename from xd_initialize. Return cached address. (xd_remove_watch): Do not unset $DBUS_SESSION_BUS_ADDRESS. (xd_close_bus): Rename from Fdbus_close_bus. Not needed on Lisp level. (Fdbus_init_bus): New optional arg PRIVATE. Cache address. Return number of recounts. (Fdbus_get_unique_name): Make stronger parameter check. (Fdbus_message_internal): New defun. (Fdbus_call_method, Fdbus_call_method_asynchronously) (Fdbus_method_return_internal, Fdbus_method_error_internal) (Fdbus_send_signal, Fdbus_register_service) (Fdbus_register_signal, Fdbus_register_method): Move to dbus.el. (xd_read_message_1): Obey new structure of Vdbus_registered_objects. (xd_read_queued_messages): Obey new structure of Vdbus_registered_buses. (Vdbus_compiled_version, Vdbus_runtime_version) (Vdbus_message_type_invalid, Vdbus_message_type_method_call) (Vdbus_message_type_method_return, Vdbus_message_type_error) (Vdbus_message_type_signal): New defvars. (Vdbus_registered_buses, Vdbus_registered_objects_table): Adapt docstring. * net/dbus.el (dbus-message-internal): Declare function. Remove unneeded function declarations. (defvar dbus-message-type-invalid, dbus-message-type-method-call) (dbus-message-type-method-return, dbus-message-type-error) (dbus-message-type-signal): Declare variables. Remove local definitions. (dbus-interface-dbus, dbus-interface-peer) (dbus-interface-introspectable, dbus-interface-properties) (dbus-path-emacs, dbus-interface-emacs, dbus-return-values-table): Adapt docstring. (dbus-interface-objectmanager): New defconst. (dbus-call-method, dbus-call-method-asynchronously) (dbus-send-signal, dbus-method-return-internal) (dbus-method-error-internal, dbus-register-service) (dbus-register-signal, dbus-register-method): New defuns, moved from dbusbind.c (dbus-call-method-handler, dbus-setenv) (dbus-get-all-managed-objects, dbus-managed-objects-handler): New defuns. (dbus-call-method-non-blocking): Make it an obsolete function. (dbus-unregister-object, dbus-unregister-service) (dbus-handle-event, dbus-register-property) (dbus-property-handler): Obey the new structure of `bus-registered-objects'. (dbus-introspect): Use `dbus-call-method'. Use a timeout. (dbus-get-property, dbus-set-property, dbus-get-all-properties): Use `dbus-call-method'. * dbus.texi (Version): New node. (Properties and Annotations): Mention the object manager interface. Describe dbus-get-all-managed-objects. (Type Conversion): Floating point numbers are allowed, if an anteger does not fit Emacs's integer range. (Synchronous Methods): Remove obsolete dbus-call-method-non-blocking. (Asynchronous Methods): Fix description of dbus-call-method-asynchronously. (Receiving Method Calls): Fix some minor errors. Add dbus-interface-emacs. (Signals): Describe unicast signals and the new match rules. (Alternative Buses): Add the PRIVATE optional argument to dbus-init-bus. Describe its new return value. Add dbus-setenv.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/ChangeLog33
-rw-r--r--lisp/net/dbus.el1065
2 files changed, 875 insertions, 223 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 14a83de342a..334e34bb712 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,36 @@
12012-04-22 Michael Albinus <michael.albinus@gmx.de>
2
3 Move functions from C to Lisp. Make non-blocking method calls
4 the default. Implement further D-Bus standard interfaces.
5
6 * net/dbus.el (dbus-message-internal): Declare function. Remove
7 unneeded function declarations.
8 (defvar dbus-message-type-invalid, dbus-message-type-method-call)
9 (dbus-message-type-method-return, dbus-message-type-error)
10 (dbus-message-type-signal): Declare variables. Remove local
11 definitions.
12 (dbus-interface-dbus, dbus-interface-peer)
13 (dbus-interface-introspectable, dbus-interface-properties)
14 (dbus-path-emacs, dbus-interface-emacs, dbus-return-values-table):
15 Adapt docstring.
16 (dbus-interface-objectmanager): New defconst.
17 (dbus-call-method, dbus-call-method-asynchronously)
18 (dbus-send-signal, dbus-method-return-internal)
19 (dbus-method-error-internal, dbus-register-service)
20 (dbus-register-signal, dbus-register-method): New defuns, moved
21 from dbusbind.c
22 (dbus-call-method-handler, dbus-setenv)
23 (dbus-get-all-managed-objects, dbus-managed-objects-handler): New
24 defuns.
25 (dbus-call-method-non-blocking): Make it an obsolete function.
26 (dbus-unregister-object, dbus-unregister-service)
27 (dbus-handle-event, dbus-register-property)
28 (dbus-property-handler): Obey the new structure of
29 `bus-registered-objects'.
30 (dbus-introspect): Use `dbus-call-method'. Use a timeout.
31 (dbus-get-property, dbus-set-property, dbus-get-all-properties):
32 Use `dbus-call-method'.
33
12012-04-22 Chong Yidong <cyd@gnu.org> 342012-04-22 Chong Yidong <cyd@gnu.org>
2 35
3 * cus-edit.el (custom-commands, custom-reset-menu) 36 * cus-edit.el (custom-commands, custom-reset-menu)
diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el
index e3144a53fab..ee2bdecb1ac 100644
--- a/lisp/net/dbus.el
+++ b/lisp/net/dbus.el
@@ -28,19 +28,19 @@
28 28
29;; Low-level language bindings are implemented in src/dbusbind.c. 29;; Low-level language bindings are implemented in src/dbusbind.c.
30 30
31;; D-Bus support in the Emacs core can be disabled with configuration
32;; option "--without-dbus".
33
31;;; Code: 34;;; Code:
32 35
33;; D-Bus support in the Emacs core can be disabled with configuration 36;; Declare used subroutines and variables.
34;; option "--without-dbus". Declare used subroutines and variables. 37(declare-function dbus-message-internal "dbusbind.c")
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-init-bus "dbusbind.c")
38(declare-function dbus-method-return-internal "dbusbind.c") 39(defvar dbus-message-type-invalid)
39(declare-function dbus-method-error-internal "dbusbind.c") 40(defvar dbus-message-type-method-call)
40(declare-function dbus-register-service "dbusbind.c") 41(defvar dbus-message-type-method-return)
41(declare-function dbus-register-signal "dbusbind.c") 42(defvar dbus-message-type-error)
42(declare-function dbus-register-method "dbusbind.c") 43(defvar dbus-message-type-signal)
43(declare-function dbus-send-signal "dbusbind.c")
44(defvar dbus-debug) 44(defvar dbus-debug)
45(defvar dbus-registered-objects-table) 45(defvar dbus-registered-objects-table)
46 46
@@ -56,39 +56,93 @@
56(defconst dbus-path-dbus "/org/freedesktop/DBus" 56(defconst dbus-path-dbus "/org/freedesktop/DBus"
57 "The object path used to talk to the bus itself.") 57 "The object path used to talk to the bus itself.")
58 58
59;; Default D-Bus interfaces.
60
59(defconst dbus-interface-dbus "org.freedesktop.DBus" 61(defconst dbus-interface-dbus "org.freedesktop.DBus"
60 "The interface exported by the object with `dbus-service-dbus' and `dbus-path-dbus'.") 62 "The interface exported by the service `dbus-service-dbus'.")
61 63
62(defconst dbus-interface-peer (concat dbus-interface-dbus ".Peer") 64(defconst dbus-interface-peer (concat dbus-interface-dbus ".Peer")
63 "The interface for peer objects.") 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>
64 75
65(defconst dbus-interface-introspectable 76(defconst dbus-interface-introspectable
66 (concat dbus-interface-dbus ".Introspectable") 77 (concat dbus-interface-dbus ".Introspectable")
67 "The interface supported by introspectable objects.") 78 "The interface supported by introspectable objects.
79See URL `http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable'.")
68 80
69(defconst dbus-interface-properties (concat dbus-interface-dbus ".Properties") 81;; <interface name="org.freedesktop.DBus.Introspectable">
70 "The interface for property objects.") 82;; <method name="Introspect">
83;; <arg name="data" type="s" direction="out"/>
84;; </method>
85;; </interface>
71 86
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.
72(defconst dbus-service-emacs "org.gnu.Emacs" 134(defconst dbus-service-emacs "org.gnu.Emacs"
73 "The well known service name of Emacs.") 135 "The well known service name of Emacs.")
74 136
75(defconst dbus-path-emacs "/org/gnu/Emacs" 137(defconst dbus-path-emacs "/org/gnu/Emacs"
76 "The object path head used by Emacs.") 138 "The object path namespace used by Emacs.
139All object paths provided by the service `dbus-service-emacs'
140shall be subdirectories of this path.")
77 141
78(defconst dbus-message-type-invalid 0 142(defconst dbus-interface-emacs "org.gnu.Emacs"
79 "This value is never a valid message type.") 143 "The interface namespace used by Emacs.")
80 144
81(defconst dbus-message-type-method-call 1 145;; D-Bus constants.
82 "Message type of a method call message.")
83
84(defconst dbus-message-type-method-return 2
85 "Message type of a method return message.")
86
87(defconst dbus-message-type-error 3
88 "Message type of an error reply message.")
89
90(defconst dbus-message-type-signal 4
91 "Message type of a signal message.")
92 146
93(defmacro dbus-ignore-errors (&rest body) 147(defmacro dbus-ignore-errors (&rest body)
94 "Execute BODY; signal D-Bus error when `dbus-debug' is non-nil. 148 "Execute BODY; signal D-Bus error when `dbus-debug' is non-nil.
@@ -105,15 +159,267 @@ Every function must accept two arguments, the event and the error variable
105caught in `condition-case' by `dbus-error'.") 159caught in `condition-case' by `dbus-error'.")
106 160
107 161
108;;; Hash table of registered functions. 162;;; Basic D-Bus message functions.
109 163
110(defvar dbus-return-values-table (make-hash-table :test 'equal) 164(defvar dbus-return-values-table (make-hash-table :test 'equal)
111 "Hash table for temporary storing arguments of reply messages. 165 "Hash table for temporary storing arguments of reply messages.
112A key in this hash table is a list (BUS SERIAL). BUS is either a 166A key in this hash table is a list (:serial BUS SERIAL), like in
113Lisp symbol, `:system' or `:session', or a string denoting the 167`dbus-registered-objects-table'. BUS is either a Lisp symbol,
114bus address. SERIAL is the serial number of the reply message. 168`:system' or `:session', or a string denoting the bus address.
115See `dbus-call-method-non-blocking-handler' and 169SERIAL is the serial number of the reply message.")
116`dbus-call-method-non-blocking'.") 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
422;;; Hash table of registered functions.
117 423
118(defun dbus-list-hash-table () 424(defun dbus-list-hash-table ()
119 "Returns all registered member registrations to D-Bus. 425 "Returns all registered member registrations to D-Bus.
@@ -126,69 +432,78 @@ hash table."
126 dbus-registered-objects-table) 432 dbus-registered-objects-table)
127 result)) 433 result))
128 434
129(defun dbus-unregister-object (object) 435(defun dbus-setenv (bus variable value)
130 "Unregister OBJECT from D-Bus. 436 "Set the value of the BUS environment variable named VARIABLE to VALUE.
131OBJECT must be the result of a preceding `dbus-register-method',
132`dbus-register-property' or `dbus-register-signal' call. It
133returns `t' if OBJECT has been unregistered, `nil' otherwise.
134 437
135When OBJECT identifies the last method or property, which is 438BUS is either a Lisp symbol, `:system' or `:session', or a string
136registered for the respective service, Emacs releases its 439denoting the bus address. Both VARIABLE and VALUE should be strings.
137association to the service from D-Bus."
138 ;; Check parameter.
139 (unless (and (consp object) (not (null (car object))) (consp (cdr object)))
140 (signal 'wrong-type-argument (list 'D-Bus object)))
141 440
142 ;; Find the corresponding entry in the hash table. 441Normally, services inherit the environment of the BUS daemon. This
143 (let* ((key (car object)) 442function adds to or modifies that environment when activating services.
144 (value (cadr object))
145 (bus (car key))
146 (service (car value))
147 (entry (gethash key dbus-registered-objects-table))
148 ret)
149 ;; key has the structure (BUS INTERFACE MEMBER).
150 ;; value has the structure (SERVICE PATH [HANDLER]).
151 ;; entry has the structure ((UNAME SERVICE PATH MEMBER [RULE]) ...).
152 ;; MEMBER is either a string (the handler), or a cons cell (a
153 ;; property value). UNAME and property values are not taken into
154 ;; account for comparison.
155 443
156 ;; Loop over the registered functions. 444Some bus instances, such as `:system', may disable setting the environment."
157 (dolist (elt entry) 445 (dbus-call-method
158 (when (equal 446 bus dbus-service-dbus dbus-path-dbus
159 value 447 dbus-interface-dbus "UpdateActivationEnvironment"
160 (butlast (cdr elt) (- (length (cdr elt)) (length value)))) 448 `(:array (:dict-entry ,variable ,value))))
161 (setq ret t) 449
162 ;; Compute new hash value. If it is empty, remove it from the 450(defun dbus-register-service (bus service &rest flags)
163 ;; hash table. 451 "Register known name SERVICE on the D-Bus BUS.
164 (unless (puthash key (delete elt entry) dbus-registered-objects-table) 452
165 (remhash key dbus-registered-objects-table)) 453BUS is either a Lisp symbol, `:system' or `:session', or a string
166 ;; Remove match rule of signals. 454denoting the bus address.
167 (let ((rule (nth 4 elt))) 455
168 (when (stringp rule) 456SERVICE is the D-Bus service name that should be registered. It must
169 (setq service nil) ; We do not need to unregister the service. 457be a known name.
170 (dbus-call-method 458
171 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus 459FLAGS are keywords, which control how the service name is registered.
172 "RemoveMatch" rule))))) 460The following keywords are recognized:
173 ;; Check, whether there is still a registered function or property 461
174 ;; for the given service. If not, unregister the service from the 462`:allow-replacement': Allow another service to become the primary
175 ;; bus. 463owner if requested.
176 (when service 464
177 (dolist (elt entry) 465`:replace-existing': Request to replace the current primary owner.
178 (let (found) 466
179 (maphash 467`:do-not-queue': If we can not become the primary owner do not place
180 (lambda (k v) 468us in the queue.
181 (dolist (e v) 469
182 (ignore-errors 470The function returns a keyword, indicating the result of the
183 (when (and (equal bus (car k)) (string-equal service (cadr e))) 471operation. One of the following keywords is returned:
184 (setq found t))))) 472
185 dbus-registered-objects-table) 473`:primary-owner': Service has become the primary owner of the
186 (unless found 474requested name.
187 (dbus-call-method 475
188 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus 476`:in-queue': Service could not become the primary owner and has been
189 "ReleaseName" service))))) 477placed in the queue.
190 ;; Return. 478
191 ret)) 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))))))
192 507
193(defun dbus-unregister-service (bus service) 508(defun dbus-unregister-service (bus service)
194 "Unregister all objects related to SERVICE from D-Bus BUS. 509 "Unregister all objects related to SERVICE from D-Bus BUS.
@@ -209,7 +524,7 @@ queue of this service."
209 (lambda (key value) 524 (lambda (key value)
210 (dolist (elt value) 525 (dolist (elt value)
211 (ignore-errors 526 (ignore-errors
212 (when (and (equal bus (car key)) (string-equal service (cadr elt))) 527 (when (and (equal bus (cadr key)) (string-equal service (cadr elt)))
213 (unless 528 (unless
214 (puthash key (delete elt value) dbus-registered-objects-table) 529 (puthash key (delete elt value) dbus-registered-objects-table)
215 (remhash key dbus-registered-objects-table)))))) 530 (remhash key dbus-registered-objects-table))))))
@@ -223,94 +538,274 @@ queue of this service."
223 (3 :not-owner) 538 (3 :not-owner)
224 (t (signal 'dbus-error (list "Could not unregister service" service)))))) 539 (t (signal 'dbus-error (list "Could not unregister service" service))))))
225 540
226(defun dbus-call-method-non-blocking-handler (&rest args) 541(defun dbus-register-signal
227 "Handler for reply messages of asynchronous D-Bus message calls. 542 (bus service path interface signal handler &rest args)
228It calls the function stored in `dbus-registered-objects-table'. 543 "Register for a signal on the D-Bus BUS.
229The result will be made available in `dbus-return-values-table'."
230 (puthash (list (dbus-event-bus-name last-input-event)
231 (dbus-event-serial-number last-input-event))
232 (if (= (length args) 1) (car args) args)
233 dbus-return-values-table))
234 544
235(defun dbus-call-method-non-blocking 545BUS is either a Lisp symbol, `:system' or `:session', or a string
236 (bus service path interface method &rest args) 546denoting the bus address.
237 "Call METHOD on the D-Bus BUS, but don't block the event queue.
238This is necessary for communicating to registered D-Bus methods,
239which are running in the same Emacs process.
240 547
241The arguments are the same as in `dbus-call-method'. 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
567matches as specified by D-Bus, whilest an `:argN' argument
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))))
242 684
243usage: (dbus-call-method-non-blocking 685 (when dbus-debug (message "Matching rule \"%s\" created" rule))
244 BUS SERVICE PATH INTERFACE METHOD
245 &optional :timeout TIMEOUT &rest ARGS)"
246 686
247 (let ((key 687 ;; Create a hash table entry.
248 (apply 688 (setq key (list :signal bus interface signal)
249 'dbus-call-method-asynchronously 689 key1 (list uname service path handler rule)
250 bus service path interface method 690 value (gethash key dbus-registered-objects-table))
251 'dbus-call-method-non-blocking-handler args))) 691 (unless (member key1 value)
252 ;; Wait until `dbus-call-method-non-blocking-handler' has put the 692 (puthash key (cons key1 value) dbus-registered-objects-table))
253 ;; result into `dbus-return-values-table'.
254 (while (eq (gethash key dbus-return-values-table :ignore) :ignore)
255 (read-event nil nil 0.1))
256 693
257 ;; Cleanup `dbus-return-values-table'. Return the result. 694 ;; Return the object.
258 (prog1 695 (list key (list service path handler))))
259 (gethash key dbus-return-values-table nil)
260 (remhash key dbus-return-values-table))))
261 696
262(defun dbus-name-owner-changed-handler (&rest args) 697(defun dbus-register-method
263 "Reapplies all member registrations to D-Bus. 698 (bus service path interface method handler &optional dont-register-service)
264This handler is applied when a \"NameOwnerChanged\" signal has 699 "Register for method METHOD on the D-Bus BUS.
265arrived. SERVICE is the object name for which the name owner has 700
266been changed. OLD-OWNER is the previous owner of SERVICE, or the 701BUS is either a Lisp symbol, `:system' or `:session', or a string
267empty string if SERVICE was not owned yet. NEW-OWNER is the new 702denoting the bus address.
268owner of SERVICE, or the empty string if SERVICE loses any name owner. 703
269 704SERVICE is the D-Bus service name of the D-Bus object METHOD is
270usage: (dbus-name-owner-changed-handler service old-owner new-owner)" 705registered for. It must be a known name (See discussion of
271 (save-match-data 706DONT-REGISTER-SERVICE below).
272 ;; Check the arguments. We should silently ignore it when they 707
273 ;; are wrong. 708PATH is the D-Bus object path SERVICE is registered (See discussion of
274 (if (and (= (length args) 3) 709DONT-REGISTER-SERVICE below). INTERFACE is the interface offered by
275 (stringp (car args)) 710SERVICE. It must provide METHOD.
276 (stringp (cadr args)) 711
277 (stringp (caddr args))) 712HANDLER is a Lisp function to be called when a method call is
278 (let ((service (car args)) 713received. It must accept the input arguments of METHOD. The return
279 (old-owner (cadr args))) 714value of HANDLER is used for composing the returning D-Bus message.
280 ;; Check whether SERVICE is a known name. 715In case HANDLER shall return a reply message with an empty argument
281 (when (not (string-match "^:" service)) 716list, HANDLER must return the symbol `:ignore'.
282 (maphash 717
283 (lambda (key value) 718When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is not
284 (dolist (elt value) 719registered. This means that other D-Bus clients have no way of
285 ;; key has the structure (BUS INTERFACE MEMBER). 720noticing the newly registered method. When interfaces are constructed
286 ;; elt has the structure (UNAME SERVICE PATH HANDLER). 721incrementally by adding single methods or properties at a time,
287 (when (string-equal old-owner (car elt)) 722DONT-REGISTER-SERVICE can be used to prevent other clients from
288 ;; Remove old key, and add new entry with changed name. 723discovering the still incomplete interface."
289 (dbus-unregister-object (list key (cdr elt))) 724
290 ;; Maybe we could arrange the lists a little bit better 725 ;; Register SERVICE.
291 ;; that we don't need to extract every single element? 726 (unless (or dont-register-service
292 (dbus-register-signal 727 (member service (dbus-list-names bus)))
293 ;; BUS SERVICE PATH 728 (dbus-register-service bus service))
294 (nth 0 key) (nth 1 elt) (nth 2 elt) 729
295 ;; INTERFACE MEMBER HANDLER 730 ;; Create a hash table entry. We use nil for the unique name,
296 (nth 1 key) (nth 2 key) (nth 3 elt))))) 731 ;; because the method might be called from anybody.
297 (copy-hash-table dbus-registered-objects-table)))) 732 (let* ((key (list :method bus interface method))
298 ;; The error is reported only in debug mode. 733 (key1 (list nil service path handler))
299 (when dbus-debug 734 (value (gethash key dbus-registered-objects-table)))
300 (signal 735
301 'dbus-error 736 (unless (member key1 value)
302 (cons 737 (puthash key (cons key1 value) dbus-registered-objects-table))
303 (format "Wrong arguments of %s.NameOwnerChanged" dbus-interface-dbus) 738
304 args)))))) 739 ;; Return the object.
305 740 (list key (list service path handler))))
306;; Register the handler. 741
307(when nil ;ignore-errors 742(defun dbus-unregister-object (object)
308 (dbus-register-signal 743 "Unregister OBJECT from D-Bus.
309 :system dbus-service-dbus dbus-path-dbus dbus-interface-dbus 744OBJECT must be the result of a preceding `dbus-register-method',
310 "NameOwnerChanged" 'dbus-name-owner-changed-handler) 745`dbus-register-property' or `dbus-register-signal' call. It
311 (dbus-register-signal 746returns `t' if OBJECT has been unregistered, `nil' otherwise.
312 :session dbus-service-dbus dbus-path-dbus dbus-interface-dbus 747
313 "NameOwnerChanged" 'dbus-name-owner-changed-handler)) 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))
314 809
315 810
316;;; D-Bus type conversion. 811;;; D-Bus type conversion.
@@ -437,9 +932,9 @@ If the HANDLER returns a `dbus-error', it is propagated as return message."
437 (dbus-ignore-errors 932 (dbus-ignore-errors
438 (if (eq result :ignore) 933 (if (eq result :ignore)
439 (dbus-method-return-internal 934 (dbus-method-return-internal
440 (nth 1 event) (nth 3 event) (nth 4 event)) 935 (nth 1 event) (nth 4 event) (nth 3 event))
441 (apply 'dbus-method-return-internal 936 (apply 'dbus-method-return-internal
442 (nth 1 event) (nth 3 event) (nth 4 event) 937 (nth 1 event) (nth 4 event) (nth 3 event)
443 (if (consp result) result (list result))))))) 938 (if (consp result) result (list result)))))))
444 ;; Error handling. 939 ;; Error handling.
445 (dbus-error 940 (dbus-error
@@ -447,7 +942,7 @@ If the HANDLER returns a `dbus-error', it is propagated as return message."
447 (when (= dbus-message-type-method-call (nth 2 event)) 942 (when (= dbus-message-type-method-call (nth 2 event))
448 (dbus-ignore-errors 943 (dbus-ignore-errors
449 (dbus-method-error-internal 944 (dbus-method-error-internal
450 (nth 1 event) (nth 3 event) (nth 4 event) (cadr err)))) 945 (nth 1 event) (nth 4 event) (nth 3 event) (cadr err))))
451 ;; Propagate D-Bus error messages. 946 ;; Propagate D-Bus error messages.
452 (run-hook-with-args 'dbus-event-error-hooks event err) 947 (run-hook-with-args 'dbus-event-error-hooks event err)
453 (when (or dbus-debug (= dbus-message-type-error (nth 2 event))) 948 (when (or dbus-debug (= dbus-message-type-error (nth 2 event)))
@@ -596,11 +1091,11 @@ are strings. The result, the introspection data, is a string in
596XML format." 1091XML format."
597 ;; We don't want to raise errors. `dbus-call-method-non-blocking' 1092 ;; We don't want to raise errors. `dbus-call-method-non-blocking'
598 ;; is used, because the handler can be registered in our Emacs 1093 ;; is used, because the handler can be registered in our Emacs
599 ;; instance; caller an callee would block each other. 1094 ;; instance; caller and callee would block each other.
600 (dbus-ignore-errors 1095 (dbus-ignore-errors
601 (funcall 1096 (dbus-call-method
602 (if noninteractive 'dbus-call-method 'dbus-call-method-non-blocking) 1097 bus service path dbus-interface-introspectable "Introspect"
603 bus service path dbus-interface-introspectable "Introspect"))) 1098 :timeout 1000)))
604 1099
605(defun dbus-introspect-xml (bus service path) 1100(defun dbus-introspect-xml (bus service path)
606 "Return the introspection data of SERVICE in D-Bus BUS at object path PATH. 1101 "Return the introspection data of SERVICE in D-Bus BUS at object path PATH.
@@ -854,12 +1349,11 @@ be \"out\"."
854It will be checked at BUS, SERVICE, PATH. The result can be any 1349It will be checked at BUS, SERVICE, PATH. The result can be any
855valid D-Bus value, or `nil' if there is no PROPERTY." 1350valid D-Bus value, or `nil' if there is no PROPERTY."
856 (dbus-ignore-errors 1351 (dbus-ignore-errors
857 ;; "Get" returns a variant, so we must use the `car'. 1352 ;; "Get" returns a variant, so we must use the `car'.
858 (car 1353 (car
859 (funcall 1354 (dbus-call-method
860 (if noninteractive 'dbus-call-method 'dbus-call-method-non-blocking) 1355 bus service path dbus-interface-properties
861 bus service path dbus-interface-properties 1356 "Get" :timeout 500 interface property))))
862 "Get" :timeout 500 interface property))))
863 1357
864(defun dbus-set-property (bus service path interface property value) 1358(defun dbus-set-property (bus service path interface property value)
865 "Set value of PROPERTY of INTERFACE to VALUE. 1359 "Set value of PROPERTY of INTERFACE to VALUE.
@@ -867,13 +1361,12 @@ It will be checked at BUS, SERVICE, PATH. When the value has
867been set successful, the result is VALUE. Otherwise, `nil' is 1361been set successful, the result is VALUE. Otherwise, `nil' is
868returned." 1362returned."
869 (dbus-ignore-errors 1363 (dbus-ignore-errors
870 ;; "Set" requires a variant. 1364 ;; "Set" requires a variant.
871 (funcall 1365 (dbus-call-method
872 (if noninteractive 'dbus-call-method 'dbus-call-method-non-blocking) 1366 bus service path dbus-interface-properties
873 bus service path dbus-interface-properties 1367 "Set" :timeout 500 interface property (list :variant value))
874 "Set" :timeout 500 interface property (list :variant value)) 1368 ;; Return VALUE.
875 ;; Return VALUE. 1369 (dbus-get-property bus service path interface property)))
876 (dbus-get-property bus service path interface property)))
877 1370
878(defun dbus-get-all-properties (bus service path interface) 1371(defun dbus-get-all-properties (bus service path interface)
879 "Return all properties of INTERFACE at BUS, SERVICE, PATH. 1372 "Return all properties of INTERFACE at BUS, SERVICE, PATH.
@@ -884,10 +1377,7 @@ name of the property, and its value. If there are no properties,
884 ;; "GetAll" returns "a{sv}". 1377 ;; "GetAll" returns "a{sv}".
885 (let (result) 1378 (let (result)
886 (dolist (dict 1379 (dolist (dict
887 (funcall 1380 (dbus-call-method
888 (if noninteractive
889 'dbus-call-method
890 'dbus-call-method-non-blocking)
891 bus service path dbus-interface-properties 1381 bus service path dbus-interface-properties
892 "GetAll" :timeout 500 interface) 1382 "GetAll" :timeout 500 interface)
893 result) 1383 result)
@@ -931,14 +1421,7 @@ constructed incrementally by adding single methods or properties
931at a time, DONT-REGISTER-SERVICE can be used to prevent other 1421at a time, DONT-REGISTER-SERVICE can be used to prevent other
932clients from discovering the still incomplete interface." 1422clients from discovering the still incomplete interface."
933 (unless (member access '(:read :readwrite)) 1423 (unless (member access '(:read :readwrite))
934 (signal 'dbus-error (list "Access type invalid" access))) 1424 (signal 'wrong-type-argument (list "Access type invalid" access)))
935
936 ;; Register SERVICE.
937 (unless (or dont-register-service
938 (member service (dbus-list-names bus)))
939 (dbus-call-method
940 bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
941 "RequestName" service 0))
942 1425
943 ;; Add handlers for the three property-related methods. 1426 ;; Add handlers for the three property-related methods.
944 (dbus-register-method 1427 (dbus-register-method
@@ -951,20 +1434,20 @@ clients from discovering the still incomplete interface."
951 bus service path dbus-interface-properties "Set" 1434 bus service path dbus-interface-properties "Set"
952 'dbus-property-handler 'dont-register) 1435 'dbus-property-handler 'dont-register)
953 1436
954 ;; Register the name SERVICE with BUS. 1437 ;; Register SERVICE.
955 (unless dont-register-service 1438 (unless (or dont-register-service (member service (dbus-list-names bus)))
956 (dbus-register-service bus service)) 1439 (dbus-register-service bus service))
957 1440
958 ;; Send the PropertiesChanged signal. 1441 ;; Send the PropertiesChanged signal.
959 (when emits-signal 1442 (when emits-signal
960 (dbus-send-signal 1443 (dbus-send-signal
961 bus service path dbus-interface-properties "PropertiesChanged" 1444 bus service path dbus-interface-properties "PropertiesChanged"
962 (list (list :dict-entry property (list :variant value))) 1445 `((:dict-entry ,property (:variant ,value)))
963 '(:array))) 1446 '(:array)))
964 1447
965 ;; Create a hash table entry. We use nil for the unique name, 1448 ;; Create a hash table entry. We use nil for the unique name,
966 ;; because the property might be accessed from anybody. 1449 ;; because the property might be accessed from anybody.
967 (let ((key (list bus interface property)) 1450 (let ((key (list :property bus interface property))
968 (val 1451 (val
969 (list 1452 (list
970 (list 1453 (list
@@ -979,7 +1462,7 @@ clients from discovering the still incomplete interface."
979 1462
980(defun dbus-property-handler (&rest args) 1463(defun dbus-property-handler (&rest args)
981 "Default handler for the \"org.freedesktop.DBus.Properties\" interface. 1464 "Default handler for the \"org.freedesktop.DBus.Properties\" interface.
982It will be registered for all objects created by `dbus-register-object'." 1465It will be registered for all objects created by `dbus-register-property'."
983 (let ((bus (dbus-event-bus-name last-input-event)) 1466 (let ((bus (dbus-event-bus-name last-input-event))
984 (service (dbus-event-service-name last-input-event)) 1467 (service (dbus-event-service-name last-input-event))
985 (path (dbus-event-path-name last-input-event)) 1468 (path (dbus-event-path-name last-input-event))
@@ -989,15 +1472,15 @@ It will be registered for all objects created by `dbus-register-object'."
989 (cond 1472 (cond
990 ;; "Get" returns a variant. 1473 ;; "Get" returns a variant.
991 ((string-equal method "Get") 1474 ((string-equal method "Get")
992 (let ((entry (gethash (list bus interface property) 1475 (let ((entry (gethash (list :property bus interface property)
993 dbus-registered-objects-table))) 1476 dbus-registered-objects-table)))
994 (when (string-equal path (nth 2 (car entry))) 1477 (when (string-equal path (nth 2 (car entry)))
995 (list (list :variant (cdar (last (car entry)))))))) 1478 `((:variant ,(cdar (last (car entry))))))))
996 1479
997 ;; "Set" expects a variant. 1480 ;; "Set" expects a variant.
998 ((string-equal method "Set") 1481 ((string-equal method "Set")
999 (let* ((value (caar (cddr args))) 1482 (let* ((value (caar (cddr args)))
1000 (entry (gethash (list bus interface property) 1483 (entry (gethash (list :property bus interface property)
1001 dbus-registered-objects-table)) 1484 dbus-registered-objects-table))
1002 ;; The value of the hash table is a list; in case of 1485 ;; The value of the hash table is a list; in case of
1003 ;; properties it contains just one element (UNAME SERVICE 1486 ;; properties it contains just one element (UNAME SERVICE
@@ -1012,7 +1495,7 @@ It will be registered for all objects created by `dbus-register-object'."
1012 (unless (member :readwrite (car object)) 1495 (unless (member :readwrite (car object))
1013 (signal 'dbus-error 1496 (signal 'dbus-error
1014 (list "Property not writable at path" property path))) 1497 (list "Property not writable at path" property path)))
1015 (puthash (list bus interface property) 1498 (puthash (list :property bus interface property)
1016 (list (append (butlast (car entry)) 1499 (list (append (butlast (car entry))
1017 (list (cons (car object) value)))) 1500 (list (cons (car object) value))))
1018 dbus-registered-objects-table) 1501 dbus-registered-objects-table)
@@ -1020,7 +1503,7 @@ It will be registered for all objects created by `dbus-register-object'."
1020 (when (member :emits-signal (car object)) 1503 (when (member :emits-signal (car object))
1021 (dbus-send-signal 1504 (dbus-send-signal
1022 bus service path dbus-interface-properties "PropertiesChanged" 1505 bus service path dbus-interface-properties "PropertiesChanged"
1023 (list (list :dict-entry property (list :variant value))) 1506 `((:dict-entry ,property (:variant ,value)))
1024 '(:array))) 1507 '(:array)))
1025 ;; Return empty reply. 1508 ;; Return empty reply.
1026 :ignore)) 1509 :ignore))
@@ -1030,7 +1513,7 @@ It will be registered for all objects created by `dbus-register-object'."
1030 (let (result) 1513 (let (result)
1031 (maphash 1514 (maphash
1032 (lambda (key val) 1515 (lambda (key val)
1033 (when (and (equal (butlast key) (list bus interface)) 1516 (when (and (equal (butlast key) (list :property bus interface))
1034 (string-equal path (nth 2 (car val))) 1517 (string-equal path (nth 2 (car val)))
1035 (not (functionp (car (last (car val)))))) 1518 (not (functionp (car (last (car val))))))
1036 (add-to-list 1519 (add-to-list
@@ -1042,15 +1525,151 @@ It will be registered for all objects created by `dbus-register-object'."
1042 ;; Return the result, or an empty array. 1525 ;; Return the result, or an empty array.
1043 (list :array (or result '(:signature "{sv}")))))))) 1526 (list :array (or result '(:signature "{sv}"))))))))
1044 1527
1528
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
1045 1658
1046;; Initialize :system and :session buses. This adds their file 1659;; Initialize `:system' and `:session' buses. This adds their file
1047;; descriptors to input_wait_mask, in order to detect incoming 1660;; descriptors to input_wait_mask, in order to detect incoming
1048;; messages immediately. 1661;; messages immediately.
1049(when (featurep 'dbusbind) 1662(when (featurep 'dbusbind)
1050 (dbus-ignore-errors 1663 (dbus-ignore-errors
1051 (dbus-init-bus :system) 1664 (dbus-init-bus :system))
1665 (dbus-ignore-errors
1052 (dbus-init-bus :session))) 1666 (dbus-init-bus :session)))
1053 1667
1054(provide 'dbus) 1668(provide 'dbus)
1055 1669
1670;;; TODO:
1671
1672;; * Implement org.freedesktop.DBus.ObjectManager.InterfacesAdded and
1673;; org.freedesktop.DBus.ObjectManager.InterfacesRemoved.
1674
1056;;; dbus.el ends here 1675;;; dbus.el ends here