2013-01-11 22:18:32 +01:00
|
|
|
/*
|
|
|
|
|
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
|
|
|
|
|
<< " ----------";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|