OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/ime/input_method_ibus.h" | 5 #include "ui/base/ime/input_method_ibus.h" |
6 | 6 |
7 #include <X11/X.h> | 7 #include <X11/X.h> |
8 #include <X11/Xlib.h> | 8 #include <X11/Xlib.h> |
9 #include <X11/Xutil.h> | 9 #include <X11/Xutil.h> |
10 #undef FocusIn | 10 #undef FocusIn |
(...skipping 20 matching lines...) Expand all Loading... |
31 #include "ui/base/ime/text_input_client.h" | 31 #include "ui/base/ime/text_input_client.h" |
32 #include "ui/base/keycodes/keyboard_code_conversion.h" | 32 #include "ui/base/keycodes/keyboard_code_conversion.h" |
33 #include "ui/base/keycodes/keyboard_code_conversion_x.h" | 33 #include "ui/base/keycodes/keyboard_code_conversion_x.h" |
34 #include "ui/base/keycodes/keyboard_codes.h" | 34 #include "ui/base/keycodes/keyboard_codes.h" |
35 #include "ui/gfx/rect.h" | 35 #include "ui/gfx/rect.h" |
36 | 36 |
37 namespace { | 37 namespace { |
38 | 38 |
39 const int kIBusReleaseMask = 1 << 30; | 39 const int kIBusReleaseMask = 1 << 30; |
40 const char kClientName[] = "chrome"; | 40 const char kClientName[] = "chrome"; |
| 41 const int kMaxRetryCount = 10; |
41 | 42 |
42 // Following capability mask is introduced from | 43 // Following capability mask is introduced from |
43 // http://ibus.googlecode.com/svn/docs/ibus-1.4/ibus-ibustypes.html#IBusCapabili
te | 44 // http://ibus.googlecode.com/svn/docs/ibus-1.4/ibus-ibustypes.html#IBusCapabili
te |
44 const uint32 kIBusCapabilityPreeditText = 1U; | 45 const uint32 kIBusCapabilityPreeditText = 1U; |
45 const uint32 kIBusCapabilityFocus = 8U; | 46 const uint32 kIBusCapabilityFocus = 8U; |
46 const uint32 kIBusCapabilitySurroundingText = 32U; | 47 const uint32 kIBusCapabilitySurroundingText = 32U; |
47 | 48 |
48 XKeyEvent* GetKeyEvent(XEvent* event) { | 49 XKeyEvent* GetKeyEvent(XEvent* event) { |
49 DCHECK(event && (event->type == KeyPress || event->type == KeyRelease)); | 50 DCHECK(event && (event->type == KeyPress || event->type == KeyRelease)); |
50 return &event->xkey; | 51 return &event->xkey; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 ibus_keyval_, | 156 ibus_keyval_, |
156 handled); | 157 handled); |
157 return; | 158 return; |
158 } | 159 } |
159 | 160 |
160 // TODO(yusukes): Support non-native event (from e.g. a virtual keyboard). | 161 // TODO(yusukes): Support non-native event (from e.g. a virtual keyboard). |
161 // See views::InputMethodIBus for details. Never forget to set 'character_' | 162 // See views::InputMethodIBus for details. Never forget to set 'character_' |
162 // and 'unmodified_character_' to support i18n VKs like a French VK! | 163 // and 'unmodified_character_' to support i18n VKs like a French VK! |
163 } | 164 } |
164 | 165 |
165 // A class to hold information of a pending request for creating an ibus input | |
166 // context. | |
167 class InputMethodIBus::PendingCreateICRequest { | |
168 public: | |
169 PendingCreateICRequest(InputMethodIBus* input_method, | |
170 PendingCreateICRequest** request_ptr); | |
171 virtual ~PendingCreateICRequest(); | |
172 | |
173 // Set up signal handlers, or destroy object proxy if the input context is | |
174 // already abandoned. | |
175 void InitOrAbandonInputContext(); | |
176 | |
177 // Called if the create input context method call is failed. | |
178 void OnCreateInputContextFailed(); | |
179 | |
180 // Abandon this pending key event. Its result will just be discarded. | |
181 void Abandon() { | |
182 input_method_ = NULL; | |
183 request_ptr_ = NULL; | |
184 // Do not reset |ibus_client_| here. | |
185 } | |
186 | |
187 private: | |
188 InputMethodIBus* input_method_; | |
189 PendingCreateICRequest** request_ptr_; | |
190 | |
191 DISALLOW_COPY_AND_ASSIGN(PendingCreateICRequest); | |
192 }; | |
193 | |
194 InputMethodIBus::PendingCreateICRequest::PendingCreateICRequest( | |
195 InputMethodIBus* input_method, | |
196 PendingCreateICRequest** request_ptr) | |
197 : input_method_(input_method), | |
198 request_ptr_(request_ptr) { | |
199 } | |
200 | |
201 InputMethodIBus::PendingCreateICRequest::~PendingCreateICRequest() { | |
202 if (request_ptr_) { | |
203 DCHECK_EQ(*request_ptr_, this); | |
204 *request_ptr_ = NULL; | |
205 } | |
206 } | |
207 | |
208 void InputMethodIBus::PendingCreateICRequest::OnCreateInputContextFailed() { | |
209 // TODO(nona): If the connection between Chrome and ibus-daemon terminates | |
210 // for some reason, the create ic request will fail. We might want to call | |
211 // ibus_client_->CreateContext() again after some delay. | |
212 } | |
213 | |
214 void InputMethodIBus::PendingCreateICRequest::InitOrAbandonInputContext() { | |
215 if (input_method_) { | |
216 DCHECK(input_method_->IsContextReady()); | |
217 input_method_->SetUpSignalHandlers(); | |
218 } else { | |
219 GetInputContextClient()->ResetObjectProxy(); | |
220 } | |
221 } | |
222 | |
223 // InputMethodIBus implementation ----------------------------------------- | 166 // InputMethodIBus implementation ----------------------------------------- |
224 InputMethodIBus::InputMethodIBus( | 167 InputMethodIBus::InputMethodIBus( |
225 internal::InputMethodDelegate* delegate) | 168 internal::InputMethodDelegate* delegate) |
226 : ibus_client_(new internal::IBusClient), | 169 : ibus_client_(new internal::IBusClient), |
227 pending_create_ic_request_(NULL), | 170 input_context_state_(INPUT_CONTEXT_STOP), |
| 171 create_input_context_fail_count_(0), |
228 context_focused_(false), | 172 context_focused_(false), |
229 composing_text_(false), | 173 composing_text_(false), |
230 composition_changed_(false), | 174 composition_changed_(false), |
231 suppress_next_result_(false), | 175 suppress_next_result_(false), |
232 weak_ptr_factory_(this) { | 176 weak_ptr_factory_(this) { |
233 SetDelegate(delegate); | 177 SetDelegate(delegate); |
234 } | 178 } |
235 | 179 |
236 InputMethodIBus::~InputMethodIBus() { | 180 InputMethodIBus::~InputMethodIBus() { |
237 AbandonAllPendingKeyEvents(); | 181 AbandonAllPendingKeyEvents(); |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 OnTextInputTypeChanged(focused); | 360 OnTextInputTypeChanged(focused); |
417 | 361 |
418 UpdateContextFocusState(); | 362 UpdateContextFocusState(); |
419 // Force to update caret bounds, in case the client thinks that the caret | 363 // Force to update caret bounds, in case the client thinks that the caret |
420 // bounds has not changed. | 364 // bounds has not changed. |
421 OnCaretBoundsChanged(focused); | 365 OnCaretBoundsChanged(focused); |
422 } | 366 } |
423 | 367 |
424 void InputMethodIBus::CreateContext() { | 368 void InputMethodIBus::CreateContext() { |
425 DCHECK(IsConnected()); | 369 DCHECK(IsConnected()); |
426 DCHECK(!pending_create_ic_request_); | |
427 | 370 |
428 pending_create_ic_request_ = new PendingCreateICRequest( | 371 if (input_context_state_ != INPUT_CONTEXT_STOP) { |
429 this, &pending_create_ic_request_); | 372 DVLOG(1) << "Input context is already created or waiting ibus-daemon" |
| 373 " response."; |
| 374 return; |
| 375 } |
| 376 |
| 377 input_context_state_ = INPUT_CONTEXT_WAIT_CREATE_INPUT_CONTEXT_RESPONSE; |
430 | 378 |
431 // Creates the input context asynchronously. | 379 // Creates the input context asynchronously. |
| 380 DCHECK(!IsContextReady()); |
432 chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext( | 381 chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext( |
433 kClientName, | 382 kClientName, |
434 base::Bind(&InputMethodIBus::CreateInputContextDone, | 383 base::Bind(&InputMethodIBus::CreateInputContextDone, |
435 weak_ptr_factory_.GetWeakPtr(), | 384 weak_ptr_factory_.GetWeakPtr()), |
436 base::Unretained(pending_create_ic_request_)), | |
437 base::Bind(&InputMethodIBus::CreateInputContextFail, | 385 base::Bind(&InputMethodIBus::CreateInputContextFail, |
438 weak_ptr_factory_.GetWeakPtr(), | 386 weak_ptr_factory_.GetWeakPtr())); |
439 base::Unretained(pending_create_ic_request_))); | |
440 } | 387 } |
441 | 388 |
442 void InputMethodIBus::SetUpSignalHandlers() { | 389 void InputMethodIBus::SetUpSignalHandlers() { |
443 DCHECK(IsContextReady()); | 390 DCHECK(IsContextReady()); |
444 | 391 |
445 // connect input context signals | 392 // connect input context signals |
446 chromeos::IBusInputContextClient* input_context_client = | 393 chromeos::IBusInputContextClient* input_context_client = |
447 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient(); | 394 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient(); |
448 input_context_client->SetCommitTextHandler( | 395 input_context_client->SetCommitTextHandler( |
449 base::Bind(&InputMethodIBus::OnCommitText, | 396 base::Bind(&InputMethodIBus::OnCommitText, |
(...skipping 21 matching lines...) Expand all Loading... |
471 | 418 |
472 UpdateContextFocusState(); | 419 UpdateContextFocusState(); |
473 // Since ibus-daemon is launched in an on-demand basis on Chrome OS, RWHVA (or | 420 // Since ibus-daemon is launched in an on-demand basis on Chrome OS, RWHVA (or |
474 // equivalents) might call OnCaretBoundsChanged() before the daemon starts. To | 421 // equivalents) might call OnCaretBoundsChanged() before the daemon starts. To |
475 // save the case, call OnCaretBoundsChanged() here. | 422 // save the case, call OnCaretBoundsChanged() here. |
476 OnCaretBoundsChanged(GetTextInputClient()); | 423 OnCaretBoundsChanged(GetTextInputClient()); |
477 OnInputMethodChanged(); | 424 OnInputMethodChanged(); |
478 } | 425 } |
479 | 426 |
480 void InputMethodIBus::DestroyContext() { | 427 void InputMethodIBus::DestroyContext() { |
481 if (pending_create_ic_request_) { | 428 if (input_context_state_ == INPUT_CONTEXT_STOP) |
482 DCHECK(!IsContextReady()); | |
483 // |pending_create_ic_request_| will be deleted in CreateInputContextDone(). | |
484 pending_create_ic_request_->Abandon(); | |
485 pending_create_ic_request_ = NULL; | |
486 return; | 429 return; |
487 } | 430 input_context_state_ = INPUT_CONTEXT_STOP; |
488 const chromeos::IBusInputContextClient* input_context = | 431 const chromeos::IBusInputContextClient* input_context = |
489 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient(); | 432 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient(); |
490 if (input_context && input_context->IsObjectProxyReady()) { | 433 if (input_context && input_context->IsObjectProxyReady()) { |
491 // We can't use IsContextReady here because we want to destroy object proxy | 434 // We can't use IsContextReady here because we want to destroy object proxy |
492 // regardless of connection. The IsContextReady contains connection check. | 435 // regardless of connection. The IsContextReady contains connection check. |
493 ResetInputContext(); | 436 ResetInputContext(); |
494 DCHECK(!IsContextReady()); | 437 DCHECK(!IsContextReady()); |
495 } | 438 } |
496 } | 439 } |
497 | 440 |
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 context_focused_ = false; | 842 context_focused_ = false; |
900 | 843 |
901 ConfirmCompositionText(); | 844 ConfirmCompositionText(); |
902 | 845 |
903 // We are dead, so we need to ask the client to stop relying on us. | 846 // We are dead, so we need to ask the client to stop relying on us. |
904 OnInputMethodChanged(); | 847 OnInputMethodChanged(); |
905 GetInputContextClient()->ResetObjectProxy(); | 848 GetInputContextClient()->ResetObjectProxy(); |
906 } | 849 } |
907 | 850 |
908 void InputMethodIBus::CreateInputContextDone( | 851 void InputMethodIBus::CreateInputContextDone( |
909 PendingCreateICRequest* ic_request, | |
910 const dbus::ObjectPath& object_path) { | 852 const dbus::ObjectPath& object_path) { |
| 853 DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_); |
| 854 |
| 855 if (input_context_state_ == INPUT_CONTEXT_STOP) { |
| 856 // DestroyContext has already been called. |
| 857 return; |
| 858 } |
| 859 |
911 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient() | 860 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient() |
912 ->Initialize(chromeos::DBusThreadManager::Get()->GetIBusBus(), | 861 ->Initialize(chromeos::DBusThreadManager::Get()->GetIBusBus(), |
913 object_path); | 862 object_path); |
914 ic_request->InitOrAbandonInputContext(); | 863 |
915 delete ic_request; | 864 input_context_state_ = INPUT_CONTEXT_RUNNING; |
| 865 DCHECK(IsContextReady()); |
| 866 SetUpSignalHandlers(); |
916 } | 867 } |
917 | 868 |
918 void InputMethodIBus::CreateInputContextFail( | 869 void InputMethodIBus::CreateInputContextFail() { |
919 PendingCreateICRequest* ic_request) { | 870 DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_); |
920 ic_request->OnCreateInputContextFailed(); | 871 if (input_context_state_ == INPUT_CONTEXT_STOP) { |
921 delete ic_request; | 872 // CreateInputContext failed but the input context is no longer |
| 873 // necessary, thus do nothing. |
| 874 return; |
| 875 } |
| 876 |
| 877 if (++create_input_context_fail_count_ >= kMaxRetryCount) { |
| 878 DVLOG(1) << "CreateInputContext failed even tried " |
| 879 << kMaxRetryCount << " times, give up."; |
| 880 create_input_context_fail_count_ = 0; |
| 881 input_context_state_ = INPUT_CONTEXT_STOP; |
| 882 return; |
| 883 } |
| 884 |
| 885 // Try CreateInputContext again. |
| 886 chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext( |
| 887 kClientName, |
| 888 base::Bind(&InputMethodIBus::CreateInputContextDone, |
| 889 weak_ptr_factory_.GetWeakPtr()), |
| 890 base::Bind(&InputMethodIBus::CreateInputContextFail, |
| 891 weak_ptr_factory_.GetWeakPtr())); |
922 } | 892 } |
923 | 893 |
924 bool InputMethodIBus::IsConnected() { | 894 bool InputMethodIBus::IsConnected() { |
925 return chromeos::DBusThreadManager::Get()->GetIBusBus() != NULL; | 895 return chromeos::DBusThreadManager::Get()->GetIBusBus() != NULL; |
926 } | 896 } |
927 | 897 |
928 bool InputMethodIBus::IsContextReady() { | 898 bool InputMethodIBus::IsContextReady() { |
929 if (!IsConnected()) | 899 if (!IsConnected()) |
930 return false; | 900 return false; |
931 if (!GetInputContextClient()) | 901 if (!GetInputContextClient()) |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1026 } | 996 } |
1027 | 997 |
1028 // Use a black thin underline by default. | 998 // Use a black thin underline by default. |
1029 if (out_composition->underlines.empty()) { | 999 if (out_composition->underlines.empty()) { |
1030 out_composition->underlines.push_back(CompositionUnderline( | 1000 out_composition->underlines.push_back(CompositionUnderline( |
1031 0, length, SK_ColorBLACK, false /* thick */)); | 1001 0, length, SK_ColorBLACK, false /* thick */)); |
1032 } | 1002 } |
1033 } | 1003 } |
1034 | 1004 |
1035 } // namespace ui | 1005 } // namespace ui |
OLD | NEW |