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.
5 #include "xwalk/ime/tizen-scim/scim_bridge.h"
7 #define Uses_SCIM_BACKEND
8 #define Uses_SCIM_IMENGINE_MODULE
9 #define Uses_SCIM_HELPER_MODULE
10 #define Uses_SCIM_PANEL_CLIENT
13 #include <x11/scim_x11_utils.h>
14 #include <isf_imcontrol_client.h>
17 #include <X11/XKBlib.h>
18 #include <X11/Xlibint.h>
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"
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";
48 bool return_key_disabled;
49 bool prediction_allow;
54 } InputSystemEngineContext;
58 class SCIMBridgeImpl : base::MessageLoopForIO::Watcher {
64 bool IsInitialized() {return is_initialized_;}
65 void ShowInputMethodPanel();
66 void HideInputMethodPanel();
67 void SetFocusedWindow(uint32_t xid);
70 bool IsDaemonRunning();
72 void InitConfigModule();
74 void InitPanelClient();
75 void ConnectPanelClientSignals();
76 void DisconnectPanelClient();
77 void InitIMEngineFactory();
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);
90 void SendXKeyEvent(const KeyEvent &key);
91 void SetInputMethodActiveWindow();
93 void OnFileCanReadWithoutBlocking(int fd) override;
94 void OnFileCanWriteWithoutBlocking(int fd) override {}
96 void HandleScimEvent();
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_;
121 DISALLOW_COPY_AND_ASSIGN(SCIMBridgeImpl);
124 SCIMBridge::SCIMBridge() : impl_(new SCIMBridgeImpl()) {
127 SCIMBridge::~SCIMBridge() {
130 void SCIMBridge::Init() {
131 if (impl_->IsInitialized())
134 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
135 content::BrowserThread::PostTask(content::BrowserThread::IO,
137 base::Bind(&SCIMBridgeImpl::Init,
138 base::Unretained(impl_.get())));
142 void SCIMBridge::SetFocusedWindow(uint32_t xid) {
143 impl_->SetFocusedWindow(xid);
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,
151 base::Bind(&SCIMBridgeImpl::HideInputMethodPanel,
152 base::Unretained(impl_.get())));
155 content::BrowserThread::PostTask(content::BrowserThread::IO,
157 base::Bind(&SCIMBridgeImpl::ShowInputMethodPanel,
158 base::Unretained(impl_.get())));
162 SCIMBridgeImpl::SCIMBridgeImpl()
163 : is_initialized_(false),
164 config_module_name_(kScimSimpleConfig),
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;
185 SCIMBridgeImpl::~SCIMBridgeImpl() {
187 DisconnectPanelClient();
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();
198 InitIMEngineFactory();
200 ConnectPanelClientSignals();
201 is_initialized_ = true;
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_),
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();
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();
224 void SCIMBridgeImpl::HideInputMethodPanel() {
225 imcontrol_client_->prepare();
226 imcontrol_client_->hide_ise(panel_client_id_, im_context_id_);
227 imcontrol_client_->send();
230 bool SCIMBridgeImpl::IsDaemonRunning() {
233 SocketAddress address;
234 address.set_address(scim_get_default_socket_frontend_address());
236 if (!client.connect(address))
239 return scim_socket_open_connection(uid, kScimConnectionTester,
240 kScimSocketFrontend, client, 1000);
243 void SCIMBridgeImpl::StartDaemon() {
244 if (!IsDaemonRunning()) {
245 std::vector<String> helper_modules;
246 std::vector<String> imengine_modules;
248 scim_get_imengine_module_list(imengine_modules);
249 scim_get_helper_module_list(helper_modules);
251 std::vector<String>::iterator it;
253 for (it = imengine_modules.begin(); it != imengine_modules.end(); it++) {
254 if (*it != kScimFrontendSocketIpc)
255 scim_modules_.push_back(*it);
258 for (it = helper_modules.begin(); it != helper_modules.end(); it++)
259 scim_modules_.push_back(*it);
261 const String engines =
262 (scim_modules_.size() > 0 ? scim_combine_string_list(scim_modules_, ',')
264 const char *argv[] = { "--no-stay", 0 };
265 scim_launch(true, config_module_name_, engines, kScimFrontendSocketIpc,
266 reinterpret_cast<char **>(argv));
270 void SCIMBridgeImpl::InitConfigModule() {
271 config_module_.reset(new ConfigModule(config_module_name_));
272 if (config_module_->valid()) {
273 config_ = config_module_->create_config();
275 LOG(WARNING) << "Cannot init scim config module: " << config_module_name_;
276 config_module_.reset();
277 config_ = new DummyConfig();
278 config_module_name_ = kScimDummyConfig;
282 void SCIMBridgeImpl::InitBackend() {
283 backend_ = new CommonBackEnd(config_, scim_modules_);
284 if (!backend_.null()) {
285 backend_->initialize(config_, scim_modules_, false, false);
287 LOG(ERROR) << "Cannot create backend";
291 void SCIMBridgeImpl::InitPanelClient() {
292 if (!panel_client_.get())
293 panel_client_.reset(new PanelClient());
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,
300 base::Bind(&SCIMBridgeImpl::WatchSCIMFd,
301 base::Unretained(this)));
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();
308 LOG(ERROR) << "Cannot get panel client connection";
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));
328 void SCIMBridgeImpl::DisconnectPanelClient() {
329 panel_client_->close_connection();
330 read_fd_controller_.StopWatchingFileDescriptor();
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";
341 im_engine_instance_ = factory->create_instance(kScimFactoryEncoding,
342 im_engine_instance_count_++);
344 if (im_engine_instance_.null()) {
345 LOG(ERROR) << "Cannot get default IM engine instance";
349 // TODO(shalamov): connect im_engine_instance_ signals.
352 void SCIMBridgeImpl::OnUpdateClientId(int, int client_id) {
353 panel_client_id_ = client_id;
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();
367 void SCIMBridgeImpl::OnResetKeyboardIse(int context) {
368 // TODO(shalamov): implement.
369 LOG(INFO) << "SCIMBridgeImpl::OnResetKeyboardIse - not implemented";
372 void SCIMBridgeImpl::OnHidePreeditString(int context) {
373 // TODO(shalamov): implement.
374 LOG(INFO) << "SCIMBridgeImpl::OnHidePreeditString - not implemented";
377 void SCIMBridgeImpl::OnProcessKeyEvent(int context, const KeyEvent &key) {
378 content::BrowserThread::PostTask(content::BrowserThread::UI,
380 base::Bind(&SCIMBridgeImpl::SendXKeyEvent,
381 base::Unretained(this), key));
384 void SCIMBridgeImpl::OnCommitString(int context, const WideString &wstr) {
385 // TODO(shalamov): implement.
386 LOG(INFO) << "SCIMBridgeImpl::OnCommitString - not implemented";
389 void SCIMBridgeImpl::SendXKeyEvent(const KeyEvent &key) {
390 ::KeyCode keycode = 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)
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));
405 void SCIMBridgeImpl::SetInputMethodActiveWindow() {
406 SetIntProperty(root_window_, kISFActiveWindowAtom, kWindowAtomType, window_);
409 void SCIMBridgeImpl::SetFocusedWindow(uint32_t xid) {
411 ise_context_.client_window = xid;
412 SetInputMethodActiveWindow();
415 void SCIMBridgeImpl::OnFileCanReadWithoutBlocking(int fd) {
416 if (panel_client_fd_ == fd)
420 void SCIMBridgeImpl::HandleScimEvent() {
421 if (panel_client_->has_pending_event()
422 && !panel_client_->filter_event()) {
423 LOG(INFO) << "Reconnecting scim PanelClient";
424 DisconnectPanelClient();
429 void SCIMBridgeImpl::WatchSCIMFd() {
430 base::MessageLoopForIO::current()->WatchFileDescriptor(
431 panel_client_fd_, true, base::MessageLoopForIO::WATCH_READ,
432 &read_fd_controller_, this);