blob: 74943e473869ca6fc6f33973fc711f7fc5e5b04d [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "unix/ibus/key_event_handler.h"
#include <map>
#include "base/logging.h"
#include "base/port.h"
#include "base/singleton.h"
namespace mozc {
namespace ibus {
namespace {
// TODO(hsumita): Removes this class, and moves |data_| into member
// variables of KeyEventhandler.
class AdditionalModifiersData {
public:
AdditionalModifiersData() {
data_[commands::KeyEvent::LEFT_ALT] = commands::KeyEvent::ALT;
data_[commands::KeyEvent::RIGHT_ALT] = commands::KeyEvent::ALT;
data_[commands::KeyEvent::LEFT_CTRL] = commands::KeyEvent::CTRL;
data_[commands::KeyEvent::RIGHT_CTRL] = commands::KeyEvent::CTRL;
data_[commands::KeyEvent::LEFT_SHIFT] = commands::KeyEvent::SHIFT;
data_[commands::KeyEvent::RIGHT_SHIFT] = commands::KeyEvent::SHIFT;
}
const map<uint32, commands::KeyEvent::ModifierKey> &data() {
return data_;
}
private:
map<uint32, commands::KeyEvent::ModifierKey> data_;
};
// TODO(hsumita): Moves this function into member functions of
// KeyEventHandler.
void AddAdditionalModifiers(
set<commands::KeyEvent::ModifierKey> *modifier_keys_set) {
DCHECK(modifier_keys_set);
const map<uint32, commands::KeyEvent::ModifierKey> &data =
Singleton<AdditionalModifiersData>::get()->data();
// Adds MODIFIER if there are (LEFT|RIGHT)_MODIFIER like LEFT_SHIFT.
for (set<commands::KeyEvent::ModifierKey>::const_iterator it =
modifier_keys_set->begin(); it != modifier_keys_set->end(); ++it) {
map<uint32, commands::KeyEvent::ModifierKey>::const_iterator item =
data.find(*it);
if (item != data.end()) {
modifier_keys_set->insert(item->second);
}
}
}
bool IsModifierToBeSentOnKeyUp(const commands::KeyEvent &key_event) {
if (key_event.modifier_keys_size() == 0) {
return false;
}
if (key_event.modifier_keys_size() == 1 &&
key_event.modifier_keys(0) == commands::KeyEvent::CAPS) {
return false;
}
return true;
}
} // namespace
KeyEventHandler::KeyEventHandler() : key_translator_(new KeyTranslator) {
Clear();
}
bool KeyEventHandler::GetKeyEvent(
guint keyval, guint keycode, guint modifiers,
config::Config::PreeditMethod preedit_method,
bool layout_is_jp, commands::KeyEvent *key) {
DCHECK(key);
key->Clear();
if (!key_translator_->Translate(
keyval, keycode, modifiers, preedit_method, layout_is_jp, key)) {
LOG(ERROR) << "Translate failed";
return false;
}
const bool is_key_up = ((modifiers & IBUS_RELEASE_MASK) != 0);
return ProcessModifiers(is_key_up, keyval, key);
}
void KeyEventHandler::Clear() {
is_non_modifier_key_pressed_ = false;
currently_pressed_modifiers_.clear();
modifiers_to_be_sent_.clear();
}
bool KeyEventHandler::ProcessModifiers(bool is_key_up, guint keyval,
commands::KeyEvent *key_event) {
// Manage modifier key event.
// Modifier key event is sent on key up if non-modifier key has not been
// pressed since key down of modifier keys and no modifier keys are pressed
// anymore.
// Following examples are expected behaviors.
//
// E.g.) Shift key is special. If Shift + printable key is pressed, key event
// does NOT have shift modifiers. It is handled by KeyTranslator class.
// <Event from ibus> <Event to server>
// Shift down | None
// "a" down | A
// "a" up | None
// Shift up | None
//
// E.g.) Usual key is sent on key down. Modifier keys are not sent if usual
// key is sent.
// <Event from ibus> <Event to server>
// Ctrl down | None
// "a" down | Ctrl+a
// "a" up | None
// Ctrl up | None
//
// E.g.) Modifier key is sent on key up.
// <Event from ibus> <Event to server>
// Shift down | None
// Shift up | Shift
//
// E.g.) Multiple modifier keys are sent on the last key up.
// <Event from ibus> <Event to server>
// Shift down | None
// Control down | None
// Shift up | None
// Control up | Control+Shift
//
// Essentialy we cannot handle modifier key evnet perfectly because
// - We cannot get current keyboard status with ibus. If some modifiers
// are pressed or released without focusing the target window, we
// cannot handle it.
// E.g.)
// <Event from ibus> <Event to server>
// Ctrl down | None
// (focuses out, Ctrl up, focuses in)
// Shift down | None
// Shift up | None (But we should send Shift key)
// To avoid a inconsistent state as much as possible, we clear states
// when key event without modifier keys is sent.
const bool is_modifier_only =
!(key_event->has_key_code() || key_event->has_special_key());
// We may get only up/down key event when a user moves a focus.
// This code handles such situation as much as possible.
// This code has a bug. If we send Shift + 'a', KeyTranslator removes a shift
// modifier and converts 'a' to 'A'. This codes does NOT consider these
// situation since we don't have enough data to handle it.
// TODO(hsumita): Moves the logic about a handling of Shift or Caps keys from
// KeyTranslator to MozcEngine.
if (key_event->modifier_keys_size() == 0) {
Clear();
}
if (!currently_pressed_modifiers_.empty() && !is_modifier_only) {
is_non_modifier_key_pressed_ = true;
}
if (is_non_modifier_key_pressed_) {
modifiers_to_be_sent_.clear();
}
if (is_key_up) {
currently_pressed_modifiers_.erase(keyval);
if (!is_modifier_only) {
return false;
}
if (!currently_pressed_modifiers_.empty() ||
modifiers_to_be_sent_.empty()) {
is_non_modifier_key_pressed_ = false;
return false;
}
if (is_non_modifier_key_pressed_) {
return false;
}
DCHECK(!is_non_modifier_key_pressed_);
// Modifier key event fires
key_event->mutable_modifier_keys()->Clear();
for (set<commands::KeyEvent::ModifierKey>::const_iterator it =
modifiers_to_be_sent_.begin();
it != modifiers_to_be_sent_.end();
++it) {
key_event->add_modifier_keys(*it);
}
modifiers_to_be_sent_.clear();
} else if (is_modifier_only) {
// TODO(hsumita): Supports a key sequence below.
// - Ctrl down
// - a down
// - Alt down
// We should add Alt key to |currently_pressed_modifiers|, but current
// implementation does NOT do it.
if (currently_pressed_modifiers_.empty() ||
!modifiers_to_be_sent_.empty()) {
for (size_t i = 0; i < key_event->modifier_keys_size(); ++i) {
modifiers_to_be_sent_.insert(key_event->modifier_keys(i));
}
AddAdditionalModifiers(&modifiers_to_be_sent_);
}
currently_pressed_modifiers_.insert(keyval);
return false;
}
// Clear modifier data just in case if |key| has no modifier keys.
if (!IsModifierToBeSentOnKeyUp(*key_event)) {
Clear();
}
return true;
}
} // namespace ibus
} // namespace mozc