| /* | |
| Copyright 2007-2009 WebDriver committers | |
| Copyright 2007-2009 Google Inc. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| #include "stdafx.h" | |
| #include <ctime> | |
| #include <string> | |
| #include <iostream> | |
| #include "errorcodes.h" | |
| #include "interactions.h" | |
| #include "interactions_common.h" | |
| #include "logging.h" | |
| #include "event_firing_thread.h" | |
| using namespace std; | |
| #pragma data_seg(".LISTENER") | |
| static bool pressed = false; | |
| // The following booleans indicate whether any of the modifier keys are | |
| // depressed. These booleans represent the state from the user's point of | |
| // view - not if a modifier is depressed for the current key press (there | |
| // is an explanation on why modifier keys have to be pressed and depressed | |
| // for some keys below). | |
| // Each modifier key can only be held down when sendKeys was called without | |
| // calling releaseModifierKeys. The modifier key will only be released when | |
| // sendKeys(Keys.NULL) is called or sendKeys() is called again (with the same | |
| // modifier). | |
| // Ordinarily, sendKeys would generate the following keys sequence when | |
| // called with sendKeys("AB"): | |
| // Shift_Down A_Down A_Up Shift_Up Shift_Down B_Down B_Up Shift_Up | |
| // However, when sendKeys is called using the new Interactions API, | |
| // the modifier keys are not released. So, we want the letters to be | |
| // capitalized if Shift is pressed. The following calls: | |
| // * sendKeyPress(SHIFT) | |
| // * sendKeys("ab") | |
| // * sendKeyRelease(SHIFT) | |
| // Should generate the following events: | |
| // Shift_Down A_Down A_Up B_Down B_Up Shift_Up | |
| // With *capital* a and b. Using this boolean, we can tell if the shift key | |
| // was held down by these calls and should generate upper-case chars. | |
| static bool shiftPressed = false; | |
| static bool controlPressed = false; | |
| static bool altPressed = false; | |
| static HHOOK hook = 0; | |
| static HINSTANCE moduleHandle = NULL; | |
| #pragma data_seg() | |
| #pragma comment(linker, "/section:.LISTENER,rws") | |
| // Left Mouse button pressed? | |
| static bool leftMouseButtonPressed = false; | |
| void backgroundUnicodeKeyPress(HWND ieWindow, wchar_t c, int pause) | |
| { | |
| pause = pause / 3; | |
| // IE can crash if keyscan < 0. It's unclear this will | |
| // do anything unless the correct keyboard layout is active, | |
| // as the unicode character 'c' will already have its | |
| // appropriate capitalization. | |
| SHORT keyscan = VkKeyScanW(c); | |
| if (keyscan < 0) { | |
| keyscan = 0; | |
| } | |
| pressed = false; | |
| PostMessage(ieWindow, WM_KEYDOWN, keyscan, 0); | |
| PostMessage(ieWindow, WM_USER, 1234, 5678); | |
| wait(pause); | |
| // TODO: There must be a better way to tell when the keydown is processed | |
| clock_t maxWait = clock() + 250; | |
| while (!pressed && clock() < maxWait) { | |
| wait(5); | |
| } | |
| PostMessage(ieWindow, WM_CHAR, c, 0); | |
| wait(pause); | |
| PostMessage(ieWindow, WM_KEYUP, keyscan, 0); | |
| wait(pause); | |
| } | |
| void sendModifierKeyDown(HWND hwnd, HKL layout, int modifierKeyCode, | |
| BYTE keyboardState[256], int pause) { | |
| keyboardState[modifierKeyCode] |= 0x80; | |
| LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16; | |
| if (!PostMessage(hwnd, WM_KEYDOWN, modifierKeyCode, modifierKey)) { | |
| LOG(WARN) << "Modifier keydown failed: " << GetLastError(); | |
| } | |
| wait(pause); | |
| } | |
| void sendModifierKeyUp(HWND hwnd, HKL layout, int modifierKeyCode, | |
| BYTE keyboardState[256], int pause) { | |
| keyboardState[modifierKeyCode] &= ~0x80; | |
| LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16; | |
| modifierKey |= 0x3 << 30; | |
| if (!PostMessage(hwnd, WM_KEYUP, modifierKeyCode, modifierKey)) { | |
| LOG(WARN) << "Modifier keyup failed: " << GetLastError(); | |
| } | |
| wait(pause); | |
| } | |
| void sendModifierKeyDownIfNeeded(bool shouldSend, HWND hwnd, HKL layout, | |
| int modifierKeyCode, BYTE keyboardState[256], int pause) { | |
| if (shouldSend) { | |
| sendModifierKeyDown(hwnd, layout, modifierKeyCode, keyboardState, | |
| pause); | |
| } | |
| } | |
| void sendModifierKeyUpIfNeeded(bool shouldSend, HWND hwnd, HKL layout, | |
| int modifierKeyCode, BYTE keyboardState[256], int pause) { | |
| if (shouldSend) { | |
| sendModifierKeyUp(hwnd, layout, modifierKeyCode, keyboardState, | |
| pause); | |
| } | |
| } | |
| bool isShiftPressNeeded(WORD keyCode) { | |
| return (keyCode & 0x0100) != 0; | |
| } | |
| bool isControlPressNeeded(WORD keyCode) { | |
| return (keyCode & 0x0200) != 0; | |
| } | |
| bool isAltPressNeeded(WORD keyCode) { | |
| return (keyCode & 0x0400) != 0; | |
| } | |
| LPARAM generateKeyMessageParam(UINT scanCode, bool extended) | |
| { | |
| LPARAM lparam = 1; | |
| lparam |= scanCode << 16; | |
| if (extended) { | |
| lparam |= 1 << 24; | |
| } | |
| return lparam; | |
| } | |
| void backgroundKeyDown(HWND hwnd, HKL layout, BYTE keyboardState[256], | |
| WORD keyCode, UINT scanCode, bool extended, int pause) | |
| { | |
| // For capital letters and symbols requiring the shift key to be pressed, | |
| // A Shift key press must preceed. Unless the shift key is pressed - if | |
| // shiftPressed is true, then a shift key-down was sent in the past. | |
| sendModifierKeyDownIfNeeded(isShiftPressNeeded(keyCode) && (!shiftPressed), hwnd, layout, | |
| VK_SHIFT, keyboardState, pause); | |
| sendModifierKeyDownIfNeeded(isControlPressNeeded(keyCode) && (!controlPressed), hwnd, layout, | |
| VK_CONTROL, keyboardState, pause); | |
| sendModifierKeyDownIfNeeded(isAltPressNeeded(keyCode) && (!altPressed), hwnd, layout, | |
| VK_MENU, keyboardState, pause); | |
| // In order to produce an upper case character, the keyboard state should | |
| // be modified. See the documentation of shiftPressed to understand why | |
| // it's done only in this case. | |
| if ((shiftPressed) || (isShiftPressNeeded(keyCode))) { | |
| keyboardState[VK_SHIFT] |= 0x80; | |
| } | |
| keyCode = LOBYTE(keyCode); | |
| keyboardState[keyCode] |= 0x80; | |
| SetKeyboardState(keyboardState); | |
| LPARAM lparam = generateKeyMessageParam(scanCode, extended); | |
| pressed = false; | |
| if (!PostMessage(hwnd, WM_KEYDOWN, keyCode, lparam)) { | |
| LOG(WARN) << "Key down failed: " << GetLastError(); | |
| } | |
| PostMessage(hwnd, WM_USER, 1234, 5678); | |
| // Listen out for the keypress event which IE synthesizes when IE | |
| // processes the keydown message. Use a time out, just in case we | |
| // have not got the logic right :) | |
| clock_t maxWait = clock() + 5000; | |
| while (!pressed) { | |
| wait(5); | |
| if (clock() >= maxWait) { | |
| LOG(WARN) << "Timeout awaiting keypress: " << keyCode; | |
| break; | |
| } | |
| } | |
| } | |
| void backgroundKeyUp(HWND hwnd, HKL layout, BYTE keyboardState[256], | |
| WORD keyCode, UINT scanCode, bool extended, int pause) | |
| { | |
| WORD origKeyCode = keyCode; | |
| keyCode = LOBYTE(keyCode); | |
| keyboardState[keyCode] &= ~0x80; | |
| LPARAM lparam = generateKeyMessageParam(scanCode, extended); | |
| lparam |= 0x3 << 30; | |
| if (!PostMessage(hwnd, WM_KEYUP, keyCode, lparam)) { | |
| LOG(WARN) << "Key up failed: " << GetLastError(); | |
| } | |
| wait(pause); | |
| sendModifierKeyUpIfNeeded(isShiftPressNeeded(origKeyCode) && (!shiftPressed), hwnd, layout, | |
| VK_SHIFT, keyboardState, pause); | |
| sendModifierKeyUpIfNeeded(isControlPressNeeded(origKeyCode) && (!controlPressed), hwnd, layout, | |
| VK_CONTROL, keyboardState, pause); | |
| sendModifierKeyUpIfNeeded(isAltPressNeeded(origKeyCode) && (!altPressed), hwnd, layout, | |
| VK_MENU, keyboardState, pause); | |
| // If Shift was held down, we should reset the keyboard state for it | |
| // as well. See the comment in backgroundKeyDown on why it is set | |
| // in the first place. | |
| if ((shiftPressed) || (isShiftPressNeeded(origKeyCode))) { | |
| keyboardState[VK_SHIFT] &= ~0x80; | |
| } | |
| SetKeyboardState(keyboardState); | |
| } | |
| void backgroundKeyPress(HWND hwnd, HKL layout, BYTE keyboardState[256], | |
| WORD keyCode, UINT scanCode, bool extended, int pause) | |
| { | |
| pause = pause / 3; | |
| backgroundKeyDown(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause); | |
| backgroundKeyUp(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause); | |
| } | |
| LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) | |
| { | |
| if ((nCode == HC_ACTION) && (wParam == PM_REMOVE)) { | |
| MSG* msg = reinterpret_cast<MSG*>(lParam); | |
| if (msg->message == WM_USER && msg->wParam == 1234 && msg->lParam == 5678) { | |
| pressed = true; | |
| } | |
| } | |
| return CallNextHookEx(hook, nCode, wParam, lParam); | |
| } | |
| bool isClearAllModifiersCode(wchar_t c) | |
| { | |
| return (c == 0xE000U); | |
| } | |
| bool isShiftCode(wchar_t c) | |
| { | |
| return (c == 0xE008U); // shift (left) | |
| } | |
| bool isControlCode(wchar_t c) | |
| { | |
| return (c == 0xE009U); // control (left) | |
| } | |
| bool isAltCode(wchar_t c) | |
| { | |
| return (c == 0xE00AU); // alt (left) | |
| } | |
| bool isModifierCharacter(wchar_t c) | |
| { | |
| return isClearAllModifiersCode(c) || isShiftCode(c) || isControlCode(c) || | |
| isAltCode(c); | |
| } | |
| // All the required information to post a keyboard event message. | |
| struct KeySendingData { | |
| HWND to_window; | |
| HKL layout; | |
| BYTE* keyboardState; | |
| int pause_time; | |
| }; | |
| void sendSingleModifierEventAndAdjustState(bool matchingModifier, | |
| bool& modifierState, int modifierKeyCode, KeySendingData sendData) | |
| { | |
| if (!matchingModifier) { | |
| return; | |
| } | |
| if (modifierState) { | |
| sendModifierKeyUp(sendData.to_window, sendData.layout, | |
| modifierKeyCode, sendData.keyboardState, sendData.pause_time); | |
| } else { | |
| sendModifierKeyDown(sendData.to_window, sendData.layout, | |
| modifierKeyCode, sendData.keyboardState, sendData.pause_time); | |
| } | |
| modifierState = !modifierState; | |
| } | |
| void postModifierReleaseMessages(bool releaseShift, bool releaseControl, bool releaseAlt, | |
| KeySendingData sendData) | |
| { | |
| sendModifierKeyUpIfNeeded(releaseShift, sendData.to_window, sendData.layout, VK_SHIFT, sendData.keyboardState, sendData.pause_time); | |
| sendModifierKeyUpIfNeeded(releaseControl, sendData.to_window, sendData.layout, VK_CONTROL, sendData.keyboardState, sendData.pause_time); | |
| sendModifierKeyUpIfNeeded(releaseAlt, sendData.to_window, sendData.layout, VK_MENU, sendData.keyboardState, sendData.pause_time); | |
| } | |
| void sendModifierKeyEvent(wchar_t c, bool& shiftKey, bool& controlKey, | |
| bool& altKey, KeySendingData sendData) | |
| { | |
| if (isClearAllModifiersCode(c)) { | |
| postModifierReleaseMessages(shiftKey, controlKey, altKey, sendData); | |
| shiftKey = controlKey = altKey = false; | |
| } else { | |
| sendSingleModifierEventAndAdjustState(isShiftCode(c), shiftKey, VK_SHIFT, sendData); | |
| sendSingleModifierEventAndAdjustState(isControlCode(c), controlKey, VK_CONTROL, sendData); | |
| sendSingleModifierEventAndAdjustState(isAltCode(c), altKey, VK_MENU, sendData); | |
| if (isShiftCode(c)) { | |
| updateShiftKeyState(shiftKey); | |
| } | |
| } | |
| } | |
| static HKL attachInputToIEThread(HWND directInputTo) | |
| { | |
| DWORD currThreadId = GetCurrentThreadId(); | |
| DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL); | |
| GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR) | |
| &sendKeys, &moduleHandle); | |
| hook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) &GetMessageProc, | |
| moduleHandle, ieWinThreadId); | |
| // Attach to the IE thread so we can send keys to it. | |
| if (ieWinThreadId != currThreadId) { | |
| AttachThreadInput(currThreadId, ieWinThreadId, true); | |
| } | |
| return GetKeyboardLayout(ieWinThreadId); | |
| } | |
| static void detachInputFromIEThread(HWND directInputTo) | |
| { | |
| DWORD currThreadId = GetCurrentThreadId(); | |
| DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL); | |
| if (hook) { | |
| UnhookWindowsHookEx(hook); | |
| } | |
| if (moduleHandle) { | |
| FreeLibrary(moduleHandle); | |
| } | |
| if (ieWinThreadId != currThreadId) { | |
| AttachThreadInput(currThreadId, ieWinThreadId, false); | |
| } | |
| } | |
| extern "C" | |
| { | |
| void sendKeys(WINDOW_HANDLE windowHandle, const wchar_t* value, int timePerKey) | |
| { | |
| if (!windowHandle) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return; | |
| } | |
| HWND directInputTo = static_cast<HWND>(windowHandle); | |
| HKL layout = attachInputToIEThread(directInputTo); | |
| BYTE keyboardState[256]; | |
| ::ZeroMemory(keyboardState, sizeof(keyboardState)); | |
| bool controlKey = controlPressed; | |
| bool shiftKey = shiftPressed; | |
| bool altKey = altPressed; | |
| KeySendingData sendData; | |
| sendData.to_window = directInputTo; | |
| sendData.layout = layout; | |
| sendData.keyboardState = keyboardState; | |
| sendData.pause_time = timePerKey; | |
| for (const wchar_t *p = value; *p; ++p) { | |
| const wchar_t c = *p; | |
| bool extended = false; | |
| UINT scanCode = 0; | |
| WORD keyCode = 0; | |
| if (isModifierCharacter(c)) { | |
| sendModifierKeyEvent(c, shiftKey, controlKey, altKey, sendData); | |
| shiftPressed = shiftKey; | |
| controlPressed = controlKey; | |
| altPressed = altKey; | |
| continue; | |
| } else if (c == 0xE001U) { // ^break | |
| keyCode = VK_CANCEL; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE002U) { // help | |
| keyCode = VK_HELP; | |
| scanCode = keyCode; | |
| } else if (c == 0xE003U) { // back space | |
| keyCode = VK_BACK; | |
| scanCode = keyCode; | |
| } else if (c == 0xE004U) { // tab | |
| keyCode = VK_TAB; | |
| scanCode = keyCode; | |
| } else if (c == 0xE005U) { // clear | |
| keyCode = VK_CLEAR; | |
| scanCode = keyCode; | |
| } else if (c == 0xE006U) { // return | |
| keyCode = VK_RETURN; | |
| scanCode = keyCode; | |
| } else if (c == 0xE007U) { // enter | |
| keyCode = VK_RETURN; | |
| scanCode = keyCode; | |
| } else if (c == 0xE00BU) { // pause | |
| keyCode = VK_PAUSE; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE00CU) { // escape | |
| keyCode = VK_ESCAPE; | |
| scanCode = keyCode; | |
| } else if (c == 0xE00DU) { // space | |
| keyCode = VK_SPACE; | |
| scanCode = keyCode; | |
| } else if (c == 0xE00EU) { // page up | |
| keyCode = VK_PRIOR; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE00FU) { // page down | |
| keyCode = VK_NEXT; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE010U) { // end | |
| keyCode = VK_END; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE011U) { // home | |
| keyCode = VK_HOME; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE012U) { // left arrow | |
| keyCode = VK_LEFT; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE013U) { // up arrow | |
| keyCode = VK_UP; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE014U) { // right arrow | |
| keyCode = VK_RIGHT; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE015U) { // down arrow | |
| keyCode = VK_DOWN; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE016U) { // insert | |
| keyCode = VK_INSERT; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE017U) { // delete | |
| keyCode = VK_DELETE; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE018U) { // semicolon | |
| keyCode = VkKeyScanExW(L';', layout); | |
| scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout); | |
| } else if (c == 0xE019U) { // equals | |
| keyCode = VkKeyScanExW(L'=', layout); | |
| scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout); | |
| } else if (c == 0xE01AU) { // numpad0 | |
| keyCode = VK_NUMPAD0; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE01BU) { // numpad1 | |
| keyCode = VK_NUMPAD1; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE01CU) { // numpad2 | |
| keyCode = VK_NUMPAD2; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE01DU) { // numpad3 | |
| keyCode = VK_NUMPAD3; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE01EU) { // numpad4 | |
| keyCode = VK_NUMPAD4; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE01FU) { // numpad5 | |
| keyCode = VK_NUMPAD5; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE020U) { // numpad6 | |
| keyCode = VK_NUMPAD6; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE021U) { // numpad7 | |
| keyCode = VK_NUMPAD7; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE022U) { // numpad8 | |
| keyCode = VK_NUMPAD8; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE023U) { // numpad9 | |
| keyCode = VK_NUMPAD9; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE024U) { // multiply | |
| keyCode = VK_MULTIPLY; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE025U) { // add | |
| keyCode = VK_ADD; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE026U) { // separator | |
| keyCode = VkKeyScanExW(L',', layout); | |
| scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout); | |
| } else if (c == 0xE027U) { // subtract | |
| keyCode = VK_SUBTRACT; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE028U) { // decimal | |
| keyCode = VK_DECIMAL; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE029U) { // divide | |
| keyCode = VK_DIVIDE; | |
| scanCode = keyCode; | |
| extended = true; | |
| } else if (c == 0xE031U) { // F1 | |
| keyCode = VK_F1; | |
| scanCode = keyCode; | |
| } else if (c == 0xE032U) { // F2 | |
| keyCode = VK_F2; | |
| scanCode = keyCode; | |
| } else if (c == 0xE033U) { // F3 | |
| keyCode = VK_F3; | |
| scanCode = keyCode; | |
| } else if (c == 0xE034U) { // F4 | |
| keyCode = VK_F4; | |
| scanCode = keyCode; | |
| } else if (c == 0xE035U) { // F5 | |
| keyCode = VK_F5; | |
| scanCode = keyCode; | |
| } else if (c == 0xE036U) { // F6 | |
| keyCode = VK_F6; | |
| scanCode = keyCode; | |
| } else if (c == 0xE037U) { // F7 | |
| keyCode = VK_F7; | |
| scanCode = keyCode; | |
| } else if (c == 0xE038U) { // F8 | |
| keyCode = VK_F8; | |
| scanCode = keyCode; | |
| } else if (c == 0xE039U) { // F9 | |
| keyCode = VK_F9; | |
| scanCode = keyCode; | |
| } else if (c == 0xE03AU) { // F10 | |
| keyCode = VK_F10; | |
| scanCode = keyCode; | |
| } else if (c == 0xE03BU) { // F11 | |
| keyCode = VK_F11; | |
| scanCode = keyCode; | |
| } else if (c == 0xE03CU) { // F12 | |
| keyCode = VK_F12; | |
| scanCode = keyCode; | |
| } else if (c == L'\n') { // line feed | |
| keyCode = VK_RETURN; | |
| scanCode = keyCode; | |
| } else if (c == L'\r') { // carriage return | |
| continue; // skip it | |
| } else { | |
| keyCode = VkKeyScanExW(c, layout); | |
| scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout); | |
| if (!scanCode || (keyCode == 0xFFFFU)) { | |
| LOG(WARN) << "No translation for key. Assuming unicode input: " << c; | |
| backgroundUnicodeKeyPress(directInputTo, c, timePerKey); | |
| continue; // bogus | |
| } | |
| } | |
| // Note: There is *no* need to OR the keyCode with 0x0100 if | |
| // shiftPressed is true. ORing the keyCode with these values is to | |
| // indicate the backgroundKeyPress procedure that a modifier key | |
| // press and release should be produced for this keyCode. However, | |
| // when shiftPressed is true the events for the modifier were | |
| // already generated by the sendKeyPress function. | |
| if (shiftKey) | |
| keyCode |= static_cast<WORD>(0x0100); | |
| if (controlKey) | |
| keyCode |= static_cast<WORD>(0x0200); | |
| if (altKey) | |
| keyCode |= static_cast<WORD>(0x0400); | |
| int pause = timePerKey; | |
| // Pause for control, alt, and shift generation: if we create these | |
| // chars too fast, the target element may generated spurious chars. | |
| if (keyCode & static_cast<WORD>(0x0100)) { | |
| pause = (35 * 3); // uppercase char | |
| } else if (shiftKey || controlKey || altKey) { | |
| pause = (35 * 3); // shift|alt|ctrl | |
| } | |
| backgroundKeyPress(directInputTo, layout, keyboardState, keyCode, scanCode, | |
| extended, pause); | |
| } | |
| detachInputFromIEThread(directInputTo); | |
| } | |
| void releaseModifierKeys(WINDOW_HANDLE windowHandle, int timePerKey) | |
| { | |
| if (!windowHandle) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return; | |
| } | |
| HWND directInputTo = static_cast<HWND>(windowHandle); | |
| HKL layout = attachInputToIEThread(directInputTo); | |
| BYTE keyboardState[256]; | |
| ::ZeroMemory(keyboardState, sizeof(keyboardState)); | |
| KeySendingData sendData; | |
| sendData.to_window = directInputTo; | |
| sendData.layout = layout; | |
| sendData.keyboardState = keyboardState; | |
| sendData.pause_time = 35; | |
| if ((shiftPressed) || (controlPressed) || (altPressed)) { | |
| postModifierReleaseMessages(shiftPressed, controlPressed, altPressed, sendData); | |
| shiftPressed = false; | |
| controlPressed = false; | |
| altPressed = false; | |
| } | |
| detachInputFromIEThread(directInputTo); | |
| } | |
| bool isSameThreadAs(HWND other) | |
| { | |
| DWORD currThreadId = GetCurrentThreadId(); | |
| DWORD winThreadId = GetWindowThreadProcessId(other, NULL); | |
| return winThreadId == currThreadId; | |
| } | |
| LRESULT clickAt(WINDOW_HANDLE handle, long x, long y, long button) | |
| { | |
| if (!handle) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| HWND directInputTo = (HWND) handle; | |
| LRESULT result = mouseDownAt(handle, x, y, button); | |
| if (result != 0) { | |
| LOG(WARN) << "Mouse down did not succeed whilst clicking"; | |
| return result; | |
| } | |
| return mouseUpAt(handle, x, y, button); | |
| } | |
| static LRESULT mouseDoubleClickDown(WINDOW_HANDLE directInputTo, long x, long y) | |
| { | |
| if (!directInputTo) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| if (!isSameThreadAs((HWND) directInputTo)) { | |
| BOOL toReturn = PostMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y)); | |
| // Wait until we know that the previous message has been processed | |
| SendMessage((HWND) directInputTo, WM_USER, 0, 0); | |
| return toReturn ? 0 : 1; // Because 0 means success. | |
| } else { | |
| return SendMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y)); | |
| } | |
| } | |
| LRESULT doubleClickAt(WINDOW_HANDLE handle, long x, long y) | |
| { | |
| // A double click consists of the sequence | |
| // 1: mouseDown | |
| // 2: mouseUp | |
| // 3: doubleClick | |
| // 4: mouseUp | |
| // Which is the equivalent to two clicks with the second mouseDown event | |
| // is replaced by a doubleClick event. | |
| if (!handle) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| LRESULT result = clickAt(handle, x, y, 0); | |
| if (result != 0) { | |
| LOG(WARN) << "Mouse down did not succeed whilst clicking"; | |
| return result; | |
| } | |
| result = mouseDoubleClickDown(handle, x, y); | |
| if (result != 0) { | |
| LOG(WARN) << "Mouse down did not succeed whilst double clicking"; | |
| return result; | |
| } | |
| return mouseUpAt(handle, x, y, 0); | |
| } | |
| static void fillEventData(long button, bool buttonDown, UINT *message, WPARAM *wparam) | |
| { | |
| if(WD_CLIENT_RIGHT_MOUSE_BUTTON == button) { | |
| if(buttonDown) { | |
| *message = WM_RBUTTONDOWN; | |
| } else { | |
| *message = WM_RBUTTONUP; | |
| } | |
| *wparam = MK_RBUTTON; | |
| } else { // middle button support is declared in json wire protocol but it is not supported | |
| leftMouseButtonPressed = buttonDown; | |
| if(buttonDown) { | |
| *message = WM_LBUTTONDOWN; | |
| } else { | |
| *message = WM_LBUTTONUP; | |
| } | |
| *wparam = MK_LBUTTON; | |
| } | |
| if (shiftPressed) { | |
| *wparam |= MK_SHIFT; | |
| } | |
| } | |
| LRESULT mouseDownAt(WINDOW_HANDLE directInputTo, long x, long y, long button) | |
| { | |
| if (!directInputTo) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| UINT message; | |
| WPARAM wparam; | |
| LRESULT returnValue; | |
| fillEventData(button, true, &message, &wparam); | |
| pausePersistentEventsFiring(); | |
| if (!isSameThreadAs((HWND) directInputTo)) { | |
| BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y)); | |
| // Wait until we know that the previous message has been processed | |
| SendMessage((HWND) directInputTo, WM_USER, 0, 0); | |
| returnValue = toReturn ? 0 : 1; // Because 0 means success. | |
| } else { | |
| returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y)); | |
| } | |
| // Assume it's the left mouse button. | |
| if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) { | |
| updateLeftMouseButtonState(true); | |
| } | |
| resumePersistentEventsFiring(); | |
| return returnValue; | |
| } | |
| LRESULT mouseUpAt(WINDOW_HANDLE directInputTo, long x, long y, long button) | |
| { | |
| if (!directInputTo) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| UINT message; | |
| WPARAM wparam; | |
| LRESULT returnValue; | |
| fillEventData(button, false, &message, &wparam); | |
| pausePersistentEventsFiring(); | |
| SendMessage((HWND) directInputTo, WM_MOUSEMOVE, (shiftPressed ? MK_SHIFT : 0), MAKELPARAM(x, y)); | |
| if (!isSameThreadAs((HWND) directInputTo)) { | |
| BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y)); | |
| // Wait until we know that the previous message has been processed | |
| SendMessage((HWND) directInputTo, WM_USER, 0, 0); | |
| returnValue = toReturn ? 0 : 1; // Because 0 means success. | |
| } else { | |
| returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y)); | |
| } | |
| // Assume it's the left mouse button. | |
| if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) { | |
| updateLeftMouseButtonState(false); | |
| } | |
| resumePersistentEventsFiring(); | |
| return returnValue; | |
| } | |
| LRESULT mouseMoveTo(WINDOW_HANDLE handle, long duration, long fromX, long fromY, long toX, long toY) | |
| { | |
| if (!handle) { | |
| LOG(WARN) << "Window handle is invalid"; | |
| return ENULLPOINTER; | |
| } | |
| pausePersistentEventsFiring(); | |
| HWND directInputTo = (HWND) handle; | |
| long pointsDistance = distanceBetweenPoints(fromX, fromY, toX, toY); | |
| const int stepSizeInPixels = 5; | |
| int steps = pointsDistance / stepSizeInPixels; | |
| long sleep = duration / max(steps, 1); | |
| WPARAM buttonValue = (leftMouseButtonPressed ? MK_LBUTTON : 0); | |
| if (shiftPressed) { | |
| buttonValue |= MK_SHIFT; | |
| } | |
| for (int i = 0; i < steps + 1; i++) { | |
| //To avoid integer division rounding and cumulative floating point errors, | |
| //calculate from scratch each time | |
| int currentX = (int)(fromX + ((toX - fromX) * ((double)i) / steps)); | |
| int currentY = (int)(fromY + ((toY - fromY) * ((double)i) / steps)); | |
| SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(currentX, currentY)); | |
| wait(sleep); | |
| } | |
| SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(toX, toY)); | |
| resumePersistentEventsFiring(directInputTo, toX, toY, buttonValue); | |
| return 0; | |
| } | |
| BOOL_TYPE pending_input_events() | |
| { | |
| return false; | |
| } | |
| } |