Now uses static number of cursors and works. Needs clean up.
[xf86-input-tuio.git] / src / tuio.c
blob744cc63505eaa7f1dfa4317be66ca87d800337c6
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;
42 /* Module Functions */
43 static pointer
44 TuioPlug(pointer, pointer, int *, int *);
46 static void
47 TuioUnplug(pointer);
49 /* Driver Functions */
50 static InputInfoPtr
51 TuioPreInit(InputDriverPtr, IDevPtr, int);
53 static void
54 TuioUnInit(InputDriverPtr, InputInfoPtr, int);
56 static void
57 TuioObjReadInput(InputInfoPtr pInfo);
59 static void
60 TuioReadInput(InputInfoPtr);
62 static int
63 TuioControl(DeviceIntPtr, int);
65 /* Internal Functions */
66 static int
67 _init_devices(InputInfoPtr pInfo, int num);
69 static int
70 _tuio_init_buttons(DeviceIntPtr device);
72 static int
73 _tuio_init_axes(DeviceIntPtr device);
75 static int
76 _tuio_lo_cur2d_handle(const char *path,
77 const char *types,
78 lo_arg **argv,
79 int argc,
80 void *data,
81 void *user_data);
83 static void
84 _free_tuiodev(TuioDevicePtr pTuio);
86 static void
87 lo_error(int num,
88 const char *msg,
89 const char *path);
91 /* Object list manipulation functions */
92 static ObjectPtr
93 _object_get(ObjectPtr head, int id);
95 static ObjectPtr
96 _object_new(TuioDevicePtr pTuio, int id);
98 static void
99 _object_remove(TuioDevicePtr pTuio, ObjectPtr *head, int id);
101 /* Driver information */
102 static XF86ModuleVersionInfo TuioVersionRec =
104 "tuio",
105 MODULEVENDORSTRING,
106 MODINFOSTRING1,
107 MODINFOSTRING2,
108 XORG_VERSION_CURRENT,
109 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
110 ABI_CLASS_XINPUT,
111 ABI_XINPUT_VERSION,
112 MOD_CLASS_XINPUT,
113 {0, 0, 0, 0}
116 _X_EXPORT InputDriverRec TUIO =
119 "tuio",
120 NULL,
121 TuioPreInit,
122 TuioUnInit,
123 NULL,
127 _X_EXPORT XF86ModuleData tuioModuleData =
129 &TuioVersionRec,
130 TuioPlug,
131 TuioUnplug
134 static pointer
135 TuioPlug(pointer module,
136 pointer options,
137 int *errmaj,
138 int *errmin)
140 xf86AddInputDriver(&TUIO, module, 0);
141 return module;
145 * Unplug
147 static void
148 TuioUnplug(pointer p)
153 * Pre-initialization of new device
154 * This can be entered by either a "core" tuio device
155 * or an extension "Object" device that is used for routing individual object
156 * events through.
158 static InputInfoPtr
159 TuioPreInit(InputDriverPtr drv,
160 IDevPtr dev,
161 int flags)
163 InputInfoPtr pInfo;
164 TuioDevicePtr pTuio = NULL;
165 ObjectPtr obj;
166 ObjectDevPtr objdev;
167 char *type;
168 int id;
170 if (!(pInfo = xf86AllocateInput(drv, 0)))
171 return NULL;
173 /* If Type == Object, this is a device for an object to use */
174 type = xf86CheckStrOption(dev->commonOptions, "Type", NULL);
176 if (type != NULL && strcmp(type, "Object") == 0) {
178 xf86Msg(X_INFO, "%s: Object device found\n", dev->identifier);
180 /* Allocate device storage and add to device list */
181 objdev = xcalloc(1, sizeof(ObjectDevRec));
182 if (!g_pTuio->unused_device_list) {
183 g_pTuio->unused_device_list = objdev;
184 } else {
185 objdev->next = g_pTuio->unused_device_list;
186 g_pTuio->unused_device_list = objdev;
188 objdev->pInfo = pInfo;
190 } else {
192 if (!(pTuio = xcalloc(1, sizeof(TuioDeviceRec)))) {
193 xf86DeleteInput(pInfo, 0);
194 return NULL;
196 g_pTuio = pTuio;
198 pInfo->private = pTuio;
201 /* Set up InputInfoPtr */
202 pInfo->name = xstrdup(dev->identifier);
203 pInfo->flags = 0;
204 pInfo->type_name = XI_TOUCHSCREEN; /* FIXME: Correct type? */
205 pInfo->conf_idev = dev;
206 pInfo->read_input = pTuio ? TuioReadInput : TuioObjReadInput; /* Set callback */
207 pInfo->device_control = TuioControl; /* Set callback */
208 pInfo->switch_mode = NULL;
210 /* Process common device options */
211 xf86CollectInputOptions(pInfo, NULL, NULL);
212 xf86ProcessCommonOptions(pInfo, pInfo->options);
214 pInfo->flags |= XI86_OPEN_ON_INIT;
215 pInfo->flags |= XI86_CONFIGURED;
217 return pInfo;
221 * Clean up
223 static void
224 TuioUnInit(InputDriverPtr drv,
225 InputInfoPtr pInfo,
226 int flags)
228 xf86DeleteInput(pInfo, 0);
232 * Empty callback for object device.
234 static void
235 TuioObjReadInput(InputInfoPtr pInfo) {
236 return;
240 * Handle new TUIO data on the socket
242 static void
243 TuioReadInput(InputInfoPtr pInfo)
245 TuioDevicePtr pTuio = pInfo->private;
246 ObjectPtr *head = &pTuio->obj_list;
247 ObjectPtr obj = pTuio->obj_list;
248 ObjectPtr objtmp;
249 int valuators[2];
251 while(xf86WaitForInput(pInfo->fd, 0) > 0)
253 /* The liblo handler will set this flag if anything was processed */
254 pTuio->processed = 0;
256 /* liblo will receive a message and call the appropriate
257 * handlers (i.e. _tuio_lo_cur2d_hande()) */
258 lo_server_recv_noblock(pTuio->server, 0);
260 /* During the processing of the previous message/bundle,
261 * any "active" messages will be handled by flagging
262 * the listed object ids. Now that processing is done,
263 * remove any dead object ids and set any pending changes.
264 * Also check to make sure the processed data was newer than
265 * the last processed data */
266 if (pTuio->processed && pTuio->fseq_new > pTuio->fseq_old) {
268 while (obj != NULL) {
269 if (!obj->alive) {
270 xf86PostButtonEvent(obj->objdev->pInfo->dev, TRUE, 1, FALSE, 0, 0);
271 objtmp = obj->next;
272 _object_remove(pTuio, head, obj->id);
273 obj = objtmp;
274 } else {
275 if (obj->pending.set && obj->objdev) {
276 obj->x = obj->pending.x;
277 obj->y = obj->pending.y;
278 obj->pending.set = False;
280 if (obj->objdev) {
281 /* OKAY FOR NOW, MAYBE UPDATE */
282 valuators[0] = obj->x * 0x7FFFFFFF;
283 valuators[1] = obj->y * 0x7FFFFFFF;
285 xf86PostMotionEventP(obj->objdev->pInfo->dev,
286 TRUE, /* is_absolute */
287 0, /* first_valuator */
288 2, /* num_valuators */
289 valuators);
292 obj->alive = 0; /* Reset for next message */
293 obj = obj->next;
296 pTuio->fseq_old = pTuio->fseq_new;
302 * Handle device state changes
304 static int
305 TuioControl(DeviceIntPtr device,
306 int what)
308 InputInfoPtr pInfo = device->public.devicePrivate;
309 TuioDevicePtr pTuio = pInfo->private;
311 switch (what)
313 case DEVICE_INIT:
314 xf86Msg(X_INFO, "%s: Init\n", pInfo->name);
315 _tuio_init_buttons(device);
316 _tuio_init_axes(device);
318 /* If this is a "core" device, create object devices */
319 if (pTuio)
320 _init_devices(pInfo, 5);
321 break;
323 case DEVICE_ON: /* Open socket and start listening! */
324 xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
325 if (device->public.on)
326 break;
328 if (!pTuio) {
329 pInfo->fd = 0;
330 goto finish;
333 /* Setup server */
334 pTuio->server = lo_server_new_with_proto("3333", LO_UDP, lo_error);
335 if (pTuio->server == NULL) {
336 xf86Msg(X_ERROR, "%s: Error allocating new lo_server\n",
337 pInfo->name);
338 return BadAlloc;
341 /* Register to receive all /tuio/2Dcur messages */
342 lo_server_add_method(pTuio->server, "/tuio/2Dcur", NULL,
343 _tuio_lo_cur2d_handle, pInfo);
345 pInfo->fd = lo_server_get_socket_fd(pTuio->server);
347 xf86FlushInput(pInfo->fd);
349 finish: xf86AddEnabledDevice(pInfo);
350 device->public.on = TRUE;
351 break;
353 case DEVICE_OFF:
354 xf86Msg(X_INFO, "%s: Off\n", pInfo->name);
355 if (!device->public.on)
356 break;
358 xf86RemoveEnabledDevice(pInfo);
360 if (pTuio) {
361 lo_server_free(pTuio->server);
362 pInfo->fd = -1;
365 device->public.on = FALSE;
366 break;
368 case DEVICE_CLOSE:
369 xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
370 break;
373 return Success;
377 * Initialize the device properties
378 * TODO
380 TuioPropertyInit() {
384 * Free a TuioDeviceRec
386 static void
387 _free_tuiodev(TuioDevicePtr pTuio) {
388 ObjectPtr obj = pTuio->obj_list;
389 ObjectPtr tmp;
391 while (obj != NULL) {
392 tmp = obj->next;
393 xfree(obj);
394 obj = tmp;
397 xfree(pTuio);
401 * Handles OSC messages in the /tuio/2Dcur address space
403 static int
404 _tuio_lo_cur2d_handle(const char *path,
405 const char *types,
406 lo_arg **argv,
407 int argc,
408 void *data,
409 void *user_data) {
410 InputInfoPtr pInfo = user_data;
411 TuioDevicePtr pTuio = pInfo->private;
412 ObjectPtr head = pTuio->obj_list;
413 ObjectPtr obj, objtemp;
414 char *act;
415 int i;
417 if (argc == 0) {
418 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (argc == 0)\n",
419 pInfo->name);
420 return 0;
423 /* Flag as being processed, used in TuioReadInput() */
424 pTuio->processed = 1;
426 /* Parse message type */
427 /* Set message type: */
428 if (strcmp((char *)argv[0], "set") == 0) {
430 /* Simple type check */
431 if (strcmp(types, "sifffff")) {
432 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d set msg (types == %s)\n",
433 pInfo->name, types);
434 return 0;
437 obj = _object_get(head, argv[1]->i);
439 /* If not found, create a new object */
440 if (obj == NULL) {
441 obj = _object_new(pTuio, argv[1]->i);
442 if (obj->objdev)
443 xf86PostButtonEvent(obj->objdev->pInfo->dev, TRUE, 1, TRUE, 0, 0);
446 obj->pending.x = argv[2]->f;
447 obj->pending.y = argv[3]->f;
448 obj->pending.set = True;
450 } else if (strcmp((char *)argv[0], "alive") == 0) {
452 /* Mark all objects that are still alive */
453 obj = head;
454 while (obj != NULL) {
455 for (i=1; i<argc; i++) {
456 if (argv[i]->i == obj->id) {
457 obj->alive = True;
458 break;
461 obj = obj->next;
464 } else if (strcmp((char *)argv[0], "fseq") == 0) {
465 /* Simple type check */
466 if (strcmp(types, "si")) {
467 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d fseq msg (types == %s)\n",
468 pInfo->name, types);
469 return 0;
471 pTuio->fseq_new = argv[1]->i;
474 return 0;
478 * liblo error handler
480 static void
481 lo_error(int num,
482 const char *msg,
483 const char *path)
485 xf86Msg(X_ERROR, "liblo: %s\n", msg);
489 * New device creation through hal
490 * I referenced the wacom hal-setup patch while writing this:
491 * http://cvs.fedoraproject.org/viewvc/rpms/linuxwacom/devel/linuxwacom-0.8.2.2-hal-setup.patch?revision=1.1&view=markup
493 * @return 0 if successful, 1 if failure
495 static int
496 _init_devices(InputInfoPtr pInfo, int num) {
497 DBusError error;
498 DBusConnection *conn;
499 LibHalContext *ctx;
500 char *newdev;
501 char *name;
502 int i;
504 /* We need a new device to send motion/button events through.
505 * There isn't a great way to do this right now without native
506 * blob events, so just hack it out for now. Woot. */
508 /* Open connection to dbus and create contex */
509 dbus_error_init(&error);
510 if ((conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) {
511 xf86Msg(X_ERROR, "%s: Failed to open dbus connection: %s\n",
512 pInfo->name, error.message);
513 return 1;
515 if ((ctx = libhal_ctx_new()) == NULL) {
516 xf86Msg(X_ERROR, "%s: Failed to obtain hal context: %s\n",
517 pInfo->name, error.message);
518 return 1;
521 dbus_error_init(&error);
522 libhal_ctx_set_dbus_connection(ctx, conn);
523 if (!libhal_ctx_init(ctx, &error)) {
524 xf86Msg(X_ERROR, "%s: Failed to initialize hal context: %s\n",
525 pInfo->name, error.message);
526 return 1;
529 /* Create new devices through hal */
530 for (i=0; i<num; i++) {
532 dbus_error_init(&error);
533 newdev = libhal_new_device(ctx, &error);
534 if (dbus_error_is_set(&error) == TRUE) {
535 xf86Msg(X_ERROR, "%s: Failed to create input device: %s\n",
536 pInfo->name, error.message);
537 return 1;
540 dbus_error_init(&error);
541 libhal_device_set_property_string(ctx, newdev, "input.device",
542 "blob", &error);
543 if (dbus_error_is_set(&error) == TRUE) {
544 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
545 pInfo->name, error.message);
546 return 1;
549 dbus_error_init(&error);
550 libhal_device_set_property_string(ctx, newdev,
551 "input.x11_driver", "tuio",
552 &error);
553 if (dbus_error_is_set(&error) == TRUE) {
554 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
555 pInfo->name, error.message);
556 return 1;
559 /* Set "Type" property. This will be used in TuioPreInit to determine
560 * whether the new device is an object device or not */
561 dbus_error_init(&error);
562 libhal_device_set_property_string(ctx, newdev,
563 "input.x11_options.Type",
564 "Object", &error);
565 if (dbus_error_is_set(&error) == TRUE) {
566 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
567 pInfo->name, error.message);
568 return 1;
571 asprintf(&name, "Tuio Obj (%s) %i", pInfo->name, i);
573 /* Set name */
574 dbus_error_init(&error);
575 libhal_device_set_property_string(ctx, newdev,
576 "info.product", name,
577 &error);
578 if (dbus_error_is_set(&error) == TRUE) {
579 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
580 pInfo->name, error.message);
581 return 1;
584 /* Finalize creation of new device */
585 dbus_error_init(&error);
586 libhal_device_commit_to_gdl(ctx, newdev, "/org/freedesktop/Hal/devices/tuio_subdev", &error);
587 if (dbus_error_is_set (&error) == TRUE) {
588 xf86Msg(X_ERROR, "%s: Failed to add input device: %s\n",
589 pInfo->name, error.message);
590 return 1;
593 xfree(name);
596 //libhal_context_free(ctx);
598 return 0;
602 * Retrieves an object from a list based on its id.
604 * @returns NULL if not found.
606 static ObjectPtr
607 _object_get(ObjectPtr head, int id) {
608 ObjectPtr obj = head;
610 while (obj != NULL && obj->id != id) {
611 obj = obj->next;
614 return obj;
618 * Allocates and inserts a new object at the beginning of a list.
619 * Pointer to the new object is returned.
620 * Doesn't check for duplicate ids, so call _object_get() beforehand
621 * to make sure it doesn't exist already!!
623 * @return ptr to newly inserted object
625 static ObjectPtr
626 _object_new(TuioDevicePtr pTuio, int id) {
627 ObjectPtr obj = pTuio->obj_list;
628 ObjectDevPtr objdev = pTuio->unused_device_list;
630 ObjectPtr new_obj = xcalloc(1, sizeof(ObjectRec));
632 if (obj)
633 new_obj->next = obj;
634 pTuio->obj_list = new_obj;
636 if (objdev)
637 pTuio->unused_device_list = objdev->next;
638 new_obj->objdev = objdev;
639 objdev = NULL;
641 new_obj->id = id;
642 new_obj->alive = True;
644 return new_obj;
648 * Removes an object from a list.
650 static void
651 _object_remove(TuioDevicePtr pTuio, ObjectPtr *head, int id) {
652 ObjectPtr obj = *head;
653 ObjectPtr objtmp;
654 ObjectDevPtr objdev;
656 if (obj != NULL && obj->id == id) {
657 *head = (*head)->next;
658 if (obj->objdev) {
659 objdev = pTuio->unused_device_list;
660 if (objdev)
661 obj->objdev->next = objdev;
662 pTuio->unused_device_list = obj->objdev;
664 } else if (obj != NULL) {
665 while (obj->next != NULL) {
666 if (obj->next->id == id) {
667 objtmp = obj->next;
668 obj->next = objtmp->next;
670 if (objtmp->objdev) {
671 objdev = pTuio->unused_device_list;
672 if (objdev)
673 objtmp->objdev->next = objdev;
674 pTuio->unused_device_list = objtmp->objdev;
677 xfree(objtmp);
678 break;
680 obj = obj->next;
686 * Init the button map device. We only use one button.
688 static int
689 _tuio_init_buttons(DeviceIntPtr device)
691 InputInfoPtr pInfo = device->public.devicePrivate;
692 CARD8 *map;
693 Atom *labels;
694 int numbuttons = 4;
695 int ret = Success;
696 int i;
698 map = xcalloc(numbuttons, sizeof(CARD8));
699 labels = xcalloc(1, sizeof(Atom));
700 for (i=0; i<numbuttons; i++)
701 map[i] = i;
703 if (!InitButtonClassDeviceStruct(device, numbuttons /* 1 button */,
704 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
705 labels,
706 #endif
707 map)) {
708 xf86Msg(X_ERROR, "%s: Failed to register buttons.\n", pInfo->name);
709 ret = BadAlloc;
712 xfree(labels);
713 return ret;
717 * Init valuators for device, use x/y coordinates.
719 static int
720 _tuio_init_axes(DeviceIntPtr device)
722 InputInfoPtr pInfo = device->public.devicePrivate;
723 int i;
724 const int num_axes = 2;
725 Atom *atoms;
727 atoms = xcalloc(num_axes, sizeof(Atom));
729 if (!InitValuatorClassDeviceStruct(device,
730 num_axes,
731 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
732 atoms,
733 #endif
734 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
735 GetMotionHistory,
736 #endif
737 GetMotionHistorySize(),
739 return BadAlloc;
741 /* Use absolute mode. Currently, TUIO coords are mapped to the
742 * full screen area */
743 pInfo->dev->valuator->mode = Absolute;
744 if (!InitAbsoluteClassDeviceStruct(device))
745 return BadAlloc;
747 for (i = 0; i < num_axes; i++)
749 xf86InitValuatorAxisStruct(device, i,
750 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
751 atoms[i],
752 #endif
753 0, 0x7FFFFFFF, 1, 1, 1);
754 xf86InitValuatorDefaults(device, i);
756 return Success;