Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / xwalk / ime / tizen-scim / scim_bridge.cc
1 // Copyright (c) 2013 Intel Corporation. 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 "xwalk/ime/tizen-scim/scim_bridge.h"
6
7 #define Uses_SCIM_BACKEND
8 #define Uses_SCIM_IMENGINE_MODULE
9 #define Uses_SCIM_HELPER_MODULE
10 #define Uses_SCIM_PANEL_CLIENT
11
12 #include <scim.h>
13 #include <x11/scim_x11_utils.h>
14 #include <isf_imcontrol_client.h>
15
16 #include <X11/Xlib.h>
17 #include <X11/XKBlib.h>
18 #include <X11/Xlibint.h>
19
20 #include <string>
21 #include <vector>
22
23 #include "base/logging.h"
24 #include "base/message_loop/message_loop.h"
25 #include "base/message_loop/message_pump_libevent.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "ui/base/x/x11_util.h"
28 #include "ui/gfx/x/x11_types.h"
29
30 using scim;
31
32 const String kScimFrontendSocketIpc = "socket";
33 const String kScimDummyConfig = "dummy";
34 const String kScimSimpleConfig = "simple";
35 const String kScimConnectionTester = "ConnectionTester";
36 const String kScimSocketFrontend = "SocketFrontEnd";
37 const String kScimFactoryEncoding = "UTF-8";
38 const char* kISFActiveWindowAtom  = "_ISF_ACTIVE_WINDOW";
39 const char* kWindowAtomType = "WINDOW";
40
41 typedef struct {
42     int language;
43     int layout;
44     int return_key_type;
45     Window client_window;
46     int imdata_size;
47     int cursor_pos;
48     bool return_key_disabled;
49     bool prediction_allow;
50     bool password_mode;
51     bool caps_mode;
52     int layout_variation;
53     int reserved[248];
54 } InputSystemEngineContext;
55
56 namespace ui {
57
58 class SCIMBridgeImpl : base::MessageLoopForIO::Watcher {
59  public:
60   SCIMBridgeImpl();
61   ~SCIMBridgeImpl();
62
63   void Init();
64   bool IsInitialized() {return is_initialized_;}
65   void ShowInputMethodPanel();
66   void HideInputMethodPanel();
67   void SetFocusedWindow(uint32_t xid);
68
69  private:
70   bool IsDaemonRunning();
71   void StartDaemon();
72   void InitConfigModule();
73   void InitBackend();
74   void InitPanelClient();
75   void ConnectPanelClientSignals();
76   void DisconnectPanelClient();
77   void InitIMEngineFactory();
78
79   // SCIM PanelClient callbacks.
80   void OnUpdateClientId(int context, int client_id);
81   void OnProcessHelperEvent(int context,
82                               const String &target_uuid,
83                               const String &helper_uuid,
84                               const Transaction &trans);
85   void OnResetKeyboardIse(int context);
86   void OnHidePreeditString(int context);
87   void OnProcessKeyEvent(int context, const KeyEvent &key);
88   void OnCommitString(int context, const WideString &wstr);
89
90   void SendXKeyEvent(const KeyEvent &key);
91   void SetInputMethodActiveWindow();
92
93   void OnFileCanReadWithoutBlocking(int fd) override;
94   void OnFileCanWriteWithoutBlocking(int fd) override {}
95   void WatchSCIMFd();
96   void HandleScimEvent();
97
98  private:
99   bool is_initialized_;
100   std::string language_code_;
101   String config_module_name_;
102   scoped_ptr<ConfigModule> config_module_;
103   ConfigPointer config_;
104   std::vector<String> config_list_;
105   std::vector<String> scim_modules_;
106   BackEndPointer backend_;
107   scoped_ptr<PanelClient> panel_client_;
108   int panel_client_id_;
109   int panel_client_fd_;
110   base::MessageLoopForIO::FileDescriptorWatcher read_fd_controller_;
111   IMEngineInstancePointer im_engine_instance_;
112   scoped_ptr<IMControlClient> imcontrol_client_;
113   InputSystemEngineContext ise_context_;
114   unsigned int im_context_id_;
115   int im_engine_instance_count_;
116
117   Display* display_;
118   Window window_;
119   Window root_window_;
120
121   DISALLOW_COPY_AND_ASSIGN(SCIMBridgeImpl);
122 };
123
124 SCIMBridge::SCIMBridge() : impl_(new SCIMBridgeImpl()) {
125 }
126
127 SCIMBridge::~SCIMBridge() {
128 }
129
130 void SCIMBridge::Init() {
131   if (impl_->IsInitialized())
132     return;
133
134   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
135     content::BrowserThread::PostTask(content::BrowserThread::IO,
136                             FROM_HERE,
137                             base::Bind(&SCIMBridgeImpl::Init,
138                                        base::Unretained(impl_.get())));
139   }
140 }
141
142 void SCIMBridge::SetFocusedWindow(uint32_t xid) {
143   impl_->SetFocusedWindow(xid);
144 }
145
146 void SCIMBridge::TextInputChanged(ui::TextInputType type) {
147   // TODO(shalamov): set proper ise_context_ fields according to type
148   if (type == ui::TEXT_INPUT_TYPE_NONE) {
149     content::BrowserThread::PostTask(content::BrowserThread::IO,
150                             FROM_HERE,
151                             base::Bind(&SCIMBridgeImpl::HideInputMethodPanel,
152                                        base::Unretained(impl_.get())));
153
154   } else {
155     content::BrowserThread::PostTask(content::BrowserThread::IO,
156                             FROM_HERE,
157                             base::Bind(&SCIMBridgeImpl::ShowInputMethodPanel,
158                                        base::Unretained(impl_.get())));
159   }
160 }
161
162 SCIMBridgeImpl::SCIMBridgeImpl()
163   : is_initialized_(false),
164     config_module_name_(kScimSimpleConfig),
165     panel_client_id_(0),
166     panel_client_fd_(-1),
167     im_context_id_(getpid() % 50000),
168     im_engine_instance_count_(0),
169     display_(gfx::GetXDisplay()),
170     root_window_(GetX11RootWindow()) {
171   // Create input system engine context data that would be sent to the daemon.
172   // TODO(shalamov): Add SCIM enums.
173   ise_context_.language = 0;
174   ise_context_.layout = 0;
175   ise_context_.return_key_type = 0;
176   ise_context_.imdata_size = 0;
177   ise_context_.return_key_disabled = false;
178   ise_context_.prediction_allow = true;
179   ise_context_.password_mode = false;
180   ise_context_.caps_mode = false;
181   ise_context_.layout_variation = 0;
182   ise_context_.return_key_disabled = false;
183 }
184
185 SCIMBridgeImpl::~SCIMBridgeImpl() {
186   if (panel_client_)
187     DisconnectPanelClient();
188 }
189
190 void SCIMBridgeImpl::Init() {
191   language_code_ = scim_get_locale_language(scim_get_current_locale());
192   imcontrol_client_.reset(new IMControlClient());
193   imcontrol_client_->open_connection();
194
195   StartDaemon();
196   InitConfigModule();
197   InitBackend();
198   InitIMEngineFactory();
199   InitPanelClient();
200   ConnectPanelClientSignals();
201   is_initialized_ = true;
202 }
203
204 void SCIMBridgeImpl::ShowInputMethodPanel() {
205   int ise_packet_length = sizeof(ise_context_);
206   void* ise_packet = calloc(1, ise_packet_length);
207   memcpy(ise_packet, reinterpret_cast<void *>(&ise_context_),
208          ise_packet_length);
209
210   panel_client_->prepare(im_context_id_);
211   panel_client_->focus_in(im_context_id_,
212                           im_engine_instance_->get_factory_uuid());
213   panel_client_->send();
214
215   imcontrol_client_->prepare();
216   int input_panel_show = 0;
217   imcontrol_client_->show_ise(panel_client_id_, im_context_id_, ise_packet,
218       ise_packet_length, &input_panel_show);
219   imcontrol_client_->send();
220
221   free(ise_packet);
222 }
223
224 void SCIMBridgeImpl::HideInputMethodPanel() {
225   imcontrol_client_->prepare();
226   imcontrol_client_->hide_ise(panel_client_id_, im_context_id_);
227   imcontrol_client_->send();
228 }
229
230 bool SCIMBridgeImpl::IsDaemonRunning() {
231   uint32_t uid;
232   SocketClient client;
233   SocketAddress address;
234   address.set_address(scim_get_default_socket_frontend_address());
235
236   if (!client.connect(address))
237       return false;
238
239   return scim_socket_open_connection(uid, kScimConnectionTester,
240                                      kScimSocketFrontend, client, 1000);
241 }
242
243 void SCIMBridgeImpl::StartDaemon() {
244   if (!IsDaemonRunning()) {
245     std::vector<String> helper_modules;
246     std::vector<String> imengine_modules;
247
248     scim_get_imengine_module_list(imengine_modules);
249     scim_get_helper_module_list(helper_modules);
250
251     std::vector<String>::iterator it;
252
253     for (it = imengine_modules.begin(); it != imengine_modules.end(); it++) {
254       if (*it != kScimFrontendSocketIpc)
255           scim_modules_.push_back(*it);
256     }
257
258     for (it = helper_modules.begin(); it != helper_modules.end(); it++)
259       scim_modules_.push_back(*it);
260
261     const String engines =
262         (scim_modules_.size() > 0 ? scim_combine_string_list(scim_modules_, ',')
263                                   : "none");
264     const char *argv[] = { "--no-stay", 0 };
265     scim_launch(true, config_module_name_, engines, kScimFrontendSocketIpc,
266         reinterpret_cast<char **>(argv));
267   }
268 }
269
270 void SCIMBridgeImpl::InitConfigModule() {
271   config_module_.reset(new ConfigModule(config_module_name_));
272   if (config_module_->valid()) {
273       config_ = config_module_->create_config();
274   } else {
275     LOG(WARNING) << "Cannot init scim config module: " << config_module_name_;
276     config_module_.reset();
277     config_ = new DummyConfig();
278     config_module_name_ = kScimDummyConfig;
279   }
280 }
281
282 void SCIMBridgeImpl::InitBackend() {
283   backend_ = new CommonBackEnd(config_, scim_modules_);
284   if (!backend_.null()) {
285     backend_->initialize(config_, scim_modules_, false, false);
286   } else {
287     LOG(ERROR) << "Cannot create backend";
288   }
289 }
290
291 void SCIMBridgeImpl::InitPanelClient() {
292   if (!panel_client_.get())
293     panel_client_.reset(new PanelClient());
294
295   if (panel_client_->open_connection(config_->get_name(),
296                                      display_->display_name) >= 0) {
297       panel_client_fd_ = panel_client_->get_connection_number();
298       content::BrowserThread::PostTask(content::BrowserThread::IO,
299                               FROM_HERE,
300                               base::Bind(&SCIMBridgeImpl::WatchSCIMFd,
301                                          base::Unretained(this)));
302
303       panel_client_->prepare(im_context_id_);
304       panel_client_->register_input_context(im_context_id_,
305                                       im_engine_instance_->get_factory_uuid());
306       panel_client_->send();
307   } else {
308     LOG(ERROR) << "Cannot get panel client connection";
309   }
310 }
311
312 void SCIMBridgeImpl::ConnectPanelClientSignals() {
313   // FIXME(shalamov):  attach rest of the signals
314   panel_client_->signal_connect_update_client_id(
315       slot(this, &SCIMBridgeImpl::OnUpdateClientId));
316   panel_client_->signal_connect_process_helper_event(
317       slot(this, &SCIMBridgeImpl::OnProcessHelperEvent));
318   panel_client_->signal_connect_reset_keyboard_ise(
319       slot(this, &SCIMBridgeImpl::OnResetKeyboardIse));
320   panel_client_->signal_connect_hide_preedit_string(
321       slot(this, &SCIMBridgeImpl::OnHidePreeditString));
322   panel_client_->signal_connect_process_key_event(
323       slot(this, &SCIMBridgeImpl::OnProcessKeyEvent));
324   panel_client_->signal_connect_commit_string(
325       slot(this, &SCIMBridgeImpl::OnCommitString));
326 }
327
328 void SCIMBridgeImpl::DisconnectPanelClient() {
329   panel_client_->close_connection();
330   read_fd_controller_.StopWatchingFileDescriptor();
331 }
332
333 void SCIMBridgeImpl::InitIMEngineFactory() {
334   IMEngineFactoryPointer factory =
335       backend_->get_default_factory(language_code_, kScimFactoryEncoding);
336   if (factory.null()) {
337       LOG(ERROR) << "Cannot get default IM engine factory";
338       return;
339   }
340
341   im_engine_instance_ = factory->create_instance(kScimFactoryEncoding,
342       im_engine_instance_count_++);
343
344   if (im_engine_instance_.null()) {
345      LOG(ERROR) << "Cannot get default IM engine instance";
346      return;
347   }
348
349   // TODO(shalamov): connect im_engine_instance_ signals.
350 }
351
352 void SCIMBridgeImpl::OnUpdateClientId(int, int client_id) {
353   panel_client_id_ = client_id;
354 }
355
356 void SCIMBridgeImpl::OnProcessHelperEvent(int context,
357                                         const String &target_uuid,
358                                         const String &helper_uuid,
359                                         const Transaction &trans) {
360   if (im_engine_instance_->get_factory_uuid() == target_uuid) {
361     panel_client_->prepare(im_context_id_);
362     im_engine_instance_->process_helper_event(helper_uuid, trans);
363     panel_client_->send();
364   }
365 }
366
367 void SCIMBridgeImpl::OnResetKeyboardIse(int context) {
368   // TODO(shalamov): implement.
369   LOG(INFO) << "SCIMBridgeImpl::OnResetKeyboardIse - not implemented";
370 }
371
372 void SCIMBridgeImpl::OnHidePreeditString(int context) {
373   // TODO(shalamov): implement.
374   LOG(INFO) << "SCIMBridgeImpl::OnHidePreeditString - not implemented";
375 }
376
377 void SCIMBridgeImpl::OnProcessKeyEvent(int context, const KeyEvent &key) {
378   content::BrowserThread::PostTask(content::BrowserThread::UI,
379                           FROM_HERE,
380                           base::Bind(&SCIMBridgeImpl::SendXKeyEvent,
381                                      base::Unretained(this), key));
382 }
383
384 void SCIMBridgeImpl::OnCommitString(int context, const WideString &wstr) {
385   // TODO(shalamov): implement.
386   LOG(INFO) << "SCIMBridgeImpl::OnCommitString - not implemented";
387 }
388
389 void SCIMBridgeImpl::SendXKeyEvent(const KeyEvent &key) {
390   ::KeyCode keycode = 0;
391   ::KeySym keysym = 0;
392   XKeyEvent event = scim_x11_keyevent_scim_to_x11(display_, key);
393   event.same_screen = True;
394   event.window = window_;
395   keysym = XStringToKeysym(key.get_key_string().c_str());
396   if (keysym == NoSymbol)
397     return;
398   keycode = XKeysymToKeycode(display_, keysym);
399   if (XkbKeycodeToKeysym(display_, keycode, 0, 1) == keysym)
400     event.state |= ShiftMask;
401   XSendEvent(display_, window_, True, event.type,
402     reinterpret_cast<XEvent *>(&event));
403 }
404
405 void SCIMBridgeImpl::SetInputMethodActiveWindow() {
406   SetIntProperty(root_window_, kISFActiveWindowAtom, kWindowAtomType, window_);
407 }
408
409 void SCIMBridgeImpl::SetFocusedWindow(uint32_t xid) {
410   window_ = xid;
411   ise_context_.client_window = xid;
412   SetInputMethodActiveWindow();
413 }
414
415 void SCIMBridgeImpl::OnFileCanReadWithoutBlocking(int fd) {
416   if (panel_client_fd_ == fd)
417     HandleScimEvent();
418 }
419
420 void SCIMBridgeImpl::HandleScimEvent() {
421   if (panel_client_->has_pending_event()
422       && !panel_client_->filter_event()) {
423         LOG(INFO) << "Reconnecting scim PanelClient";
424         DisconnectPanelClient();
425         InitPanelClient();
426   }
427 }
428
429 void SCIMBridgeImpl::WatchSCIMFd() {
430   base::MessageLoopForIO::current()->WatchFileDescriptor(
431           panel_client_fd_, true, base::MessageLoopForIO::WATCH_READ,
432           &read_fd_controller_, this);
433 }
434
435 }  // namespace ui