First public contribution.
1 // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // wins\specific\multitouch.cpp
17 #include "multitouch.h"
20 #define KDefaultMaxProximity -100
21 #define KDefaultMaxPressure 5000
22 #define KDefaultPressureStep 500
23 #define KDefaultProximityStep 5
26 DMultiMouse DMultiMouse::iMice[KMaxMice];
27 int DMultiMouse::iNumMice = 0;
28 DMultiMouse* DMultiMouse::iPrimary = 0;
29 int DMultiMouse::iMouseId = 0;
30 int DMultiTouch::iNumberOfMice = 0;
31 bool DMultiTouch::iMultiTouchSupported= FALSE;
32 bool DMultiTouch::iMultiTouchCreated= FALSE;
33 bool DMultiTouch::iMultiTouchTempEnabled= FALSE;
35 // Function pointers for raw input APIs
36 TYPEOF_RegisterRawInputDevices pfnRegisterRawInputDevices= NULL;
37 TYPEOF_GetRawInputData pfnGetRawInputData= NULL;
38 TYPEOF_GetRawInputDeviceList pfnGetRawInputDeviceList=NULL;
41 * Initialise the proximity and pressure information if undefined by the user
43 DMultiTouch::DMultiTouch(TInt aProximityStep, TInt aPressureStep)
45 iZMaxRange = KDefaultMaxProximity;
46 iMaxPressure = KDefaultMaxPressure;
47 iProximityStep = (aProximityStep == -1) ? KDefaultProximityStep : aProximityStep;
48 iPressureStep = (aPressureStep == -1) ? KDefaultPressureStep : aPressureStep;
52 * Register all the mice
54 BOOL DMultiTouch::Register()
56 RAWINPUTDEVICE device;
57 device.usUsagePage = 0x01;
58 device.usUsage = 0x02;
59 device.dwFlags = RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages
60 device.hwndTarget = 0;
62 return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE));
66 * Unregister mice devices
68 BOOL DMultiTouch::UnRegister()
70 RAWINPUTDEVICE device;
71 device.usUsagePage = 0x01;
72 device.usUsage = 0x02;
73 device.dwFlags = RIDEV_REMOVE;
74 device.hwndTarget = NULL;
76 return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE));
80 /* * Handle multi-input Window messages
82 void DMultiTouch::OnWmInput(HWND aHWnd,TUint aMessage,TUint aWParam,TUint aLParam,HWND aParentHwnd)
85 UINT dwSize = sizeof(ri);
86 if (pfnGetRawInputData((HRAWINPUT)aLParam, RID_INPUT, &ri, &dwSize, sizeof(RAWINPUTHEADER))==(UINT)-1)
88 OutputDebugString(TEXT("GetRawInputData has an error !\n"));
90 else if (ri.header.dwType == RIM_TYPEMOUSE)
92 DMultiMouse* mouse = DMultiMouse::Find(ri.header.hDevice);
95 if (!DMultiMouse::iPrimary)
97 DMultiMouse::iPrimary = mouse;
98 DMultiMouse::iPrimary->iIsPrimary = TRUE;
100 mouse->HandleRawMouseEvent(ri.data.mouse, aParentHwnd);
103 DefWindowProcA(aHWnd, aMessage, aWParam, aLParam);
106 void DMultiTouch::HideCursors()
108 for (int ii=0; ii<DMultiMouse::iNumMice; ii++)
110 DMultiMouse::iMice[ii].iCursorWnd.Hide();
114 void DMultiTouch::ShowCursors()
116 for (int ii=0; ii<DMultiMouse::iNumMice; ii++)
118 DMultiMouse::iMice[ii].iCursorWnd.Show();
123 * The cursor window procedure
125 static LRESULT CALLBACK CursorWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
130 HDC hdc = BeginPaint(hwnd, &ps);
131 CursorWindow* wnd = (CursorWindow*)GetWindowLong(hwnd, GWL_USERDATA);
132 DrawIconEx(hdc,0,0, wnd->iCursor,0,0,0,NULL, DI_NORMAL | DI_DEFAULTSIZE);
136 return DefWindowProc(hwnd, msg, wp, lp);
139 CursorWindow::CursorWindow(): iHwnd(NULL),iCursor(NULL),iNumber(0)
143 HWND CursorWindow::Create(HMODULE hm, HWND hwndParent, int number)
146 static ATOM atom = NULL;
150 ZeroMemory(&wcex, sizeof(wcex));
151 wcex.cbSize = sizeof(WNDCLASSEX);
152 wcex.style = CS_OWNDC;
153 wcex.lpfnWndProc = (WNDPROC)CursorWndProc;
154 wcex.hInstance = (HINSTANCE)hm;
155 wcex.lpszClassName = TEXT("CursorWndClass");
156 wcex.hbrBackground = CreateSolidBrush(RGB(255,0,0));//Background color is also for the number
157 atom = RegisterClassEx(&wcex);
159 iHwnd = CreateWindowA((LPCSTR)atom, NULL, WS_CHILD|WS_CLIPSIBLINGS, 0,0,64,64, hwndParent, NULL, hm, NULL);
160 SetWindowLong(iHwnd, GWL_USERDATA, (LONG)this);
165 void CursorWindow::Show()
167 ShowWindow(iHwnd, SW_NORMAL);
170 void CursorWindow::Hide()
172 ShowWindow(iHwnd, SW_HIDE);
175 BOOL CursorWindow::SetCursor(HCURSOR hc)
177 // Duplicate the cursor (because we're going to draw a number on the mask)
180 DestroyCursor(iCursor);
183 iCursor = CopyCursor(hc);
185 // Get information about the cursor, and select its mask bitmap into a temporary DC.
187 GetIconInfo(iCursor, &ii);
188 iHotspot.x = ii.xHotspot;
189 iHotspot.y = ii.yHotspot;
190 HDC hdc = CreateCompatibleDC(NULL);
191 SelectObject(hdc, ii.hbmMask);
193 // Get the cursor's pixel size
195 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
196 bmi.bmiHeader.biBitCount = 0;
197 GetDIBits(hdc, ii.hbmMask, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
198 int cx = bmi.bmiHeader.biWidth;
199 int cy = bmi.bmiHeader.biHeight;
201 // Monochrome cursors have a double-height hbmMask. The top half contains the AND
202 // bitmap (which we do want) and the bottom half contains the XOR bitmap (which we
203 // dont want). Colour cursors have a single normal-height AND mask.
204 BOOL isMonochrome = (ii.hbmColor==NULL);
205 int cyy = isMonochrome ? (cy>>1) : cy;
207 // Draw the number into the mask
210 int rd = iNumber % 10;
213 wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d%d"), ld,rd);
217 wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d"), rd);
220 HFONT hf = CreateFontA(12,0, 0,0,FW_THIN, FALSE,FALSE,FALSE, 0,DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, "Arial");
221 SelectObject(hdc, hf);
222 SetBkMode(hdc, TRANSPARENT);
223 TextOutA(hdc, 0,cyy-12, ach, 2);
226 // Get the bits of the mask (in 32-bit colour)
227 HANDLE heap = GetProcessHeap();
228 bmi.bmiHeader.biBitCount = 32;
229 DWORD* bits = (DWORD*)HeapAlloc(heap, 0, cx*cy*4);
230 GetDIBits(hdc, ii.hbmMask, 0, cy, bits, &bmi, DIB_RGB_COLORS);
232 // Set the window to the size of the cursor
233 SetWindowPos(iHwnd, NULL,0,0,cx,cyy, SWP_NOMOVE);
235 // Create a cursor-shaped region by starting out with an empty region
236 // and ORing in each zero-valued mask pixel.
237 HRGN rgn = CreateRectRgn(0,0,0,0);
238 for (int y=0 ; y<cyy ; y++)
240 for (int x=0 ; x<cx ; x++)
242 if (bits[(cy-y-1)*cx+x]==0)
244 HRGN rgnPix = CreateRectRgn(x,y, x+1,y+1);
245 CombineRgn(rgn, rgn, rgnPix, RGN_OR);
246 DeleteObject(rgnPix);
252 HeapFree(heap, 0, bits);
255 // Set the window's clipping region to the cursor-shaped region
256 SetWindowRgn(iHwnd, rgn, TRUE);
260 void CursorWindow::GetPosition(POINT& pt)
264 MapWindowPoints(iHwnd, GetParent(iHwnd), &pt, 1);
269 void CursorWindow::SetPosition(POINT& pt)
271 SetWindowPos(iHwnd, NULL, pt.x - iHotspot.x, pt.y - iHotspot.y, 0, 0, SWP_NOSIZE);
275 * Add the mouse device to the collection
277 TInt DMultiMouse::Add(RAWINPUTDEVICELIST& aDev)
279 if (iNumMice < KMaxMice)
281 DMultiMouse& mouse = iMice[iNumMice];
282 mouse.iDevice = aDev.hDevice;
292 DMultiMouse::DMultiMouse() :
293 iX(-1), iY(-1), iZ(0), iDevice(0),iId(-1)
297 DMultiMouse* DMultiMouse::Find(HANDLE aHandle)
299 for (TInt ii=0; ii<iNumMice; ii++)
301 DMultiMouse& mouse = iMice[ii];
302 if (mouse.iDevice == aHandle)
308 void DMultiMouse::HandleRawMouseEvent(RAWMOUSE& aEvent, HWND aWnd)
310 // give this pointer an id, if it doesn't already have one
316 // Create the cursor window and set the cursor if not done yet
317 if (iCursorWnd.iHwnd == NULL)
319 iCursorWnd.Create((HINSTANCE)0, aWnd,iId);
320 iCursorWnd.SetCursor(LoadCursorA(NULL, (LPCSTR)IDC_ARROW));
323 CorrectSystemMouse();
325 // recalc mouse position
333 if (aEvent.usFlags & MOUSE_MOVE_ABSOLUTE)
335 // absolute position info can update all pointers
339 else if (!iIsPrimary)
341 // relative position info updates non-primary pointers,
346 // Show the cursor window
349 TInt message = WM_MOUSEMOVE;
352 if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
354 message = WM_LBUTTONDOWN;
358 if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
360 message = WM_LBUTTONUP;
364 if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
366 message = WM_RBUTTONDOWN;
369 if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
371 message = WM_RBUTTONUP;
374 if (aEvent.usButtonFlags & RI_MOUSE_WHEEL)
376 message = WM_MOUSEWHEEL;
377 if ((TInt16)(aEvent.usButtonData&0x8000)== 0) // positive number
379 if (iZ < TheMultiTouch->iMaxPressure)
383 iZ += TheMultiTouch->iPressureStep;//Pressure step
384 if (iZ > TheMultiTouch->iMaxPressure)
386 iZ = TheMultiTouch->iMaxPressure;
391 iZ += TheMultiTouch->iProximityStep; //Proximity step
397 if (iZ > TheMultiTouch->iZMaxRange)
401 iZ -= TheMultiTouch->iProximityStep;//Proximity step
402 if (iZ < TheMultiTouch->iZMaxRange)
404 iZ = TheMultiTouch->iZMaxRange;
409 iZ -= TheMultiTouch->iPressureStep;//Pressure step
415 MultiTouchWndPointer(message, iX, iY, iZ, iId);
420 * Show the cursor window when the cursor is inside the client area
422 void DMultiMouse::ShowMousePos(HWND aHWnd)
424 RECT client = {0,0,0,0};
427 GetWindowRect(aHWnd, &client);
428 POINT pt = {iX-client.left,iY-client.top};
429 iCursorWnd.SetPosition(pt);
434 void DMultiMouse::CorrectSystemMouse()
439 if (GetCursorPos(&pos)) // if failed, pos contains garbage.
447 SetCursorPos(iPrimary->iX,iPrimary->iY);
452 * a static function to check how many mice are connected
454 bool DMultiTouch::Init()
456 HMODULE hModule = GetModuleHandleA("user32.dll");
461 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
462 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
465 // Check for Win 2K or higher version
466 if (osvi.dwMajorVersion < 5)
471 // Not supported on Win2K
472 if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0))
477 pfnRegisterRawInputDevices = (TYPEOF_RegisterRawInputDevices)GetProcAddress(hModule, "RegisterRawInputDevices");
478 pfnGetRawInputData = (TYPEOF_GetRawInputData)GetProcAddress(hModule, "GetRawInputData");
479 pfnGetRawInputDeviceList = (TYPEOF_GetRawInputDeviceList)GetProcAddress(hModule, "GetRawInputDeviceList");
481 if((pfnRegisterRawInputDevices == NULL) || (pfnGetRawInputData == NULL) || (pfnGetRawInputDeviceList == NULL))
488 if (pfnGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
493 RAWINPUTDEVICELIST* pRawInputDeviceList = new RAWINPUTDEVICELIST[ nDevices ];
494 if (!pRawInputDeviceList)
499 pfnGetRawInputDeviceList(pRawInputDeviceList, &nDevices,
500 sizeof(RAWINPUTDEVICELIST));
503 for (UINT i=0; i<nDevices; i++)
505 RAWINPUTDEVICELIST& dev = pRawInputDeviceList[i];
506 if (dev.dwType == RIM_TYPEMOUSE)
508 if (DMultiMouse::Add(dev)!=KErrNone)
510 //free the device list
511 delete[] pRawInputDeviceList;
517 delete[] pRawInputDeviceList;
519 // Multitouch is supported when more than 2 mice are connected (including the hidden RID mouse)
520 return DMultiMouse::iNumMice > 2;