| // 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 "win32/base/deleter.h" |
| |
| #include <deque> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "session/commands.pb.h" |
| #include "win32/base/input_state.h" |
| #include "win32/base/keyboard.h" |
| |
| namespace mozc { |
| namespace win32 { |
| |
| class VKBackBasedDeleterQueue |
| : public deque<pair<VKBackBasedDeleter::DeletionWaitState, |
| VKBackBasedDeleter::ClientAction> > { |
| }; |
| |
| VKBackBasedDeleter::VKBackBasedDeleter() |
| : wait_queue_(new VKBackBasedDeleterQueue), |
| keyboard_(Win32KeyboardInterface::CreateDefault()), |
| pending_ime_state_(new InputState()), |
| pending_output_(new mozc::commands::Output()) { |
| } |
| |
| VKBackBasedDeleter::VKBackBasedDeleter(Win32KeyboardInterface *keyboard_mock) |
| : wait_queue_(new VKBackBasedDeleterQueue), |
| keyboard_(keyboard_mock), |
| pending_ime_state_(new InputState()), |
| pending_output_(new mozc::commands::Output()) { |
| } |
| |
| VKBackBasedDeleter::~VKBackBasedDeleter() { |
| } |
| |
| void VKBackBasedDeleter::BeginDeletion(int deletion_count, |
| const mozc::commands::Output &output, |
| const InputState &ime_state) { |
| vector<INPUT> inputs; |
| |
| wait_queue_->clear(); |
| *pending_ime_state_ = InputState(); |
| pending_output_->Clear(); |
| |
| if (deletion_count == 0) { |
| return; |
| } |
| |
| *pending_ime_state_ = ime_state; |
| pending_output_->CopyFrom(output); |
| |
| wait_queue_->push_back(make_pair( |
| WAIT_INITIAL_VK_BACK_TESTDOWN, SEND_KEY_TO_APPLICATION)); |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_TESTUP, SEND_KEY_TO_APPLICATION)); |
| |
| for (int i = 1; i < deletion_count; ++i) { |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_TESTDOWN, SEND_KEY_TO_APPLICATION)); |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_TESTUP, SEND_KEY_TO_APPLICATION)); |
| } |
| |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_TESTDOWN, CONSUME_KEY_BUT_NEVER_SEND_TO_SERVER)); |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_DOWN, APPLY_PENDING_STATUS)); |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_TESTUP, CONSUME_KEY_BUT_NEVER_SEND_TO_SERVER)); |
| wait_queue_->push_back(make_pair( |
| WAIT_VK_BACK_UP, |
| CALL_END_DELETION_BUT_NEVER_SEND_TO_SERVER)); |
| |
| const KEYBDINPUT keyboard_input = {VK_BACK, 0, 0, 0, 0}; |
| INPUT keydown = {}; |
| keydown.type = INPUT_KEYBOARD; |
| keydown.ki = keyboard_input; |
| |
| INPUT keyup = keydown; |
| keyup.type = INPUT_KEYBOARD; |
| keyup.ki.dwFlags = KEYEVENTF_KEYUP; |
| |
| for (size_t i = 0; i < deletion_count; ++i) { |
| inputs.push_back(keydown); |
| inputs.push_back(keyup); |
| } |
| |
| inputs.push_back(keydown); // Sentinel Keydown |
| inputs.push_back(keyup); // Sentinel Keyup |
| |
| UnsetModifiers(); |
| keyboard_->SendInput(inputs); |
| } |
| |
| VKBackBasedDeleter::ClientAction VKBackBasedDeleter::OnKeyEvent( |
| UINT vk, bool is_keydown, bool is_test_key) { |
| // Default action when no auto-deletion is ongoing. |
| if (!IsDeletionOngoing()) { |
| return DO_DEFAULT_ACTION; |
| } |
| |
| // Hereafter, auto-deletion is ongoing. |
| const pair<DeletionWaitState, ClientAction> next = |
| wait_queue_->front(); |
| if (next.first == WAIT_INITIAL_VK_BACK_TESTDOWN) { |
| if ((vk == VK_BACK) && is_keydown && is_test_key) { |
| wait_queue_->pop_front(); |
| return next.second; |
| } else { |
| // Do not pop front. |
| return DO_DEFAULT_ACTION; |
| } |
| } |
| |
| wait_queue_->pop_front(); |
| |
| bool matched = false; |
| switch (next.first) { |
| case WAIT_VK_BACK_TESTDOWN: |
| matched = ((vk == VK_BACK) && is_keydown && is_test_key); |
| break; |
| case WAIT_VK_BACK_TESTUP: |
| matched = ((vk == VK_BACK) && !is_keydown && is_test_key); |
| break; |
| case WAIT_VK_BACK_DOWN: |
| matched = ((vk == VK_BACK) && is_keydown && !is_test_key); |
| break; |
| case WAIT_VK_BACK_UP: |
| matched = ((vk == VK_BACK) && !is_keydown && !is_test_key); |
| break; |
| default: |
| DLOG(FATAL) << "unexpected state found"; |
| break; |
| } |
| if (matched) { |
| return next.second; |
| } |
| |
| return CALL_END_DELETION_THEN_DO_DEFAULT_ACTION; |
| } |
| |
| void VKBackBasedDeleter::UnsetModifiers() { |
| // Ensure that Shift, Control, and Alt key do not affect generated key |
| // events. See b/3419452 for details. |
| // TODO(yukawa): Use 3rd argument of ImeToAsciiEx to obtain the keyboard |
| // state instead of GetKeyboardState API. |
| // TODO(yukawa): Update VKBackBasedDeleter to consider this case. |
| KeyboardStatus keyboard_state; |
| if (!keyboard_->GetKeyboardState(&keyboard_state)) { |
| return; |
| } |
| |
| // If any side-effect is found, we might want to clear only the highest |
| // bit. |
| const BYTE kUnsetState = 0; |
| bool to_be_updated = false; |
| if (keyboard_state.IsPressed(VK_SHIFT)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_SHIFT, kUnsetState); |
| } |
| if (keyboard_state.IsPressed(VK_CONTROL)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_CONTROL, kUnsetState); |
| } |
| if (keyboard_state.IsPressed(VK_MENU)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_MENU, kUnsetState); |
| } |
| if (to_be_updated) { |
| keyboard_->SetKeyboardState(keyboard_state); |
| } |
| } |
| |
| void VKBackBasedDeleter::EndDeletion() { |
| bool to_be_updated = false; |
| KeyboardStatus keyboard_state; |
| if (!keyboard_->GetKeyboardState(&keyboard_state)) { |
| return; |
| } |
| |
| const BYTE kPressed = 0x80; |
| if (keyboard_->AsyncIsKeyPressed(VK_SHIFT)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_SHIFT, kPressed); |
| } |
| if (keyboard_->AsyncIsKeyPressed(VK_CONTROL)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_CONTROL, kPressed); |
| } |
| if (keyboard_->AsyncIsKeyPressed(VK_MENU)) { |
| to_be_updated = true; |
| keyboard_state.SetState(VK_MENU, kPressed); |
| } |
| if (to_be_updated) { |
| keyboard_->SetKeyboardState(keyboard_state); |
| } |
| wait_queue_->clear(); |
| } |
| |
| bool VKBackBasedDeleter::IsDeletionOngoing() const { |
| return !wait_queue_->empty(); |
| } |
| |
| const mozc::commands::Output &VKBackBasedDeleter::pending_output() const { |
| return *pending_output_; |
| } |
| |
| const InputState &VKBackBasedDeleter::pending_ime_state() const { |
| return *pending_ime_state_; |
| } |
| |
| } // namespace win32 |
| } // namespace mozc |