Added NULL check to avoid seg fault...
[xf86-input-tuio.git] / src / tuio.c
blob4a9a98b1711e5b8f2064e5d0d5059df317bb7880
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 /* Module Functions */
40 static pointer
41 TuioPlug(pointer, pointer, int *, int *);
43 static void
44 TuioUnplug(pointer);
46 /* Driver Functions */
47 static InputInfoPtr
48 TuioPreInit(InputDriverPtr, IDevPtr, int);
50 static void
51 TuioUnInit(InputDriverPtr, InputInfoPtr, int);
53 static void
54 TuioReadInput(InputInfoPtr);
56 static int
57 TuioControl(DeviceIntPtr, int);
59 /* Internal Functions */
60 static int
61 _tuio_init_buttons(DeviceIntPtr device);
63 static int
64 _tuio_init_axes(DeviceIntPtr device);
66 static int
67 _tuio_lo_cur2d_handle(const char *path,
68 const char *types,
69 lo_arg **argv,
70 int argc,
71 void *data,
72 void *user_data);
74 static void
75 lo_error(int num,
76 const char *msg,
77 const char *path);
79 /* Object list manipulation functions */
80 static ObjectPtr
81 _object_get(ObjectList list, int id);
83 static ObjectPtr
84 _object_insert(ObjectList list);
86 static void
87 _object_remove(ObjectList list, ObjectPtr obj);
89 /* Driver information */
90 static XF86ModuleVersionInfo TuioVersionRec =
92 "tuio",
93 MODULEVENDORSTRING,
94 MODINFOSTRING1,
95 MODINFOSTRING2,
96 XORG_VERSION_CURRENT,
97 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
98 ABI_CLASS_XINPUT,
99 ABI_XINPUT_VERSION,
100 MOD_CLASS_XINPUT,
101 {0, 0, 0, 0}
104 _X_EXPORT InputDriverRec TUIO =
107 "tuio",
108 NULL,
109 TuioPreInit,
110 TuioUnInit,
111 NULL,
115 _X_EXPORT XF86ModuleData tuioModuleData =
117 &TuioVersionRec,
118 TuioPlug,
119 TuioUnplug
122 static pointer
123 TuioPlug(pointer module,
124 pointer options,
125 int *errmaj,
126 int *errmin)
128 xf86AddInputDriver(&TUIO, module, 0);
129 return module;
132 /* Remove this? */
133 static void
134 TuioUnplug(pointer p)
139 * Pre-initialization of new device
141 * TODO:
142 * - Parse configuration options
144 static InputInfoPtr
145 TuioPreInit(InputDriverPtr drv,
146 IDevPtr dev,
147 int flags)
149 InputInfoPtr pInfo;
150 TuioDevicePtr pTuio;
152 if (!(pInfo = xf86AllocateInput(drv, 0)))
153 return NULL;
155 /* The TuioDevicePtr will hold our blobs */
156 pTuio = xcalloc(1, sizeof(TuioDeviceRec));
157 if (!pTuio) {
158 pInfo->private = NULL;
159 xf86DeleteInput(pInfo, 0);
160 return NULL;
163 pInfo->private = pTuio;
165 /* Set up InputInfoPtr */
166 pInfo->name = xstrdup(dev->identifier);
167 pInfo->flags = 0;
168 pInfo->type_name = XI_TOUCHSCREEN; /* FIXME: Correct type? */
169 pInfo->conf_idev = dev;
170 pInfo->read_input = TuioReadInput; /* Set callback */
171 pInfo->device_control = TuioControl; /* Set callback */
172 pInfo->switch_mode = NULL;
174 /* Process common device options */
175 xf86CollectInputOptions(pInfo, NULL, NULL);
176 xf86ProcessCommonOptions(pInfo, pInfo->options);
178 pInfo->flags |= XI86_OPEN_ON_INIT;
179 pInfo->flags |= XI86_CONFIGURED;
181 return pInfo;
185 * Clean up
187 static void
188 TuioUnInit(InputDriverPtr drv,
189 InputInfoPtr pInfo,
190 int flags)
192 TuioDevicePtr pTuio = pInfo->private;
194 xfree(pTuio);
195 xf86DeleteInput(pInfo, 0);
199 * Handle new data on the socket
201 static void
202 TuioReadInput(InputInfoPtr pInfo)
204 TuioDevicePtr pTuio = pInfo->private;
205 ObjectList list = pTuio->list;
206 ObjectPtr obj = *list;
207 ObjectPtr objtemp;
209 while(xf86WaitForInput(pInfo->fd, 0) > 0)
211 /* liblo will receive a message and call the appropriate
212 * handlers (i.e. _tuio_lo_cur2d_hande())
213 * If nothing is found (this SHOULDN'T happen, but if it did,
214 * all the objects would be deleted), just return */
215 if(!lo_server_recv_noblock(pTuio->server, 0))
216 return;
218 /* During the processing of the previous message/bundle,
219 * any "active" messages will be handled by flagging
220 * the listed object ids. Now that processing is done,
221 * remove any dead object ids. */
222 if (obj != NULL) {
223 if (!obj->alive) {
224 objtemp = obj->next;
225 _object_remove(list, obj);
226 obj = objtemp;
227 } else {
228 obj->alive = 0; /* Reset for next message */
229 obj = obj->next;
236 * Handle device state changes
238 static int
239 TuioControl(DeviceIntPtr device,
240 int what)
242 InputInfoPtr pInfo = device->public.devicePrivate;
243 TuioDevicePtr pTuio = pInfo->private;
245 switch (what)
247 case DEVICE_INIT:
248 xf86Msg(X_INFO, "%s: Init\n", pInfo->name);
249 _tuio_init_buttons(device);
250 _tuio_init_axes(device);
251 break;
253 case DEVICE_ON: /* Open device socket and start listening! */
254 xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
255 if (device->public.on) /* already on! */
256 break;
258 /* Setup server */
259 pTuio->server = lo_server_new_with_proto("3333", LO_UDP, lo_error);
260 if (pTuio->server == NULL) {
261 xf86Msg(X_ERROR, "%s: Error allocating new lo_server\n",
262 pInfo->name);
263 return BadAlloc;
265 pTuio->list = xcalloc(1, sizeof(ObjectPtr));
267 /* Register to receive all /tuio/2Dcur messages */
268 lo_server_add_method(pTuio->server, "/tuio/2Dcur", NULL,
269 _tuio_lo_cur2d_handle, pInfo);
271 pInfo->fd = lo_server_get_socket_fd(pTuio->server);
272 xf86Msg(X_INFO, "%s: Socket = %i\n", pInfo->name, pInfo->fd);
274 xf86AddEnabledDevice(pInfo);
275 device->public.on = TRUE;
276 break;
278 case DEVICE_OFF:
279 xf86Msg(X_INFO, "%s: Off\n", pInfo->name);
280 if (!device->public.on)
281 break;
283 xf86RemoveEnabledDevice(pInfo);
285 lo_server_free(pTuio->server);
286 pInfo->fd = -1;
287 device->public.on = FALSE;
288 break;
290 case DEVICE_CLOSE:
291 xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
293 lo_server_free(pTuio->server);
294 pInfo->fd = -1;
295 device->public.on = FALSE;
296 break;
299 return Success;
302 static int
303 _tuio_create_master() {
304 //XICreateMasterInfo ci;
305 //unsigned int blobid;
306 //char cur_name[21]; /* Max len: 20 char + \0 */
308 //sprintf(cur_name, "tuio_blob_%u", blobid);
310 //c.type = XICreateMaster;
311 //c.name = cur_name;
315 * Handles OSC messages in the /tuio/2Dcur address space
317 static int
318 _tuio_lo_cur2d_handle(const char *path,
319 const char *types,
320 lo_arg **argv,
321 int argc,
322 void *data,
323 void *user_data)
325 InputInfoPtr pInfo = user_data;
326 TuioDevicePtr pTuio = pInfo->private;
327 ObjectList list = pTuio->list;
328 ObjectPtr obj, objtemp;
329 int valuators[2];
330 int i;
332 if (argc < 1) {
333 xf86Msg(X_ERROR, "%s: \n", pInfo->name);
334 return 1;
337 /* Parse message type */
339 /* Set message type: */
340 if (strcmp(argv[0], "set") == 0) {
341 obj = _object_get(list, argv[1]->i);
342 if(obj == NULL) {
343 obj = _object_insert(list);
344 obj->id = argv[1]->i;
347 obj->x = argv[2]->f;
348 obj->y = argv[3]->f;
350 /* THIS NEEDS TO BE FIXED!! */
351 valuators[0] = obj->x * 0xFFFFFFFF;
352 valuators[1] = obj->y * 0xFFFFFFFF;
354 xf86PostMotionEventP(pInfo->dev,
355 TRUE, /* is_absolute */
356 0, /* first_valuator */
357 2, /* num_valuators */
358 valuators);
360 } else if (strcmp(argv[0], "alive") == 0) {
361 obj = *list;
362 while (obj != NULL) {
363 obj->alive = 0;
364 for (i=1; i<argc; i++) {
365 if (argv[i]->i == obj->id) {
366 obj->alive = 1;
367 break;
373 } else if (strcmp(argv[0], "fseq") == 0) {
376 return 0;
380 * liblo error handler
382 static void
383 lo_error(int num,
384 const char *msg,
385 const char *path)
387 xf86Msg(X_ERROR, "liblo: %s\n", msg);
391 * Retrieves an object from a list based on its id. Returns NULL if
392 * not found.
394 static ObjectPtr
395 _object_get(ObjectList list, int id) {
396 ObjectPtr obj = *list;
398 while (obj != NULL && obj->id != id) {
399 obj = obj->next;
402 return obj;
406 * Allocates and inserts a new object at the beginning of a list.
407 * Pointer to the new object is returned.
409 static ObjectPtr
410 _object_insert(ObjectList list) {
411 ObjectPtr obj = xcalloc(1, sizeof(ObjectRec));
412 obj->previous = NULL;
413 obj->next = *list;
414 if (*list != NULL)
415 (*list)->previous = obj;
416 *list = obj;
417 return obj;
421 * Removes an object from a list. It is assumed that the object
422 * is in the list
424 static void
425 _object_remove(ObjectList list, ObjectPtr obj) {
426 if (obj->next != NULL)
427 obj->next->previous = obj->previous;
429 if (obj->previous != NULL)
430 obj->previous->next = obj->next;
431 else
432 *list = obj->next;
433 free(obj);
437 * Init the button map device. We only use one button.
439 static int
440 _tuio_init_buttons(DeviceIntPtr device)
442 InputInfoPtr pInfo = device->public.devicePrivate;
443 CARD8 *map;
444 Atom *labels;
445 const int num_buttons = 1; /* left-click */
446 int ret = Success;
447 int i;
449 map = xcalloc(num_buttons, sizeof(CARD8));
450 labels = xcalloc(num_buttons, sizeof(Atom));
452 for (i = 0; i < num_buttons; i++)
453 map[i] = i;
455 if (!InitButtonClassDeviceStruct(device, num_buttons,
456 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
457 labels,
458 #endif
459 map)) {
460 xf86Msg(X_ERROR, "%s: Failed to register buttons.\n", pInfo->name);
461 ret = BadAlloc;
464 xfree(labels);
465 xfree(map);
466 return ret;
470 * Init valuators for device, use x/y coordinates.
472 static int
473 _tuio_init_axes(DeviceIntPtr device)
475 InputInfoPtr pInfo = device->public.devicePrivate;
476 int i;
477 const int num_axes = 2;
478 Atom *atoms;
480 atoms = xcalloc(2, sizeof(Atom));
482 if (!InitValuatorClassDeviceStruct(device,
483 num_axes,
484 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
485 atoms,
486 #endif
487 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
488 GetMotionHistory,
489 #endif
490 GetMotionHistorySize(),
492 return BadAlloc;
494 /* Use absolute mode. Currently, TUIO coords are mapped to the
495 * full screen area */
496 pInfo->dev->valuator->mode = Absolute;
497 if (!InitAbsoluteClassDeviceStruct(device))
498 return BadAlloc;
500 for (i = 0; i < num_axes; i++)
502 xf86InitValuatorAxisStruct(device, i,
503 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
504 atoms[i],
505 #endif
506 0, 0xFFFFFFFF, 1, 1, 1);
507 xf86InitValuatorDefaults(device, i);
509 return Success;