LB Booster
Programming >> BASIC code examples >> Multi-touch demo
http://lbb.conforums.com/index.cgi?board=code&action=display&num=1459005073

Multi-touch demo
Post by Richard Russell on Mar 26th, 2016, 3:11pm

Over at the Liberty BASIC Yahoo! Group there's a thread about getting multiple touch entry. As is so often the case, the replies there (I suspect from people who don't actually have a touch screen themselves) seem to miss the point and don't address the real issues.

For those of you with a touch screen here is a solution in LBB (it can't be easily adapted to run in LB because it uses an array of structures - somebody prepared to spend enough time could no doubt find a workaround for LB). It detects up to six simultaneous touch points in real time and plots red disks under each one.

The program requires Windows 7 or later and uses the WMLiberty DLL (the download link, for those who don't already have it, is in the program).

Code:
    ' Multi-touch demo by Richard Russell, http://www.rtrussell.co.uk/
    ' For LB Booster (cannot easily be adapted to run in Liberty BASIC)
    ' Uses WMLiberty.DLL (http://www.b6sw.com/forum/download.php?id=16)

    nomainwin
    global WM.TOUCH, hWnd, nPoints
    WM.TOUCH = hexdec("0240")
    struct pt, x as long, y as long
    struct ti(5), x as long, y as long, hSource as handle, dwID as long, _
        dwFlags as long, dwMask as long, dwTime as long, _
        dwExtraInfo as long, cxContact as long, cyContact as long
    
    open "WMLiberty" for DLL As #wmlib
    WindowWidth = 800
    WindowHeight = 600
    open "Multi-touch demo" for graphics As #w 
    hWnd = hwnd(#w)
    #w "trapclose quit"
    #w "down; backcolor red" 
    callback lpwmtouch, wmtouch(long, long, long, ulong), long 
    calldll #wmlib, "SetWMHandler", hWnd as ulong, WM.TOUCH as long, _ 
        lpwmtouch as long, 1 as long, ret as long 
    calldll #user32, "RegisterTouchWindow", hWnd as ulong, 0 as long, _
        lpwmtouch as long, ret as long
        
    timer 40, plot
    wait

sub plot
    #w "cls; down"
    for i = 0 to nPoints-1
        pt.x.struct = ti(i).x.struct / 100
        pt.y.struct = ti(i).y.struct / 100
        calldll #user32, "ScreenToClient", hWnd as ulong, pt as struct, _
            ret as long
        #w "place ";pt.x.struct;" ";pt.y.struct
        #w "circlefilled 50"    
    next i
    nPoints = 0
end sub    
    
function wmtouch(hw, msg, wparam, lparam)
    nPoints = wparam and hexdec("FFFF")
    if nPoints > 6 then nPoints = 6
    size = len(ti(0).struct)
    calldll #user32, "GetTouchInputInfo", lparam as ulong, _
        nPoints as long, ti(0) as struct, size as long, ret as long
    calldll #user32, "CloseTouchInputHandle", lparam as ulong, ret as long
end function

sub quit handle$
    timer 0
    close #w
    close #wmlib
    end
end sub 

This version does not attempt to track which touch point is which (the information is available from Windows, but it's rather fiddly to use).

Richard.

Re: Multi-touch demo
Post by Rod on Mar 27th, 2016, 11:01am

I found this link, not sure if it allows the array of structs that would be required to make this work in LB. Not had time to properly read the article as yet.

http://lbpe.wikispaces.com/ArraysAndStructs
Re: Multi-touch demo
Post by Richard Russell on Mar 27th, 2016, 12:21pm

on Mar 27th, 2016, 11:01am, Rod wrote:
I found this link, not sure if it allows the array of structs that would be required to make this work in LB.

Yes, I've seen that technique used successfully before, even though it's rather messy.

Other things you would need to think about if attempting to adapt the program to run in LB4 would be:Richard.

*Edit: actually I think it will be OK.

Re: Multi-touch demo
Post by Rod on Mar 28th, 2016, 09:14am

I may be being naive, I wanted to try a single ti struct rather than an array hoping that a single touch would let me prove it could work on LB. I erased the timer and while it does not crash it fails to return touch info.

The registration works but the getTouchInputInfo always fails with return code 5 which I read is access denied. My API skills are not great.

Code:
    ' Multi-touch demo by Richard Russell, http://www.rtrussell.co.uk/
    ' For LB Booster (cannot easily be adapted to run in Liberty BASIC)
    ' Uses WMLiberty.DLL (http://www.b6sw.com/forum/download.php?id=16)

    'nomainwin
    global WM.TOUCH, hWnd, nPoints
    WM.TOUCH = hexdec("0240")
    struct pt, x as long, y as long
    struct ti, x as long, y as long, hSource as handle, dwID as long, _
        dwFlags as long, dwMask as long, dwTime as long, _
        dwExtraInfo as long, cxContact as long, cyContact as long

    open "WMLiberty" for DLL As #wmlib
    WindowWidth = 800
    WindowHeight = 600
    open "Multi-touch demo" for graphics As #w
    hWnd = hwnd(#w)
    #w "trapclose quit"
    #w "down; fill black ; backcolor red"
    callback lpwmtouch, wmtouch(long, long, long, ulong), long
    calldll #wmlib, "SetWMHandler", hWnd as ulong, WM.TOUCH as long, _
        lpwmtouch as long, 1 as long, ret as long
    calldll #user32, "RegisterTouchWindow", hWnd as ulong, 0 as long, _
        lpwmtouch as long, ret as long


    [wait] ' Must use a Scan loop.
     Scan
     call plot
     CallDLL #kernel32, "Sleep", 50 As Long, ret As Void
     GoTo [wait]




sub plot
    for i = 0 to nPoints-1
        'print ti.x.struct,ti.y.struct
        pt.x.struct = ti.x.struct / 100
        pt.y.struct = ti.y.struct / 100
        calldll #user32, "ScreenToClient", hWnd as ulong, pt as struct, _
            ret as long
        #w "place ";pt.x.struct;" ";pt.y.struct
        #w "circlefilled 50"
    next i
    nPoints = 0
end sub

sub quit h$
    timer 0
    close #wmlib
    close #w
    end
end sub

function wmtouch(hw, msg, wparam, lparam)
    nPoints = wparam and hexdec("FFFF")
    if nPoints > 6 then nPoints = 6
    size = len(ti.struct)
    calldll #user32, "GetTouchInputInfo", lparam as ulong, _
        nPoints as long, ti as struct, size as long, ret as long
            print "Touch returned ";ret;" No Points ";nPoints
            CallDLL #kernel32, "GetLastError",_
            GetLastError as long
            print GetLastError '5 Access Denied?
    calldll #user32, "CloseTouchInputHandle", lparam as ulong, ret as long
end function

 


The above code runs in LBB, crashes if more than one touch but single touches are returned and plotted.
Re: Multi-touch demo
Post by Richard Russell on Mar 28th, 2016, 1:14pm

on Mar 28th, 2016, 09:14am, Rod wrote:
The above code runs in LBB, crashes if more than one touch but single touches are returned and plotted.

You've not told Windows to return a maximum of one touch, so as soon as two or more points are touched it is writing into unallocated memory and crashing. You need to modify this line in the wmtouch function:

Code:
    if nPoints > 1 then nPoints = 1 

I did realise that I should have made the maximum number of touch points a global constant rather than embedding it in the code, but I was lazy and didn't bother. By way of contrition here's a version of my original with that change made:

Code:
    ' Multi-touch demo by Richard Russell, http://www.rtrussell.co.uk/
    ' For LB Booster (cannot easily be adapted to run in Liberty BASIC)
    ' Uses WMLiberty.DLL (http://www.b6sw.com/forum/download.php?id=16)

    nomainwin
    global MAX.POINTS, WM.TOUCH, hWnd, nPoints
    MAX.POINTS = 6
    WM.TOUCH = hexdec("0240")
    struct pt, x as long, y as long
    struct ti(MAX.POINTS-1), x as long, y as long, hSource as handle, _
        dwID as long, dwFlags as long, dwMask as long, dwTime as long, _
        dwExtraInfo as long, cxContact as long, cyContact as long
    
    open "WMLiberty" for DLL As #wmlib
    WindowWidth = 800
    WindowHeight = 600
    open "Multi-touch demo" for graphics As #w 
    hWnd = hwnd(#w)
    #w "trapclose quit"
    #w "down; backcolor red" 
    callback lpwmtouch, wmtouch(long, long, long, ulong), long 
    calldll #wmlib, "SetWMHandler", hWnd as ulong, WM.TOUCH as long, _ 
        lpwmtouch as long, 1 as long, ret as long 
    calldll #user32, "RegisterTouchWindow", hWnd as ulong, 0 as long, _
        lpwmtouch as long, ret as long
        
    timer 40, plot
    wait

sub plot
    #w "cls"
    for i = 0 to nPoints-1
        pt.x.struct = ti(i).x.struct / 100
        pt.y.struct = ti(i).y.struct / 100
        calldll #user32, "ScreenToClient", hWnd as ulong, pt as struct, _
            ret as long
        #w "place ";pt.x.struct;" ";pt.y.struct
        #w "circlefilled 50"    
    next i
    nPoints = 0
end sub    
    
function wmtouch(hw, msg, wparam, lparam)
    nPoints = wparam and hexdec("FFFF")
    if nPoints > MAX.POINTS then nPoints = MAX.POINTS
    size = len(ti(0).struct)
    calldll #user32, "GetTouchInputInfo", lparam as ulong, _
        nPoints as long, ti(0) as struct, size as long, ret as long
    calldll #user32, "CloseTouchInputHandle", lparam as ulong, ret as long
end function

sub quit handle$
    timer 0
    close #w
    close #wmlib
    end
end sub 

Richard.

Re: Multi-touch demo
Post by Richard Russell on Mar 28th, 2016, 1:32pm

on Mar 28th, 2016, 09:14am, Rod wrote:
The registration works but the getTouchInputInfo always fails with return code 5 which I read is access denied. My API skills are not great.

Ah, silly me, of course. I've used hSource as handle in the structure declaration, which is fine in LBB but in LB 4 the data type 'handle' is only 16 bits (why?). Change that to hSource as ulong and it will work.

Has Carl ever given an explanation for handle (and I think boolean as well) being 16-bits in LB 4? I presume they were 16-bits in the ancient Windows 3.1 versions of LB, but it doesn't make sense to me that they weren't changed to 32-bits when LB was ported to Win32 - and especially that they haven't been corrected in any of the many subsequent releases. shocked

Richard.

Re: Multi-touch demo
Post by Rod on Mar 28th, 2016, 3:40pm

Brilliant, that works for single touches. Never seen type handle used before so I should have been more inquisitive about that.

Now to play with creating an array of structs. I am wondering if I can just stuff a struct with a sequence of six structs rather than Denis's more complex routine.

I progress it because we recently had a blind user who was interested in braille input, so six touch points would be ideal.

Thanks for the support.
Re: Multi-touch demo
Post by Richard Russell on Mar 28th, 2016, 4:01pm

on Mar 28th, 2016, 3:40pm, Rod wrote:
I am wondering if I can just stuff a struct with a sequence of six structs rather than Denis's more complex routine.

You can, but you then lose the convenience of accessing the individual touch points by means of an array index (running from 0 to 5).

I am confident that Denis's code can be simplified. For example he allocates memory from Windows using GlobalAlloc followed by GlobalLock but that's an unnecessary complication: GlobalAlloc alone is fine so long as you pass the GMEM_FIXED flag.

After that he copies initial data into his structure array using RTLMoveMemory, but you don't need to do that either because there's no requirement to initialise the structures in the case of GetTouchInputInfo.

Once you've stripped out the unnecessary code you should find that it's quite manageable. I'd make the changes myself but this is an LBB forum so I'd obviously suggest you use that instead, and save both you and me (and the OP) the effort!

Quote:
I progress it because we recently had a blind user who was interested in braille input

If that's Ray McAllister he's now fully converted to using LBB. There's not the slightest point making it run in LB for his benefit!

Richard.

Re: Multi-touch demo
Post by Rod on Mar 29th, 2016, 5:38pm

With a bit of help I managed to convert this to run in Liberty BASIC.

Code:
        ' Multi-touch demo by Richard Russell, http://www.rtrussell.co.uk/
    ' amended for Liberty BASIC by Brandon and Rod using Dennis McKinney's
    ' http://lbpe.wikispaces.com/ArraysAndStructs
    ' Uses WMLiberty.DLL (http://www.b6sw.com/forum/download.php?id=16)

    'nomainwin
    Global WM.TOUCH, hWnd, nPoints, ptrStructArray,sizeofTi
    WM.TOUCH = hexdec("0240")

    'a normal struct for the xy points
    Struct pt, x As long, y As long

    'a normal struct that will be used to define an array of structs
    Struct ti, x            As long, _
               y            As long, _
               hSource      As ulong, _
               dwID         As long, _
               dwFlags      As long, _
               dwMask       As long, _
               dwTime       As long, _
               dwExtraInfo  As long, _
               cxContact    As long, _
               cyContact    As long

    'the size of the structure is
    sizeofTi = Len(ti.struct)

    'the amount of memory needed for the array is
    maxPoints=6
    memBlockSize = maxPoints*sizeofTi

    'allocate memory and get pointer using Richards simplifications
    'structs are accessed by sizeofTi offset
    'so ti.struct=ptrStructArray+sizeofTi*i i=0-5
    ptrStructArray = GlobalAlloc(memBlockSize)

    Open "WMLiberty" For DLL As #wmlib
    WindowWidth = 800
    WindowHeight = 600
    Open "Multi-touch demo" For graphics As #w
    hWnd = hwnd(#w)
    #w "trapclose quit"
    #w "down; fill black ; backcolor red"

    ' set up callback to receive touch messages
    callback lpwmtouch, wmtouch(ulong, ulong, ulong, ulong), long
    calldll #wmlib, "SetWMHandler", hWnd        As ulong, _
                                    WM.TOUCH    As ulong, _
                                    lpwmtouch   As ulong, _
                                    1           As long, _
                                    ret         As long

    ' register the window for touch messaging
    calldll #user32, "RegisterTouchWindow", hWnd        As ulong, _
                                            0           As ulong, _
                                            lpwmtouch   As ulong, _
                                            ret         As long

    ' now scan for messages
    [wait] ' Must use a Scan loop.
    Scan
    Call plot
    CallDLL #kernel32, "Sleep", 5  As Long, ret As Void
    GoTo [wait]



Sub plot
    For i = 0 To nPoints-1
        'fill the ti.struct from the offset
        ti.struct=ptrStructArray+sizeofTi*i
        'get the screen coordinates
        pt.x.struct = ti.x.struct / 100
        pt.y.struct = ti.y.struct / 100
        CallDLL #user32, "ScreenToClient", hWnd As ulong, _
                                           pt   As struct, _
                                           ret  As long
        #w "place ";pt.x.struct;" ";pt.y.struct
        #w "circlefilled 50"
    Next i
    nPoints = 0
End Sub

Sub quit h$
    Close #wmlib
    ret = GlobalFree(ptrStructArray)
    Close #w
    End
End Sub

Function wmtouch(hw, msg, wparam, lparam)
    nPoints = wparam And hexdec("FFFF")
    If nPoints > 6 Then nPoints = 6
    CallDLL #user32, "GetTouchInputInfo", lparam            As ulong, _
                                          nPoints           As ulong, _
                                          ptrStructArray    As ulong, _
                                          sizeofTi          As long, _
                                          ret               As long
    CallDLL #user32, "CloseTouchInputHandle", lparam    As ulong, _
                                              ret       As long
End Function

Function GlobalAlloc( dwBytes )
    'returns pointer to fixed memory block.
    CallDLL #kernel32, "GlobalAlloc",  0              As long, _
                                     dwBytes        As ulong, _
                                     GlobalAlloc    As long
End Function

Function GlobalFree( hMem )
    CallDLL #kernel32, "GlobalFree", hMem         As ulong, _
                                   GlobalFree   As long
End Function



 

Re: Multi-touch demo
Post by Richard Russell on Mar 29th, 2016, 9:29pm

on Mar 29th, 2016, 5:38pm, Rod wrote:
With a bit of help I managed to convert this to run in Liberty BASIC.

It seems to work, although unlike my original the 'disks' don't disappear when you stop touching the screen - new ones just keep appearing on top.

As I'm sure you know, it's not necessary to open and close kernel32.dll: it's one of the standard DLLs that LB and LBB open for you and provide a ready-made handle to (#kernel32).

Richard.

Re: Multi-touch demo
Post by Rod on Mar 30th, 2016, 08:23am

Thanks made those changes in the above code, I took out the cls to make it obvious that the programmer is fielding a stream of these messages.