// 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/input_source.h" #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "base/win/scoped_comptr.h" #include "ui/base/win/atl_module.h" #include "win8/metro_driver/ime/input_source_observer.h" namespace metro_driver { namespace { // An implementation of ITfLanguageProfileNotifySink interface, which will be // used to receive notifications when the text input source is changed. class ATL_NO_VTABLE InputSourceMonitor : public CComObjectRootEx, public ITfLanguageProfileNotifySink { public: InputSourceMonitor() : cookie_(TF_INVALID_COOKIE) { } BEGIN_COM_MAP(InputSourceMonitor) COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink) END_COM_MAP() bool Initialize(ITfSource* source) { DWORD cookie = TF_INVALID_COOKIE; HRESULT hr = source->AdviseSink(IID_ITfLanguageProfileNotifySink, this, &cookie); if (FAILED(hr)) { LOG(ERROR) << "ITfSource::AdviseSink failed. hr = " << hr; return false; } cookie_ = cookie; source_ = source; return true; } void SetCallback(base::Closure on_language_chanaged) { on_language_chanaged_ = on_language_chanaged; } void Unadvise() { if (cookie_ == TF_INVALID_COOKIE || !source_.get()) return; if (FAILED(source_->UnadviseSink(cookie_))) return; cookie_ = TF_INVALID_COOKIE; source_.Release(); } private: // ITfLanguageProfileNotifySink overrides: STDMETHOD(OnLanguageChange)(LANGID langid, BOOL *accept) override { if (!accept) return E_INVALIDARG; *accept = TRUE; return S_OK; } STDMETHOD(OnLanguageChanged)() override { if (!on_language_chanaged_.is_null()) on_language_chanaged_.Run(); return S_OK; } base::Closure on_language_chanaged_; base::win::ScopedComPtr source_; DWORD cookie_; DISALLOW_COPY_AND_ASSIGN(InputSourceMonitor); }; class InputSourceImpl : public InputSource { public: InputSourceImpl(ITfInputProcessorProfileMgr* profile_manager, InputSourceMonitor* monitor) : profile_manager_(profile_manager), monitor_(monitor) { monitor_->SetCallback(base::Bind(&InputSourceImpl::OnLanguageChanged, base::Unretained(this))); } ~InputSourceImpl() override { monitor_->SetCallback(base::Closure()); monitor_->Unadvise(); } private: // InputSource overrides. bool GetActiveSource(LANGID* langid, bool* is_ime) override { TF_INPUTPROCESSORPROFILE profile = {}; HRESULT hr = profile_manager_->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); if (FAILED(hr)) { LOG(ERROR) << "ITfInputProcessorProfileMgr::GetActiveProfile failed." << " hr = " << hr; return false; } *langid = profile.langid; *is_ime = profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR; return true; } void AddObserver(InputSourceObserver* observer) override { observer_list_.AddObserver(observer); } void RemoveObserver(InputSourceObserver* observer) override { observer_list_.RemoveObserver(observer); } void OnLanguageChanged() { FOR_EACH_OBSERVER(InputSourceObserver, observer_list_, OnInputSourceChanged()); } base::win::ScopedComPtr profile_manager_; scoped_refptr monitor_; base::ObserverList observer_list_; DISALLOW_COPY_AND_ASSIGN(InputSourceImpl); }; } // namespace // static scoped_ptr InputSource::Create() { ui::win::CreateATLModuleIfNeeded(); base::win::ScopedComPtr profile_manager; HRESULT hr = profile_manager.CreateInstance(CLSID_TF_InputProcessorProfiles); if (FAILED(hr)) { LOG(ERROR) << "Failed to instantiate CLSID_TF_InputProcessorProfiles." << " hr = " << hr; return scoped_ptr(); } base::win::ScopedComPtr profiles_source; hr = profiles_source.QueryFrom(profile_manager.get()); if (FAILED(hr)) { LOG(ERROR) << "QueryFrom to ITfSource failed. hr = " << hr; return scoped_ptr(); } CComObject* monitor = NULL; hr = CComObject::CreateInstance(&monitor); if (FAILED(hr)) { LOG(ERROR) << "CComObject::CreateInstance failed." << " hr = " << hr; return scoped_ptr(); } if (!monitor->Initialize(profiles_source.get())) { LOG(ERROR) << "Failed to initialize the monitor."; return scoped_ptr(); } // Transfer the ownership. return scoped_ptr( new InputSourceImpl(profile_manager.get(), monitor)); } } // namespace metro_driver