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 "chromeos/dbus/ibus/ibus_input_context_client.h" 6 7 #include <string> 8 #include "base/bind.h" 9 #include "base/callback.h" 10 #include "chromeos/dbus/ibus/ibus_constants.h" 11 #include "chromeos/dbus/ibus/ibus_engine_service.h" 12 #include "chromeos/dbus/ibus/ibus_panel_service.h" 13 #include "chromeos/dbus/ibus/ibus_text.h" 14 #include "chromeos/ime/ibus_bridge.h" 15 #include "dbus/bus.h" 16 #include "dbus/message.h" 17 #include "dbus/object_path.h" 18 #include "dbus/object_proxy.h" 19 20 namespace chromeos { 21 22 using chromeos::IBusText; 23 24 namespace { 25 26 // The IBusInputContextClient implementation. 27 class IBusInputContextClientImpl : public IBusInputContextClient { 28 public: 29 IBusInputContextClientImpl() 30 : proxy_(NULL), 31 is_xkb_layout_(true), 32 weak_ptr_factory_(this) { 33 } 34 35 virtual ~IBusInputContextClientImpl() {} 36 37 public: 38 // IBusInputContextClient override. 39 virtual void Initialize(dbus::Bus* bus, 40 const dbus::ObjectPath& object_path) OVERRIDE { 41 if (proxy_ != NULL) { 42 LOG(ERROR) << "IBusInputContextClient is already initialized."; 43 return; 44 } 45 proxy_ = bus->GetObjectProxy(ibus::kServiceName, object_path); 46 47 ConnectSignals(); 48 } 49 50 // IBusInputContextClient override. 51 virtual void SetInputContextHandler( 52 IBusInputContextHandlerInterface* handler) OVERRIDE { 53 handler_ = handler; 54 } 55 56 // IBusInputContextClient override. 57 virtual void SetSetCursorLocationHandler( 58 const SetCursorLocationHandler& set_cursor_location_handler) OVERRIDE { 59 DCHECK(!set_cursor_location_handler.is_null()); 60 set_cursor_location_handler_ = set_cursor_location_handler; 61 } 62 63 // IBusInputContextClient override. 64 virtual void UnsetSetCursorLocationHandler() OVERRIDE { 65 set_cursor_location_handler_.Reset(); 66 } 67 68 // IBusInputContextClient override. 69 virtual void ResetObjectProxy() OVERRIDE { 70 // Do not delete proxy here, proxy object is managed by dbus::Bus object. 71 proxy_ = NULL; 72 } 73 74 // IBusInputContextClient override. 75 virtual bool IsObjectProxyReady() const OVERRIDE { 76 return proxy_ != NULL; 77 } 78 79 // IBusInputContextClient override. 80 virtual void SetCapabilities(uint32 capabilities) OVERRIDE { 81 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 82 ibus::input_context::kSetCapabilitiesMethod); 83 dbus::MessageWriter writer(&method_call); 84 writer.AppendUint32(capabilities); 85 CallNoResponseMethod(&method_call, 86 ibus::input_context::kSetCapabilitiesMethod); 87 } 88 89 // IBusInputContextClient override. 90 virtual void FocusIn() OVERRIDE { 91 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 92 ibus::input_context::kFocusInMethod); 93 CallNoResponseMethod(&method_call, ibus::input_context::kFocusInMethod); 94 } 95 96 // IBusInputContextClient override. 97 virtual void FocusOut() OVERRIDE { 98 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 99 ibus::input_context::kFocusOutMethod); 100 CallNoResponseMethod(&method_call, ibus::input_context::kFocusOutMethod); 101 } 102 103 // IBusInputContextClient override. 104 virtual void Reset() OVERRIDE { 105 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 106 ibus::input_context::kResetMethod); 107 CallNoResponseMethod(&method_call, ibus::input_context::kResetMethod); 108 } 109 110 // IBusInputContextClient override. 111 virtual void SetCursorLocation(const ibus::Rect& cursor_location, 112 const ibus::Rect& composition_head) OVERRIDE { 113 if (!set_cursor_location_handler_.is_null()) 114 set_cursor_location_handler_.Run(cursor_location, composition_head); 115 } 116 117 // IBusInputContextClient override. 118 virtual void ProcessKeyEvent( 119 uint32 keyval, 120 uint32 keycode, 121 uint32 state, 122 const ProcessKeyEventCallback& callback, 123 const ErrorCallback& error_callback) OVERRIDE { 124 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 125 ibus::input_context::kProcessKeyEventMethod); 126 dbus::MessageWriter writer(&method_call); 127 writer.AppendUint32(keyval); 128 writer.AppendUint32(keycode); 129 writer.AppendUint32(state); 130 proxy_->CallMethodWithErrorCallback( 131 &method_call, 132 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 133 base::Bind(&IBusInputContextClientImpl::OnProcessKeyEvent, 134 callback, 135 error_callback), 136 base::Bind(&IBusInputContextClientImpl::OnProecessKeyEventFail, 137 error_callback)); 138 } 139 140 // IBusInputContextClient override. 141 virtual void SetSurroundingText(const std::string& text, 142 uint32 start_index, 143 uint32 end_index) OVERRIDE { 144 dbus::MethodCall method_call( 145 ibus::input_context::kServiceInterface, 146 ibus::input_context::kSetSurroundingTextMethod); 147 dbus::MessageWriter writer(&method_call); 148 AppendStringAsIBusText(text, &writer); 149 writer.AppendUint32(start_index); 150 writer.AppendUint32(end_index); 151 CallNoResponseMethod(&method_call, 152 ibus::input_context::kSetSurroundingTextMethod); 153 } 154 155 // IBusInputContextClient override. 156 virtual void PropertyActivate(const std::string& key, 157 ibus::IBusPropertyState state) OVERRIDE { 158 dbus::MethodCall method_call(ibus::input_context::kServiceInterface, 159 ibus::input_context::kPropertyActivateMethod); 160 dbus::MessageWriter writer(&method_call); 161 writer.AppendString(key); 162 if (state == ibus::IBUS_PROPERTY_STATE_CHECKED) { 163 writer.AppendUint32(ibus::IBUS_PROPERTY_STATE_CHECKED); 164 } else { 165 writer.AppendUint32(ibus::IBUS_PROPERTY_STATE_UNCHECKED); 166 } 167 CallNoResponseMethod(&method_call, 168 ibus::input_context::kPropertyActivateMethod); 169 } 170 171 // IBusInputContextClient override. 172 virtual bool IsXKBLayout() OVERRIDE { 173 return is_xkb_layout_; 174 } 175 176 // IBusInputContextClient override. 177 virtual void SetIsXKBLayout(bool is_xkb_layout) OVERRIDE { 178 is_xkb_layout_ = is_xkb_layout; 179 } 180 181 private: 182 void CallNoResponseMethod(dbus::MethodCall* method_call, 183 const std::string& method_name) { 184 proxy_->CallMethodWithErrorCallback( 185 method_call, 186 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 187 base::Bind(&IBusInputContextClientImpl::DefaultCallback, 188 method_name), 189 base::Bind(&IBusInputContextClientImpl::DefaultErrorCallback, 190 method_name)); 191 } 192 193 // Handles no response method call reply. 194 static void DefaultCallback(const std::string& method_name, 195 dbus::Response* response) { 196 if (!response) { 197 LOG(ERROR) << "Failed to call method: " << method_name; 198 return; 199 } 200 } 201 202 // Handles error response of default method call. 203 static void DefaultErrorCallback(const std::string& method_name, 204 dbus::ErrorResponse* response) { 205 LOG(ERROR) << "Failed to call method: " << method_name; 206 } 207 208 // Handles ProcessKeyEvent method call reply. 209 static void OnProcessKeyEvent(const ProcessKeyEventCallback& callback, 210 const ErrorCallback& error_callback, 211 dbus::Response* response) { 212 if (!response) { 213 LOG(ERROR) << "Cannot get input context: " << response->ToString(); 214 error_callback.Run(); 215 return; 216 } 217 dbus::MessageReader reader(response); 218 bool is_keyevent_used; 219 if (!reader.PopBool(&is_keyevent_used)) { 220 // The IBus message structure may be changed. 221 LOG(ERROR) << "Invalid response: " << response->ToString(); 222 error_callback.Run(); 223 return; 224 } 225 DCHECK(!callback.is_null()); 226 callback.Run(is_keyevent_used); 227 } 228 229 // Handles error response of ProcessKeyEvent method call. 230 static void OnProecessKeyEventFail(const ErrorCallback& error_callback, 231 dbus::ErrorResponse* response) { 232 error_callback.Run(); 233 } 234 235 // Handles CommitText signal. 236 void OnCommitText(dbus::Signal* signal) { 237 if (!handler_) 238 return; 239 dbus::MessageReader reader(signal); 240 IBusText ibus_text; 241 if (!PopIBusText(&reader, &ibus_text)) { 242 // The IBus message structure may be changed. 243 LOG(ERROR) << "Invalid signal: " << signal->ToString(); 244 return; 245 } 246 handler_->CommitText(ibus_text); 247 } 248 249 // Handles ForwardKeyEvetn signal. 250 void OnForwardKeyEvent(dbus::Signal* signal) { 251 if (!handler_) 252 return; 253 dbus::MessageReader reader(signal); 254 uint32 keyval = 0; 255 uint32 keycode = 0; 256 uint32 state = 0; 257 if (!reader.PopUint32(&keyval) || 258 !reader.PopUint32(&keycode) || 259 !reader.PopUint32(&state)) { 260 // The IBus message structure may be changed. 261 LOG(ERROR) << "Invalid signal: " << signal->ToString(); 262 return; 263 } 264 handler_->ForwardKeyEvent(keyval, keycode, state); 265 } 266 267 // Handles UpdatePreeditText signal. 268 void OnUpdatePreeditText(dbus::Signal* signal) { 269 if (!handler_) 270 return; 271 dbus::MessageReader reader(signal); 272 IBusText ibus_text; 273 uint32 cursor_pos = 0; 274 bool visible = true; 275 if (!PopIBusText(&reader, &ibus_text) || 276 !reader.PopUint32(&cursor_pos) || 277 !reader.PopBool(&visible)) { 278 // The IBus message structure may be changed. 279 LOG(ERROR) << "Invalid signal: " << signal->ToString(); 280 return; 281 } 282 handler_->UpdatePreeditText(ibus_text, cursor_pos, visible); 283 } 284 285 // Handles ShowPreeditText signal. 286 void OnShowPreeditText(dbus::Signal* signal) { 287 if (handler_) 288 handler_->ShowPreeditText(); 289 } 290 291 // Handles HidePreeditText signal. 292 void OnHidePreeditText(dbus::Signal* signal) { 293 if (handler_) 294 handler_->HidePreeditText(); 295 } 296 297 // Handle DeleteSurroundingText signal. 298 void OnDeleteSurroundingText(dbus::Signal* signal) { 299 if (!handler_) 300 return; 301 302 dbus::MessageReader reader(signal); 303 int32 offset = 0; 304 uint32 length = 0; 305 if (!reader.PopInt32(&offset) || !reader.PopUint32(&length)) { 306 // The IBus message structure may be changed. 307 LOG(ERROR) << "Invalid signal: " << signal->ToString(); 308 return; 309 } 310 handler_->DeleteSurroundingText(offset, length); 311 } 312 313 // Connects signals to signal handlers. 314 void ConnectSignals() { 315 proxy_->ConnectToSignal( 316 ibus::input_context::kServiceInterface, 317 ibus::input_context::kCommitTextSignal, 318 base::Bind(&IBusInputContextClientImpl::OnCommitText, 319 weak_ptr_factory_.GetWeakPtr()), 320 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 321 weak_ptr_factory_.GetWeakPtr())); 322 323 proxy_->ConnectToSignal( 324 ibus::input_context::kServiceInterface, 325 ibus::input_context::kForwardKeyEventSignal, 326 base::Bind(&IBusInputContextClientImpl::OnForwardKeyEvent, 327 weak_ptr_factory_.GetWeakPtr()), 328 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 329 weak_ptr_factory_.GetWeakPtr())); 330 331 proxy_->ConnectToSignal( 332 ibus::input_context::kServiceInterface, 333 ibus::input_context::kUpdatePreeditTextSignal, 334 base::Bind(&IBusInputContextClientImpl::OnUpdatePreeditText, 335 weak_ptr_factory_.GetWeakPtr()), 336 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 337 weak_ptr_factory_.GetWeakPtr())); 338 339 proxy_->ConnectToSignal( 340 ibus::input_context::kServiceInterface, 341 ibus::input_context::kShowPreeditTextSignal, 342 base::Bind(&IBusInputContextClientImpl::OnShowPreeditText, 343 weak_ptr_factory_.GetWeakPtr()), 344 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 345 weak_ptr_factory_.GetWeakPtr())); 346 347 proxy_->ConnectToSignal( 348 ibus::input_context::kServiceInterface, 349 ibus::input_context::kHidePreeditTextSignal, 350 base::Bind(&IBusInputContextClientImpl::OnHidePreeditText, 351 weak_ptr_factory_.GetWeakPtr()), 352 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 353 weak_ptr_factory_.GetWeakPtr())); 354 355 proxy_->ConnectToSignal( 356 ibus::input_context::kServiceInterface, 357 ibus::input_context::kDeleteSurroundingTextSignal, 358 base::Bind(&IBusInputContextClientImpl::OnDeleteSurroundingText, 359 weak_ptr_factory_.GetWeakPtr()), 360 base::Bind(&IBusInputContextClientImpl::OnSignalConnected, 361 weak_ptr_factory_.GetWeakPtr())); 362 } 363 364 // Handles the result of signal connection setup. 365 void OnSignalConnected(const std::string& interface, 366 const std::string& signal, 367 bool succeeded) { 368 LOG_IF(ERROR, !succeeded) << "Connect to " << interface << " " 369 << signal << " failed."; 370 } 371 372 dbus::ObjectProxy* proxy_; 373 374 // The pointer for input context handler. This can be NULL. 375 IBusInputContextHandlerInterface* handler_; 376 377 SetCursorLocationHandler set_cursor_location_handler_; 378 379 // True if the current input method is xkb layout. 380 bool is_xkb_layout_; 381 382 base::WeakPtrFactory<IBusInputContextClientImpl> weak_ptr_factory_; 383 384 DISALLOW_COPY_AND_ASSIGN(IBusInputContextClientImpl); 385 }; 386 387 // An implementation of IBusInputContextClient without ibus-daemon interaction. 388 // Currently this class is used only on linux desktop. 389 // TODO(nona): Use this on ChromeOS device once crbug.com/171351 is fixed. 390 class IBusInputContextClientDaemonlessImpl : public IBusInputContextClient { 391 public: 392 IBusInputContextClientDaemonlessImpl() 393 : is_xkb_layout_(true), 394 initialized_(false) 395 {} 396 virtual ~IBusInputContextClientDaemonlessImpl() {} 397 398 // IBusInputContextClient override. 399 virtual void Initialize(dbus::Bus* bus, 400 const dbus::ObjectPath& object_path) OVERRIDE { 401 initialized_ = true; 402 } 403 404 virtual void SetInputContextHandler( 405 IBusInputContextHandlerInterface* handler) OVERRIDE { 406 IBusBridge::Get()->SetInputContextHandler(handler); 407 } 408 409 virtual void SetSetCursorLocationHandler( 410 const SetCursorLocationHandler& set_cursor_location_handler) OVERRIDE { 411 } 412 413 virtual void UnsetSetCursorLocationHandler() OVERRIDE { 414 } 415 416 virtual void ResetObjectProxy() OVERRIDE { 417 initialized_ = false; 418 } 419 420 virtual bool IsObjectProxyReady() const OVERRIDE { 421 return initialized_; 422 } 423 424 virtual void SetCapabilities(uint32 capability) OVERRIDE { 425 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 426 if (engine) 427 engine->SetCapability( 428 static_cast<IBusEngineHandlerInterface::IBusCapability>(capability)); 429 } 430 431 virtual void FocusIn() OVERRIDE { 432 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 433 if (engine) 434 engine->FocusIn(); 435 } 436 437 virtual void FocusOut() OVERRIDE { 438 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 439 if (engine) 440 engine->FocusOut(); 441 } 442 443 virtual void Reset() OVERRIDE { 444 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 445 if (engine) 446 engine->Reset(); 447 } 448 449 virtual void SetCursorLocation(const ibus::Rect& cursor_location, 450 const ibus::Rect& composition_head) OVERRIDE { 451 IBusPanelCandidateWindowHandlerInterface* candidate_window = 452 IBusBridge::Get()->GetCandidateWindowHandler(); 453 454 if (candidate_window) 455 candidate_window->SetCursorLocation(cursor_location, composition_head); 456 } 457 458 virtual void ProcessKeyEvent( 459 uint32 keyval, 460 uint32 keycode, 461 uint32 state, 462 const ProcessKeyEventCallback& callback, 463 const ErrorCallback& error_callback) OVERRIDE { 464 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 465 if (engine) 466 engine->ProcessKeyEvent(keyval, keycode, state, callback); 467 } 468 469 virtual void SetSurroundingText(const std::string& text, 470 uint32 start_index, 471 uint32 end_index) OVERRIDE { 472 // TODO(nona): Implement this. 473 } 474 475 virtual void PropertyActivate(const std::string& key, 476 ibus::IBusPropertyState state) OVERRIDE { 477 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler(); 478 if (engine) 479 engine->PropertyActivate(key, 480 static_cast<ibus::IBusPropertyState>(state)); 481 } 482 483 virtual bool IsXKBLayout() OVERRIDE { 484 return is_xkb_layout_; 485 } 486 487 virtual void SetIsXKBLayout(bool is_xkb_layout) OVERRIDE { 488 is_xkb_layout_ = is_xkb_layout; 489 } 490 491 private: 492 // True if the current input method is xkb layout. 493 bool is_xkb_layout_; 494 bool initialized_; 495 496 DISALLOW_COPY_AND_ASSIGN(IBusInputContextClientDaemonlessImpl); 497 }; 498 499 } // namespace 500 501 /////////////////////////////////////////////////////////////////////////////// 502 // IBusInputContextClient 503 504 IBusInputContextClient::IBusInputContextClient() {} 505 506 IBusInputContextClient::~IBusInputContextClient() {} 507 508 // static 509 IBusInputContextClient* IBusInputContextClient::Create( 510 DBusClientImplementationType type) { 511 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) { 512 return new IBusInputContextClientImpl(); 513 } 514 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type); 515 return new IBusInputContextClientDaemonlessImpl(); 516 } 517 } // namespace chromeos 518