2 * Copyright (c) 2009 Ryan Huffman
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
23 * Ryan Huffman (ryanhuffman@gmail.com)
28 #include <xf86Xinput.h>
29 #include <X11/extensions/XIproto.h>
30 #include <X11/extensions/XInput2.h>
31 #include <xf86_OSlib.h>
39 /* InputInfoPtr for main tuio device */
40 static TuioDevicePtr g_pTuio
;
42 /* Module Functions */
44 TuioPlug(pointer
, pointer
, int *, int *);
49 /* Driver Functions */
51 TuioPreInit(InputDriverPtr
, IDevPtr
, int);
54 TuioUnInit(InputDriverPtr
, InputInfoPtr
, int);
57 TuioObjReadInput(InputInfoPtr pInfo
);
60 TuioReadInput(InputInfoPtr
);
63 TuioControl(DeviceIntPtr
, int);
65 /* Internal Functions */
67 _init_devices(InputInfoPtr pInfo
, int num
);
70 _tuio_init_buttons(DeviceIntPtr device
);
73 _tuio_init_axes(DeviceIntPtr device
);
76 _tuio_lo_cur2d_handle(const char *path
,
84 _free_tuiodev(TuioDevicePtr pTuio
);
91 /* Object list manipulation functions */
93 _object_get(ObjectPtr head
, int id
);
96 _object_new(TuioDevicePtr pTuio
, int id
);
99 _object_remove(TuioDevicePtr pTuio
, ObjectPtr
*head
, int id
);
101 /* Driver information */
102 static XF86ModuleVersionInfo TuioVersionRec
=
108 XORG_VERSION_CURRENT
,
109 PACKAGE_VERSION_MAJOR
, PACKAGE_VERSION_MINOR
, PACKAGE_VERSION_PATCHLEVEL
,
116 _X_EXPORT InputDriverRec TUIO
=
127 _X_EXPORT XF86ModuleData tuioModuleData
=
135 TuioPlug(pointer module
,
140 xf86AddInputDriver(&TUIO
, module
, 0);
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
159 TuioPreInit(InputDriverPtr drv
,
164 TuioDevicePtr pTuio
= NULL
;
170 if (!(pInfo
= xf86AllocateInput(drv
, 0)))
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
;
185 objdev
->next
= g_pTuio
->unused_device_list
;
186 g_pTuio
->unused_device_list
= objdev
;
188 objdev
->pInfo
= pInfo
;
192 if (!(pTuio
= xcalloc(1, sizeof(TuioDeviceRec
)))) {
193 xf86DeleteInput(pInfo
, 0);
198 pInfo
->private = pTuio
;
201 /* Set up InputInfoPtr */
202 pInfo
->name
= xstrdup(dev
->identifier
);
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
;
224 TuioUnInit(InputDriverPtr drv
,
228 xf86DeleteInput(pInfo
, 0);
232 * Empty callback for object device.
235 TuioObjReadInput(InputInfoPtr pInfo
) {
240 * Handle new TUIO data on the socket
243 TuioReadInput(InputInfoPtr pInfo
)
245 TuioDevicePtr pTuio
= pInfo
->private;
246 ObjectPtr
*head
= &pTuio
->obj_list
;
247 ObjectPtr obj
= pTuio
->obj_list
;
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
) {
270 xf86PostButtonEvent(obj
->objdev
->pInfo
->dev
, TRUE
, 1, FALSE
, 0, 0);
272 _object_remove(pTuio
, head
, obj
->id
);
275 if (obj
->pending
.set
&& obj
->objdev
) {
276 obj
->x
= obj
->pending
.x
;
277 obj
->y
= obj
->pending
.y
;
278 obj
->pending
.set
= False
;
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 */
292 obj
->alive
= 0; /* Reset for next message */
296 pTuio
->fseq_old
= pTuio
->fseq_new
;
302 * Handle device state changes
305 TuioControl(DeviceIntPtr device
,
308 InputInfoPtr pInfo
= device
->public.devicePrivate
;
309 TuioDevicePtr pTuio
= pInfo
->private;
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 */
320 _init_devices(pInfo
, 5);
323 case DEVICE_ON
: /* Open socket and start listening! */
324 xf86Msg(X_INFO
, "%s: On.\n", pInfo
->name
);
325 if (device
->public.on
)
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",
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
;
354 xf86Msg(X_INFO
, "%s: Off\n", pInfo
->name
);
355 if (!device
->public.on
)
358 xf86RemoveEnabledDevice(pInfo
);
361 lo_server_free(pTuio
->server
);
365 device
->public.on
= FALSE
;
369 xf86Msg(X_INFO
, "%s: Close\n", pInfo
->name
);
377 * Initialize the device properties
384 * Free a TuioDeviceRec
387 _free_tuiodev(TuioDevicePtr pTuio
) {
388 ObjectPtr obj
= pTuio
->obj_list
;
391 while (obj
!= NULL
) {
401 * Handles OSC messages in the /tuio/2Dcur address space
404 _tuio_lo_cur2d_handle(const char *path
,
410 InputInfoPtr pInfo
= user_data
;
411 TuioDevicePtr pTuio
= pInfo
->private;
412 ObjectPtr head
= pTuio
->obj_list
;
413 ObjectPtr obj
, objtemp
;
418 xf86Msg(X_ERROR
, "%s: Error in /tuio/cur2d (argc == 0)\n",
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",
437 obj
= _object_get(head
, argv
[1]->i
);
439 /* If not found, create a new object */
441 obj
= _object_new(pTuio
, argv
[1]->i
);
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 */
454 while (obj
!= NULL
) {
455 for (i
=1; i
<argc
; i
++) {
456 if (argv
[i
]->i
== obj
->id
) {
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",
471 pTuio
->fseq_new
= argv
[1]->i
;
478 * liblo error handler
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
496 _init_devices(InputInfoPtr pInfo
, int num
) {
498 DBusConnection
*conn
;
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
);
515 if ((ctx
= libhal_ctx_new()) == NULL
) {
516 xf86Msg(X_ERROR
, "%s: Failed to obtain hal context: %s\n",
517 pInfo
->name
, error
.message
);
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
);
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
);
540 dbus_error_init(&error
);
541 libhal_device_set_property_string(ctx
, newdev
, "input.device",
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
);
549 dbus_error_init(&error
);
550 libhal_device_set_property_string(ctx
, newdev
,
551 "input.x11_driver", "tuio",
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
);
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",
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
);
571 asprintf(&name
, "Tuio Obj (%s) %i", pInfo
->name
, i
);
574 dbus_error_init(&error
);
575 libhal_device_set_property_string(ctx
, newdev
,
576 "info.product", name
,
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
);
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
);
596 //libhal_context_free(ctx);
602 * Retrieves an object from a list based on its id.
604 * @returns NULL if not found.
607 _object_get(ObjectPtr head
, int id
) {
608 ObjectPtr obj
= head
;
610 while (obj
!= NULL
&& obj
->id
!= id
) {
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
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
));
634 pTuio
->obj_list
= new_obj
;
637 pTuio
->unused_device_list
= objdev
->next
;
638 new_obj
->objdev
= objdev
;
642 new_obj
->alive
= True
;
648 * Removes an object from a list.
651 _object_remove(TuioDevicePtr pTuio
, ObjectPtr
*head
, int id
) {
652 ObjectPtr obj
= *head
;
656 if (obj
!= NULL
&& obj
->id
== id
) {
657 *head
= (*head
)->next
;
659 objdev
= pTuio
->unused_device_list
;
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
) {
668 obj
->next
= objtmp
->next
;
670 if (objtmp
->objdev
) {
671 objdev
= pTuio
->unused_device_list
;
673 objtmp
->objdev
->next
= objdev
;
674 pTuio
->unused_device_list
= objtmp
->objdev
;
686 * Init the button map device. We only use one button.
689 _tuio_init_buttons(DeviceIntPtr device
)
691 InputInfoPtr pInfo
= device
->public.devicePrivate
;
698 map
= xcalloc(numbuttons
, sizeof(CARD8
));
699 labels
= xcalloc(1, sizeof(Atom
));
700 for (i
=0; i
<numbuttons
; i
++)
703 if (!InitButtonClassDeviceStruct(device
, numbuttons
/* 1 button */,
704 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
708 xf86Msg(X_ERROR
, "%s: Failed to register buttons.\n", pInfo
->name
);
717 * Init valuators for device, use x/y coordinates.
720 _tuio_init_axes(DeviceIntPtr device
)
722 InputInfoPtr pInfo
= device
->public.devicePrivate
;
724 const int num_axes
= 2;
727 atoms
= xcalloc(num_axes
, sizeof(Atom
));
729 if (!InitValuatorClassDeviceStruct(device
,
731 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
734 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
737 GetMotionHistorySize(),
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
))
747 for (i
= 0; i
< num_axes
; i
++)
749 xf86InitValuatorAxisStruct(device
, i
,
750 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
753 0, 0x7FFFFFFF, 1, 1, 1);
754 xf86InitValuatorDefaults(device
, i
);