Path: blob/trunk/cpp/webdriver-interactions/interactions_linux.cpp
2867 views
/*1Copyright 2007-2009 WebDriver committers2Copyright 2007-2009 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"23#include "logging.h"2425#include <gdk/gdk.h>26#include <gdk/gdkkeysyms.h>27#include <X11/Xlib.h>28#include <time.h>29#include <stdlib.h>30#include <assert.h>31#include <list>32#include <algorithm>33#include <functional>3435#include "translate_keycode_linux.h"36#include "interactions_linux.h"3738using namespace std;3940// This class represents a single modifier key. A modifier key is Shift,41// Ctrl or Alt. A key has, besides a GDK symbol related to it, a Mask42// that must be appended to each keyboard event when this modifier is43// set.44class XModifierKey45{46public:47// Stores the key associated with this modifier and the bit-mask48// to set when this key was toggled.49XModifierKey(const guint& associated_gdk_key, const GdkModifierType& gdk_mod,50const guint32& stored_state);51// if a_key matches the associated gdk key, toggeles the modifier52// key state.53void ToggleIfKeyMatches(const guint a_key);54// Returns true if the key given matches the key associated with55// this modifier.56bool KeyMatches(const guint a_key) const;57// if the modifier key was pressed, return the mask to OR with.58// If not, return 0.59guint GetAppropriateMask() const;60// Set the modifier to false.61void ClearModifier();62// Returns the associated key63guint get_associated_key() const;64// Returns true if the modifier is set, false otherwise.65bool get_toggle() const;66// Store the current state of the modifier key into the provided int.67void StoreState(guint32* state_store) const;68private:69bool toggle_;70guint associated_key_;71GdkModifierType gdk_mod_mask_;72};7374XModifierKey::XModifierKey(const guint& associated_gdk_key,75const GdkModifierType& gdk_mod,76const guint32& stored_state) :77toggle_(stored_state & gdk_mod), associated_key_(associated_gdk_key), gdk_mod_mask_(gdk_mod)78{79LOG(DEBUG) << "Restored state for " << gdk_mod_mask_ << " : " << toggle_;80}8182bool XModifierKey::KeyMatches(const guint a_key) const83{84return (a_key == associated_key_);85}8687void XModifierKey::ToggleIfKeyMatches(const guint a_key)88{89if (KeyMatches(a_key)) {90toggle_ = !toggle_;91}92}9394guint XModifierKey::GetAppropriateMask() const95{96if (toggle_) {97return gdk_mod_mask_;98}99return 0;100}101102guint XModifierKey::get_associated_key() const103{104return associated_key_;105}106107void XModifierKey::ClearModifier()108{109toggle_ = false;110}111112bool XModifierKey::get_toggle() const113{114return toggle_;115}116117void XModifierKey::StoreState(guint32* state_store) const118{119guint32 non_mask_bits = ~gdk_mod_mask_;120guint32 toggle_bit = (toggle_ ? gdk_mod_mask_ : 0);121122*state_store = (*state_store & non_mask_bits) | toggle_bit;123LOG(DEBUG) << "Storing state for " << gdk_mod_mask_ << " toggled? " << toggle_ <<124" state store: " << *state_store << " non-mask bits: " << std::hex << non_mask_bits;125}126127// Definition of a key press, release events pair.128typedef std::pair<GdkEvent*, GdkEvent*> KeyEventsPair;129enum KeyEventType { kKeyPress, kKeyRelease };130// This class handles generation of key press / release events.131// Events will be generated according to the given key to emulate132// and state of modifier keys.133class KeypressEventsHandler134{135public:136KeypressEventsHandler(GdkDrawable* win_handle, guint32 modifiers_state);137virtual ~KeypressEventsHandler();138139// Create a series of key release events that were left on at the end of140// a sendKeys call.141list<GdkEvent*> CreateModifierReleaseEvents();142143// Creates a series of key events according to the key to emulate144// Cases:145// 1. Null key: Reset modifiers state and return no events.146// 2. lowercase letter: Create KeyPress, KeyRelease events.147// 3. Uppercase letter: Creates Shift Down, KeyPress, KeyRelease148// and Shift Up events.149// 4. Modifier: KeyPress event only, unless it was down150// already - in which case, a KeyRelease151list<GdkEvent*> CreateEventsForKey(wchar_t key_to_emulate);152// Returns the time of the latest event.153guint32 get_last_event_time();154// Returns the state of modifier keys, to be stored between calls.155guint32 getModifierKeysState();156157158private:159// Create a keyboard event for a character or a non-modifier key160// (arrow or tab keys, for example).161GdkEvent* CreateKeyEvent(wchar_t key_to_emulate, KeyEventType ev_type);162// Create a keyboard event for a modifier key - for example, when163// shift is pressed.164GdkEvent* CreateModifierKeyEvent(wchar_t key_to_emulate);165// Returns true if the given character represents any of the modifier keys166// the instance of this class knows about.167bool IsModifierKey(wchar_t key);168// Generates key down / up pair for a regular character.169KeyEventsPair CreateKeyDownUpEvents(wchar_t key_to_emulate);170171// Creates a generic key event - used by the public methods172// that generate events. Not used for modifier keys.173GdkEvent* CreateGenericKeyEvent(wchar_t key_to_emulate, KeyEventType ev_type);174175// Similar to CreateGenericKeyEvent, but for modifier keys.176GdkEvent* CreateGenericModifierKeyEvent(guint gdk_key, KeyEventType ev_type);177// Creates an empty event.178GdkEvent* CreateEmptyKeyEvent(KeyEventType ev_type);179180// Modifiers related.181// Clears all of the modifiers182void ClearModifiers();183// Creates XModifierKey instances for a list of known, hard-coded184// modifier keys.185void InitModifiers();186// Stores the state of all modifier keys into the static field.187void StoreModifiersState();188// Given a mask, add bits representing all of the relevant set modifiers189// to it.190void AddModifiersToMask(guint& mask_to_modifiy);191// Returns true if a modifier, representing this gdk key, is set.192bool IsModifierSet(guint gdk_key);193// Called during handling of a modifier key, this method stores194// the change of the appropriate modifier key (toggles it).195void StoreModifierKeyState(guint gdk_mod_key);196// Returns true if the Shift modifier is set.197bool IsShiftSet();198199// Members.200// Known modifiers and their states.201list<XModifierKey> modifiers_;202// The window handle to be used.203GdkDrawable* win_handle_;204// Time of the most recent event created.205guint32 last_event_time_;206// State of modifier keys - initialized from a global207guint32 modifiers_state_;208};209210// Sets the is_modifier field of the GdkEvent according to the supplied211// boolean.212static void SetIsModifierEvent(GdkEvent* p_ev, bool is_modifier)213{214assert(p_ev->type == GDK_KEY_RELEASE || p_ev->type == GDK_KEY_PRESS);215p_ev->key.is_modifier = (int) is_modifier;216}217218KeypressEventsHandler::KeypressEventsHandler(GdkDrawable* win_handle, guint32 modifiers_state) :219modifiers_(), win_handle_(win_handle), last_event_time_(TimeSinceBootMsec()),220modifiers_state_(modifiers_state)221{222InitModifiers();223}224225// Will be called for the "Null" key.226void KeypressEventsHandler::ClearModifiers()227{228for_each(modifiers_.begin(), modifiers_.end(),229mem_fun_ref(&XModifierKey::ClearModifier));230}231232void KeypressEventsHandler::InitModifiers()233{234if (modifiers_.empty() == false) {235modifiers_.clear();236}237238modifiers_.push_back(XModifierKey(GDK_Shift_L, GDK_SHIFT_MASK, modifiers_state_));239modifiers_.push_back(XModifierKey(GDK_Control_L, GDK_CONTROL_MASK, modifiers_state_));240modifiers_.push_back(XModifierKey(GDK_Alt_L, GDK_MOD1_MASK, modifiers_state_));241}242243void KeypressEventsHandler::StoreModifiersState()244{245for_each(modifiers_.begin(), modifiers_.end(),246bind2nd(mem_fun_ref(&XModifierKey::StoreState), &modifiers_state_));247LOG(DEBUG) << "Stored modifiers: " << modifiers_state_;248}249250bool KeypressEventsHandler::IsModifierKey(wchar_t key)251{252bool is_modifier = false;253guint gdk_key_sym = translate_code_to_gdk_symbol(key);254for (list<XModifierKey>::iterator it = modifiers_.begin();255it != modifiers_.end(); ++it) {256is_modifier |= it->KeyMatches(gdk_key_sym);257}258259return is_modifier;260}261262bool KeypressEventsHandler::IsModifierSet(guint gdk_key)263{264list<XModifierKey>::iterator it =265find_if(modifiers_.begin(), modifiers_.end(),266bind2nd(mem_fun_ref(&XModifierKey::KeyMatches), gdk_key));267268if (it == modifiers_.end()) {269return false;270}271272return it->get_toggle();273}274275void KeypressEventsHandler::StoreModifierKeyState(guint gdk_mod_key)276{277for_each(modifiers_.begin(), modifiers_.end(),278bind2nd(mem_fun_ref(&XModifierKey::ToggleIfKeyMatches),279gdk_mod_key));280StoreModifiersState();281}282283void KeypressEventsHandler::AddModifiersToMask(guint& mask_to_modifiy)284{285for (list<XModifierKey>::iterator it = modifiers_.begin();286it != modifiers_.end(); ++it) {287mask_to_modifiy|= it->GetAppropriateMask();288}289}290291bool modifier_is_shift(const XModifierKey& k)292{293return (k.get_associated_key() == GDK_Shift_L);294}295296bool KeypressEventsHandler::IsShiftSet()297{298list<XModifierKey>::iterator it =299find_if(modifiers_.begin(), modifiers_.end(), modifier_is_shift);300assert(it != modifiers_.end());301return it->get_toggle();302}303304guint32 KeypressEventsHandler::get_last_event_time()305{306return last_event_time_;307}308309guint32 KeypressEventsHandler::getModifierKeysState()310{311return modifiers_state_;312}313314315GdkEvent* KeypressEventsHandler::CreateEmptyKeyEvent(KeyEventType ev_type)316{317GdkEventType gdk_ev = GDK_KEY_PRESS;318if (ev_type == kKeyRelease) {319gdk_ev = GDK_KEY_RELEASE;320}321GdkEvent* p_ev = gdk_event_new(gdk_ev);322p_ev->key.window = GDK_WINDOW(g_object_ref(win_handle_));323p_ev->key.send_event = 0; // NOT a synthesized event.324p_ev->key.time = TimeSinceBootMsec();325// Also update the latest event time326last_event_time_ = p_ev->key.time;327// Deprecated.328p_ev->key.length = 0;329p_ev->key.string = NULL;330// Put a default key code for space. This will be fixed later331// by callers, that will translate the given character to332// its appropriate keycode.333const guint16 kSpaceKeycode = 65;334p_ev->key.hardware_keycode = kSpaceKeycode;335// This flag will be set to true later, if we indeed create336// a modifier key event.337SetIsModifierEvent(p_ev, false);338339// This applies to regular characters, keys and modifiers.340// This must be done before the special handling for modifier341// keys, as it will change the internal state of the modifiers.342AddModifiersToMask(p_ev->key.state);343344return p_ev;345}346347static guint16 get_keycode_for_key(guint for_key)348{349guint16 ret_kc;350const char* display_name = gdk_display_get_name(gdk_display_get_default());351Display* xdisplay = XOpenDisplay(display_name);352assert(xdisplay != NULL);353354KeyCode kc = XKeysymToKeycode(xdisplay, for_key);355LOG(DEBUG) << "Got keycode: " << (int) kc;356XCloseDisplay(xdisplay);357ret_kc = (int) kc;358359return ret_kc;360}361362GdkEvent* KeypressEventsHandler::CreateGenericKeyEvent(wchar_t key_to_emulate,363KeyEventType ev_type)364{365GdkEvent* p_ev = CreateEmptyKeyEvent(ev_type);366367guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);368// Common case - key is not a modifier or a special key (arrow, tab, etc)369if (translated_key == GDK_VoidSymbol) {370// Ordinary character.371p_ev->key.keyval = gdk_unicode_to_keyval(key_to_emulate);372} else {373// Special key374p_ev->key.keyval = translated_key;375}376377p_ev->key.hardware_keycode = get_keycode_for_key(p_ev->key.keyval);378379if (IsShiftSet()) {380p_ev->key.keyval = gdk_keyval_to_upper(p_ev->key.keyval);381}382383return p_ev;384}385386GdkEvent* KeypressEventsHandler::CreateGenericModifierKeyEvent(387guint gdk_key, KeyEventType ev_type)388{389GdkEvent* p_ev = CreateEmptyKeyEvent(ev_type);390391p_ev->key.keyval = gdk_key;392p_ev->key.hardware_keycode = get_keycode_for_key(p_ev->key.keyval);393394SetIsModifierEvent(p_ev, true);395396return p_ev;397}398GdkEvent* KeypressEventsHandler::CreateKeyEvent(wchar_t key_to_emulate,399KeyEventType ev_type)400{401// Should only be called with non-modifier keys.402assert(IsModifierKey(key_to_emulate) == false);403return CreateGenericKeyEvent(key_to_emulate, ev_type);404}405406KeyEventsPair KeypressEventsHandler::CreateKeyDownUpEvents(407wchar_t key_to_emulate)408{409GdkEvent* down = CreateKeyEvent(key_to_emulate, kKeyPress);410GdkEvent* up = CreateKeyEvent(key_to_emulate, kKeyRelease);411return std::make_pair(down, up);412}413414GdkEvent* KeypressEventsHandler::CreateModifierKeyEvent(415wchar_t key_to_emulate)416{417guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);418assert(translated_key != GDK_VoidSymbol);419// If the modifier is set - this is a release event, otherwise -420// a key press.421KeyEventType ev_type = kKeyPress;422if (IsModifierSet(translated_key)) {423ev_type = kKeyRelease;424}425GdkEvent* ret_event =426CreateGenericModifierKeyEvent(translated_key, ev_type);427428StoreModifierKeyState(translated_key);429return ret_event;430}431432list<GdkEvent*> KeypressEventsHandler::CreateModifierReleaseEvents()433{434list<GdkEvent*> ret_list;435for (list<XModifierKey>::iterator it = modifiers_.begin();436it != modifiers_.end(); ++it) {437if (it->get_toggle()) {438GdkEvent* rel_event =439CreateGenericModifierKeyEvent(it->get_associated_key(), kKeyRelease);440ret_list.push_back(rel_event);441it->ClearModifier();442}443}444445StoreModifiersState();446447return ret_list;448}449450bool is_lowercase_symbol(wchar_t key_to_emulate)451{452// Note that it is *only* allowed for keys that cannot be translated453// this bears the assumption that keys defined in Keys.java do not454// have a different "capitalized" representation.455// This makes sense as the keys in Keys.java are non-alphanumeric456// keys (arrows, tab, etc);457//458assert(translate_code_to_gdk_symbol(key_to_emulate) == GDK_VoidSymbol);459460string chars_req_shift = "!$^*()+{}:?|~@#%&_\"<>";461462bool shift_needed = (chars_req_shift.find(toascii(key_to_emulate)) !=463string::npos);464// If the representation is different than the lowercase465// representation, this is not a lowercase character.466if (shift_needed || (key_to_emulate != towlower(key_to_emulate))) {467return false;468}469return true;470}471472list<GdkEvent*> KeypressEventsHandler::CreateEventsForKey(473wchar_t key_to_emulate)474{475list<GdkEvent*> ret_list;476// First case - is it the NULL symbol? If so, reset modifiers and exit.477if (key_to_emulate == gNullKey) {478LOG(DEBUG) << "Null key - clearing modifiers.";479return CreateModifierReleaseEvents();480}481482// Now: The key is either a modifier key or character key.483// Common case - not a modifier key. Need two events - Key press and484// key release.485if (IsModifierKey(key_to_emulate) == false) {486LOG(DEBUG) << "Key: " << key_to_emulate << " is not a modifier.";487488guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);489// First - check to see if this is an lowercase letter or is a490// non-alphanumeric key (which cannot be capitalized)491if ((translated_key != GDK_VoidSymbol) ||492(is_lowercase_symbol(key_to_emulate))) {493LOG(DEBUG) << "Lowercase letter or non void gdk symbol.";494// More common case - lowercase letter.495// Create only two events.496// Note that if the Shift modifier is set, this character will497// be converted to uppercase by CreateKeyEvent method.498KeyEventsPair ev = CreateKeyDownUpEvents(key_to_emulate);499ret_list.push_back(ev.first);500ret_list.push_back(ev.second);501} else {502// Uppercase letter/symbol: Fire up shift down event, this key and503// shift up event (unless the Shift modifier is already set)504bool shift_was_set = IsShiftSet();505LOG(DEBUG) << "Uppercase letter. Was shift set? " << shift_was_set;506if (shift_was_set == false) {507// push shift down event508ret_list.push_front(509CreateGenericModifierKeyEvent(GDK_Shift_L, kKeyPress));510StoreModifierKeyState(GDK_Shift_L);511}512KeyEventsPair ev = CreateKeyDownUpEvents(key_to_emulate);513// Push the events themselves.514ret_list.push_back(ev.first);515ret_list.push_back(ev.second);516517if (shift_was_set == false) {518// push shift up event519ret_list.push_back(520CreateGenericModifierKeyEvent(GDK_Shift_L, kKeyRelease));521// Turn OFF the shift modifier!522StoreModifierKeyState(GDK_Shift_L);523}524}525} else { // Modifier key.526// When a modifier key is pressed, the state does not yet change to reflect527// it (on the KeyPress event for the modifier key). When the modifier key is528// released, the state indeed reflects that it was pressed.529LOG(DEBUG) << "Key: " << key_to_emulate << " IS a modifier.";530// generate only one keypress event, either press or release.531GdkEvent* p_ev = CreateModifierKeyEvent(key_to_emulate);532ret_list.push_back(p_ev);533}534535return ret_list;536}537538KeypressEventsHandler::~KeypressEventsHandler()539{540modifiers_.clear();541}542543static void submit_and_free_event(GdkEvent* p_key_event, int sleep_time_ms)544{545gdk_event_put(p_key_event);546gdk_event_free(p_key_event);547sleep_for_ms(sleep_time_ms);548}549550static void submit_and_free_events_list(list<GdkEvent*>& events_list,551int sleep_time_ms)552{553for_each(events_list.begin(), events_list.end(), print_key_event);554555for_each(events_list.begin(), events_list.end(),556bind2nd(ptr_fun(submit_and_free_event), sleep_time_ms));557558events_list.clear();559}560561// global variable declared here so it is not used beforehand.562guint32 gModifiersState = 0;563564int getTimePerKey(int proposedTimePerKey)565{566const int minTimePerKey = 10 /* ms */;567if (proposedTimePerKey < minTimePerKey) {568return minTimePerKey;569}570571return proposedTimePerKey;572}573574void updateLastEventTime(const guint32 lastEventTime) {575if (gLatestEventTime < lastEventTime) {576gLatestEventTime = lastEventTime;577}578}579580extern "C"581{582void sendKeys(WINDOW_HANDLE windowHandle, const wchar_t* value, int requestedTimePerKey)583{584init_logging();585int timePerKey = getTimePerKey(requestedTimePerKey);586587LOG(DEBUG) << "---------- starting sendKeys: " << windowHandle << " tpk: " <<588timePerKey << "---------";589GdkDrawable* hwnd = (GdkDrawable*) windowHandle;590591// The keyp_handler will remember the state of modifier keys and592// will be used to generate the events themselves.593KeypressEventsHandler keyp_handler(hwnd, gModifiersState);594595struct timespec sleep_time;596sleep_time.tv_sec = timePerKey / 1000;597sleep_time.tv_nsec = (timePerKey % 1000) * 1000000;598LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<599sleep_time.tv_nsec << " nanoseconds.";600601int i = 0;602while (value[i] != '\0') {603list<GdkEvent*> events_for_key =604keyp_handler.CreateEventsForKey(value[i]);605606submit_and_free_events_list(events_for_key, timePerKey);607608i++;609}610611updateLastEventTime(keyp_handler.get_last_event_time());612gModifiersState = keyp_handler.getModifierKeysState();613614LOG(DEBUG) << "---------- Ending sendKeys. Total keys: " << i615<< " ----------";616}617618void releaseModifierKeys(WINDOW_HANDLE windowHandle, int requestedTimePerKey)619{620init_logging();621int timePerKey = getTimePerKey(requestedTimePerKey);622623LOG(DEBUG) << "---------- starting releaseModifierKeys: " << windowHandle << " tpk: " <<624timePerKey << "---------";625GdkDrawable* hwnd = (GdkDrawable*) windowHandle;626627// The state of the modifier keys is stored - just calling release will work.628KeypressEventsHandler keyp_handler(hwnd, gModifiersState);629630// Free the remaining modifiers that are still set.631list<GdkEvent*> modifier_release_events =632keyp_handler.CreateModifierReleaseEvents();633int num_released = modifier_release_events.size();634635submit_and_free_events_list(modifier_release_events, timePerKey);636637updateLastEventTime(keyp_handler.get_last_event_time());638gModifiersState = keyp_handler.getModifierKeysState();639640LOG(DEBUG) << "---------- Ending releaseModifierKeys. Released: " << num_released641<< " ----------";642}643644}645646647