Reorganized obj/subdev functions
[xf86-input-tuio.git] / src / tuio.c
blobcaee553cf6368d38612bd43f2db86cbb0d70c708
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>
30 #include <X11/extensions/XInput2.h>
31 #include <xf86_OSlib.h>
33 #include "tuio.h"
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 /* InputInfoPtr for main tuio device */
40 static TuioDevicePtr g_pTuio;
41 static int pipefd[2] = {-1, -1};
43 /* Module Functions */
44 static pointer
45 TuioPlug(pointer, pointer, int *, int *);
47 static void
48 TuioUnplug(pointer);
50 /* Driver Functions */
51 static InputInfoPtr
52 TuioPreInit(InputDriverPtr, IDevPtr, int);
54 static void
55 TuioUnInit(InputDriverPtr, InputInfoPtr, int);
57 static void
58 TuioObjReadInput(InputInfoPtr pInfo);
60 static void
61 TuioReadInput(InputInfoPtr);
63 static int
64 TuioControl(DeviceIntPtr, int);
66 /* Internal Functions */
67 static int
68 _init_devices(InputInfoPtr pInfo, int num);
70 static int
71 _tuio_init_buttons(DeviceIntPtr device);
73 static int
74 _tuio_init_axes(DeviceIntPtr device);
76 static int
77 _tuio_lo_cur2d_handle(const char *path,
78 const char *types,
79 lo_arg **argv,
80 int argc,
81 void *data,
82 void *user_data);
84 static void
85 _free_tuiodev(TuioDevicePtr pTuio);
87 static void
88 _lo_error(int num,
89 const char *msg,
90 const char *path);
92 /* Object list manipulation functions */
93 static ObjectPtr
94 _object_get(ObjectPtr head, int id);
96 static ObjectPtr
97 _object_new(int id);
99 static void
100 _object_add(ObjectPtr *obj_list, ObjectPtr obj);
102 static ObjectPtr
103 _object_remove(ObjectPtr *obj_list, int id);
105 static void
106 _subdev_add(SubDevicePtr *subdev_list, SubDevicePtr subdev);
108 static SubDevicePtr
109 _subdev_remove(SubDevicePtr *subdev_list);
111 static SubDevicePtr
112 _subdev_remove_sd(SubDevicePtr *subdev_list, SubDevicePtr subdev);
114 /* Driver information */
115 static XF86ModuleVersionInfo TuioVersionRec =
117 "tuio",
118 MODULEVENDORSTRING,
119 MODINFOSTRING1,
120 MODINFOSTRING2,
121 XORG_VERSION_CURRENT,
122 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
123 ABI_CLASS_XINPUT,
124 ABI_XINPUT_VERSION,
125 MOD_CLASS_XINPUT,
126 {0, 0, 0, 0}
129 _X_EXPORT InputDriverRec TUIO =
132 "tuio",
133 NULL,
134 TuioPreInit,
135 TuioUnInit,
136 NULL,
140 _X_EXPORT XF86ModuleData tuioModuleData =
142 &TuioVersionRec,
143 TuioPlug,
144 TuioUnplug
147 static pointer
148 TuioPlug(pointer module,
149 pointer options,
150 int *errmaj,
151 int *errmin)
153 xf86AddInputDriver(&TUIO, module, 0);
154 return module;
158 * Unplug
160 static void
161 TuioUnplug(pointer p)
166 * Pre-initialization of new device
167 * This can be entered by either a "core" tuio device
168 * or an extension "Object" device that is used for routing individual object
169 * events through.
171 static InputInfoPtr
172 TuioPreInit(InputDriverPtr drv,
173 IDevPtr dev,
174 int flags)
176 InputInfoPtr pInfo;
177 TuioDevicePtr pTuio = NULL;
178 ObjectPtr obj;
179 SubDevicePtr subdev;
180 char *type;
181 int id;
182 int num_subdev, tuio_port;
184 if (!(pInfo = xf86AllocateInput(drv, 0)))
185 return NULL;
187 /* If Type == Object, this is a device for an object to use */
188 type = xf86CheckStrOption(dev->commonOptions, "Type", NULL);
190 if (type != NULL && strcmp(type, "Object") == 0) {
192 xf86Msg(X_INFO, "%s: Object device found\n", dev->identifier);
194 /* Allocate device storage and add to device list */
195 subdev = xcalloc(1, sizeof(SubDeviceRec));
196 subdev->pInfo = pInfo;
197 _subdev_add(&g_pTuio->subdev_list, subdev);
199 } else {
201 if (!(pTuio = xcalloc(1, sizeof(TuioDeviceRec)))) {
202 xf86DeleteInput(pInfo, 0);
203 return NULL;
205 g_pTuio = pTuio;
207 pInfo->private = pTuio;
209 /* Get the number of subdevices we need to create */
210 num_subdev = xf86CheckIntOption(dev->commonOptions, "SubDevices", 5);
211 if (num_subdev > MAX_SUBDEVICES) {
212 num_subdev = MAX_SUBDEVICES;
213 } else if (num_subdev < MIN_SUBDEVICES) {
214 num_subdev = MIN_SUBDEVICES;
216 pTuio->num_subdev = num_subdev;
218 /* Get the TUIO port number to use */
219 tuio_port = xf86CheckIntOption(dev->commonOptions, "Port", DEFAULT_PORT);
220 if (tuio_port < 0 || tuio_port > 65535) {
221 xf86Msg(X_INFO, "%s: Invalid port number (%i), defaulting to %i\n",
222 dev->identifier, tuio_port, DEFAULT_PORT);
223 tuio_port = DEFAULT_PORT;
225 pTuio->tuio_port = tuio_port;
228 /* Set up InputInfoPtr */
229 pInfo->name = xstrdup(dev->identifier);
230 pInfo->flags = 0;
231 pInfo->type_name = XI_TOUCHSCREEN; /* FIXME: Correct type? */
232 pInfo->conf_idev = dev;
233 pInfo->read_input = pTuio ? TuioReadInput : TuioObjReadInput; /* Set callback */
234 pInfo->device_control = TuioControl; /* Set callback */
235 pInfo->switch_mode = NULL;
237 /* Process common device options */
238 xf86CollectInputOptions(pInfo, NULL, NULL);
239 xf86ProcessCommonOptions(pInfo, pInfo->options);
241 pInfo->flags |= XI86_OPEN_ON_INIT;
242 pInfo->flags |= XI86_CONFIGURED;
244 return pInfo;
248 * Clean up
250 static void
251 TuioUnInit(InputDriverPtr drv,
252 InputInfoPtr pInfo,
253 int flags)
255 xf86DeleteInput(pInfo, 0);
259 * Empty callback for object device.
261 static void
262 TuioObjReadInput(InputInfoPtr pInfo) {
263 return;
267 * Handle new TUIO data on the socket
269 static void
270 TuioReadInput(InputInfoPtr pInfo)
272 TuioDevicePtr pTuio = pInfo->private;
273 ObjectPtr *obj_list = &pTuio->obj_list;
274 ObjectPtr obj = pTuio->obj_list;
275 SubDevicePtr *subdev_list = &pTuio->subdev_list;
276 ObjectPtr objtmp;
277 int valuators[2];
279 while(xf86WaitForInput(pInfo->fd, 0) > 0)
281 /* The liblo handler will set this flag if anything was processed */
282 pTuio->processed = 0;
284 /* liblo will receive a message and call the appropriate
285 * handlers (i.e. _tuio_lo_cur2d_hande()) */
286 lo_server_recv_noblock(pTuio->server, 0);
288 /* During the processing of the previous message/bundle,
289 * any "active" messages will be handled by flagging
290 * the listed object ids. Now that processing is done,
291 * remove any dead object ids and set any pending changes.
292 * Also check to make sure the processed data was newer than
293 * the last processed data */
294 if (pTuio->processed && pTuio->fseq_new > pTuio->fseq_old) {
296 while (obj != NULL) {
297 if (!obj->alive) {
298 if (obj->subdev) {
299 /* Post button "up" event */
300 xf86PostButtonEvent(obj->subdev->pInfo->dev, TRUE, 1, FALSE, 0, 0);
302 objtmp = obj->next;
303 obj = _object_remove(obj_list, obj->id);
304 _subdev_add(subdev_list, obj->subdev);
305 xfree(obj);
306 obj = objtmp;
308 } else {
309 if (obj->pending.set && obj->subdev) {
310 obj->x = obj->pending.x;
311 obj->y = obj->pending.y;
312 obj->pending.set = False;
314 if (obj->subdev) {
315 /* OKAY FOR NOW, MAYBE UPDATE */
316 /* TODO: Add more valuators with additional information */
317 valuators[0] = obj->x * 0x7FFFFFFF;
318 valuators[1] = obj->y * 0x7FFFFFFF;
320 xf86PostMotionEventP(obj->subdev->pInfo->dev,
321 TRUE, /* is_absolute */
322 0, /* first_valuator */
323 2, /* num_valuators */
324 valuators);
327 obj->alive = 0; /* Reset for next message */
328 obj = obj->next;
331 pTuio->fseq_old = pTuio->fseq_new;
337 * Handle device state changes
339 static int
340 TuioControl(DeviceIntPtr device,
341 int what)
343 InputInfoPtr pInfo = device->public.devicePrivate;
344 TuioDevicePtr pTuio = pInfo->private;
345 int res;
347 switch (what)
349 case DEVICE_INIT:
350 xf86Msg(X_INFO, "%s: Init\n", pInfo->name);
351 _tuio_init_buttons(device);
352 _tuio_init_axes(device);
354 /* If this is a "core" device, create object devices */
355 if (pTuio) {
356 _init_devices(pInfo, pTuio->num_subdev - 1);
358 break;
360 case DEVICE_ON: /* Open socket and start listening! */
361 xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
362 if (device->public.on)
363 break;
365 /* If this is an object device, use a dummy pipe */
366 if (!pTuio) {
367 if (pipefd[0] == -1) {
368 SYSCALL(res = pipe(pipefd));
369 if (res == -1) {
370 xf86Msg(X_ERROR, "%s: failed to open pipe\n",
371 pInfo->name);
372 return BadAlloc;
376 pInfo->fd = pipefd[0];
378 goto finish;
381 /* Setup server */
382 pTuio->server = lo_server_new_with_proto("3333", LO_UDP, _lo_error);
383 if (pTuio->server == NULL) {
384 xf86Msg(X_ERROR, "%s: Error allocating new lo_server\n",
385 pInfo->name);
386 return BadAlloc;
389 /* Register to receive all /tuio/2Dcur messages */
390 lo_server_add_method(pTuio->server, "/tuio/2Dcur", NULL,
391 _tuio_lo_cur2d_handle, pInfo);
393 pInfo->fd = lo_server_get_socket_fd(pTuio->server);
395 xf86FlushInput(pInfo->fd);
397 finish: xf86AddEnabledDevice(pInfo);
398 device->public.on = TRUE;
399 break;
401 case DEVICE_OFF:
402 xf86Msg(X_INFO, "%s: Off\n", pInfo->name);
403 if (!device->public.on)
404 break;
406 xf86RemoveEnabledDevice(pInfo);
408 if (pTuio) {
409 lo_server_free(pTuio->server);
410 pInfo->fd = -1;
413 device->public.on = FALSE;
414 break;
416 case DEVICE_CLOSE:
417 xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
418 break;
421 return Success;
425 * Initialize the device properties
426 * TODO
428 TuioPropertyInit() {
432 * Free a TuioDeviceRec
434 static void
435 _free_tuiodev(TuioDevicePtr pTuio) {
436 ObjectPtr obj = pTuio->obj_list;
437 ObjectPtr tmp;
439 while (obj != NULL) {
440 tmp = obj->next;
441 xfree(obj);
442 obj = tmp;
445 xfree(pTuio);
449 * Handles OSC messages in the /tuio/2Dcur address space
451 static int
452 _tuio_lo_cur2d_handle(const char *path,
453 const char *types,
454 lo_arg **argv,
455 int argc,
456 void *data,
457 void *user_data) {
458 InputInfoPtr pInfo = user_data;
459 TuioDevicePtr pTuio = pInfo->private;
460 ObjectPtr *obj_list = &pTuio->obj_list;
461 ObjectPtr obj, objtemp;
462 char *act;
463 int i;
465 if (argc == 0) {
466 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (argc == 0)\n",
467 pInfo->name);
468 return 0;
469 } else if(*types != 's') {
470 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (types[0] != 's')\n",
471 pInfo->name);
472 return 0;
475 /* Flag as being processed, used in TuioReadInput() */
476 pTuio->processed = 1;
478 /* Parse message type */
479 /* Set message type: */
480 if (strcmp((char *)argv[0], "set") == 0) {
482 /* Simple type check */
483 if (strcmp(types, "sifffff")) {
484 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d set msg (types == %s)\n",
485 pInfo->name, types);
486 return 0;
489 obj = _object_get(*obj_list, argv[1]->i);
491 /* If not found, create a new object */
492 if (obj == NULL) {
493 obj = _object_new(argv[1]->i);
494 _object_add(obj_list, obj);
495 obj->subdev = _subdev_remove(&pTuio->subdev_list);
496 if (obj->subdev)
497 xf86PostButtonEvent(obj->subdev->pInfo->dev, TRUE, 1, TRUE, 0, 0);
500 obj->pending.x = argv[2]->f;
501 obj->pending.y = argv[3]->f;
502 obj->pending.set = True;
504 } else if (strcmp((char *)argv[0], "alive") == 0) {
506 /* Mark all objects that are still alive */
507 obj = *obj_list;
508 while (obj != NULL) {
509 for (i=1; i<argc; i++) {
510 if (argv[i]->i == obj->id) {
511 obj->alive = True;
512 break;
515 obj = obj->next;
518 } else if (strcmp((char *)argv[0], "fseq") == 0) {
519 /* Simple type check */
520 if (strcmp(types, "si")) {
521 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d fseq msg (types == %s)\n",
522 pInfo->name, types);
523 return 0;
525 pTuio->fseq_new = argv[1]->i;
528 return 0;
532 * liblo error handler
534 static void
535 _lo_error(int num,
536 const char *msg,
537 const char *path)
539 xf86Msg(X_ERROR, "liblo: %s\n", msg);
543 * Retrieves an object from a list based on its id.
545 * @return NULL if not found.
547 static ObjectPtr
548 _object_get(ObjectPtr head, int id) {
549 ObjectPtr obj = head;
551 while (obj != NULL && obj->id != id) {
552 obj = obj->next;
555 return obj;
559 * Allocates and inserts a new object at the beginning of a list.
560 * Pointer to the new object is returned.
561 * Doesn't check for duplicate ids, so call _object_get() beforehand
562 * to make sure it doesn't exist already!!
564 * @return ptr to newly inserted object
566 static ObjectPtr
567 _object_new(int id) {
568 ObjectPtr new_obj = xcalloc(1, sizeof(ObjectRec));
570 new_obj->id = id;
571 new_obj->alive = True;
573 return new_obj;
577 * Adds a SubDevice to the beginning of the subdev_list list
579 static void
580 _object_add(ObjectPtr *obj_list, ObjectPtr obj) {
581 if (obj_list == NULL || obj == NULL)
582 return;
584 if (*obj_list != NULL) {
585 obj->next = *obj_list;
587 *obj_list = obj;
592 * Removes an Object with a specific id from a list.
594 static ObjectPtr
595 _object_remove(ObjectPtr *obj_list, int id) {
596 ObjectPtr obj = *obj_list;
597 ObjectPtr objtmp;
599 if (obj == NULL) return; /* Empty list */
601 if (obj->id == id) { /* Remove from head */
602 *obj_list = obj->next;
603 } else {
604 while (obj->next != NULL) {
605 if (obj->next->id == id) {
606 objtmp = obj->next;
607 obj->next = objtmp->next;
608 obj = objtmp;
609 obj->next = NULL;
610 return obj;
612 obj = obj->next;
614 obj = NULL;
617 return obj;
621 * Adds a SubDevice to the beginning of the subdev_list list
623 static void
624 _subdev_add(SubDevicePtr *subdev_list, SubDevicePtr subdev) {
625 if (subdev_list == NULL || subdev == NULL)
626 return;
628 if (*subdev_list != NULL) {
629 subdev->next = *subdev_list;
631 *subdev_list = subdev;
635 * Removes a SubDevice from the subdev_list list
637 static SubDevicePtr
638 _subdev_remove(SubDevicePtr *subdev_list) {
639 SubDevicePtr subdev;
641 if (subdev_list == NULL || *subdev_list == NULL)
642 return NULL;
644 subdev = *subdev_list;
645 *subdev_list = subdev->next;
646 subdev->next = NULL;
648 return subdev;
652 * Init the button map device. We only use one button.
654 static int
655 _tuio_init_buttons(DeviceIntPtr device)
657 InputInfoPtr pInfo = device->public.devicePrivate;
658 CARD8 *map;
659 Atom *labels;
660 int numbuttons = 4;
661 int ret = Success;
662 int i;
664 map = xcalloc(numbuttons, sizeof(CARD8));
665 labels = xcalloc(1, sizeof(Atom));
666 for (i=0; i<numbuttons; i++)
667 map[i] = i;
669 if (!InitButtonClassDeviceStruct(device, numbuttons /* 1 button */,
670 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
671 labels,
672 #endif
673 map)) {
674 xf86Msg(X_ERROR, "%s: Failed to register buttons.\n", pInfo->name);
675 ret = BadAlloc;
678 xfree(labels);
679 return ret;
683 * Init valuators for device, use x/y coordinates.
685 static int
686 _tuio_init_axes(DeviceIntPtr device)
688 InputInfoPtr pInfo = device->public.devicePrivate;
689 int i;
690 const int num_axes = 2;
691 Atom *atoms;
693 atoms = xcalloc(num_axes, sizeof(Atom));
695 if (!InitValuatorClassDeviceStruct(device,
696 num_axes,
697 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
698 atoms,
699 #endif
700 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
701 GetMotionHistory,
702 #endif
703 GetMotionHistorySize(),
705 return BadAlloc;
707 /* Use absolute mode. Currently, TUIO coords are mapped to the
708 * full screen area */
709 pInfo->dev->valuator->mode = Absolute;
710 if (!InitAbsoluteClassDeviceStruct(device))
711 return BadAlloc;
713 for (i = 0; i < num_axes; i++)
715 xf86InitValuatorAxisStruct(device, i,
716 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
717 atoms[i],
718 #endif
719 0, 0x7FFFFFFF, 1, 1, 1);
720 xf86InitValuatorDefaults(device, i);
722 return Success;
726 * New device creation through hal
727 * I referenced the wacom hal-setup patch while writing this:
728 * http://cvs.fedoraproject.org/viewvc/rpms/linuxwacom/devel/linuxwacom-0.8.2.2-hal-setup.patch?revision=1.1&view=markup
730 * @return 0 if successful, 1 if failure
732 static int
733 _init_devices(InputInfoPtr pInfo, int num) {
734 DBusError error;
735 DBusConnection *conn;
736 LibHalContext *ctx;
737 char *newdev;
738 char *name;
739 int i;
741 /* We need a new device to send motion/button events through.
742 * There isn't a great way to do this right now without native
743 * blob events, so just hack it out for now. Woot. */
745 /* Open connection to dbus and create contex */
746 dbus_error_init(&error);
747 if ((conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) {
748 xf86Msg(X_ERROR, "%s: Failed to open dbus connection: %s\n",
749 pInfo->name, error.message);
750 return 1;
752 if ((ctx = libhal_ctx_new()) == NULL) {
753 xf86Msg(X_ERROR, "%s: Failed to obtain hal context: %s\n",
754 pInfo->name, error.message);
755 return 1;
758 dbus_error_init(&error);
759 libhal_ctx_set_dbus_connection(ctx, conn);
760 if (!libhal_ctx_init(ctx, &error)) {
761 xf86Msg(X_ERROR, "%s: Failed to initialize hal context: %s\n",
762 pInfo->name, error.message);
763 return 1;
766 /* Create new devices through hal */
767 for (i=0; i<num; i++) {
769 dbus_error_init(&error);
770 newdev = libhal_new_device(ctx, &error);
771 if (dbus_error_is_set(&error) == TRUE) {
772 xf86Msg(X_ERROR, "%s: Failed to create input device: %s\n",
773 pInfo->name, error.message);
774 return 1;
777 dbus_error_init(&error);
778 libhal_device_set_property_string(ctx, newdev, "input.device",
779 "blob", &error);
780 if (dbus_error_is_set(&error) == TRUE) {
781 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
782 pInfo->name, error.message);
783 return 1;
786 dbus_error_init(&error);
787 libhal_device_set_property_string(ctx, newdev,
788 "input.x11_driver", "tuio",
789 &error);
790 if (dbus_error_is_set(&error) == TRUE) {
791 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
792 pInfo->name, error.message);
793 return 1;
796 /* Set "Type" property. This will be used in TuioPreInit to determine
797 * whether the new device is an object device or not */
798 dbus_error_init(&error);
799 libhal_device_set_property_string(ctx, newdev,
800 "input.x11_options.Type",
801 "Object", &error);
802 if (dbus_error_is_set(&error) == TRUE) {
803 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
804 pInfo->name, error.message);
805 return 1;
808 asprintf(&name, "%s subdev %i", pInfo->name, i);
810 /* Set name */
811 dbus_error_init(&error);
812 libhal_device_set_property_string(ctx, newdev,
813 "info.product", name,
814 &error);
815 if (dbus_error_is_set(&error) == TRUE) {
816 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
817 pInfo->name, error.message);
818 return 1;
821 /* Finalize creation of new device */
822 dbus_error_init(&error);
823 libhal_device_commit_to_gdl(ctx, newdev, "/org/freedesktop/Hal/devices/tuio_subdev", &error);
824 if (dbus_error_is_set (&error) == TRUE) {
825 xf86Msg(X_ERROR, "%s: Failed to add input device: %s\n",
826 pInfo->name, error.message);
827 return 1;
830 xfree(name);
833 //libhal_context_free(ctx);
835 return 0;