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:I don't think WMLiberty plays well with WAIT in LB. You may have to replace it with a Sleep/SCAN loop.
I use a SUB as the timer handler, to eliminate any 'branch label scope' issues. LB usually crashes if you attempt to do that.
There's a kludge in the way the RegisterTouchWindow API is called, and I'm not sure it will be compatible with LB*.
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.
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.