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