Path: blob/trunk/cpp/webdriver-interactions/interactions.cpp
2867 views
/*1Licensed to the Software Freedom Conservancy (SFC) under one2or more contributor license agreements. See the NOTICE file3distributed with this work for additional information4regarding copyright ownership. The SFC licenses this file5to you under the Apache License, Version 2.0 (the "License");6you may not use this file except in compliance with the License.7You may obtain a copy of the License at89http://www.apache.org/licenses/LICENSE-2.01011Unless required by applicable law or agreed to in writing, software12distributed under the License is distributed on an "AS IS" BASIS,13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14See the License for the specific language governing permissions and15limitations under the License.16*/1718#include "stdafx.h"1920#include <ctime>21#include <string>22#include <iostream>2324#include "interactions.h"25#include "interactions_common.h"26#include "logging.h"27#include "event_firing_thread.h"2829#define ENULLPOINTER 223031using namespace std;3233#pragma data_seg(".LISTENER")34static bool pressed = false;35// The following booleans indicate whether any of the modifier keys are36// depressed. These booleans represent the state from the user's point of37// view - not if a modifier is depressed for the current key press (there38// is an explanation on why modifier keys have to be pressed and depressed39// for some keys below).40// Each modifier key can only be held down when sendKeys was called without41// calling releaseModifierKeys. The modifier key will only be released when42// sendKeys(Keys.NULL) is called or sendKeys() is called again (with the same43// modifier).44// Ordinarily, sendKeys would generate the following keys sequence when45// called with sendKeys("AB"):46// Shift_Down A_Down A_Up Shift_Up Shift_Down B_Down B_Up Shift_Up47// However, when sendKeys is called using the new Interactions API,48// the modifier keys are not released. So, we want the letters to be49// capitalized if Shift is pressed. The following calls:50// * sendKeyPress(SHIFT)51// * sendKeys("ab")52// * sendKeyRelease(SHIFT)53// Should generate the following events:54// Shift_Down A_Down A_Up B_Down B_Up Shift_Up55// With *capital* a and b. Using this boolean, we can tell if the shift key56// was held down by these calls and should generate upper-case chars.57static bool shiftPressed = false;58static bool controlPressed = false;59static bool altPressed = false;60static HHOOK hook = 0;61static HINSTANCE moduleHandle = NULL;6263#pragma data_seg()64#pragma comment(linker, "/section:.LISTENER,rws")6566// Left Mouse button pressed?67static bool leftMouseButtonPressed = false;6869void backgroundUnicodeKeyPress(HWND ieWindow, wchar_t c, int pause)70{71pause = pause / 3;7273// IE can crash if keyscan < 0. It's unclear this will74// do anything unless the correct keyboard layout is active,75// as the unicode character 'c' will already have its76// appropriate capitalization.77SHORT keyscan = VkKeyScanW(c);78if (keyscan < 0) {79keyscan = 0;80}81pressed = false;82PostMessage(ieWindow, WM_KEYDOWN, keyscan, 0);83PostMessage(ieWindow, WM_USER, 1234, 5678);84wait(pause);8586// TODO: There must be a better way to tell when the keydown is processed87clock_t maxWait = clock() + 250;88while (!pressed && clock() < maxWait) {89wait(5);90}9192PostMessage(ieWindow, WM_CHAR, c, 0);9394wait(pause);9596PostMessage(ieWindow, WM_KEYUP, keyscan, 0);9798wait(pause);99}100101void sendModifierKeyDown(HWND hwnd, HKL layout, int modifierKeyCode,102BYTE keyboardState[256], int pause) {103keyboardState[modifierKeyCode] |= 0x80;104105LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16;106if (!PostMessage(hwnd, WM_KEYDOWN, modifierKeyCode, modifierKey)) {107LOG(WARN) << "Modifier keydown failed: " << GetLastError();108}109110wait(pause);111}112113void sendModifierKeyUp(HWND hwnd, HKL layout, int modifierKeyCode,114BYTE keyboardState[256], int pause) {115keyboardState[modifierKeyCode] &= ~0x80;116117LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16;118modifierKey |= 0x3 << 30;119120if (!PostMessage(hwnd, WM_KEYUP, modifierKeyCode, modifierKey)) {121LOG(WARN) << "Modifier keyup failed: " << GetLastError();122}123wait(pause);124125}126127void sendModifierKeyDownIfNeeded(bool shouldSend, HWND hwnd, HKL layout,128int modifierKeyCode, BYTE keyboardState[256], int pause) {129130if (shouldSend) {131sendModifierKeyDown(hwnd, layout, modifierKeyCode, keyboardState,132pause);133}134}135136void sendModifierKeyUpIfNeeded(bool shouldSend, HWND hwnd, HKL layout,137int modifierKeyCode, BYTE keyboardState[256], int pause) {138139if (shouldSend) {140sendModifierKeyUp(hwnd, layout, modifierKeyCode, keyboardState,141pause);142}143}144145bool isShiftPressNeeded(WORD keyCode) {146return (keyCode & 0x0100) != 0;147}148149bool isControlPressNeeded(WORD keyCode) {150return (keyCode & 0x0200) != 0;151}152153bool isAltPressNeeded(WORD keyCode) {154return (keyCode & 0x0400) != 0;155}156157LPARAM generateKeyMessageParam(UINT scanCode, bool extended)158{159LPARAM lparam = 1;160lparam |= scanCode << 16;161if (extended) {162lparam |= 1 << 24;163}164165return lparam;166}167168void backgroundKeyDown(HWND hwnd, HKL layout, BYTE keyboardState[256],169WORD keyCode, UINT scanCode, bool extended, int pause)170{171// For capital letters and symbols requiring the shift key to be pressed,172// A Shift key press must preceed. Unless the shift key is pressed - if173// shiftPressed is true, then a shift key-down was sent in the past.174sendModifierKeyDownIfNeeded(isShiftPressNeeded(keyCode) && (!shiftPressed), hwnd, layout,175VK_SHIFT, keyboardState, pause);176177sendModifierKeyDownIfNeeded(isControlPressNeeded(keyCode) && (!controlPressed), hwnd, layout,178VK_CONTROL, keyboardState, pause);179180sendModifierKeyDownIfNeeded(isAltPressNeeded(keyCode) && (!altPressed), hwnd, layout,181VK_MENU, keyboardState, pause);182183// In order to produce an upper case character, the keyboard state should184// be modified. See the documentation of shiftPressed to understand why185// it's done only in this case.186if ((shiftPressed) || (isShiftPressNeeded(keyCode))) {187keyboardState[VK_SHIFT] |= 0x80;188}189190keyCode = LOBYTE(keyCode);191keyboardState[keyCode] |= 0x80;192193SetKeyboardState(keyboardState);194195LPARAM lparam = generateKeyMessageParam(scanCode, extended);196197pressed = false;198if (!PostMessage(hwnd, WM_KEYDOWN, keyCode, lparam)) {199LOG(WARN) << "Key down failed: " << GetLastError();200}201202PostMessage(hwnd, WM_USER, 1234, 5678);203204// Listen out for the keypress event which IE synthesizes when IE205// processes the keydown message. Use a time out, just in case we206// have not got the logic right :)207208clock_t maxWait = clock() + 5000;209while (!pressed) {210wait(5);211if (clock() >= maxWait) {212LOG(WARN) << "Timeout awaiting keypress: " << keyCode;213break;214}215}216}217218void backgroundKeyUp(HWND hwnd, HKL layout, BYTE keyboardState[256],219WORD keyCode, UINT scanCode, bool extended, int pause)220{221WORD origKeyCode = keyCode;222keyCode = LOBYTE(keyCode);223keyboardState[keyCode] &= ~0x80;224225LPARAM lparam = generateKeyMessageParam(scanCode, extended);226lparam |= 0x3 << 30;227if (!PostMessage(hwnd, WM_KEYUP, keyCode, lparam)) {228LOG(WARN) << "Key up failed: " << GetLastError();229}230231wait(pause);232233sendModifierKeyUpIfNeeded(isShiftPressNeeded(origKeyCode) && (!shiftPressed), hwnd, layout,234VK_SHIFT, keyboardState, pause);235sendModifierKeyUpIfNeeded(isControlPressNeeded(origKeyCode) && (!controlPressed), hwnd, layout,236VK_CONTROL, keyboardState, pause);237sendModifierKeyUpIfNeeded(isAltPressNeeded(origKeyCode) && (!altPressed), hwnd, layout,238VK_MENU, keyboardState, pause);239240// If Shift was held down, we should reset the keyboard state for it241// as well. See the comment in backgroundKeyDown on why it is set242// in the first place.243if ((shiftPressed) || (isShiftPressNeeded(origKeyCode))) {244keyboardState[VK_SHIFT] &= ~0x80;245}246247SetKeyboardState(keyboardState);248}249250251void backgroundKeyPress(HWND hwnd, HKL layout, BYTE keyboardState[256],252WORD keyCode, UINT scanCode, bool extended, int pause)253{254pause = pause / 3;255256backgroundKeyDown(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause);257backgroundKeyUp(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause);258}259260LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)261{262if ((nCode == HC_ACTION) && (wParam == PM_REMOVE)) {263MSG* msg = reinterpret_cast<MSG*>(lParam);264if (msg->message == WM_USER && msg->wParam == 1234 && msg->lParam == 5678) {265pressed = true;266}267}268269return CallNextHookEx(hook, nCode, wParam, lParam);270}271272bool isClearAllModifiersCode(wchar_t c)273{274return (c == 0xE000U);275}276277bool isShiftCode(wchar_t c)278{279return (c == 0xE008U); // shift (left)280}281282bool isControlCode(wchar_t c)283{284return (c == 0xE009U); // control (left)285}286287bool isAltCode(wchar_t c)288{289return (c == 0xE00AU); // alt (left)290}291292bool isModifierCharacter(wchar_t c)293{294return isClearAllModifiersCode(c) || isShiftCode(c) || isControlCode(c) ||295isAltCode(c);296}297298// All the required information to post a keyboard event message.299struct KeySendingData {300HWND to_window;301HKL layout;302BYTE* keyboardState;303int pause_time;304};305306void sendSingleModifierEventAndAdjustState(bool matchingModifier,307bool& modifierState, int modifierKeyCode, KeySendingData sendData)308{309if (!matchingModifier) {310return;311}312313if (modifierState) {314sendModifierKeyUp(sendData.to_window, sendData.layout,315modifierKeyCode, sendData.keyboardState, sendData.pause_time);316} else {317sendModifierKeyDown(sendData.to_window, sendData.layout,318modifierKeyCode, sendData.keyboardState, sendData.pause_time);319}320modifierState = !modifierState;321}322323void postModifierReleaseMessages(bool releaseShift, bool releaseControl, bool releaseAlt,324KeySendingData sendData)325{326sendModifierKeyUpIfNeeded(releaseShift, sendData.to_window, sendData.layout, VK_SHIFT, sendData.keyboardState, sendData.pause_time);327sendModifierKeyUpIfNeeded(releaseControl, sendData.to_window, sendData.layout, VK_CONTROL, sendData.keyboardState, sendData.pause_time);328sendModifierKeyUpIfNeeded(releaseAlt, sendData.to_window, sendData.layout, VK_MENU, sendData.keyboardState, sendData.pause_time);329}330331void sendModifierKeyEvent(wchar_t c, bool& shiftKey, bool& controlKey,332bool& altKey, KeySendingData sendData)333334{335if (isClearAllModifiersCode(c)) {336postModifierReleaseMessages(shiftKey, controlKey, altKey, sendData);337338shiftKey = controlKey = altKey = false;339} else {340sendSingleModifierEventAndAdjustState(isShiftCode(c), shiftKey, VK_SHIFT, sendData);341sendSingleModifierEventAndAdjustState(isControlCode(c), controlKey, VK_CONTROL, sendData);342sendSingleModifierEventAndAdjustState(isAltCode(c), altKey, VK_MENU, sendData);343if (isShiftCode(c)) {344updateShiftKeyState(shiftKey);345}346}347}348349static HKL attachInputToIEThread(HWND directInputTo)350{351DWORD currThreadId = GetCurrentThreadId();352DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL);353354GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)355&sendKeys, &moduleHandle);356357hook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) &GetMessageProc,358moduleHandle, ieWinThreadId);359360if (hook == NULL) {361LOGERR(WARN) << "Unable to set Windows hook. Individual keystrokes will be very slow";362}363364// Attach to the IE thread so we can send keys to it.365if (ieWinThreadId != currThreadId) {366AttachThreadInput(currThreadId, ieWinThreadId, true);367}368369return GetKeyboardLayout(ieWinThreadId);370}371372static void detachInputFromIEThread(HWND directInputTo)373{374DWORD currThreadId = GetCurrentThreadId();375DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL);376377if (hook) {378UnhookWindowsHookEx(hook);379}380381if (moduleHandle) {382FreeLibrary(moduleHandle);383}384385if (ieWinThreadId != currThreadId) {386AttachThreadInput(currThreadId, ieWinThreadId, false);387}388}389390extern "C"391{392void sendKeys(WINDOW_HANDLE windowHandle, const wchar_t* value, int timePerKey)393{394if (!windowHandle) {395LOG(WARN) << "Window handle is invalid";396return;397}398399HWND directInputTo = static_cast<HWND>(windowHandle);400401HKL layout = attachInputToIEThread(directInputTo);402BYTE keyboardState[256];403::ZeroMemory(keyboardState, sizeof(keyboardState));404405bool controlKey = controlPressed;406bool shiftKey = shiftPressed;407bool altKey = altPressed;408KeySendingData sendData;409sendData.to_window = directInputTo;410sendData.layout = layout;411sendData.keyboardState = keyboardState;412sendData.pause_time = timePerKey;413414for (const wchar_t *p = value; *p; ++p) {415const wchar_t c = *p;416417bool extended = false;418419UINT scanCode = 0;420WORD keyCode = 0;421422if (isModifierCharacter(c)) {423sendModifierKeyEvent(c, shiftKey, controlKey, altKey, sendData);424shiftPressed = shiftKey;425controlPressed = controlKey;426altPressed = altKey;427continue;428} else if (c == 0xE001U) { // ^break429keyCode = VK_CANCEL;430scanCode = keyCode;431extended = true;432} else if (c == 0xE002U) { // help433keyCode = VK_HELP;434scanCode = keyCode;435} else if (c == 0xE003U) { // back space436keyCode = VK_BACK;437scanCode = keyCode;438} else if (c == 0xE004U) { // tab439keyCode = VK_TAB;440scanCode = keyCode;441} else if (c == 0xE005U) { // clear442keyCode = VK_CLEAR;443scanCode = keyCode;444} else if (c == 0xE006U) { // return445keyCode = VK_RETURN;446scanCode = keyCode;447} else if (c == 0xE007U) { // enter448keyCode = VK_RETURN;449scanCode = keyCode;450} else if (c == 0xE00BU) { // pause451keyCode = VK_PAUSE;452scanCode = keyCode;453extended = true;454} else if (c == 0xE00CU) { // escape455keyCode = VK_ESCAPE;456scanCode = keyCode;457} else if (c == 0xE00DU) { // space458keyCode = VK_SPACE;459scanCode = keyCode;460} else if (c == 0xE00EU) { // page up461keyCode = VK_PRIOR;462scanCode = keyCode;463extended = true;464} else if (c == 0xE00FU) { // page down465keyCode = VK_NEXT;466scanCode = keyCode;467extended = true;468} else if (c == 0xE010U) { // end469keyCode = VK_END;470scanCode = keyCode;471extended = true;472} else if (c == 0xE011U) { // home473keyCode = VK_HOME;474scanCode = keyCode;475extended = true;476} else if (c == 0xE012U) { // left arrow477keyCode = VK_LEFT;478scanCode = keyCode;479extended = true;480} else if (c == 0xE013U) { // up arrow481keyCode = VK_UP;482scanCode = keyCode;483extended = true;484} else if (c == 0xE014U) { // right arrow485keyCode = VK_RIGHT;486scanCode = keyCode;487extended = true;488} else if (c == 0xE015U) { // down arrow489keyCode = VK_DOWN;490scanCode = keyCode;491extended = true;492} else if (c == 0xE016U) { // insert493keyCode = VK_INSERT;494scanCode = keyCode;495extended = true;496} else if (c == 0xE017U) { // delete497keyCode = VK_DELETE;498scanCode = keyCode;499extended = true;500} else if (c == 0xE018U) { // semicolon501keyCode = VkKeyScanExW(L';', layout);502scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);503} else if (c == 0xE019U) { // equals504keyCode = VkKeyScanExW(L'=', layout);505scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);506} else if (c == 0xE01AU) { // numpad0507keyCode = VK_NUMPAD0;508scanCode = keyCode;509extended = true;510} else if (c == 0xE01BU) { // numpad1511keyCode = VK_NUMPAD1;512scanCode = keyCode;513extended = true;514} else if (c == 0xE01CU) { // numpad2515keyCode = VK_NUMPAD2;516scanCode = keyCode;517extended = true;518} else if (c == 0xE01DU) { // numpad3519keyCode = VK_NUMPAD3;520scanCode = keyCode;521extended = true;522} else if (c == 0xE01EU) { // numpad4523keyCode = VK_NUMPAD4;524scanCode = keyCode;525extended = true;526} else if (c == 0xE01FU) { // numpad5527keyCode = VK_NUMPAD5;528scanCode = keyCode;529extended = true;530} else if (c == 0xE020U) { // numpad6531keyCode = VK_NUMPAD6;532scanCode = keyCode;533extended = true;534} else if (c == 0xE021U) { // numpad7535keyCode = VK_NUMPAD7;536scanCode = keyCode;537extended = true;538} else if (c == 0xE022U) { // numpad8539keyCode = VK_NUMPAD8;540scanCode = keyCode;541extended = true;542} else if (c == 0xE023U) { // numpad9543keyCode = VK_NUMPAD9;544scanCode = keyCode;545extended = true;546} else if (c == 0xE024U) { // multiply547keyCode = VK_MULTIPLY;548scanCode = keyCode;549extended = true;550} else if (c == 0xE025U) { // add551keyCode = VK_ADD;552scanCode = keyCode;553extended = true;554} else if (c == 0xE026U) { // separator555keyCode = VkKeyScanExW(L',', layout);556scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);557} else if (c == 0xE027U) { // subtract558keyCode = VK_SUBTRACT;559scanCode = keyCode;560extended = true;561} else if (c == 0xE028U) { // decimal562keyCode = VK_DECIMAL;563scanCode = keyCode;564extended = true;565} else if (c == 0xE029U) { // divide566keyCode = VK_DIVIDE;567scanCode = keyCode;568extended = true;569} else if (c == 0xE031U) { // F1570keyCode = VK_F1;571scanCode = keyCode;572} else if (c == 0xE032U) { // F2573keyCode = VK_F2;574scanCode = keyCode;575} else if (c == 0xE033U) { // F3576keyCode = VK_F3;577scanCode = keyCode;578} else if (c == 0xE034U) { // F4579keyCode = VK_F4;580scanCode = keyCode;581} else if (c == 0xE035U) { // F5582keyCode = VK_F5;583scanCode = keyCode;584} else if (c == 0xE036U) { // F6585keyCode = VK_F6;586scanCode = keyCode;587} else if (c == 0xE037U) { // F7588keyCode = VK_F7;589scanCode = keyCode;590} else if (c == 0xE038U) { // F8591keyCode = VK_F8;592scanCode = keyCode;593} else if (c == 0xE039U) { // F9594keyCode = VK_F9;595scanCode = keyCode;596} else if (c == 0xE03AU) { // F10597keyCode = VK_F10;598scanCode = keyCode;599} else if (c == 0xE03BU) { // F11600keyCode = VK_F11;601scanCode = keyCode;602} else if (c == 0xE03CU) { // F12603keyCode = VK_F12;604scanCode = keyCode;605} else if (c == L'\n') { // line feed606keyCode = VK_RETURN;607scanCode = keyCode;608} else if (c == L'\r') { // carriage return609continue; // skip it610} else {611keyCode = VkKeyScanExW(c, layout);612scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);613if (!scanCode || (keyCode == 0xFFFFU)) {614LOG(WARN) << "No translation for key. Assuming unicode input: " << c;615backgroundUnicodeKeyPress(directInputTo, c, timePerKey);616continue; // bogus617}618}619620// Note: There is *no* need to OR the keyCode with 0x0100 if621// shiftPressed is true. ORing the keyCode with these values is to622// indicate the backgroundKeyPress procedure that a modifier key623// press and release should be produced for this keyCode. However,624// when shiftPressed is true the events for the modifier were625// already generated by the sendKeyPress function.626if (shiftKey)627keyCode |= static_cast<WORD>(0x0100);628if (controlKey)629keyCode |= static_cast<WORD>(0x0200);630if (altKey)631keyCode |= static_cast<WORD>(0x0400);632633int pause = timePerKey;634635// Pause for control, alt, and shift generation: if we create these636// chars too fast, the target element may generated spurious chars.637638if (keyCode & static_cast<WORD>(0x0100)) {639pause = (35 * 3); // uppercase char640} else if (shiftKey || controlKey || altKey) {641pause = (35 * 3); // shift|alt|ctrl642}643644backgroundKeyPress(directInputTo, layout, keyboardState, keyCode, scanCode,645extended, pause);646}647648detachInputFromIEThread(directInputTo);649}650651void releaseModifierKeys(WINDOW_HANDLE windowHandle, int timePerKey)652{653if (!windowHandle) {654LOG(WARN) << "Window handle is invalid";655return;656}657658HWND directInputTo = static_cast<HWND>(windowHandle);659660HKL layout = attachInputToIEThread(directInputTo);661BYTE keyboardState[256];662::ZeroMemory(keyboardState, sizeof(keyboardState));663664KeySendingData sendData;665666sendData.to_window = directInputTo;667sendData.layout = layout;668sendData.keyboardState = keyboardState;669sendData.pause_time = 35;670671if ((shiftPressed) || (controlPressed) || (altPressed)) {672postModifierReleaseMessages(shiftPressed, controlPressed, altPressed, sendData);673shiftPressed = false;674controlPressed = false;675altPressed = false;676}677678detachInputFromIEThread(directInputTo);679}680681bool isSameThreadAs(HWND other)682{683DWORD currThreadId = GetCurrentThreadId();684DWORD winThreadId = GetWindowThreadProcessId(other, NULL);685686return winThreadId == currThreadId;687}688689LRESULT clickAt(WINDOW_HANDLE handle, long x, long y, long button)690{691if (!handle) {692LOG(WARN) << "Window handle is invalid";693return ENULLPOINTER;694}695696HWND directInputTo = (HWND) handle;697698LRESULT result = mouseDownAt(handle, x, y, button);699if (result != 0) {700LOG(WARN) << "Mouse down did not succeed whilst clicking";701return result;702}703704return mouseUpAt(handle, x, y, button);705}706707static LRESULT mouseDoubleClickDown(WINDOW_HANDLE directInputTo, long x, long y)708{709if (!directInputTo) {710LOG(WARN) << "Window handle is invalid";711return ENULLPOINTER;712}713714if (!isSameThreadAs((HWND) directInputTo)) {715BOOL toReturn = PostMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y));716717// Wait until we know that the previous message has been processed718SendMessage((HWND) directInputTo, WM_USER, 0, 0);719return toReturn ? 0 : 1; // Because 0 means success.720} else {721return SendMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y));722}723}724725LRESULT doubleClickAt(WINDOW_HANDLE handle, long x, long y)726{727// A double click consists of the sequence728// 1: mouseDown729// 2: mouseUp730// 3: doubleClick731// 4: mouseUp732// Which is the equivalent to two clicks with the second mouseDown event733// is replaced by a doubleClick event.734735if (!handle) {736LOG(WARN) << "Window handle is invalid";737return ENULLPOINTER;738}739740LRESULT result = clickAt(handle, x, y, 0);741if (result != 0) {742LOG(WARN) << "Mouse down did not succeed whilst clicking";743return result;744}745746result = mouseDoubleClickDown(handle, x, y);747if (result != 0) {748LOG(WARN) << "Mouse down did not succeed whilst double clicking";749return result;750}751752return mouseUpAt(handle, x, y, 0);753}754755static void fillEventData(long button, bool buttonDown, UINT *message, WPARAM *wparam)756{757if(WD_CLIENT_RIGHT_MOUSE_BUTTON == button) {758if(buttonDown) {759*message = WM_RBUTTONDOWN;760} else {761*message = WM_RBUTTONUP;762}763*wparam = MK_RBUTTON;764} else { // middle button support is declared in json wire protocol but it is not supported765leftMouseButtonPressed = buttonDown;766if(buttonDown) {767*message = WM_LBUTTONDOWN;768} else {769*message = WM_LBUTTONUP;770}771*wparam = MK_LBUTTON;772}773if (shiftPressed) {774*wparam |= MK_SHIFT;775}776}777778LRESULT mouseDownAt(WINDOW_HANDLE directInputTo, long x, long y, long button)779{780if (!directInputTo) {781LOG(WARN) << "Window handle is invalid";782return ENULLPOINTER;783}784785UINT message;786WPARAM wparam;787LRESULT returnValue;788789fillEventData(button, true, &message, &wparam);790pausePersistentEventsFiring();791792if (!isSameThreadAs((HWND) directInputTo)) {793BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));794795// Wait until we know that the previous message has been processed796SendMessage((HWND) directInputTo, WM_USER, 0, 0);797returnValue = toReturn ? 0 : 1; // Because 0 means success.798} else {799returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));800}801802// Assume it's the left mouse button.803if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) {804updateLeftMouseButtonState(true);805}806resumePersistentEventsFiring();807808return returnValue;809}810811LRESULT mouseUpAt(WINDOW_HANDLE directInputTo, long x, long y, long button)812{813if (!directInputTo) {814LOG(WARN) << "Window handle is invalid";815return ENULLPOINTER;816}817818UINT message;819WPARAM wparam;820LRESULT returnValue;821822fillEventData(button, false, &message, &wparam);823pausePersistentEventsFiring();824825SendMessage((HWND) directInputTo, WM_MOUSEMOVE, (shiftPressed ? MK_SHIFT : 0), MAKELPARAM(x, y));826if (!isSameThreadAs((HWND) directInputTo)) {827BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));828829// Wait until we know that the previous message has been processed830SendMessage((HWND) directInputTo, WM_USER, 0, 0);831returnValue = toReturn ? 0 : 1; // Because 0 means success.832} else {833returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));834}835// Assume it's the left mouse button.836if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) {837updateLeftMouseButtonState(false);838}839resumePersistentEventsFiring();840841return returnValue;842}843844LRESULT mouseMoveTo(WINDOW_HANDLE handle, long duration, long fromX, long fromY, long toX, long toY)845{846if (!handle) {847LOG(WARN) << "Window handle is invalid";848return ENULLPOINTER;849}850851pausePersistentEventsFiring();852853HWND directInputTo = (HWND) handle;854long pointsDistance = distanceBetweenPoints(fromX, fromY, toX, toY);855const int stepSizeInPixels = 5;856int steps = pointsDistance / stepSizeInPixels;857858long sleep = duration / max(steps, 1);859860WPARAM buttonValue = (leftMouseButtonPressed ? MK_LBUTTON : 0);861if (shiftPressed) {862buttonValue |= MK_SHIFT;863}864865for (int i = 0; i < steps + 1; i++) {866//To avoid integer division rounding and cumulative floating point errors,867//calculate from scratch each time868int currentX = (int)(fromX + ((toX - fromX) * ((double)i) / steps));869int currentY = (int)(fromY + ((toY - fromY) * ((double)i) / steps));870SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(currentX, currentY));871wait(sleep);872}873874SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(toX, toY));875resumePersistentEventsFiring(directInputTo, toX, toY, buttonValue);876877return 0;878}879880bool pending_input_events()881{882return false;883}884885}886887888