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 13 namespace metro_driver { 14 namespace { 15 16 ImePopupObserver* g_observer_ = NULL; 17 HWINEVENTHOOK g_hook_handle_ = NULL; 18 19 void 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 47 void 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 71 void 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