blob: 8a1a3f452304602eae21eed6580d31cd81b09d76 [file] [log] [blame] [edit]
/*
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 <ctime>
#include <string>
#include <iostream>
#include <fstream>
#include "interactions.h"
#include "logging.h"
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <X11/Xlib.h>
#include <time.h>
#include <stdlib.h>
#include <assert.h>
#include <list>
#include <algorithm>
#include <functional>
#include "translate_keycode_linux.h"
#include "interactions_linux.h"
using namespace std;
// This class represents a single modifier key. A modifier key is Shift,
// Ctrl or Alt. A key has, besides a GDK symbol related to it, a Mask
// that must be appended to each keyboard event when this modifier is
// set.
class XModifierKey
{
public:
// Stores the key associated with this modifier and the bit-mask
// to set when this key was toggled.
XModifierKey(const guint& associated_gdk_key, const GdkModifierType& gdk_mod,
const guint32& stored_state);
// if a_key matches the associated gdk key, toggeles the modifier
// key state.
void ToggleIfKeyMatches(const guint a_key);
// Returns true if the key given matches the key associated with
// this modifier.
bool KeyMatches(const guint a_key) const;
// if the modifier key was pressed, return the mask to OR with.
// If not, return 0.
guint GetAppropriateMask() const;
// Set the modifier to false.
void ClearModifier();
// Returns the associated key
guint get_associated_key() const;
// Returns true if the modifier is set, false otherwise.
bool get_toggle() const;
// Store the current state of the modifier key into the provided int.
void StoreState(guint32* state_store) const;
private:
bool toggle_;
guint associated_key_;
GdkModifierType gdk_mod_mask_;
};
XModifierKey::XModifierKey(const guint& associated_gdk_key,
const GdkModifierType& gdk_mod,
const guint32& stored_state) :
toggle_(stored_state & gdk_mod), associated_key_(associated_gdk_key), gdk_mod_mask_(gdk_mod)
{
LOG(DEBUG) << "Restored state for " << gdk_mod_mask_ << " : " << toggle_;
}
bool XModifierKey::KeyMatches(const guint a_key) const
{
return (a_key == associated_key_);
}
void XModifierKey::ToggleIfKeyMatches(const guint a_key)
{
if (KeyMatches(a_key)) {
toggle_ = !toggle_;
}
}
guint XModifierKey::GetAppropriateMask() const
{
if (toggle_) {
return gdk_mod_mask_;
}
return 0;
}
guint XModifierKey::get_associated_key() const
{
return associated_key_;
}
void XModifierKey::ClearModifier()
{
toggle_ = false;
}
bool XModifierKey::get_toggle() const
{
return toggle_;
}
void XModifierKey::StoreState(guint32* state_store) const
{
guint32 non_mask_bits = ~gdk_mod_mask_;
guint32 toggle_bit = (toggle_ ? gdk_mod_mask_ : 0);
*state_store = (*state_store & non_mask_bits) | toggle_bit;
LOG(DEBUG) << "Storing state for " << gdk_mod_mask_ << " toggled? " << toggle_ <<
" state store: " << *state_store << " non-mask bits: " << std::hex << non_mask_bits;
}
// Definition of a key press, release events pair.
typedef std::pair<GdkEvent*, GdkEvent*> KeyEventsPair;
enum KeyEventType { kKeyPress, kKeyRelease };
// This class handles generation of key press / release events.
// Events will be generated according to the given key to emulate
// and state of modifier keys.
class KeypressEventsHandler
{
public:
KeypressEventsHandler(GdkDrawable* win_handle, guint32 modifiers_state);
virtual ~KeypressEventsHandler();
// Create a series of key release events that were left on at the end of
// a sendKeys call.
list<GdkEvent*> CreateModifierReleaseEvents();
// Creates a series of key events according to the key to emulate
// Cases:
// 1. Null key: Reset modifiers state and return no events.
// 2. lowercase letter: Create KeyPress, KeyRelease events.
// 3. Uppercase letter: Creates Shift Down, KeyPress, KeyRelease
// and Shift Up events.
// 4. Modifier: KeyPress event only, unless it was down
// already - in which case, a KeyRelease
list<GdkEvent*> CreateEventsForKey(wchar_t key_to_emulate);
// Returns the time of the latest event.
guint32 get_last_event_time();
// Returns the state of modifier keys, to be stored between calls.
guint32 getModifierKeysState();
private:
// Create a keyboard event for a character or a non-modifier key
// (arrow or tab keys, for example).
GdkEvent* CreateKeyEvent(wchar_t key_to_emulate, KeyEventType ev_type);
// Create a keyboard event for a modifier key - for example, when
// shift is pressed.
GdkEvent* CreateModifierKeyEvent(wchar_t key_to_emulate);
// Returns true if the given character represents any of the modifier keys
// the instance of this class knows about.
bool IsModifierKey(wchar_t key);
// Generates key down / up pair for a regular character.
KeyEventsPair CreateKeyDownUpEvents(wchar_t key_to_emulate);
// Creates a generic key event - used by the public methods
// that generate events. Not used for modifier keys.
GdkEvent* CreateGenericKeyEvent(wchar_t key_to_emulate, KeyEventType ev_type);
// Similar to CreateGenericKeyEvent, but for modifier keys.
GdkEvent* CreateGenericModifierKeyEvent(guint gdk_key, KeyEventType ev_type);
// Creates an empty event.
GdkEvent* CreateEmptyKeyEvent(KeyEventType ev_type);
// Modifiers related.
// Clears all of the modifiers
void ClearModifiers();
// Creates XModifierKey instances for a list of known, hard-coded
// modifier keys.
void InitModifiers();
// Stores the state of all modifier keys into the static field.
void StoreModifiersState();
// Given a mask, add bits representing all of the relevant set modifiers
// to it.
void AddModifiersToMask(guint& mask_to_modifiy);
// Returns true if a modifier, representing this gdk key, is set.
bool IsModifierSet(guint gdk_key);
// Called during handling of a modifier key, this method stores
// the change of the appropriate modifier key (toggles it).
void StoreModifierKeyState(guint gdk_mod_key);
// Returns true if the Shift modifier is set.
bool IsShiftSet();
// Members.
// Known modifiers and their states.
list<XModifierKey> modifiers_;
// The window handle to be used.
GdkDrawable* win_handle_;
// Time of the most recent event created.
guint32 last_event_time_;
// State of modifier keys - initialized from a global
guint32 modifiers_state_;
};
// Sets the is_modifier field of the GdkEvent according to the supplied
// boolean.
static void SetIsModifierEvent(GdkEvent* p_ev, bool is_modifier)
{
assert(p_ev->type == GDK_KEY_RELEASE || p_ev->type == GDK_KEY_PRESS);
p_ev->key.is_modifier = (int) is_modifier;
}
KeypressEventsHandler::KeypressEventsHandler(GdkDrawable* win_handle, guint32 modifiers_state) :
modifiers_(), win_handle_(win_handle), last_event_time_(TimeSinceBootMsec()),
modifiers_state_(modifiers_state)
{
InitModifiers();
}
// Will be called for the "Null" key.
void KeypressEventsHandler::ClearModifiers()
{
for_each(modifiers_.begin(), modifiers_.end(),
mem_fun_ref(&XModifierKey::ClearModifier));
}
void KeypressEventsHandler::InitModifiers()
{
if (modifiers_.empty() == false) {
modifiers_.clear();
}
modifiers_.push_back(XModifierKey(GDK_Shift_L, GDK_SHIFT_MASK, modifiers_state_));
modifiers_.push_back(XModifierKey(GDK_Control_L, GDK_CONTROL_MASK, modifiers_state_));
modifiers_.push_back(XModifierKey(GDK_Alt_L, GDK_MOD1_MASK, modifiers_state_));
}
void KeypressEventsHandler::StoreModifiersState()
{
for_each(modifiers_.begin(), modifiers_.end(),
bind2nd(mem_fun_ref(&XModifierKey::StoreState), &modifiers_state_));
LOG(DEBUG) << "Stored modifiers: " << modifiers_state_;
}
bool KeypressEventsHandler::IsModifierKey(wchar_t key)
{
bool is_modifier = false;
guint gdk_key_sym = translate_code_to_gdk_symbol(key);
for (list<XModifierKey>::iterator it = modifiers_.begin();
it != modifiers_.end(); ++it) {
is_modifier |= it->KeyMatches(gdk_key_sym);
}
return is_modifier;
}
bool KeypressEventsHandler::IsModifierSet(guint gdk_key)
{
list<XModifierKey>::iterator it =
find_if(modifiers_.begin(), modifiers_.end(),
bind2nd(mem_fun_ref(&XModifierKey::KeyMatches), gdk_key));
if (it == modifiers_.end()) {
return false;
}
return it->get_toggle();
}
void KeypressEventsHandler::StoreModifierKeyState(guint gdk_mod_key)
{
for_each(modifiers_.begin(), modifiers_.end(),
bind2nd(mem_fun_ref(&XModifierKey::ToggleIfKeyMatches),
gdk_mod_key));
StoreModifiersState();
}
void KeypressEventsHandler::AddModifiersToMask(guint& mask_to_modifiy)
{
for (list<XModifierKey>::iterator it = modifiers_.begin();
it != modifiers_.end(); ++it) {
mask_to_modifiy|= it->GetAppropriateMask();
}
}
bool modifier_is_shift(const XModifierKey& k)
{
return (k.get_associated_key() == GDK_Shift_L);
}
bool KeypressEventsHandler::IsShiftSet()
{
list<XModifierKey>::iterator it =
find_if(modifiers_.begin(), modifiers_.end(), modifier_is_shift);
assert(it != modifiers_.end());
return it->get_toggle();
}
guint32 KeypressEventsHandler::get_last_event_time()
{
return last_event_time_;
}
guint32 KeypressEventsHandler::getModifierKeysState()
{
return modifiers_state_;
}
GdkEvent* KeypressEventsHandler::CreateEmptyKeyEvent(KeyEventType ev_type)
{
GdkEventType gdk_ev = GDK_KEY_PRESS;
if (ev_type == kKeyRelease) {
gdk_ev = GDK_KEY_RELEASE;
}
GdkEvent* p_ev = gdk_event_new(gdk_ev);
p_ev->key.window = GDK_WINDOW(g_object_ref(win_handle_));
p_ev->key.send_event = 0; // NOT a synthesized event.
p_ev->key.time = TimeSinceBootMsec();
// Also update the latest event time
last_event_time_ = p_ev->key.time;
// Deprecated.
p_ev->key.length = 0;
p_ev->key.string = NULL;
// Put a default key code for space. This will be fixed later
// by callers, that will translate the given character to
// its appropriate keycode.
const guint16 kSpaceKeycode = 65;
p_ev->key.hardware_keycode = kSpaceKeycode;
// This flag will be set to true later, if we indeed create
// a modifier key event.
SetIsModifierEvent(p_ev, false);
// This applies to regular characters, keys and modifiers.
// This must be done before the special handling for modifier
// keys, as it will change the internal state of the modifiers.
AddModifiersToMask(p_ev->key.state);
return p_ev;
}
static guint16 get_keycode_for_key(guint for_key)
{
guint16 ret_kc;
const char* display_name = gdk_display_get_name(gdk_display_get_default());
Display* xdisplay = XOpenDisplay(display_name);
assert(xdisplay != NULL);
KeyCode kc = XKeysymToKeycode(xdisplay, for_key);
LOG(DEBUG) << "Got keycode: " << (int) kc;
XCloseDisplay(xdisplay);
ret_kc = (int) kc;
return ret_kc;
}
GdkEvent* KeypressEventsHandler::CreateGenericKeyEvent(wchar_t key_to_emulate,
KeyEventType ev_type)
{
GdkEvent* p_ev = CreateEmptyKeyEvent(ev_type);
guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);
// Common case - key is not a modifier or a special key (arrow, tab, etc)
if (translated_key == GDK_VoidSymbol) {
// Ordinary character.
p_ev->key.keyval = gdk_unicode_to_keyval(key_to_emulate);
} else {
// Special key
p_ev->key.keyval = translated_key;
}
p_ev->key.hardware_keycode = get_keycode_for_key(p_ev->key.keyval);
if (IsShiftSet()) {
p_ev->key.keyval = gdk_keyval_to_upper(p_ev->key.keyval);
}
return p_ev;
}
GdkEvent* KeypressEventsHandler::CreateGenericModifierKeyEvent(
guint gdk_key, KeyEventType ev_type)
{
GdkEvent* p_ev = CreateEmptyKeyEvent(ev_type);
p_ev->key.keyval = gdk_key;
p_ev->key.hardware_keycode = get_keycode_for_key(p_ev->key.keyval);
SetIsModifierEvent(p_ev, true);
return p_ev;
}
GdkEvent* KeypressEventsHandler::CreateKeyEvent(wchar_t key_to_emulate,
KeyEventType ev_type)
{
// Should only be called with non-modifier keys.
assert(IsModifierKey(key_to_emulate) == false);
return CreateGenericKeyEvent(key_to_emulate, ev_type);
}
KeyEventsPair KeypressEventsHandler::CreateKeyDownUpEvents(
wchar_t key_to_emulate)
{
GdkEvent* down = CreateKeyEvent(key_to_emulate, kKeyPress);
GdkEvent* up = CreateKeyEvent(key_to_emulate, kKeyRelease);
return std::make_pair(down, up);
}
GdkEvent* KeypressEventsHandler::CreateModifierKeyEvent(
wchar_t key_to_emulate)
{
guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);
assert(translated_key != GDK_VoidSymbol);
// If the modifier is set - this is a release event, otherwise -
// a key press.
KeyEventType ev_type = kKeyPress;
if (IsModifierSet(translated_key)) {
ev_type = kKeyRelease;
}
GdkEvent* ret_event =
CreateGenericModifierKeyEvent(translated_key, ev_type);
StoreModifierKeyState(translated_key);
return ret_event;
}
list<GdkEvent*> KeypressEventsHandler::CreateModifierReleaseEvents()
{
list<GdkEvent*> ret_list;
for (list<XModifierKey>::iterator it = modifiers_.begin();
it != modifiers_.end(); ++it) {
if (it->get_toggle()) {
GdkEvent* rel_event =
CreateGenericModifierKeyEvent(it->get_associated_key(), kKeyRelease);
ret_list.push_back(rel_event);
it->ClearModifier();
}
}
StoreModifiersState();
return ret_list;
}
bool is_lowercase_symbol(wchar_t key_to_emulate)
{
// Note that it is *only* allowed for keys that cannot be translated
// this bears the assumption that keys defined in Keys.java do not
// have a different "capitalized" representation.
// This makes sense as the keys in Keys.java are non-alphanumeric
// keys (arrows, tab, etc);
//
assert(translate_code_to_gdk_symbol(key_to_emulate) == GDK_VoidSymbol);
string chars_req_shift = "!$^*()+{}:?|~@#%&_\"<>";
bool shift_needed = (chars_req_shift.find(toascii(key_to_emulate)) !=
string::npos);
// If the representation is different than the lowercase
// representation, this is not a lowercase character.
if (shift_needed || (key_to_emulate != towlower(key_to_emulate))) {
return false;
}
return true;
}
list<GdkEvent*> KeypressEventsHandler::CreateEventsForKey(
wchar_t key_to_emulate)
{
list<GdkEvent*> ret_list;
// First case - is it the NULL symbol? If so, reset modifiers and exit.
if (key_to_emulate == gNullKey) {
LOG(DEBUG) << "Null key - clearing modifiers.";
return CreateModifierReleaseEvents();
}
// Now: The key is either a modifier key or character key.
// Common case - not a modifier key. Need two events - Key press and
// key release.
if (IsModifierKey(key_to_emulate) == false) {
LOG(DEBUG) << "Key: " << key_to_emulate << " is not a modifier.";
guint translated_key = translate_code_to_gdk_symbol(key_to_emulate);
// First - check to see if this is an lowercase letter or is a
// non-alphanumeric key (which cannot be capitalized)
if ((translated_key != GDK_VoidSymbol) ||
(is_lowercase_symbol(key_to_emulate))) {
LOG(DEBUG) << "Lowercase letter or non void gdk symbol.";
// More common case - lowercase letter.
// Create only two events.
// Note that if the Shift modifier is set, this character will
// be converted to uppercase by CreateKeyEvent method.
KeyEventsPair ev = CreateKeyDownUpEvents(key_to_emulate);
ret_list.push_back(ev.first);
ret_list.push_back(ev.second);
} else {
// Uppercase letter/symbol: Fire up shift down event, this key and
// shift up event (unless the Shift modifier is already set)
bool shift_was_set = IsShiftSet();
LOG(DEBUG) << "Uppercase letter. Was shift set? " << shift_was_set;
if (shift_was_set == false) {
// push shift down event
ret_list.push_front(
CreateGenericModifierKeyEvent(GDK_Shift_L, kKeyPress));
StoreModifierKeyState(GDK_Shift_L);
}
KeyEventsPair ev = CreateKeyDownUpEvents(key_to_emulate);
// Push the events themselves.
ret_list.push_back(ev.first);
ret_list.push_back(ev.second);
if (shift_was_set == false) {
// push shift up event
ret_list.push_back(
CreateGenericModifierKeyEvent(GDK_Shift_L, kKeyRelease));
// Turn OFF the shift modifier!
StoreModifierKeyState(GDK_Shift_L);
}
}
} else { // Modifier key.
// When a modifier key is pressed, the state does not yet change to reflect
// it (on the KeyPress event for the modifier key). When the modifier key is
// released, the state indeed reflects that it was pressed.
LOG(DEBUG) << "Key: " << key_to_emulate << " IS a modifier.";
// generate only one keypress event, either press or release.
GdkEvent* p_ev = CreateModifierKeyEvent(key_to_emulate);
ret_list.push_back(p_ev);
}
return ret_list;
}
KeypressEventsHandler::~KeypressEventsHandler()
{
modifiers_.clear();
}
static void submit_and_free_event(GdkEvent* p_key_event, int sleep_time_ms)
{
gdk_event_put(p_key_event);
gdk_event_free(p_key_event);
sleep_for_ms(sleep_time_ms);
}
static void submit_and_free_events_list(list<GdkEvent*>& events_list,
int sleep_time_ms)
{
for_each(events_list.begin(), events_list.end(), print_key_event);
for_each(events_list.begin(), events_list.end(),
bind2nd(ptr_fun(submit_and_free_event), sleep_time_ms));
events_list.clear();
}
// global variable declared here so it is not used beforehand.
guint32 gModifiersState = 0;
int getTimePerKey(int proposedTimePerKey)
{
const int minTimePerKey = 10 /* ms */;
if (proposedTimePerKey < minTimePerKey) {
return minTimePerKey;
}
return proposedTimePerKey;
}
void updateLastEventTime(const guint32 lastEventTime) {
if (gLatestEventTime < lastEventTime) {
gLatestEventTime = lastEventTime;
}
}
extern "C"
{
void sendKeys(WINDOW_HANDLE windowHandle, const wchar_t* value, int requestedTimePerKey)
{
init_logging();
int timePerKey = getTimePerKey(requestedTimePerKey);
LOG(DEBUG) << "---------- starting sendKeys: " << windowHandle << " tpk: " <<
timePerKey << "---------";
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
// The keyp_handler will remember the state of modifier keys and
// will be used to generate the events themselves.
KeypressEventsHandler keyp_handler(hwnd, gModifiersState);
struct timespec sleep_time;
sleep_time.tv_sec = timePerKey / 1000;
sleep_time.tv_nsec = (timePerKey % 1000) * 1000000;
LOG(DEBUG) << "Sleep time is " << sleep_time.tv_sec << " seconds and " <<
sleep_time.tv_nsec << " nanoseconds.";
int i = 0;
while (value[i] != '\0') {
list<GdkEvent*> events_for_key =
keyp_handler.CreateEventsForKey(value[i]);
submit_and_free_events_list(events_for_key, timePerKey);
i++;
}
updateLastEventTime(keyp_handler.get_last_event_time());
gModifiersState = keyp_handler.getModifierKeysState();
LOG(DEBUG) << "---------- Ending sendKeys. Total keys: " << i
<< " ----------";
}
void releaseModifierKeys(WINDOW_HANDLE windowHandle, int requestedTimePerKey)
{
init_logging();
int timePerKey = getTimePerKey(requestedTimePerKey);
LOG(DEBUG) << "---------- starting releaseModifierKeys: " << windowHandle << " tpk: " <<
timePerKey << "---------";
GdkDrawable* hwnd = (GdkDrawable*) windowHandle;
// The state of the modifier keys is stored - just calling release will work.
KeypressEventsHandler keyp_handler(hwnd, gModifiersState);
// Free the remaining modifiers that are still set.
list<GdkEvent*> modifier_release_events =
keyp_handler.CreateModifierReleaseEvents();
int num_released = modifier_release_events.size();
submit_and_free_events_list(modifier_release_events, timePerKey);
updateLastEventTime(keyp_handler.get_last_event_time());
gModifiersState = keyp_handler.getModifierKeysState();
LOG(DEBUG) << "---------- Ending releaseModifierKeys. Released: " << num_released
<< " ----------";
}
}