2 * Copyright © 2006-2007 Daniel Stone
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
23 * Author: Daniel Stone <daniel@fooishbar.org>
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
30 #include <dbus/dbus.h>
35 #include "config-backends.h"
36 #include "opaque.h" /* for 'display': there should be a better way. */
42 #define MATCH_RULE "type='method_call',interface='org.x.config.input'"
44 #define MALFORMED_MSG "[config/dbus] malformed message, dropping"
45 #define MALFORMED_MESSAGE() { DebugF(MALFORMED_MSG "\n"); \
48 #define MALFORMED_MESSAGE_ERROR() { DebugF(MALFORMED_MSG ": %s, %s", \
49 error->name, error->message); \
53 struct connection_info
{
56 DBusConnection
*connection
;
60 reset_info(struct connection_info
*info
)
62 info
->connection
= NULL
;
63 info
->busname
[0] = '\0';
64 info
->busobject
[0] = '\0';
68 add_device(DBusMessage
* message
, DBusMessage
* reply
, DBusError
* error
)
70 DBusMessageIter iter
, reply_iter
, subiter
;
71 InputOption
*input_options
= NULL
;
73 DeviceIntPtr dev
= NULL
;
75 dbus_message_iter_init_append(reply
, &reply_iter
);
77 if (!dbus_message_iter_init(message
, &iter
)) {
78 ErrorF("[config/dbus] couldn't initialise iterator\n");
82 input_options
= input_option_new(input_options
, "_source", "client/dbus");
84 ErrorF("[config/dbus] couldn't allocate first key/value pair\n");
89 /* signature should be [ss][ss]... */
90 while (dbus_message_iter_get_arg_type(&iter
) == DBUS_TYPE_ARRAY
) {
93 dbus_message_iter_recurse(&iter
, &subiter
);
95 if (dbus_message_iter_get_arg_type(&subiter
) != DBUS_TYPE_STRING
)
98 dbus_message_iter_get_basic(&subiter
, &key
);
101 /* The _ prefix refers to internal settings, and may not be given by
104 ErrorF("[config/dbus] attempted subterfuge: option name %s given\n",
109 if (!dbus_message_iter_has_next(&subiter
))
111 dbus_message_iter_next(&subiter
);
112 if (dbus_message_iter_get_arg_type(&subiter
) != DBUS_TYPE_STRING
)
115 dbus_message_iter_get_basic(&subiter
, &value
);
119 input_options
= input_option_new(input_options
, key
, value
);
121 dbus_message_iter_next(&iter
);
124 ret
= NewInputDeviceRequest(input_options
, NULL
, &dev
);
125 if (ret
!= Success
) {
126 DebugF("[config/dbus] NewInputDeviceRequest failed\n");
131 DebugF("[config/dbus] NewInputDeviceRequest provided no device\n");
132 ret
= BadImplementation
;
136 /* XXX: If we fail halfway through, we don't seem to have any way to
137 * empty the iterator, so you'll end up with some device IDs,
138 * plus an error. This seems to be a shortcoming in the D-Bus
140 for (; dev
; dev
= dev
->next
) {
141 if (!dbus_message_iter_append_basic(&reply_iter
, DBUS_TYPE_INT32
,
143 ErrorF("[config/dbus] couldn't append to iterator\n");
150 if (ret
!= Success
) {
152 RemoveDevice(dev
, TRUE
);
155 dbus_message_iter_append_basic(&reply_iter
, DBUS_TYPE_INT32
, &err
);
158 input_option_free_list(&input_options
);
164 remove_device(DBusMessage
* message
, DBusMessage
* reply
, DBusError
* error
)
166 int deviceid
, ret
, err
;
168 DBusMessageIter iter
, reply_iter
;
170 dbus_message_iter_init_append(reply
, &reply_iter
);
172 if (!dbus_message_iter_init(message
, &iter
)) {
173 ErrorF("[config/dbus] failed to init iterator\n");
177 if (!dbus_message_get_args(message
, error
, DBUS_TYPE_UINT32
,
178 &deviceid
, DBUS_TYPE_INVALID
)) {
179 MALFORMED_MESSAGE_ERROR();
182 dixLookupDevice(&dev
, deviceid
, serverClient
, DixDestroyAccess
);
184 DebugF("[config/dbus] bogus device id %d given\n", deviceid
);
189 DebugF("[config/dbus] removing device %s (id %d)\n", dev
->name
, deviceid
);
191 /* Call PIE here so we don't try to dereference a device that's
192 * already been removed. */
194 ProcessInputEvents();
195 DeleteInputDeviceRequest(dev
);
201 err
= (ret
== Success
) ? ret
: -ret
;
202 dbus_message_iter_append_basic(&reply_iter
, DBUS_TYPE_INT32
, &err
);
208 list_devices(DBusMessage
* message
, DBusMessage
* reply
, DBusError
* error
)
211 DBusMessageIter iter
, subiter
;
213 dbus_message_iter_init_append(reply
, &iter
);
215 for (dev
= inputInfo
.devices
; dev
; dev
= dev
->next
) {
216 if (!dbus_message_iter_open_container(&iter
, DBUS_TYPE_STRUCT
, NULL
,
218 ErrorF("[config/dbus] couldn't init container\n");
221 if (!dbus_message_iter_append_basic(&subiter
, DBUS_TYPE_UINT32
,
223 ErrorF("[config/dbus] couldn't append to iterator\n");
226 if (!dbus_message_iter_append_basic(&subiter
, DBUS_TYPE_STRING
,
228 ErrorF("[config/dbus] couldn't append to iterator\n");
231 if (!dbus_message_iter_close_container(&iter
, &subiter
)) {
232 ErrorF("[config/dbus] couldn't close container\n");
241 get_version(DBusMessage
* message
, DBusMessage
* reply
, DBusError
* error
)
243 DBusMessageIter iter
;
244 unsigned int version
= API_VERSION
;
246 dbus_message_iter_init_append(reply
, &iter
);
247 if (!dbus_message_iter_append_basic(&iter
, DBUS_TYPE_UINT32
, &version
)) {
248 ErrorF("[config/dbus] couldn't append version\n");
255 static DBusHandlerResult
256 message_handler(DBusConnection
* connection
, DBusMessage
* message
, void *data
)
260 struct connection_info
*info
= data
;
262 /* ret is the overall D-Bus handler result, whereas err is the internal
263 * X error from our individual functions. */
264 int ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
267 DebugF("[config/dbus] received a message for %s\n",
268 dbus_message_get_interface(message
));
270 dbus_error_init(&error
);
272 reply
= dbus_message_new_method_return(message
);
274 ErrorF("[config/dbus] failed to create reply\n");
275 ret
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
279 if (strcmp(dbus_message_get_member(message
), "add") == 0)
280 err
= add_device(message
, reply
, &error
);
281 else if (strcmp(dbus_message_get_member(message
), "remove") == 0)
282 err
= remove_device(message
, reply
, &error
);
283 else if (strcmp(dbus_message_get_member(message
), "listDevices") == 0)
284 err
= list_devices(message
, reply
, &error
);
285 else if (strcmp(dbus_message_get_member(message
), "version") == 0)
286 err
= get_version(message
, reply
, &error
);
290 /* Failure to allocate is a special case. */
291 if (err
== BadAlloc
) {
292 ret
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
296 /* While failure here is always an OOM, we don't return that,
297 * since that would result in devices being double-added/removed. */
298 if (dbus_connection_send(info
->connection
, reply
, NULL
))
299 dbus_connection_flush(info
->connection
);
301 ErrorF("[config/dbus] failed to send reply\n");
303 ret
= DBUS_HANDLER_RESULT_HANDLED
;
306 dbus_message_unref(reply
);
308 dbus_error_free(&error
);
314 connect_hook(DBusConnection
* connection
, void *data
)
317 DBusObjectPathVTable vtable
= {.message_function
= message_handler
, };
318 struct connection_info
*info
= data
;
320 info
->connection
= connection
;
322 dbus_error_init(&error
);
324 dbus_bus_request_name(info
->connection
, info
->busname
, 0, &error
);
325 if (dbus_error_is_set(&error
)) {
326 ErrorF("[config/dbus] couldn't take over org.x.config: %s (%s)\n",
327 error
.name
, error
.message
);
331 /* blocks until we get a reply. */
332 dbus_bus_add_match(info
->connection
, MATCH_RULE
, &error
);
333 if (dbus_error_is_set(&error
)) {
334 ErrorF("[config/dbus] couldn't add match: %s (%s)\n", error
.name
,
339 if (!dbus_connection_register_object_path(info
->connection
,
340 info
->busobject
, &vtable
, info
)) {
341 ErrorF("[config/dbus] couldn't register object path\n");
345 DebugF("[dbus] registered %s, %s\n", info
->busname
, info
->busobject
);
347 dbus_error_free(&error
);
352 dbus_bus_remove_match(info
->connection
, MATCH_RULE
, &error
);
354 dbus_bus_release_name(info
->connection
, info
->busname
, &error
);
356 dbus_error_free(&error
);
362 disconnect_hook(void *data
)
368 pre_disconnect_hook(void)
372 dbus_error_init(&error
);
373 dbus_connection_unregister_object_path(connection_data
->connection
,
374 connection_data
->busobject
);
375 dbus_bus_remove_match(connection_data
->connection
, MATCH_RULE
, &error
);
376 dbus_bus_release_name(connection_data
->connection
,
377 connection_data
->busname
, &error
);
378 dbus_error_free(&error
);
382 static struct connection_info connection_data
;
384 static struct config_dbus_core_hook core_hook
= {
385 .connect
= connect_hook
,
386 .disconnect
= disconnect_hook
,
387 .data
= &connection_data
,
391 config_dbus_init(void)
393 snprintf(connection_data
.busname
, sizeof(connection_data
.busname
),
394 "org.x.config.display%d", atoi(display
));
395 snprintf(connection_data
.busobject
, sizeof(connection_data
.busobject
),
396 "/org/x/config/%d", atoi(display
));
398 return config_dbus_core_add_hook(&core_hook
);
402 config_dbus_fini(void)
404 config_dbus_core_remove_hook(&core_hook
);
405 connection_data
.busname
[0] = '\0';
406 connection_data
.busobject
[0] = '\0';