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/command_line.h"
8 #include "base/observer_list.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/win/metro.h"
11 #include "base/win/scoped_handle.h"
12 #include "ui/base/ime/input_method.h"
13 #include "ui/base/ime/input_method_delegate.h"
14 #include "ui/base/ime/input_method_observer.h"
15 #include "ui/base/ime/remote_input_method_delegate_win.h"
16 #include "ui/base/ime/text_input_client.h"
17 #include "ui/base/ime/win/tsf_input_scope.h"
18 #include "ui/base/ui_base_switches.h"
19 #include "ui/events/event.h"
20 #include "ui/events/event_utils.h"
21 #include "ui/gfx/geometry/rect.h"
26 const LANGID kFallbackLangID
=
27 MAKELANGID(LANG_NEUTRAL
, SUBLANG_UI_CUSTOM_DEFAULT
);
29 InputMethod
* g_public_interface_
= NULL
;
30 RemoteInputMethodPrivateWin
* g_private_interface_
= NULL
;
32 void RegisterInstance(InputMethod
* public_interface
,
33 RemoteInputMethodPrivateWin
* private_interface
) {
34 CHECK(g_public_interface_
== NULL
)
35 << "Only one instance is supported at the same time";
36 CHECK(g_private_interface_
== NULL
)
37 << "Only one instance is supported at the same time";
38 g_public_interface_
= public_interface
;
39 g_private_interface_
= private_interface
;
42 RemoteInputMethodPrivateWin
* GetPrivate(InputMethod
* public_interface
) {
43 if (g_public_interface_
!= public_interface
)
45 return g_private_interface_
;
48 void UnregisterInstance(InputMethod
* public_interface
) {
49 RemoteInputMethodPrivateWin
* private_interface
= GetPrivate(public_interface
);
50 if (g_public_interface_
== public_interface
&&
51 g_private_interface_
== private_interface
) {
52 g_public_interface_
= NULL
;
53 g_private_interface_
= NULL
;
57 std::string
GetLocaleString(LCID Locale_id
, LCTYPE locale_type
) {
58 wchar_t buffer
[16] = {};
60 //|chars_written| includes NUL terminator.
61 const int chars_written
=
62 GetLocaleInfo(Locale_id
, locale_type
, buffer
, arraysize(buffer
));
63 if (chars_written
<= 1 || arraysize(buffer
) < chars_written
)
66 base::WideToUTF8(buffer
, chars_written
- 1, &result
);
70 std::vector
<int32
> GetInputScopesAsInt(TextInputType text_input_type
,
71 TextInputMode text_input_mode
) {
72 std::vector
<int32
> result
;
73 // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE.
74 if (text_input_type
== TEXT_INPUT_TYPE_NONE
)
77 const std::vector
<InputScope
>& input_scopes
=
78 tsf_inputscope::GetInputScopes(text_input_type
, text_input_mode
);
79 result
.reserve(input_scopes
.size());
80 for (size_t i
= 0; i
< input_scopes
.size(); ++i
)
81 result
.push_back(static_cast<int32
>(input_scopes
[i
]));
85 std::vector
<gfx::Rect
> GetCompositionCharacterBounds(
86 const TextInputClient
* client
) {
88 return std::vector
<gfx::Rect
>();
90 std::vector
<gfx::Rect
> bounds
;
91 if (client
->HasCompositionText()) {
93 if (client
->GetCompositionTextRange(&range
)) {
94 for (uint32 i
= 0; i
< range
.length(); ++i
) {
96 if (!client
->GetCompositionCharacterBounds(i
, &rect
))
98 bounds
.push_back(rect
);
103 // Use the caret bounds as a fallback if no composition character bounds is
104 // available. One typical use case is PPAPI Flash, which does not support
105 // GetCompositionCharacterBounds at all. crbug.com/133472
107 bounds
.push_back(client
->GetCaretBounds());
111 class RemoteInputMethodWin
: public InputMethod
,
112 public RemoteInputMethodPrivateWin
{
114 explicit RemoteInputMethodWin(internal::InputMethodDelegate
* delegate
)
115 : delegate_(delegate
),
116 remote_delegate_(NULL
),
117 text_input_client_(NULL
),
118 is_candidate_popup_open_(false),
120 langid_(kFallbackLangID
) {
121 RegisterInstance(this, this);
124 ~RemoteInputMethodWin() override
{
125 FOR_EACH_OBSERVER(InputMethodObserver
,
127 OnInputMethodDestroyed(this));
128 UnregisterInstance(this);
132 // Overridden from InputMethod:
133 void SetDelegate(internal::InputMethodDelegate
* delegate
) override
{
134 delegate_
= delegate
;
137 void OnFocus() override
{}
139 void OnBlur() override
{}
141 bool OnUntranslatedIMEMessage(const base::NativeEvent
& event
,
142 NativeEventResult
* result
) override
{
146 void SetFocusedTextInputClient(TextInputClient
* client
) override
{
147 std::vector
<int32
> prev_input_scopes
;
148 std::swap(input_scopes_
, prev_input_scopes
);
149 std::vector
<gfx::Rect
> prev_bounds
;
150 std::swap(composition_character_bounds_
, prev_bounds
);
152 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
153 client
->GetTextInputMode());
154 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
157 const bool text_input_client_changed
= text_input_client_
!= client
;
158 text_input_client_
= client
;
159 if (text_input_client_changed
) {
160 FOR_EACH_OBSERVER(InputMethodObserver
,
162 OnTextInputStateChanged(client
));
165 if (!remote_delegate_
|| (prev_input_scopes
== input_scopes_
&&
166 prev_bounds
== composition_character_bounds_
))
168 remote_delegate_
->OnTextInputClientUpdated(input_scopes_
,
169 composition_character_bounds_
);
172 void DetachTextInputClient(TextInputClient
* client
) override
{
173 if (text_input_client_
!= client
)
175 SetFocusedTextInputClient(NULL
);
178 TextInputClient
* GetTextInputClient() const override
{
179 return text_input_client_
;
182 bool DispatchKeyEvent(const ui::KeyEvent
& event
) override
{
183 if (event
.HasNativeEvent()) {
184 const base::NativeEvent
& native_key_event
= event
.native_event();
185 if (native_key_event
.message
!= WM_CHAR
)
187 if (!text_input_client_
)
189 text_input_client_
->InsertChar(
190 static_cast<base::char16
>(native_key_event
.wParam
),
191 ui::GetModifiersFromKeyState());
195 if (event
.is_char()) {
196 if (text_input_client_
) {
197 text_input_client_
->InsertChar(
198 event
.GetCharacter(),
199 ui::GetModifiersFromKeyState());
205 return delegate_
->DispatchKeyEventPostIME(event
);
208 void OnTextInputTypeChanged(const TextInputClient
* client
) override
{
209 if (!text_input_client_
|| text_input_client_
!= client
)
211 std::vector
<int32
> prev_input_scopes
;
212 std::swap(input_scopes_
, prev_input_scopes
);
213 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
214 client
->GetTextInputMode());
215 if (input_scopes_
!= prev_input_scopes
&& remote_delegate_
) {
216 remote_delegate_
->OnTextInputClientUpdated(
217 input_scopes_
, composition_character_bounds_
);
221 void OnCaretBoundsChanged(const TextInputClient
* client
) override
{
222 if (!text_input_client_
|| text_input_client_
!= client
)
224 std::vector
<gfx::Rect
> prev_rects
;
225 std::swap(composition_character_bounds_
, prev_rects
);
226 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
227 if (composition_character_bounds_
!= prev_rects
&& remote_delegate_
) {
228 remote_delegate_
->OnTextInputClientUpdated(
229 input_scopes_
, composition_character_bounds_
);
233 void CancelComposition(const TextInputClient
* client
) override
{
234 if (CanSendRemoteNotification(client
))
235 remote_delegate_
->CancelComposition();
238 void OnInputLocaleChanged() override
{}
240 std::string
GetInputLocale() override
{
241 const LCID locale_id
= MAKELCID(langid_
, SORT_DEFAULT
);
242 std::string language
=
243 GetLocaleString(locale_id
, LOCALE_SISO639LANGNAME
);
244 if (SUBLANGID(langid_
) == SUBLANG_NEUTRAL
|| language
.empty())
246 const std::string
& region
=
247 GetLocaleString(locale_id
, LOCALE_SISO3166CTRYNAME
);
250 return language
.append(1, '-').append(region
);
253 TextInputType
GetTextInputType() const override
{
254 return text_input_client_
? text_input_client_
->GetTextInputType()
255 : TEXT_INPUT_TYPE_NONE
;
258 TextInputMode
GetTextInputMode() const override
{
259 return text_input_client_
? text_input_client_
->GetTextInputMode()
260 : TEXT_INPUT_MODE_DEFAULT
;
263 int GetTextInputFlags() const override
{
264 return text_input_client_
? text_input_client_
->GetTextInputFlags()
268 bool CanComposeInline() const override
{
269 return text_input_client_
? text_input_client_
->CanComposeInline() : true;
272 bool IsCandidatePopupOpen() const override
{
273 return is_candidate_popup_open_
;
276 void ShowImeIfNeeded() override
{}
278 void AddObserver(InputMethodObserver
* observer
) override
{
279 observer_list_
.AddObserver(observer
);
282 void RemoveObserver(InputMethodObserver
* observer
) override
{
283 observer_list_
.RemoveObserver(observer
);
286 // Overridden from RemoteInputMethodPrivateWin:
287 void SetRemoteDelegate(
288 internal::RemoteInputMethodDelegateWin
* delegate
) override
{
289 remote_delegate_
= delegate
;
291 // Sync initial state.
292 if (remote_delegate_
) {
293 remote_delegate_
->OnTextInputClientUpdated(
294 input_scopes_
, composition_character_bounds_
);
298 void OnCandidatePopupChanged(bool visible
) override
{
299 is_candidate_popup_open_
= visible
;
302 void OnInputSourceChanged(LANGID langid
, bool /*is_ime*/) override
{
303 // Note: Currently |is_ime| is not utilized yet.
304 const bool changed
= (langid_
!= langid
);
306 if (changed
&& GetTextInputClient())
307 GetTextInputClient()->OnInputMethodChanged();
310 void OnCompositionChanged(const CompositionText
& composition_text
) override
{
311 if (!text_input_client_
)
313 text_input_client_
->SetCompositionText(composition_text
);
316 void OnTextCommitted(const base::string16
& text
) override
{
317 if (!text_input_client_
)
319 if (text_input_client_
->GetTextInputType() == TEXT_INPUT_TYPE_NONE
) {
320 // According to the comment in text_input_client.h,
321 // TextInputClient::InsertText should never be called when the
322 // text input type is TEXT_INPUT_TYPE_NONE.
323 for (size_t i
= 0; i
< text
.size(); ++i
)
324 text_input_client_
->InsertChar(text
[i
], 0);
327 text_input_client_
->InsertText(text
);
330 bool CanSendRemoteNotification(
331 const TextInputClient
* text_input_client
) const {
332 return text_input_client_
&&
333 text_input_client_
== text_input_client
&&
337 base::ObserverList
<InputMethodObserver
> observer_list_
;
339 internal::InputMethodDelegate
* delegate_
;
340 internal::RemoteInputMethodDelegateWin
* remote_delegate_
;
342 TextInputClient
* text_input_client_
;
343 std::vector
<int32
> input_scopes_
;
344 std::vector
<gfx::Rect
> composition_character_bounds_
;
345 bool is_candidate_popup_open_
;
349 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin
);
354 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget
) {
355 // If the remote input method is already registered then don't do it again.
356 if (ui::g_public_interface_
&& ui::g_private_interface_
)
359 DWORD process_id
= 0;
360 if (GetWindowThreadProcessId(widget
, &process_id
) == 0)
362 base::win::ScopedHandle
process_handle(::OpenProcess(
363 PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
, process_id
));
364 if (!process_handle
.IsValid())
366 return base::win::IsProcessImmersive(process_handle
.Get()) ||
367 base::CommandLine::ForCurrentProcess()->HasSwitch(
368 switches::kViewerConnect
);
371 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
373 scoped_ptr
<InputMethod
> CreateRemoteInputMethodWin(
374 internal::InputMethodDelegate
* delegate
) {
375 return make_scoped_ptr(new RemoteInputMethodWin(delegate
));
379 RemoteInputMethodPrivateWin
* RemoteInputMethodPrivateWin::Get(
380 InputMethod
* input_method
) {
381 return GetPrivate(input_method
);