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 static TuioDevicePtr g_dev_ptr
;
41 /* Module Functions */
43 TuioPlug(pointer
, pointer
, int *, int *);
48 /* Driver Functions */
50 TuioPreInit(InputDriverPtr
, IDevPtr
, int);
53 TuioUnInit(InputDriverPtr
, InputInfoPtr
, int);
56 TuioObjReadInput(InputInfoPtr pInfo
);
59 TuioReadInput(InputInfoPtr
);
62 TuioControl(DeviceIntPtr
, int);
64 /* Internal Functions */
66 _tuio_init_buttons(DeviceIntPtr device
);
69 _tuio_init_axes(DeviceIntPtr device
);
72 _tuio_lo_cur2d_handle(const char *path
,
85 _object_new(ObjectPtr obj
, InputInfoPtr pInfo
);
87 /* Object list manipulation functions */
89 _object_get(ObjectPtr head
, int id
);
92 _object_insert(ObjectPtr head
);
95 _object_remove(ObjectPtr head
, int id
);
97 /* Driver information */
98 static XF86ModuleVersionInfo TuioVersionRec
=
104 XORG_VERSION_CURRENT
,
105 PACKAGE_VERSION_MAJOR
, PACKAGE_VERSION_MINOR
, PACKAGE_VERSION_PATCHLEVEL
,
112 _X_EXPORT InputDriverRec TUIO
=
123 _X_EXPORT XF86ModuleData tuioModuleData
=
131 TuioPlug(pointer module
,
136 xf86AddInputDriver(&TUIO
, module
, 0);
144 TuioUnplug(pointer p
)
149 * Pre-initialization of new device
152 * - Parse configuration options
155 TuioPreInit(InputDriverPtr drv
,
165 if (!(pInfo
= xf86AllocateInput(drv
, 0)))
168 /* The TuioDevicePtr will hold object and other
170 if (!(pTuio
= xcalloc(1, sizeof(TuioDeviceRec
)))) {
171 xf86DeleteInput(pInfo
, 0);
175 if (!(pTuio
->list_head
= xcalloc(1, sizeof(ObjectRec
)))) {
177 xf86DeleteInput(pInfo
, 0);
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
);
189 /* Find object this device is linked to */
190 id
= xf86CheckIntOption(dev
->commonOptions
, "Id", -1);
192 xf86Msg(X_ERROR
, "%s: blob id option was not set\n", dev
->identifier
);
193 _free_tuiodev(pTuio
);
194 xf86DeleteInput(pInfo
, 0);
198 obj
= _object_get(g_dev_ptr
->list_head
, id
);
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);
212 pInfo
->private = pTuio
;
213 if (!pTuio
->isObject
)
216 /* Set up InputInfoPtr */
217 pInfo
->name
= xstrdup(dev
->identifier
);
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
;
239 TuioUnInit(InputDriverPtr drv
,
243 TuioDevicePtr pTuio
= pInfo
->private;
246 xf86DeleteInput(pInfo
, 0);
250 TuioObjReadInput(InputInfoPtr pInfo
) {
255 * Handle new data on the socket
258 TuioReadInput(InputInfoPtr pInfo
)
260 TuioDevicePtr pTuio
= pInfo
->private;
261 ObjectPtr head
= pTuio
->list_head
;
262 ObjectPtr obj
= head
->next
;
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
) {
286 _object_remove(head
, obj
->id
);
288 xf86PostButtonEvent(pInfo
->dev
, TRUE
, 1, FALSE
, 0, 0);
290 if (obj
->pending
.set
) {
291 obj
->x
= obj
->pending
.x
;
292 obj
->y
= obj
->pending
.y
;
293 obj
->pending
.set
= False
;
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 */
307 obj
->alive
= 0; /* Reset for next message */
317 * Handle device state changes
320 TuioControl(DeviceIntPtr device
,
323 InputInfoPtr pInfo
= device
->public.devicePrivate
;
324 TuioDevicePtr pTuio
= pInfo
->private;
329 xf86Msg(X_INFO
, "%s: Init\n", pInfo
->name
);
330 _tuio_init_buttons(device
);
331 _tuio_init_axes(device
);
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! */
339 if (pTuio
->isObject
) {
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",
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
;
365 xf86Msg(X_INFO
, "%s: Off\n", pInfo
->name
);
366 if (!device
->public.on
)
369 xf86RemoveEnabledDevice(pInfo
);
371 lo_server_free(pTuio
->server
);
373 device
->public.on
= FALSE
;
377 xf86Msg(X_INFO
, "%s: Close\n", pInfo
->name
);
379 _free_tuiodev(pTuio
);
387 * Initialize the device properties
392 TuioObjPostAnalyze() {
397 * Free a TuioDeviceRec
399 _free_tuiodev(TuioDevicePtr pTuio
) {
400 ObjectPtr obj
= pTuio
->list_head
;
403 while (obj
!= NULL
) {
413 * Handles OSC messages in the /tuio/2Dcur address space
416 _tuio_lo_cur2d_handle(const char *path
,
422 InputInfoPtr pInfo
= user_data
;
423 TuioDevicePtr pTuio
= pInfo
->private;
424 ObjectPtr head
= pTuio
->list_head
;
425 ObjectPtr obj
, objtemp
;
430 xf86Msg(X_ERROR
, "%s: Error in /tuio/cur2d (argc == 0)\n",
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",
449 obj
= _object_get(head
, argv
[1]->i
);
451 /* If not found, create a new object */
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) {
466 while (obj
!= NULL
) {
467 for (i
=1; i
<argc
; i
++) {
468 if (argv
[i
]->i
== obj
->id
) {
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",
483 pTuio
->fseq_old
= pTuio
->fseq_new
;
484 pTuio
->fseq_new
= argv
[1]->i
;
491 * liblo error handler
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
509 _object_new(ObjectPtr obj
, InputInfoPtr pInfo
) {
511 DBusConnection
*conn
;
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
);
529 if ((ctx
= libhal_ctx_new()) == NULL
) {
530 xf86Msg(X_ERROR
, "%s: Failed to obtain hal context: %s\n",
531 pInfo
->name
, error
.message
);
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
);
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
);
552 dbus_error_init(&error
);
553 libhal_device_set_property_string(ctx
, newdev
, "input.device",
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
);
561 dbus_error_init(&error
);
562 libhal_device_set_property_string(ctx
, newdev
,
563 "input.x11_driver", "tuio",
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 dbus_error_init(&error
);
572 libhal_device_set_property_string(ctx
, newdev
,
573 "info.product", name
,
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
);
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",
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
);
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",
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
);
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
);
617 * Retrieves an object from a list based on its id.
619 * @returns NULL if not found.
622 _object_get(ObjectPtr head
, int id
) {
623 ObjectPtr obj
= head
->next
;
625 while (obj
!= NULL
&& obj
->id
!= id
) {
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
641 _object_insert(ObjectPtr head
) {
642 ObjectPtr obj
= xcalloc(1, sizeof(ObjectRec
));
643 obj
->next
= head
->next
;
649 * Removes an object from a list. It is assumed that the object
653 _object_remove(ObjectPtr head
, int id
) {
654 ObjectPtr obj
= head
;
657 while (obj
->next
!= NULL
) {
658 if (obj
->next
->id
== id
) {
660 obj
->next
= objtmp
->next
;
669 * Init the button map device. We only use one button.
672 _tuio_init_buttons(DeviceIntPtr device
)
674 InputInfoPtr pInfo
= device
->public.devicePrivate
;
681 labels
= xcalloc(1, sizeof(Atom
));
683 if (!InitButtonClassDeviceStruct(device
, 1 /* 1 button */,
684 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
688 xf86Msg(X_ERROR
, "%s: Failed to register buttons.\n", pInfo
->name
);
697 * Init valuators for device, use x/y coordinates.
700 _tuio_init_axes(DeviceIntPtr device
)
702 InputInfoPtr pInfo
= device
->public.devicePrivate
;
704 const int num_axes
= 2;
707 atoms
= xcalloc(num_axes
, sizeof(Atom
));
709 if (!InitValuatorClassDeviceStruct(device
,
711 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
714 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
717 GetMotionHistorySize(),
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
))
727 for (i
= 0; i
< num_axes
; i
++)
729 xf86InitValuatorAxisStruct(device
, i
,
730 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
733 0, 0x7FFFFFFF, 1, 1, 1);
734 xf86InitValuatorDefaults(device
, i
);