1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "win8/metro_driver/ime/ime_popup_monitor.h" 6 7#include <windows.h> 8 9#include "base/logging.h" 10#include "base/message_loop/message_loop.h" 11#include "win8/metro_driver/ime/ime_popup_observer.h" 12 13namespace metro_driver { 14namespace { 15 16ImePopupObserver* g_observer_ = NULL; 17HWINEVENTHOOK g_hook_handle_ = NULL; 18 19void CALLBACK ImeEventCallback(HWINEVENTHOOK win_event_hook_handle, 20 DWORD event, 21 HWND window_handle, 22 LONG object_id, 23 LONG child_id, 24 DWORD event_thread, 25 DWORD event_time) { 26 // This function is registered to SetWinEventHook to be called back on the UI 27 // thread. 28 DCHECK(base::MessageLoopForUI::IsCurrent()); 29 30 if (!g_observer_) 31 return; 32 switch (event) { 33 case EVENT_OBJECT_IME_SHOW: 34 g_observer_->OnImePopupChanged(ImePopupObserver::kPopupShown); 35 return; 36 case EVENT_OBJECT_IME_HIDE: 37 g_observer_->OnImePopupChanged(ImePopupObserver::kPopupHidden); 38 return; 39 case EVENT_OBJECT_IME_CHANGE: 40 g_observer_->OnImePopupChanged(ImePopupObserver::kPopupUpdated); 41 return; 42 } 43} 44 45} // namespace 46 47void AddImePopupObserver(ImePopupObserver* observer) { 48 CHECK(g_observer_ == NULL) 49 << "Currently only one observer is supported at the same time."; 50 g_observer_ = observer; 51 52 // IMEs running under immersive mode are supposed to generate WinEvent 53 // whenever their popup UI such as candidate window is shown, updated, and 54 // hidden to support accessibility applications. 55 // http://msdn.microsoft.com/en-us/library/windows/apps/hh967425.aspx#accessibility 56 // Note that there is another mechanism in TSF, called ITfUIElementSink, to 57 // subscribe when the visibility of an IME's UI element is changed. However, 58 // MS-IME running under immersive mode does not fully support this API. 59 // Thus, WinEvent is more reliable for this purpose. 60 g_hook_handle_ = SetWinEventHook( 61 EVENT_OBJECT_IME_SHOW, 62 EVENT_OBJECT_IME_CHANGE, 63 NULL, 64 ImeEventCallback, 65 GetCurrentProcessId(), // monitor the metro_driver process only 66 0, // hook all threads because MS-IME emits WinEvent in a worker thread 67 WINEVENT_OUTOFCONTEXT); // allows us to receive message in the UI thread 68 LOG_IF(ERROR, !g_hook_handle_) << "SetWinEventHook failed."; 69} 70 71void RemoveImePopupObserver(ImePopupObserver* observer) { 72 if (g_observer_ != observer) 73 return; 74 g_observer_ = NULL; 75 if (!g_hook_handle_) 76 return; 77 const bool unhook_succeeded = !!UnhookWinEvent(g_hook_handle_); 78 LOG_IF(ERROR, !unhook_succeeded) << "UnhookWinEvent failed."; 79 g_hook_handle_ = NULL; 80} 81 82} // namespace metro_driver 83