protected override void processData(byte[] dataChunk)
        {
            /*  the expected data here is ..
             * 1 byte = total touches
             *
             * 1 byte = touch id
             * 2 bytes = touch x
             * 2 bytes = touch y
             *
             *  1 byte = touch id for next touch  (optional)
             *  ... etc
             *
             * */

            if (dataChunk == emptyByte)
            {
                return;
            }

            uint touchScreenId = dataChunk[0]; //if a device has multiple screens, this is how we distinguish them. (this also tells us the orientation of the given screen)
            uint totalTouches  = dataChunk[1];

            if (dataChunk.Length != (totalTouches * 5) + 2) //unexpected chunk length! Assume it is corrupted, and dump it.
            {
                return;
            }

            //assume no one is touched.
            //this code assumes that touches for all touch screens are sent together during every frame (not a Unity frame, but a hardware frame).
            //as opposed to touch panel A sends something frame 1, then panel B sends it's own thing the next frame.  This will not work.
            for (int t = 0; t < touchScreens.Length; t++)
            {
                if (touchScreens[t].active)
                {
                    touchScreens[t].prepareNextFrame();
                }
            }

            rawTouchData d = new rawTouchData();

            for (int i = 2; i < dataChunk.Length; i = i + 5) //start at 1 and jump 5 each time.
            {
                d.id = dataChunk[i];
                d.o  = (touchScreenOrientation)touchScreenId;
                d.iX = System.BitConverter.ToUInt16(dataChunk, i + 1);
                d.iY = System.BitConverter.ToUInt16(dataChunk, i + 3);
                d.x  = (float)d.iX;
                d.y  = (float)d.iY;

                touchScreens[touchScreenId]._interface(d);
            }
        }
        //this is the method through which the touchScreenInputManager injects data into the touchScreen object
        public void _interface(rawTouchData d)
        {
            if (!active)
            {
                active = true; //if we get touch events, automatically activate this touch screen. (it obviously exists on this device)
            }
            //sometimes the hardware sends us funky data.
            //if the stats are funky, throw it out.
            if (d.id == 0 || d.id >= maxTouches)
            {
                return;
            }
            if (d.x < 0 || d.x > screenResX)
            {
                return;
            }
            if (d.y < 0 || d.y > screenResY)
            {
                return;
            }

            //is this a new touch?  If so, assign it to a new item in the pool, and update our iterators.
            if (touchPool[touchIdMap[d.id]].state < touch.activationState.ACTIVE) //a new touch!  Point it to a new element in our touchPool  (we know it is new because the place where the itr is pointing to is deactivated. Hence, it must have gone through at least 1 frame where no touch info was received for it.)
            {
                //we can not allow either key duplicates or value duplicates in the map.
                //key duplicates are already handled because an array index is by definition unique.
                //however here, we have to make sure another id is not already pointing to our desired touchPoolItr.
                for (int k = 0; k < maxTouches; k++)
                {
                    if (touchIdMap[k] == touchPoolItr && k != d.id)
                    {
                        touchIdMap[k] = 0; //point it to our always defunct element.  Without this, we can cause our touchCount to be incorrect.
                    }
                }

                touchIdMap[d.id] = touchPoolItr; //point the id to the current iterator

                currentTouchID++;
                interfaces[touchIdMap[d.id]]._id = currentTouchID; //this id, is purely for external convenience and does not affect our functions here.

                touchPoolItr++;
                if (touchPoolItr >= touchPoolSize)
                {
                    touchPoolItr = 1;  //we rely on element 0 always being inactive so we can use it to fix any duplicates.
                }
            }

            interfaces[touchIdMap[d.id]].active = true;

            interfaces[touchIdMap[d.id]].rawTouchScreenX = (int)d.iX;
            interfaces[touchIdMap[d.id]].rawTouchScreenY = (int)d.iY;

            //interfaces[touchIdMap[d.id]].orientation = d.o; //already set.

            //set the normalized x/y to the touchscreen limits
            mapToRange(d.x / screenResX, d.y / screenResY, topLimit, rightLimit, bottomLimit, leftLimit,
                       out interfaces[touchIdMap[d.id]].normalizedPos.x, out interfaces[touchIdMap[d.id]].normalizedPos.y);

            interfaces[touchIdMap[d.id]].physicalPos.x = (d.x / screenResX) * touchScreenWidth;
            interfaces[touchIdMap[d.id]].physicalPos.y = (d.y / screenResY) * touchScreenHeight;
        }