Added 'FseqCheck' Option
[xf86-input-tuio.git] / src / tuio.c
blobb212749fdd076efb2138ec3272eda6dbde347683
1 /*
2 * Copyright (c) 2009 Ryan Huffman
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
22 * Authors:
23 * Ryan Huffman (ryanhuffman@gmail.com)
26 #include <unistd.h>
28 #include <xf86Xinput.h>
29 #include <X11/extensions/XIproto.h>
31 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
32 #include <X11/extensions/XInput2.h>
33 #endif
35 #include <xf86_OSlib.h>
37 #include "tuio.h"
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
43 /* InputInfoPtr for main tuio device */
44 static TuioDevicePtr g_pTuio;
45 static int pipefd[2] = {-1, -1};
47 /* Module Functions */
48 static pointer
49 TuioPlug(pointer, pointer, int *, int *);
51 static void
52 TuioUnplug(pointer);
54 /* Driver Functions */
55 static InputInfoPtr
56 TuioPreInit(InputDriverPtr, IDevPtr, int);
58 static void
59 TuioUnInit(InputDriverPtr, InputInfoPtr, int);
61 static void
62 TuioObjReadInput(InputInfoPtr pInfo);
64 static void
65 TuioReadInput(InputInfoPtr);
67 static int
68 TuioControl(DeviceIntPtr, int);
70 /* Internal Functions */
71 static int
72 _init_devices(InputInfoPtr pInfo, int num);
74 static int
75 _tuio_init_buttons(DeviceIntPtr device);
77 static int
78 _tuio_init_axes(DeviceIntPtr device);
80 static int
81 _tuio_lo_cur2d_handle(const char *path,
82 const char *types,
83 lo_arg **argv,
84 int argc,
85 void *data,
86 void *user_data);
88 static void
89 _free_tuiodev(TuioDevicePtr pTuio);
91 static void
92 _lo_error(int num,
93 const char *msg,
94 const char *path);
96 /* Object list manipulation functions */
97 static ObjectPtr
98 _object_get(ObjectPtr head, int id);
100 static ObjectPtr
101 _object_new(int id);
103 static void
104 _object_add(ObjectPtr *obj_list, ObjectPtr obj);
106 static ObjectPtr
107 _object_remove(ObjectPtr *obj_list, int id);
109 static void
110 _subdev_add(SubDevicePtr *subdev_list, SubDevicePtr subdev);
112 static SubDevicePtr
113 _subdev_remove(SubDevicePtr *subdev_list);
115 static SubDevicePtr
116 _subdev_remove_sd(SubDevicePtr *subdev_list, SubDevicePtr subdev);
118 /* Driver information */
119 static XF86ModuleVersionInfo TuioVersionRec =
121 "tuio",
122 MODULEVENDORSTRING,
123 MODINFOSTRING1,
124 MODINFOSTRING2,
125 XORG_VERSION_CURRENT,
126 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
127 ABI_CLASS_XINPUT,
128 ABI_XINPUT_VERSION,
129 MOD_CLASS_XINPUT,
130 {0, 0, 0, 0}
133 _X_EXPORT InputDriverRec TUIO =
136 "tuio",
137 NULL,
138 TuioPreInit,
139 TuioUnInit,
140 NULL,
144 _X_EXPORT XF86ModuleData tuioModuleData =
146 &TuioVersionRec,
147 TuioPlug,
148 TuioUnplug
151 static pointer
152 TuioPlug(pointer module,
153 pointer options,
154 int *errmaj,
155 int *errmin)
157 xf86AddInputDriver(&TUIO, module, 0);
158 return module;
162 * Unplug
164 static void
165 TuioUnplug(pointer p)
170 * Pre-initialization of new device
171 * This can be entered by either a "core" tuio device
172 * or an extension "Object" device that is used for routing individual object
173 * events through.
175 static InputInfoPtr
176 TuioPreInit(InputDriverPtr drv,
177 IDevPtr dev,
178 int flags)
180 InputInfoPtr pInfo;
181 TuioDevicePtr pTuio = NULL;
182 ObjectPtr obj;
183 SubDevicePtr subdev;
184 char *type;
185 int id;
186 int num_subdev, tuio_port;
188 if (!(pInfo = xf86AllocateInput(drv, 0)))
189 return NULL;
191 /* If Type == Object, this is a device for an object to use */
192 type = xf86CheckStrOption(dev->commonOptions, "Type", NULL);
194 if (type != NULL && strcmp(type, "Object") == 0) {
196 xf86Msg(X_INFO, "%s: Object device found\n", dev->identifier);
198 /* Allocate device storage and add to device list */
199 subdev = xcalloc(1, sizeof(SubDeviceRec));
200 subdev->pInfo = pInfo;
201 _subdev_add(&g_pTuio->subdev_list, subdev);
203 } else {
205 if (!(pTuio = xcalloc(1, sizeof(TuioDeviceRec)))) {
206 xf86DeleteInput(pInfo, 0);
207 return NULL;
209 g_pTuio = pTuio;
211 pInfo->private = pTuio;
213 /* Get the number of subdevices we need to create */
214 num_subdev = xf86CheckIntOption(dev->commonOptions, "SubDevices", 5);
215 if (num_subdev > MAX_SUBDEVICES) {
216 num_subdev = MAX_SUBDEVICES;
217 } else if (num_subdev < MIN_SUBDEVICES) {
218 num_subdev = MIN_SUBDEVICES;
220 pTuio->num_subdev = num_subdev;
222 /* Get the TUIO port number to use */
223 tuio_port = xf86CheckIntOption(dev->commonOptions, "Port", DEFAULT_PORT);
224 if (tuio_port < 0 || tuio_port > 65535) {
225 xf86Msg(X_INFO, "%s: Invalid port number (%i), defaulting to %i\n",
226 dev->identifier, tuio_port, DEFAULT_PORT);
227 tuio_port = DEFAULT_PORT;
229 pTuio->tuio_port = tuio_port;
231 /* Get setting for checking fseq numbers in TUIO packets */
232 pTuio->check_fseq= xf86CheckBoolOption(dev->commonOptions, "CheckFseq", True);
233 if (!pTuio->check_fseq) {
234 xf86Msg(X_INFO, "%s: CheckFseq set to False\n",
235 dev->identifier);
239 /* Set up InputInfoPtr */
240 pInfo->name = xstrdup(dev->identifier);
241 pInfo->flags = 0;
242 pInfo->type_name = XI_TOUCHSCREEN; /* FIXME: Correct type? */
243 pInfo->conf_idev = dev;
244 pInfo->read_input = pTuio ? TuioReadInput : TuioObjReadInput; /* Set callback */
245 pInfo->device_control = TuioControl; /* Set callback */
246 pInfo->switch_mode = NULL;
248 /* Process common device options */
249 xf86CollectInputOptions(pInfo, NULL, NULL);
250 xf86ProcessCommonOptions(pInfo, pInfo->options);
252 pInfo->flags |= XI86_OPEN_ON_INIT;
253 pInfo->flags |= XI86_CONFIGURED;
255 return pInfo;
259 * Clean up
261 static void
262 TuioUnInit(InputDriverPtr drv,
263 InputInfoPtr pInfo,
264 int flags)
266 xf86DeleteInput(pInfo, 0);
270 * Empty callback for object device.
272 static void
273 TuioObjReadInput(InputInfoPtr pInfo) {
274 return;
278 * Handle new TUIO data on the socket
280 static void
281 TuioReadInput(InputInfoPtr pInfo)
283 TuioDevicePtr pTuio = pInfo->private;
284 ObjectPtr *obj_list = &pTuio->obj_list;
285 ObjectPtr obj = pTuio->obj_list;
286 SubDevicePtr *subdev_list = &pTuio->subdev_list;
287 ObjectPtr objtmp;
288 int valuators[2];
290 while(xf86WaitForInput(pInfo->fd, 0) > 0)
292 /* The liblo handler will set this flag if anything was processed */
293 pTuio->processed = 0;
295 /* liblo will receive a message and call the appropriate
296 * handlers (i.e. _tuio_lo_cur2d_hande()) */
297 lo_server_recv_noblock(pTuio->server, 0);
299 /* During the processing of the previous message/bundle,
300 * any "active" messages will be handled by flagging
301 * the listed object ids. Now that processing is done,
302 * remove any dead object ids and set any pending changes.
303 * Also check to make sure the processed data was newer than
304 * the last processed data */
305 if (pTuio->processed &&
306 (pTuio->fseq_new > pTuio->fseq_old || !pTuio->check_fseq)) {
308 while (obj != NULL) {
309 if (!obj->alive) {
310 if (obj->subdev) {
311 /* Post button "up" event */
312 xf86PostButtonEvent(obj->subdev->pInfo->dev, TRUE, 1, FALSE, 0, 0);
314 objtmp = obj->next;
315 obj = _object_remove(obj_list, obj->id);
316 _subdev_add(subdev_list, obj->subdev);
317 xfree(obj);
318 obj = objtmp;
320 } else {
321 if (obj->pending.set && obj->subdev) {
322 obj->x = obj->pending.x;
323 obj->y = obj->pending.y;
324 obj->pending.set = False;
326 if (obj->subdev) {
327 /* OKAY FOR NOW, MAYBE UPDATE */
328 /* TODO: Add more valuators with additional information */
329 valuators[0] = obj->x * 0x7FFFFFFF;
330 valuators[1] = obj->y * 0x7FFFFFFF;
332 xf86PostMotionEventP(obj->subdev->pInfo->dev,
333 TRUE, /* is_absolute */
334 0, /* first_valuator */
335 2, /* num_valuators */
336 valuators);
339 obj->alive = 0; /* Reset for next message */
340 obj = obj->next;
343 pTuio->fseq_old = pTuio->fseq_new;
349 * Handle device state changes
351 static int
352 TuioControl(DeviceIntPtr device,
353 int what)
355 InputInfoPtr pInfo = device->public.devicePrivate;
356 TuioDevicePtr pTuio = pInfo->private;
357 char *tuio_port;
358 int res;
360 switch (what)
362 case DEVICE_INIT:
363 xf86Msg(X_INFO, "%s: Init\n", pInfo->name);
364 _tuio_init_buttons(device);
365 _tuio_init_axes(device);
367 /* If this is a "core" device, create object devices */
368 if (pTuio) {
369 _init_devices(pInfo, pTuio->num_subdev - 1);
371 break;
373 case DEVICE_ON: /* Open socket and start listening! */
374 xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
375 if (device->public.on)
376 break;
378 /* If this is an object device, use a dummy pipe */
379 if (!pTuio) {
380 if (pipefd[0] == -1) {
381 SYSCALL(res = pipe(pipefd));
382 if (res == -1) {
383 xf86Msg(X_ERROR, "%s: failed to open pipe\n",
384 pInfo->name);
385 return BadAlloc;
389 pInfo->fd = pipefd[0];
391 goto finish;
394 /* Setup server */
395 asprintf(&tuio_port, "%i", pTuio->tuio_port);
396 pTuio->server = lo_server_new_with_proto(tuio_port, LO_UDP, _lo_error);
397 if (pTuio->server == NULL) {
398 xf86Msg(X_ERROR, "%s: Error allocating new lo_server\n",
399 pInfo->name);
400 return BadAlloc;
403 /* Register to receive all /tuio/2Dcur messages */
404 lo_server_add_method(pTuio->server, "/tuio/2Dcur", NULL,
405 _tuio_lo_cur2d_handle, pInfo);
407 pInfo->fd = lo_server_get_socket_fd(pTuio->server);
409 xf86FlushInput(pInfo->fd);
411 finish: xf86AddEnabledDevice(pInfo);
412 device->public.on = TRUE;
413 break;
415 case DEVICE_OFF:
416 xf86Msg(X_INFO, "%s: Off\n", pInfo->name);
417 if (!device->public.on)
418 break;
420 xf86RemoveEnabledDevice(pInfo);
422 if (pTuio) {
423 lo_server_free(pTuio->server);
424 pInfo->fd = -1;
427 device->public.on = FALSE;
428 break;
430 case DEVICE_CLOSE:
431 xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
432 break;
435 return Success;
439 * Initialize the device properties
440 * TODO
442 TuioPropertyInit() {
446 * Free a TuioDeviceRec
448 static void
449 _free_tuiodev(TuioDevicePtr pTuio) {
450 ObjectPtr obj = pTuio->obj_list;
451 ObjectPtr tmp;
453 while (obj != NULL) {
454 tmp = obj->next;
455 xfree(obj);
456 obj = tmp;
459 xfree(pTuio);
463 * Handles OSC messages in the /tuio/2Dcur address space
465 static int
466 _tuio_lo_cur2d_handle(const char *path,
467 const char *types,
468 lo_arg **argv,
469 int argc,
470 void *data,
471 void *user_data) {
472 InputInfoPtr pInfo = user_data;
473 TuioDevicePtr pTuio = pInfo->private;
474 ObjectPtr *obj_list = &pTuio->obj_list;
475 ObjectPtr obj, objtemp;
476 char *act;
477 int i;
479 if (argc == 0) {
480 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (argc == 0)\n",
481 pInfo->name);
482 return 0;
483 } else if(*types != 's') {
484 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (types[0] != 's')\n",
485 pInfo->name);
486 return 0;
489 /* Flag as being processed, used in TuioReadInput() */
490 pTuio->processed = 1;
492 /* Parse message type */
493 /* Set message type: */
494 if (strcmp((char *)argv[0], "set") == 0) {
496 /* Simple type check */
497 if (strcmp(types, "sifffff")) {
498 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d set msg (types == %s)\n",
499 pInfo->name, types);
500 return 0;
503 obj = _object_get(*obj_list, argv[1]->i);
505 /* If not found, create a new object */
506 if (obj == NULL) {
507 obj = _object_new(argv[1]->i);
508 _object_add(obj_list, obj);
509 obj->subdev = _subdev_remove(&pTuio->subdev_list);
510 if (obj->subdev)
511 xf86PostButtonEvent(obj->subdev->pInfo->dev, TRUE, 1, TRUE, 0, 0);
514 obj->pending.x = argv[2]->f;
515 obj->pending.y = argv[3]->f;
516 obj->pending.set = True;
518 } else if (strcmp((char *)argv[0], "alive") == 0) {
520 /* Mark all objects that are still alive */
521 obj = *obj_list;
522 while (obj != NULL) {
523 for (i=1; i<argc; i++) {
524 if (argv[i]->i == obj->id) {
525 obj->alive = True;
526 break;
529 obj = obj->next;
532 } else if (strcmp((char *)argv[0], "fseq") == 0) {
533 /* Simple type check */
534 if (strcmp(types, "si")) {
535 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d fseq msg (types == %s)\n",
536 pInfo->name, types);
537 return 0;
539 pTuio->fseq_new = argv[1]->i;
542 return 0;
546 * liblo error handler
548 static void
549 _lo_error(int num,
550 const char *msg,
551 const char *path)
553 xf86Msg(X_ERROR, "liblo: %s\n", msg);
557 * Retrieves an object from a list based on its id.
559 * @return NULL if not found.
561 static ObjectPtr
562 _object_get(ObjectPtr head, int id) {
563 ObjectPtr obj = head;
565 while (obj != NULL && obj->id != id) {
566 obj = obj->next;
569 return obj;
573 * Allocates and inserts a new object at the beginning of a list.
574 * Pointer to the new object is returned.
575 * Doesn't check for duplicate ids, so call _object_get() beforehand
576 * to make sure it doesn't exist already!!
578 * @return ptr to newly inserted object
580 static ObjectPtr
581 _object_new(int id) {
582 ObjectPtr new_obj = xcalloc(1, sizeof(ObjectRec));
584 new_obj->id = id;
585 new_obj->alive = True;
587 return new_obj;
591 * Adds a SubDevice to the beginning of the subdev_list list
593 static void
594 _object_add(ObjectPtr *obj_list, ObjectPtr obj) {
595 if (obj_list == NULL || obj == NULL)
596 return;
598 if (*obj_list != NULL) {
599 obj->next = *obj_list;
601 *obj_list = obj;
606 * Removes an Object with a specific id from a list.
608 static ObjectPtr
609 _object_remove(ObjectPtr *obj_list, int id) {
610 ObjectPtr obj = *obj_list;
611 ObjectPtr objtmp;
613 if (obj == NULL) return; /* Empty list */
615 if (obj->id == id) { /* Remove from head */
616 *obj_list = obj->next;
617 } else {
618 while (obj->next != NULL) {
619 if (obj->next->id == id) {
620 objtmp = obj->next;
621 obj->next = objtmp->next;
622 obj = objtmp;
623 obj->next = NULL;
624 return obj;
626 obj = obj->next;
628 obj = NULL;
631 return obj;
635 * Adds a SubDevice to the beginning of the subdev_list list
637 static void
638 _subdev_add(SubDevicePtr *subdev_list, SubDevicePtr subdev) {
639 if (subdev_list == NULL || subdev == NULL)
640 return;
642 if (*subdev_list != NULL) {
643 subdev->next = *subdev_list;
645 *subdev_list = subdev;
649 * Removes a SubDevice from the subdev_list list
651 static SubDevicePtr
652 _subdev_remove(SubDevicePtr *subdev_list) {
653 SubDevicePtr subdev;
655 if (subdev_list == NULL || *subdev_list == NULL)
656 return NULL;
658 subdev = *subdev_list;
659 *subdev_list = subdev->next;
660 subdev->next = NULL;
662 return subdev;
666 * Init the button map device. We only use one button.
668 static int
669 _tuio_init_buttons(DeviceIntPtr device)
671 InputInfoPtr pInfo = device->public.devicePrivate;
672 CARD8 *map;
673 Atom *labels;
674 int numbuttons = 4;
675 int ret = Success;
676 int i;
678 map = xcalloc(numbuttons, sizeof(CARD8));
679 labels = xcalloc(1, sizeof(Atom));
680 for (i=0; i<numbuttons; i++)
681 map[i] = i;
683 if (!InitButtonClassDeviceStruct(device, numbuttons /* 1 button */,
684 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
685 labels,
686 #endif
687 map)) {
688 xf86Msg(X_ERROR, "%s: Failed to register buttons.\n", pInfo->name);
689 ret = BadAlloc;
692 xfree(labels);
693 return ret;
697 * Init valuators for device, use x/y coordinates.
699 static int
700 _tuio_init_axes(DeviceIntPtr device)
702 InputInfoPtr pInfo = device->public.devicePrivate;
703 int i;
704 const int num_axes = 2;
705 Atom *atoms;
707 atoms = xcalloc(num_axes, sizeof(Atom));
709 if (!InitValuatorClassDeviceStruct(device,
710 num_axes,
711 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
712 atoms,
713 #endif
714 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
715 GetMotionHistory,
716 #endif
717 GetMotionHistorySize(),
719 return BadAlloc;
721 /* Use absolute mode. Currently, TUIO coords are mapped to the
722 * full screen area */
723 pInfo->dev->valuator->mode = Absolute;
724 if (!InitAbsoluteClassDeviceStruct(device))
725 return BadAlloc;
727 for (i = 0; i < num_axes; i++)
729 xf86InitValuatorAxisStruct(device, i,
730 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
731 atoms[i],
732 #endif
733 0, 0x7FFFFFFF, 1, 1, 1);
734 xf86InitValuatorDefaults(device, i);
736 return Success;
740 * New device creation through hal
741 * I referenced the wacom hal-setup patch while writing this:
742 * http://cvs.fedoraproject.org/viewvc/rpms/linuxwacom/devel/linuxwacom-0.8.2.2-hal-setup.patch?revision=1.1&view=markup
744 * @return 0 if successful, 1 if failure
746 static int
747 _init_devices(InputInfoPtr pInfo, int num) {
748 DBusError error;
749 DBusConnection *conn;
750 LibHalContext *ctx;
751 char *newdev;
752 char *name;
753 int i;
755 /* We need a new device to send motion/button events through.
756 * There isn't a great way to do this right now without native
757 * blob events, so just hack it out for now. Woot. */
759 /* Open connection to dbus and create contex */
760 dbus_error_init(&error);
761 if ((conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) {
762 xf86Msg(X_ERROR, "%s: Failed to open dbus connection: %s\n",
763 pInfo->name, error.message);
764 return 1;
766 if ((ctx = libhal_ctx_new()) == NULL) {
767 xf86Msg(X_ERROR, "%s: Failed to obtain hal context: %s\n",
768 pInfo->name, error.message);
769 return 1;
772 dbus_error_init(&error);
773 libhal_ctx_set_dbus_connection(ctx, conn);
774 if (!libhal_ctx_init(ctx, &error)) {
775 xf86Msg(X_ERROR, "%s: Failed to initialize hal context: %s\n",
776 pInfo->name, error.message);
777 return 1;
780 /* Create new devices through hal */
781 for (i=0; i<num; i++) {
783 dbus_error_init(&error);
784 newdev = libhal_new_device(ctx, &error);
785 if (dbus_error_is_set(&error) == TRUE) {
786 xf86Msg(X_ERROR, "%s: Failed to create input device: %s\n",
787 pInfo->name, error.message);
788 return 1;
791 dbus_error_init(&error);
792 libhal_device_set_property_string(ctx, newdev, "input.device",
793 "blob", &error);
794 if (dbus_error_is_set(&error) == TRUE) {
795 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
796 pInfo->name, error.message);
797 return 1;
800 dbus_error_init(&error);
801 libhal_device_set_property_string(ctx, newdev,
802 "input.x11_driver", "tuio",
803 &error);
804 if (dbus_error_is_set(&error) == TRUE) {
805 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
806 pInfo->name, error.message);
807 return 1;
810 /* Set "Type" property. This will be used in TuioPreInit to determine
811 * whether the new device is an object device or not */
812 dbus_error_init(&error);
813 libhal_device_set_property_string(ctx, newdev,
814 "input.x11_options.Type",
815 "Object", &error);
816 if (dbus_error_is_set(&error) == TRUE) {
817 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
818 pInfo->name, error.message);
819 return 1;
822 asprintf(&name, "%s subdev %i", pInfo->name, i);
824 /* Set name */
825 dbus_error_init(&error);
826 libhal_device_set_property_string(ctx, newdev,
827 "info.product", name,
828 &error);
829 if (dbus_error_is_set(&error) == TRUE) {
830 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
831 pInfo->name, error.message);
832 return 1;
835 /* Finalize creation of new device */
836 dbus_error_init(&error);
837 libhal_device_commit_to_gdl(ctx, newdev, "/org/freedesktop/Hal/devices/tuio_subdev", &error);
838 if (dbus_error_is_set (&error) == TRUE) {
839 xf86Msg(X_ERROR, "%s: Failed to add input device: %s\n",
840 pInfo->name, error.message);
841 return 1;
844 xfree(name);
847 //libhal_context_free(ctx);
849 return 0;