Path: blob/trunk/cpp/webdriver-interactions/interactions_linux_mouse.cpp
2867 views
/*1Copyright 2007-2010 WebDriver committers2Copyright 2007-2010 Google Inc.34Licensed under the Apache License, Version 2.0 (the "License");5you may not use this file except in compliance with the License.6You may obtain a copy of the License at78http://www.apache.org/licenses/LICENSE-2.0910Unless required by applicable law or agreed to in writing, software11distributed under the License is distributed on an "AS IS" BASIS,12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13See the License for the specific language governing permissions and14limitations under the License.15*/1617#include <ctime>18#include <string>19#include <iostream>20#include <fstream>2122#include "interactions.h"2324#include "logging.h"2526#include <gdk/gdk.h>27#include <gdk/gdkkeysyms.h>28#include <X11/Xlib.h>29#include <time.h>30#include <stdlib.h>31#include <assert.h>32#include <list>33#include <algorithm>34#include <functional>3536#include "translate_keycode_linux.h"37#include "interactions_linux.h"38#include "interactions_common.h"3940using namespace std;4142enum MouseEventType { bMousePress, bMouseRelease, bMouse2ButtonPress };43// This class handles generation of mouse press / release events.44class MouseEventsHandler45{46public:47MouseEventsHandler(GdkDrawable* win_handle);48virtual ~MouseEventsHandler();4950// Creates a series of mouse events (i.e mouse up/down)51list<GdkEvent*> CreateEventsForMouseMove(long x, long y);52list<GdkEvent*> CreateEventsForMouseClick(long x, long y, long button);53list<GdkEvent*> CreateEventsForMouseDoubleClick(long x, long y);54list<GdkEvent*> CreateEventsForMouseDown(long x, long y, long button);55list<GdkEvent*> CreateEventsForMouseUp(long x, long y, long button);56// Returns the time of the latest event.57guint32 get_last_event_time();5859private:60// Create mouse move event61GdkEvent* CreateMouseMotionEvent(long x, long y);6263// Create mouse button event (up/down)64GdkEvent* CreateMouseButtonEvent(MouseEventType ev_type, long x, long y, long button);6566// The window handle to be used.67GdkDrawable* win_handle_;68// Time of the most recent event created.69guint32 last_event_time_;70};7172MouseEventsHandler::MouseEventsHandler(GdkDrawable* win_handle) :73win_handle_(win_handle), last_event_time_(TimeSinceBootMsec())74{75}7677guint32 MouseEventsHandler::get_last_event_time()78{79return last_event_time_;80}8182GdkDevice* getSomeDevice()83{84GList *pList = gdk_devices_list();85GList *currNode = pList;86GdkDevice *currDevice = NULL;87while ((currNode != NULL) && (currDevice == NULL)) {88currDevice = (GdkDevice*) currNode->data;89currNode = currNode->next;90}9192return (GdkDevice*) g_object_ref(currDevice);93}9495GdkEvent* MouseEventsHandler::CreateMouseMotionEvent(long x, long y)96{97GdkEvent* p_ev = gdk_event_new(GDK_MOTION_NOTIFY);98p_ev->motion.window = GDK_WINDOW(g_object_ref(win_handle_));99p_ev->motion.send_event = 0; // NOT a synthesized event.100p_ev->motion.time = TimeSinceBootMsec();101p_ev->motion.x = x;102p_ev->motion.y = y;103p_ev->motion.axes = NULL;104p_ev->motion.is_hint = 0;105// It is necessary to provide a device. any device.106p_ev->motion.device = getSomeDevice();107p_ev->motion.state = gModifiersState;108109// Also update the latest event time110last_event_time_ = p_ev->motion.time;111return p_ev;112}113114115GdkEvent* MouseEventsHandler::CreateMouseButtonEvent(MouseEventType ev_type, long x, long y, long button)116{117GdkEventType gdk_ev = GDK_BUTTON_PRESS;118if (ev_type == bMouseRelease) {119gdk_ev = GDK_BUTTON_RELEASE;120} else if (ev_type == bMouse2ButtonPress) {121gdk_ev = GDK_2BUTTON_PRESS;122}123GdkEvent* p_ev = gdk_event_new(gdk_ev);124p_ev->button.window = GDK_WINDOW(g_object_ref(win_handle_));125p_ev->button.send_event = 0; // NOT a synthesized event.126p_ev->button.time = TimeSinceBootMsec();127p_ev->button.x = x;128p_ev->button.y = y;129p_ev->button.button = button;130p_ev->button.device = getSomeDevice();131p_ev->button.state = gModifiersState;132133// Also update the latest event time134last_event_time_ = p_ev->motion.time;135return p_ev;136}137138139list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseDown(long x, long y, long button)140{141GdkEvent* down = CreateMouseButtonEvent(bMousePress, x, y, button);142list<GdkEvent*> ret_list;143ret_list.push_back(down);144return ret_list;145}146147148list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseUp(long x, long y, long button)149{150GdkEvent* up = CreateMouseButtonEvent(bMouseRelease, x, y, button);151list<GdkEvent*> ret_list;152ret_list.push_back(up);153return ret_list;154}155156list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseDoubleClick(long x, long y)157{158// double click is only possible with the left mouse button159const int leftMouseButton = 1;160list<GdkEvent*> ret_list;161ret_list.push_back(CreateMouseButtonEvent(bMousePress, x, y, leftMouseButton));162ret_list.push_back(CreateMouseButtonEvent(bMouseRelease, x, y, leftMouseButton));163ret_list.push_back(CreateMouseButtonEvent(bMousePress, x, y, leftMouseButton));164ret_list.push_back(CreateMouseButtonEvent(bMouse2ButtonPress, x, y, leftMouseButton));165ret_list.push_back(CreateMouseButtonEvent(bMouseRelease, x, y, leftMouseButton));166return ret_list;167}168169list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseClick(long x, long y, long button)170{171GdkEvent* down = CreateMouseButtonEvent(bMousePress, x, y, button);172GdkEvent* up = CreateMouseButtonEvent(bMouseRelease, x, y, button);173174list<GdkEvent*> ret_list;175ret_list.push_back(down);176ret_list.push_back(up);177return ret_list;178}179180list<GdkEvent*> MouseEventsHandler::CreateEventsForMouseMove(long x, long y)181{182GdkEvent* move = CreateMouseMotionEvent(x, y);183list<GdkEvent*> ret_list;184ret_list.push_back(move);185return ret_list;186}187188MouseEventsHandler::~MouseEventsHandler()189{190}191192static void submit_and_free_event(GdkEvent* p_mouse_event, int sleep_time_ms)193{194gdk_event_put(p_mouse_event);195GdkDevice* usedDevice = NULL;196if (p_mouse_event->type == GDK_MOTION_NOTIFY) {197usedDevice = p_mouse_event->motion.device;198} else {199usedDevice = p_mouse_event->button.device;200}201g_object_unref(usedDevice);202gdk_event_free(p_mouse_event);203sleep_for_ms(sleep_time_ms);204}205206static void print_mouse_event(GdkEvent* p_ev)207{208if (!((p_ev->type == GDK_BUTTON_PRESS) || (p_ev->type == GDK_BUTTON_RELEASE)209|| (p_ev->type == GDK_MOTION_NOTIFY) || p_ev->type == GDK_2BUTTON_PRESS)) {210LOG(DEBUG) << "Not a mouse event.";211return;212}213214std::string ev_type;215if (p_ev->type == GDK_BUTTON_PRESS) {216ev_type = "press";217};218219if (p_ev->type == GDK_BUTTON_RELEASE) {220ev_type = "release";221};222223if (p_ev->type == GDK_MOTION_NOTIFY) {224ev_type = "motion";225};226227if (p_ev->type == GDK_2BUTTON_PRESS) {228ev_type = "2press";229};230LOG(DEBUG) << "Type: " << ev_type << " time: " <<231p_ev->key.time;232}233234static void submit_and_free_events_list(list<GdkEvent*>& events_list,235int sleep_time_ms)236{237for_each(events_list.begin(), events_list.end(), print_mouse_event);238239for_each(events_list.begin(), events_list.end(),240bind2nd(ptr_fun(submit_and_free_event), sleep_time_ms));241242events_list.clear();243}244245extern "C"246{247WD_RESULT clickAt(WINDOW_HANDLE windowHandle, long x, long y, long button)248{249init_logging();250251LOG(DEBUG) << "---------- starting clickAt: " << windowHandle << "---------";252GdkDrawable* hwnd = (GdkDrawable*) windowHandle;253254if (button == 2) {255// the right mouse button has the value 3 in GDK256button = 3;257} else {258// the left mouse button is default259button = 1;260}261262MouseEventsHandler mousep_handler(hwnd);263264list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseClick(x, y, button);265const int timePerEvent = 10 /* ms */;266submit_and_free_events_list(events_for_mouse, timePerEvent);267268269if (gLatestEventTime < mousep_handler.get_last_event_time()) {270gLatestEventTime = mousep_handler.get_last_event_time();271}272273LOG(DEBUG) << "---------- Ending clickAt ----------";274return 0;275}276277WD_RESULT doubleClickAt(WINDOW_HANDLE windowHandle, long x, long y)278{279init_logging();280281LOG(DEBUG) << "---------- starting doubleClickAt: " << windowHandle << "---------";282GdkDrawable* hwnd = (GdkDrawable*) windowHandle;283284MouseEventsHandler mousep_handler(hwnd);285286const int timePerEvent = 10 /* ms */;287list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseDoubleClick(x, y);288submit_and_free_events_list(events_for_mouse, timePerEvent);289290if (gLatestEventTime < mousep_handler.get_last_event_time()) {291gLatestEventTime = mousep_handler.get_last_event_time();292}293294LOG(DEBUG) << "---------- Ending doubleClickAt ----------";295return 0;296}297298/**299* mouseMoveTo300*/301WD_RESULT mouseMoveTo(WINDOW_HANDLE windowHandle, long duration, long fromX, long fromY, long toX, long toY)302{303init_logging();304const int timePerEvent = 10 /* ms */;305306LOG(DEBUG) << "---------- starting mouseMoveTo: " << windowHandle << "---------";307GdkDrawable* hwnd = (GdkDrawable*) windowHandle;308309MouseEventsHandler mousep_handler(hwnd);310311long pointsDistance = distanceBetweenPoints(fromX, fromY, toX, toY);312const int stepSizeInPixels = 5;313int steps = pointsDistance / stepSizeInPixels;314315// If the distance between the points is less than stepSizeInPixels,316// make sure enough move events aregenerated.317// * If the start and finish points are the same, one step is needed.318// Otherwise, generate at least 2 move events: one one the start319// point and the other on the end point.320if ((fromX == toX) && (fromY == toY)) {321steps = 1;322} else {323steps = max(steps, 2);324}325326assert(steps > 0);327LOG(DEBUG) << "From: (" << fromX << ", " << fromY << ") to: (" << toX << ", " << toY << ")";328LOG(DEBUG) << "Distance: " << pointsDistance << " steps: " << steps;329330for (int i = 0; i < steps; ++i) {331// To avoid integer division rounding and cumulative floating point errors,332// calculate from scratch each time. We adjust the divider to steps - 1333// to get a move event generated on the exact starting point as well334// as the end point.335int div_by = max(steps - 1, 1);336int currentX = fromX + ((toX - fromX) * ((double)i) / div_by);337int currentY = fromY + ((toY - fromY) * ((double)i) / div_by);338LOG(DEBUG) << "Moving to: (" << currentX << ", " << currentY << ")";339list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseMove(currentX, currentY);340submit_and_free_events_list(events_for_mouse, timePerEvent);341}342343344345if (gLatestEventTime < mousep_handler.get_last_event_time()) {346gLatestEventTime = mousep_handler.get_last_event_time();347}348349LOG(DEBUG) << "---------- Ending mouseMoveTo ----------";350return 0;351}352353/**354* mouseDownAt355*/356WD_RESULT mouseDownAt(WINDOW_HANDLE windowHandle, long x, long y, long button)357{358init_logging();359360const int timePerEvent = 10 /* ms */;361362LOG(DEBUG) << "---------- starting mouseDownAt: " << windowHandle << "---------";363GdkDrawable* hwnd = (GdkDrawable*) windowHandle;364365MouseEventsHandler mousep_handler(hwnd);366367struct timespec sleep_time;368sleep_time.tv_sec = timePerEvent / 1000;369sleep_time.tv_nsec = (timePerEvent % 1000) * 1000000;370LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<371sleep_time.tv_nsec << " nanoseconds.";372373list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseDown(x, y, button);374submit_and_free_events_list(events_for_mouse, timePerEvent);375376377if (gLatestEventTime < mousep_handler.get_last_event_time()) {378gLatestEventTime = mousep_handler.get_last_event_time();379}380381LOG(DEBUG) << "---------- Ending mouseDownAt ----------";382return 0;383}384385/**386* mouseUpAt387*/388WD_RESULT mouseUpAt(WINDOW_HANDLE windowHandle, long x, long y, long button)389{390init_logging();391392const int timePerEvent = 10 /* ms */;393394LOG(DEBUG) << "---------- starting mouseUpAt: " << windowHandle << "---------";395GdkDrawable* hwnd = (GdkDrawable*) windowHandle;396397MouseEventsHandler mousep_handler(hwnd);398399struct timespec sleep_time;400sleep_time.tv_sec = timePerEvent / 1000;401sleep_time.tv_nsec = (timePerEvent % 1000) * 1000000;402LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<403sleep_time.tv_nsec << " nanoseconds.";404405list<GdkEvent*> events_for_mouse = mousep_handler.CreateEventsForMouseUp(x, y, button);406submit_and_free_events_list(events_for_mouse, timePerEvent);407408409if (gLatestEventTime < mousep_handler.get_last_event_time()) {410gLatestEventTime = mousep_handler.get_last_event_time();411}412413LOG(DEBUG) << "---------- Ending mouseUpAt ----------";414return 0;415}416417bool pending_mouse_events()418{419init_logging();420LOG(DEBUG) << "Waiting for all events to be processed";421GdkEvent* lastEvent = gdk_event_peek();422LOG(DEBUG) << "Got event: " <<423(lastEvent != NULL ? lastEvent->type : 0);424425bool ret_val = false;426if (lastEvent != NULL && is_gdk_mouse_event(lastEvent) &&427event_earlier_than(lastEvent, gLatestEventTime)) {428ret_val = true;429}430431if (lastEvent != NULL) {432gdk_event_free(lastEvent);433}434LOG(DEBUG) << "Returning: " << ret_val;435436return ret_val;437}438439} // extern C440441442