0032473: Visualization, WNT_Window::ProcessMessage() - handle WM_TOUCH
[occt.git] / src / WNT / WNT_Window.cxx
1 // Copyright (c) 1998-1999 Matra Datavision
2 // Copyright (c) 1999-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #if defined(_WIN32)
16   // include windows.h first to have all definitions available
17   #include <windows.h>
18 #endif
19
20 #include <WNT_Window.hxx>
21
22 #if defined(_WIN32) && !defined(OCCT_UWP)
23
24 #include <Aspect_ScrollDelta.hxx>
25 #include <Aspect_WindowDefinitionError.hxx>
26 #include <Aspect_WindowError.hxx>
27 #include <Aspect_WindowInputListener.hxx>
28 #include <Message.hxx>
29 #include <NCollection_LocalArray.hxx>
30 #include <TCollection_ExtendedString.hxx>
31 #include <WNT_WClass.hxx>
32 #include <WNT_HIDSpaceMouse.hxx>
33
34 IMPLEMENT_STANDARD_RTTIEXT(WNT_Window, Aspect_Window)
35
36 #ifndef MOUSEEVENTF_FROMTOUCH
37 #define MOUSEEVENTF_FROMTOUCH 0xFF515700
38 #endif
39
40 //! Auxiliary tool for handling WM_TOUCH events.
41 //! Dynamically loads functions from User32 available since Win7 and later.
42 class WNT_Window::TouchInputHelper : public Standard_Transient
43 {
44 public:
45   typedef BOOL (WINAPI *RegisterTouchWindow_t)(HWND hwnd, ULONG ulFlags);
46   typedef BOOL (WINAPI *UnregisterTouchWindow_t)(HWND hwnd);
47   typedef BOOL (WINAPI *GetTouchInputInfo_t)(HTOUCHINPUT hTouchInput,
48                                              UINT cInputs,
49                                              PTOUCHINPUT pInputs,
50                                              int         cbSize);
51   typedef BOOL (WINAPI *CloseTouchInputHandle_t)(HTOUCHINPUT hTouchInput);
52
53   typedef NCollection_LocalArray<TOUCHINPUT, 16> InnerTouchArray;
54
55 public:
56
57   //! Main constructor.
58   TouchInputHelper()
59   : myRegisterTouchWindow (NULL),
60     myUnregisterTouchWindow (NULL),
61     myGetTouchInputInfo (NULL),
62     myCloseTouchInputHandle (NULL),
63     myIsRegistered (false)
64   {
65     HMODULE aUser32Module = GetModuleHandleW (L"User32");
66     if (aUser32Module != NULL)
67     {
68       // User32 should be already loaded
69       myRegisterTouchWindow   = (RegisterTouchWindow_t   )GetProcAddress (aUser32Module, "RegisterTouchWindow");
70       myUnregisterTouchWindow = (UnregisterTouchWindow_t )GetProcAddress (aUser32Module, "UnregisterTouchWindow");
71       myGetTouchInputInfo     = (GetTouchInputInfo_t     )GetProcAddress (aUser32Module, "GetTouchInputInfo");
72       myCloseTouchInputHandle = (CloseTouchInputHandle_t )GetProcAddress (aUser32Module, "CloseTouchInputHandle");
73     }
74   }
75
76   //! Return TRUE if window has been registered.
77   bool IsRegistered() const { return myIsRegistered; }
78
79   //! Register window to receive WM_TOUCH events.
80   bool Register (HWND theWin)
81   {
82     if (myRegisterTouchWindow == NULL)
83     {
84       return false;
85     }
86
87     if (myRegisterTouchWindow (theWin, TWF_FINETOUCH))
88     {
89       myIsRegistered = true;
90       return true;
91     }
92     //Message::SendTrace() << "RegisterTouchWindow() FAILED";
93     return false;
94   }
95
96   //! Array of touches retrieved from HTOUCHINPUT.
97   class TouchInputInfo : public InnerTouchArray
98   {
99   public:
100     //! Main constructor.
101     TouchInputInfo (const TouchInputHelper& theHelper,
102                     const MSG& theMsg)
103     : InnerTouchArray (0),
104       myHelper (&theHelper),
105       myHInput ((HTOUCHINPUT )theMsg.lParam)
106     {
107       const int aNbTouches = LOWORD(theMsg.wParam);
108       if (aNbTouches > 0
109        && theHelper.myGetTouchInputInfo != NULL)
110       {
111         InnerTouchArray::Allocate (aNbTouches);
112         TOUCHINPUT* aTouches = InnerTouchArray::operator TOUCHINPUT*();
113         if (!theHelper.myGetTouchInputInfo (myHInput, aNbTouches, aTouches, sizeof(TOUCHINPUT)))
114         {
115           InnerTouchArray::Deallocate();
116         }
117       }
118     }
119
120     //! Destructor.
121     ~TouchInputInfo()
122     {
123       if (myHelper->myCloseTouchInputHandle != NULL)
124       {
125         myHelper->myCloseTouchInputHandle (myHInput);
126       }
127     }
128
129   private:
130     const TouchInputHelper* myHelper;
131     HTOUCHINPUT myHInput;
132   };
133
134 private:
135
136   RegisterTouchWindow_t   myRegisterTouchWindow;
137   UnregisterTouchWindow_t myUnregisterTouchWindow;
138   GetTouchInputInfo_t     myGetTouchInputInfo;
139   CloseTouchInputHandle_t myCloseTouchInputHandle;
140   bool                    myIsRegistered;
141
142 };
143
144 // =======================================================================
145 // function : WNT_Window
146 // purpose  :
147 // =======================================================================
148 WNT_Window::WNT_Window (const Standard_CString           theTitle,
149                         const Handle(WNT_WClass)&        theClass,
150                         const WNT_Dword&                 theStyle,
151                         const Standard_Integer           thePxLeft,
152                         const Standard_Integer           thePxTop,
153                         const Standard_Integer           thePxWidth,
154                         const Standard_Integer           thePxHeight,
155                         const Quantity_NameOfColor       theBackColor,
156                         const Aspect_Handle              theParent,
157                         const Aspect_Handle              theMenu,
158                         const Standard_Address           theClientStruct)
159 : Aspect_Window(),
160   myWClass (theClass),
161   myHWindow (NULL),
162   myHParentWindow (NULL),
163   myXLeft (thePxLeft),
164   myYTop  (thePxTop),
165   myXRight (thePxLeft + thePxWidth),
166   myYBottom (thePxTop + thePxHeight),
167   myIsForeign (Standard_False)
168 {
169   if (thePxWidth <= 0 || thePxHeight <= 0)
170   {
171     throw Aspect_WindowDefinitionError("Coordinate(s) out of range");
172   }
173
174   DWORD aStyle = theStyle;
175   if (theParent && !(theStyle & WS_CHILD))
176   {
177     aStyle |= WS_CHILD | WS_CLIPSIBLINGS;
178   }
179   else if (!theParent && !(theStyle & WS_CLIPCHILDREN))
180   {
181     aStyle |= WS_CLIPCHILDREN;
182   }
183
184   // include decorations in the window dimensions to reproduce same behavior of Xw_Window
185   RECT aRect;
186   aRect.top    = myYTop;
187   aRect.bottom = myYBottom;
188   aRect.left   = myXLeft;
189   aRect.right  = myXRight;
190   AdjustWindowRect (&aRect, aStyle, theMenu != NULL ? TRUE : FALSE);
191   myXLeft   = aRect.left;
192   myYTop    = aRect.top;
193   myXRight  = aRect.right;
194   myYBottom = aRect.bottom;
195
196   const TCollection_ExtendedString aTitleW (theTitle);
197   const TCollection_ExtendedString aClassNameW (myWClass->Name());
198   myHWindow = CreateWindowW (aClassNameW.ToWideString(), aTitleW.ToWideString(),
199                              aStyle,
200                              myXLeft, myYTop,
201                              (myXRight - myXLeft), (myYBottom - myYTop),
202                              (HWND )theParent,
203                              (HMENU )theMenu,
204                              (HINSTANCE )myWClass->Instance(),
205                              theClientStruct);
206   if (!myHWindow)
207   {
208     throw Aspect_WindowDefinitionError("Unable to create window");
209   }
210
211   myHParentWindow = theParent;
212   SetBackground (theBackColor);
213 }
214
215 // =======================================================================
216 // function : WNT_Window
217 // purpose  :
218 // =======================================================================
219 WNT_Window::WNT_Window (const Aspect_Handle        theHandle,
220                         const Quantity_NameOfColor theBackColor)
221 : myHWindow (theHandle),
222   myHParentWindow (GetParent ((HWND )theHandle)),
223   myXLeft (0),
224   myYTop  (0),
225   myXRight (0),
226   myYBottom (0),
227   myIsForeign (Standard_True)
228 {
229   SetBackground (theBackColor);
230
231   WINDOWPLACEMENT aPlace = {};
232   aPlace.length = sizeof(WINDOWPLACEMENT);
233   ::GetWindowPlacement ((HWND )myHWindow, &aPlace);
234
235   myXLeft   = aPlace.rcNormalPosition.left;
236   myYTop    = aPlace.rcNormalPosition.top;
237   myXRight  = aPlace.rcNormalPosition.right;
238   myYBottom = aPlace.rcNormalPosition.bottom;
239 }
240
241 // =======================================================================
242 // function : ~WNT_Window
243 // purpose  :
244 // =======================================================================
245 WNT_Window::~WNT_Window()
246 {
247   if (myHWindow == NULL
248    || myIsForeign)
249   {
250     return;
251   }
252
253   DestroyWindow ((HWND )myHWindow);
254   myIsForeign = Standard_False;
255 }
256
257 // =======================================================================
258 // function : SetCursor
259 // purpose  :
260 // =======================================================================
261 void WNT_Window::SetCursor (const Aspect_Handle theCursor) const
262 {
263   ::SetClassLongPtrW ((HWND )myHWindow, GCLP_HCURSOR, (LONG_PTR )theCursor);
264 }
265
266 // =======================================================================
267 // function : IsMapped
268 // purpose  :
269 // =======================================================================
270 Standard_Boolean WNT_Window::IsMapped() const
271 {
272   if (IsVirtual())
273   {
274     return Standard_True;
275   }
276
277   WINDOWPLACEMENT aPlace = {};
278   aPlace.length = sizeof(WINDOWPLACEMENT);
279   ::GetWindowPlacement ((HWND )myHWindow, &aPlace);
280   return !(aPlace.showCmd == SW_HIDE
281         || aPlace.showCmd == SW_MINIMIZE);
282 }
283
284 // =======================================================================
285 // function : Map
286 // purpose  :
287 // =======================================================================
288 void WNT_Window::Map() const
289 {
290   if (!IsVirtual())
291   {
292     Map (SW_SHOW);
293   }
294 }
295
296 // =======================================================================
297 // function : Map
298 // purpose  :
299 // =======================================================================
300 void WNT_Window::Map (const Standard_Integer theMapMode) const
301 {
302   if (IsVirtual())
303   {
304     return;
305   }
306
307   ::ShowWindow   ((HWND )myHWindow, theMapMode);
308   ::UpdateWindow ((HWND )myHWindow);
309 }
310
311 // =======================================================================
312 // function : Unmap
313 // purpose  :
314 // =======================================================================
315 void WNT_Window::Unmap() const
316 {
317   Map (SW_HIDE);
318 }
319
320 // =======================================================================
321 // function : DoResize
322 // purpose  :
323 // =======================================================================
324 Aspect_TypeOfResize WNT_Window::DoResize()
325 {
326   if (IsVirtual())
327   {
328     return Aspect_TOR_UNKNOWN;
329   }
330
331   WINDOWPLACEMENT aPlace = {};
332   aPlace.length = sizeof(WINDOWPLACEMENT);
333   GetWindowPlacement ((HWND )myHWindow, &aPlace);
334   if (aPlace.showCmd == SW_SHOWMINIMIZED)
335   {
336     return Aspect_TOR_UNKNOWN;
337   }
338
339   int aMask = 0;
340   if (Abs ((int )aPlace.rcNormalPosition.left   - myXLeft  ) > 2) { aMask |= 1; }
341   if (Abs ((int )aPlace.rcNormalPosition.right  - myXRight ) > 2) { aMask |= 2; }
342   if (Abs ((int )aPlace.rcNormalPosition.top    - myYTop   ) > 2) { aMask |= 4; }
343   if (Abs ((int )aPlace.rcNormalPosition.bottom - myYBottom) > 2) { aMask |= 8; }
344
345   myXLeft   = aPlace.rcNormalPosition.left;
346   myXRight  = aPlace.rcNormalPosition.right;
347   myYTop    = aPlace.rcNormalPosition.top;
348   myYBottom = aPlace.rcNormalPosition.bottom;
349   switch (aMask)
350   {
351     case 0:  return Aspect_TOR_NO_BORDER;
352     case 1:  return Aspect_TOR_LEFT_BORDER;
353     case 2:  return Aspect_TOR_RIGHT_BORDER;
354     case 4:  return Aspect_TOR_TOP_BORDER;
355     case 5:  return Aspect_TOR_LEFT_AND_TOP_BORDER;
356     case 6:  return Aspect_TOR_TOP_AND_RIGHT_BORDER;
357     case 8:  return Aspect_TOR_BOTTOM_BORDER;
358     case 9:  return Aspect_TOR_BOTTOM_AND_LEFT_BORDER;
359     case 10: return Aspect_TOR_RIGHT_AND_BOTTOM_BORDER;
360   }
361   return Aspect_TOR_UNKNOWN;
362 }
363
364 // =======================================================================
365 // function : Ratio
366 // purpose  :
367 // =======================================================================
368 Standard_Real WNT_Window::Ratio() const
369 {
370   if (IsVirtual())
371   {
372     return Standard_Real(myXRight - myXLeft)/ Standard_Real(myYBottom - myYTop);
373   }
374
375   RECT aRect = {};
376   GetClientRect ((HWND )myHWindow, &aRect);
377   return Standard_Real(aRect.right - aRect.left) / Standard_Real(aRect.bottom - aRect.top);
378 }
379
380 // =======================================================================
381 // function : Position
382 // purpose  :
383 // =======================================================================
384 void WNT_Window::Position (Standard_Integer& theX1, Standard_Integer& theY1,
385                            Standard_Integer& theX2, Standard_Integer& theY2) const
386 {
387   if (IsVirtual())
388   {
389     theX1  = myXLeft;
390     theX2  = myXRight;
391     theY1  = myYTop;
392     theY2  = myYBottom;
393     return;
394   }
395
396   RECT aRect = {};
397   ::GetClientRect ((HWND )myHWindow, &aRect);
398
399   POINT aPntLeft, aPntRight;
400   aPntLeft.x = aPntLeft.y = 0;
401   ::ClientToScreen ((HWND )myHWindow, &aPntLeft);
402   aPntRight.x = aRect.right;
403   aPntRight.y = aRect.bottom;
404   ::ClientToScreen ((HWND )myHWindow, &aPntRight);
405
406   if (myHParentWindow != NULL)
407   {
408     ::ScreenToClient ((HWND )myHParentWindow, &aPntLeft);
409     ::ScreenToClient ((HWND )myHParentWindow, &aPntRight);
410   }
411
412   theX1 = aPntLeft.x;
413   theX2 = aPntRight.x;
414   theY1 = aPntLeft.y;
415   theY2 = aPntRight.y;
416 }
417
418 // =======================================================================
419 // function : Size
420 // purpose  :
421 // =======================================================================
422 void WNT_Window::Size (Standard_Integer& theWidth,
423                        Standard_Integer& theHeight) const
424 {
425   if (IsVirtual())
426   {
427     theWidth  = myXRight - myXLeft;
428     theHeight = myYBottom - myYTop;
429     return;
430   }
431
432   RECT aRect = {};
433   ::GetClientRect ((HWND )myHWindow, &aRect);
434   theWidth  = aRect.right;
435   theHeight = aRect.bottom;
436 }
437
438 // =======================================================================
439 // function : SetPos
440 // purpose  :
441 // =======================================================================
442 void WNT_Window::SetPos (const Standard_Integer theX,  const Standard_Integer theY,
443                          const Standard_Integer theX1, const Standard_Integer theY1)
444 {
445   myXLeft   = theX;
446   myYTop    = theY;
447   myXRight  = theX1;
448   myYBottom = theY1;
449 }
450
451 // =======================================================================
452 // function : SetTitle
453 // purpose  :
454 // =======================================================================
455 void WNT_Window::SetTitle (const TCollection_AsciiString& theTitle)
456 {
457   const TCollection_ExtendedString aTitleW (theTitle);
458   SetWindowTextW ((HWND )myHWindow, aTitleW.ToWideString());
459 }
460
461 // =======================================================================
462 // function : InvalidateContent
463 // purpose  :
464 // =======================================================================
465 void WNT_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
466 {
467   if (myHWindow != NULL)
468   {
469     ::InvalidateRect ((HWND )myHWindow, NULL, TRUE);
470   }
471 }
472
473 // =======================================================================
474 // function : VirtualKeyFromNative
475 // purpose  :
476 // =======================================================================
477 Aspect_VKey WNT_Window::VirtualKeyFromNative (Standard_Integer theKey)
478 {
479   if (theKey >= Standard_Integer('0')
480    && theKey <= Standard_Integer('9'))
481   {
482     return Aspect_VKey((theKey - Standard_Integer('0')) + Aspect_VKey_0);
483   }
484   if (theKey >= Standard_Integer('A')
485    && theKey <= Standard_Integer('Z'))
486   {
487     // main latin alphabet keys
488     return Aspect_VKey((theKey - Standard_Integer('A')) + Aspect_VKey_A);
489   }
490   if (theKey >= VK_F1
491    && theKey <= VK_F24)
492   {
493     // special keys
494     if (theKey <= VK_F12)
495     {
496       return Aspect_VKey((theKey - VK_F1) + Aspect_VKey_F1);
497     }
498     return Aspect_VKey_UNKNOWN;
499   }
500   if (theKey >= VK_NUMPAD0
501    && theKey <= VK_NUMPAD9)
502   {
503     // numpad keys
504     return Aspect_VKey((theKey - VK_NUMPAD0) + Aspect_VKey_Numpad0);
505   }
506
507   switch (theKey)
508   {
509     case VK_LBUTTON:
510     case VK_RBUTTON:
511     case VK_CANCEL:
512     case VK_MBUTTON:
513     case VK_XBUTTON1:
514     case VK_XBUTTON2:
515       return Aspect_VKey_UNKNOWN;
516     case VK_BACK:
517       return Aspect_VKey_Backspace;
518     case VK_TAB:
519       return Aspect_VKey_Tab;
520     case VK_CLEAR:
521       return Aspect_VKey_UNKNOWN;
522     case VK_RETURN:
523       return Aspect_VKey_Enter;
524     case VK_SHIFT:
525       return Aspect_VKey_Shift;
526     case VK_CONTROL:
527       return Aspect_VKey_Control;
528     case VK_MENU:
529       return Aspect_VKey_Alt; //Aspect_VKey_Menu;
530     case VK_PAUSE:
531     case VK_CAPITAL:
532       return Aspect_VKey_UNKNOWN;
533     case VK_ESCAPE:
534       return Aspect_VKey_Escape;
535     case VK_CONVERT:
536     case VK_NONCONVERT:
537     case VK_ACCEPT:
538     case VK_MODECHANGE:
539       return Aspect_VKey_UNKNOWN;
540     case VK_SPACE:
541       return Aspect_VKey_Space;
542     case VK_PRIOR:
543       return Aspect_VKey_PageUp;
544     case VK_NEXT:
545       return Aspect_VKey_PageDown;
546     case VK_END:
547       return Aspect_VKey_End;
548     case VK_HOME:
549       return Aspect_VKey_Home;
550     case VK_LEFT:
551       return Aspect_VKey_Left;
552     case VK_UP:
553       return Aspect_VKey_Up;
554     case VK_DOWN:
555       return Aspect_VKey_Down;
556     case VK_RIGHT:
557       return Aspect_VKey_Right;
558     case VK_SELECT:
559     case VK_PRINT:
560     case VK_EXECUTE:
561     case VK_SNAPSHOT:
562       return Aspect_VKey_UNKNOWN;
563     case VK_INSERT:
564       return Aspect_VKey_UNKNOWN; // Aspect_VKey_Insert
565     case VK_DELETE:
566       return Aspect_VKey_Delete;
567     case VK_HELP:
568     case VK_LWIN:
569     case VK_RWIN:
570     case VK_APPS:
571     case VK_SLEEP:
572       return Aspect_VKey_UNKNOWN;
573     case VK_MULTIPLY:
574       return Aspect_VKey_NumpadMultiply;
575     case VK_ADD:
576       return Aspect_VKey_NumpadAdd;
577     case VK_SEPARATOR:
578     case VK_DECIMAL:
579       return Aspect_VKey_UNKNOWN;
580     case VK_SUBTRACT:
581       return Aspect_VKey_NumpadSubtract;
582     case VK_DIVIDE:
583       return Aspect_VKey_NumpadDivide;
584     case VK_NUMLOCK:
585       return Aspect_VKey_Numlock;
586     case VK_SCROLL:
587       return Aspect_VKey_Scroll;
588     case VK_LSHIFT:
589     case VK_RSHIFT:
590     case VK_LCONTROL:
591     case VK_RCONTROL:
592     case VK_LMENU:
593     case VK_RMENU:
594       return Aspect_VKey_UNKNOWN;
595     case VK_BROWSER_BACK:
596       return Aspect_VKey_BrowserBack;
597     case VK_BROWSER_FORWARD:
598       return Aspect_VKey_BrowserForward;
599     case VK_BROWSER_REFRESH:
600       return Aspect_VKey_BrowserRefresh;
601     case VK_BROWSER_STOP:
602       return Aspect_VKey_BrowserStop;
603     case VK_BROWSER_SEARCH:
604       return Aspect_VKey_BrowserSearch;
605     case VK_BROWSER_FAVORITES:
606       return Aspect_VKey_BrowserFavorites;
607     case VK_BROWSER_HOME:
608       return Aspect_VKey_BrowserHome;
609     case VK_VOLUME_MUTE:
610       return Aspect_VKey_VolumeMute;
611     case VK_VOLUME_DOWN:
612       return Aspect_VKey_VolumeDown;
613     case VK_VOLUME_UP:
614       return Aspect_VKey_VolumeUp;
615     case VK_MEDIA_NEXT_TRACK:
616       return Aspect_VKey_MediaNextTrack;
617     case VK_MEDIA_PREV_TRACK:
618       return Aspect_VKey_MediaPreviousTrack;
619     case VK_MEDIA_STOP:
620       return Aspect_VKey_MediaStop;
621     case VK_MEDIA_PLAY_PAUSE:
622       return Aspect_VKey_MediaPlayPause;
623     case VK_OEM_1:
624       return Aspect_VKey_Semicolon;
625     case VK_OEM_PLUS:
626       return Aspect_VKey_Plus;
627     case VK_OEM_COMMA:
628       return Aspect_VKey_Comma;
629     case VK_OEM_MINUS:
630       return Aspect_VKey_Minus;
631     case VK_OEM_PERIOD:
632       return Aspect_VKey_Period;
633     case VK_OEM_2:
634       return Aspect_VKey_Slash;
635     case VK_OEM_3:
636       return Aspect_VKey_Tilde;
637     case VK_OEM_4:
638       return Aspect_VKey_BracketLeft;
639     case VK_OEM_5:
640       return Aspect_VKey_Backslash;
641     case VK_OEM_6:
642       return Aspect_VKey_BracketRight;
643     case VK_OEM_7:
644       return Aspect_VKey_Apostrophe;
645   }
646   return Aspect_VKey_UNKNOWN;
647 }
648
649 // =======================================================================
650 // function : MouseKeyFlagsFromEvent
651 // purpose  :
652 // =======================================================================
653 Aspect_VKeyFlags WNT_Window::MouseKeyFlagsFromEvent (WPARAM theKeys)
654 {
655   Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
656   if ((theKeys & MK_CONTROL) != 0)
657   {
658     aFlags |= Aspect_VKeyFlags_CTRL;
659   }
660   if ((theKeys & MK_SHIFT) != 0)
661   {
662     aFlags |= Aspect_VKeyFlags_SHIFT;
663   }
664   if (GetKeyState (VK_MENU) < 0)
665   {
666     aFlags |= Aspect_VKeyFlags_ALT;
667   }
668   return aFlags;
669 }
670
671 // =======================================================================
672 // function : MouseKeyFlagsAsync
673 // purpose  :
674 // =======================================================================
675 Aspect_VKeyFlags WNT_Window::MouseKeyFlagsAsync()
676 {
677   Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
678   if ((GetAsyncKeyState (VK_CONTROL) & 0x8000) != 0)
679   {
680     aFlags |= Aspect_VKeyFlags_CTRL;
681   }
682   if ((GetAsyncKeyState (VK_SHIFT) & 0x8000) != 0)
683   {
684     aFlags |= Aspect_VKeyFlags_SHIFT;
685   }
686   if ((GetAsyncKeyState (VK_MENU) & 0x8000) != 0)
687   {
688     aFlags |= Aspect_VKeyFlags_ALT;
689   }
690   return aFlags;
691 }
692
693 // =======================================================================
694 // function : MouseButtonsFromEvent
695 // purpose  :
696 // =======================================================================
697 Aspect_VKeyMouse WNT_Window::MouseButtonsFromEvent (WPARAM theKeys)
698 {
699   Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
700   if ((theKeys & MK_LBUTTON) != 0)
701   {
702     aButtons |= Aspect_VKeyMouse_LeftButton;
703   }
704   if ((theKeys & MK_MBUTTON) != 0)
705   {
706     aButtons |= Aspect_VKeyMouse_MiddleButton;
707   }
708   if ((theKeys & MK_RBUTTON) != 0)
709   {
710     aButtons |= Aspect_VKeyMouse_RightButton;
711   }
712   return aButtons;
713 }
714
715 // =======================================================================
716 // function : MouseButtonsAsync
717 // purpose  :
718 // =======================================================================
719 Aspect_VKeyMouse WNT_Window::MouseButtonsAsync()
720 {
721   Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
722   const bool isSwapped = GetSystemMetrics (SM_SWAPBUTTON) != 0;
723
724   if ((GetAsyncKeyState (!isSwapped ? VK_LBUTTON : VK_RBUTTON) & 0x8000) != 0)
725   {
726     aButtons |= Aspect_VKeyMouse_LeftButton;
727   }
728   if ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) != 0)
729   {
730     aButtons |= Aspect_VKeyMouse_MiddleButton;
731   }
732   if ((GetAsyncKeyState (!isSwapped ? VK_RBUTTON : VK_LBUTTON) & 0x8000) != 0)
733   {
734     aButtons |= Aspect_VKeyMouse_RightButton;
735   }
736   return aButtons;
737 }
738
739 // =======================================================================
740 // function : RegisterRawInputDevices
741 // purpose  :
742 // =======================================================================
743 int WNT_Window::RegisterRawInputDevices (unsigned int theRawDeviceMask)
744 {
745   if (IsVirtual()
746    || myHWindow == NULL)
747   {
748     return 0;
749   }
750
751   // hidusage.h
752   enum HidUsagePage { THE_HID_USAGE_PAGE_GENERIC = 0x01 }; // HID_USAGE_PAGE_GENERIC
753   enum HidUsage
754   {
755     THE_HID_USAGE_GENERIC_MOUSE                 = 0x02, // HID_USAGE_GENERIC_MOUSE
756     THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER = 0x08, // HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
757   };
758
759   int aNbDevices = 0;
760   RAWINPUTDEVICE aRawInDevList[2];
761   if ((theRawDeviceMask & RawInputMask_Mouse) != 0)
762   {
763     // mouse
764     RAWINPUTDEVICE& aRawMouse = aRawInDevList[aNbDevices++];
765     aRawMouse.usUsagePage = THE_HID_USAGE_PAGE_GENERIC;
766     aRawMouse.usUsage     = THE_HID_USAGE_GENERIC_MOUSE;
767     aRawMouse.dwFlags     = RIDEV_INPUTSINK;
768     aRawMouse.hwndTarget  = (HWND )myHWindow;
769   }
770   if ((theRawDeviceMask & RawInputMask_SpaceMouse) != 0)
771   {
772     // space mouse
773     RAWINPUTDEVICE& aRawSpace = aRawInDevList[aNbDevices++];
774     aRawSpace.usUsagePage = THE_HID_USAGE_PAGE_GENERIC;
775     aRawSpace.usUsage     = THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER;
776     aRawSpace.dwFlags     = 0; // RIDEV_DEVNOTIFY
777     aRawSpace.hwndTarget  = (HWND )myHWindow;
778   }
779
780   for (int aTryIter = aNbDevices; aTryIter > 0; --aTryIter)
781   {
782     if (::RegisterRawInputDevices (aRawInDevList, aTryIter, sizeof(aRawInDevList[0])))
783     {
784       return aTryIter;
785     }
786
787     Message::SendTrace (aRawInDevList[aTryIter - 1].usUsage == THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
788                       ? "Warning: RegisterRawInputDevices() failed to register RAW multi-axis controller input"
789                       : "Warning: RegisterRawInputDevices() failed to register RAW mouse input");
790   }
791   return 0;
792 }
793
794 // =======================================================================
795 // function : ProcessMessage
796 // purpose  :
797 // =======================================================================
798 bool WNT_Window::ProcessMessage (Aspect_WindowInputListener& theListener,
799                                  MSG& theMsg)
800 {
801   if (myTouchInputHelper.IsNull())
802   {
803     myTouchInputHelper = new TouchInputHelper();
804     myTouchInputHelper->Register ((HWND )myHWindow);
805   }
806
807   switch (theMsg.message)
808   {
809     case WM_CLOSE:
810     {
811       if (theMsg.hwnd == (HWND )myHWindow)
812       {
813         theListener.ProcessClose();
814         return true;
815       }
816       return false;
817     }
818     case WM_ACTIVATE:
819     {
820       if (theMsg.hwnd == (HWND )myHWindow)
821       {
822         theListener.ProcessFocus (LOWORD(theMsg.wParam) == WA_CLICKACTIVE
823                                || LOWORD(theMsg.wParam) == WA_ACTIVE);
824         return true;
825       }
826       return false;
827     }
828     case WM_PAINT:
829     {
830       PAINTSTRUCT aPaint;
831       BeginPaint(theMsg.hwnd, &aPaint);
832       EndPaint  (theMsg.hwnd, &aPaint);
833       theListener.ProcessExpose();
834       return true;
835     }
836     case WM_SIZE:
837     case WM_MOVE:
838     case WM_MOVING:
839     case WM_SIZING:
840     {
841       theListener.ProcessConfigure (theMsg.message == WM_SIZE);
842       return true;
843     }
844     case WM_KEYUP:
845     case WM_KEYDOWN:
846     {
847       const Aspect_VKey aVKey = WNT_Window::VirtualKeyFromNative ((Standard_Integer )theMsg.wParam);
848       if (aVKey != Aspect_VKey_UNKNOWN)
849       {
850         const double aTimeStamp = theListener.EventTime();
851         if (theMsg.message == WM_KEYDOWN)
852         {
853           theListener.KeyDown (aVKey, aTimeStamp);
854         }
855         else
856         {
857           theListener.KeyUp (aVKey, aTimeStamp);
858         }
859         theListener.ProcessInput();
860       }
861       return true;
862     }
863     case WM_LBUTTONUP:
864     case WM_MBUTTONUP:
865     case WM_RBUTTONUP:
866     case WM_LBUTTONDOWN:
867     case WM_MBUTTONDOWN:
868     case WM_RBUTTONDOWN:
869     {
870       const LPARAM anExtraInfo = GetMessageExtraInfo();
871       bool isEmulated = false;
872       if ((anExtraInfo & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH)
873       {
874         isEmulated = true;
875         if (!myTouchInputHelper.IsNull()
876           && myTouchInputHelper->IsRegistered())
877         {
878           //Message::SendTrace ("Skipping mouse message emulated from touches...");
879           break;
880         }
881       }
882
883       const Graphic3d_Vec2i aPos (LOWORD(theMsg.lParam), HIWORD(theMsg.lParam));
884       const Aspect_VKeyFlags aFlags = WNT_Window::MouseKeyFlagsFromEvent (theMsg.wParam);
885       Aspect_VKeyMouse aButton = Aspect_VKeyMouse_NONE;
886       switch (theMsg.message)
887       {
888         case WM_LBUTTONUP:
889         case WM_LBUTTONDOWN:
890           aButton = Aspect_VKeyMouse_LeftButton;
891           break;
892         case WM_MBUTTONUP:
893         case WM_MBUTTONDOWN:
894           aButton = Aspect_VKeyMouse_MiddleButton;
895           break;
896         case WM_RBUTTONUP:
897         case WM_RBUTTONDOWN:
898           aButton = Aspect_VKeyMouse_RightButton;
899           break;
900       }
901       if (theMsg.message == WM_LBUTTONDOWN
902        || theMsg.message == WM_MBUTTONDOWN
903        || theMsg.message == WM_RBUTTONDOWN)
904       {
905         SetFocus  (theMsg.hwnd);
906         SetCapture(theMsg.hwnd);
907         theListener.PressMouseButton (aPos, aButton, aFlags, isEmulated);
908       }
909       else
910       {
911         ReleaseCapture();
912         theListener.ReleaseMouseButton (aPos, aButton, aFlags, isEmulated);
913       }
914       theListener.ProcessInput();
915       return true;
916     }
917     case WM_MOUSEWHEEL:
918     {
919       const int aDelta = GET_WHEEL_DELTA_WPARAM (theMsg.wParam);
920       const Standard_Real aDeltaF = Standard_Real(aDelta) / Standard_Real(WHEEL_DELTA);
921       const Aspect_VKeyFlags aFlags = WNT_Window::MouseKeyFlagsFromEvent (theMsg.wParam);
922       Graphic3d_Vec2i aPos (int(short(LOWORD(theMsg.lParam))), int(short(HIWORD(theMsg.lParam))));
923       POINT aCursorPnt = { aPos.x(), aPos.y() };
924       if (ScreenToClient (theMsg.hwnd, &aCursorPnt))
925       {
926         aPos.SetValues (aCursorPnt.x, aCursorPnt.y);
927       }
928
929       if (theMsg.hwnd != (HWND )myHWindow)
930       {
931         return false;
932       }
933
934       theListener.UpdateMouseScroll (Aspect_ScrollDelta (aPos, aDeltaF, aFlags));
935       theListener.ProcessInput();
936       return true;
937     }
938     case WM_MOUSEMOVE:
939     {
940       Graphic3d_Vec2i aPos (LOWORD(theMsg.lParam), HIWORD(theMsg.lParam));
941       Aspect_VKeyMouse aButtons = WNT_Window::MouseButtonsFromEvent (theMsg.wParam);
942       Aspect_VKeyFlags aFlags   = WNT_Window::MouseKeyFlagsFromEvent(theMsg.wParam);
943
944       // don't make a slide-show from input events - fetch the actual mouse cursor position
945       CURSORINFO aCursor;
946       aCursor.cbSize = sizeof(aCursor);
947       if (::GetCursorInfo (&aCursor) != FALSE)
948       {
949         POINT aCursorPnt = { aCursor.ptScreenPos.x, aCursor.ptScreenPos.y };
950         if (ScreenToClient (theMsg.hwnd, &aCursorPnt))
951         {
952           // as we override mouse position, we need overriding also mouse state
953           aPos.SetValues (aCursorPnt.x, aCursorPnt.y);
954           aButtons = WNT_Window::MouseButtonsAsync();
955           aFlags   = WNT_Window::MouseKeyFlagsAsync();
956         }
957       }
958
959       if (theMsg.hwnd != (HWND )myHWindow)
960       {
961         // mouse move events come also for inactive windows
962         return false;
963       }
964
965       theListener.UpdateMousePosition (aPos, aButtons, aFlags, false);
966       theListener.ProcessInput();
967       return true;
968     }
969     case WM_INPUT:
970     {
971       UINT aSize = 0;
972       ::GetRawInputData ((HRAWINPUT )theMsg.lParam, RID_INPUT, NULL, &aSize, sizeof(RAWINPUTHEADER));
973       NCollection_LocalArray<BYTE> aRawData (aSize);
974       if (aSize == 0 || ::GetRawInputData ((HRAWINPUT )theMsg.lParam, RID_INPUT, aRawData, &aSize, sizeof(RAWINPUTHEADER)) != aSize)
975       {
976         return true;
977       }
978
979       const RAWINPUT* aRawInput = (RAWINPUT* )(BYTE* )aRawData;
980       if (aRawInput->header.dwType != RIM_TYPEHID)
981       {
982         return true;
983       }
984
985       RID_DEVICE_INFO aDevInfo;
986       aDevInfo.cbSize = sizeof(RID_DEVICE_INFO);
987       UINT aDevInfoSize = sizeof(RID_DEVICE_INFO);
988       if (::GetRawInputDeviceInfoW (aRawInput->header.hDevice, RIDI_DEVICEINFO, &aDevInfo, &aDevInfoSize) != sizeof(RID_DEVICE_INFO)
989        || (aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_LOGITECH
990         && aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_3DCONNEXION))
991       {
992         return true;
993       }
994
995       WNT_HIDSpaceMouse aSpaceData (aDevInfo.hid.dwProductId, aRawInput->data.hid.bRawData, aRawInput->data.hid.dwSizeHid);
996       if (theListener.Update3dMouse (aSpaceData))
997       {
998         InvalidateContent (Handle(Aspect_DisplayConnection)());
999       }
1000       return true;
1001     }
1002     case WM_TOUCH:
1003     {
1004       if (theMsg.hwnd != (HWND )myHWindow
1005        || myTouchInputHelper.IsNull())
1006       {
1007         return false;
1008       }
1009
1010       TouchInputHelper::TouchInputInfo aSrcTouches (*myTouchInputHelper, theMsg);
1011       if (aSrcTouches.Size() < 1)
1012       {
1013         break;
1014       }
1015
1016       Graphic3d_Vec2i aWinTopLeft, aWinBotRight;
1017       Position (aWinTopLeft.x(),  aWinTopLeft.y(),
1018                 aWinBotRight.x(), aWinBotRight.y());
1019
1020       bool hasUpdates = false;
1021       for (size_t aTouchIter = 0; aTouchIter < aSrcTouches.Size(); ++aTouchIter)
1022       {
1023         const TOUCHINPUT& aTouchSrc = aSrcTouches[aTouchIter];
1024         const Standard_Size aTouchId = (Standard_Size )aTouchSrc.dwID;
1025         //const Standard_Size aDeviceId = (Standard_Size )aTouchSrc.hSource;
1026
1027         const Graphic3d_Vec2i aSize = aWinBotRight - aWinTopLeft;
1028         const Graphic3d_Vec2d aNewPos2d = Graphic3d_Vec2d (double(aTouchSrc.x), double(aTouchSrc.y)) * 0.01
1029                                         - Graphic3d_Vec2d (aWinTopLeft);
1030         const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
1031         if ((aTouchSrc.dwFlags & TOUCHEVENTF_DOWN) == TOUCHEVENTF_DOWN)
1032         {
1033           if (aNewPos2i.x() >= 0 && aNewPos2i.x() < aSize.x()
1034            && aNewPos2i.y() >= 0 && aNewPos2i.y() < aSize.y())
1035           {
1036             hasUpdates = true;
1037             theListener.AddTouchPoint (aTouchId, aNewPos2d);
1038           }
1039         }
1040         else if ((aTouchSrc.dwFlags & TOUCHEVENTF_MOVE) == TOUCHEVENTF_MOVE)
1041         {
1042           const int anOldIndex = theListener.TouchPoints().FindIndex (aTouchId);
1043           if (anOldIndex != 0)
1044           {
1045             hasUpdates = true;
1046             theListener.UpdateTouchPoint (aTouchId, aNewPos2d);
1047           }
1048         }
1049         else if ((aTouchSrc.dwFlags & TOUCHEVENTF_UP) == TOUCHEVENTF_UP)
1050         {
1051           if (theListener.RemoveTouchPoint (aTouchId))
1052           {
1053             hasUpdates = true;
1054           }
1055         }
1056       }
1057
1058       if (hasUpdates)
1059       {
1060         InvalidateContent (Handle(Aspect_DisplayConnection)());
1061       }
1062       return true;
1063     }
1064   }
1065   return false;
1066 }
1067
1068 #endif // _WIN32