1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsWindowBase.h"
7
8 #include "gfxPrefs.h"
9 #include "mozilla/MiscEvents.h"
10 #include "KeyboardLayout.h"
11 #include "WinUtils.h"
12 #include "npapi.h"
13 #include "nsAutoPtr.h"
14 #include "nsIPresShell.h"
15
16 using namespace mozilla;
17 using namespace mozilla::widget;
18
19 static const wchar_t kUser32LibName[] = L"user32.dll";
20 bool nsWindowBase::sTouchInjectInitialized = false;
21 InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr;
22
DispatchPluginEvent(const MSG & aMsg)23 bool nsWindowBase::DispatchPluginEvent(const MSG& aMsg) {
24 if (!ShouldDispatchPluginEvent()) {
25 return false;
26 }
27 WidgetPluginEvent pluginEvent(true, ePluginInputEvent, this);
28 LayoutDeviceIntPoint point(0, 0);
29 InitEvent(pluginEvent, &point);
30 NPEvent npEvent;
31 npEvent.event = aMsg.message;
32 npEvent.wParam = aMsg.wParam;
33 npEvent.lParam = aMsg.lParam;
34 pluginEvent.mPluginEvent.Copy(npEvent);
35 pluginEvent.mRetargetToFocusedDocument = true;
36 return DispatchWindowEvent(&pluginEvent);
37 }
38
ShouldDispatchPluginEvent()39 bool nsWindowBase::ShouldDispatchPluginEvent() { return PluginHasFocus(); }
40
41 // static
InitTouchInjection()42 bool nsWindowBase::InitTouchInjection() {
43 if (!sTouchInjectInitialized) {
44 // Initialize touch injection on the first call
45 HMODULE hMod = LoadLibraryW(kUser32LibName);
46 if (!hMod) {
47 return false;
48 }
49
50 InitializeTouchInjectionPtr func =
51 (InitializeTouchInjectionPtr)GetProcAddress(hMod,
52 "InitializeTouchInjection");
53 if (!func) {
54 WinUtils::Log("InitializeTouchInjection not available.");
55 return false;
56 }
57
58 if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) {
59 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d",
60 GetLastError());
61 return false;
62 }
63
64 sInjectTouchFuncPtr =
65 (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput");
66 if (!sInjectTouchFuncPtr) {
67 WinUtils::Log("InjectTouchInput not available.");
68 return false;
69 }
70 sTouchInjectInitialized = true;
71 }
72 return true;
73 }
74
InjectTouchPoint(uint32_t aId,LayoutDeviceIntPoint & aPoint,POINTER_FLAGS aFlags,uint32_t aPressure,uint32_t aOrientation)75 bool nsWindowBase::InjectTouchPoint(uint32_t aId, LayoutDeviceIntPoint& aPoint,
76 POINTER_FLAGS aFlags, uint32_t aPressure,
77 uint32_t aOrientation) {
78 if (aId > TOUCH_INJECT_MAX_POINTS) {
79 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
80 return false;
81 }
82
83 POINTER_TOUCH_INFO info;
84 memset(&info, 0, sizeof(POINTER_TOUCH_INFO));
85
86 info.touchFlags = TOUCH_FLAG_NONE;
87 info.touchMask =
88 TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
89 info.pressure = aPressure;
90 info.orientation = aOrientation;
91
92 info.pointerInfo.pointerFlags = aFlags;
93 info.pointerInfo.pointerType = PT_TOUCH;
94 info.pointerInfo.pointerId = aId;
95 info.pointerInfo.ptPixelLocation.x = aPoint.x;
96 info.pointerInfo.ptPixelLocation.y = aPoint.y;
97
98 info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2;
99 info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2;
100 info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2;
101 info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2;
102
103 if (!sInjectTouchFuncPtr(1, &info)) {
104 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError());
105 return false;
106 }
107 return true;
108 }
109
ChangedDPI()110 void nsWindowBase::ChangedDPI() {
111 if (mWidgetListener) {
112 nsIPresShell* presShell = mWidgetListener->GetPresShell();
113 if (presShell) {
114 presShell->BackingScaleFactorChanged();
115 }
116 mWidgetListener->UIResolutionChanged();
117 }
118 }
119
SynthesizeNativeTouchPoint(uint32_t aPointerId,nsIWidget::TouchPointerState aPointerState,LayoutDeviceIntPoint aPoint,double aPointerPressure,uint32_t aPointerOrientation,nsIObserver * aObserver)120 nsresult nsWindowBase::SynthesizeNativeTouchPoint(
121 uint32_t aPointerId, nsIWidget::TouchPointerState aPointerState,
122 LayoutDeviceIntPoint aPoint, double aPointerPressure,
123 uint32_t aPointerOrientation, nsIObserver* aObserver) {
124 AutoObserverNotifier notifier(aObserver, "touchpoint");
125
126 if (gfxPrefs::APZTestFailsWithNativeInjection() || !InitTouchInjection()) {
127 // If we don't have touch injection from the OS, or if we are running a test
128 // that cannot properly inject events to satisfy the OS requirements (see
129 // bug 1313170) we can just fake it and synthesize the events from here.
130 MOZ_ASSERT(NS_IsMainThread());
131 if (aPointerState == TOUCH_HOVER) {
132 return NS_ERROR_UNEXPECTED;
133 }
134
135 if (!mSynthesizedTouchInput) {
136 mSynthesizedTouchInput = MakeUnique<MultiTouchInput>();
137 }
138
139 WidgetEventTime time = CurrentMessageWidgetEventTime();
140 LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
141 MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState(
142 mSynthesizedTouchInput.get(), time.mTime, time.mTimeStamp, aPointerId,
143 aPointerState, pointInWindow, aPointerPressure, aPointerOrientation);
144 DispatchTouchInput(inputToDispatch);
145 return NS_OK;
146 }
147
148 bool hover = aPointerState & TOUCH_HOVER;
149 bool contact = aPointerState & TOUCH_CONTACT;
150 bool remove = aPointerState & TOUCH_REMOVE;
151 bool cancel = aPointerState & TOUCH_CANCEL;
152
153 // win api expects a value from 0 to 1024. aPointerPressure is a value
154 // from 0.0 to 1.0.
155 uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024);
156
157 // If we already know about this pointer id get it's record
158 PointerInfo* info = mActivePointers.Get(aPointerId);
159
160 // We know about this pointer, send an update
161 if (info) {
162 POINTER_FLAGS flags = POINTER_FLAG_UPDATE;
163 if (hover) {
164 flags |= POINTER_FLAG_INRANGE;
165 } else if (contact) {
166 flags |= POINTER_FLAG_INCONTACT | POINTER_FLAG_INRANGE;
167 } else if (remove) {
168 flags = POINTER_FLAG_UP;
169 // Remove the pointer from our tracking list. This is nsAutPtr wrapped,
170 // so shouldn't leak.
171 mActivePointers.Remove(aPointerId);
172 }
173
174 if (cancel) {
175 flags |= POINTER_FLAG_CANCELED;
176 }
177
178 return !InjectTouchPoint(aPointerId, aPoint, flags, pressure,
179 aPointerOrientation)
180 ? NS_ERROR_UNEXPECTED
181 : NS_OK;
182 }
183
184 // Missing init state, error out
185 if (remove || cancel) {
186 return NS_ERROR_INVALID_ARG;
187 }
188
189 // Create a new pointer
190 info = new PointerInfo(aPointerId, aPoint);
191
192 POINTER_FLAGS flags = POINTER_FLAG_INRANGE;
193 if (contact) {
194 flags |= POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
195 }
196
197 mActivePointers.Put(aPointerId, info);
198 return !InjectTouchPoint(aPointerId, aPoint, flags, pressure,
199 aPointerOrientation)
200 ? NS_ERROR_UNEXPECTED
201 : NS_OK;
202 }
203
ClearNativeTouchSequence(nsIObserver * aObserver)204 nsresult nsWindowBase::ClearNativeTouchSequence(nsIObserver* aObserver) {
205 AutoObserverNotifier notifier(aObserver, "cleartouch");
206 if (!sTouchInjectInitialized) {
207 return NS_OK;
208 }
209
210 // cancel all input points
211 for (auto iter = mActivePointers.Iter(); !iter.Done(); iter.Next()) {
212 nsAutoPtr<PointerInfo>& info = iter.Data();
213 InjectTouchPoint(info.get()->mPointerId, info.get()->mPosition,
214 POINTER_FLAG_CANCELED);
215 iter.Remove();
216 }
217
218 nsBaseWidget::ClearNativeTouchSequence(nullptr);
219
220 return NS_OK;
221 }
222
HandleAppCommandMsg(const MSG & aAppCommandMsg,LRESULT * aRetValue)223 bool nsWindowBase::HandleAppCommandMsg(const MSG& aAppCommandMsg,
224 LRESULT* aRetValue) {
225 ModifierKeyState modKeyState;
226 NativeKey nativeKey(this, aAppCommandMsg, modKeyState);
227 bool consumed = nativeKey.HandleAppCommandMessage();
228 *aRetValue = consumed ? 1 : 0;
229 return consumed;
230 }
231