hi all, so I got this working with a version of the multi touch sample today, pasted below. I have it set to receive individual touch events similar to how the TUIO block events work. Integrating this into the core is beyond me at the moment although I imagine would be trivial for someone familiar with that code. I might get a chance to revisit after a couple deadlines.
MultiTouchBasicApp.cpp:
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/System.h"
#include "cinder/Rand.h"
#include "cinder/Log.h"
#include <vector>
#include <map>
#include <map>
#include <list>
using namespace ci;
using namespace ci::app;
using namespace std;
#include "TouchWin8.h"
struct TouchPoint {
TouchPoint() {}
TouchPoint( const vec2 &initialPt, const Color &color ) : mColor( color ), mTimeOfDeath( -1.0 )
{
mLine.push_back( initialPt );
}
void addPoint( const vec2 &pt ) { mLine.push_back( pt ); }
void draw() const
{
if( mTimeOfDeath > 0 ) // are we dying? then fade out
gl::color( ColorA( mColor, ( mTimeOfDeath - getElapsedSeconds() ) / 2.0f ) );
else
gl::color( mColor );
gl::draw( mLine );
}
void startDying() { mTimeOfDeath = getElapsedSeconds() + 2.0f; } // two seconds til dead
bool isDead() const { return getElapsedSeconds() > mTimeOfDeath; }
PolyLine2f mLine;
Color mColor;
float mTimeOfDeath;
};
class MultiTouchApp : public App {
public:
void touchAdded(vec2 pos, int id);
void touchUpdated(vec2 pos, int id);
void touchRemoved(vec2 pos, int id);
void setup() override;
void draw() override;
private:
map<uint32_t,TouchPoint> mActivePoints;
list<TouchPoint> mDyingPoints;
};
void prepareSettings( MultiTouchApp::Settings *settings )
{
settings->setConsoleWindowEnabled(true);
}
void MultiTouchApp::setup()
{
//set up touches from win 8 api
initTouch();
getSignalOnTouchAdded().connect(signals::slot(this, &MultiTouchApp::touchAdded));
getSignalOnTouchUpdated().connect(signals::slot(this, &MultiTouchApp::touchUpdated));
getSignalOnTouchRemoved().connect(signals::slot(this, &MultiTouchApp::touchRemoved));
}
void MultiTouchApp::touchAdded(vec2 pos, int id)
{
Color newColor(CM_HSV, Rand::randFloat(), 1, 1);
mActivePoints.insert(make_pair(id, TouchPoint( pos, newColor)));
}
void MultiTouchApp::touchUpdated(vec2 pos, int id)
{
mActivePoints[id].addPoint(pos);
}
void MultiTouchApp::touchRemoved(vec2 pos, int id)
{
mActivePoints[id].startDying();
mDyingPoints.push_back(mActivePoints[id]);
mActivePoints.erase(id);
}
void MultiTouchApp::draw()
{
gl::enableAlphaBlending();
gl::clear( Color( 0.1f, 0.1f, 0.1f ) );
for( const auto &activePoint : mActivePoints ) {
activePoint.second.draw();
}
for( auto dyingIt = mDyingPoints.begin(); dyingIt != mDyingPoints.end(); ) {
dyingIt->draw();
if( dyingIt->isDead() )
dyingIt = mDyingPoints.erase( dyingIt );
else
++dyingIt;
}
// draw yellow circles at the active touch points
gl::color( Color( 1, 1, 0 ) );
for( const auto &touch : getActiveTouches() )
gl::drawStrokedCircle( touch.getPos(), 20 );
}
CINDER_APP( MultiTouchApp, RendererGl, prepareSettings )
TouchWin8.h:
/*
// Thanks to code from TouchScript:
// https://github.com/TouchScript/TouchScript/tree/master/External/WindowsTouch
*/
#pragma once
#include "cinder/app/App.h"
#include "cinder/Log.h"
#include "cinder/Signals.h"
#include <Windows.h>
ci::signals::Signal<void(ci::vec2, int)> signalOnTouchAdded;
ci::signals::Signal<void(ci::vec2, int)>& getSignalOnTouchAdded() { return signalOnTouchAdded; }
ci::signals::Signal<void(ci::vec2, int)> signalOnTouchUpdated;
ci::signals::Signal<void(ci::vec2, int)>& getSignalOnTouchUpdated() { return signalOnTouchUpdated; }
ci::signals::Signal<void(ci::vec2, int)> signalOnTouchRemoved;
ci::signals::Signal<void(ci::vec2, int)>& getSignalOnTouchRemoved() { return signalOnTouchRemoved; }
// <Windows 8 touch API>
#define WM_POINTERENTER 0x0249
#define WM_POINTERLEAVE 0x024A
#define WM_POINTERUPDATE 0x0245
#define WM_POINTERDOWN 0x0246
#define WM_POINTERUP 0x0247
#define WM_POINTERCAPTURECHANGED 0x024C
#define POINTER_CANCELLED 0x1000
#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
typedef enum {
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004,
PT_TOUCHPAD = 0x00000005
} POINTER_INPUT_TYPE;
typedef enum {
POINTER_FLAG_NONE = 0x00000000,
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,
POINTER_FLAG_HASTRANSFORM = 0x00400000
} POINTER_FLAGS;
typedef enum {
POINTER_CHANGE_NONE,
POINTER_CHANGE_FIRSTBUTTON_DOWN,
POINTER_CHANGE_FIRSTBUTTON_UP,
POINTER_CHANGE_SECONDBUTTON_DOWN,
POINTER_CHANGE_SECONDBUTTON_UP,
POINTER_CHANGE_THIRDBUTTON_DOWN,
POINTER_CHANGE_THIRDBUTTON_UP,
POINTER_CHANGE_FOURTHBUTTON_DOWN,
POINTER_CHANGE_FOURTHBUTTON_UP,
POINTER_CHANGE_FIFTHBUTTON_DOWN,
POINTER_CHANGE_FIFTHBUTTON_UP,
} POINTER_BUTTON_CHANGE_TYPE;
typedef enum {
TOUCH_FLAG_NONE = 0x00000000
} TOUCH_FLAGS;
typedef enum {
TOUCH_MASK_NONE = 0x00000000,
TOUCH_MASK_CONTACTAREA = 0x00000001,
TOUCH_MASK_ORIENTATION = 0x00000002,
TOUCH_MASK_PRESSURE = 0x00000004
} TOUCH_MASK;
typedef enum {
PEN_FLAG_NONE = 0x00000000,
PEN_FLAG_BARREL = 0x00000001,
PEN_FLAG_INVERTED = 0x00000002,
PEN_FLAG_ERASER = 0x00000004
} PEN_FLAGS;
typedef enum {
PEN_MASK_NONE = 0x00000000,
PEN_MASK_PRESSURE = 0x00000001,
PEN_MASK_ROTATION = 0x00000002,
PEN_MASK_TILT_X = 0x00000004,
PEN_MASK_TILT_Y = 0x00000008
} PEN_MASK;
typedef struct {
POINTER_INPUT_TYPE pointerType;
UINT32 pointerId;
UINT32 frameId;
POINTER_FLAGS pointerFlags;
HANDLE sourceDevice;
HWND hwndTarget;
POINT ptPixelLocation;
POINT ptHimetricLocation;
POINT ptPixelLocationRaw;
POINT ptHimetricLocationRaw;
DWORD dwTime;
UINT32 historyCount;
INT32 InputData;
DWORD dwKeyStates;
UINT64 PerformanceCount;
POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
} POINTER_INFO;
typedef struct {
POINTER_INFO pointerInfo;
TOUCH_FLAGS touchFlags;
TOUCH_MASK touchMask;
RECT rcContact;
RECT rcContactRaw;
UINT32 orientation;
UINT32 pressure;
} POINTER_TOUCH_INFO;
typedef struct {
POINTER_INFO pointerInfo;
PEN_FLAGS penFlags;
PEN_MASK penMask;
UINT32 pressure;
UINT32 rotation;
INT32 tiltX;
INT32 tiltY;
} POINTER_PEN_INFO;
typedef BOOL(WINAPI *GET_POINTER_INFO)(UINT32 pointerId, POINTER_INFO *pointerInfo);
typedef BOOL(WINAPI *GET_POINTER_TOUCH_INFO)(UINT32 pointerId, POINTER_TOUCH_INFO *pointerInfo);
typedef BOOL(WINAPI *GET_POINTER_PEN_INFO)(UINT32 pointerId, POINTER_PEN_INFO *pointerInfo);
GET_POINTER_INFO GetPointerInfo;
GET_POINTER_TOUCH_INFO GetPointerTouchInfo;
GET_POINTER_PEN_INFO GetPointerPenInfo;
// </Windows 8 touch API>
HWND _currentWindow;
LONG_PTR _oldWindowProc;
void decodeWin8Touches(UINT msg, WPARAM wParam, LPARAM lParam)
{
int pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INFO pointerInfo;
if (!GetPointerInfo(pointerId, &pointerInfo)) return;
POINT p;
p.x = pointerInfo.ptPixelLocation.x;
p.y = pointerInfo.ptPixelLocation.y;
ScreenToClient(_currentWindow, &p);
ci::vec2 position(p.x, p.y);
switch (msg)
{
case WM_TOUCH:
//CI_LOG_I("WM_TOUCH");
//not used
break;
case WM_POINTERENTER:
//CI_LOG_I("WM_POINTERENTER");
//redundant as followed by WM_POINTERDOWN with same coords
break;
case WM_POINTERLEAVE:
//CI_LOG_I("WM_POINTERLEAVE");
//redundant as follows WM_POINTERUP with same coords
break;
case WM_POINTERDOWN:
//CI_LOG_I("WM_POINTERDOWN");
signalOnTouchAdded.emit(position, pointerId);
break;
case WM_POINTERUP:
//CI_LOG_I("WM_POINTERUP");
signalOnTouchRemoved.emit(position, pointerId);
break;
case WM_POINTERUPDATE:
//CI_LOG_I("WM_POINTERUPDATE");
signalOnTouchUpdated.emit(position, pointerId);
break;
case WM_POINTERCAPTURECHANGED:
//CI_LOG_I("WM_POINTERCAPTURECHANGED");
//not used
break;
}
}
LRESULT CALLBACK wndProc8(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_TOUCH:
CloseTouchInputHandle((HTOUCHINPUT)lParam);
break;
case WM_POINTERENTER:
case WM_POINTERLEAVE:
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERUPDATE:
case WM_POINTERCAPTURECHANGED:
decodeWin8Touches(msg, wParam, lParam);
break;
default:
return CallWindowProc((WNDPROC)_oldWindowProc, hwnd, msg, wParam, lParam);
}
return 0;
}
void initTouch()
{
_currentWindow = (HWND)getWindow()->getNative();
HINSTANCE h = LoadLibrary(TEXT("user32.dll"));
GetPointerInfo = (GET_POINTER_INFO)GetProcAddress(h, "GetPointerInfo");
GetPointerTouchInfo = (GET_POINTER_TOUCH_INFO)GetProcAddress(h, "GetPointerTouchInfo");
GetPointerPenInfo = (GET_POINTER_PEN_INFO)GetProcAddress(h, "GetPointerPenInfo");
_oldWindowProc = SetWindowLongPtr(_currentWindow, GWLP_WNDPROC, (LONG_PTR)wndProc8);
}