Home | History | Annotate | Download | only in ime
      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