gecko/widget/windows/winrt/MetroInput.cpp
2013-03-13 15:09:46 +00:00

1686 lines
66 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MetroInput.h"
// Moz headers (alphabetical)
#include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc
#include "MetroWidget.h" // MetroInput::mWidget
#include "npapi.h" // NPEvent
#include "nsDOMTouchEvent.h" // nsDOMTouch
#include "nsTArray.h" // Touch lists
#include "nsIDOMSimpleGestureEvent.h" // Constants for gesture events
// System headers (alphabetical)
#include <windows.ui.core.h> // ABI::Window::UI::Core namespace
#include <windows.ui.input.h> // ABI::Window::UI::Input namespace
//#define DEBUG_INPUT
// Using declarations
using namespace ABI::Windows; // UI, System, Foundation namespaces
using namespace Microsoft; // WRL namespace (ComPtr, possibly others)
using namespace mozilla::widget::winrt;
// File-scoped statics (unnamed namespace)
namespace {
// XXX: Set these min values appropriately
const double SWIPE_MIN_DISTANCE = 5.0;
const double SWIPE_MIN_VELOCITY = 5.0;
const double WHEEL_DELTA_DOUBLE = static_cast<double>(WHEEL_DELTA);
// Convenience typedefs for event handler types
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CEdgeGesture_Windows__CUI__CInput__CEdgeGestureEventArgs_t EdgeGestureHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs_t AcceleratorKeyActivatedHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CPointerEventArgs_t PointerEventHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CTappedEventArgs_t TappedEventHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CRightTappedEventArgs_t RightTappedEventHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationStartedEventArgs_t ManipulationStartedEventHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationUpdatedEventArgs_t ManipulationUpdatedEventHandler;
typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationCompletedEventArgs_t ManipulationCompletedEventHandler;
// Other convenience typedefs
typedef ABI::Windows::UI::Core::ICoreAcceleratorKeys ICoreAcceleratorKeys;
/**
* Creates and returns a new {@link nsDOMTouch} from the given
* ABI::Windows::UI::Input::IPointerPoint. Note that the caller is
* responsible for freeing the memory for the nsDOMTouch returned from
* this function.
*
* @param aPoint the ABI::Windows::UI::Input::IPointerPoint containing the
* metadata from which to create our new {@link nsDOMTouch}
* @return a new {@link nsDOMTouch} representing the touch point. The caller
* is responsible for freeing the memory for this touch point.
*/
nsDOMTouch*
CreateDOMTouch(UI::Input::IPointerPoint* aPoint) {
WRL::ComPtr<UI::Input::IPointerPointProperties> props;
Foundation::Point position;
uint32_t pointerId;
Foundation::Rect contactRect;
float pressure;
aPoint->get_Properties(props.GetAddressOf());
aPoint->get_Position(&position);
aPoint->get_PointerId(&pointerId);
props->get_ContactRect(&contactRect);
props->get_Pressure(&pressure);
nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
nsIntPoint touchRadius;
touchRadius.x = MetroUtils::LogToPhys(contactRect.Width) / 2;
touchRadius.y = MetroUtils::LogToPhys(contactRect.Height) / 2;
return new nsDOMTouch(pointerId,
touchPoint,
// Rotation radius and angle.
// W3C touch events v1 do not use these.
// The draft for W3C touch events v2 explains that
// radius and angle should describe the ellipse that
// most closely circumscribes the touching area. Since
// Windows gives us a bounding rectangle rather than an
// ellipse, we provide the ellipse that is most closely
// circumscribed by the bounding rectangle that Windows
// gave us.
touchRadius,
0.0f,
// Pressure
// W3C touch events v1 do not use this.
// The current draft for W3C touch events v2 says that
// this should be a value between 0.0 and 1.0, which is
// consistent with what Windows provides us here.
// XXX: Windows defaults to 0.5, but the current W3C
// draft says that the value should be 0.0 if no value
// known.
pressure);
}
bool
IsControlCharacter(uint32_t aCharCode) {
return (0x1F >= aCharCode
|| (0x7F <= aCharCode && 0x9F >= aCharCode));
}
/**
* Converts from the Devices::Input::PointerDeviceType enumeration
* to a nsIDOMMouseEvent::MOZ_SOURCE_* value.
*
* @param aDeviceType the value to convert
* @param aMozInputSource the converted value
*/
void
MozInputSourceFromDeviceType(
Devices::Input::PointerDeviceType const& aDeviceType,
unsigned short& aMozInputSource) {
if (Devices::Input::PointerDeviceType::PointerDeviceType_Mouse
== aDeviceType) {
aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
} else if (Devices::Input::PointerDeviceType::PointerDeviceType_Touch
== aDeviceType) {
aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
} else if (Devices::Input::PointerDeviceType::PointerDeviceType_Pen
== aDeviceType) {
aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN;
}
}
/**
* This function is for use with mTouches.Enumerate. It will
* append each element it encounters to the {@link nsTArray}
* of {@link nsIDOMTouch}es passed in through the third (void*)
* parameter.
*
* NOTE: This function will set the `mChanged` member of each
* element it encounters to `false`, since this function is only
* used to populate a touchlist that is about to be dispatched
* in a gecko touch event.
*
* @param aKey the key of the current element being enumerated
* @param aData the value of the current element being enumerated
* @param aTouchList the {@link nsTArray} to append to
*/
PLDHashOperator
AppendToTouchList(const unsigned int& aKey,
nsCOMPtr<nsIDOMTouch>& aData,
void *aTouchList)
{
nsTArray<nsCOMPtr<nsIDOMTouch> > *touches =
static_cast<nsTArray<nsCOMPtr<nsIDOMTouch> > *>(aTouchList);
touches->AppendElement(aData);
aData->mChanged = false;
return PL_DHASH_NEXT;
}
// In response to keyboard events, we create an `NPEvent` and point the
// `pluginEvent` member of our `nsInputEvent` to it. We must set the
// `wParam` and `lParam` members of our `NPEvent` according to what
// Windows would have sent for this particular WM_* message. See the
// following documentation for descriptions of what these values should
// be for the various keyboard events.
//
// This union is used when setting up the lParam for keyboard events.
union LParamForKeyEvents {
uintptr_t lParam;
struct {
// bits 0-15 Repeat count, always 1 for WM_KEYUP
uint16_t repeatCount;
// bits 16-23 Scan code
uint8_t scanCode;
// See LParamFlagsForKeyEvents
uint8_t flags;
} parts;
};
// The high-order byte of the lParam for keyboard events is a set of bit
// flags that specifies information about the key being pressed/released.
enum LParamFlagsForKeyEvents {
// bit 24 1 if the key is an extended key, 0 otherwise
isExtendedKey = 1,
// bits 25-28 Reserved; do not use
// bit 29 1 if ALT is held down while key was pressed
// Always 0 for WM_KEYDOWN
// Always 0 for WM_KEYUP
isMenuKeyDown = 1<<5,
// bit 30 Previous key state. 1 if key was down before this message.
// Always 1 for WM_KEYUP
wasKeyDown = 1<<6,
// bit 31 1 if key is being released, 0 if key is being pressed.
// Always 0 for WM_KEYDOWN
// Always 1 for WM_KEYUP
isKeyReleased = 1<<7
};
// This helper function sets up the lParam for plugin events that we create
// in response to keyboard events.
void
InitPluginKeyEventLParamFromKeyStatus(
uintptr_t& aLParam,
UI::Core::CorePhysicalKeyStatus const& aKeyStatus) {
LParamForKeyEvents lParam;
lParam.parts.repeatCount = aKeyStatus.RepeatCount;
lParam.parts.scanCode = aKeyStatus.ScanCode;
if (aKeyStatus.IsExtendedKey) {
lParam.parts.flags |= LParamFlagsForKeyEvents::isExtendedKey;
}
if (aKeyStatus.IsMenuKeyDown) {
lParam.parts.flags |= LParamFlagsForKeyEvents::isMenuKeyDown;
}
if (aKeyStatus.WasKeyDown) {
lParam.parts.flags |= LParamFlagsForKeyEvents::wasKeyDown;
}
if (aKeyStatus.IsKeyReleased) {
lParam.parts.flags |= LParamFlagsForKeyEvents::isKeyReleased;
}
aLParam = lParam.lParam;
}
// In response to mouse events, we create an `NPEvent` and point the
// `pluginEvent` member of our `nsInputEvent` to it. We must set the
// `wParam` and `lParam` members of our `NPEvent` according to what
// Windows would have sent for this particular WM_* message. See the
// following documentation for descriptions of what these values should
// be for the various mouse events.
//
// This union is used when setting up the lParam for mouse events.
union LParamForMouseEvents {
uintptr_t lParam;
// lParam is the position at which the event occurred.
struct lParamDeconstruction {
// The low-order word is the x-coordinate
uint16_t x;
// The high-order word is the y-coordinate
uint16_t y;
} parts;
};
// This helper function sets up the wParam and lParam for plugin events
// that we create in response to mouse events.
void
InitPluginMouseEventParams(nsInputEvent const& aEvent,
uintptr_t& aWParam,
uintptr_t& aLParam) {
// wParam is a mask indicating whether certain virtual keys are down.
aWParam = 0;
if (IS_VK_DOWN(VK_LBUTTON)) {
aWParam |= MK_LBUTTON;
}
if (IS_VK_DOWN(VK_MBUTTON)) {
aWParam |= MK_MBUTTON;
}
if (IS_VK_DOWN(VK_RBUTTON)) {
aWParam |= MK_RBUTTON;
}
if (aEvent.IsControl()) {
aWParam |= MK_CONTROL;
}
if (aEvent.IsShift()) {
aWParam |= MK_SHIFT;
}
Foundation::Point logPoint = MetroUtils::PhysToLog(aEvent.refPoint);
LParamForMouseEvents lParam;
lParam.parts.x = static_cast<uint16_t>(NS_round(logPoint.X));
lParam.parts.y = static_cast<uint16_t>(NS_round(logPoint.Y));
aLParam = lParam.lParam;
}
}
namespace mozilla {
namespace widget {
namespace winrt {
MetroInput::MetroInput(MetroWidget* aWidget,
UI::Core::ICoreWindow* aWindow,
UI::Core::ICoreDispatcher* aDispatcher)
: mWidget(aWidget),
mWindow(aWindow),
mDispatcher(aDispatcher),
mTouchEvent(true, NS_TOUCH_MOVE, aWidget)
{
LogFunction();
NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
mTokenPointerPressed.value = 0;
mTokenPointerReleased.value = 0;
mTokenPointerMoved.value = 0;
mTokenPointerEntered.value = 0;
mTokenPointerExited.value = 0;
mTokenPointerWheelChanged.value = 0;
mTokenAcceleratorKeyActivated.value = 0;
mTokenEdgeGesture.value = 0;
mTokenManipulationStarted.value = 0;
mTokenManipulationUpdated.value = 0;
mTokenManipulationCompleted.value = 0;
mTokenTapped.value = 0;
mTokenRightTapped.value = 0;
mTouches.Init();
// Note that this is not thread-safe.
if (!sIsVirtualKeyMapInitialized) {
InitializeVirtualKeyMap();
sIsVirtualKeyMapInitialized = true;
}
// Create our Gesture Recognizer
ActivateGenericInstance(RuntimeClass_Windows_UI_Input_GestureRecognizer,
mGestureRecognizer);
NS_ASSERTION(mGestureRecognizer, "Failed to create GestureRecognizer!");
RegisterInputEvents();
}
MetroInput::~MetroInput()
{
LogFunction();
UnregisterInputEvents();
}
/**
* This function handles the `AcceleratorKeyActivated` event, which is fired
* every time a keyboard key is pressed or released, and every time that a
* character (unicode or otherwise) is identified as having been inputted
* by the user.
*
* @param sender the CoreDispatcher that fired this event
* @param aArgs the event-specific args we use when processing this event
* @returns S_OK
*/
HRESULT
MetroInput::OnAcceleratorKeyActivated(UI::Core::ICoreDispatcher* sender,
UI::Core::IAcceleratorKeyEventArgs* aArgs) {
UI::Core::CoreAcceleratorKeyEventType type;
System::VirtualKey vkey;
UI::Core::CorePhysicalKeyStatus keyStatus;
aArgs->get_EventType(&type);
aArgs->get_VirtualKey(&vkey);
aArgs->get_KeyStatus(&keyStatus);
#ifdef DEBUG_INPUT
LogFunction();
Log(L"Accelerator key! Type: %d Value: %d", type, vkey);
#endif
switch(type) {
case UI::Core::CoreAcceleratorKeyEventType_KeyUp:
case UI::Core::CoreAcceleratorKeyEventType_SystemKeyUp:
OnKeyUp(vkey, keyStatus);
break;
case UI::Core::CoreAcceleratorKeyEventType_KeyDown:
case UI::Core::CoreAcceleratorKeyEventType_SystemKeyDown:
OnKeyDown(vkey, keyStatus);
break;
case UI::Core::CoreAcceleratorKeyEventType_Character:
case UI::Core::CoreAcceleratorKeyEventType_SystemCharacter:
case UI::Core::CoreAcceleratorKeyEventType_UnicodeCharacter:
OnCharacterReceived(vkey, keyStatus);
break;
}
return S_OK;
}
// "Edge Gesture" event. This indicates that the user has swiped in from the
// top or bottom of the screen and means we should show our context UI. This
// event can also be triggered through keyboard input.
// According to MSDN, this event will only be received through touch
// (user swipes in from edge) or from keyboard (user presses Win+Z)
HRESULT
MetroInput::OnEdgeGestureCompleted(UI::Input::IEdgeGesture* sender,
UI::Input::IEdgeGestureEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
nsSimpleGestureEvent geckoEvent(true,
NS_SIMPLE_GESTURE_EDGEUI,
mWidget.Get(),
0,
0.0);
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(geckoEvent);
geckoEvent.time = ::GetMessageTime();
UI::Input::EdgeGestureKind value;
aArgs->get_Kind(&value);
if (value == UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard) {
geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
} else {
geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
}
DispatchEventIgnoreStatus(&geckoEvent);
return S_OK;
}
// This event is received when the user rotates a mouse wheel. MSDN does not
// seem to indicate that this event can be triggered from other types of input
// (i.e. pen, touch).
HRESULT
MetroInput::OnPointerWheelChanged(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<UI::Input::IPointerPointProperties> props;
Foundation::Point position;
uint64_t timestamp;
float pressure;
boolean horzEvent;
int32_t delta;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_Position(&position);
currentPoint->get_Timestamp(&timestamp);
currentPoint->get_Properties(props.GetAddressOf());
props->get_Pressure(&pressure);
props->get_IsHorizontalMouseWheel(&horzEvent);
props->get_MouseWheelDelta(&delta);
WheelEvent wheelEvent(true, NS_WHEEL_WHEEL, mWidget.Get());
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(wheelEvent);
wheelEvent.refPoint = MetroUtils::LogToPhys(position);
wheelEvent.time = timestamp;
wheelEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
wheelEvent.pressure = pressure;
wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
static int previousVertLeftOverDelta = 0;
static int previousHorLeftOverDelta = 0;
// Since we have chosen DOM_DELTA_LINE as our deltaMode, deltaX or deltaY
// should be the number of lines that we want to scroll. Windows has given
// us delta, which is a more precise value, and the constant WHEEL_DELTA,
// which defines the threshold of wheel movement before an action should
// be taken.
if (horzEvent) {
wheelEvent.deltaX = delta / WHEEL_DELTA_DOUBLE;
if ((delta > 0 && previousHorLeftOverDelta < 0)
|| (delta < 0 && previousHorLeftOverDelta > 0)) {
previousHorLeftOverDelta = 0;
}
previousHorLeftOverDelta += delta;
wheelEvent.lineOrPageDeltaX = previousHorLeftOverDelta / WHEEL_DELTA;
previousHorLeftOverDelta %= WHEEL_DELTA;
} else {
int mouseWheelDelta = -1 * delta;
wheelEvent.deltaY = mouseWheelDelta / WHEEL_DELTA_DOUBLE;
if ((mouseWheelDelta > 0 && previousVertLeftOverDelta < 0)
|| (mouseWheelDelta < 0 && previousVertLeftOverDelta > 0)) {
previousVertLeftOverDelta = 0;
}
previousVertLeftOverDelta += mouseWheelDelta;
wheelEvent.lineOrPageDeltaY = previousVertLeftOverDelta / WHEEL_DELTA;
previousVertLeftOverDelta %= WHEEL_DELTA;
}
NPEvent pluginEvent;
pluginEvent.event = horzEvent ? WM_MOUSEHWHEEL : WM_MOUSEWHEEL;
union {
uintptr_t wParam;
uint16_t parts[2];
} wParam;
// This sets lParam and the low-order word of wParam
InitPluginMouseEventParams(wheelEvent,
wParam.wParam,
pluginEvent.lParam);
// The high-order word of wParam is the number of detents the wheel has
// traveled. Positive values mean the wheel was rotated forward or to the
// right. Negative values mean the wheel was rotated backward (toward the
// user) or to the left.
wParam.parts[1] = horzEvent
? wheelEvent.lineOrPageDeltaX
: wheelEvent.lineOrPageDeltaY * -1;
pluginEvent.wParam = wParam.wParam;
wheelEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&wheelEvent);
WRL::ComPtr<UI::Input::IPointerPoint> point;
aArgs->get_CurrentPoint(point.GetAddressOf());
mGestureRecognizer->ProcessMouseWheelEvent(point.Get(),
wheelEvent.IsShift(),
wheelEvent.IsControl());
return S_OK;
}
// This helper function is used when a character has been received by our
// app. This function is responsible for sending an appropriate gecko
// event in response to the character entered.
void
MetroInput::OnCharacterReceived(uint32_t aCharCode,
UI::Core::CorePhysicalKeyStatus const& aKeyStatus)
{
// We only send NS_KEY_PRESS events for printable characters
if (IsControlCharacter(aCharCode)) {
return;
}
nsKeyEvent keyEvent(true, NS_KEY_PRESS, mWidget.Get());
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(keyEvent);
keyEvent.time = ::GetMessageTime();
keyEvent.isChar = true;
keyEvent.charCode = aCharCode;
NPEvent pluginEvent;
pluginEvent.event = WM_CHAR;
InitPluginKeyEventLParamFromKeyStatus(pluginEvent.lParam,
aKeyStatus);
// The wParam of WM_CHAR messages is a single utf-16 character.
// If charCode cannot be represented in a single utf-16 character,
// then we must send two WM_CHAR messages; one with a high surrogate
// and one with a low surrogate.
if (IS_IN_BMP(aCharCode)) {
pluginEvent.wParam = aCharCode;
} else {
pluginEvent.wParam = H_SURROGATE(aCharCode);
nsPluginEvent surrogateEvent(true, NS_PLUGIN_INPUT_EVENT, mWidget.Get());
surrogateEvent.time = ::GetMessageTime();
surrogateEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&surrogateEvent);
pluginEvent.wParam = L_SURROGATE(aCharCode);
}
keyEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&keyEvent);
}
// This helper function is responsible for sending an appropriate gecko
// event in response to a keyboard key being pressed down.
void
MetroInput::OnKeyDown(uint32_t aVKey,
UI::Core::CorePhysicalKeyStatus const& aKeyStatus)
{
// We can only send a gecko event if there is a gecko virtual key code that
// corresponds to the Windows virtual key code
uint32_t mozKey = GetMozKeyCode(aVKey);
if (!mozKey) {
return;
}
nsKeyEvent keyEvent(true, NS_KEY_DOWN, mWidget.Get());
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(keyEvent);
keyEvent.time = ::GetMessageTime();
keyEvent.keyCode = mozKey;
NPEvent pluginEvent;
pluginEvent.event = WM_KEYDOWN;
InitPluginKeyEventLParamFromKeyStatus(pluginEvent.lParam,
aKeyStatus);
// wParam is the virtual key code
pluginEvent.wParam = aVKey;
keyEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&keyEvent);
// If the key being pressed is not a printable character (e.g.
// enter, delete, backspace, alt, etc), we will not receive a character
// event for this key press, which means that our OnCharacterReceived
// function won't get called, and that we will never send a gecko
// keypress event for this character.
//
// If the control key is currently down while this key was pressed,
// then Windows will either send us a character event indicating that a
// control character has been generated, or it will not send us any
// character event (depending on which key has been pressed).
//
// We want gecko to be aware of every keypress, so we send the
// keypress here.
//
// Note: We use MapVirtualKey to determine what character code we will
// send in our NS_KEY_PRESS event. MapVirtualKey does not provide a very
// good mapping from virtual key code to character. It doesn't
// take into account any additional keys (shift, caps lock, etc), or
// any diacritics that might already have been pressed, and it
// ALWAYS gives the upper-case version of latin characters a-z. Thus,
// we only use it if we absolutely have to. In this case, Windows isn't
// going to send us character events, so the only way to come up with a
// character value to send in our NS_KEY_PRESS is to use MapVirtualKey.
keyEvent.charCode = MapVirtualKey(aVKey, MAPVK_VK_TO_CHAR);
keyEvent.isChar = !IsControlCharacter(keyEvent.charCode);
if (!keyEvent.isChar
|| mModifierKeyState.IsControl()) {
// We don't want to send another message to our plugin,
// so reset keyEvent.pluginEvent
keyEvent.pluginEvent = nullptr;
keyEvent.message = NS_KEY_PRESS;
DispatchEventIgnoreStatus(&keyEvent);
}
}
// This helper function is responsible for sending an appropriate gecko
// event in response to a keyboard key being released.
void
MetroInput::OnKeyUp(uint32_t aVKey,
UI::Core::CorePhysicalKeyStatus const& aKeyStatus)
{
uint32_t mozKey = GetMozKeyCode(aVKey);
if (!mozKey) {
return;
}
nsKeyEvent keyEvent(true, NS_KEY_UP, mWidget.Get());
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(keyEvent);
keyEvent.time = ::GetMessageTime();
keyEvent.keyCode = mozKey;
NPEvent pluginEvent;
pluginEvent.event = WM_KEYUP;
InitPluginKeyEventLParamFromKeyStatus(pluginEvent.lParam,
aKeyStatus);
// wParam is the virtual key code
pluginEvent.wParam = aVKey;
keyEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&keyEvent);
}
/**
* This helper function is used by our processing of PointerPressed,
* PointerReleased, and PointerMoved events.
* It dispatches a gecko event in response to the input received. This
* function should only be called for non-touch (i.e. pen or mouse) input
* events.
*
* @param aPoint the PointerPoint for the input event
*/
void
MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) {
WRL::ComPtr<UI::Input::IPointerPointProperties> props;
UI::Input::PointerUpdateKind pointerUpdateKind;
boolean canBeDoubleTap;
aPoint->get_Properties(props.GetAddressOf());
props->get_PointerUpdateKind(&pointerUpdateKind);
mGestureRecognizer->CanBeDoubleTap(aPoint, &canBeDoubleTap);
nsMouseEvent mouseEvent(true,
NS_MOUSE_MOVE,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
NPEvent pluginEvent;
pluginEvent.event = WM_MOUSEMOVE;
switch (pointerUpdateKind) {
case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed:
// We don't bother setting mouseEvent.button because it is already
// set to nsMouseEvent::buttonType::eLeftButton whose value is 0.
mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
pluginEvent.event = (canBeDoubleTap
? WM_LBUTTONDBLCLK
: WM_LBUTTONDOWN);
break;
case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed:
mouseEvent.button = nsMouseEvent::buttonType::eMiddleButton;
mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
pluginEvent.event = (canBeDoubleTap
? WM_MBUTTONDBLCLK
: WM_MBUTTONDOWN);
break;
case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed:
mouseEvent.button = nsMouseEvent::buttonType::eRightButton;
mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
pluginEvent.event = (canBeDoubleTap
? WM_RBUTTONDBLCLK
: WM_RBUTTONDOWN);
break;
case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased:
// We don't bother setting mouseEvent.button because it is already
// set to nsMouseEvent::buttonType::eLeftButton whose value is 0.
mouseEvent.message = NS_MOUSE_BUTTON_UP;
pluginEvent.event = WM_LBUTTONUP;
break;
case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased:
mouseEvent.button = nsMouseEvent::buttonType::eMiddleButton;
mouseEvent.message = NS_MOUSE_BUTTON_UP;
pluginEvent.event = WM_MBUTTONUP;
break;
case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
mouseEvent.button = nsMouseEvent::buttonType::eRightButton;
mouseEvent.message = NS_MOUSE_BUTTON_UP;
pluginEvent.event = WM_RBUTTONUP;
break;
}
InitGeckoMouseEventFromPointerPoint(mouseEvent, aPoint);
mouseEvent.pluginEvent = static_cast<void*>(&pluginEvent);
DispatchEventIgnoreStatus(&mouseEvent);
return;
}
// This event is raised when the user pushes the left mouse button, presses a
// pen to the surface, or presses a touch screen.
HRESULT
MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
// For mouse and pen input, simply call our helper function
if (deviceType !=
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
OnPointerNonTouch(currentPoint.Get());
mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
return S_OK;
}
// This is touch input.
// Create the new touch point and add it to our event.
uint32_t pointerId;
currentPoint->get_PointerId(&pointerId);
nsCOMPtr<nsIDOMTouch> touch = CreateDOMTouch(currentPoint.Get());
touch->mChanged = true;
mTouches.Put(pointerId, touch);
mTouchEvent.message = NS_TOUCH_START;
// If this is the first touchstart of a touch session,
// dispatch it now so we can see if preventDefault gets called on it.
if (mTouches.Count() == 1) {
nsEventStatus status;
DispatchPendingTouchEvent(status);
mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
// If the first touchstart event has preventDefault called on it, then
// we will not perform any default actions associated with any touch
// events for this session, including touchmove events.
// Thus, mTouchStartDefaultPrevented implies mTouchMoveDefaultPrevented.
mTouchMoveDefaultPrevented = mTouchStartDefaultPrevented;
mIsFirstTouchMove = !mTouchStartDefaultPrevented;
}
// If the first touchstart of this touch session had its preventDefault
// called on it, we will not perform any default actions for any of the
// touches in this touch session.
if (!mTouchStartDefaultPrevented) {
mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
}
return S_OK;
}
// This event is raised when the user lifts the left mouse button, lifts a
// pen from the surface, or lifts her/his finger from a touch screen.
HRESULT
MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
// For mouse and pen input, simply call our helper function
if (deviceType !=
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
OnPointerNonTouch(currentPoint.Get());
mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
return S_OK;
}
// This is touch input.
// Get the touch associated with this touch point.
uint32_t pointerId;
currentPoint->get_PointerId(&pointerId);
nsCOMPtr<nsIDOMTouch> touch = mTouches.Get(pointerId);
// We are about to dispatch a touchend. Before we do that, we should make
// sure that we don't have a touchmove or touchstart sitting around for this
// point.
if (touch->mChanged) {
DispatchPendingTouchEvent();
}
mTouches.Remove(pointerId);
// touchend events only have a single touch; the touch that has been removed
mTouchEvent.message = NS_TOUCH_END;
mTouchEvent.touches.Clear();
mTouchEvent.touches.AppendElement(CreateDOMTouch(currentPoint.Get()));
mTouchEvent.time = ::GetMessageTime();
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(mTouchEvent);
DispatchEventIgnoreStatus(&mTouchEvent);
// mTouchEvent.message should always be set to NS_TOUCH_MOVE
mTouchEvent.message = NS_TOUCH_MOVE;
// If the first touchstart of this touch session had its preventDefault
// called on it, we will not perform any default actions for any of the
// touches in this touch session. Note that we don't check
// mTouchMoveDefaultPrevented here. The reason is that, even if
// preventDefault was called on the first touchmove event, we might still
// want to dispatch a click (mousemove, mousedown, mouseup) in response to
// this touch.
if (!mTouchStartDefaultPrevented) {
mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
}
return S_OK;
}
// This event is raised when the user moves the mouse, moves a pen that is
// in contact with the surface, or moves a finger that is in contact with
// a touch screen.
HRESULT
MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
// For mouse and pen input, simply call our helper function
if (deviceType !=
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
OnPointerNonTouch(currentPoint.Get());
WRL::ComPtr<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
pointerPoints;
aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
return S_OK;
}
// This is touch input.
// Get the touch associated with this touch point.
uint32_t pointerId;
currentPoint->get_PointerId(&pointerId);
nsCOMPtr<nsIDOMTouch> touch = mTouches.Get(pointerId);
// Some old drivers cause us to receive a PointerMoved event for a touchId
// after we've already received a PointerReleased event for that touchId.
// To work around those busted drivers, we simply ignore TouchMoved events
// for touchIds that we are not currently tracking. See bug 819223.
if (!touch) {
return S_OK;
}
// If we're modifying a touch entry that has a pending update, go through
// with the update.
if (touch->mChanged) {
DispatchPendingTouchEvent();
}
touch = CreateDOMTouch(currentPoint.Get());
touch->mChanged = true;
mTouches.Put(pointerId, touch);
// If this is the first touch move of our session, we should dispatch it
// and store our mTouchMoveDefaultPrevented value
if (mIsFirstTouchMove) {
nsEventStatus status;
DispatchPendingTouchEvent(status);
mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
mIsFirstTouchMove = false;
}
// We will perform default actions for touchmove events only if
// preventDefault was not called on the first touchmove event and
// preventDefault was not called on the first touchstart event. Checking
// mTouchMoveDefaultPrevented is enough here because it will be set if
// mTouchStartDefaultPrevented is true.
if (!mTouchMoveDefaultPrevented) {
WRL::ComPtr<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
pointerPoints;
aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
}
return S_OK;
}
void
MetroInput::InitGeckoMouseEventFromPointerPoint(
nsMouseEvent& aEvent,
UI::Input::IPointerPoint* aPointerPoint) {
NS_ASSERTION(aPointerPoint, "InitGeckoMouseEventFromPointerPoint "
"called with null PointerPoint!");
WRL::ComPtr<UI::Input::IPointerPointProperties> props;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
Foundation::Point position;
uint64_t timestamp;
float pressure;
boolean canBeDoubleTap;
aPointerPoint->get_Position(&position);
aPointerPoint->get_Timestamp(&timestamp);
aPointerPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
aPointerPoint->get_Properties(props.GetAddressOf());
props->get_Pressure(&pressure);
mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap);
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(aEvent);
aEvent.refPoint = MetroUtils::LogToPhys(position);
aEvent.time = timestamp;
if (!canBeDoubleTap) {
aEvent.clickCount = 1;
} else {
aEvent.clickCount = 2;
}
aEvent.pressure = pressure;
MozInputSourceFromDeviceType(deviceType, aEvent.inputSource);
}
// This event is raised when a precise pointer moves into the bounding box of
// our window. For touch input, this will be raised before the PointerPressed
// event.
HRESULT
MetroInput::OnPointerEntered(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
// We only dispatch mouseenter and mouseexit events for mouse and pen input.
if (deviceType !=
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
nsMouseEvent mouseEvent(true,
NS_MOUSE_ENTER,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
InitGeckoMouseEventFromPointerPoint(mouseEvent, currentPoint.Get());
DispatchEventIgnoreStatus(&mouseEvent);
}
return S_OK;
}
// This event is raised when a precise pointer leaves the bounding box of
// our window. For touch input, this will be raised before the
// PointerReleased event.
HRESULT
MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender,
UI::Core::IPointerEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> device;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
currentPoint->get_PointerDevice(device.GetAddressOf());
device->get_PointerDeviceType(&deviceType);
// We only dispatch mouseenter and mouseexit events for mouse and pen input.
if (deviceType !=
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
nsMouseEvent mouseEvent(true,
NS_MOUSE_EXIT,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
InitGeckoMouseEventFromPointerPoint(mouseEvent, currentPoint.Get());
DispatchEventIgnoreStatus(&mouseEvent);
}
return S_OK;
}
/**
* This helper function is called by our processing of "manipulation events".
* Manipulation events are how Windows sends us information about swipes,
* magnification gestures, and rotation gestures.
*
* @param aDelta the gesture change since the last update
* @param aPosition the position at which the gesture is taking place
* @param aMagEventType the event type of the gecko magnification gesture to
* send
* @param aRotEventType the event type of the gecko rotation gesture to send
*/
void
MetroInput::ProcessManipulationDelta(
UI::Input::ManipulationDelta const& aDelta,
Foundation::Point const& aPosition,
uint32_t aMagEventType,
uint32_t aRotEventType) {
// If we ONLY have translation (no rotation, no expansion), then this
// gesture isn't a two-finger gesture. We ignore it here, since the only
// thing it could eventually be is a swipe, and we deal with swipes in
// OnManipulationCompleted.
if ((aDelta.Translation.X != 0.0f
|| aDelta.Translation.Y != 0.0f)
&& (aDelta.Rotation == 0.0f
&& aDelta.Expansion == 0.0f)) {
return;
}
// Send a gecko event indicating the magnification since the last update.
nsSimpleGestureEvent magEvent(true,
aMagEventType,
mWidget.Get(), 0, 0.0);
magEvent.delta = aDelta.Expansion;
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(magEvent);
magEvent.time = ::GetMessageTime();
magEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
magEvent.refPoint = MetroUtils::LogToPhys(aPosition);
DispatchEventIgnoreStatus(&magEvent);
// Send a gecko event indicating the rotation since the last update.
nsSimpleGestureEvent rotEvent(true,
aRotEventType,
mWidget.Get(), 0, 0.0);
rotEvent.delta = aDelta.Rotation;
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(rotEvent);
rotEvent.time = ::GetMessageTime();
rotEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
rotEvent.refPoint = MetroUtils::LogToPhys(aPosition);
if (rotEvent.delta >= 0) {
rotEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE;
} else {
rotEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
}
DispatchEventIgnoreStatus(&rotEvent);
}
// This event is raised when a gesture is detected to have started. The
// data that is in "aArgs->Cumulative" represents the initial update, so
// it is equivalent to what we will receive later during ManipulationUpdated
// events.
HRESULT
MetroInput::OnManipulationStarted(
UI::Input::IGestureRecognizer* aSender,
UI::Input::IManipulationStartedEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
UI::Input::ManipulationDelta delta;
Foundation::Point position;
aArgs->get_Cumulative(&delta);
aArgs->get_Position(&position);
ProcessManipulationDelta(delta,
position,
NS_SIMPLE_GESTURE_MAGNIFY_START,
NS_SIMPLE_GESTURE_ROTATE_START);
return S_OK;
}
// This event is raised to inform us of changes in the gesture
// that is occurring. We simply pass "aArgs->Delta" (which gives us the
// changes since the last update or start event), and "aArgs->Position"
// to our helper function.
HRESULT
MetroInput::OnManipulationUpdated(
UI::Input::IGestureRecognizer* aSender,
UI::Input::IManipulationUpdatedEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
UI::Input::ManipulationDelta delta;
Foundation::Point position;
aArgs->get_Delta(&delta);
aArgs->get_Position(&position);
ProcessManipulationDelta(delta,
position,
NS_SIMPLE_GESTURE_MAGNIFY_UPDATE,
NS_SIMPLE_GESTURE_ROTATE_UPDATE);
return S_OK;
}
// Gecko expects a "finished" event to be sent that has the cumulative
// changes since the gesture began. The idea is that consumers could hook
// only this last event and still effectively support magnification and
// rotation. We accomplish sending this "finished" event by calling our
// helper function with a cumulative "delta" value.
//
// After sending the "finished" event, this function detects and sends
// swipe gestures.
HRESULT
MetroInput::OnManipulationCompleted(
UI::Input::IGestureRecognizer* aSender,
UI::Input::IManipulationCompletedEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
UI::Input::ManipulationDelta delta;
Foundation::Point position;
Devices::Input::PointerDeviceType deviceType;
aArgs->get_Position(&position);
aArgs->get_Cumulative(&delta);
aArgs->get_PointerDeviceType(&deviceType);
// Send the "finished" events. Note that we are setting
// delta to the cumulative ManipulationDelta.
ProcessManipulationDelta(delta,
position,
NS_SIMPLE_GESTURE_MAGNIFY,
NS_SIMPLE_GESTURE_ROTATE);
// If any rotation or expansion has occurred, we know we're not dealing
// with a swipe gesture, so let's bail early. Also, the GestureRecognizer
// will send us Manipulation events even for mouse input under certain
// conditions. I was able to initiate swipe events consistently by
// clicking as I threw the mouse from one side of the screen to the other.
// Thus the check for mouse input here.
if (delta.Rotation != 0.0f
|| delta.Expansion != 0.0f
|| deviceType ==
Devices::Input::PointerDeviceType::PointerDeviceType_Mouse) {
return S_OK;
}
// No rotation or expansion occurred, so it is possible that we have a
// swipe gesture. We must check that the distance the user's finger
// traveled and the velocity with which it traveled exceed our thresholds
// for classifying the movement as a swipe.
UI::Input::ManipulationVelocities velocities;
aArgs->get_Velocities(&velocities);
bool isHorizontalSwipe =
abs(velocities.Linear.X) >= SWIPE_MIN_VELOCITY
&& abs(delta.Translation.X) >= SWIPE_MIN_DISTANCE;
bool isVerticalSwipe =
abs(velocities.Linear.Y) >= SWIPE_MIN_VELOCITY
&& abs(delta.Translation.Y) >= SWIPE_MIN_DISTANCE;
// If our thresholds were exceeded for both a vertical and a horizontal
// swipe, it means the user is flinging her/his finger around and we
// should just ignore the input.
if (isHorizontalSwipe && isVerticalSwipe) {
return S_OK;
}
if (isHorizontalSwipe) {
nsSimpleGestureEvent swipeEvent(true, NS_SIMPLE_GESTURE_SWIPE,
mWidget.Get(), 0, 0.0);
swipeEvent.direction = delta.Translation.X > 0
? nsIDOMSimpleGestureEvent::DIRECTION_RIGHT
: nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
swipeEvent.delta = delta.Translation.X;
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(swipeEvent);
swipeEvent.time = ::GetMessageTime();
swipeEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
swipeEvent.refPoint = MetroUtils::LogToPhys(position);
DispatchEventIgnoreStatus(&swipeEvent);
}
if (isVerticalSwipe) {
nsSimpleGestureEvent swipeEvent(true, NS_SIMPLE_GESTURE_SWIPE,
mWidget.Get(), 0, 0.0);
swipeEvent.direction = delta.Translation.Y > 0
? nsIDOMSimpleGestureEvent::DIRECTION_DOWN
: nsIDOMSimpleGestureEvent::DIRECTION_UP;
swipeEvent.delta = delta.Translation.Y;
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(swipeEvent);
swipeEvent.time = ::GetMessageTime();
swipeEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
swipeEvent.refPoint = MetroUtils::LogToPhys(position);
DispatchEventIgnoreStatus(&swipeEvent);
}
return S_OK;
}
// This event is raised when a sequence of pointer events has been
// interpreted by the GestureRecognizer as a tap (this could be a mouse
// click, a pen tap, or a tap on a touch surface).
HRESULT
MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
UI::Input::ITappedEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
Devices::Input::PointerDeviceType deviceType;
aArgs->get_PointerDeviceType(&deviceType);
// For mouse and pen input, we send mousedown/mouseup/mousemove
// events as soon as we detect the input event. For touch input, a set of
// mousedown/mouseup events will be sent only once a tap has been detected.
if (deviceType ==
Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
Foundation::Point position;
aArgs->get_Position(&position);
// Set up the mouse event that we'll reuse for mousemove, mousedown, and
// mouseup
nsMouseEvent mouseEvent(true,
NS_MOUSE_MOVE,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(mouseEvent);
mouseEvent.refPoint = MetroUtils::LogToPhys(position);
mouseEvent.time = ::GetMessageTime();
aArgs->get_TapCount(&mouseEvent.clickCount);
mouseEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
// Send the mousemove
DispatchEventIgnoreStatus(&mouseEvent);
// Send the mousedown
mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
mouseEvent.button = nsMouseEvent::buttonType::eLeftButton;
DispatchEventIgnoreStatus(&mouseEvent);
// Send the mouseup
mouseEvent.message = NS_MOUSE_BUTTON_UP;
DispatchEventIgnoreStatus(&mouseEvent);
}
return S_OK;
}
// This event is raised when a sequence of pointer events has been
// interpreted by the GestureRecognizer as a right tap.
// This could be a mouse right-click, a right-click on a pen, or
// a tap-and-hold on a touch surface.
HRESULT
MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
UI::Input::IRightTappedEventArgs* aArgs)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
Devices::Input::PointerDeviceType deviceType;
Foundation::Point position;
aArgs->get_PointerDeviceType(&deviceType);
aArgs->get_Position(&position);
nsMouseEvent contextMenu(true,
NS_CONTEXTMENU,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(contextMenu);
contextMenu.refPoint = MetroUtils::LogToPhys(position);
contextMenu.time = ::GetMessageTime();
MozInputSourceFromDeviceType(deviceType, contextMenu.inputSource);
DispatchEventIgnoreStatus(&contextMenu);
return S_OK;
}
/**
* Implementation Details
*/
nsEventStatus MetroInput::sThrowawayStatus;
// This function allows us to call MetroWidget's DispatchEvent function
// without passing in a status. It uses a static nsEventStatus whose value
// is never read. This allows us to avoid the (admittedly small) overhead
// of creating a new nsEventStatus every time we dispatch an event.
void
MetroInput::DispatchEventIgnoreStatus(nsGUIEvent *aEvent) {
mWidget->DispatchEvent(aEvent, sThrowawayStatus);
}
void
MetroInput::DispatchPendingTouchEvent(nsEventStatus& aStatus) {
mTouchEvent.touches.Clear();
mTouches.Enumerate(&AppendToTouchList,
static_cast<void*>(&mTouchEvent.touches));
mTouchEvent.time = ::GetMessageTime();
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(mTouchEvent);
mWidget->DispatchEvent(&mTouchEvent, aStatus);
// mTouchEvent.message should always be set to NS_TOUCH_MOVE
mTouchEvent.message = NS_TOUCH_MOVE;
}
void
MetroInput::DispatchPendingTouchEvent() {
DispatchPendingTouchEvent(sThrowawayStatus);
}
void
MetroInput::UnregisterInputEvents() {
// Unregister ourselves for the edge swipe event
WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
if (SUCCEEDED(Foundation::GetActivationFactory(
WRL::Wrappers::HStringReference(
RuntimeClass_Windows_UI_Input_EdgeGesture).Get(),
edgeStatics.GetAddressOf()))) {
WRL::ComPtr<UI::Input::IEdgeGesture> edge;
if (SUCCEEDED(edgeStatics->GetForCurrentView(edge.GetAddressOf()))) {
edge->remove_Completed(mTokenEdgeGesture);
}
}
// Unregister ourselves from the AcceleratorKeyActivated event
WRL::ComPtr<ICoreAcceleratorKeys> coreAcceleratorKeys;
if (SUCCEEDED(mDispatcher.As<ICoreAcceleratorKeys>(&coreAcceleratorKeys))) {
coreAcceleratorKeys->remove_AcceleratorKeyActivated(
mTokenAcceleratorKeyActivated);
}
// Unregister ourselves from the window events. This is extremely important;
// once this object is destroyed we don't want Windows to try to send events
// to it.
mWindow->remove_PointerPressed(mTokenPointerPressed);
mWindow->remove_PointerReleased(mTokenPointerReleased);
mWindow->remove_PointerMoved(mTokenPointerMoved);
mWindow->remove_PointerEntered(mTokenPointerEntered);
mWindow->remove_PointerExited(mTokenPointerExited);
mWindow->remove_PointerWheelChanged(mTokenPointerWheelChanged);
// Unregistering from the gesture recognizer events probably isn't as
// necessary since we're about to destroy the gesture recognizer, but
// it can't hurt.
mGestureRecognizer->remove_ManipulationStarted(mTokenManipulationStarted);
mGestureRecognizer->remove_ManipulationUpdated(mTokenManipulationUpdated);
mGestureRecognizer->remove_ManipulationCompleted(
mTokenManipulationCompleted);
mGestureRecognizer->remove_Tapped(mTokenTapped);
mGestureRecognizer->remove_RightTapped(mTokenRightTapped);
}
// Since this is static, it should automatically be initialized
// to all 0 bytes.
uint32_t MetroInput::sVirtualKeyMap[255];
bool MetroInput::sIsVirtualKeyMapInitialized = false;
// References
// nsVKList.h - defines NS_VK_*
// nsIDOMKeyEvent.idl - defines the values that NS_VK_* are based on
void
MetroInput::InitializeVirtualKeyMap() {
sVirtualKeyMap[System::VirtualKey::VirtualKey_Cancel] = NS_VK_CANCEL;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Help] = NS_VK_HELP;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Back] = NS_VK_BACK;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Tab] = NS_VK_TAB;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Clear] = NS_VK_CLEAR;
// XXX: Do we want RETURN or ENTER here?
sVirtualKeyMap[System::VirtualKey::VirtualKey_Enter] = NS_VK_RETURN;
// NS_VK_ENTER
sVirtualKeyMap[System::VirtualKey::VirtualKey_Shift] = NS_VK_SHIFT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Control] = NS_VK_CONTROL;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Menu] = NS_VK_ALT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Pause] = NS_VK_PAUSE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_CapitalLock] = NS_VK_CAPS_LOCK;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Kana] = NS_VK_KANA;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Hangul] = NS_VK_HANGUL;
// NS_VK_EISU (Japanese Mac keyboard only)
sVirtualKeyMap[System::VirtualKey::VirtualKey_Junja] = NS_VK_JUNJA;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Final] = NS_VK_FINAL;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Hanja] = NS_VK_HANJA;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Kanji] = NS_VK_KANJI;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Escape] = NS_VK_ESCAPE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Convert] = NS_VK_CONVERT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NonConvert] = NS_VK_NONCONVERT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Accept] = NS_VK_ACCEPT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_ModeChange] = NS_VK_MODECHANGE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Space] = NS_VK_SPACE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_PageUp] = NS_VK_PAGE_UP;
sVirtualKeyMap[System::VirtualKey::VirtualKey_PageDown] = NS_VK_PAGE_DOWN;
sVirtualKeyMap[System::VirtualKey::VirtualKey_End] = NS_VK_END;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Home] = NS_VK_HOME;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Left] = NS_VK_LEFT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Up] = NS_VK_UP;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Right] = NS_VK_RIGHT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Down] = NS_VK_DOWN;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Select] = NS_VK_SELECT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Print] = NS_VK_PRINT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Execute] = NS_VK_EXECUTE;
sVirtualKeyMap[VK_SNAPSHOT] = NS_VK_PRINTSCREEN;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Insert] = NS_VK_INSERT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Delete] = NS_VK_DELETE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number0] = NS_VK_0;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number1] = NS_VK_1;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number2] = NS_VK_2;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number3] = NS_VK_3;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number4] = NS_VK_4;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number5] = NS_VK_5;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number6] = NS_VK_6;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number7] = NS_VK_7;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number8] = NS_VK_8;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Number9] = NS_VK_9;
// NS_VK_COLON
sVirtualKeyMap[VK_OEM_1] = NS_VK_SEMICOLON;
// NS_VK_LESS_THAN
// NS_VK_EQUALS
// NS_VK_GREATER_THAN
// NS_VK_QUESTION_MARK
// NS_VK_AT
sVirtualKeyMap[System::VirtualKey::VirtualKey_A] = NS_VK_A;
sVirtualKeyMap[System::VirtualKey::VirtualKey_B] = NS_VK_B;
sVirtualKeyMap[System::VirtualKey::VirtualKey_C] = NS_VK_C;
sVirtualKeyMap[System::VirtualKey::VirtualKey_D] = NS_VK_D;
sVirtualKeyMap[System::VirtualKey::VirtualKey_E] = NS_VK_E;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F] = NS_VK_F;
sVirtualKeyMap[System::VirtualKey::VirtualKey_G] = NS_VK_G;
sVirtualKeyMap[System::VirtualKey::VirtualKey_H] = NS_VK_H;
sVirtualKeyMap[System::VirtualKey::VirtualKey_I] = NS_VK_I;
sVirtualKeyMap[System::VirtualKey::VirtualKey_J] = NS_VK_J;
sVirtualKeyMap[System::VirtualKey::VirtualKey_K] = NS_VK_K;
sVirtualKeyMap[System::VirtualKey::VirtualKey_L] = NS_VK_L;
sVirtualKeyMap[System::VirtualKey::VirtualKey_M] = NS_VK_M;
sVirtualKeyMap[System::VirtualKey::VirtualKey_N] = NS_VK_N;
sVirtualKeyMap[System::VirtualKey::VirtualKey_O] = NS_VK_O;
sVirtualKeyMap[System::VirtualKey::VirtualKey_P] = NS_VK_P;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Q] = NS_VK_Q;
sVirtualKeyMap[System::VirtualKey::VirtualKey_R] = NS_VK_R;
sVirtualKeyMap[System::VirtualKey::VirtualKey_S] = NS_VK_S;
sVirtualKeyMap[System::VirtualKey::VirtualKey_T] = NS_VK_T;
sVirtualKeyMap[System::VirtualKey::VirtualKey_U] = NS_VK_U;
sVirtualKeyMap[System::VirtualKey::VirtualKey_V] = NS_VK_V;
sVirtualKeyMap[System::VirtualKey::VirtualKey_W] = NS_VK_W;
sVirtualKeyMap[System::VirtualKey::VirtualKey_X] = NS_VK_X;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Y] = NS_VK_Y;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Z] = NS_VK_Z;
sVirtualKeyMap[System::VirtualKey::VirtualKey_LeftWindows] = NS_VK_WIN;
sVirtualKeyMap[System::VirtualKey::VirtualKey_RightWindows] = NS_VK_WIN;
sVirtualKeyMap[System::VirtualKey::VirtualKey_LeftMenu] = NS_VK_CONTEXT_MENU;
sVirtualKeyMap[System::VirtualKey::VirtualKey_RightMenu] = NS_VK_CONTEXT_MENU;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Sleep] = NS_VK_SLEEP;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad0] = NS_VK_NUMPAD0;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad1] = NS_VK_NUMPAD1;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad2] = NS_VK_NUMPAD2;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad3] = NS_VK_NUMPAD3;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad4] = NS_VK_NUMPAD4;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad5] = NS_VK_NUMPAD5;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad6] = NS_VK_NUMPAD6;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad7] = NS_VK_NUMPAD7;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad8] = NS_VK_NUMPAD8;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberPad9] = NS_VK_NUMPAD9;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Multiply] = NS_VK_MULTIPLY;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Add] = NS_VK_ADD;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Separator] = NS_VK_SEPARATOR;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Subtract] = NS_VK_SUBTRACT;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Decimal] = NS_VK_DECIMAL;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Divide] = NS_VK_DIVIDE;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F1] = NS_VK_F1;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F2] = NS_VK_F2;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F3] = NS_VK_F3;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F4] = NS_VK_F4;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F5] = NS_VK_F5;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F6] = NS_VK_F6;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F7] = NS_VK_F7;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F8] = NS_VK_F8;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F9] = NS_VK_F9;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F10] = NS_VK_F10;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F11] = NS_VK_F11;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F12] = NS_VK_F12;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F13] = NS_VK_F13;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F14] = NS_VK_F14;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F15] = NS_VK_F15;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F16] = NS_VK_F16;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F17] = NS_VK_F17;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F18] = NS_VK_F18;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F19] = NS_VK_F19;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F20] = NS_VK_F20;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F21] = NS_VK_F21;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F22] = NS_VK_F22;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F23] = NS_VK_F23;
sVirtualKeyMap[System::VirtualKey::VirtualKey_F24] = NS_VK_F24;
sVirtualKeyMap[System::VirtualKey::VirtualKey_NumberKeyLock] = NS_VK_NUM_LOCK;
sVirtualKeyMap[System::VirtualKey::VirtualKey_Scroll] = NS_VK_SCROLL_LOCK;
// NS_VK_CIRCUMFLEX
// NS_VK_EXCLAMATION
// NS_VK_DOUBLE_QUOTE
// NS_VK_HASH
// NS_VK_DOLLAR
// NS_VK_PERCENT
// NS_VK_AMPERSAND
// NS_VK_UNDERSCORE
// NS_VK_OPEN_PAREN
// NS_VK_CLOSE_PAREN
// NS_VK_ASTERISK
sVirtualKeyMap[VK_OEM_PLUS] = NS_VK_PLUS;
// NS_VK_PIPE
sVirtualKeyMap[VK_OEM_MINUS] = NS_VK_HYPHEN_MINUS;
// NS_VK_OPEN_CURLY_BRACKET
// NS_VK_CLOSE_CURLY_BRACKET
// NS_VK_TILDE
sVirtualKeyMap[VK_OEM_COMMA] = NS_VK_COMMA;
sVirtualKeyMap[VK_OEM_PERIOD] = NS_VK_PERIOD;
sVirtualKeyMap[VK_OEM_2] = NS_VK_SLASH;
sVirtualKeyMap[VK_OEM_3] = NS_VK_BACK_QUOTE;
sVirtualKeyMap[VK_OEM_4] = NS_VK_OPEN_BRACKET;
sVirtualKeyMap[VK_OEM_5] = NS_VK_BACK_SLASH;
sVirtualKeyMap[VK_OEM_6] = NS_VK_CLOSE_BRACKET;
sVirtualKeyMap[VK_OEM_7] = NS_VK_QUOTE;
// NS_VK_META
// NS_VK_ALTGR
}
uint32_t
MetroInput::GetMozKeyCode(uint32_t aKey)
{
return sVirtualKeyMap[aKey];
}
void
MetroInput::RegisterInputEvents()
{
NS_ASSERTION(mWindow, "Must have a window to register for input events!");
NS_ASSERTION(mGestureRecognizer,
"Must have a GestureRecognizer for input events!");
NS_ASSERTION(mDispatcher,
"Must have a CoreDispatcher to register for input events!");
// Register for the AcceleratorKeyActivated event. This is sent to us
// from our CoreDispatcher, but we have to use the ICoreAcceleratorKeys
// interface of our CoreDispatcher.
WRL::ComPtr<ICoreAcceleratorKeys> coreAcceleratorKeys;
mDispatcher.As<ICoreAcceleratorKeys>(&coreAcceleratorKeys);
coreAcceleratorKeys->add_AcceleratorKeyActivated(
WRL::Callback<AcceleratorKeyActivatedHandler>(
this,
&MetroInput::OnAcceleratorKeyActivated).Get(),
&mTokenAcceleratorKeyActivated);
// Register for edge swipe
WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
Foundation::GetActivationFactory(
WRL::Wrappers::HStringReference(
RuntimeClass_Windows_UI_Input_EdgeGesture)
.Get(),
edgeStatics.GetAddressOf());
WRL::ComPtr<UI::Input::IEdgeGesture> edge;
edgeStatics->GetForCurrentView(edge.GetAddressOf());
edge->add_Completed(
WRL::Callback<EdgeGestureHandler>(
this,
&MetroInput::OnEdgeGestureCompleted).Get(),
&mTokenEdgeGesture);
// Set up our Gesture Recognizer to raise events for the gestures we
// care about
mGestureRecognizer->put_GestureSettings(
UI::Input::GestureSettings::GestureSettings_Tap
| UI::Input::GestureSettings::GestureSettings_DoubleTap
| UI::Input::GestureSettings::GestureSettings_RightTap
| UI::Input::GestureSettings::GestureSettings_Hold
| UI::Input::GestureSettings::GestureSettings_ManipulationTranslateX
| UI::Input::GestureSettings::GestureSettings_ManipulationTranslateY
| UI::Input::GestureSettings::GestureSettings_ManipulationScale
| UI::Input::GestureSettings::GestureSettings_ManipulationRotate);
// Register for the pointer events on our Window
mWindow->add_PointerPressed(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerPressed).Get(),
&mTokenPointerPressed);
mWindow->add_PointerReleased(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerReleased).Get(),
&mTokenPointerReleased);
mWindow->add_PointerMoved(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerMoved).Get(),
&mTokenPointerMoved);
mWindow->add_PointerEntered(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerEntered).Get(),
&mTokenPointerEntered);
mWindow->add_PointerExited(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerExited).Get(),
&mTokenPointerExited);
mWindow->add_PointerWheelChanged(
WRL::Callback<PointerEventHandler>(
this,
&MetroInput::OnPointerWheelChanged).Get(),
&mTokenPointerWheelChanged);
// Register for the events raised by our Gesture Recognizer
mGestureRecognizer->add_Tapped(
WRL::Callback<TappedEventHandler>(
this,
&MetroInput::OnTapped).Get(),
&mTokenTapped);
mGestureRecognizer->add_RightTapped(
WRL::Callback<RightTappedEventHandler>(
this,
&MetroInput::OnRightTapped).Get(),
&mTokenRightTapped);
mGestureRecognizer->add_ManipulationStarted(
WRL::Callback<ManipulationStartedEventHandler>(
this,
&MetroInput::OnManipulationStarted).Get(),
&mTokenManipulationStarted);
mGestureRecognizer->add_ManipulationUpdated(
WRL::Callback<ManipulationUpdatedEventHandler>(
this,
&MetroInput::OnManipulationUpdated).Get(),
&mTokenManipulationUpdated);
mGestureRecognizer->add_ManipulationCompleted(
WRL::Callback<ManipulationCompletedEventHandler>(
this,
&MetroInput::OnManipulationCompleted).Get(),
&mTokenManipulationCompleted);
}
} } }