emulator/emulatorbsp/specific/multitouch.cpp
changeset 0 cec860690d41
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emulator/emulatorbsp/specific/multitouch.cpp	Tue Feb 02 01:39:10 2010 +0200
@@ -0,0 +1,522 @@
+// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// wins\specific\multitouch.cpp
+// 
+//
+#include "multitouch.h"
+#include "resource.h"
+
+#define KDefaultMaxProximity -100
+#define KDefaultMaxPressure 5000
+#define KDefaultPressureStep 500
+#define KDefaultProximityStep 5
+
+// Static members
+DMultiMouse DMultiMouse::iMice[KMaxMice];
+int DMultiMouse::iNumMice = 0;
+DMultiMouse* DMultiMouse::iPrimary = 0;
+int DMultiMouse::iMouseId = 0;
+int DMultiTouch::iNumberOfMice = 0;
+bool DMultiTouch::iMultiTouchSupported= FALSE;
+bool DMultiTouch::iMultiTouchCreated= FALSE;
+bool DMultiTouch::iMultiTouchTempEnabled= FALSE;
+
+// Function pointers for raw input APIs
+TYPEOF_RegisterRawInputDevices pfnRegisterRawInputDevices= NULL;
+TYPEOF_GetRawInputData pfnGetRawInputData= NULL;
+TYPEOF_GetRawInputDeviceList pfnGetRawInputDeviceList=NULL;
+
+/**
+ *  Initialise the proximity and pressure information if undefined by the user
+ */
+DMultiTouch::DMultiTouch(TInt aProximityStep, TInt aPressureStep)
+	{
+	iZMaxRange = KDefaultMaxProximity;
+	iMaxPressure = KDefaultMaxPressure;
+	iProximityStep = (aProximityStep == -1) ? KDefaultProximityStep : aProximityStep;
+	iPressureStep = (aPressureStep == -1) ? KDefaultPressureStep : aPressureStep;
+	}
+
+/** 
+ * Register all the mice 
+*/
+BOOL DMultiTouch::Register()
+	{
+	RAWINPUTDEVICE device;
+	device.usUsagePage = 0x01;
+	device.usUsage = 0x02;
+	device.dwFlags = RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages
+	device.hwndTarget = 0;
+	ShowCursors();
+	return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE));
+	}
+
+/**
+ * Unregister mice devices
+ */
+BOOL DMultiTouch::UnRegister()
+	{
+	RAWINPUTDEVICE device;
+	device.usUsagePage = 0x01;
+	device.usUsage = 0x02;
+	device.dwFlags = RIDEV_REMOVE;
+	device.hwndTarget = NULL;
+	HideCursors();
+	return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE));
+	}
+
+
+/* * Handle multi-input Window messages
+ */
+void DMultiTouch::OnWmInput(HWND aHWnd,TUint aMessage,TUint aWParam,TUint aLParam,HWND aParentHwnd)
+	{
+	RAWINPUT ri;
+	UINT dwSize = sizeof(ri);
+	if (pfnGetRawInputData((HRAWINPUT)aLParam, RID_INPUT, &ri, &dwSize, sizeof(RAWINPUTHEADER))==(UINT)-1)
+		{
+		OutputDebugString(TEXT("GetRawInputData has an error !\n"));
+		}
+	else if (ri.header.dwType == RIM_TYPEMOUSE) 
+		{
+		DMultiMouse* mouse = DMultiMouse::Find(ri.header.hDevice);
+		if (mouse)
+			{
+			if (!DMultiMouse::iPrimary)
+				{
+				DMultiMouse::iPrimary = mouse;
+				DMultiMouse::iPrimary->iIsPrimary = TRUE;
+				}
+			mouse->HandleRawMouseEvent(ri.data.mouse, aParentHwnd);
+			}
+		}
+	DefWindowProcA(aHWnd, aMessage, aWParam, aLParam);
+	}
+
+void DMultiTouch::HideCursors()
+	{
+	for (int ii=0; ii<DMultiMouse::iNumMice; ii++)
+		{
+		DMultiMouse::iMice[ii].iCursorWnd.Hide();
+		}
+	}
+
+void DMultiTouch::ShowCursors()
+	{
+	for (int ii=0; ii<DMultiMouse::iNumMice; ii++)
+		{
+		DMultiMouse::iMice[ii].iCursorWnd.Show();
+		}
+	}
+
+/**
+ * The cursor window procedure
+ */
+static LRESULT CALLBACK CursorWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+	{
+	if (msg==WM_PAINT)
+		{
+		PAINTSTRUCT ps;
+		HDC hdc = BeginPaint(hwnd, &ps);
+		CursorWindow* wnd = (CursorWindow*)GetWindowLong(hwnd, GWL_USERDATA);
+		DrawIconEx(hdc,0,0, wnd->iCursor,0,0,0,NULL, DI_NORMAL | DI_DEFAULTSIZE);
+		EndPaint(hwnd, &ps);
+		return 0;
+		}
+	return DefWindowProc(hwnd, msg, wp, lp);
+	}
+
+CursorWindow::CursorWindow(): iHwnd(NULL),iCursor(NULL),iNumber(0)
+	{
+	}
+
+HWND CursorWindow::Create(HMODULE hm, HWND hwndParent, int number)
+	{
+	// Create the window
+	static ATOM atom = NULL;
+	if (!atom) 
+		{
+		WNDCLASSEX wcex;
+		ZeroMemory(&wcex, sizeof(wcex));
+		wcex.cbSize = sizeof(WNDCLASSEX); 
+		wcex.style			= CS_OWNDC;
+		wcex.lpfnWndProc	= (WNDPROC)CursorWndProc;
+		wcex.hInstance		= (HINSTANCE)hm;
+		wcex.lpszClassName	= TEXT("CursorWndClass");
+		wcex.hbrBackground = CreateSolidBrush(RGB(255,0,0));//Background color is also for the number
+		atom = RegisterClassEx(&wcex);
+		}
+	iHwnd = CreateWindowA((LPCSTR)atom, NULL, WS_CHILD|WS_CLIPSIBLINGS, 0,0,64,64, hwndParent, NULL, hm, NULL);
+	SetWindowLong(iHwnd, GWL_USERDATA, (LONG)this);
+	iNumber = number;
+	return iHwnd;
+	}
+
+void CursorWindow::Show()
+	{
+	ShowWindow(iHwnd, SW_NORMAL);
+	}
+
+void CursorWindow::Hide()
+	{
+	ShowWindow(iHwnd, SW_HIDE);
+	}
+
+BOOL CursorWindow::SetCursor(HCURSOR hc)
+	{
+	// Duplicate the cursor (because we're going to draw a number on the mask)
+	if (iCursor) 
+		{
+		DestroyCursor(iCursor);
+		iCursor = NULL;
+		}
+	iCursor = CopyCursor(hc);
+
+	// Get information about the cursor, and select its mask bitmap into a temporary DC.
+	ICONINFO ii;
+	GetIconInfo(iCursor, &ii);
+	iHotspot.x = ii.xHotspot;
+	iHotspot.y = ii.yHotspot;
+	HDC hdc = CreateCompatibleDC(NULL);
+	SelectObject(hdc, ii.hbmMask);
+
+	// Get the cursor's pixel size	
+	BITMAPINFO bmi;
+	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bmi.bmiHeader.biBitCount = 0;
+	GetDIBits(hdc, ii.hbmMask, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
+	int cx = bmi.bmiHeader.biWidth;
+	int cy = bmi.bmiHeader.biHeight;
+
+	// Monochrome cursors have a double-height hbmMask. The top half contains the AND 
+	// bitmap (which we do want) and the bottom half contains the XOR bitmap (which we 
+	// dont want). Colour cursors have a single normal-height AND mask.
+	BOOL isMonochrome = (ii.hbmColor==NULL);
+	int cyy = isMonochrome ? (cy>>1) : cy; 
+
+	// Draw the number into the mask
+	char ach[4];
+	int ld = iNumber/10;
+	int rd = iNumber % 10;
+	if (ld > 0)
+		{
+		wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d%d"), ld,rd);
+		}
+	else 
+		{
+		wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d"), rd);
+		}
+
+	HFONT hf = CreateFontA(12,0, 0,0,FW_THIN, FALSE,FALSE,FALSE, 0,DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, "Arial");
+	SelectObject(hdc, hf);
+	SetBkMode(hdc, TRANSPARENT);
+	TextOutA(hdc, 0,cyy-12, ach, 2);
+	DeleteObject(hf);
+
+	// Get the bits of the mask (in 32-bit colour)
+	HANDLE heap = GetProcessHeap();
+	bmi.bmiHeader.biBitCount = 32;
+	DWORD* bits = (DWORD*)HeapAlloc(heap, 0, cx*cy*4);
+	GetDIBits(hdc, ii.hbmMask, 0, cy, bits, &bmi, DIB_RGB_COLORS);
+
+	// Set the window to the size of the cursor
+	SetWindowPos(iHwnd, NULL,0,0,cx,cyy, SWP_NOMOVE);
+
+	// Create a cursor-shaped region by starting out with an empty region
+	// and ORing in each zero-valued mask pixel.
+	HRGN rgn = CreateRectRgn(0,0,0,0);
+	for (int y=0 ; y<cyy ; y++) 
+		{
+		for (int x=0 ; x<cx ; x++) 
+			{
+			if (bits[(cy-y-1)*cx+x]==0) 
+				{
+				HRGN rgnPix = CreateRectRgn(x,y, x+1,y+1);
+				CombineRgn(rgn, rgn, rgnPix, RGN_OR);
+				DeleteObject(rgnPix);
+				}
+			}
+		}
+
+	// Cleanup
+	HeapFree(heap, 0, bits);
+	DeleteDC(hdc);
+
+	// Set the window's clipping region to the cursor-shaped region
+	SetWindowRgn(iHwnd, rgn, TRUE);
+	return TRUE;
+	}
+	
+void CursorWindow::GetPosition(POINT& pt)
+	{
+	pt.x = 0;
+	pt.y = 0;
+	MapWindowPoints(iHwnd, GetParent(iHwnd), &pt, 1);
+	pt.x += iHotspot.x;
+	pt.y += iHotspot.y;
+	}
+
+void CursorWindow::SetPosition(POINT& pt)
+	{
+	SetWindowPos(iHwnd, NULL, pt.x - iHotspot.x, pt.y - iHotspot.y, 0, 0, SWP_NOSIZE);
+	}
+
+/** 
+ * Add the mouse device to the collection
+ */
+TInt DMultiMouse::Add(RAWINPUTDEVICELIST& aDev)
+	{
+	if (iNumMice < KMaxMice)
+		{
+		DMultiMouse& mouse = iMice[iNumMice];
+		mouse.iDevice = aDev.hDevice;
+		iNumMice++;
+		return KErrNone;
+		}
+	else
+		{
+		return KErrOverflow;
+		}
+	}
+
+DMultiMouse::DMultiMouse() :
+	iX(-1), iY(-1), iZ(0), iDevice(0),iId(-1) 
+	{
+	}
+
+DMultiMouse* DMultiMouse::Find(HANDLE aHandle)
+	{
+	for (TInt ii=0; ii<iNumMice; ii++)
+		{
+		DMultiMouse& mouse = iMice[ii];
+		if (mouse.iDevice == aHandle)
+			return &mouse;
+		}
+	return NULL;
+	}
+
+void DMultiMouse::HandleRawMouseEvent(RAWMOUSE& aEvent, HWND aWnd)
+	{
+	// give this pointer an id, if it doesn't already have one
+	if (iId == -1)
+		{
+		iId = iMouseId++;
+		}
+
+	// Create the cursor window and set the cursor if not done yet
+	if (iCursorWnd.iHwnd == NULL)
+		{
+		iCursorWnd.Create((HINSTANCE)0, aWnd,iId);
+		iCursorWnd.SetCursor(LoadCursorA(NULL, (LPCSTR)IDC_ARROW));
+		}
+
+	CorrectSystemMouse();
+			
+	// recalc mouse position
+	if (iX == -1)
+		{
+		// initial position
+		iX = iPrimary->iX;
+		iY = iPrimary->iY;
+		}
+
+	if (aEvent.usFlags & MOUSE_MOVE_ABSOLUTE)
+		{
+		// absolute position info can update all pointers
+		iX = aEvent.lLastX;
+		iY = aEvent.lLastY;
+		}
+	else if (!iIsPrimary)
+			{
+			// relative position info updates non-primary pointers,
+			iX += aEvent.lLastX;
+			iY += aEvent.lLastY;
+			}
+
+	// Show the cursor window
+	ShowMousePos(aWnd);
+
+	TInt message = WM_MOUSEMOVE;
+
+	// get button state
+	if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
+		{
+		message = WM_LBUTTONDOWN;
+		iZ = 0;
+		}
+	
+	if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
+		{
+		message = WM_LBUTTONUP;
+		iZ = 0;
+		}
+	
+	if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
+		{
+		message = WM_RBUTTONDOWN;
+		}
+	
+	if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
+		{
+		message = WM_RBUTTONUP;
+		}
+	
+	if (aEvent.usButtonFlags & RI_MOUSE_WHEEL)
+		{
+		message = WM_MOUSEWHEEL;
+		if ((TInt16)(aEvent.usButtonData&0x8000)== 0) // positive number
+			{
+			if (iZ < TheMultiTouch->iMaxPressure)
+				{
+				if (iZ>=0)
+					{ // pressure range 
+					iZ += TheMultiTouch->iPressureStep;//Pressure step
+					if (iZ > TheMultiTouch->iMaxPressure)
+						{
+						iZ = TheMultiTouch->iMaxPressure;
+						}
+					}
+				else
+					{ // proximity range
+					iZ += TheMultiTouch->iProximityStep; //Proximity step
+					}
+				}
+			}
+		else
+			{
+			if (iZ > TheMultiTouch->iZMaxRange)
+				{
+				if (iZ <= 0)
+					{// proximity range 
+					iZ -= TheMultiTouch->iProximityStep;//Proximity step
+					if (iZ < TheMultiTouch->iZMaxRange)
+						{
+						iZ = TheMultiTouch->iZMaxRange;
+						}
+					}
+				else
+					{// pressure range
+					iZ -= TheMultiTouch->iPressureStep;//Pressure step
+					}
+				}
+			}
+		}
+	
+	MultiTouchWndPointer(message, iX, iY, iZ, iId);
+	
+	}
+
+/**
+ * Show the cursor window when the cursor is inside the client area
+ */
+void DMultiMouse::ShowMousePos(HWND aHWnd)
+	{
+	RECT client = {0,0,0,0};
+	if(aHWnd)
+		{
+		GetWindowRect(aHWnd, &client); 
+		POINT pt = {iX-client.left,iY-client.top};
+		iCursorWnd.SetPosition(pt);
+		}
+	iCursorWnd.Show();
+	}
+
+void DMultiMouse::CorrectSystemMouse()
+	{
+	if (iIsPrimary)
+		{
+		POINT pos;
+		if (GetCursorPos(&pos)) // if failed, pos contains garbage.
+			{
+			iX = pos.x;
+			iY = pos.y;	
+			}
+		}
+	else
+		{
+		SetCursorPos(iPrimary->iX,iPrimary->iY);
+		}
+	}
+	
+/** 
+ * a static function to check how many mice are connected 
+*/
+bool DMultiTouch::Init()
+	{
+	HMODULE hModule = GetModuleHandleA("user32.dll");
+	if(hModule == NULL)
+		return FALSE;
+	
+	OSVERSIONINFO osvi;
+	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+	
+	GetVersionEx(&osvi);
+	// Check for Win 2K or higher version
+	if (osvi.dwMajorVersion < 5)
+		{
+		return FALSE;
+		}
+	
+	// Not supported on Win2K
+	if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0))
+		{
+		return FALSE;
+		}
+		
+	pfnRegisterRawInputDevices 	= (TYPEOF_RegisterRawInputDevices)GetProcAddress(hModule, "RegisterRawInputDevices");
+	pfnGetRawInputData 			= (TYPEOF_GetRawInputData)GetProcAddress(hModule, "GetRawInputData");
+	pfnGetRawInputDeviceList 	= (TYPEOF_GetRawInputDeviceList)GetProcAddress(hModule, "GetRawInputDeviceList");
+	
+	if((pfnRegisterRawInputDevices == NULL) || (pfnGetRawInputData == NULL) || (pfnGetRawInputDeviceList == NULL))
+		{
+		return FALSE;
+		}
+		
+	UINT nDevices;
+	
+	if (pfnGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
+		{
+		return FALSE;
+		}
+
+	RAWINPUTDEVICELIST* pRawInputDeviceList = new RAWINPUTDEVICELIST[ nDevices ];
+	if (!pRawInputDeviceList)
+		{
+		return FALSE;
+		}
+
+	pfnGetRawInputDeviceList(pRawInputDeviceList, &nDevices,
+			sizeof(RAWINPUTDEVICELIST));
+
+	
+	for (UINT i=0; i<nDevices; i++)
+		{
+		RAWINPUTDEVICELIST& dev = pRawInputDeviceList[i];
+		if (dev.dwType == RIM_TYPEMOUSE)
+			{
+			if (DMultiMouse::Add(dev)!=KErrNone)
+				{
+				//free the device list
+				delete[] pRawInputDeviceList;
+				return FALSE;
+				}
+			}
+		}
+
+	delete[] pRawInputDeviceList;
+
+	// Multitouch is supported when more than 2 mice are connected (including the hidden RID mouse)
+	return DMultiMouse::iNumMice > 2;
+	}
+