Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/webdriver-interactions/interactions_linux_mouse.cpp
2867 views
1
/*
2
Copyright 2007-2010 WebDriver committers
3
Copyright 2007-2010 Google Inc.
4
5
Licensed under the Apache License, Version 2.0 (the "License");
6
you may not use this file except in compliance with the License.
7
You may obtain a copy of the License at
8
9
http://www.apache.org/licenses/LICENSE-2.0
10
11
Unless required by applicable law or agreed to in writing, software
12
distributed under the License is distributed on an "AS IS" BASIS,
13
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
See the License for the specific language governing permissions and
15
limitations under the License.
16
*/
17
18
#include <ctime>
19
#include <string>
20
#include <iostream>
21
#include <fstream>
22
23
#include "interactions.h"
24
25
#include "logging.h"
26
27
#include <gdk/gdk.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <X11/Xlib.h>
30
#include <time.h>
31
#include <stdlib.h>
32
#include <assert.h>
33
#include <list>
34
#include <algorithm>
35
#include <functional>
36
37
#include "translate_keycode_linux.h"
38
#include "interactions_linux.h"
39
#include "interactions_common.h"
40
41
using namespace std;
42
43
enum MouseEventType { bMousePress, bMouseRelease, bMouse2ButtonPress };
44
// This class handles generation of mouse press / release events.
45
class MouseEventsHandler
46
{
47
public:
48
MouseEventsHandler(GdkDrawable* win_handle);
49
virtual ~MouseEventsHandler();
50
51
// Creates a series of mouse events (i.e mouse up/down)
52
list<GdkEvent*> CreateEventsForMouseMove(long x, long y);
53
list<GdkEvent*> CreateEventsForMouseClick(long x, long y, long button);
54
list<GdkEvent*> CreateEventsForMouseDoubleClick(long x, long y);
55
list<GdkEvent*> CreateEventsForMouseDown(long x, long y, long button);
56
list<GdkEvent*> CreateEventsForMouseUp(long x, long y, long button);
57
// Returns the time of the latest event.
58
guint32 get_last_event_time();
59
60
private:
61
// Create mouse move event
62
GdkEvent* CreateMouseMotionEvent(long x, long y);
63
64
// Create mouse button event (up/down)
65
GdkEvent* CreateMouseButtonEvent(MouseEventType ev_type, long x, long y, long button);
66
67
// The window handle to be used.
68
GdkDrawable* win_handle_;
69
// Time of the most recent event created.
70
guint32 last_event_time_;
71
};
72
73
MouseEventsHandler::MouseEventsHandler(GdkDrawable* win_handle) :
74
win_handle_(win_handle), last_event_time_(TimeSinceBootMsec())
75
{
76
}
77
78
guint32 MouseEventsHandler::get_last_event_time()
79
{
80
return last_event_time_;
81
}
82
83
GdkDevice* getSomeDevice()
84
{
85
GList *pList = gdk_devices_list();
86
GList *currNode = pList;
87
GdkDevice *currDevice = NULL;
88
while ((currNode != NULL) && (currDevice == NULL)) {
89
currDevice = (GdkDevice*) currNode->data;
90
currNode = currNode->next;
91
}
92
93
return (GdkDevice*) g_object_ref(currDevice);
94
}
95
96
GdkEvent* MouseEventsHandler::CreateMouseMotionEvent(long x, long y)
97
{
98
GdkEvent* p_ev = gdk_event_new(GDK_MOTION_NOTIFY);
99
p_ev->motion.window = GDK_WINDOW(g_object_ref(win_handle_));
100
p_ev->motion.send_event = 0; // NOT a synthesized event.
101
p_ev->motion.time = TimeSinceBootMsec();
102
p_ev->motion.x = x;
103
p_ev->motion.y = y;
104
p_ev->motion.axes = NULL;
105
p_ev->motion.is_hint = 0;
106
// It is necessary to provide a device. any device.
107
p_ev->motion.device = getSomeDevice();
108
p_ev->motion.state = gModifiersState;
109
110
// Also update the latest event time
111
last_event_time_ = p_ev->motion.time;
112
return p_ev;
113
}
114
115
116
GdkEvent* MouseEventsHandler::CreateMouseButtonEvent(MouseEventType ev_type, long x, long y, long button)
117
{
118
GdkEventType gdk_ev = GDK_BUTTON_PRESS;
119
if (ev_type == bMouseRelease) {
120
gdk_ev = GDK_BUTTON_RELEASE;
121
} else if (ev_type == bMouse2ButtonPress) {
122
gdk_ev = GDK_2BUTTON_PRESS;
123
}
124
GdkEvent* p_ev = gdk_event_new(gdk_ev);
125
p_ev->button.window = GDK_WINDOW(g_object_ref(win_handle_));
126
p_ev->button.send_event = 0; // NOT a synthesized event.
127
p_ev->button.time = TimeSinceBootMsec();
128
p_ev->button.x = x;
129
p_ev->button.y = y;
130
p_ev->button.button = button;
131
p_ev->button.device = getSomeDevice();
132
p_ev->button.state = gModifiersState;
133
134
// Also update the latest event time
135
last_event_time_ = p_ev->motion.time;
136
return p_ev;
137
}
138
139
140
list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseDown(long x, long y, long button)
141
{
142
GdkEvent* down = CreateMouseButtonEvent(bMousePress, x, y, button);
143
list<GdkEvent*> ret_list;
144
ret_list.push_back(down);
145
return ret_list;
146
}
147
148
149
list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseUp(long x, long y, long button)
150
{
151
GdkEvent* up = CreateMouseButtonEvent(bMouseRelease, x, y, button);
152
list<GdkEvent*> ret_list;
153
ret_list.push_back(up);
154
return ret_list;
155
}
156
157
list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseDoubleClick(long x, long y)
158
{
159
// double click is only possible with the left mouse button
160
const int leftMouseButton = 1;
161
list<GdkEvent*> ret_list;
162
ret_list.push_back(CreateMouseButtonEvent(bMousePress, x, y, leftMouseButton));
163
ret_list.push_back(CreateMouseButtonEvent(bMouseRelease, x, y, leftMouseButton));
164
ret_list.push_back(CreateMouseButtonEvent(bMousePress, x, y, leftMouseButton));
165
ret_list.push_back(CreateMouseButtonEvent(bMouse2ButtonPress, x, y, leftMouseButton));
166
ret_list.push_back(CreateMouseButtonEvent(bMouseRelease, x, y, leftMouseButton));
167
return ret_list;
168
}
169
170
list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseClick(long x, long y, long button)
171
{
172
GdkEvent* down = CreateMouseButtonEvent(bMousePress, x, y, button);
173
GdkEvent* up = CreateMouseButtonEvent(bMouseRelease, x, y, button);
174
175
list<GdkEvent*> ret_list;
176
ret_list.push_back(down);
177
ret_list.push_back(up);
178
return ret_list;
179
}
180
181
list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseMove(long x, long y)
182
{
183
GdkEvent* move = CreateMouseMotionEvent(x, y);
184
list<GdkEvent*> ret_list;
185
ret_list.push_back(move);
186
return ret_list;
187
}
188
189
MouseEventsHandler::~MouseEventsHandler()
190
{
191
}
192
193
static void submit_and_free_event(GdkEvent* p_mouse_event, int sleep_time_ms)
194
{
195
gdk_event_put(p_mouse_event);
196
GdkDevice* usedDevice = NULL;
197
if (p_mouse_event->type == GDK_MOTION_NOTIFY) {
198
usedDevice = p_mouse_event->motion.device;
199
} else {
200
usedDevice = p_mouse_event->button.device;
201
}
202
g_object_unref(usedDevice);
203
gdk_event_free(p_mouse_event);
204
sleep_for_ms(sleep_time_ms);
205
}
206
207
static void print_mouse_event(GdkEvent* p_ev)
208
{
209
if (!((p_ev->type == GDK_BUTTON_PRESS) || (p_ev->type == GDK_BUTTON_RELEASE)
210
|| (p_ev->type == GDK_MOTION_NOTIFY) || p_ev->type == GDK_2BUTTON_PRESS)) {
211
LOG(DEBUG) << "Not a mouse event.";
212
return;
213
}
214
215
std::string ev_type;
216
if (p_ev->type == GDK_BUTTON_PRESS) {
217
ev_type = "press";
218
};
219
220
if (p_ev->type == GDK_BUTTON_RELEASE) {
221
ev_type = "release";
222
};
223
224
if (p_ev->type == GDK_MOTION_NOTIFY) {
225
ev_type = "motion";
226
};
227
228
if (p_ev->type == GDK_2BUTTON_PRESS) {
229
ev_type = "2press";
230
};
231
LOG(DEBUG) << "Type: " << ev_type << " time: " <<
232
p_ev->key.time;
233
}
234
235
static void submit_and_free_events_list(list<GdkEvent*>& events_list,
236
int sleep_time_ms)
237
{
238
for_each(events_list.begin(), events_list.end(), print_mouse_event);
239
240
for_each(events_list.begin(), events_list.end(),
241
bind2nd(ptr_fun(submit_and_free_event), sleep_time_ms));
242
243
events_list.clear();
244
}
245
246
extern "C"
247
{
248
WD_RESULT clickAt(WINDOW_HANDLE windowHandle, long x, long y, long button)
249
{
250
init_logging();
251
252
LOG(DEBUG) << "---------- starting clickAt: " << windowHandle << "---------";
253
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
254
255
if (button == 2) {
256
// the right mouse button has the value 3 in GDK
257
button = 3;
258
} else {
259
// the left mouse button is default
260
button = 1;
261
}
262
263
MouseEventsHandler mousep_handler(hwnd);
264
265
list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseClick(x, y, button);
266
const int timePerEvent = 10 /* ms */;
267
submit_and_free_events_list(events_for_mouse, timePerEvent);
268
269
270
if (gLatestEventTime < mousep_handler.get_last_event_time()) {
271
gLatestEventTime = mousep_handler.get_last_event_time();
272
}
273
274
LOG(DEBUG) << "---------- Ending clickAt ----------";
275
return 0;
276
}
277
278
WD_RESULT doubleClickAt(WINDOW_HANDLE windowHandle, long x, long y)
279
{
280
init_logging();
281
282
LOG(DEBUG) << "---------- starting doubleClickAt: " << windowHandle << "---------";
283
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
284
285
MouseEventsHandler mousep_handler(hwnd);
286
287
const int timePerEvent = 10 /* ms */;
288
list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseDoubleClick(x, y);
289
submit_and_free_events_list(events_for_mouse, timePerEvent);
290
291
if (gLatestEventTime < mousep_handler.get_last_event_time()) {
292
gLatestEventTime = mousep_handler.get_last_event_time();
293
}
294
295
LOG(DEBUG) << "---------- Ending doubleClickAt ----------";
296
return 0;
297
}
298
299
/**
300
* mouseMoveTo
301
*/
302
WD_RESULT mouseMoveTo(WINDOW_HANDLE windowHandle, long duration, long fromX, long fromY, long toX, long toY)
303
{
304
init_logging();
305
const int timePerEvent = 10 /* ms */;
306
307
LOG(DEBUG) << "---------- starting mouseMoveTo: " << windowHandle << "---------";
308
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
309
310
MouseEventsHandler mousep_handler(hwnd);
311
312
long pointsDistance = distanceBetweenPoints(fromX, fromY, toX, toY);
313
const int stepSizeInPixels = 5;
314
int steps = pointsDistance / stepSizeInPixels;
315
316
// If the distance between the points is less than stepSizeInPixels,
317
// make sure enough move events aregenerated.
318
// * If the start and finish points are the same, one step is needed.
319
// Otherwise, generate at least 2 move events: one one the start
320
// point and the other on the end point.
321
if ((fromX == toX) && (fromY == toY)) {
322
steps = 1;
323
} else {
324
steps = max(steps, 2);
325
}
326
327
assert(steps > 0);
328
LOG(DEBUG) << "From: (" << fromX << ", " << fromY << ") to: (" << toX << ", " << toY << ")";
329
LOG(DEBUG) << "Distance: " << pointsDistance << " steps: " << steps;
330
331
for (int i = 0; i < steps; ++i) {
332
// To avoid integer division rounding and cumulative floating point errors,
333
// calculate from scratch each time. We adjust the divider to steps - 1
334
// to get a move event generated on the exact starting point as well
335
// as the end point.
336
int div_by = max(steps - 1, 1);
337
int currentX = fromX + ((toX - fromX) * ((double)i) / div_by);
338
int currentY = fromY + ((toY - fromY) * ((double)i) / div_by);
339
LOG(DEBUG) << "Moving to: (" << currentX << ", " << currentY << ")";
340
list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseMove(currentX, currentY);
341
submit_and_free_events_list(events_for_mouse, timePerEvent);
342
}
343
344
345
346
if (gLatestEventTime < mousep_handler.get_last_event_time()) {
347
gLatestEventTime = mousep_handler.get_last_event_time();
348
}
349
350
LOG(DEBUG) << "---------- Ending mouseMoveTo ----------";
351
return 0;
352
}
353
354
/**
355
* mouseDownAt
356
*/
357
WD_RESULT mouseDownAt(WINDOW_HANDLE windowHandle, long x, long y, long button)
358
{
359
init_logging();
360
361
const int timePerEvent = 10 /* ms */;
362
363
LOG(DEBUG) << "---------- starting mouseDownAt: " << windowHandle << "---------";
364
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
365
366
MouseEventsHandler mousep_handler(hwnd);
367
368
struct timespec sleep_time;
369
sleep_time.tv_sec = timePerEvent / 1000;
370
sleep_time.tv_nsec = (timePerEvent % 1000) * 1000000;
371
LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<
372
sleep_time.tv_nsec << " nanoseconds.";
373
374
list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseDown(x, y, button);
375
submit_and_free_events_list(events_for_mouse, timePerEvent);
376
377
378
if (gLatestEventTime < mousep_handler.get_last_event_time()) {
379
gLatestEventTime = mousep_handler.get_last_event_time();
380
}
381
382
LOG(DEBUG) << "---------- Ending mouseDownAt ----------";
383
return 0;
384
}
385
386
/**
387
* mouseUpAt
388
*/
389
WD_RESULT mouseUpAt(WINDOW_HANDLE windowHandle, long x, long y, long button)
390
{
391
init_logging();
392
393
const int timePerEvent = 10 /* ms */;
394
395
LOG(DEBUG) << "---------- starting mouseUpAt: " << windowHandle << "---------";
396
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
397
398
MouseEventsHandler mousep_handler(hwnd);
399
400
struct timespec sleep_time;
401
sleep_time.tv_sec = timePerEvent / 1000;
402
sleep_time.tv_nsec = (timePerEvent % 1000) * 1000000;
403
LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<
404
sleep_time.tv_nsec << " nanoseconds.";
405
406
list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseUp(x, y, button);
407
submit_and_free_events_list(events_for_mouse, timePerEvent);
408
409
410
if (gLatestEventTime < mousep_handler.get_last_event_time()) {
411
gLatestEventTime = mousep_handler.get_last_event_time();
412
}
413
414
LOG(DEBUG) << "---------- Ending mouseUpAt ----------";
415
return 0;
416
}
417
418
bool pending_mouse_events()
419
{
420
init_logging();
421
LOG(DEBUG) << "Waiting for all events to be processed";
422
GdkEvent* lastEvent = gdk_event_peek();
423
LOG(DEBUG) << "Got event: " <<
424
(lastEvent != NULL ? lastEvent->type : 0);
425
426
bool ret_val = false;
427
if (lastEvent != NULL && is_gdk_mouse_event(lastEvent) &&
428
event_earlier_than(lastEvent, gLatestEventTime)) {
429
ret_val = true;
430
}
431
432
if (lastEvent != NULL) {
433
gdk_event_free(lastEvent);
434
}
435
LOG(DEBUG) << "Returning: " << ret_val;
436
437
return ret_val;
438
}
439
440
} // extern C
441
442