Fixed asprintf missing argument
[xf86-input-tuio.git] / src / tuio.c
blob70934671d16731a16404686837cc2f5bc6d529bb
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 static TuioDevicePtr g_dev_ptr;
41 /* Module Functions */
42 static pointer
43 TuioPlug(pointer, pointer, int *, int *);
45 static void
46 TuioUnplug(pointer);
48 /* Driver Functions */
49 static InputInfoPtr
50 TuioPreInit(InputDriverPtr, IDevPtr, int);
52 static void
53 TuioUnInit(InputDriverPtr, InputInfoPtr, int);
55 static void
56 TuioObjReadInput(InputInfoPtr pInfo);
58 static void
59 TuioReadInput(InputInfoPtr);
61 static int
62 TuioControl(DeviceIntPtr, int);
64 /* Internal Functions */
65 static int
66 _tuio_init_buttons(DeviceIntPtr device);
68 static int
69 _tuio_init_axes(DeviceIntPtr device);
71 static int
72 _tuio_lo_cur2d_handle(const char *path,
73 const char *types,
74 lo_arg **argv,
75 int argc,
76 void *data,
77 void *user_data);
79 static void
80 lo_error(int num,
81 const char *msg,
82 const char *path);
84 static int
85 _object_new(ObjectPtr obj, InputInfoPtr pInfo);
87 /* Object list manipulation functions */
88 static ObjectPtr
89 _object_get(ObjectPtr head, int id);
91 static ObjectPtr
92 _object_insert(ObjectPtr head);
94 static void
95 _object_remove(ObjectPtr head, int id);
97 /* Driver information */
98 static XF86ModuleVersionInfo TuioVersionRec =
100 "tuio",
101 MODULEVENDORSTRING,
102 MODINFOSTRING1,
103 MODINFOSTRING2,
104 XORG_VERSION_CURRENT,
105 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
106 ABI_CLASS_XINPUT,
107 ABI_XINPUT_VERSION,
108 MOD_CLASS_XINPUT,
109 {0, 0, 0, 0}
112 _X_EXPORT InputDriverRec TUIO =
115 "tuio",
116 NULL,
117 TuioPreInit,
118 TuioUnInit,
119 NULL,
123 _X_EXPORT XF86ModuleData tuioModuleData =
125 &TuioVersionRec,
126 TuioPlug,
127 TuioUnplug
130 static pointer
131 TuioPlug(pointer module,
132 pointer options,
133 int *errmaj,
134 int *errmin)
136 xf86AddInputDriver(&TUIO, module, 0);
137 return module;
141 * Unplug
143 static void
144 TuioUnplug(pointer p)
149 * Pre-initialization of new device
151 * TODO:
152 * - Parse configuration options
154 static InputInfoPtr
155 TuioPreInit(InputDriverPtr drv,
156 IDevPtr dev,
157 int flags)
159 InputInfoPtr pInfo;
160 TuioDevicePtr pTuio;
161 ObjectPtr obj;
162 char *type;
163 int id;
165 if (!(pInfo = xf86AllocateInput(drv, 0)))
166 return NULL;
168 /* The TuioDevicePtr will hold object and other
169 * information */
170 if (!(pTuio = xcalloc(1, sizeof(TuioDeviceRec)))) {
171 xf86DeleteInput(pInfo, 0);
172 return NULL;
175 if (!(pTuio->list_head = xcalloc(1, sizeof(ObjectRec)))) {
176 xfree(pTuio);
177 xf86DeleteInput(pInfo, 0);
178 return NULL;
180 pTuio->list_head->id = -1;
181 pTuio->list_head->next = NULL;
183 /* If Type == Object, this is a device needed for a blob object. */
184 type = xf86CheckStrOption(dev->commonOptions, "Type", NULL);
185 if (type != NULL && strcmp(type, "Object") == 0) {
186 xf86Msg(X_INFO, "%s: blob device found\n", dev->identifier);
187 pTuio->isObject = 1;
189 /* Find object this device is linked to */
190 id = xf86CheckIntOption(dev->commonOptions, "Id", -1);
191 if (id == -1) {
192 xf86Msg(X_ERROR, "%s: blob id option was not set\n", dev->identifier);
193 _free_tuiodev(pTuio);
194 xf86DeleteInput(pInfo, 0);
195 return NULL;
198 obj = _object_get(g_dev_ptr->list_head, id);
199 if (obj == NULL) {
200 xf86Msg(X_ERROR, "%s: blob id could not be found; possibly removed before" \
201 " device initialization\n", dev->identifier);
202 _free_tuiodev(pTuio);
203 xf86DeleteInput(pInfo, 0);
204 return NULL;
206 obj->pInfo = pInfo;
208 } else {
209 pTuio->isObject = 0;
212 pInfo->private = pTuio;
213 if (!pTuio->isObject)
214 g_dev_ptr = pTuio;
216 /* Set up InputInfoPtr */
217 pInfo->name = xstrdup(dev->identifier);
218 pInfo->flags = 0;
219 pInfo->type_name = XI_TOUCHSCREEN; /* FIXME: Correct type? */
220 pInfo->conf_idev = dev;
221 pInfo->read_input = pTuio->isObject ? TuioObjReadInput : TuioReadInput; /* Set callback */
222 pInfo->device_control = TuioControl; /* Set callback */
223 pInfo->switch_mode = NULL;
225 /* Process common device options */
226 xf86CollectInputOptions(pInfo, NULL, NULL);
227 xf86ProcessCommonOptions(pInfo, pInfo->options);
229 pInfo->flags |= XI86_OPEN_ON_INIT;
230 pInfo->flags |= XI86_CONFIGURED;
232 return pInfo;
236 * Clean up
238 static void
239 TuioUnInit(InputDriverPtr drv,
240 InputInfoPtr pInfo,
241 int flags)
243 TuioDevicePtr pTuio = pInfo->private;
245 xfree(pTuio);
246 xf86DeleteInput(pInfo, 0);
249 static void
250 TuioObjReadInput(InputInfoPtr pInfo) {
251 return;
255 * Handle new data on the socket
257 static void
258 TuioReadInput(InputInfoPtr pInfo)
260 TuioDevicePtr pTuio = pInfo->private;
261 ObjectPtr head = pTuio->list_head;
262 ObjectPtr obj = head->next;
263 ObjectPtr objtemp;
264 int valuators[2];
266 while(xf86WaitForInput(pInfo->fd, 0) > 0)
268 /* The liblo handler will set this flag if anything was processed */
269 pTuio->processed = 0;
271 /* liblo will receive a message and call the appropriate
272 * handlers (i.e. _tuio_lo_cur2d_hande()) */
273 lo_server_recv_noblock(pTuio->server, 0);
275 /* During the processing of the previous message/bundle,
276 * any "active" messages will be handled by flagging
277 * the listed object ids. Now that processing is done,
278 * remove any dead object ids and set any pending changes. */
279 if (pTuio->processed) {
281 /* Was this bundle newer than the previously received bundle? */
282 if (pTuio->fseq_new > pTuio->fseq_old) {
283 while (obj != NULL) {
284 if (!obj->alive) {
285 objtemp = obj->next;
286 _object_remove(head, obj->id);
287 obj = objtemp;
288 xf86PostButtonEvent(pInfo->dev, TRUE, 1, FALSE, 0, 0);
289 } else {
290 if (obj->pending.set) {
291 obj->x = obj->pending.x;
292 obj->y = obj->pending.y;
293 obj->pending.set = False;
295 if (!obj->pInfo) {
296 /* OKAY FOR NOW, MAYBE UPDATE */
297 valuators[0] = obj->x * 0x7FFFFFFF;
298 valuators[1] = obj->y * 0x7FFFFFFF;
300 xf86PostMotionEventP(obj->pInfo->dev,
301 TRUE, /* is_absolute */
302 0, /* first_valuator */
303 2, /* num_valuators */
304 valuators);
307 obj->alive = 0; /* Reset for next message */
308 obj = obj->next;
317 * Handle device state changes
319 static int
320 TuioControl(DeviceIntPtr device,
321 int what)
323 InputInfoPtr pInfo = device->public.devicePrivate;
324 TuioDevicePtr pTuio = pInfo->private;
326 switch (what)
328 case DEVICE_INIT:
329 xf86Msg(X_INFO, "%s: Init\n", pInfo->name);
330 _tuio_init_buttons(device);
331 _tuio_init_axes(device);
332 break;
334 case DEVICE_ON: /* Open socket and start listening! */
335 xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
336 if (device->public.on) /* already on! */
337 break;
339 if (pTuio->isObject) {
340 pInfo->fd = 0;
341 goto finish;
344 /* Setup server */
345 pTuio->server = lo_server_new_with_proto("3333", LO_UDP, lo_error);
346 if (pTuio->server == NULL) {
347 xf86Msg(X_ERROR, "%s: Error allocating new lo_server\n",
348 pInfo->name);
349 return BadAlloc;
352 /* Register to receive all /tuio/2Dcur messages */
353 lo_server_add_method(pTuio->server, "/tuio/2Dcur", NULL,
354 _tuio_lo_cur2d_handle, pInfo);
356 pInfo->fd = lo_server_get_socket_fd(pTuio->server);
357 xf86Msg(X_INFO, "%s: Socket = %i\n", pInfo->name, pInfo->fd);
359 xf86FlushInput(pInfo->fd);
360 finish: xf86AddEnabledDevice(pInfo);
361 device->public.on = TRUE;
362 break;
364 case DEVICE_OFF:
365 xf86Msg(X_INFO, "%s: Off\n", pInfo->name);
366 if (!device->public.on)
367 break;
369 xf86RemoveEnabledDevice(pInfo);
371 lo_server_free(pTuio->server);
372 pInfo->fd = -1;
373 device->public.on = FALSE;
374 break;
376 case DEVICE_CLOSE:
377 xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
379 _free_tuiodev(pTuio);
380 break;
383 return Success;
387 * Initialize the device properties
389 TuioPropertyInit() {
392 TuioObjPostAnalyze() {
397 * Free a TuioDeviceRec
399 _free_tuiodev(TuioDevicePtr pTuio) {
400 ObjectPtr obj = pTuio->list_head;
401 ObjectPtr tmp;
403 while (obj != NULL) {
404 tmp = obj->next;
405 xfree(obj);
406 obj = tmp;
409 xfree(pTuio);
413 * Handles OSC messages in the /tuio/2Dcur address space
415 static int
416 _tuio_lo_cur2d_handle(const char *path,
417 const char *types,
418 lo_arg **argv,
419 int argc,
420 void *data,
421 void *user_data) {
422 InputInfoPtr pInfo = user_data;
423 TuioDevicePtr pTuio = pInfo->private;
424 ObjectPtr head = pTuio->list_head;
425 ObjectPtr obj, objtemp;
426 char *act;
427 int i;
429 if (argc == 0) {
430 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d (argc == 0)\n",
431 pInfo->name);
432 return 0;
435 /* Flag as being processed, used in TuioReadInput() */
436 pTuio->processed = 1;
438 /* Parse message type */
439 /* Set message type: */
440 if (strcmp((char *)argv[0], "set") == 0) {
442 /* Simple type check */
443 if (strcmp(types, "sifffff")) {
444 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d set msg (types == %s)\n",
445 pInfo->name, types);
446 return 0;
449 obj = _object_get(head, argv[1]->i);
451 /* If not found, create a new object */
452 if (obj == NULL) {
453 obj = _object_insert(head);
454 obj->id = argv[1]->i;
455 xf86PostButtonEvent(pInfo->dev, TRUE, 1, TRUE, 0, 0);
456 _object_new(obj, pInfo);
459 obj->pending.x = argv[2]->f;
460 obj->pending.y = argv[3]->f;
461 obj->pending.set = True;
463 } else if (strcmp((char *)argv[0], "alive") == 0) {
465 obj = head->next;
466 while (obj != NULL) {
467 for (i=1; i<argc; i++) {
468 if (argv[i]->i == obj->id) {
469 obj->alive = 1;
470 break;
473 obj = obj->next;
476 } else if (strcmp((char *)argv[0], "fseq") == 0) {
477 /* Simple type check */
478 if (strcmp(types, "si")) {
479 xf86Msg(X_ERROR, "%s: Error in /tuio/cur2d fseq msg (types == %s)\n",
480 pInfo->name, types);
481 return 0;
483 pTuio->fseq_old = pTuio->fseq_new;
484 pTuio->fseq_new = argv[1]->i;
487 return 0;
491 * liblo error handler
493 static void
494 lo_error(int num,
495 const char *msg,
496 const char *path)
498 xf86Msg(X_ERROR, "liblo: %s\n", msg);
502 * Allocates a new object and sets up new device creation through hal
503 * I referenced the wacom hal-setup patch while writing this:
504 * http://cvs.fedoraproject.org/viewvc/rpms/linuxwacom/devel/linuxwacom-0.8.2.2-hal-setup.patch?revision=1.1&view=markup
506 * @return 0 if successful, 1 if failure
508 static int
509 _object_new(ObjectPtr obj, InputInfoPtr pInfo) {
510 DBusError error;
511 DBusConnection *conn;
512 LibHalContext *ctx;
513 char *newdev;
514 char *name;
516 /* We need a new device to send motion/button events through.
517 * There isn't a great way to do this right now without native
518 * blob events, so just hack it out for now. Woot. */
520 asprintf(&name, "Tuio Obj (%s) %i", pInfo->name, obj->id);
522 /* Open connection to dbus and create contex */
523 dbus_error_init(&error);
524 if ((conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) {
525 xf86Msg(X_ERROR, "%s: Failed to open dbus connection: %s\n",
526 pInfo->name, error.message);
527 return 1;
529 if ((ctx = libhal_ctx_new()) == NULL) {
530 xf86Msg(X_ERROR, "%s: Failed to obtain hal context: %s\n",
531 pInfo->name, error.message);
532 return 1;
535 dbus_error_init(&error);
536 libhal_ctx_set_dbus_connection(ctx, conn);
537 if (!libhal_ctx_init(ctx, &error)) {
538 xf86Msg(X_ERROR, "%s: Failed to initialize hal context: %s\n",
539 pInfo->name, error.message);
540 return 1;
543 /* Create a new device through hal */
544 dbus_error_init(&error);
545 newdev = libhal_new_device(ctx, &error);
546 if (dbus_error_is_set(&error) == TRUE) {
547 xf86Msg(X_ERROR, "%s: Failed to create input device: %s\n",
548 pInfo->name, error.message);
549 return 1;
552 dbus_error_init(&error);
553 libhal_device_set_property_string(ctx, newdev, "input.device",
554 "blob", &error);
555 if (dbus_error_is_set(&error) == TRUE) {
556 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
557 pInfo->name, error.message);
558 return 1;
561 dbus_error_init(&error);
562 libhal_device_set_property_string(ctx, newdev,
563 "input.x11_driver", "tuio",
564 &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 dbus_error_init(&error);
572 libhal_device_set_property_string(ctx, newdev,
573 "info.product", name,
574 &error);
575 if (dbus_error_is_set(&error) == TRUE) {
576 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
577 pInfo->name, error.message);
578 return 1;
581 /* Set "Type" property. This will be used in TuioPreInit to determine
582 * whether the new device is an object device or not */
583 dbus_error_init(&error);
584 libhal_device_set_property_string(ctx, newdev,
585 "input.x11_options.Type",
586 "Object", &error);
587 if (dbus_error_is_set(&error) == TRUE) {
588 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
589 pInfo->name, error.message);
590 return 1;
593 /* Set the id so that we know which object to associate the device with */
594 dbus_error_init(&error);
595 libhal_device_set_property_string(ctx, newdev,
596 "input.x11_options.Id",
597 "1", &error);
598 if (dbus_error_is_set(&error) == TRUE) {
599 xf86Msg(X_ERROR, "%s: Failed to set hal property: %s\n",
600 pInfo->name, error.message);
601 return 1;
604 dbus_error_init(&error);
605 libhal_device_commit_to_gdl(ctx, newdev, "/org/freedesktop/Hal/devices/tuio_subdev", &error);
607 if (dbus_error_is_set (&error) == TRUE) {
608 xf86Msg(X_ERROR, "%s: Failed to add input device: %s\n",
609 pInfo->name, error.message);
610 return 1;
613 return 0;
617 * Retrieves an object from a list based on its id.
619 * @returns NULL if not found.
621 static ObjectPtr
622 _object_get(ObjectPtr head, int id) {
623 ObjectPtr obj = head->next;
625 while (obj != NULL && obj->id != id) {
626 obj = obj->next;
629 return obj;
633 * Allocates and inserts a new object at the beginning of a list.
634 * Pointer to the new object is returned.
635 * Doesn't check for duplicate ids, so call _object_get() beforehand
636 * to make sure it doesn't exist already!!
638 * @return ptr to newly inserted object
640 static ObjectPtr
641 _object_insert(ObjectPtr head) {
642 ObjectPtr obj = xcalloc(1, sizeof(ObjectRec));
643 obj->next = head->next;
644 head->next = obj;
645 return obj;
649 * Removes an object from a list. It is assumed that the object
650 * is in the list
652 static void
653 _object_remove(ObjectPtr head, int id) {
654 ObjectPtr obj = head;
655 ObjectPtr objtmp;
657 while (obj->next != NULL) {
658 if (obj->next->id == id) {
659 objtmp = obj->next;
660 obj->next = objtmp->next;
661 xfree(objtmp);
662 break;
664 obj = obj->next;
669 * Init the button map device. We only use one button.
671 static int
672 _tuio_init_buttons(DeviceIntPtr device)
674 InputInfoPtr pInfo = device->public.devicePrivate;
675 CARD8 map;
676 Atom *labels;
677 int ret = Success;
678 int i;
680 map = 1;
681 labels = xcalloc(1, sizeof(Atom));
683 if (!InitButtonClassDeviceStruct(device, 1 /* 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;