Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/webdriver-interactions/interactions.cpp
2867 views
1
/*
2
Licensed to the Software Freedom Conservancy (SFC) under one
3
or more contributor license agreements. See the NOTICE file
4
distributed with this work for additional information
5
regarding copyright ownership. The SFC licenses this file
6
to you under the Apache License, Version 2.0 (the "License");
7
you may not use this file except in compliance with the License.
8
You may obtain a copy of the License at
9
10
http://www.apache.org/licenses/LICENSE-2.0
11
12
Unless required by applicable law or agreed to in writing, software
13
distributed under the License is distributed on an "AS IS" BASIS,
14
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
See the License for the specific language governing permissions and
16
limitations under the License.
17
*/
18
19
#include "stdafx.h"
20
21
#include <ctime>
22
#include <string>
23
#include <iostream>
24
25
#include "interactions.h"
26
#include "interactions_common.h"
27
#include "logging.h"
28
#include "event_firing_thread.h"
29
30
#define ENULLPOINTER 22
31
32
using namespace std;
33
34
#pragma data_seg(".LISTENER")
35
static bool pressed = false;
36
// The following booleans indicate whether any of the modifier keys are
37
// depressed. These booleans represent the state from the user's point of
38
// view - not if a modifier is depressed for the current key press (there
39
// is an explanation on why modifier keys have to be pressed and depressed
40
// for some keys below).
41
// Each modifier key can only be held down when sendKeys was called without
42
// calling releaseModifierKeys. The modifier key will only be released when
43
// sendKeys(Keys.NULL) is called or sendKeys() is called again (with the same
44
// modifier).
45
// Ordinarily, sendKeys would generate the following keys sequence when
46
// called with sendKeys("AB"):
47
// Shift_Down A_Down A_Up Shift_Up Shift_Down B_Down B_Up Shift_Up
48
// However, when sendKeys is called using the new Interactions API,
49
// the modifier keys are not released. So, we want the letters to be
50
// capitalized if Shift is pressed. The following calls:
51
// * sendKeyPress(SHIFT)
52
// * sendKeys("ab")
53
// * sendKeyRelease(SHIFT)
54
// Should generate the following events:
55
// Shift_Down A_Down A_Up B_Down B_Up Shift_Up
56
// With *capital* a and b. Using this boolean, we can tell if the shift key
57
// was held down by these calls and should generate upper-case chars.
58
static bool shiftPressed = false;
59
static bool controlPressed = false;
60
static bool altPressed = false;
61
static HHOOK hook = 0;
62
static HINSTANCE moduleHandle = NULL;
63
64
#pragma data_seg()
65
#pragma comment(linker, "/section:.LISTENER,rws")
66
67
// Left Mouse button pressed?
68
static bool leftMouseButtonPressed = false;
69
70
void backgroundUnicodeKeyPress(HWND ieWindow, wchar_t c, int pause)
71
{
72
pause = pause / 3;
73
74
// IE can crash if keyscan < 0. It's unclear this will
75
// do anything unless the correct keyboard layout is active,
76
// as the unicode character 'c' will already have its
77
// appropriate capitalization.
78
SHORT keyscan = VkKeyScanW(c);
79
if (keyscan < 0) {
80
keyscan = 0;
81
}
82
pressed = false;
83
PostMessage(ieWindow, WM_KEYDOWN, keyscan, 0);
84
PostMessage(ieWindow, WM_USER, 1234, 5678);
85
wait(pause);
86
87
// TODO: There must be a better way to tell when the keydown is processed
88
clock_t maxWait = clock() + 250;
89
while (!pressed && clock() < maxWait) {
90
wait(5);
91
}
92
93
PostMessage(ieWindow, WM_CHAR, c, 0);
94
95
wait(pause);
96
97
PostMessage(ieWindow, WM_KEYUP, keyscan, 0);
98
99
wait(pause);
100
}
101
102
void sendModifierKeyDown(HWND hwnd, HKL layout, int modifierKeyCode,
103
BYTE keyboardState[256], int pause) {
104
keyboardState[modifierKeyCode] |= 0x80;
105
106
LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16;
107
if (!PostMessage(hwnd, WM_KEYDOWN, modifierKeyCode, modifierKey)) {
108
LOG(WARN) << "Modifier keydown failed: " << GetLastError();
109
}
110
111
wait(pause);
112
}
113
114
void sendModifierKeyUp(HWND hwnd, HKL layout, int modifierKeyCode,
115
BYTE keyboardState[256], int pause) {
116
keyboardState[modifierKeyCode] &= ~0x80;
117
118
LPARAM modifierKey = 1 | MapVirtualKeyEx(modifierKeyCode, 0, layout) << 16;
119
modifierKey |= 0x3 << 30;
120
121
if (!PostMessage(hwnd, WM_KEYUP, modifierKeyCode, modifierKey)) {
122
LOG(WARN) << "Modifier keyup failed: " << GetLastError();
123
}
124
wait(pause);
125
126
}
127
128
void sendModifierKeyDownIfNeeded(bool shouldSend, HWND hwnd, HKL layout,
129
int modifierKeyCode, BYTE keyboardState[256], int pause) {
130
131
if (shouldSend) {
132
sendModifierKeyDown(hwnd, layout, modifierKeyCode, keyboardState,
133
pause);
134
}
135
}
136
137
void sendModifierKeyUpIfNeeded(bool shouldSend, HWND hwnd, HKL layout,
138
int modifierKeyCode, BYTE keyboardState[256], int pause) {
139
140
if (shouldSend) {
141
sendModifierKeyUp(hwnd, layout, modifierKeyCode, keyboardState,
142
pause);
143
}
144
}
145
146
bool isShiftPressNeeded(WORD keyCode) {
147
return (keyCode & 0x0100) != 0;
148
}
149
150
bool isControlPressNeeded(WORD keyCode) {
151
return (keyCode & 0x0200) != 0;
152
}
153
154
bool isAltPressNeeded(WORD keyCode) {
155
return (keyCode & 0x0400) != 0;
156
}
157
158
LPARAM generateKeyMessageParam(UINT scanCode, bool extended)
159
{
160
LPARAM lparam = 1;
161
lparam |= scanCode << 16;
162
if (extended) {
163
lparam |= 1 << 24;
164
}
165
166
return lparam;
167
}
168
169
void backgroundKeyDown(HWND hwnd, HKL layout, BYTE keyboardState[256],
170
WORD keyCode, UINT scanCode, bool extended, int pause)
171
{
172
// For capital letters and symbols requiring the shift key to be pressed,
173
// A Shift key press must preceed. Unless the shift key is pressed - if
174
// shiftPressed is true, then a shift key-down was sent in the past.
175
sendModifierKeyDownIfNeeded(isShiftPressNeeded(keyCode) && (!shiftPressed), hwnd, layout,
176
VK_SHIFT, keyboardState, pause);
177
178
sendModifierKeyDownIfNeeded(isControlPressNeeded(keyCode) && (!controlPressed), hwnd, layout,
179
VK_CONTROL, keyboardState, pause);
180
181
sendModifierKeyDownIfNeeded(isAltPressNeeded(keyCode) && (!altPressed), hwnd, layout,
182
VK_MENU, keyboardState, pause);
183
184
// In order to produce an upper case character, the keyboard state should
185
// be modified. See the documentation of shiftPressed to understand why
186
// it's done only in this case.
187
if ((shiftPressed) || (isShiftPressNeeded(keyCode))) {
188
keyboardState[VK_SHIFT] |= 0x80;
189
}
190
191
keyCode = LOBYTE(keyCode);
192
keyboardState[keyCode] |= 0x80;
193
194
SetKeyboardState(keyboardState);
195
196
LPARAM lparam = generateKeyMessageParam(scanCode, extended);
197
198
pressed = false;
199
if (!PostMessage(hwnd, WM_KEYDOWN, keyCode, lparam)) {
200
LOG(WARN) << "Key down failed: " << GetLastError();
201
}
202
203
PostMessage(hwnd, WM_USER, 1234, 5678);
204
205
// Listen out for the keypress event which IE synthesizes when IE
206
// processes the keydown message. Use a time out, just in case we
207
// have not got the logic right :)
208
209
clock_t maxWait = clock() + 5000;
210
while (!pressed) {
211
wait(5);
212
if (clock() >= maxWait) {
213
LOG(WARN) << "Timeout awaiting keypress: " << keyCode;
214
break;
215
}
216
}
217
}
218
219
void backgroundKeyUp(HWND hwnd, HKL layout, BYTE keyboardState[256],
220
WORD keyCode, UINT scanCode, bool extended, int pause)
221
{
222
WORD origKeyCode = keyCode;
223
keyCode = LOBYTE(keyCode);
224
keyboardState[keyCode] &= ~0x80;
225
226
LPARAM lparam = generateKeyMessageParam(scanCode, extended);
227
lparam |= 0x3 << 30;
228
if (!PostMessage(hwnd, WM_KEYUP, keyCode, lparam)) {
229
LOG(WARN) << "Key up failed: " << GetLastError();
230
}
231
232
wait(pause);
233
234
sendModifierKeyUpIfNeeded(isShiftPressNeeded(origKeyCode) && (!shiftPressed), hwnd, layout,
235
VK_SHIFT, keyboardState, pause);
236
sendModifierKeyUpIfNeeded(isControlPressNeeded(origKeyCode) && (!controlPressed), hwnd, layout,
237
VK_CONTROL, keyboardState, pause);
238
sendModifierKeyUpIfNeeded(isAltPressNeeded(origKeyCode) && (!altPressed), hwnd, layout,
239
VK_MENU, keyboardState, pause);
240
241
// If Shift was held down, we should reset the keyboard state for it
242
// as well. See the comment in backgroundKeyDown on why it is set
243
// in the first place.
244
if ((shiftPressed) || (isShiftPressNeeded(origKeyCode))) {
245
keyboardState[VK_SHIFT] &= ~0x80;
246
}
247
248
SetKeyboardState(keyboardState);
249
}
250
251
252
void backgroundKeyPress(HWND hwnd, HKL layout, BYTE keyboardState[256],
253
WORD keyCode, UINT scanCode, bool extended, int pause)
254
{
255
pause = pause / 3;
256
257
backgroundKeyDown(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause);
258
backgroundKeyUp(hwnd, layout, keyboardState, keyCode, scanCode, extended, pause);
259
}
260
261
LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
262
{
263
if ((nCode == HC_ACTION) && (wParam == PM_REMOVE)) {
264
MSG* msg = reinterpret_cast<MSG*>(lParam);
265
if (msg->message == WM_USER && msg->wParam == 1234 && msg->lParam == 5678) {
266
pressed = true;
267
}
268
}
269
270
return CallNextHookEx(hook, nCode, wParam, lParam);
271
}
272
273
bool isClearAllModifiersCode(wchar_t c)
274
{
275
return (c == 0xE000U);
276
}
277
278
bool isShiftCode(wchar_t c)
279
{
280
return (c == 0xE008U); // shift (left)
281
}
282
283
bool isControlCode(wchar_t c)
284
{
285
return (c == 0xE009U); // control (left)
286
}
287
288
bool isAltCode(wchar_t c)
289
{
290
return (c == 0xE00AU); // alt (left)
291
}
292
293
bool isModifierCharacter(wchar_t c)
294
{
295
return isClearAllModifiersCode(c) || isShiftCode(c) || isControlCode(c) ||
296
isAltCode(c);
297
}
298
299
// All the required information to post a keyboard event message.
300
struct KeySendingData {
301
HWND to_window;
302
HKL layout;
303
BYTE* keyboardState;
304
int pause_time;
305
};
306
307
void sendSingleModifierEventAndAdjustState(bool matchingModifier,
308
bool& modifierState, int modifierKeyCode, KeySendingData sendData)
309
{
310
if (!matchingModifier) {
311
return;
312
}
313
314
if (modifierState) {
315
sendModifierKeyUp(sendData.to_window, sendData.layout,
316
modifierKeyCode, sendData.keyboardState, sendData.pause_time);
317
} else {
318
sendModifierKeyDown(sendData.to_window, sendData.layout,
319
modifierKeyCode, sendData.keyboardState, sendData.pause_time);
320
}
321
modifierState = !modifierState;
322
}
323
324
void postModifierReleaseMessages(bool releaseShift, bool releaseControl, bool releaseAlt,
325
KeySendingData sendData)
326
{
327
sendModifierKeyUpIfNeeded(releaseShift, sendData.to_window, sendData.layout, VK_SHIFT, sendData.keyboardState, sendData.pause_time);
328
sendModifierKeyUpIfNeeded(releaseControl, sendData.to_window, sendData.layout, VK_CONTROL, sendData.keyboardState, sendData.pause_time);
329
sendModifierKeyUpIfNeeded(releaseAlt, sendData.to_window, sendData.layout, VK_MENU, sendData.keyboardState, sendData.pause_time);
330
}
331
332
void sendModifierKeyEvent(wchar_t c, bool& shiftKey, bool& controlKey,
333
bool& altKey, KeySendingData sendData)
334
335
{
336
if (isClearAllModifiersCode(c)) {
337
postModifierReleaseMessages(shiftKey, controlKey, altKey, sendData);
338
339
shiftKey = controlKey = altKey = false;
340
} else {
341
sendSingleModifierEventAndAdjustState(isShiftCode(c), shiftKey, VK_SHIFT, sendData);
342
sendSingleModifierEventAndAdjustState(isControlCode(c), controlKey, VK_CONTROL, sendData);
343
sendSingleModifierEventAndAdjustState(isAltCode(c), altKey, VK_MENU, sendData);
344
if (isShiftCode(c)) {
345
updateShiftKeyState(shiftKey);
346
}
347
}
348
}
349
350
static HKL attachInputToIEThread(HWND directInputTo)
351
{
352
DWORD currThreadId = GetCurrentThreadId();
353
DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL);
354
355
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)
356
&sendKeys, &moduleHandle);
357
358
hook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) &GetMessageProc,
359
moduleHandle, ieWinThreadId);
360
361
if (hook == NULL) {
362
LOGERR(WARN) << "Unable to set Windows hook. Individual keystrokes will be very slow";
363
}
364
365
// Attach to the IE thread so we can send keys to it.
366
if (ieWinThreadId != currThreadId) {
367
AttachThreadInput(currThreadId, ieWinThreadId, true);
368
}
369
370
return GetKeyboardLayout(ieWinThreadId);
371
}
372
373
static void detachInputFromIEThread(HWND directInputTo)
374
{
375
DWORD currThreadId = GetCurrentThreadId();
376
DWORD ieWinThreadId = GetWindowThreadProcessId(directInputTo, NULL);
377
378
if (hook) {
379
UnhookWindowsHookEx(hook);
380
}
381
382
if (moduleHandle) {
383
FreeLibrary(moduleHandle);
384
}
385
386
if (ieWinThreadId != currThreadId) {
387
AttachThreadInput(currThreadId, ieWinThreadId, false);
388
}
389
}
390
391
extern "C"
392
{
393
void sendKeys(WINDOW_HANDLE windowHandle, const wchar_t* value, int timePerKey)
394
{
395
if (!windowHandle) {
396
LOG(WARN) << "Window handle is invalid";
397
return;
398
}
399
400
HWND directInputTo = static_cast<HWND>(windowHandle);
401
402
HKL layout = attachInputToIEThread(directInputTo);
403
BYTE keyboardState[256];
404
::ZeroMemory(keyboardState, sizeof(keyboardState));
405
406
bool controlKey = controlPressed;
407
bool shiftKey = shiftPressed;
408
bool altKey = altPressed;
409
KeySendingData sendData;
410
sendData.to_window = directInputTo;
411
sendData.layout = layout;
412
sendData.keyboardState = keyboardState;
413
sendData.pause_time = timePerKey;
414
415
for (const wchar_t *p = value; *p; ++p) {
416
const wchar_t c = *p;
417
418
bool extended = false;
419
420
UINT scanCode = 0;
421
WORD keyCode = 0;
422
423
if (isModifierCharacter(c)) {
424
sendModifierKeyEvent(c, shiftKey, controlKey, altKey, sendData);
425
shiftPressed = shiftKey;
426
controlPressed = controlKey;
427
altPressed = altKey;
428
continue;
429
} else if (c == 0xE001U) { // ^break
430
keyCode = VK_CANCEL;
431
scanCode = keyCode;
432
extended = true;
433
} else if (c == 0xE002U) { // help
434
keyCode = VK_HELP;
435
scanCode = keyCode;
436
} else if (c == 0xE003U) { // back space
437
keyCode = VK_BACK;
438
scanCode = keyCode;
439
} else if (c == 0xE004U) { // tab
440
keyCode = VK_TAB;
441
scanCode = keyCode;
442
} else if (c == 0xE005U) { // clear
443
keyCode = VK_CLEAR;
444
scanCode = keyCode;
445
} else if (c == 0xE006U) { // return
446
keyCode = VK_RETURN;
447
scanCode = keyCode;
448
} else if (c == 0xE007U) { // enter
449
keyCode = VK_RETURN;
450
scanCode = keyCode;
451
} else if (c == 0xE00BU) { // pause
452
keyCode = VK_PAUSE;
453
scanCode = keyCode;
454
extended = true;
455
} else if (c == 0xE00CU) { // escape
456
keyCode = VK_ESCAPE;
457
scanCode = keyCode;
458
} else if (c == 0xE00DU) { // space
459
keyCode = VK_SPACE;
460
scanCode = keyCode;
461
} else if (c == 0xE00EU) { // page up
462
keyCode = VK_PRIOR;
463
scanCode = keyCode;
464
extended = true;
465
} else if (c == 0xE00FU) { // page down
466
keyCode = VK_NEXT;
467
scanCode = keyCode;
468
extended = true;
469
} else if (c == 0xE010U) { // end
470
keyCode = VK_END;
471
scanCode = keyCode;
472
extended = true;
473
} else if (c == 0xE011U) { // home
474
keyCode = VK_HOME;
475
scanCode = keyCode;
476
extended = true;
477
} else if (c == 0xE012U) { // left arrow
478
keyCode = VK_LEFT;
479
scanCode = keyCode;
480
extended = true;
481
} else if (c == 0xE013U) { // up arrow
482
keyCode = VK_UP;
483
scanCode = keyCode;
484
extended = true;
485
} else if (c == 0xE014U) { // right arrow
486
keyCode = VK_RIGHT;
487
scanCode = keyCode;
488
extended = true;
489
} else if (c == 0xE015U) { // down arrow
490
keyCode = VK_DOWN;
491
scanCode = keyCode;
492
extended = true;
493
} else if (c == 0xE016U) { // insert
494
keyCode = VK_INSERT;
495
scanCode = keyCode;
496
extended = true;
497
} else if (c == 0xE017U) { // delete
498
keyCode = VK_DELETE;
499
scanCode = keyCode;
500
extended = true;
501
} else if (c == 0xE018U) { // semicolon
502
keyCode = VkKeyScanExW(L';', layout);
503
scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);
504
} else if (c == 0xE019U) { // equals
505
keyCode = VkKeyScanExW(L'=', layout);
506
scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);
507
} else if (c == 0xE01AU) { // numpad0
508
keyCode = VK_NUMPAD0;
509
scanCode = keyCode;
510
extended = true;
511
} else if (c == 0xE01BU) { // numpad1
512
keyCode = VK_NUMPAD1;
513
scanCode = keyCode;
514
extended = true;
515
} else if (c == 0xE01CU) { // numpad2
516
keyCode = VK_NUMPAD2;
517
scanCode = keyCode;
518
extended = true;
519
} else if (c == 0xE01DU) { // numpad3
520
keyCode = VK_NUMPAD3;
521
scanCode = keyCode;
522
extended = true;
523
} else if (c == 0xE01EU) { // numpad4
524
keyCode = VK_NUMPAD4;
525
scanCode = keyCode;
526
extended = true;
527
} else if (c == 0xE01FU) { // numpad5
528
keyCode = VK_NUMPAD5;
529
scanCode = keyCode;
530
extended = true;
531
} else if (c == 0xE020U) { // numpad6
532
keyCode = VK_NUMPAD6;
533
scanCode = keyCode;
534
extended = true;
535
} else if (c == 0xE021U) { // numpad7
536
keyCode = VK_NUMPAD7;
537
scanCode = keyCode;
538
extended = true;
539
} else if (c == 0xE022U) { // numpad8
540
keyCode = VK_NUMPAD8;
541
scanCode = keyCode;
542
extended = true;
543
} else if (c == 0xE023U) { // numpad9
544
keyCode = VK_NUMPAD9;
545
scanCode = keyCode;
546
extended = true;
547
} else if (c == 0xE024U) { // multiply
548
keyCode = VK_MULTIPLY;
549
scanCode = keyCode;
550
extended = true;
551
} else if (c == 0xE025U) { // add
552
keyCode = VK_ADD;
553
scanCode = keyCode;
554
extended = true;
555
} else if (c == 0xE026U) { // separator
556
keyCode = VkKeyScanExW(L',', layout);
557
scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);
558
} else if (c == 0xE027U) { // subtract
559
keyCode = VK_SUBTRACT;
560
scanCode = keyCode;
561
extended = true;
562
} else if (c == 0xE028U) { // decimal
563
keyCode = VK_DECIMAL;
564
scanCode = keyCode;
565
extended = true;
566
} else if (c == 0xE029U) { // divide
567
keyCode = VK_DIVIDE;
568
scanCode = keyCode;
569
extended = true;
570
} else if (c == 0xE031U) { // F1
571
keyCode = VK_F1;
572
scanCode = keyCode;
573
} else if (c == 0xE032U) { // F2
574
keyCode = VK_F2;
575
scanCode = keyCode;
576
} else if (c == 0xE033U) { // F3
577
keyCode = VK_F3;
578
scanCode = keyCode;
579
} else if (c == 0xE034U) { // F4
580
keyCode = VK_F4;
581
scanCode = keyCode;
582
} else if (c == 0xE035U) { // F5
583
keyCode = VK_F5;
584
scanCode = keyCode;
585
} else if (c == 0xE036U) { // F6
586
keyCode = VK_F6;
587
scanCode = keyCode;
588
} else if (c == 0xE037U) { // F7
589
keyCode = VK_F7;
590
scanCode = keyCode;
591
} else if (c == 0xE038U) { // F8
592
keyCode = VK_F8;
593
scanCode = keyCode;
594
} else if (c == 0xE039U) { // F9
595
keyCode = VK_F9;
596
scanCode = keyCode;
597
} else if (c == 0xE03AU) { // F10
598
keyCode = VK_F10;
599
scanCode = keyCode;
600
} else if (c == 0xE03BU) { // F11
601
keyCode = VK_F11;
602
scanCode = keyCode;
603
} else if (c == 0xE03CU) { // F12
604
keyCode = VK_F12;
605
scanCode = keyCode;
606
} else if (c == L'\n') { // line feed
607
keyCode = VK_RETURN;
608
scanCode = keyCode;
609
} else if (c == L'\r') { // carriage return
610
continue; // skip it
611
} else {
612
keyCode = VkKeyScanExW(c, layout);
613
scanCode = MapVirtualKeyExW(LOBYTE(keyCode), 0, layout);
614
if (!scanCode || (keyCode == 0xFFFFU)) {
615
LOG(WARN) << "No translation for key. Assuming unicode input: " << c;
616
backgroundUnicodeKeyPress(directInputTo, c, timePerKey);
617
continue; // bogus
618
}
619
}
620
621
// Note: There is *no* need to OR the keyCode with 0x0100 if
622
// shiftPressed is true. ORing the keyCode with these values is to
623
// indicate the backgroundKeyPress procedure that a modifier key
624
// press and release should be produced for this keyCode. However,
625
// when shiftPressed is true the events for the modifier were
626
// already generated by the sendKeyPress function.
627
if (shiftKey)
628
keyCode |= static_cast<WORD>(0x0100);
629
if (controlKey)
630
keyCode |= static_cast<WORD>(0x0200);
631
if (altKey)
632
keyCode |= static_cast<WORD>(0x0400);
633
634
int pause = timePerKey;
635
636
// Pause for control, alt, and shift generation: if we create these
637
// chars too fast, the target element may generated spurious chars.
638
639
if (keyCode & static_cast<WORD>(0x0100)) {
640
pause = (35 * 3); // uppercase char
641
} else if (shiftKey || controlKey || altKey) {
642
pause = (35 * 3); // shift|alt|ctrl
643
}
644
645
backgroundKeyPress(directInputTo, layout, keyboardState, keyCode, scanCode,
646
extended, pause);
647
}
648
649
detachInputFromIEThread(directInputTo);
650
}
651
652
void releaseModifierKeys(WINDOW_HANDLE windowHandle, int timePerKey)
653
{
654
if (!windowHandle) {
655
LOG(WARN) << "Window handle is invalid";
656
return;
657
}
658
659
HWND directInputTo = static_cast<HWND>(windowHandle);
660
661
HKL layout = attachInputToIEThread(directInputTo);
662
BYTE keyboardState[256];
663
::ZeroMemory(keyboardState, sizeof(keyboardState));
664
665
KeySendingData sendData;
666
667
sendData.to_window = directInputTo;
668
sendData.layout = layout;
669
sendData.keyboardState = keyboardState;
670
sendData.pause_time = 35;
671
672
if ((shiftPressed) || (controlPressed) || (altPressed)) {
673
postModifierReleaseMessages(shiftPressed, controlPressed, altPressed, sendData);
674
shiftPressed = false;
675
controlPressed = false;
676
altPressed = false;
677
}
678
679
detachInputFromIEThread(directInputTo);
680
}
681
682
bool isSameThreadAs(HWND other)
683
{
684
DWORD currThreadId = GetCurrentThreadId();
685
DWORD winThreadId = GetWindowThreadProcessId(other, NULL);
686
687
return winThreadId == currThreadId;
688
}
689
690
LRESULT clickAt(WINDOW_HANDLE handle, long x, long y, long button)
691
{
692
if (!handle) {
693
LOG(WARN) << "Window handle is invalid";
694
return ENULLPOINTER;
695
}
696
697
HWND directInputTo = (HWND) handle;
698
699
LRESULT result = mouseDownAt(handle, x, y, button);
700
if (result != 0) {
701
LOG(WARN) << "Mouse down did not succeed whilst clicking";
702
return result;
703
}
704
705
return mouseUpAt(handle, x, y, button);
706
}
707
708
static LRESULT mouseDoubleClickDown(WINDOW_HANDLE directInputTo, long x, long y)
709
{
710
if (!directInputTo) {
711
LOG(WARN) << "Window handle is invalid";
712
return ENULLPOINTER;
713
}
714
715
if (!isSameThreadAs((HWND) directInputTo)) {
716
BOOL toReturn = PostMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y));
717
718
// Wait until we know that the previous message has been processed
719
SendMessage((HWND) directInputTo, WM_USER, 0, 0);
720
return toReturn ? 0 : 1; // Because 0 means success.
721
} else {
722
return SendMessage((HWND) directInputTo, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELONG(x, y));
723
}
724
}
725
726
LRESULT doubleClickAt(WINDOW_HANDLE handle, long x, long y)
727
{
728
// A double click consists of the sequence
729
// 1: mouseDown
730
// 2: mouseUp
731
// 3: doubleClick
732
// 4: mouseUp
733
// Which is the equivalent to two clicks with the second mouseDown event
734
// is replaced by a doubleClick event.
735
736
if (!handle) {
737
LOG(WARN) << "Window handle is invalid";
738
return ENULLPOINTER;
739
}
740
741
LRESULT result = clickAt(handle, x, y, 0);
742
if (result != 0) {
743
LOG(WARN) << "Mouse down did not succeed whilst clicking";
744
return result;
745
}
746
747
result = mouseDoubleClickDown(handle, x, y);
748
if (result != 0) {
749
LOG(WARN) << "Mouse down did not succeed whilst double clicking";
750
return result;
751
}
752
753
return mouseUpAt(handle, x, y, 0);
754
}
755
756
static void fillEventData(long button, bool buttonDown, UINT *message, WPARAM *wparam)
757
{
758
if(WD_CLIENT_RIGHT_MOUSE_BUTTON == button) {
759
if(buttonDown) {
760
*message = WM_RBUTTONDOWN;
761
} else {
762
*message = WM_RBUTTONUP;
763
}
764
*wparam = MK_RBUTTON;
765
} else { // middle button support is declared in json wire protocol but it is not supported
766
leftMouseButtonPressed = buttonDown;
767
if(buttonDown) {
768
*message = WM_LBUTTONDOWN;
769
} else {
770
*message = WM_LBUTTONUP;
771
}
772
*wparam = MK_LBUTTON;
773
}
774
if (shiftPressed) {
775
*wparam |= MK_SHIFT;
776
}
777
}
778
779
LRESULT mouseDownAt(WINDOW_HANDLE directInputTo, long x, long y, long button)
780
{
781
if (!directInputTo) {
782
LOG(WARN) << "Window handle is invalid";
783
return ENULLPOINTER;
784
}
785
786
UINT message;
787
WPARAM wparam;
788
LRESULT returnValue;
789
790
fillEventData(button, true, &message, &wparam);
791
pausePersistentEventsFiring();
792
793
if (!isSameThreadAs((HWND) directInputTo)) {
794
BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));
795
796
// Wait until we know that the previous message has been processed
797
SendMessage((HWND) directInputTo, WM_USER, 0, 0);
798
returnValue = toReturn ? 0 : 1; // Because 0 means success.
799
} else {
800
returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));
801
}
802
803
// Assume it's the left mouse button.
804
if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) {
805
updateLeftMouseButtonState(true);
806
}
807
resumePersistentEventsFiring();
808
809
return returnValue;
810
}
811
812
LRESULT mouseUpAt(WINDOW_HANDLE directInputTo, long x, long y, long button)
813
{
814
if (!directInputTo) {
815
LOG(WARN) << "Window handle is invalid";
816
return ENULLPOINTER;
817
}
818
819
UINT message;
820
WPARAM wparam;
821
LRESULT returnValue;
822
823
fillEventData(button, false, &message, &wparam);
824
pausePersistentEventsFiring();
825
826
SendMessage((HWND) directInputTo, WM_MOUSEMOVE, (shiftPressed ? MK_SHIFT : 0), MAKELPARAM(x, y));
827
if (!isSameThreadAs((HWND) directInputTo)) {
828
BOOL toReturn = PostMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));
829
830
// Wait until we know that the previous message has been processed
831
SendMessage((HWND) directInputTo, WM_USER, 0, 0);
832
returnValue = toReturn ? 0 : 1; // Because 0 means success.
833
} else {
834
returnValue = SendMessage((HWND) directInputTo, message, wparam, MAKELONG(x, y));
835
}
836
// Assume it's the left mouse button.
837
if(WD_CLIENT_RIGHT_MOUSE_BUTTON != button) {
838
updateLeftMouseButtonState(false);
839
}
840
resumePersistentEventsFiring();
841
842
return returnValue;
843
}
844
845
LRESULT mouseMoveTo(WINDOW_HANDLE handle, long duration, long fromX, long fromY, long toX, long toY)
846
{
847
if (!handle) {
848
LOG(WARN) << "Window handle is invalid";
849
return ENULLPOINTER;
850
}
851
852
pausePersistentEventsFiring();
853
854
HWND directInputTo = (HWND) handle;
855
long pointsDistance = distanceBetweenPoints(fromX, fromY, toX, toY);
856
const int stepSizeInPixels = 5;
857
int steps = pointsDistance / stepSizeInPixels;
858
859
long sleep = duration / max(steps, 1);
860
861
WPARAM buttonValue = (leftMouseButtonPressed ? MK_LBUTTON : 0);
862
if (shiftPressed) {
863
buttonValue |= MK_SHIFT;
864
}
865
866
for (int i = 0; i < steps + 1; i++) {
867
//To avoid integer division rounding and cumulative floating point errors,
868
//calculate from scratch each time
869
int currentX = (int)(fromX + ((toX - fromX) * ((double)i) / steps));
870
int currentY = (int)(fromY + ((toY - fromY) * ((double)i) / steps));
871
SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(currentX, currentY));
872
wait(sleep);
873
}
874
875
SendMessage(directInputTo, WM_MOUSEMOVE, buttonValue, MAKELPARAM(toX, toY));
876
resumePersistentEventsFiring(directInputTo, toX, toY, buttonValue);
877
878
return 0;
879
}
880
881
bool pending_input_events()
882
{
883
return false;
884
}
885
886
}
887
888