|
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". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // wins\specific\multitouch.cpp |
|
15 // |
|
16 // |
|
17 #include "multitouch.h" |
|
18 #include "resource.h" |
|
19 |
|
20 #define KDefaultMaxProximity -100 |
|
21 #define KDefaultMaxPressure 5000 |
|
22 #define KDefaultPressureStep 500 |
|
23 #define KDefaultProximityStep 5 |
|
24 |
|
25 // Static members |
|
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; |
|
34 |
|
35 // Function pointers for raw input APIs |
|
36 TYPEOF_RegisterRawInputDevices pfnRegisterRawInputDevices= NULL; |
|
37 TYPEOF_GetRawInputData pfnGetRawInputData= NULL; |
|
38 TYPEOF_GetRawInputDeviceList pfnGetRawInputDeviceList=NULL; |
|
39 |
|
40 /** |
|
41 * Initialise the proximity and pressure information if undefined by the user |
|
42 */ |
|
43 DMultiTouch::DMultiTouch(TInt aProximityStep, TInt aPressureStep) |
|
44 { |
|
45 iZMaxRange = KDefaultMaxProximity; |
|
46 iMaxPressure = KDefaultMaxPressure; |
|
47 iProximityStep = (aProximityStep == -1) ? KDefaultProximityStep : aProximityStep; |
|
48 iPressureStep = (aPressureStep == -1) ? KDefaultPressureStep : aPressureStep; |
|
49 } |
|
50 |
|
51 /** |
|
52 * Register all the mice |
|
53 */ |
|
54 BOOL DMultiTouch::Register() |
|
55 { |
|
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; |
|
61 ShowCursors(); |
|
62 return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); |
|
63 } |
|
64 |
|
65 /** |
|
66 * Unregister mice devices |
|
67 */ |
|
68 BOOL DMultiTouch::UnRegister() |
|
69 { |
|
70 RAWINPUTDEVICE device; |
|
71 device.usUsagePage = 0x01; |
|
72 device.usUsage = 0x02; |
|
73 device.dwFlags = RIDEV_REMOVE; |
|
74 device.hwndTarget = NULL; |
|
75 HideCursors(); |
|
76 return pfnRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); |
|
77 } |
|
78 |
|
79 |
|
80 /* * Handle multi-input Window messages |
|
81 */ |
|
82 void DMultiTouch::OnWmInput(HWND aHWnd,TUint aMessage,TUint aWParam,TUint aLParam,HWND aParentHwnd) |
|
83 { |
|
84 RAWINPUT ri; |
|
85 UINT dwSize = sizeof(ri); |
|
86 if (pfnGetRawInputData((HRAWINPUT)aLParam, RID_INPUT, &ri, &dwSize, sizeof(RAWINPUTHEADER))==(UINT)-1) |
|
87 { |
|
88 OutputDebugString(TEXT("GetRawInputData has an error !\n")); |
|
89 } |
|
90 else if (ri.header.dwType == RIM_TYPEMOUSE) |
|
91 { |
|
92 DMultiMouse* mouse = DMultiMouse::Find(ri.header.hDevice); |
|
93 if (mouse) |
|
94 { |
|
95 if (!DMultiMouse::iPrimary) |
|
96 { |
|
97 DMultiMouse::iPrimary = mouse; |
|
98 DMultiMouse::iPrimary->iIsPrimary = TRUE; |
|
99 } |
|
100 mouse->HandleRawMouseEvent(ri.data.mouse, aParentHwnd); |
|
101 } |
|
102 } |
|
103 DefWindowProcA(aHWnd, aMessage, aWParam, aLParam); |
|
104 } |
|
105 |
|
106 void DMultiTouch::HideCursors() |
|
107 { |
|
108 for (int ii=0; ii<DMultiMouse::iNumMice; ii++) |
|
109 { |
|
110 DMultiMouse::iMice[ii].iCursorWnd.Hide(); |
|
111 } |
|
112 } |
|
113 |
|
114 void DMultiTouch::ShowCursors() |
|
115 { |
|
116 for (int ii=0; ii<DMultiMouse::iNumMice; ii++) |
|
117 { |
|
118 DMultiMouse::iMice[ii].iCursorWnd.Show(); |
|
119 } |
|
120 } |
|
121 |
|
122 /** |
|
123 * The cursor window procedure |
|
124 */ |
|
125 static LRESULT CALLBACK CursorWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) |
|
126 { |
|
127 if (msg==WM_PAINT) |
|
128 { |
|
129 PAINTSTRUCT ps; |
|
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); |
|
133 EndPaint(hwnd, &ps); |
|
134 return 0; |
|
135 } |
|
136 return DefWindowProc(hwnd, msg, wp, lp); |
|
137 } |
|
138 |
|
139 CursorWindow::CursorWindow(): iHwnd(NULL),iCursor(NULL),iNumber(0) |
|
140 { |
|
141 } |
|
142 |
|
143 HWND CursorWindow::Create(HMODULE hm, HWND hwndParent, int number) |
|
144 { |
|
145 // Create the window |
|
146 static ATOM atom = NULL; |
|
147 if (!atom) |
|
148 { |
|
149 WNDCLASSEX wcex; |
|
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); |
|
158 } |
|
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); |
|
161 iNumber = number; |
|
162 return iHwnd; |
|
163 } |
|
164 |
|
165 void CursorWindow::Show() |
|
166 { |
|
167 ShowWindow(iHwnd, SW_NORMAL); |
|
168 } |
|
169 |
|
170 void CursorWindow::Hide() |
|
171 { |
|
172 ShowWindow(iHwnd, SW_HIDE); |
|
173 } |
|
174 |
|
175 BOOL CursorWindow::SetCursor(HCURSOR hc) |
|
176 { |
|
177 // Duplicate the cursor (because we're going to draw a number on the mask) |
|
178 if (iCursor) |
|
179 { |
|
180 DestroyCursor(iCursor); |
|
181 iCursor = NULL; |
|
182 } |
|
183 iCursor = CopyCursor(hc); |
|
184 |
|
185 // Get information about the cursor, and select its mask bitmap into a temporary DC. |
|
186 ICONINFO ii; |
|
187 GetIconInfo(iCursor, &ii); |
|
188 iHotspot.x = ii.xHotspot; |
|
189 iHotspot.y = ii.yHotspot; |
|
190 HDC hdc = CreateCompatibleDC(NULL); |
|
191 SelectObject(hdc, ii.hbmMask); |
|
192 |
|
193 // Get the cursor's pixel size |
|
194 BITMAPINFO bmi; |
|
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; |
|
200 |
|
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; |
|
206 |
|
207 // Draw the number into the mask |
|
208 char ach[4]; |
|
209 int ld = iNumber/10; |
|
210 int rd = iNumber % 10; |
|
211 if (ld > 0) |
|
212 { |
|
213 wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d%d"), ld,rd); |
|
214 } |
|
215 else |
|
216 { |
|
217 wsprintf((LPTSTR)ach, (LPCTSTR)TEXT("%d"), rd); |
|
218 } |
|
219 |
|
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); |
|
224 DeleteObject(hf); |
|
225 |
|
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); |
|
231 |
|
232 // Set the window to the size of the cursor |
|
233 SetWindowPos(iHwnd, NULL,0,0,cx,cyy, SWP_NOMOVE); |
|
234 |
|
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++) |
|
239 { |
|
240 for (int x=0 ; x<cx ; x++) |
|
241 { |
|
242 if (bits[(cy-y-1)*cx+x]==0) |
|
243 { |
|
244 HRGN rgnPix = CreateRectRgn(x,y, x+1,y+1); |
|
245 CombineRgn(rgn, rgn, rgnPix, RGN_OR); |
|
246 DeleteObject(rgnPix); |
|
247 } |
|
248 } |
|
249 } |
|
250 |
|
251 // Cleanup |
|
252 HeapFree(heap, 0, bits); |
|
253 DeleteDC(hdc); |
|
254 |
|
255 // Set the window's clipping region to the cursor-shaped region |
|
256 SetWindowRgn(iHwnd, rgn, TRUE); |
|
257 return TRUE; |
|
258 } |
|
259 |
|
260 void CursorWindow::GetPosition(POINT& pt) |
|
261 { |
|
262 pt.x = 0; |
|
263 pt.y = 0; |
|
264 MapWindowPoints(iHwnd, GetParent(iHwnd), &pt, 1); |
|
265 pt.x += iHotspot.x; |
|
266 pt.y += iHotspot.y; |
|
267 } |
|
268 |
|
269 void CursorWindow::SetPosition(POINT& pt) |
|
270 { |
|
271 SetWindowPos(iHwnd, NULL, pt.x - iHotspot.x, pt.y - iHotspot.y, 0, 0, SWP_NOSIZE); |
|
272 } |
|
273 |
|
274 /** |
|
275 * Add the mouse device to the collection |
|
276 */ |
|
277 TInt DMultiMouse::Add(RAWINPUTDEVICELIST& aDev) |
|
278 { |
|
279 if (iNumMice < KMaxMice) |
|
280 { |
|
281 DMultiMouse& mouse = iMice[iNumMice]; |
|
282 mouse.iDevice = aDev.hDevice; |
|
283 iNumMice++; |
|
284 return KErrNone; |
|
285 } |
|
286 else |
|
287 { |
|
288 return KErrOverflow; |
|
289 } |
|
290 } |
|
291 |
|
292 DMultiMouse::DMultiMouse() : |
|
293 iX(-1), iY(-1), iZ(0), iDevice(0),iId(-1) |
|
294 { |
|
295 } |
|
296 |
|
297 DMultiMouse* DMultiMouse::Find(HANDLE aHandle) |
|
298 { |
|
299 for (TInt ii=0; ii<iNumMice; ii++) |
|
300 { |
|
301 DMultiMouse& mouse = iMice[ii]; |
|
302 if (mouse.iDevice == aHandle) |
|
303 return &mouse; |
|
304 } |
|
305 return NULL; |
|
306 } |
|
307 |
|
308 void DMultiMouse::HandleRawMouseEvent(RAWMOUSE& aEvent, HWND aWnd) |
|
309 { |
|
310 // give this pointer an id, if it doesn't already have one |
|
311 if (iId == -1) |
|
312 { |
|
313 iId = iMouseId++; |
|
314 } |
|
315 |
|
316 // Create the cursor window and set the cursor if not done yet |
|
317 if (iCursorWnd.iHwnd == NULL) |
|
318 { |
|
319 iCursorWnd.Create((HINSTANCE)0, aWnd,iId); |
|
320 iCursorWnd.SetCursor(LoadCursorA(NULL, (LPCSTR)IDC_ARROW)); |
|
321 } |
|
322 |
|
323 CorrectSystemMouse(); |
|
324 |
|
325 // recalc mouse position |
|
326 if (iX == -1) |
|
327 { |
|
328 // initial position |
|
329 iX = iPrimary->iX; |
|
330 iY = iPrimary->iY; |
|
331 } |
|
332 |
|
333 if (aEvent.usFlags & MOUSE_MOVE_ABSOLUTE) |
|
334 { |
|
335 // absolute position info can update all pointers |
|
336 iX = aEvent.lLastX; |
|
337 iY = aEvent.lLastY; |
|
338 } |
|
339 else if (!iIsPrimary) |
|
340 { |
|
341 // relative position info updates non-primary pointers, |
|
342 iX += aEvent.lLastX; |
|
343 iY += aEvent.lLastY; |
|
344 } |
|
345 |
|
346 // Show the cursor window |
|
347 ShowMousePos(aWnd); |
|
348 |
|
349 TInt message = WM_MOUSEMOVE; |
|
350 |
|
351 // get button state |
|
352 if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) |
|
353 { |
|
354 message = WM_LBUTTONDOWN; |
|
355 iZ = 0; |
|
356 } |
|
357 |
|
358 if (aEvent.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) |
|
359 { |
|
360 message = WM_LBUTTONUP; |
|
361 iZ = 0; |
|
362 } |
|
363 |
|
364 if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) |
|
365 { |
|
366 message = WM_RBUTTONDOWN; |
|
367 } |
|
368 |
|
369 if (aEvent.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) |
|
370 { |
|
371 message = WM_RBUTTONUP; |
|
372 } |
|
373 |
|
374 if (aEvent.usButtonFlags & RI_MOUSE_WHEEL) |
|
375 { |
|
376 message = WM_MOUSEWHEEL; |
|
377 if ((TInt16)(aEvent.usButtonData&0x8000)== 0) // positive number |
|
378 { |
|
379 if (iZ < TheMultiTouch->iMaxPressure) |
|
380 { |
|
381 if (iZ>=0) |
|
382 { // pressure range |
|
383 iZ += TheMultiTouch->iPressureStep;//Pressure step |
|
384 if (iZ > TheMultiTouch->iMaxPressure) |
|
385 { |
|
386 iZ = TheMultiTouch->iMaxPressure; |
|
387 } |
|
388 } |
|
389 else |
|
390 { // proximity range |
|
391 iZ += TheMultiTouch->iProximityStep; //Proximity step |
|
392 } |
|
393 } |
|
394 } |
|
395 else |
|
396 { |
|
397 if (iZ > TheMultiTouch->iZMaxRange) |
|
398 { |
|
399 if (iZ <= 0) |
|
400 {// proximity range |
|
401 iZ -= TheMultiTouch->iProximityStep;//Proximity step |
|
402 if (iZ < TheMultiTouch->iZMaxRange) |
|
403 { |
|
404 iZ = TheMultiTouch->iZMaxRange; |
|
405 } |
|
406 } |
|
407 else |
|
408 {// pressure range |
|
409 iZ -= TheMultiTouch->iPressureStep;//Pressure step |
|
410 } |
|
411 } |
|
412 } |
|
413 } |
|
414 |
|
415 MultiTouchWndPointer(message, iX, iY, iZ, iId); |
|
416 |
|
417 } |
|
418 |
|
419 /** |
|
420 * Show the cursor window when the cursor is inside the client area |
|
421 */ |
|
422 void DMultiMouse::ShowMousePos(HWND aHWnd) |
|
423 { |
|
424 RECT client = {0,0,0,0}; |
|
425 if(aHWnd) |
|
426 { |
|
427 GetWindowRect(aHWnd, &client); |
|
428 POINT pt = {iX-client.left,iY-client.top}; |
|
429 iCursorWnd.SetPosition(pt); |
|
430 } |
|
431 iCursorWnd.Show(); |
|
432 } |
|
433 |
|
434 void DMultiMouse::CorrectSystemMouse() |
|
435 { |
|
436 if (iIsPrimary) |
|
437 { |
|
438 POINT pos; |
|
439 if (GetCursorPos(&pos)) // if failed, pos contains garbage. |
|
440 { |
|
441 iX = pos.x; |
|
442 iY = pos.y; |
|
443 } |
|
444 } |
|
445 else |
|
446 { |
|
447 SetCursorPos(iPrimary->iX,iPrimary->iY); |
|
448 } |
|
449 } |
|
450 |
|
451 /** |
|
452 * a static function to check how many mice are connected |
|
453 */ |
|
454 bool DMultiTouch::Init() |
|
455 { |
|
456 HMODULE hModule = GetModuleHandleA("user32.dll"); |
|
457 if(hModule == NULL) |
|
458 return FALSE; |
|
459 |
|
460 OSVERSIONINFO osvi; |
|
461 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); |
|
462 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
|
463 |
|
464 GetVersionEx(&osvi); |
|
465 // Check for Win 2K or higher version |
|
466 if (osvi.dwMajorVersion < 5) |
|
467 { |
|
468 return FALSE; |
|
469 } |
|
470 |
|
471 // Not supported on Win2K |
|
472 if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) |
|
473 { |
|
474 return FALSE; |
|
475 } |
|
476 |
|
477 pfnRegisterRawInputDevices = (TYPEOF_RegisterRawInputDevices)GetProcAddress(hModule, "RegisterRawInputDevices"); |
|
478 pfnGetRawInputData = (TYPEOF_GetRawInputData)GetProcAddress(hModule, "GetRawInputData"); |
|
479 pfnGetRawInputDeviceList = (TYPEOF_GetRawInputDeviceList)GetProcAddress(hModule, "GetRawInputDeviceList"); |
|
480 |
|
481 if((pfnRegisterRawInputDevices == NULL) || (pfnGetRawInputData == NULL) || (pfnGetRawInputDeviceList == NULL)) |
|
482 { |
|
483 return FALSE; |
|
484 } |
|
485 |
|
486 UINT nDevices; |
|
487 |
|
488 if (pfnGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) |
|
489 { |
|
490 return FALSE; |
|
491 } |
|
492 |
|
493 RAWINPUTDEVICELIST* pRawInputDeviceList = new RAWINPUTDEVICELIST[ nDevices ]; |
|
494 if (!pRawInputDeviceList) |
|
495 { |
|
496 return FALSE; |
|
497 } |
|
498 |
|
499 pfnGetRawInputDeviceList(pRawInputDeviceList, &nDevices, |
|
500 sizeof(RAWINPUTDEVICELIST)); |
|
501 |
|
502 |
|
503 for (UINT i=0; i<nDevices; i++) |
|
504 { |
|
505 RAWINPUTDEVICELIST& dev = pRawInputDeviceList[i]; |
|
506 if (dev.dwType == RIM_TYPEMOUSE) |
|
507 { |
|
508 if (DMultiMouse::Add(dev)!=KErrNone) |
|
509 { |
|
510 //free the device list |
|
511 delete[] pRawInputDeviceList; |
|
512 return FALSE; |
|
513 } |
|
514 } |
|
515 } |
|
516 |
|
517 delete[] pRawInputDeviceList; |
|
518 |
|
519 // Multitouch is supported when more than 2 mice are connected (including the hidden RID mouse) |
|
520 return DMultiMouse::iNumMice > 2; |
|
521 } |
|
522 |