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 void DispatchKeyEvent(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
&& text_input_client_
) {
186 text_input_client_
->InsertChar(
187 static_cast<base::char16
>(native_key_event
.wParam
),
188 ui::GetModifiersFromKeyState());
189 event
->StopPropagation();
194 if (event
->is_char()) {
195 if (text_input_client_
) {
196 text_input_client_
->InsertChar(
197 event
->GetCharacter(),
198 ui::GetModifiersFromKeyState());
200 event
->StopPropagation();
204 ignore_result(delegate_
->DispatchKeyEventPostIME(event
));
207 void OnTextInputTypeChanged(const TextInputClient
* client
) override
{
208 if (!text_input_client_
|| text_input_client_
!= client
)
210 std::vector
<int32
> prev_input_scopes
;
211 std::swap(input_scopes_
, prev_input_scopes
);
212 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
213 client
->GetTextInputMode());
214 if (input_scopes_
!= prev_input_scopes
&& remote_delegate_
) {
215 remote_delegate_
->OnTextInputClientUpdated(
216 input_scopes_
, composition_character_bounds_
);
220 void OnCaretBoundsChanged(const TextInputClient
* client
) override
{
221 if (!text_input_client_
|| text_input_client_
!= client
)
223 std::vector
<gfx::Rect
> prev_rects
;
224 std::swap(composition_character_bounds_
, prev_rects
);
225 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
226 if (composition_character_bounds_
!= prev_rects
&& remote_delegate_
) {
227 remote_delegate_
->OnTextInputClientUpdated(
228 input_scopes_
, composition_character_bounds_
);
232 void CancelComposition(const TextInputClient
* client
) override
{
233 if (CanSendRemoteNotification(client
))
234 remote_delegate_
->CancelComposition();
237 void OnInputLocaleChanged() override
{}
239 std::string
GetInputLocale() override
{
240 const LCID locale_id
= MAKELCID(langid_
, SORT_DEFAULT
);
241 std::string language
=
242 GetLocaleString(locale_id
, LOCALE_SISO639LANGNAME
);
243 if (SUBLANGID(langid_
) == SUBLANG_NEUTRAL
|| language
.empty())
245 const std::string
& region
=
246 GetLocaleString(locale_id
, LOCALE_SISO3166CTRYNAME
);
249 return language
.append(1, '-').append(region
);
252 TextInputType
GetTextInputType() const override
{
253 return text_input_client_
? text_input_client_
->GetTextInputType()
254 : TEXT_INPUT_TYPE_NONE
;
257 TextInputMode
GetTextInputMode() const override
{
258 return text_input_client_
? text_input_client_
->GetTextInputMode()
259 : TEXT_INPUT_MODE_DEFAULT
;
262 int GetTextInputFlags() const override
{
263 return text_input_client_
? text_input_client_
->GetTextInputFlags()
267 bool CanComposeInline() const override
{
268 return text_input_client_
? text_input_client_
->CanComposeInline() : true;
271 bool IsCandidatePopupOpen() const override
{
272 return is_candidate_popup_open_
;
275 void ShowImeIfNeeded() override
{}
277 void AddObserver(InputMethodObserver
* observer
) override
{
278 observer_list_
.AddObserver(observer
);
281 void RemoveObserver(InputMethodObserver
* observer
) override
{
282 observer_list_
.RemoveObserver(observer
);
285 // Overridden from RemoteInputMethodPrivateWin:
286 void SetRemoteDelegate(
287 internal::RemoteInputMethodDelegateWin
* delegate
) override
{
288 remote_delegate_
= delegate
;
290 // Sync initial state.
291 if (remote_delegate_
) {
292 remote_delegate_
->OnTextInputClientUpdated(
293 input_scopes_
, composition_character_bounds_
);
297 void OnCandidatePopupChanged(bool visible
) override
{
298 is_candidate_popup_open_
= visible
;
301 void OnInputSourceChanged(LANGID langid
, bool /*is_ime*/) override
{
302 // Note: Currently |is_ime| is not utilized yet.
303 const bool changed
= (langid_
!= langid
);
305 if (changed
&& GetTextInputClient())
306 GetTextInputClient()->OnInputMethodChanged();
309 void OnCompositionChanged(const CompositionText
& composition_text
) override
{
310 if (!text_input_client_
)
312 text_input_client_
->SetCompositionText(composition_text
);
315 void OnTextCommitted(const base::string16
& text
) override
{
316 if (!text_input_client_
)
318 if (text_input_client_
->GetTextInputType() == TEXT_INPUT_TYPE_NONE
) {
319 // According to the comment in text_input_client.h,
320 // TextInputClient::InsertText should never be called when the
321 // text input type is TEXT_INPUT_TYPE_NONE.
322 for (size_t i
= 0; i
< text
.size(); ++i
)
323 text_input_client_
->InsertChar(text
[i
], 0);
326 text_input_client_
->InsertText(text
);
329 bool CanSendRemoteNotification(
330 const TextInputClient
* text_input_client
) const {
331 return text_input_client_
&&
332 text_input_client_
== text_input_client
&&
336 base::ObserverList
<InputMethodObserver
> observer_list_
;
338 internal::InputMethodDelegate
* delegate_
;
339 internal::RemoteInputMethodDelegateWin
* remote_delegate_
;
341 TextInputClient
* text_input_client_
;
342 std::vector
<int32
> input_scopes_
;
343 std::vector
<gfx::Rect
> composition_character_bounds_
;
344 bool is_candidate_popup_open_
;
348 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin
);
353 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget
) {
354 // If the remote input method is already registered then don't do it again.
355 if (ui::g_public_interface_
&& ui::g_private_interface_
)
358 DWORD process_id
= 0;
359 if (GetWindowThreadProcessId(widget
, &process_id
) == 0)
361 base::win::ScopedHandle
process_handle(::OpenProcess(
362 PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
, process_id
));
363 if (!process_handle
.IsValid())
365 return base::win::IsProcessImmersive(process_handle
.Get()) ||
366 base::CommandLine::ForCurrentProcess()->HasSwitch(
367 switches::kViewerConnect
);
370 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
372 scoped_ptr
<InputMethod
> CreateRemoteInputMethodWin(
373 internal::InputMethodDelegate
* delegate
) {
374 return make_scoped_ptr(new RemoteInputMethodWin(delegate
));
378 RemoteInputMethodPrivateWin
* RemoteInputMethodPrivateWin::Get(
379 InputMethod
* input_method
) {
380 return GetPrivate(input_method
);