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.
5 #include "ui/base/ime/remote_input_method_win.h"
7 #include "base/observer_list.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/metro.h"
10 #include "base/win/scoped_handle.h"
11 #include "ui/base/ime/input_method.h"
12 #include "ui/base/ime/input_method_delegate.h"
13 #include "ui/base/ime/input_method_observer.h"
14 #include "ui/base/ime/remote_input_method_delegate_win.h"
15 #include "ui/base/ime/text_input_client.h"
16 #include "ui/base/ime/win/tsf_input_scope.h"
17 #include "ui/events/event.h"
18 #include "ui/events/event_utils.h"
19 #include "ui/gfx/rect.h"
24 const LANGID kFallbackLangID
=
25 MAKELANGID(LANG_NEUTRAL
, SUBLANG_UI_CUSTOM_DEFAULT
);
27 InputMethod
* g_public_interface_
= NULL
;
28 RemoteInputMethodPrivateWin
* g_private_interface_
= NULL
;
30 void RegisterInstance(InputMethod
* public_interface
,
31 RemoteInputMethodPrivateWin
* private_interface
) {
32 CHECK(g_public_interface_
== NULL
)
33 << "Only one instance is supported at the same time";
34 CHECK(g_private_interface_
== NULL
)
35 << "Only one instance is supported at the same time";
36 g_public_interface_
= public_interface
;
37 g_private_interface_
= private_interface
;
40 RemoteInputMethodPrivateWin
* GetPrivate(InputMethod
* public_interface
) {
41 if (g_public_interface_
!= public_interface
)
43 return g_private_interface_
;
46 void UnregisterInstance(InputMethod
* public_interface
) {
47 RemoteInputMethodPrivateWin
* private_interface
= GetPrivate(public_interface
);
48 if (g_public_interface_
== public_interface
&&
49 g_private_interface_
== private_interface
) {
50 g_public_interface_
= NULL
;
51 g_private_interface_
= NULL
;
55 std::string
GetLocaleString(LCID Locale_id
, LCTYPE locale_type
) {
56 wchar_t buffer
[16] = {};
58 //|chars_written| includes NUL terminator.
59 const int chars_written
=
60 GetLocaleInfo(Locale_id
, locale_type
, buffer
, arraysize(buffer
));
61 if (chars_written
<= 1 || arraysize(buffer
) < chars_written
)
64 base::WideToUTF8(buffer
, chars_written
- 1, &result
);
68 std::vector
<int32
> GetInputScopesAsInt(TextInputType text_input_type
,
69 TextInputMode text_input_mode
) {
70 std::vector
<int32
> result
;
71 // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE.
72 if (text_input_type
== TEXT_INPUT_TYPE_NONE
)
75 const std::vector
<InputScope
>& input_scopes
=
76 tsf_inputscope::GetInputScopes(text_input_type
, text_input_mode
);
77 result
.reserve(input_scopes
.size());
78 for (size_t i
= 0; i
< input_scopes
.size(); ++i
)
79 result
.push_back(static_cast<int32
>(input_scopes
[i
]));
83 std::vector
<gfx::Rect
> GetCompositionCharacterBounds(
84 const TextInputClient
* client
) {
86 return std::vector
<gfx::Rect
>();
88 std::vector
<gfx::Rect
> bounds
;
89 if (client
->HasCompositionText()) {
91 if (client
->GetCompositionTextRange(&range
)) {
92 for (uint32 i
= 0; i
< range
.length(); ++i
) {
94 if (!client
->GetCompositionCharacterBounds(i
, &rect
))
96 bounds
.push_back(rect
);
101 // Use the caret bounds as a fallback if no composition character bounds is
102 // available. One typical use case is PPAPI Flash, which does not support
103 // GetCompositionCharacterBounds at all. crbug.com/133472
105 bounds
.push_back(client
->GetCaretBounds());
109 class RemoteInputMethodWin
: public InputMethod
,
110 public RemoteInputMethodPrivateWin
{
112 explicit RemoteInputMethodWin(internal::InputMethodDelegate
* delegate
)
113 : delegate_(delegate
),
114 remote_delegate_(NULL
),
115 text_input_client_(NULL
),
116 is_candidate_popup_open_(false),
118 langid_(kFallbackLangID
) {
119 RegisterInstance(this, this);
122 virtual ~RemoteInputMethodWin() {
123 FOR_EACH_OBSERVER(InputMethodObserver
,
125 OnInputMethodDestroyed(this));
126 UnregisterInstance(this);
130 // Overridden from InputMethod:
131 virtual void SetDelegate(internal::InputMethodDelegate
* delegate
) OVERRIDE
{
132 delegate_
= delegate
;
135 virtual void Init(bool focused
) OVERRIDE
{
138 virtual void OnFocus() OVERRIDE
{
141 virtual void OnBlur() OVERRIDE
{
144 virtual bool OnUntranslatedIMEMessage(const base::NativeEvent
& event
,
145 NativeEventResult
* result
) OVERRIDE
{
149 virtual void SetFocusedTextInputClient(TextInputClient
* client
) OVERRIDE
{
150 std::vector
<int32
> prev_input_scopes
;
151 std::swap(input_scopes_
, prev_input_scopes
);
152 std::vector
<gfx::Rect
> prev_bounds
;
153 std::swap(composition_character_bounds_
, prev_bounds
);
155 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
156 client
->GetTextInputMode());
157 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
160 const bool text_input_client_changed
= text_input_client_
!= client
;
161 text_input_client_
= client
;
162 if (text_input_client_changed
) {
163 FOR_EACH_OBSERVER(InputMethodObserver
,
165 OnTextInputStateChanged(client
));
168 if (!remote_delegate_
|| (prev_input_scopes
== input_scopes_
&&
169 prev_bounds
== composition_character_bounds_
))
171 remote_delegate_
->OnTextInputClientUpdated(input_scopes_
,
172 composition_character_bounds_
);
175 virtual void DetachTextInputClient(TextInputClient
* client
) OVERRIDE
{
176 if (text_input_client_
!= client
)
178 SetFocusedTextInputClient(NULL
);
181 virtual TextInputClient
* GetTextInputClient() const OVERRIDE
{
182 return text_input_client_
;
185 virtual bool DispatchKeyEvent(const ui::KeyEvent
& event
) OVERRIDE
{
186 if (event
.HasNativeEvent()) {
187 const base::NativeEvent
& native_key_event
= event
.native_event();
188 if (native_key_event
.message
!= WM_CHAR
)
190 if (!text_input_client_
)
192 text_input_client_
->InsertChar(
193 static_cast<base::char16
>(native_key_event
.wParam
),
194 ui::GetModifiersFromKeyState());
198 if (event
.is_char()) {
199 if (text_input_client_
) {
200 text_input_client_
->InsertChar(event
.key_code(),
201 ui::GetModifiersFromKeyState());
207 return delegate_
->DispatchKeyEventPostIME(event
);
210 virtual void OnTextInputTypeChanged(const TextInputClient
* client
) OVERRIDE
{
211 if (!text_input_client_
|| text_input_client_
!= client
)
213 std::vector
<int32
> prev_input_scopes
;
214 std::swap(input_scopes_
, prev_input_scopes
);
215 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
216 client
->GetTextInputMode());
217 if (input_scopes_
!= prev_input_scopes
&& remote_delegate_
) {
218 remote_delegate_
->OnTextInputClientUpdated(
219 input_scopes_
, composition_character_bounds_
);
223 virtual void OnCaretBoundsChanged(const TextInputClient
* client
) OVERRIDE
{
224 if (!text_input_client_
|| text_input_client_
!= client
)
226 std::vector
<gfx::Rect
> prev_rects
;
227 std::swap(composition_character_bounds_
, prev_rects
);
228 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
229 if (composition_character_bounds_
!= prev_rects
&& remote_delegate_
) {
230 remote_delegate_
->OnTextInputClientUpdated(
231 input_scopes_
, composition_character_bounds_
);
235 virtual void CancelComposition(const TextInputClient
* client
) OVERRIDE
{
236 if (CanSendRemoteNotification(client
))
237 remote_delegate_
->CancelComposition();
240 virtual void OnInputLocaleChanged() OVERRIDE
{
243 virtual std::string
GetInputLocale() OVERRIDE
{
244 const LCID locale_id
= MAKELCID(langid_
, SORT_DEFAULT
);
245 std::string language
=
246 GetLocaleString(locale_id
, LOCALE_SISO639LANGNAME
);
247 if (SUBLANGID(langid_
) == SUBLANG_NEUTRAL
|| language
.empty())
249 const std::string
& region
=
250 GetLocaleString(locale_id
, LOCALE_SISO3166CTRYNAME
);
253 return language
.append(1, '-').append(region
);
256 virtual bool IsActive() OVERRIDE
{
257 return true; // always turned on
260 virtual TextInputType
GetTextInputType() const OVERRIDE
{
261 return text_input_client_
? text_input_client_
->GetTextInputType()
262 : TEXT_INPUT_TYPE_NONE
;
265 virtual TextInputMode
GetTextInputMode() const OVERRIDE
{
266 return text_input_client_
? text_input_client_
->GetTextInputMode()
267 : TEXT_INPUT_MODE_DEFAULT
;
270 virtual bool CanComposeInline() const OVERRIDE
{
271 return text_input_client_
? text_input_client_
->CanComposeInline() : true;
274 virtual bool IsCandidatePopupOpen() const OVERRIDE
{
275 return is_candidate_popup_open_
;
278 virtual void ShowImeIfNeeded() OVERRIDE
{
281 virtual void AddObserver(InputMethodObserver
* observer
) OVERRIDE
{
282 observer_list_
.AddObserver(observer
);
285 virtual void RemoveObserver(InputMethodObserver
* observer
) OVERRIDE
{
286 observer_list_
.RemoveObserver(observer
);
289 // Overridden from RemoteInputMethodPrivateWin:
290 virtual void SetRemoteDelegate(
291 internal::RemoteInputMethodDelegateWin
* delegate
) OVERRIDE
{
292 remote_delegate_
= delegate
;
294 // Sync initial state.
295 if (remote_delegate_
) {
296 remote_delegate_
->OnTextInputClientUpdated(
297 input_scopes_
, composition_character_bounds_
);
301 virtual void OnCandidatePopupChanged(bool visible
) OVERRIDE
{
302 is_candidate_popup_open_
= visible
;
303 if (!text_input_client_
)
305 // TODO(kochi): Support 'update' case, in addition to show/hide.
306 // http://crbug.com/238585
308 text_input_client_
->OnCandidateWindowShown();
310 text_input_client_
->OnCandidateWindowHidden();
313 virtual void OnInputSourceChanged(LANGID langid
, bool /*is_ime*/) OVERRIDE
{
314 // Note: Currently |is_ime| is not utilized yet.
315 const bool changed
= (langid_
!= langid
);
317 if (changed
&& GetTextInputClient())
318 GetTextInputClient()->OnInputMethodChanged();
321 virtual void OnCompositionChanged(
322 const CompositionText
& composition_text
) OVERRIDE
{
323 if (!text_input_client_
)
325 text_input_client_
->SetCompositionText(composition_text
);
328 virtual void OnTextCommitted(const base::string16
& text
) OVERRIDE
{
329 if (!text_input_client_
)
331 if (text_input_client_
->GetTextInputType() == TEXT_INPUT_TYPE_NONE
) {
332 // According to the comment in text_input_client.h,
333 // TextInputClient::InsertText should never be called when the
334 // text input type is TEXT_INPUT_TYPE_NONE.
335 for (size_t i
= 0; i
< text
.size(); ++i
)
336 text_input_client_
->InsertChar(text
[i
], 0);
339 text_input_client_
->InsertText(text
);
342 bool CanSendRemoteNotification(
343 const TextInputClient
* text_input_client
) const {
344 return text_input_client_
&&
345 text_input_client_
== text_input_client
&&
349 ObserverList
<InputMethodObserver
> observer_list_
;
351 internal::InputMethodDelegate
* delegate_
;
352 internal::RemoteInputMethodDelegateWin
* remote_delegate_
;
354 TextInputClient
* text_input_client_
;
355 std::vector
<int32
> input_scopes_
;
356 std::vector
<gfx::Rect
> composition_character_bounds_
;
357 bool is_candidate_popup_open_
;
361 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin
);
366 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget
) {
367 DWORD process_id
= 0;
368 if (GetWindowThreadProcessId(widget
, &process_id
) == 0)
370 base::win::ScopedHandle
process_handle(::OpenProcess(
371 PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
, process_id
));
372 if (!process_handle
.IsValid())
374 return base::win::IsProcessImmersive(process_handle
.Get());
377 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
379 scoped_ptr
<InputMethod
> CreateRemoteInputMethodWin(
380 internal::InputMethodDelegate
* delegate
) {
381 return scoped_ptr
<InputMethod
>(new RemoteInputMethodWin(delegate
));
385 RemoteInputMethodPrivateWin
* RemoteInputMethodPrivateWin::Get(
386 InputMethod
* input_method
) {
387 return GetPrivate(input_method
);