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
;
41 static int pipefd
[2] = {-1, -1};
43 /* Module Functions */
45 TuioPlug(pointer
, pointer
, int *, int *);
50 /* Driver Functions */
52 TuioPreInit(InputDriverPtr
, IDevPtr
, int);
55 TuioUnInit(InputDriverPtr
, InputInfoPtr
, int);
58 TuioObjReadInput(InputInfoPtr pInfo
);
61 TuioReadInput(InputInfoPtr
);
64 TuioControl(DeviceIntPtr
, int);
66 /* Internal Functions */
68 _init_devices(InputInfoPtr pInfo
, int num
);
71 _tuio_init_buttons(DeviceIntPtr device
);
74 _tuio_init_axes(DeviceIntPtr device
);
77 _tuio_lo_cur2d_handle(const char *path
,
85 _free_tuiodev(TuioDevicePtr pTuio
);
92 /* Object list manipulation functions */
94 _object_get(ObjectPtr head
, int id
);
100 _object_add(ObjectPtr
*obj_list
, ObjectPtr obj
);
103 _object_remove(ObjectPtr
*obj_list
, int id
);
106 _subdev_add(SubDevicePtr
*subdev_list
, SubDevicePtr subdev
);
109 _subdev_remove(SubDevicePtr
*subdev_list
);
112 _subdev_remove_sd(SubDevicePtr
*subdev_list
, SubDevicePtr subdev
);
114 /* Driver information */
115 static XF86ModuleVersionInfo TuioVersionRec
=
121 XORG_VERSION_CURRENT
,
122 PACKAGE_VERSION_MAJOR
, PACKAGE_VERSION_MINOR
, PACKAGE_VERSION_PATCHLEVEL
,
129 _X_EXPORT InputDriverRec TUIO
=
140 _X_EXPORT XF86ModuleData tuioModuleData
=
148 TuioPlug(pointer module
,
153 xf86AddInputDriver(&TUIO
, module
, 0);
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
172 TuioPreInit(InputDriverPtr drv
,
177 TuioDevicePtr pTuio
= NULL
;
182 int num_subdev
, tuio_port
;
184 if (!(pInfo
= xf86AllocateInput(drv
, 0)))
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
);
201 if (!(pTuio
= xcalloc(1, sizeof(TuioDeviceRec
)))) {
202 xf86DeleteInput(pInfo
, 0);
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
);
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
;
251 TuioUnInit(InputDriverPtr drv
,
255 xf86DeleteInput(pInfo
, 0);
259 * Empty callback for object device.
262 TuioObjReadInput(InputInfoPtr pInfo
) {
267 * Handle new TUIO data on the socket
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
;
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
) {
299 /* Post button "up" event */
300 xf86PostButtonEvent(obj
->subdev
->pInfo
->dev
, TRUE
, 1, FALSE
, 0, 0);
303 obj
= _object_remove(obj_list
, obj
->id
);
304 _subdev_add(subdev_list
, obj
->subdev
);
309 if (obj
->pending
.set
&& obj
->subdev
) {
310 obj
->x
= obj
->pending
.x
;
311 obj
->y
= obj
->pending
.y
;
312 obj
->pending
.set
= False
;
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 */
327 obj
->alive
= 0; /* Reset for next message */
331 pTuio
->fseq_old
= pTuio
->fseq_new
;
337 * Handle device state changes
340 TuioControl(DeviceIntPtr device
,
343 InputInfoPtr pInfo
= device
->public.devicePrivate
;
344 TuioDevicePtr pTuio
= pInfo
->private;
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 */
356 _init_devices(pInfo
, pTuio
->num_subdev
- 1);
360 case DEVICE_ON
: /* Open socket and start listening! */
361 xf86Msg(X_INFO
, "%s: On.\n", pInfo
->name
);
362 if (device
->public.on
)
365 /* If this is an object device, use a dummy pipe */
367 if (pipefd
[0] == -1) {
368 SYSCALL(res
= pipe(pipefd
));
370 xf86Msg(X_ERROR
, "%s: failed to open pipe\n",
376 pInfo
->fd
= pipefd
[0];
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",
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
;
402 xf86Msg(X_INFO
, "%s: Off\n", pInfo
->name
);
403 if (!device
->public.on
)
406 xf86RemoveEnabledDevice(pInfo
);
409 lo_server_free(pTuio
->server
);
413 device
->public.on
= FALSE
;
417 xf86Msg(X_INFO
, "%s: Close\n", pInfo
->name
);
425 * Initialize the device properties
432 * Free a TuioDeviceRec
435 _free_tuiodev(TuioDevicePtr pTuio
) {
436 ObjectPtr obj
= pTuio
->obj_list
;
439 while (obj
!= NULL
) {
449 * Handles OSC messages in the /tuio/2Dcur address space
452 _tuio_lo_cur2d_handle(const char *path
,
458 InputInfoPtr pInfo
= user_data
;
459 TuioDevicePtr pTuio
= pInfo
->private;
460 ObjectPtr
*obj_list
= &pTuio
->obj_list
;
461 ObjectPtr obj
, objtemp
;
466 xf86Msg(X_ERROR
, "%s: Error in /tuio/cur2d (argc == 0)\n",
469 } else if(*types
!= 's') {
470 xf86Msg(X_ERROR
, "%s: Error in /tuio/cur2d (types[0] != 's')\n",
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",
489 obj
= _object_get(*obj_list
, argv
[1]->i
);
491 /* If not found, create a new object */
493 obj
= _object_new(argv
[1]->i
);
494 _object_add(obj_list
, obj
);
495 obj
->subdev
= _subdev_remove(&pTuio
->subdev_list
);
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 */
508 while (obj
!= NULL
) {
509 for (i
=1; i
<argc
; i
++) {
510 if (argv
[i
]->i
== obj
->id
) {
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",
525 pTuio
->fseq_new
= argv
[1]->i
;
532 * liblo error handler
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.
548 _object_get(ObjectPtr head
, int id
) {
549 ObjectPtr obj
= head
;
551 while (obj
!= NULL
&& obj
->id
!= id
) {
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
567 _object_new(int id
) {
568 ObjectPtr new_obj
= xcalloc(1, sizeof(ObjectRec
));
571 new_obj
->alive
= True
;
577 * Adds a SubDevice to the beginning of the subdev_list list
580 _object_add(ObjectPtr
*obj_list
, ObjectPtr obj
) {
581 if (obj_list
== NULL
|| obj
== NULL
)
584 if (*obj_list
!= NULL
) {
585 obj
->next
= *obj_list
;
592 * Removes an Object with a specific id from a list.
595 _object_remove(ObjectPtr
*obj_list
, int id
) {
596 ObjectPtr obj
= *obj_list
;
599 if (obj
== NULL
) return; /* Empty list */
601 if (obj
->id
== id
) { /* Remove from head */
602 *obj_list
= obj
->next
;
604 while (obj
->next
!= NULL
) {
605 if (obj
->next
->id
== id
) {
607 obj
->next
= objtmp
->next
;
621 * Adds a SubDevice to the beginning of the subdev_list list
624 _subdev_add(SubDevicePtr
*subdev_list
, SubDevicePtr subdev
) {
625 if (subdev_list
== NULL
|| subdev
== NULL
)
628 if (*subdev_list
!= NULL
) {
629 subdev
->next
= *subdev_list
;
631 *subdev_list
= subdev
;
635 * Removes a SubDevice from the subdev_list list
638 _subdev_remove(SubDevicePtr
*subdev_list
) {
641 if (subdev_list
== NULL
|| *subdev_list
== NULL
)
644 subdev
= *subdev_list
;
645 *subdev_list
= subdev
->next
;
652 * Init the button map device. We only use one button.
655 _tuio_init_buttons(DeviceIntPtr device
)
657 InputInfoPtr pInfo
= device
->public.devicePrivate
;
664 map
= xcalloc(numbuttons
, sizeof(CARD8
));
665 labels
= xcalloc(1, sizeof(Atom
));
666 for (i
=0; i
<numbuttons
; i
++)
669 if (!InitButtonClassDeviceStruct(device
, numbuttons
/* 1 button */,
670 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
674 xf86Msg(X_ERROR
, "%s: Failed to register buttons.\n", pInfo
->name
);
683 * Init valuators for device, use x/y coordinates.
686 _tuio_init_axes(DeviceIntPtr device
)
688 InputInfoPtr pInfo
= device
->public.devicePrivate
;
690 const int num_axes
= 2;
693 atoms
= xcalloc(num_axes
, sizeof(Atom
));
695 if (!InitValuatorClassDeviceStruct(device
,
697 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
700 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
703 GetMotionHistorySize(),
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
))
713 for (i
= 0; i
< num_axes
; i
++)
715 xf86InitValuatorAxisStruct(device
, i
,
716 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
719 0, 0x7FFFFFFF, 1, 1, 1);
720 xf86InitValuatorDefaults(device
, i
);
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
733 _init_devices(InputInfoPtr pInfo
, int num
) {
735 DBusConnection
*conn
;
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
);
752 if ((ctx
= libhal_ctx_new()) == NULL
) {
753 xf86Msg(X_ERROR
, "%s: Failed to obtain hal context: %s\n",
754 pInfo
->name
, error
.message
);
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
);
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
);
777 dbus_error_init(&error
);
778 libhal_device_set_property_string(ctx
, newdev
, "input.device",
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
);
786 dbus_error_init(&error
);
787 libhal_device_set_property_string(ctx
, newdev
,
788 "input.x11_driver", "tuio",
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
);
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",
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
);
808 asprintf(&name
, "%s subdev %i", pInfo
->name
, i
);
811 dbus_error_init(&error
);
812 libhal_device_set_property_string(ctx
, newdev
,
813 "info.product", name
,
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
);
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
);
833 //libhal_context_free(ctx);