1 // Copyright 2010-2012, Google Inc.
2 // Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include "unix/fcitx/fcitx_key_event_handler.h"
32
33 #include <map>
34
35 #include "base/logging.h"
36 #include "base/singleton.h"
37
38 namespace mozc {
39 namespace fcitx {
40
41 namespace {
42 // TODO(hsumita): Removes this class, and moves |data_| into member
43 // variables of KeyEventhandler.
44 class AdditionalModifiersData {
45 public:
AdditionalModifiersData()46 AdditionalModifiersData() {
47 data_[commands::KeyEvent::LEFT_ALT] = commands::KeyEvent::ALT;
48 data_[commands::KeyEvent::RIGHT_ALT] = commands::KeyEvent::ALT;
49 data_[commands::KeyEvent::LEFT_CTRL] = commands::KeyEvent::CTRL;
50 data_[commands::KeyEvent::RIGHT_CTRL] = commands::KeyEvent::CTRL;
51 data_[commands::KeyEvent::LEFT_SHIFT] = commands::KeyEvent::SHIFT;
52 data_[commands::KeyEvent::RIGHT_SHIFT] = commands::KeyEvent::SHIFT;
53 }
data()54 const std::map<uint32, commands::KeyEvent::ModifierKey> &data() {
55 return data_;
56 }
57
58 private:
59 std::map<uint32, commands::KeyEvent::ModifierKey> data_;
60 };
61
62 // TODO(hsumita): Moves this function into member functions of
63 // KeyEventHandler.
AddAdditionalModifiers(std::set<commands::KeyEvent::ModifierKey> * modifier_keys_set)64 void AddAdditionalModifiers(
65 std::set<commands::KeyEvent::ModifierKey> *modifier_keys_set) {
66 DCHECK(modifier_keys_set);
67
68 const std::map<uint32, commands::KeyEvent::ModifierKey> &data =
69 Singleton<AdditionalModifiersData>::get()->data();
70
71 // Adds MODIFIER if there are (LEFT|RIGHT)_MODIFIER like LEFT_SHIFT.
72 for (std::set<commands::KeyEvent::ModifierKey>::const_iterator it =
73 modifier_keys_set->begin(); it != modifier_keys_set->end(); ++it) {
74 std::map<uint32, commands::KeyEvent::ModifierKey>::const_iterator item =
75 data.find(*it);
76 if (item != data.end()) {
77 modifier_keys_set->insert(item->second);
78 }
79 }
80 }
81
IsModifierToBeSentOnKeyUp(const commands::KeyEvent & key_event)82 bool IsModifierToBeSentOnKeyUp(const commands::KeyEvent &key_event) {
83 if (key_event.modifier_keys_size() == 0) {
84 return false;
85 }
86
87 if (key_event.modifier_keys_size() == 1 &&
88 key_event.modifier_keys(0) == commands::KeyEvent::CAPS) {
89 return false;
90 }
91
92 return true;
93 }
94 } // namespace
95
KeyEventHandler()96 KeyEventHandler::KeyEventHandler() : key_translator_(new KeyTranslator) {
97 Clear();
98 }
99
GetKeyEvent(FcitxKeySym keyval,uint32 keycode,uint32 modifiers,config::Config::PreeditMethod preedit_method,bool layout_is_jp,bool is_key_up,commands::KeyEvent * key)100 bool KeyEventHandler::GetKeyEvent(
101 FcitxKeySym keyval, uint32 keycode, uint32 modifiers,
102 config::Config::PreeditMethod preedit_method,
103 bool layout_is_jp, bool is_key_up, commands::KeyEvent *key) {
104 DCHECK(key);
105 key->Clear();
106
107 if (!key_translator_->Translate(
108 keyval, keycode, modifiers, preedit_method, layout_is_jp, key)) {
109 LOG(ERROR) << "Translate failed";
110 return false;
111 }
112
113 return ProcessModifiers(is_key_up, keyval, key);
114 }
115
Clear()116 void KeyEventHandler::Clear() {
117 is_non_modifier_key_pressed_ = false;
118 currently_pressed_modifiers_.clear();
119 modifiers_to_be_sent_.clear();
120 }
121
ProcessModifiers(bool is_key_up,uint32 keyval,commands::KeyEvent * key_event)122 bool KeyEventHandler::ProcessModifiers(bool is_key_up, uint32 keyval,
123 commands::KeyEvent *key_event) {
124 // Manage modifier key event.
125 // Modifier key event is sent on key up if non-modifier key has not been
126 // pressed since key down of modifier keys and no modifier keys are pressed
127 // anymore.
128 // Following examples are expected behaviors.
129 //
130 // E.g.) Shift key is special. If Shift + printable key is pressed, key event
131 // does NOT have shift modifiers. It is handled by KeyTranslator class.
132 // <Event from ibus> <Event to server>
133 // Shift down | None
134 // "a" down | A
135 // "a" up | None
136 // Shift up | None
137 //
138 // E.g.) Usual key is sent on key down. Modifier keys are not sent if usual
139 // key is sent.
140 // <Event from ibus> <Event to server>
141 // Ctrl down | None
142 // "a" down | Ctrl+a
143 // "a" up | None
144 // Ctrl up | None
145 //
146 // E.g.) Modifier key is sent on key up.
147 // <Event from ibus> <Event to server>
148 // Shift down | None
149 // Shift up | Shift
150 //
151 // E.g.) Multiple modifier keys are sent on the last key up.
152 // <Event from ibus> <Event to server>
153 // Shift down | None
154 // Control down | None
155 // Shift up | None
156 // Control up | Control+Shift
157 //
158 // Essentialy we cannot handle modifier key evnet perfectly because
159 // - We cannot get current keyboard status with ibus. If some modifiers
160 // are pressed or released without focusing the target window, we
161 // cannot handle it.
162 // E.g.)
163 // <Event from ibus> <Event to server>
164 // Ctrl down | None
165 // (focuses out, Ctrl up, focuses in)
166 // Shift down | None
167 // Shift up | None (But we should send Shift key)
168 // To avoid a inconsistent state as much as possible, we clear states
169 // when key event without modifier keys is sent.
170
171 const bool is_modifier_only =
172 !(key_event->has_key_code() || key_event->has_special_key());
173
174 // We may get only up/down key event when a user moves a focus.
175 // This code handles such situation as much as possible.
176 // This code has a bug. If we send Shift + 'a', KeyTranslator removes a shift
177 // modifier and converts 'a' to 'A'. This codes does NOT consider these
178 // situation since we don't have enough data to handle it.
179 // TODO(hsumita): Moves the logic about a handling of Shift or Caps keys from
180 // KeyTranslator to MozcEngine.
181 if (key_event->modifier_keys_size() == 0) {
182 Clear();
183 }
184
185 if (!currently_pressed_modifiers_.empty() && !is_modifier_only) {
186 is_non_modifier_key_pressed_ = true;
187 }
188 if (is_non_modifier_key_pressed_) {
189 modifiers_to_be_sent_.clear();
190 }
191
192 if (is_key_up) {
193 currently_pressed_modifiers_.erase(keyval);
194 if (!is_modifier_only) {
195 return false;
196 }
197 if (!currently_pressed_modifiers_.empty() ||
198 modifiers_to_be_sent_.empty()) {
199 is_non_modifier_key_pressed_ = false;
200 return false;
201 }
202 if (is_non_modifier_key_pressed_) {
203 return false;
204 }
205 DCHECK(!is_non_modifier_key_pressed_);
206
207 // Modifier key event fires
208 key_event->mutable_modifier_keys()->Clear();
209 for (std::set<commands::KeyEvent::ModifierKey>::const_iterator it =
210 modifiers_to_be_sent_.begin();
211 it != modifiers_to_be_sent_.end();
212 ++it) {
213 key_event->add_modifier_keys(*it);
214 }
215 modifiers_to_be_sent_.clear();
216 } else if (is_modifier_only) {
217 // TODO(hsumita): Supports a key sequence below.
218 // - Ctrl down
219 // - a down
220 // - Alt down
221 // We should add Alt key to |currently_pressed_modifiers|, but current
222 // implementation does NOT do it.
223 if (currently_pressed_modifiers_.empty() ||
224 !modifiers_to_be_sent_.empty()) {
225 for (size_t i = 0; i < key_event->modifier_keys_size(); ++i) {
226 modifiers_to_be_sent_.insert(key_event->modifier_keys(i));
227 }
228 AddAdditionalModifiers(&modifiers_to_be_sent_);
229 }
230 currently_pressed_modifiers_.insert(keyval);
231 return false;
232 }
233
234 // Clear modifier data just in case if |key| has no modifier keys.
235 if (!IsModifierToBeSentOnKeyUp(*key_event)) {
236 Clear();
237 }
238
239 return true;
240 }
241
242 } // namespace ibus
243 } // namespace mozc
244