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 3 // found in the LICENSE file. 4 5 #include "ui/base/ime/input_method_ibus.h" 6 7 #include <X11/X.h> 8 #include <X11/Xlib.h> 9 #include <X11/Xutil.h> 10 #undef FocusIn 11 #undef FocusOut 12 13 #include <algorithm> 14 #include <cstring> 15 #include <set> 16 #include <vector> 17 18 #include "base/basictypes.h" 19 #include "base/bind.h" 20 #include "base/chromeos/chromeos_version.h" 21 #include "base/i18n/char_iterator.h" 22 #include "base/logging.h" 23 #include "base/strings/string_util.h" 24 #include "base/strings/utf_string_conversions.h" 25 #include "base/third_party/icu/icu_utf.h" 26 #include "chromeos/dbus/dbus_thread_manager.h" 27 #include "chromeos/dbus/ibus/ibus_client.h" 28 #include "chromeos/dbus/ibus/ibus_input_context_client.h" 29 #include "chromeos/dbus/ibus/ibus_text.h" 30 #include "ui/base/events/event.h" 31 #include "ui/base/events/event_constants.h" 32 #include "ui/base/events/event_utils.h" 33 #include "ui/base/ime/text_input_client.h" 34 #include "ui/base/keycodes/keyboard_code_conversion.h" 35 #include "ui/base/keycodes/keyboard_code_conversion_x.h" 36 #include "ui/base/keycodes/keyboard_codes.h" 37 #include "ui/gfx/rect.h" 38 39 namespace { 40 41 const int kIBusReleaseMask = 1 << 30; 42 const char kClientName[] = "chrome"; 43 const int kMaxRetryCount = 10; 44 45 // Following capability mask is introduced from 46 // http://ibus.googlecode.com/svn/docs/ibus-1.4/ibus-ibustypes.html#IBusCapabilite 47 const uint32 kIBusCapabilityPreeditText = 1U; 48 const uint32 kIBusCapabilityFocus = 8U; 49 const uint32 kIBusCapabilitySurroundingText = 32U; 50 51 XKeyEvent* GetKeyEvent(XEvent* event) { 52 DCHECK(event && (event->type == KeyPress || event->type == KeyRelease)); 53 return &event->xkey; 54 } 55 56 // Converts X (and ibus) state to event flags. 57 int EventFlagsFromXState(unsigned int state) { 58 return (state & LockMask ? ui::EF_CAPS_LOCK_DOWN : 0) | 59 (state & ControlMask ? ui::EF_CONTROL_DOWN : 0) | 60 (state & ShiftMask ? ui::EF_SHIFT_DOWN : 0) | 61 (state & Mod1Mask ? ui::EF_ALT_DOWN : 0) | 62 (state & Button1Mask ? ui::EF_LEFT_MOUSE_BUTTON : 0) | 63 (state & Button2Mask ? ui::EF_MIDDLE_MOUSE_BUTTON : 0) | 64 (state & Button3Mask ? ui::EF_RIGHT_MOUSE_BUTTON : 0); 65 } 66 67 // Converts X state to ibus key and button state. 68 uint32 IBusStateFromXState(unsigned int state) { 69 return (state & (LockMask | ControlMask | ShiftMask | Mod1Mask | 70 Button1Mask | Button2Mask | Button3Mask)); 71 } 72 73 chromeos::IBusInputContextClient* GetInputContextClient() { 74 return chromeos::DBusThreadManager::Get()->GetIBusInputContextClient(); 75 } 76 77 // Converts gfx::Rect to ibus::Rect. 78 chromeos::ibus::Rect GfxRectToIBusRect(const gfx::Rect& rect) { 79 return chromeos::ibus::Rect(rect.x(), rect.y(), rect.width(), rect.height()); 80 } 81 82 } // namespace 83 84 namespace ui { 85 86 // InputMethodIBus implementation ----------------------------------------- 87 InputMethodIBus::InputMethodIBus( 88 internal::InputMethodDelegate* delegate) 89 : input_context_state_(INPUT_CONTEXT_STOP), 90 create_input_context_fail_count_(0), 91 context_focused_(false), 92 composing_text_(false), 93 composition_changed_(false), 94 suppress_next_result_(false), 95 current_keyevent_id_(0), 96 weak_ptr_factory_(this) { 97 SetDelegate(delegate); 98 99 // chromeos::IBusDaemonController is not available in case of some testing, 100 // e.g. content_browser test can't initialize IBusDaemonController. 101 DCHECK(!base::chromeos::IsRunningOnChromeOS() || 102 chromeos::IBusDaemonController::GetInstance()); 103 104 if (chromeos::IBusDaemonController::GetInstance()) 105 chromeos::IBusDaemonController::GetInstance()->AddObserver(this); 106 } 107 108 InputMethodIBus::~InputMethodIBus() { 109 AbandonAllPendingKeyEvents(); 110 if (IsContextReady()) 111 DestroyContext(); 112 if (GetInputContextClient()) 113 GetInputContextClient()->SetInputContextHandler(NULL); 114 if (chromeos::IBusDaemonController::GetInstance()) 115 chromeos::IBusDaemonController::GetInstance()->RemoveObserver(this); 116 } 117 118 void InputMethodIBus::OnFocus() { 119 InputMethodBase::OnFocus(); 120 UpdateContextFocusState(); 121 } 122 123 void InputMethodIBus::OnBlur() { 124 ConfirmCompositionText(); 125 InputMethodBase::OnBlur(); 126 UpdateContextFocusState(); 127 } 128 129 bool InputMethodIBus::OnUntranslatedIMEMessage(const base::NativeEvent& event, 130 NativeEventResult* result) { 131 return false; 132 } 133 134 void InputMethodIBus::Init(bool focused) { 135 // Initializes the connection to ibus daemon. It may happen asynchronously, 136 // and as soon as the connection is established, the |context_| will be 137 // created automatically. 138 139 // Create the input context if the connection is already established. 140 if (IsConnected()) 141 CreateContext(); 142 143 InputMethodBase::Init(focused); 144 } 145 146 void InputMethodIBus::ProcessKeyEventDone(uint32 id, 147 XEvent* event, 148 uint32 ibus_keyval, 149 uint32 ibus_keycode, 150 uint32 ibus_state, 151 bool is_handled) { 152 DCHECK(event); 153 std::set<uint32>::iterator it = pending_key_events_.find(id); 154 155 if (it == pending_key_events_.end()) 156 return; // Abandoned key event. 157 158 if (event->type == KeyPress) { 159 if (is_handled) { 160 // IME event has a priority to be handled, so that character composer 161 // should be reset. 162 character_composer_.Reset(); 163 } else { 164 // If IME does not handle key event, passes keyevent to character composer 165 // to be able to compose complex characters. 166 is_handled = ExecuteCharacterComposer(ibus_keyval, ibus_keycode, 167 ibus_state); 168 } 169 } 170 171 if (event->type == KeyPress || event->type == KeyRelease) 172 ProcessKeyEventPostIME(event, ibus_state, is_handled); 173 174 // Do not use |it| for erasing, ProcessKeyEventPostIME may change the 175 // |pending_key_events_|. 176 pending_key_events_.erase(id); 177 } 178 179 bool InputMethodIBus::DispatchKeyEvent(const base::NativeEvent& native_event) { 180 DCHECK(native_event && (native_event->type == KeyPress || 181 native_event->type == KeyRelease)); 182 DCHECK(system_toplevel_window_focused()); 183 184 uint32 ibus_keyval = 0; 185 uint32 ibus_keycode = 0; 186 uint32 ibus_state = 0; 187 IBusKeyEventFromNativeKeyEvent( 188 native_event, 189 &ibus_keyval, &ibus_keycode, &ibus_state); 190 191 // If |context_| is not usable, then we can only dispatch the key event as is. 192 // We also dispatch the key event directly if the current text input type is 193 // TEXT_INPUT_TYPE_PASSWORD, to bypass the input method. 194 // Note: We need to send the key event to ibus even if the |context_| is not 195 // enabled, so that ibus can have a chance to enable the |context_|. 196 if (!context_focused_ || 197 GetTextInputType() == TEXT_INPUT_TYPE_PASSWORD || 198 !GetInputContextClient() || 199 GetInputContextClient()->IsXKBLayout()) { 200 if (native_event->type == KeyPress) { 201 if (ExecuteCharacterComposer(ibus_keyval, ibus_keycode, ibus_state)) { 202 // Treating as PostIME event if character composer handles key event and 203 // generates some IME event, 204 ProcessKeyEventPostIME(native_event, ibus_state, true); 205 return true; 206 } 207 ProcessUnfilteredKeyPressEvent(native_event, ibus_state); 208 } else { 209 DispatchKeyEventPostIME(native_event); 210 } 211 return true; 212 } 213 214 pending_key_events_.insert(current_keyevent_id_); 215 216 // Since |native_event| might be treated as XEvent whose size is bigger than 217 // XKeyEvent e.g. in CopyNativeEvent() in ui/base/events/event.cc, allocating 218 // |event| as XKeyEvent and casting it to XEvent is unsafe. crbug.com/151884 219 XEvent* event = new XEvent; 220 *event = *native_event; 221 const chromeos::IBusInputContextClient::ProcessKeyEventCallback callback = 222 base::Bind(&InputMethodIBus::ProcessKeyEventDone, 223 weak_ptr_factory_.GetWeakPtr(), 224 current_keyevent_id_, 225 base::Owned(event), // Pass the ownership of |event|. 226 ibus_keyval, 227 ibus_keycode, 228 ibus_state); 229 230 GetInputContextClient()->ProcessKeyEvent(ibus_keyval, 231 ibus_keycode, 232 ibus_state, 233 callback, 234 base::Bind(callback, false)); 235 ++current_keyevent_id_; 236 237 // We don't want to suppress the result generated by this key event, but it 238 // may cause problem. See comment in ResetContext() method. 239 suppress_next_result_ = false; 240 return true; 241 } 242 243 bool InputMethodIBus::DispatchFabricatedKeyEvent(const ui::KeyEvent& event) { 244 // TODO(bryeung): The fabricated events should also pass through IME. 245 if (event.type() == ET_KEY_PRESSED) { 246 ProcessUnfilteredFabricatedKeyPressEvent( 247 ET_KEY_PRESSED, event.key_code(), event.flags()); 248 } else { 249 DispatchFabricatedKeyEventPostIME( 250 ET_KEY_RELEASED, 251 event.key_code(), 252 event.flags()); 253 } 254 return true; 255 } 256 257 void InputMethodIBus::OnTextInputTypeChanged(const TextInputClient* client) { 258 if (IsContextReady() && IsTextInputClientFocused(client)) { 259 ResetContext(); 260 UpdateContextFocusState(); 261 } 262 InputMethodBase::OnTextInputTypeChanged(client); 263 } 264 265 void InputMethodIBus::OnCaretBoundsChanged(const TextInputClient* client) { 266 if (!context_focused_ || !IsTextInputClientFocused(client)) 267 return; 268 269 // The current text input type should not be NONE if |context_| is focused. 270 DCHECK(!IsTextInputTypeNone()); 271 const gfx::Rect rect = GetTextInputClient()->GetCaretBounds(); 272 273 gfx::Rect composition_head; 274 if (!GetTextInputClient()->GetCompositionCharacterBounds(0, 275 &composition_head)) { 276 composition_head = rect; 277 } 278 279 GetInputContextClient()->SetCursorLocation( 280 GfxRectToIBusRect(rect), 281 GfxRectToIBusRect(composition_head)); 282 283 ui::Range text_range; 284 ui::Range selection_range; 285 string16 surrounding_text; 286 if (!GetTextInputClient()->GetTextRange(&text_range) || 287 !GetTextInputClient()->GetTextFromRange(text_range, &surrounding_text) || 288 !GetTextInputClient()->GetSelectionRange(&selection_range)) { 289 previous_surrounding_text_.clear(); 290 previous_selection_range_ = ui::Range::InvalidRange(); 291 return; 292 } 293 294 if (previous_selection_range_ == selection_range && 295 previous_surrounding_text_ == surrounding_text) 296 return; 297 298 previous_selection_range_ = selection_range; 299 previous_surrounding_text_ = surrounding_text; 300 301 if (!selection_range.IsValid()) { 302 // TODO(nona): Ideally selection_range should not be invalid. 303 // TODO(nona): If javascript changes the focus on page loading, even (0,0) 304 // can not be obtained. Need investigation. 305 return; 306 } 307 308 // Here SetSurroundingText accepts relative position of |surrounding_text|, so 309 // we have to convert |selection_range| from node coordinates to 310 // |surrounding_text| coordinates. 311 GetInputContextClient()->SetSurroundingText( 312 UTF16ToUTF8(surrounding_text), 313 selection_range.start() - text_range.start(), 314 selection_range.end() - text_range.start()); 315 } 316 317 void InputMethodIBus::CancelComposition(const TextInputClient* client) { 318 if (context_focused_ && IsTextInputClientFocused(client)) 319 ResetContext(); 320 } 321 322 void InputMethodIBus::OnInputLocaleChanged() { 323 // Not supported. 324 } 325 326 std::string InputMethodIBus::GetInputLocale() { 327 // Not supported. 328 return ""; 329 } 330 331 base::i18n::TextDirection InputMethodIBus::GetInputTextDirection() { 332 // Not supported. 333 return base::i18n::UNKNOWN_DIRECTION; 334 } 335 336 bool InputMethodIBus::IsActive() { 337 return true; 338 } 339 340 bool InputMethodIBus::IsCandidatePopupOpen() const { 341 // TODO(yukishiino): Implement this method. 342 return false; 343 } 344 345 void InputMethodIBus::OnWillChangeFocusedClient(TextInputClient* focused_before, 346 TextInputClient* focused) { 347 ConfirmCompositionText(); 348 } 349 350 void InputMethodIBus::OnDidChangeFocusedClient(TextInputClient* focused_before, 351 TextInputClient* focused) { 352 // Force to update the input type since client's TextInputStateChanged() 353 // function might not be called if text input types before the client loses 354 // focus and after it acquires focus again are the same. 355 OnTextInputTypeChanged(focused); 356 357 UpdateContextFocusState(); 358 // Force to update caret bounds, in case the client thinks that the caret 359 // bounds has not changed. 360 OnCaretBoundsChanged(focused); 361 } 362 363 void InputMethodIBus::CreateContext() { 364 DCHECK(IsConnected()); 365 366 if (input_context_state_ != INPUT_CONTEXT_STOP) { 367 DVLOG(1) << "Input context is already created or waiting ibus-daemon" 368 " response."; 369 return; 370 } 371 372 input_context_state_ = INPUT_CONTEXT_WAIT_CREATE_INPUT_CONTEXT_RESPONSE; 373 374 // Creates the input context asynchronously. 375 DCHECK(!IsContextReady()); 376 chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext( 377 kClientName, 378 base::Bind(&InputMethodIBus::CreateInputContextDone, 379 weak_ptr_factory_.GetWeakPtr()), 380 base::Bind(&InputMethodIBus::CreateInputContextFail, 381 weak_ptr_factory_.GetWeakPtr())); 382 } 383 384 void InputMethodIBus::SetUpSignalHandlers() { 385 DCHECK(IsContextReady()); 386 387 // We should reset the handler to NULL before |this| is deleted so handler 388 // functions are not called after |this| is deleted. 389 GetInputContextClient()->SetInputContextHandler(this); 390 391 GetInputContextClient()->SetCapabilities( 392 kIBusCapabilityPreeditText | kIBusCapabilityFocus | 393 kIBusCapabilitySurroundingText); 394 395 UpdateContextFocusState(); 396 // Since ibus-daemon is launched in an on-demand basis on Chrome OS, RWHVA (or 397 // equivalents) might call OnCaretBoundsChanged() before the daemon starts. To 398 // save the case, call OnCaretBoundsChanged() here. 399 OnCaretBoundsChanged(GetTextInputClient()); 400 OnInputMethodChanged(); 401 } 402 403 void InputMethodIBus::DestroyContext() { 404 if (input_context_state_ == INPUT_CONTEXT_STOP) 405 return; 406 input_context_state_ = INPUT_CONTEXT_STOP; 407 chromeos::IBusInputContextClient* input_context = GetInputContextClient(); 408 if (!input_context) 409 return; 410 if (input_context->IsObjectProxyReady()) { 411 // We can't use IsContextReady here because we want to destroy object proxy 412 // regardless of connection. The IsContextReady contains connection check. 413 ResetInputContext(); 414 DCHECK(!IsContextReady()); 415 } 416 } 417 418 void InputMethodIBus::ConfirmCompositionText() { 419 TextInputClient* client = GetTextInputClient(); 420 if (client && client->HasCompositionText()) 421 client->ConfirmCompositionText(); 422 423 ResetContext(); 424 } 425 426 void InputMethodIBus::ResetContext() { 427 if (!context_focused_ || !GetTextInputClient()) 428 return; 429 430 DCHECK(system_toplevel_window_focused()); 431 432 // Because ibus runs in asynchronous mode, the input method may still send us 433 // results after sending out the reset request, so we use a flag to discard 434 // all results generated by previous key events. But because ibus does not 435 // have a mechanism to identify each key event and corresponding results, this 436 // approach will not work for some corner cases. For example if the user types 437 // very fast, then the next key event may come in before the |context_| is 438 // really reset. Then we actually cannot know whether or not the next 439 // result should be discard. 440 suppress_next_result_ = true; 441 442 composition_.Clear(); 443 result_text_.clear(); 444 composing_text_ = false; 445 composition_changed_ = false; 446 447 // We need to abandon all pending key events, but as above comment says, there 448 // is no reliable way to abandon all results generated by these abandoned key 449 // events. 450 AbandonAllPendingKeyEvents(); 451 452 // This function runs asynchronously. 453 // Note: some input method engines may not support reset method, such as 454 // ibus-anthy. But as we control all input method engines by ourselves, we can 455 // make sure that all of the engines we are using support it correctly. 456 GetInputContextClient()->Reset(); 457 458 character_composer_.Reset(); 459 } 460 461 void InputMethodIBus::UpdateContextFocusState() { 462 if (!IsContextReady()) { 463 context_focused_ = false; 464 return; 465 } 466 467 const bool old_context_focused = context_focused_; 468 // Use switch here in case we are going to add more text input types. 469 switch (GetTextInputType()) { 470 case TEXT_INPUT_TYPE_NONE: 471 case TEXT_INPUT_TYPE_PASSWORD: 472 context_focused_ = false; 473 break; 474 default: 475 context_focused_ = true; 476 break; 477 } 478 479 // We only focus in |context_| when the focus is in a normal textfield. 480 // ibus_input_context_focus_{in|out}() run asynchronously. 481 if (old_context_focused && !context_focused_) 482 GetInputContextClient()->FocusOut(); 483 else if (!old_context_focused && context_focused_) 484 GetInputContextClient()->FocusIn(); 485 486 if (context_focused_) { 487 uint32 capability = kIBusCapabilityFocus | kIBusCapabilitySurroundingText; 488 if (CanComposeInline()) 489 capability |= kIBusCapabilityPreeditText; 490 GetInputContextClient()->SetCapabilities(capability); 491 } 492 } 493 494 void InputMethodIBus::ProcessKeyEventPostIME( 495 const base::NativeEvent& native_event, 496 uint32 ibus_state, 497 bool handled) { 498 TextInputClient* client = GetTextInputClient(); 499 500 if (!client) { 501 // As ibus works asynchronously, there is a chance that the focused client 502 // loses focus before this method gets called. 503 DispatchKeyEventPostIME(native_event); 504 return; 505 } 506 507 if (native_event->type == KeyPress && handled) 508 ProcessFilteredKeyPressEvent(native_event); 509 510 // In case the focus was changed by the key event. The |context_| should have 511 // been reset when the focused window changed. 512 if (client != GetTextInputClient()) 513 return; 514 515 if (HasInputMethodResult()) 516 ProcessInputMethodResult(native_event, handled); 517 518 // In case the focus was changed when sending input method results to the 519 // focused window. 520 if (client != GetTextInputClient()) 521 return; 522 523 if (native_event->type == KeyPress && !handled) 524 ProcessUnfilteredKeyPressEvent(native_event, ibus_state); 525 else if (native_event->type == KeyRelease) 526 DispatchKeyEventPostIME(native_event); 527 } 528 529 void InputMethodIBus::IBusKeyEventFromNativeKeyEvent( 530 const base::NativeEvent& native_event, 531 uint32* ibus_keyval, 532 uint32* ibus_keycode, 533 uint32* ibus_state) { 534 DCHECK(native_event); // A fabricated event is not supported here. 535 XKeyEvent* x_key = GetKeyEvent(native_event); 536 537 // Yes, ibus uses X11 keysym. We cannot use XLookupKeysym(), which doesn't 538 // translate Shift and CapsLock states. 539 KeySym keysym = NoSymbol; 540 ::XLookupString(x_key, NULL, 0, &keysym, NULL); 541 *ibus_keyval = keysym; 542 *ibus_keycode = x_key->keycode; 543 *ibus_state = IBusStateFromXState(x_key->state); 544 if (native_event->type == KeyRelease) 545 *ibus_state |= kIBusReleaseMask; 546 } 547 548 void InputMethodIBus::ProcessFilteredKeyPressEvent( 549 const base::NativeEvent& native_event) { 550 if (NeedInsertChar()) 551 DispatchKeyEventPostIME(native_event); 552 else 553 DispatchFabricatedKeyEventPostIME( 554 ET_KEY_PRESSED, 555 VKEY_PROCESSKEY, 556 EventFlagsFromXState(GetKeyEvent(native_event)->state)); 557 } 558 559 void InputMethodIBus::ProcessUnfilteredKeyPressEvent( 560 const base::NativeEvent& native_event, 561 uint32 ibus_state) { 562 // For a fabricated event, ProcessUnfilteredFabricatedKeyPressEvent should be 563 // called instead. 564 DCHECK(native_event); 565 566 TextInputClient* client = GetTextInputClient(); 567 DispatchKeyEventPostIME(native_event); 568 569 // We shouldn't dispatch the character anymore if the key event dispatch 570 // caused focus change. For example, in the following scenario, 571 // 1. visit a web page which has a <textarea>. 572 // 2. click Omnibox. 573 // 3. enable Korean IME, press A, then press Tab to move the focus to the web 574 // page. 575 // We should return here not to send the Tab key event to RWHV. 576 if (client != GetTextInputClient()) 577 return; 578 579 const uint32 event_flags = EventFlagsFromXState(ibus_state); 580 581 // If a key event was not filtered by |context_| and |character_composer_|, 582 // then it means the key event didn't generate any result text. So we need 583 // to send corresponding character to the focused text input client. 584 client = GetTextInputClient(); 585 586 uint16 ch = 0; 587 if (!(event_flags & ui::EF_CONTROL_DOWN)) 588 ch = ui::GetCharacterFromXEvent(native_event); 589 if (!ch) { 590 ch = ui::GetCharacterFromKeyCode( 591 ui::KeyboardCodeFromNative(native_event), event_flags); 592 } 593 594 if (client && ch) 595 client->InsertChar(ch, event_flags); 596 } 597 598 void InputMethodIBus::ProcessUnfilteredFabricatedKeyPressEvent( 599 EventType type, 600 KeyboardCode key_code, 601 int event_flags) { 602 TextInputClient* client = GetTextInputClient(); 603 DispatchFabricatedKeyEventPostIME(type, key_code, event_flags); 604 605 if (client != GetTextInputClient()) 606 return; 607 608 client = GetTextInputClient(); 609 const uint16 ch = ui::GetCharacterFromKeyCode(key_code, event_flags); 610 if (client && ch) 611 client->InsertChar(ch, event_flags); 612 } 613 614 void InputMethodIBus::ProcessInputMethodResult( 615 const base::NativeEvent& native_event, 616 bool handled) { 617 TextInputClient* client = GetTextInputClient(); 618 DCHECK(client); 619 620 if (result_text_.length()) { 621 if (handled && NeedInsertChar()) { 622 const uint32 state = 623 EventFlagsFromXState(GetKeyEvent(native_event)->state); 624 for (string16::const_iterator i = result_text_.begin(); 625 i != result_text_.end(); ++i) { 626 client->InsertChar(*i, state); 627 } 628 } else { 629 client->InsertText(result_text_); 630 composing_text_ = false; 631 } 632 } 633 634 if (composition_changed_ && !IsTextInputTypeNone()) { 635 if (composition_.text.length()) { 636 composing_text_ = true; 637 client->SetCompositionText(composition_); 638 } else if (result_text_.empty()) { 639 client->ClearCompositionText(); 640 } 641 } 642 643 // We should not clear composition text here, as it may belong to the next 644 // composition session. 645 result_text_.clear(); 646 composition_changed_ = false; 647 } 648 649 bool InputMethodIBus::NeedInsertChar() const { 650 return GetTextInputClient() && 651 (IsTextInputTypeNone() || 652 (!composing_text_ && result_text_.length() == 1)); 653 } 654 655 bool InputMethodIBus::HasInputMethodResult() const { 656 return result_text_.length() || composition_changed_; 657 } 658 659 void InputMethodIBus::AbandonAllPendingKeyEvents() { 660 pending_key_events_.clear(); 661 } 662 663 void InputMethodIBus::CommitText(const chromeos::IBusText& text) { 664 if (suppress_next_result_ || text.text().empty()) 665 return; 666 667 // We need to receive input method result even if the text input type is 668 // TEXT_INPUT_TYPE_NONE, to make sure we can always send correct 669 // character for each key event to the focused text input client. 670 if (!GetTextInputClient()) 671 return; 672 673 const string16 utf16_text = UTF8ToUTF16(text.text()); 674 if (utf16_text.empty()) 675 return; 676 677 // Append the text to the buffer, because commit signal might be fired 678 // multiple times when processing a key event. 679 result_text_.append(utf16_text); 680 681 // If we are not handling key event, do not bother sending text result if the 682 // focused text input client does not support text input. 683 if (pending_key_events_.empty() && !IsTextInputTypeNone()) { 684 GetTextInputClient()->InsertText(utf16_text); 685 result_text_.clear(); 686 } 687 } 688 689 void InputMethodIBus::ForwardKeyEvent(uint32 keyval, 690 uint32 keycode, 691 uint32 state) { 692 KeyboardCode ui_key_code = KeyboardCodeFromXKeysym(keyval); 693 if (!ui_key_code) 694 return; 695 696 const EventType event_type = 697 (state & kIBusReleaseMask) ? ET_KEY_RELEASED : ET_KEY_PRESSED; 698 const int event_flags = EventFlagsFromXState(state); 699 700 // It is not clear when the input method will forward us a fake key event. 701 // If there is a pending key event, then we may already received some input 702 // method results, so we dispatch this fake key event directly rather than 703 // calling ProcessKeyEventPostIME(), which will clear pending input method 704 // results. 705 if (event_type == ET_KEY_PRESSED) { 706 ProcessUnfilteredFabricatedKeyPressEvent(event_type, ui_key_code, 707 event_flags); 708 } else { 709 DispatchFabricatedKeyEventPostIME(event_type, ui_key_code, event_flags); 710 } 711 } 712 713 void InputMethodIBus::ShowPreeditText() { 714 if (suppress_next_result_ || IsTextInputTypeNone()) 715 return; 716 717 composing_text_ = true; 718 } 719 720 void InputMethodIBus::UpdatePreeditText(const chromeos::IBusText& text, 721 uint32 cursor_pos, 722 bool visible) { 723 if (suppress_next_result_ || IsTextInputTypeNone()) 724 return; 725 726 // |visible| argument is very confusing. For example, what's the correct 727 // behavior when: 728 // 1. OnUpdatePreeditText() is called with a text and visible == false, then 729 // 2. OnShowPreeditText() is called afterwards. 730 // 731 // If it's only for clearing the current preedit text, then why not just use 732 // OnHidePreeditText()? 733 if (!visible) { 734 HidePreeditText(); 735 return; 736 } 737 738 ExtractCompositionText(text, cursor_pos, &composition_); 739 740 composition_changed_ = true; 741 742 // In case OnShowPreeditText() is not called. 743 if (composition_.text.length()) 744 composing_text_ = true; 745 746 // If we receive a composition text without pending key event, then we need to 747 // send it to the focused text input client directly. 748 if (pending_key_events_.empty()) { 749 GetTextInputClient()->SetCompositionText(composition_); 750 composition_changed_ = false; 751 composition_.Clear(); 752 } 753 } 754 755 void InputMethodIBus::HidePreeditText() { 756 if (composition_.text.empty() || IsTextInputTypeNone()) 757 return; 758 759 // Intentionally leaves |composing_text_| unchanged. 760 composition_changed_ = true; 761 composition_.Clear(); 762 763 if (pending_key_events_.empty()) { 764 TextInputClient* client = GetTextInputClient(); 765 if (client && client->HasCompositionText()) 766 client->ClearCompositionText(); 767 composition_changed_ = false; 768 } 769 } 770 771 void InputMethodIBus::DeleteSurroundingText(int32 offset, uint32 length) { 772 if (!composition_.text.empty()) 773 return; // do nothing if there is ongoing composition. 774 if (offset < 0 && static_cast<uint32>(-1 * offset) != length) 775 return; // only preceding text can be deletable. 776 if (GetTextInputClient()) 777 GetTextInputClient()->ExtendSelectionAndDelete(length, 0U); 778 } 779 780 void InputMethodIBus::ResetInputContext() { 781 context_focused_ = false; 782 783 ConfirmCompositionText(); 784 785 // We are dead, so we need to ask the client to stop relying on us. 786 OnInputMethodChanged(); 787 GetInputContextClient()->ResetObjectProxy(); 788 } 789 790 void InputMethodIBus::CreateInputContextDone( 791 const dbus::ObjectPath& object_path) { 792 DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_); 793 794 if (input_context_state_ == INPUT_CONTEXT_STOP) { 795 // DestroyContext has already been called. 796 return; 797 } 798 799 chromeos::DBusThreadManager::Get()->GetIBusInputContextClient() 800 ->Initialize(chromeos::DBusThreadManager::Get()->GetIBusBus(), 801 object_path); 802 803 input_context_state_ = INPUT_CONTEXT_RUNNING; 804 DCHECK(IsContextReady()); 805 SetUpSignalHandlers(); 806 } 807 808 void InputMethodIBus::CreateInputContextFail() { 809 DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_); 810 if (input_context_state_ == INPUT_CONTEXT_STOP) { 811 // CreateInputContext failed but the input context is no longer 812 // necessary, thus do nothing. 813 return; 814 } 815 816 if (++create_input_context_fail_count_ >= kMaxRetryCount) { 817 DVLOG(1) << "CreateInputContext failed even tried " 818 << kMaxRetryCount << " times, give up."; 819 create_input_context_fail_count_ = 0; 820 input_context_state_ = INPUT_CONTEXT_STOP; 821 return; 822 } 823 824 // Try CreateInputContext again. 825 chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext( 826 kClientName, 827 base::Bind(&InputMethodIBus::CreateInputContextDone, 828 weak_ptr_factory_.GetWeakPtr()), 829 base::Bind(&InputMethodIBus::CreateInputContextFail, 830 weak_ptr_factory_.GetWeakPtr())); 831 } 832 833 bool InputMethodIBus::IsConnected() { 834 return chromeos::DBusThreadManager::Get()->GetIBusBus() != NULL; 835 } 836 837 bool InputMethodIBus::IsContextReady() { 838 if (!IsConnected()) 839 return false; 840 if (!GetInputContextClient()) 841 return false; 842 return GetInputContextClient()->IsObjectProxyReady(); 843 } 844 845 bool InputMethodIBus::ExecuteCharacterComposer(uint32 ibus_keyval, 846 uint32 ibus_keycode, 847 uint32 ibus_state) { 848 bool consumed = character_composer_.FilterKeyPress( 849 ibus_keyval, 850 ibus_keycode, 851 EventFlagsFromXState(ibus_state)); 852 853 suppress_next_result_ = false; 854 chromeos::IBusText preedit; 855 preedit.set_text( 856 UTF16ToUTF8(character_composer_.preedit_string())); 857 UpdatePreeditText(preedit, preedit.text().size(), 858 !preedit.text().empty()); 859 std::string commit_text = 860 UTF16ToUTF8(character_composer_.composed_character()); 861 if (!commit_text.empty()) { 862 chromeos::IBusText ibus_text; 863 ibus_text.set_text(commit_text); 864 CommitText(ibus_text); 865 } 866 return consumed; 867 } 868 869 void InputMethodIBus::OnConnected() { 870 DCHECK(IsConnected()); 871 // If already input context is initialized, do nothing. 872 if (IsContextReady()) 873 return; 874 875 DestroyContext(); 876 CreateContext(); 877 } 878 879 void InputMethodIBus::OnDisconnected() { 880 DestroyContext(); 881 } 882 883 void InputMethodIBus::ExtractCompositionText( 884 const chromeos::IBusText& text, 885 uint32 cursor_position, 886 CompositionText* out_composition) const { 887 out_composition->Clear(); 888 out_composition->text = UTF8ToUTF16(text.text()); 889 890 if (out_composition->text.empty()) 891 return; 892 893 // ibus uses character index for cursor position and attribute range, but we 894 // use char16 offset for them. So we need to do conversion here. 895 std::vector<size_t> char16_offsets; 896 size_t length = out_composition->text.length(); 897 base::i18n::UTF16CharIterator char_iterator(&out_composition->text); 898 do { 899 char16_offsets.push_back(char_iterator.array_pos()); 900 } while (char_iterator.Advance()); 901 902 // The text length in Unicode characters. 903 uint32 char_length = static_cast<uint32>(char16_offsets.size()); 904 // Make sure we can convert the value of |char_length| as well. 905 char16_offsets.push_back(length); 906 907 size_t cursor_offset = 908 char16_offsets[std::min(char_length, cursor_position)]; 909 910 out_composition->selection = Range(cursor_offset); 911 912 const std::vector<chromeos::IBusText::UnderlineAttribute>& 913 underline_attributes = text.underline_attributes(); 914 const std::vector<chromeos::IBusText::SelectionAttribute>& 915 selection_attributes = text.selection_attributes(); 916 917 if (!underline_attributes.empty()) { 918 for (size_t i = 0; i < underline_attributes.size(); ++i) { 919 const uint32 start = underline_attributes[i].start_index; 920 const uint32 end = underline_attributes[i].end_index; 921 if (start >= end) 922 continue; 923 CompositionUnderline underline( 924 char16_offsets[start], char16_offsets[end], 925 SK_ColorBLACK, false /* thick */); 926 if (underline_attributes[i].type == 927 chromeos::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE) 928 underline.thick = true; 929 else if (underline_attributes[i].type == 930 chromeos::IBusText::IBUS_TEXT_UNDERLINE_ERROR) 931 underline.color = SK_ColorRED; 932 out_composition->underlines.push_back(underline); 933 } 934 } 935 936 if (!selection_attributes.empty()) { 937 LOG_IF(ERROR, selection_attributes.size() != 1) 938 << "Chrome does not support multiple selection"; 939 for (uint32 i = 0; i < selection_attributes.size(); ++i) { 940 const uint32 start = selection_attributes[i].start_index; 941 const uint32 end = selection_attributes[i].end_index; 942 if (start >= end) 943 continue; 944 CompositionUnderline underline( 945 char16_offsets[start], char16_offsets[end], 946 SK_ColorBLACK, true /* thick */); 947 out_composition->underlines.push_back(underline); 948 // If the cursor is at start or end of this underline, then we treat 949 // it as the selection range as well, but make sure to set the cursor 950 // position to the selection end. 951 if (underline.start_offset == cursor_offset) { 952 out_composition->selection.set_start(underline.end_offset); 953 out_composition->selection.set_end(cursor_offset); 954 } else if (underline.end_offset == cursor_offset) { 955 out_composition->selection.set_start(underline.start_offset); 956 out_composition->selection.set_end(cursor_offset); 957 } 958 } 959 } 960 961 // Use a black thin underline by default. 962 if (out_composition->underlines.empty()) { 963 out_composition->underlines.push_back(CompositionUnderline( 964 0, length, SK_ColorBLACK, false /* thick */)); 965 } 966 } 967 968 } // namespace ui 969