Delphi 2010 introduced Windows 7 touch and gesture (T/G) processing at VCL level. For those that still use Delphi 7, this text illustrates how to handle T/G messages in a graphic component. The sample code contains 3 files: a container Form, a graphic component and a small T/G API header translation.

The sample component is not very useful. It allows you to manipulate a bunch of rectangles with two finger gestures. You can move, resize and deform the selected rectangle (the one with fat lines). In order to receive WM_ messages, your component must be a window (i.e. must descend from TWinControl or TCustomControl).

The graphic component is created at runtime, so you don't have to install it in the D7 component palette. The idea is to illustrate how to handle T/G messages in your component. Of course you need Windows 7/8 and a multi-touch enabled hardware (a Windows tablet or desktop). If the hardware and/or OS does not provide multi-touch service, the sample program still runs, but you can't edit the rectangles :(

A checkbox click changes the component state from gesture to multi-touch. In this mode, you can draw several curves simultaneously, by dragging multiple fingers on the screen.

The gesture API is located at user32.dll in Windows 7 and 8. In previous versions of the OS, these functions are not present. For the program to run on Vista and before, the gesture API function addresses must be loaded at runtime (dynamically). If you declare the functions statically, the program will not start on Vista and XP. Not acceptable.


Source code

WM_GESTURE - This message passes a gesture info record. It can be retrieved by a call to GetGestureInfo(Msg.lParam , gi) , like shown below.

procedure TMyRectangleDesigner.WMGesture(var Msg: TMessage);
var gi:TGestureInfo; bResult,bHandled:BOOL; dwError:DWORD; L:Integer;
ptZoomCenterX,ptZoomCenterY,K,angle:Single; _ptSecond:TPoint;
begin
bHandled:= False;
if bTouchGestureAPIPresent then
begin
L:=SizeOf(gi);
FillChar(gi,L,#0);
gi.cbSize := L;
bResult := GetGestureInfoFn(Msg.lParam , gi); // retrieve gesture info with lParam

if bResult then
begin
bHandled:=True;
case gi.dwID of
GID_BEGIN: ;
GID_END: ;
GID_PAN: begin
if gi.dwFlags=GF_Begin then
begin
f_ptFirst.x := gi.ptsLocation.x;
f_ptFirst.y := gi.ptsLocation.y;
f_ptFirst := ScreenToClient(f_ptFirst);
end
else begin // read the second point (middle between fingers in the position)
_ptSecond.x := gi.ptsLocation.x;
_ptSecond.y := gi.ptsLocation.y;
_ptSecond := ScreenToClient(_ptSecond);
ProcessGestureMove(_ptSecond.x-f_ptFirst.x,_ptSecond.y-f_ptFirst.y);
f_ptFirst := _ptSecond; //save
end;
end;
GID_ZOOM: begin
if gi.dwFlags=GF_Begin then
begin
f_ptFirst.x := gi.ptsLocation.x;
f_ptFirst.y := gi.ptsLocation.y;
f_ptFirst := ScreenToClient(f_ptFirst);
f_dwArguments := gi.ullArguments;
end
else begin
_ptSecond.x := gi.ptsLocation.x;
_ptSecond.y := gi.ptsLocation.y;
_ptSecond := ScreenToClient(_ptSecond);
ptZoomCenterX := (f_ptFirst.x + _ptSecond.x)/2;
ptZoomCenterY := (f_ptFirst.y + _ptSecond.y)/2;
k := gi.ullArguments/f_dwArguments;
ProcessGestureZoom(k,ptZoomCenterX,ptZoomCenterY);
f_ptFirst := _ptSecond;
f_dwArguments := gi.ullArguments;
end;
end;
GID_ROTATE: begin
if gi.dwFlags=GF_BEGIN then f_dwArguments := 0
else begin
f_ptFirst.x := gi.ptsLocation.x;
f_ptFirst.y := gi.ptsLocation.y;
f_ptFirst := ScreenToClient(f_ptFirst);
angle := GID_ROTATE_ANGLE_FROM_ARGUMENT(LODWORD(gi.ullArguments)) -
GID_ROTATE_ANGLE_FROM_ARGUMENT(f_dwArguments);
ProcessGestureRotate( angle,f_ptFirst.x,f_ptFirst.y); //angle in radians
f_dwArguments := LODWORD(gi.ullArguments); //save previous
end;
end;
GID_TWOFINGERTAP: ; // 2 finger tap
GID_PRESSANDTAP: ; // press n tap
else bHandled:= False;
end;
end
else begin //GetGestureInfo failed
dwError := GetLastError;
// Handle dwError ??
end;

CloseGestureInfoHandleFn(Msg.lParam);
end;
if bHandled then Msg.Result := 0
else Msg.Result := DefWindowProc(Handle, Msg.Msg, Msg.WParam, Msg.LParam);
end;

WM_TOUCH handling (multi-touch) has to be enabled by a call to RegisterTouchWindow () - The component then receives WM_TOUCH messages containing a collection of touch points. The sample component simply draws a circle in each point (not persistent). To return to gesture mode, call UnRegisterTouchWindow()

procedure TMyRectangleDesigner.WMTouch(var Msg: TMessage);

function TouchPointToPoint(const TouchPoint: TTouchInput): TPoint;
begin
Result := Point(TouchPoint.X div 100, TouchPoint.Y div 100);
PhysicalToLogicalPointFn(Handle, Result);
Result := ScreenToClient(Result);
end ;

var
TouchInputs: array of TTouchInput;
counter: Integer;
Handled: Boolean;
Point: TPoint;
R:TRect;
begin
Handled := False;
if not bTouchGestureAPIPresent then Exit;

SetLength(TouchInputs, Msg.WParam);
GetTouchInputInfoFn(Msg.LParam, Msg.WParam, @TouchInputs[0], SizeOf(TTouchInput));

try
for counter := 1 to Length (TouchInputs) do
begin
Point := TouchPointToPoint(TouchInputs [counter-1]);
R := Rect(Point.X-5,Point.Y-5,Point.X+5,Point.Y+5);
Canvas.Brush.Style := bsClear;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clBlack;
Canvas.Ellipse(R);
end;
Handled := True;
finally
if Handled then CloseTouchInputHandleFn(Msg.LParam)
else inherited ;
end;
end;

Download

Download source code: > D7TouchSample.zip (md5 checksum 1D9EC7997613416236EE6BA24C829446)

External links

> LES OUTILS DU DÉVELOPPEUR

> Windows 7 Multitouch

> Windows touch overview

> Building Windows - Touch hardware for Windows 8

Tags

Delphi 7 , multi-touch , gestures , WM_GESTURE , WM_TOUCH , GID_ZOOM , GID_PAN , GID_ROTATE

 

by omar reis - fev/2013