blob: a478607aad4e6d7e45dbb125ca465c0c59d46dd6 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "win8/metro_driver/ime/ime_popup_monitor.h"
#include <windows.h>
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "win8/metro_driver/ime/ime_popup_observer.h"
namespace metro_driver {
namespace {
ImePopupObserver* g_observer_ = NULL;
HWINEVENTHOOK g_hook_handle_ = NULL;
void CALLBACK ImeEventCallback(HWINEVENTHOOK win_event_hook_handle,
DWORD event,
HWND window_handle,
LONG object_id,
LONG child_id,
DWORD event_thread,
DWORD event_time) {
// This function is registered to SetWinEventHook to be called back on the UI
// thread.
DCHECK(base::MessageLoopForUI::IsCurrent());
if (!g_observer_)
return;
switch (event) {
case EVENT_OBJECT_IME_SHOW:
g_observer_->OnImePopupChanged(ImePopupObserver::kPopupShown);
return;
case EVENT_OBJECT_IME_HIDE:
g_observer_->OnImePopupChanged(ImePopupObserver::kPopupHidden);
return;
case EVENT_OBJECT_IME_CHANGE:
g_observer_->OnImePopupChanged(ImePopupObserver::kPopupUpdated);
return;
}
}
} // namespace
void AddImePopupObserver(ImePopupObserver* observer) {
CHECK(g_observer_ == NULL)
<< "Currently only one observer is supported at the same time.";
g_observer_ = observer;
// IMEs running under immersive mode are supposed to generate WinEvent
// whenever their popup UI such as candidate window is shown, updated, and
// hidden to support accessibility applications.
// http://msdn.microsoft.com/en-us/library/windows/apps/hh967425.aspx#accessibility
// Note that there is another mechanism in TSF, called ITfUIElementSink, to
// subscribe when the visibility of an IME's UI element is changed. However,
// MS-IME running under immersive mode does not fully support this API.
// Thus, WinEvent is more reliable for this purpose.
g_hook_handle_ = SetWinEventHook(
EVENT_OBJECT_IME_SHOW,
EVENT_OBJECT_IME_CHANGE,
NULL,
ImeEventCallback,
GetCurrentProcessId(), // monitor the metro_driver process only
0, // hook all threads because MS-IME emits WinEvent in a worker thread
WINEVENT_OUTOFCONTEXT); // allows us to receive message in the UI thread
LOG_IF(ERROR, !g_hook_handle_) << "SetWinEventHook failed.";
}
void RemoveImePopupObserver(ImePopupObserver* observer) {
if (g_observer_ != observer)
return;
g_observer_ = NULL;
if (!g_hook_handle_)
return;
const bool unhook_succeeded = !!UnhookWinEvent(g_hook_handle_);
LOG_IF(ERROR, !unhook_succeeded) << "UnhookWinEvent failed.";
g_hook_handle_ = NULL;
}
} // namespace metro_driver