Home | History | Annotate | Download | only in ibus
      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