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 virtual ~RemoteInputMethodWin() {
125 FOR_EACH_OBSERVER(InputMethodObserver
,
127 OnInputMethodDestroyed(this));
128 UnregisterInstance(this);
132 // Overridden from InputMethod:
133 virtual void SetDelegate(internal::InputMethodDelegate
* delegate
) override
{
134 delegate_
= delegate
;
137 virtual void Init(bool focused
) override
{
140 virtual void OnFocus() override
{
143 virtual void OnBlur() override
{
146 virtual bool OnUntranslatedIMEMessage(const base::NativeEvent
& event
,
147 NativeEventResult
* result
) override
{
151 virtual void SetFocusedTextInputClient(TextInputClient
* client
) override
{
152 std::vector
<int32
> prev_input_scopes
;
153 std::swap(input_scopes_
, prev_input_scopes
);
154 std::vector
<gfx::Rect
> prev_bounds
;
155 std::swap(composition_character_bounds_
, prev_bounds
);
157 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
158 client
->GetTextInputMode());
159 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
162 const bool text_input_client_changed
= text_input_client_
!= client
;
163 text_input_client_
= client
;
164 if (text_input_client_changed
) {
165 FOR_EACH_OBSERVER(InputMethodObserver
,
167 OnTextInputStateChanged(client
));
170 if (!remote_delegate_
|| (prev_input_scopes
== input_scopes_
&&
171 prev_bounds
== composition_character_bounds_
))
173 remote_delegate_
->OnTextInputClientUpdated(input_scopes_
,
174 composition_character_bounds_
);
177 virtual void DetachTextInputClient(TextInputClient
* client
) override
{
178 if (text_input_client_
!= client
)
180 SetFocusedTextInputClient(NULL
);
183 virtual TextInputClient
* GetTextInputClient() const override
{
184 return text_input_client_
;
187 virtual bool DispatchKeyEvent(const ui::KeyEvent
& event
) override
{
188 if (event
.HasNativeEvent()) {
189 const base::NativeEvent
& native_key_event
= event
.native_event();
190 if (native_key_event
.message
!= WM_CHAR
)
192 if (!text_input_client_
)
194 text_input_client_
->InsertChar(
195 static_cast<base::char16
>(native_key_event
.wParam
),
196 ui::GetModifiersFromKeyState());
200 if (event
.is_char()) {
201 if (text_input_client_
) {
202 text_input_client_
->InsertChar(
203 event
.GetCharacter(),
204 ui::GetModifiersFromKeyState());
210 return delegate_
->DispatchKeyEventPostIME(event
);
213 virtual void OnTextInputTypeChanged(const TextInputClient
* client
) override
{
214 if (!text_input_client_
|| text_input_client_
!= client
)
216 std::vector
<int32
> prev_input_scopes
;
217 std::swap(input_scopes_
, prev_input_scopes
);
218 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
219 client
->GetTextInputMode());
220 if (input_scopes_
!= prev_input_scopes
&& remote_delegate_
) {
221 remote_delegate_
->OnTextInputClientUpdated(
222 input_scopes_
, composition_character_bounds_
);
226 virtual void OnCaretBoundsChanged(const TextInputClient
* client
) override
{
227 if (!text_input_client_
|| text_input_client_
!= client
)
229 std::vector
<gfx::Rect
> prev_rects
;
230 std::swap(composition_character_bounds_
, prev_rects
);
231 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
232 if (composition_character_bounds_
!= prev_rects
&& remote_delegate_
) {
233 remote_delegate_
->OnTextInputClientUpdated(
234 input_scopes_
, composition_character_bounds_
);
238 virtual void CancelComposition(const TextInputClient
* client
) override
{
239 if (CanSendRemoteNotification(client
))
240 remote_delegate_
->CancelComposition();
243 virtual void OnInputLocaleChanged() override
{
246 virtual std::string
GetInputLocale() override
{
247 const LCID locale_id
= MAKELCID(langid_
, SORT_DEFAULT
);
248 std::string language
=
249 GetLocaleString(locale_id
, LOCALE_SISO639LANGNAME
);
250 if (SUBLANGID(langid_
) == SUBLANG_NEUTRAL
|| language
.empty())
252 const std::string
& region
=
253 GetLocaleString(locale_id
, LOCALE_SISO3166CTRYNAME
);
256 return language
.append(1, '-').append(region
);
259 virtual bool IsActive() override
{
260 return true; // always turned on
263 virtual TextInputType
GetTextInputType() const override
{
264 return text_input_client_
? text_input_client_
->GetTextInputType()
265 : TEXT_INPUT_TYPE_NONE
;
268 virtual TextInputMode
GetTextInputMode() const override
{
269 return text_input_client_
? text_input_client_
->GetTextInputMode()
270 : TEXT_INPUT_MODE_DEFAULT
;
273 virtual int GetTextInputFlags() const override
{
274 return text_input_client_
? text_input_client_
->GetTextInputFlags()
278 virtual bool CanComposeInline() const override
{
279 return text_input_client_
? text_input_client_
->CanComposeInline() : true;
282 virtual bool IsCandidatePopupOpen() const override
{
283 return is_candidate_popup_open_
;
286 virtual void ShowImeIfNeeded() override
{
289 virtual void AddObserver(InputMethodObserver
* observer
) override
{
290 observer_list_
.AddObserver(observer
);
293 virtual void RemoveObserver(InputMethodObserver
* observer
) override
{
294 observer_list_
.RemoveObserver(observer
);
297 // Overridden from RemoteInputMethodPrivateWin:
298 virtual void SetRemoteDelegate(
299 internal::RemoteInputMethodDelegateWin
* delegate
) override
{
300 remote_delegate_
= delegate
;
302 // Sync initial state.
303 if (remote_delegate_
) {
304 remote_delegate_
->OnTextInputClientUpdated(
305 input_scopes_
, composition_character_bounds_
);
309 virtual void OnCandidatePopupChanged(bool visible
) override
{
310 is_candidate_popup_open_
= visible
;
311 if (!text_input_client_
)
313 // TODO(kochi): Support 'update' case, in addition to show/hide.
314 // http://crbug.com/238585
316 text_input_client_
->OnCandidateWindowShown();
318 text_input_client_
->OnCandidateWindowHidden();
321 virtual void OnInputSourceChanged(LANGID langid
, bool /*is_ime*/) override
{
322 // Note: Currently |is_ime| is not utilized yet.
323 const bool changed
= (langid_
!= langid
);
325 if (changed
&& GetTextInputClient())
326 GetTextInputClient()->OnInputMethodChanged();
329 virtual void OnCompositionChanged(
330 const CompositionText
& composition_text
) override
{
331 if (!text_input_client_
)
333 text_input_client_
->SetCompositionText(composition_text
);
336 virtual void OnTextCommitted(const base::string16
& text
) override
{
337 if (!text_input_client_
)
339 if (text_input_client_
->GetTextInputType() == TEXT_INPUT_TYPE_NONE
) {
340 // According to the comment in text_input_client.h,
341 // TextInputClient::InsertText should never be called when the
342 // text input type is TEXT_INPUT_TYPE_NONE.
343 for (size_t i
= 0; i
< text
.size(); ++i
)
344 text_input_client_
->InsertChar(text
[i
], 0);
347 text_input_client_
->InsertText(text
);
350 bool CanSendRemoteNotification(
351 const TextInputClient
* text_input_client
) const {
352 return text_input_client_
&&
353 text_input_client_
== text_input_client
&&
357 ObserverList
<InputMethodObserver
> observer_list_
;
359 internal::InputMethodDelegate
* delegate_
;
360 internal::RemoteInputMethodDelegateWin
* remote_delegate_
;
362 TextInputClient
* text_input_client_
;
363 std::vector
<int32
> input_scopes_
;
364 std::vector
<gfx::Rect
> composition_character_bounds_
;
365 bool is_candidate_popup_open_
;
369 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin
);
374 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget
) {
375 // If the remote input method is already registered then don't do it again.
376 if (ui::g_public_interface_
&& ui::g_private_interface_
)
379 DWORD process_id
= 0;
380 if (GetWindowThreadProcessId(widget
, &process_id
) == 0)
382 base::win::ScopedHandle
process_handle(::OpenProcess(
383 PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
, process_id
));
384 if (!process_handle
.IsValid())
386 return base::win::IsProcessImmersive(process_handle
.Get()) ||
387 base::CommandLine::ForCurrentProcess()->HasSwitch(
388 switches::kViewerConnect
);
391 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
393 scoped_ptr
<InputMethod
> CreateRemoteInputMethodWin(
394 internal::InputMethodDelegate
* delegate
) {
395 return make_scoped_ptr(new RemoteInputMethodWin(delegate
));
399 RemoteInputMethodPrivateWin
* RemoteInputMethodPrivateWin::Get(
400 InputMethod
* input_method
) {
401 return GetPrivate(input_method
);