| 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 |