Emulate Touch Event in Windows 8 using Python

Question:

I am trying to write a sort of driver using python for windows 8. I will be receiving touch coordinates via serial which I then want to use to make it appear as though someone touched those coordinates on the screen (emulate a touch).

My first question is:

Is a touch event in Windows 8 different from just a mouse click at the area? (I know they revamped everything for touch events but am unsure of what that involved — possibly like an area of effect thing to make it more touch screen friendly?)

Secondly:

If so, is there a library for python to emulate a ‘touch’ at a coordinate instead of a ‘click’?

UPDATE:

Using ctypes and the page linked in the comments, I have created this:

from ctypes import *

#Constants

#For touchMask
TOUCH_MASK_NONE=          0x00000000 #Default
TOUCH_MASK_CONTACTAREA=   0x00000001
TOUCH_MASK_ORIENTATION=   0x00000002
TOUCH_MASK_PRESSURE=      0x00000004
TOUCH_MASK_ALL=           0x00000007

#For touchFlag
TOUCH_FLAG_NONE=          0x00000000

#For pointerType
PT_POINTER=               0x00000001#All
PT_TOUCH=                 0x00000002
PT_PEN=                   0x00000003
PT_MOUSE=                 0x00000004

#For pointerFlags
POINTER_FLAG_NONE=        0x00000000#Default
POINTER_FLAG_NEW=         0x00000001
POINTER_FLAG_INRANGE=     0x00000002
POINTER_FLAG_INCONTACT=   0x00000004
POINTER_FLAG_FIRSTBUTTON= 0x00000010
POINTER_FLAG_SECONDBUTTON=0x00000020
POINTER_FLAG_THIRDBUTTON= 0x00000040
POINTER_FLAG_FOURTHBUTTON=0x00000080
POINTER_FLAG_FIFTHBUTTON= 0x00000100
POINTER_FLAG_PRIMARY=     0x00002000
POINTER_FLAG_CONFIDENCE=  0x00004000
POINTER_FLAG_CANCELED=    0x00008000
POINTER_FLAG_DOWN=        0x00010000
POINTER_FLAG_UPDATE=      0x00020000
POINTER_FLAG_UP=          0x00040000
POINTER_FLAG_WHEEL=       0x00080000
POINTER_FLAG_HWHEEL=      0x00100000
POINTER_FLAG_CAPTURECHANGED=0x00200000


#Structs Needed

class POINT(Structure):
    _fields_=[("x", c_long),
              ("y", c_long)]

class POINTER_INFO(Structure):
    _fields_=[("pointerType",c_int32),
              ("pointerId",c_uint32),
              ("frameId",c_uint32),
              ("pointerFlags",c_int),
              ("sourceDevice",c_uint32),
              ("hwndTarget",c_uint32),
              ("ptPixelLocation",POINT),
              ("ptHimetricLocation",POINT),
              ("ptPixelLocationRaw",POINT),
              ("ptHimetricLocationRaw",POINT),
              ("dwTime",c_uint32),
              ("historyCount",c_uint32),
              ("inputData",c_int32),
              ("dwKeyStates",c_uint32),
              ("PerformanceCount",c_uint64),
              ("ButtonChangeType",c_int)
              ]

class RECT(Structure):
    _fields_=[("left",c_long),
              ("top",c_long),
              ("right",c_long),
              ("bottom",c_long)]

class POINTER_TOUCH_INFO(Structure):
    _fields_=[("pointerInfo",POINTER_INFO),
              ("touchFlags",c_int),
              ("touchMask",c_int),
              ("rcContact", RECT),
              ("rcContactRaw",RECT),
              ("orientation", c_uint32),
              ("pressure", c_uint32)]



#Initialize Touch Injection

pointerInfo=POINTER_INFO(pointerType=PT_TOUCH,
                         pointerId=0,
                         ptPixelLocation=POINT(950,540))

touchInfo=POINTER_TOUCH_INFO(pointerInfo=pointerInfo,
                             touchFlags=TOUCH_FLAG_NONE,
                             touchMask=TOUCH_MASK_ALL,
                             rcContact=RECT(pointerInfo.ptPixelLocation.x-5,
                                  pointerInfo.ptPixelLocation.y-5,
                                  pointerInfo.ptPixelLocation.x+5,
                                  pointerInfo.ptPixelLocation.y+5),
                             orientation=90,
                             pressure=32000)


if (windll.user32.InitializeTouchInjection(1,1) != 0):
    print "Initialized Touch Injection"
#Press Down
touchInfo.pointerInfo.pointerFlags=(POINTER_FLAG_DOWN|
                                    POINTER_FLAG_INRANGE|
                                    POINTER_FLAG_INCONTACT)
if (windll.user32.InjectTouchInput(1, byref(touchInfo))==0):
    print "Failed with Error: "+ FormatError()

else:
    print "Touch Down Succeeded!"

#Pull Up
touchInfo.pointerInfo.pointerFlags=POINTER_FLAG_UP
if (windll.user32.InjectTouchInput(1,byref(touchInfo))==0):
    print "Failed with Error: "+FormatError()

else:
    print "Pull Up Succeeded!"

Fails everytime with error about the input parameters.
I’ve gone through every reference and can’t find a type that seems incorrect. Does anyone see something obvious?

Thanks

Asked By: Legault

||

Answers:

Thanks to Eryksum and Xyroid, I was able to get it working. Thanks for putting up with my C-type / Windows ignorance. Here is the final script with the touch emulation packaged as a function (extra constants as well):

from ctypes import *
from ctypes.wintypes import *

#Constants

#For touchMask
TOUCH_MASK_NONE=          0x00000000 #Default
TOUCH_MASK_CONTACTAREA=   0x00000001
TOUCH_MASK_ORIENTATION=   0x00000002
TOUCH_MASK_PRESSURE=      0x00000004
TOUCH_MASK_ALL=           0x00000007

#For touchFlag
TOUCH_FLAG_NONE=          0x00000000

#For pointerType
PT_POINTER=               0x00000001#All
PT_TOUCH=                 0x00000002
PT_PEN=                   0x00000003
PT_MOUSE=                 0x00000004

#For pointerFlags
POINTER_FLAG_NONE=        0x00000000#Default
POINTER_FLAG_NEW=         0x00000001
POINTER_FLAG_INRANGE=     0x00000002
POINTER_FLAG_INCONTACT=   0x00000004
POINTER_FLAG_FIRSTBUTTON= 0x00000010
POINTER_FLAG_SECONDBUTTON=0x00000020
POINTER_FLAG_THIRDBUTTON= 0x00000040
POINTER_FLAG_FOURTHBUTTON=0x00000080
POINTER_FLAG_FIFTHBUTTON= 0x00000100
POINTER_FLAG_PRIMARY=     0x00002000
POINTER_FLAG_CONFIDENCE=  0x00004000
POINTER_FLAG_CANCELED=    0x00008000
POINTER_FLAG_DOWN=        0x00010000
POINTER_FLAG_UPDATE=      0x00020000
POINTER_FLAG_UP=          0x00040000
POINTER_FLAG_WHEEL=       0x00080000
POINTER_FLAG_HWHEEL=      0x00100000
POINTER_FLAG_CAPTURECHANGED=0x00200000


#Structs Needed

class POINTER_INFO(Structure):
    _fields_=[("pointerType",c_uint32),
              ("pointerId",c_uint32),
              ("frameId",c_uint32),
              ("pointerFlags",c_int),
              ("sourceDevice",HANDLE),
              ("hwndTarget",HWND),
              ("ptPixelLocation",POINT),
              ("ptHimetricLocation",POINT),
              ("ptPixelLocationRaw",POINT),
              ("ptHimetricLocationRaw",POINT),
              ("dwTime",DWORD),
              ("historyCount",c_uint32),
              ("inputData",c_int32),
              ("dwKeyStates",DWORD),
              ("PerformanceCount",c_uint64),
              ("ButtonChangeType",c_int)
              ]


class POINTER_TOUCH_INFO(Structure):
    _fields_=[("pointerInfo",POINTER_INFO),
              ("touchFlags",c_int),
              ("touchMask",c_int),
              ("rcContact", RECT),
              ("rcContactRaw",RECT),
              ("orientation", c_uint32),
              ("pressure", c_uint32)]



#Initialize Pointer and Touch info

pointerInfo=POINTER_INFO(pointerType=PT_TOUCH,
                         pointerId=0,
                         ptPixelLocation=POINT(950,540))

touchInfo=POINTER_TOUCH_INFO(pointerInfo=pointerInfo,
                             touchFlags=TOUCH_FLAG_NONE,
                             touchMask=TOUCH_MASK_ALL,
                             rcContact=RECT(pointerInfo.ptPixelLocation.x-5,
                                  pointerInfo.ptPixelLocation.y-5,
                                  pointerInfo.ptPixelLocation.x+5,
                                  pointerInfo.ptPixelLocation.y+5),
                             orientation=90,
                             pressure=32000)


def makeTouch(x,y,fingerRadius):
    touchInfo.pointerInfo.ptPixelLocation.x=x
    touchInfo.pointerInfo.ptPixelLocation.y=y

    touchInfo.rcContact.left=x-fingerRadius
    touchInfo.rcContact.right=x+fingerRadius
    touchInfo.rcContact.top=y-fingerRadius
    touchInfo.rcContact.bottom=y+fingerRadius

    #Initialize Touch Injection
    if (windll.user32.InitializeTouchInjection(1,1) != 0):
        print "Initialized Touch Injection"

    #Press Down
    touchInfo.pointerInfo.pointerFlags=(POINTER_FLAG_DOWN|
                                        POINTER_FLAG_INRANGE|
                                        POINTER_FLAG_INCONTACT)

    if (windll.user32.InjectTouchInput(1, byref(touchInfo))==0):
        print "Failed with Error: "+ FormatError()

    else:
        print "Touch Down Succeeded!"

    #Pull Up
    touchInfo.pointerInfo.pointerFlags=POINTER_FLAG_UP

    if (windll.user32.InjectTouchInput(1,byref(touchInfo))==0):
        print "Failed with Error: "+FormatError()

    else:
        print "Pull Up Succeeded!"

    return

#Ex:
#makeTouch(950,270,5)
Answered By: Legault

For future multitouch travelers, the above example needs some significant massaging to create multiple touch events with a proper lifetime.

This includes maintaining persistent POINTER_TOUCH_INFO arrays, setting their ID’s appropriately, and properly calling DOWN, UPDATE, and UP events as needed.

Hopefully this addition to the example script will save others time:

from ctypes import *
from ctypes.wintypes import *

# Constants
# For touchMask
TOUCH_MASK_NONE=          0x00000000 # Default
TOUCH_MASK_CONTACTAREA=   0x00000001
TOUCH_MASK_ORIENTATION=   0x00000002
TOUCH_MASK_PRESSURE=      0x00000004
TOUCH_MASK_ALL=           0x00000007

# For touchFlag
TOUCH_FLAG_NONE=          0x00000000

# For pointerType
PT_POINTER=               0x00000001 # All
PT_TOUCH=                 0x00000002
PT_PEN=                   0x00000003
PT_MOUSE=                 0x00000004

#For pointerFlags
POINTER_FLAG_NONE=        0x00000000 # Default
POINTER_FLAG_NEW=         0x00000001
POINTER_FLAG_INRANGE=     0x00000002
POINTER_FLAG_INCONTACT=   0x00000004
POINTER_FLAG_FIRSTBUTTON= 0x00000010
POINTER_FLAG_SECONDBUTTON=0x00000020
POINTER_FLAG_THIRDBUTTON= 0x00000040
POINTER_FLAG_FOURTHBUTTON=0x00000080
POINTER_FLAG_FIFTHBUTTON= 0x00000100
POINTER_FLAG_PRIMARY=     0x00002000
POINTER_FLAG_CONFIDENCE=  0x00004000
POINTER_FLAG_CANCELED=    0x00008000
POINTER_FLAG_DOWN=        0x00010000
POINTER_FLAG_UPDATE=      0x00020000
POINTER_FLAG_UP=          0x00040000
POINTER_FLAG_WHEEL=       0x00080000
POINTER_FLAG_HWHEEL=      0x00100000
POINTER_FLAG_CAPTURECHANGED=0x00200000

# Structs Needed
class POINTER_INFO(Structure):
    _fields_=[("pointerType",c_uint32),
              ("pointerId",c_uint32),
              ("frameId",c_uint32),
              ("pointerFlags",c_int),
              ("sourceDevice",HANDLE),
              ("hwndTarget",HWND),
              ("ptPixelLocation",POINT),
              ("ptHimetricLocation",POINT),
              ("ptPixelLocationRaw",POINT),
              ("ptHimetricLocationRaw",POINT),
              ("dwTime",DWORD),
              ("historyCount",c_uint32),
              ("inputData",c_int32),
              ("dwKeyStates",DWORD),
              ("PerformanceCount",c_uint64),
              ("ButtonChangeType",c_int)
              ]

class POINTER_TOUCH_INFO(Structure):
    _fields_=[("pointerInfo",POINTER_INFO),
              ("touchFlags",c_int),
              ("touchMask",c_int),
              ("rcContact", RECT),
              ("rcContactRaw",RECT),
              ("orientation", c_uint32),
              ("pressure", c_uint32)]

# Initialize Pointer and Touch info
def initialize(maxtouches=10, dwmode=1):
    global touches
    touches = (POINTER_TOUCH_INFO * maxtouches)()

    for ind in range(maxtouches):
        pointerInfo=POINTER_INFO(pointerType     = PT_TOUCH,
                                 pointerId       = ind,
                                 ptPixelLocation = POINT(950,540),
                                 pointerFlags    = POINTER_FLAG_NEW)
        touchInfo=POINTER_TOUCH_INFO(pointerInfo = pointerInfo,
                                    touchFlags   = TOUCH_FLAG_NONE,
                                    touchMask    = TOUCH_MASK_ALL,
                                    rcContact    = RECT(pointerInfo.ptPixelLocation.x-5,
                                                        pointerInfo.ptPixelLocation.y-5,
                                                        pointerInfo.ptPixelLocation.x+5,
                                                        pointerInfo.ptPixelLocation.y+5),
                                    orientation  = 90,
                                    pressure     = 32000)
        touches[ind] = touchInfo

    if (windll.user32.InitializeTouchInjection(len(touches), 1) != 0):
        print("Initialized Touch Injection")

ids_to_update  = 0
currently_down = None
def updateTouchInfo(id, down, x = 0, y = 0, fingerRadius=1, orientation = 90, pressure = 32000):
    global currently_down, ids_to_update
    if currently_down == None or len(currently_down) != len(touches):
        currently_down = [False] * len(touches)

    if down:
        touches[id].pointerInfo.pointerFlags = (((POINTER_FLAG_DOWN) if not currently_down[id] else POINTER_FLAG_UPDATE)|
                                                  POINTER_FLAG_INRANGE |
                                                  POINTER_FLAG_INCONTACT)
        touches[id].orientation                   = orientation
        touches[id].pressure                      = pressure
        touches[id].pointerInfo.ptPixelLocation.x = x
        touches[id].pointerInfo.ptPixelLocation.y = y
        touches[id].rcContact.left                = x - fingerRadius
        touches[id].rcContact.right               = x + fingerRadius
        touches[id].rcContact.top                 = y - fingerRadius
        touches[id].rcContact.bottom              = y + fingerRadius
    else:
        touches[id].pointerInfo.pointerFlags = POINTER_FLAG_UP #if currently_down[id] else POINTER_FLAG_UPDATE #if currently_down[id] else POINTER_FLAG_UPDATE
    ids_to_update     += 1 if down or currently_down[id] else 0
    currently_down[id] = down

def applyTouches():
    global ids_to_update
    if ids_to_update > 0:
        if (windll.user32.InjectTouchInput(int(ids_to_update), byref(touches[0])) == 0):
            print("Failed trying to update", ids_to_update, "points with Error:", FormatError())
    ids_to_update = 0


# Use like:
#import multitouch       # Call once
#multitouch.initialize() # Call once
#for i in range(10):     # Call every frame
#    if i < len(keypoints):
#        multitouch.updateTouchInfo(i, True,
#            int(keypoints[i].pt[0] * 3),
#            int(keypoints[i].pt[1] * 3),
#            int(keypoints[i].size  / 2))
#    else:
#        multitouch.updateTouchInfo(i, False)
#multitouch.applyTouches()
Answered By: zalo