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/input_source.h" 6 7 #include <atlbase.h> 8 #include <atlcom.h> 9 #include <msctf.h> 10 11 #include "base/bind.h" 12 #include "base/callback.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/observer_list.h" 16 #include "base/win/scoped_comptr.h" 17 #include "ui/base/win/atl_module.h" 18 #include "win8/metro_driver/ime/input_source_observer.h" 19 20 namespace metro_driver { 21 namespace { 22 23 // An implementation of ITfLanguageProfileNotifySink interface, which will be 24 // used to receive notifications when the text input source is changed. 25 class ATL_NO_VTABLE InputSourceMonitor 26 : public CComObjectRootEx<CComMultiThreadModel>, 27 public ITfLanguageProfileNotifySink { 28 public: 29 InputSourceMonitor() 30 : cookie_(TF_INVALID_COOKIE) { 31 } 32 33 BEGIN_COM_MAP(InputSourceMonitor) 34 COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink) 35 END_COM_MAP() 36 37 bool Initialize(ITfSource* source) { 38 DWORD cookie = TF_INVALID_COOKIE; 39 HRESULT hr = source->AdviseSink(IID_ITfLanguageProfileNotifySink, 40 this, 41 &cookie); 42 if (FAILED(hr)) { 43 LOG(ERROR) << "ITfSource::AdviseSink failed. hr = " << hr; 44 return false; 45 } 46 cookie_ = cookie; 47 source_ = source; 48 return true; 49 } 50 51 void SetCallback(base::Closure on_language_chanaged) { 52 on_language_chanaged_ = on_language_chanaged; 53 } 54 55 void Unadvise() { 56 if (cookie_ == TF_INVALID_COOKIE || !source_) 57 return; 58 if (FAILED(source_->UnadviseSink(cookie_))) 59 return; 60 cookie_ = TF_INVALID_COOKIE; 61 source_.Release(); 62 } 63 64 private: 65 // ITfLanguageProfileNotifySink overrides: 66 STDMETHOD(OnLanguageChange)(LANGID langid, BOOL *accept) OVERRIDE { 67 if (!accept) 68 return E_INVALIDARG; 69 *accept = TRUE; 70 return S_OK; 71 } 72 73 STDMETHOD(OnLanguageChanged)() OVERRIDE { 74 if (!on_language_chanaged_.is_null()) 75 on_language_chanaged_.Run(); 76 return S_OK; 77 } 78 79 base::Closure on_language_chanaged_; 80 base::win::ScopedComPtr<ITfSource> source_; 81 DWORD cookie_; 82 83 DISALLOW_COPY_AND_ASSIGN(InputSourceMonitor); 84 }; 85 86 class InputSourceImpl : public InputSource { 87 public: 88 InputSourceImpl(ITfInputProcessorProfileMgr* profile_manager, 89 InputSourceMonitor* monitor) 90 : profile_manager_(profile_manager), 91 monitor_(monitor) { 92 monitor_->SetCallback(base::Bind(&InputSourceImpl::OnLanguageChanged, 93 base::Unretained(this))); 94 } 95 virtual ~InputSourceImpl() { 96 monitor_->SetCallback(base::Closure()); 97 monitor_->Unadvise(); 98 } 99 100 private: 101 // InputSource overrides. 102 virtual bool GetActiveSource(LANGID* langid, bool* is_ime) OVERRIDE { 103 TF_INPUTPROCESSORPROFILE profile = {}; 104 HRESULT hr = profile_manager_->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, 105 &profile); 106 if (FAILED(hr)) { 107 LOG(ERROR) << "ITfInputProcessorProfileMgr::GetActiveProfile failed." 108 << " hr = " << hr; 109 return false; 110 } 111 *langid = profile.langid; 112 *is_ime = profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR; 113 return true; 114 } 115 virtual void AddObserver(InputSourceObserver* observer) OVERRIDE { 116 observer_list_.AddObserver(observer); 117 } 118 virtual void RemoveObserver(InputSourceObserver* observer) OVERRIDE { 119 observer_list_.RemoveObserver(observer); 120 } 121 void OnLanguageChanged() { 122 FOR_EACH_OBSERVER(InputSourceObserver, 123 observer_list_, 124 OnInputSourceChanged()); 125 } 126 127 base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager_; 128 scoped_refptr<InputSourceMonitor> monitor_; 129 ObserverList<InputSourceObserver> observer_list_; 130 131 DISALLOW_COPY_AND_ASSIGN(InputSourceImpl); 132 }; 133 134 } // namespace 135 136 // static 137 scoped_ptr<InputSource> InputSource::Create() { 138 ui::win::CreateATLModuleIfNeeded(); 139 140 base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager; 141 HRESULT hr = profile_manager.CreateInstance(CLSID_TF_InputProcessorProfiles); 142 if (FAILED(hr)) { 143 LOG(ERROR) << "Failed to instantiate CLSID_TF_InputProcessorProfiles." 144 << " hr = " << hr; 145 return scoped_ptr<InputSource>(); 146 } 147 base::win::ScopedComPtr<ITfSource> profiles_source; 148 hr = profiles_source.QueryFrom(profile_manager); 149 if (FAILED(hr)) { 150 LOG(ERROR) << "QueryFrom to ITfSource failed. hr = " << hr; 151 return scoped_ptr<InputSource>(); 152 } 153 154 CComObject<InputSourceMonitor>* monitor = NULL; 155 hr = CComObject<InputSourceMonitor>::CreateInstance(&monitor); 156 if (FAILED(hr)) { 157 LOG(ERROR) << "CComObject<InputSourceMonitor>::CreateInstance failed." 158 << " hr = " << hr; 159 return scoped_ptr<InputSource>(); 160 } 161 if (!monitor->Initialize(profiles_source)) { 162 LOG(ERROR) << "Failed to initialize the monitor."; 163 return scoped_ptr<InputSource>(); 164 } 165 166 // Transfer the ownership. 167 return scoped_ptr<InputSource>(new InputSourceImpl(profile_manager, monitor)); 168 } 169 170 } // namespace metro_driver 171