diff options
| author | Michael Albinus | 2012-04-22 16:11:43 +0200 |
|---|---|---|
| committer | Michael Albinus | 2012-04-22 16:11:43 +0200 |
| commit | dcbf5805ac7ade7fc83f3d209e2d56f029918402 (patch) | |
| tree | ca2d664f76032c4cd39d798ae659e23a30f0b4f8 /lisp | |
| parent | cf20dee0248049a925275f54381cf63bb2017e35 (diff) | |
| download | emacs-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/ChangeLog | 33 | ||||
| -rw-r--r-- | lisp/net/dbus.el | 1065 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-04-22 Chong Yidong <cyd@gnu.org> | 34 | 2012-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. |
| 66 | See 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. |
| 79 | See 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. | ||
| 89 | See 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. | ||
| 116 | See 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. |
| 139 | All object paths provided by the service `dbus-service-emacs' | ||
| 140 | shall 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 | |||
| 105 | caught in `condition-case' by `dbus-error'.") | 159 | caught 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. |
| 112 | A key in this hash table is a list (BUS SERIAL). BUS is either a | 166 | A key in this hash table is a list (:serial BUS SERIAL), like in |
| 113 | Lisp symbol, `:system' or `:session', or a string denoting the | 167 | `dbus-registered-objects-table'. BUS is either a Lisp symbol, |
| 114 | bus address. SERIAL is the serial number of the reply message. | 168 | `:system' or `:session', or a string denoting the bus address. |
| 115 | See `dbus-call-method-non-blocking-handler' and | 169 | SERIAL 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. | ||
| 173 | It calls the function stored in `dbus-registered-objects-table'. | ||
| 174 | The 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 | |||
| 184 | BUS is either a Lisp symbol, `:system' or `:session', or a string | ||
| 185 | denoting the bus address. | ||
| 186 | |||
| 187 | SERVICE is the D-Bus service name to be used. PATH is the D-Bus | ||
| 188 | object path SERVICE is registered at. INTERFACE is an interface | ||
| 189 | offered by SERVICE. It must provide METHOD. | ||
| 190 | |||
| 191 | If the parameter `:timeout' is given, the following integer TIMEOUT | ||
| 192 | specifies the maximum number of milliseconds the method call must | ||
| 193 | return. The default value is 25,000. If the method call doesn't | ||
| 194 | return in time, a D-Bus error is raised. | ||
| 195 | |||
| 196 | All other arguments ARGS are passed to METHOD as arguments. They are | ||
| 197 | converted 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 | |||
| 206 | All arguments can be preceded by a type symbol. For details about | ||
| 207 | type symbols, see Info node `(dbus)Type Conversion'. | ||
| 208 | |||
| 209 | `dbus-call-method' returns the resulting values of METHOD as a list of | ||
| 210 | Lisp objects. The type conversion happens the other direction as for | ||
| 211 | input 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 | |||
| 231 | Example: | ||
| 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 | |||
| 240 | If the result of the METHOD call is just one value, the converted Lisp | ||
| 241 | object 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 | |||
| 286 | BUS is either a Lisp symbol, `:system' or `:session', or a string | ||
| 287 | denoting the bus address. | ||
| 288 | |||
| 289 | SERVICE is the D-Bus service name to be used. PATH is the D-Bus | ||
| 290 | object path SERVICE is registered at. INTERFACE is an interface | ||
| 291 | offered by SERVICE. It must provide METHOD. | ||
| 292 | |||
| 293 | HANDLER is a Lisp function, which is called when the corresponding | ||
| 294 | return message has arrived. If HANDLER is nil, no return message | ||
| 295 | will be expected. | ||
| 296 | |||
| 297 | If the parameter `:timeout' is given, the following integer TIMEOUT | ||
| 298 | specifies the maximum number of milliseconds the method call must | ||
| 299 | return. The default value is 25,000. If the method call doesn't | ||
| 300 | return in time, a D-Bus error is raised. | ||
| 301 | |||
| 302 | All other arguments ARGS are passed to METHOD as arguments. They are | ||
| 303 | converted 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 | |||
| 312 | All arguments can be preceded by a type symbol. For details about | ||
| 313 | type symbols, see Info node `(dbus)Type Conversion'. | ||
| 314 | |||
| 315 | If HANDLER is a Lisp function, the function returns a key into the | ||
| 316 | hash table `dbus-registered-objects-table'. The corresponding entry | ||
| 317 | in the hash table is removed, when the return message has been arrived, | ||
| 318 | and HANDLER is called. | ||
| 319 | |||
| 320 | Example: | ||
| 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 | |||
| 350 | BUS is either a Lisp symbol, `:system' or `:session', or a string | ||
| 351 | denoting the bus address. The signal is sent from the D-Bus object | ||
| 352 | Emacs is registered at BUS. | ||
| 353 | |||
| 354 | SERVICE is the D-Bus name SIGNAL is sent to. It can be either a known | ||
| 355 | name or a unique name. If SERVICE is nil, the signal is sent as | ||
| 356 | broadcast message. PATH is the D-Bus object path SIGNAL is sent from. | ||
| 357 | INTERFACE is an interface available at PATH. It must provide signal | ||
| 358 | SIGNAL. | ||
| 359 | |||
| 360 | All other arguments ARGS are passed to SIGNAL as arguments. They are | ||
| 361 | converted 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 | |||
| 370 | All arguments can be preceded by a type symbol. For details about | ||
| 371 | type symbols, see Info node `(dbus)Type Conversion'. | ||
| 372 | |||
| 373 | Example: | ||
| 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. | ||
| 395 | This 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. | ||
| 409 | This 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. |
| 131 | OBJECT must be the result of a preceding `dbus-register-method', | ||
| 132 | `dbus-register-property' or `dbus-register-signal' call. It | ||
| 133 | returns `t' if OBJECT has been unregistered, `nil' otherwise. | ||
| 134 | 437 | ||
| 135 | When OBJECT identifies the last method or property, which is | 438 | BUS is either a Lisp symbol, `:system' or `:session', or a string |
| 136 | registered for the respective service, Emacs releases its | 439 | denoting the bus address. Both VARIABLE and VALUE should be strings. |
| 137 | association 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. | 441 | Normally, services inherit the environment of the BUS daemon. This |
| 143 | (let* ((key (car object)) | 442 | function 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. | 444 | Some 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)) | 453 | BUS is either a Lisp symbol, `:system' or `:session', or a string |
| 166 | ;; Remove match rule of signals. | 454 | denoting the bus address. |
| 167 | (let ((rule (nth 4 elt))) | 455 | |
| 168 | (when (stringp rule) | 456 | SERVICE is the D-Bus service name that should be registered. It must |
| 169 | (setq service nil) ; We do not need to unregister the service. | 457 | be a known name. |
| 170 | (dbus-call-method | 458 | |
| 171 | bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus | 459 | FLAGS are keywords, which control how the service name is registered. |
| 172 | "RemoveMatch" rule))))) | 460 | The 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. | 463 | owner 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) | 468 | us in the queue. |
| 181 | (dolist (e v) | 469 | |
| 182 | (ignore-errors | 470 | The function returns a keyword, indicating the result of the |
| 183 | (when (and (equal bus (car k)) (string-equal service (cadr e))) | 471 | operation. 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 | 474 | requested 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))))) | 477 | placed 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) |
| 228 | It calls the function stored in `dbus-registered-objects-table'. | 543 | "Register for a signal on the D-Bus BUS. |
| 229 | The 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 | 545 | BUS is either a Lisp symbol, `:system' or `:session', or a string |
| 236 | (bus service path interface method &rest args) | 546 | denoting the bus address. |
| 237 | "Call METHOD on the D-Bus BUS, but don't block the event queue. | ||
| 238 | This is necessary for communicating to registered D-Bus methods, | ||
| 239 | which are running in the same Emacs process. | ||
| 240 | 547 | ||
| 241 | The arguments are the same as in `dbus-call-method'. | 548 | SERVICE is the D-Bus service name used by the sending D-Bus object. |
| 549 | It can be either a known name or the unique name of the D-Bus object | ||
| 550 | sending the signal. | ||
| 551 | |||
| 552 | PATH is the D-Bus object path SERVICE is registered. INTERFACE | ||
| 553 | is an interface offered by SERVICE. It must provide SIGNAL. | ||
| 554 | HANDLER is a Lisp function to be called when the signal is | ||
| 555 | received. It must accept as arguments the values SIGNAL is | ||
| 556 | sending. | ||
| 557 | |||
| 558 | SERVICE, PATH, INTERFACE and SIGNAL can be nil. This is | ||
| 559 | interpreted as a wildcard for the respective argument. | ||
| 560 | |||
| 561 | The remaining arguments ARGS can be keywords or keyword string pairs. | ||
| 562 | The meaning is as follows: | ||
| 563 | |||
| 564 | `:argN' STRING: | ||
| 565 | `:pathN' STRING: This stands for the Nth argument of the | ||
| 566 | signal. `:pathN' arguments can be used for object path wildcard | ||
| 567 | matches as specified by D-Bus, whilest an `:argN' argument | ||
| 568 | requires an exact match. | ||
| 569 | |||
| 570 | `:arg-namespace' STRING: Register for the signals, which first | ||
| 571 | argument defines the service or interface namespace STRING. | ||
| 572 | |||
| 573 | `:path-namespace' STRING: Register for the object path namespace | ||
| 574 | STRING. All signals sent from an object path, which has STRING as | ||
| 575 | the preceding string, are matched. This requires PATH to be nil. | ||
| 576 | |||
| 577 | `:eavesdrop': Register for unicast signals which are not directed | ||
| 578 | to the D-Bus object Emacs is registered at D-Bus BUS, if the | ||
| 579 | security policy of BUS allows this. | ||
| 580 | |||
| 581 | Example: | ||
| 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 | ||
| 243 | usage: (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) |
| 264 | This handler is applied when a \"NameOwnerChanged\" signal has | 699 | "Register for method METHOD on the D-Bus BUS. |
| 265 | arrived. SERVICE is the object name for which the name owner has | 700 | |
| 266 | been changed. OLD-OWNER is the previous owner of SERVICE, or the | 701 | BUS is either a Lisp symbol, `:system' or `:session', or a string |
| 267 | empty string if SERVICE was not owned yet. NEW-OWNER is the new | 702 | denoting the bus address. |
| 268 | owner of SERVICE, or the empty string if SERVICE loses any name owner. | 703 | |
| 269 | 704 | SERVICE is the D-Bus service name of the D-Bus object METHOD is | |
| 270 | usage: (dbus-name-owner-changed-handler service old-owner new-owner)" | 705 | registered for. It must be a known name (See discussion of |
| 271 | (save-match-data | 706 | DONT-REGISTER-SERVICE below). |
| 272 | ;; Check the arguments. We should silently ignore it when they | 707 | |
| 273 | ;; are wrong. | 708 | PATH is the D-Bus object path SERVICE is registered (See discussion of |
| 274 | (if (and (= (length args) 3) | 709 | DONT-REGISTER-SERVICE below). INTERFACE is the interface offered by |
| 275 | (stringp (car args)) | 710 | SERVICE. It must provide METHOD. |
| 276 | (stringp (cadr args)) | 711 | |
| 277 | (stringp (caddr args))) | 712 | HANDLER is a Lisp function to be called when a method call is |
| 278 | (let ((service (car args)) | 713 | received. It must accept the input arguments of METHOD. The return |
| 279 | (old-owner (cadr args))) | 714 | value of HANDLER is used for composing the returning D-Bus message. |
| 280 | ;; Check whether SERVICE is a known name. | 715 | In case HANDLER shall return a reply message with an empty argument |
| 281 | (when (not (string-match "^:" service)) | 716 | list, HANDLER must return the symbol `:ignore'. |
| 282 | (maphash | 717 | |
| 283 | (lambda (key value) | 718 | When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is not |
| 284 | (dolist (elt value) | 719 | registered. This means that other D-Bus clients have no way of |
| 285 | ;; key has the structure (BUS INTERFACE MEMBER). | 720 | noticing the newly registered method. When interfaces are constructed |
| 286 | ;; elt has the structure (UNAME SERVICE PATH HANDLER). | 721 | incrementally by adding single methods or properties at a time, |
| 287 | (when (string-equal old-owner (car elt)) | 722 | DONT-REGISTER-SERVICE can be used to prevent other clients from |
| 288 | ;; Remove old key, and add new entry with changed name. | 723 | discovering 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 | 744 | OBJECT 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 | 746 | returns `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)) | 748 | When OBJECT identifies the last method or property, which is |
| 749 | registered for the respective service, Emacs releases its | ||
| 750 | association 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 | |||
| 596 | XML format." | 1091 | XML 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\"." | |||
| 854 | It will be checked at BUS, SERVICE, PATH. The result can be any | 1349 | It will be checked at BUS, SERVICE, PATH. The result can be any |
| 855 | valid D-Bus value, or `nil' if there is no PROPERTY." | 1350 | valid 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 | |||
| 867 | been set successful, the result is VALUE. Otherwise, `nil' is | 1361 | been set successful, the result is VALUE. Otherwise, `nil' is |
| 868 | returned." | 1362 | returned." |
| 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 | |||
| 931 | at a time, DONT-REGISTER-SERVICE can be used to prevent other | 1421 | at a time, DONT-REGISTER-SERVICE can be used to prevent other |
| 932 | clients from discovering the still incomplete interface." | 1422 | clients 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. |
| 982 | It will be registered for all objects created by `dbus-register-object'." | 1465 | It 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. | ||
| 1533 | The result is a list of objects. Every object is a cons of an | ||
| 1534 | existing path name, and the list of available interface objects. | ||
| 1535 | An interface object is another cons, which car is the interface | ||
| 1536 | name, 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 | |||
| 1559 | If possible, \"org.freedesktop.DBus.ObjectManager.GetManagedObjects\" | ||
| 1560 | is used for retrieving the information. Otherwise, the information | ||
| 1561 | is collected via \"org.freedesktop.DBus.Introspectable.Introspect\" | ||
| 1562 | and \"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. | ||
| 1603 | It 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 |