1 // SHE library 2 // Copyright (C) 2012-2018 David Capello 3 // 4 // This file is released under the terms of the MIT license. 5 // Read LICENSE.txt for more information. 6 7 #ifndef SHE_WIN_WINDOW_H_INCLUDED 8 #define SHE_WIN_WINDOW_H_INCLUDED 9 #pragma once 10 11 #include "base/time.h" 12 #include "gfx/size.h" 13 #include "she/event.h" 14 #include "she/native_cursor.h" 15 #include "she/pointer_type.h" 16 #include "she/win/pen.h" 17 18 #include <string> 19 #include <windows.h> 20 #include <interactioncontext.h> 21 22 #define SHE_USE_POINTER_API_FOR_MOUSE 0 23 24 namespace she { 25 class Surface; 26 class WindowSystem; 27 28 class WinWindow { 29 public: 30 WinWindow(int width, int height, int scale); 31 ~WinWindow(); 32 33 void queueEvent(Event& ev); scale()34 int scale() const { return m_scale; } 35 void setScale(int scale); 36 void setVisible(bool visible); 37 void maximize(); 38 bool isMaximized() const; 39 bool isMinimized() const; 40 gfx::Size clientSize() const; 41 gfx::Size restoredSize() const; 42 void setTitle(const std::string& title); 43 void captureMouse(); 44 void releaseMouse(); 45 void setMousePosition(const gfx::Point& position); 46 bool setNativeMouseCursor(NativeCursor cursor); 47 bool setNativeMouseCursor(const she::Surface* surface, 48 const gfx::Point& focus, 49 const int scale); 50 void updateWindow(const gfx::Rect& bounds); 51 std::string getLayout(); 52 53 void setLayout(const std::string& layout); 54 void setTranslateDeadKeys(bool state); 55 void setInterpretOneFingerGestureAsMouseMovement(bool state); 56 handle()57 HWND handle() { return m_hwnd; } 58 59 private: 60 bool setCursor(HCURSOR hcursor, bool custom); 61 LRESULT wndProc(UINT msg, WPARAM wparam, LPARAM lparam); 62 void mouseEvent(LPARAM lparam, Event& ev); 63 bool pointerEvent(WPARAM wparam, Event& ev, POINTER_INFO& pi); 64 void handlePointerButtonChange(Event& ev, POINTER_INFO& pi); 65 void handleInteractionContextOutput( 66 const INTERACTION_CONTEXT_OUTPUT* output); 67 68 void waitTimerToConvertFingerAsMouseMovement(); 69 void convertFingerAsMouseMovement(); 70 void delegateFingerToInteractionContext(); 71 void sendDelayedTouchEvents(); 72 void clearDelayedTouchEvents(); 73 void killTouchTimer(); 74 onQueueEvent(Event & ev)75 virtual void onQueueEvent(Event& ev) { } onResize(const gfx::Size & sz)76 virtual void onResize(const gfx::Size& sz) { } onPaint(HDC hdc)77 virtual void onPaint(HDC hdc) { } 78 79 static void registerClass(); 80 static HWND createHwnd(WinWindow* self, int width, int height); 81 static LRESULT CALLBACK staticWndProc( 82 HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); 83 static void CALLBACK staticInteractionContextCallback( 84 void* clientData, 85 const INTERACTION_CONTEXT_OUTPUT* output); 86 87 static WindowSystem* system(); 88 89 mutable HWND m_hwnd; 90 HCURSOR m_hcursor; 91 gfx::Size m_clientSize; 92 gfx::Size m_restoredSize; 93 int m_scale; 94 bool m_isCreated; 95 bool m_translateDeadKeys; 96 bool m_hasMouse; 97 bool m_captureMouse; 98 bool m_customHcursor; 99 100 // Windows 8 pointer API 101 bool m_usePointerApi; 102 UINT32 m_lastPointerId; 103 UINT32 m_capturePointerId; 104 HINTERACTIONCONTEXT m_ictx; 105 106 // This might be the most ugliest hack I've done to fix a Windows 107 // bug. Here's the thing: 108 // 1) When we use the pen on a Surface device, it send us 109 // WM_POINTERUPDATE messages, but after a WM_POINTERUPDATE 110 // we'll receive a WM_MOUSEMOVE with the position of the mouse 111 // (which is different to the position of the pen). This 112 // happens constantly if we press a modifier key like Alt, 113 // Ctrl, or Shift, and randomly if we don't use a modifier. 114 // 2) First I tried to fix this issue disabling the WM_MOUSEMOVE 115 // processing when a pointer device is inside the window (with 116 // WM_POINTERENTER/LEAVE events), but that generated some buggy 117 // behavior (sometimes we stopped processing WM_MOUSEMOVE 118 // messages completely, it looks like a WM_POINTERLEAVE could 119 // be lost in time). 120 // 3) Then I tried to fix this using the Windows pointer API for 121 // mouse messages (EnableMouseInPointer(TRUE)) but that API 122 // doesn't work well with Wacom drivers. And I guess it might 123 // depend on the specific driver version. 124 // 4) Now I've reverted the pointer API change (we keep using the 125 // WM_MOUSE* messages) and fixed the original issue with this 126 // "m_ignoreRandomMouseEvents" variable. This is a counter that 127 // indicates the number of WM_MOUSEMOVE messages that we must 128 // ignore after a WM_POINTERUPDATE. 129 // 5) Each time we receive a WM_POINTERUPDATE this counter is set 130 // to 2. A value that I calculated practically just testing the 131 // pen in a Surface Pro using the Alt modifier and seeing how 132 // many random WM_MOUSEMOVE messages were received. 133 // 6) When a WM_MOUSEMOVE is received this counter is decreased, 134 // so when it backs to 0 we start processing WM_MOUSEMOVE 135 // messages again. 136 int m_ignoreRandomMouseEvents; 137 138 // Variables used to convert one finger in mouse-like movement, 139 // and two/more fingers in scroll movement/pan/zoom. The idea 140 // is as follows: 141 // 1) When a PT_TOUCH is received, we count this event in 142 // m_fingers and setup a timer (m_fingerTimerID) to wait for 143 // another touch event. 144 // 2) If another touch event is received, we process the messages 145 // with the interaction context (m_ictx) to handle special 146 // gestures (pan, magnify, etc.). 147 // 3) If the timeout is reached, and we've received only one 148 // finger on the windows, we can send all awaiting events 149 // (m_fingerEvents) like mouse movement messages/button 150 // presses/releases. 151 struct Touch { 152 int fingers; // Number of fingers in the window 153 // True when the timeout wasn't reached yet and the finger can be 154 // converted to mouse events yet. 155 bool canBeMouse; 156 // True if we're already processing finger/touch events as mouse 157 // movement events. 158 bool asMouse; 159 // Timeout (WM_TIMER) when the finger is converted to mouse events. 160 UINT_PTR timerID; 161 // Queued events to be sent when the finger is converted to mouse 162 // events (these events are discarded if another finger is 163 // introduced in the gesture e.g. to pan) 164 std::vector<Event> delayedEvents; 165 Touch(); 166 } *m_touch; 167 168 #if SHE_USE_POINTER_API_FOR_MOUSE 169 // Emulate double-click with pointer API. I guess that this should 170 // be done by the Interaction Context API but it looks like 171 // messages with pointerType != PT_TOUCH or PT_PEN are just 172 // ignored by the ProcessPointerFramesInteractionContext() 173 // function even when we call AddPointerInteractionContext() with 174 // the given PT_MOUSE pointer. 175 bool m_emulateDoubleClick; 176 base::tick_t m_doubleClickMsecs; 177 base::tick_t m_lastPointerDownTime; 178 Event::MouseButton m_lastPointerDownButton; 179 int m_pointerDownCount; 180 #endif 181 182 // Wintab API data 183 HCTX m_hpenctx; 184 PointerType m_pointerType; 185 double m_pressure; 186 }; 187 188 } // namespace she 189 190 #endif 191