Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: chrome/browser/chromeos/input_method/ibus_ui_connection.cc

Issue 7108038: Rename ibus_ui_connection.* to ibus_ui_controller.*. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium OS 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_input_method_ui.h"
6
7 #include <base/logging.h>
8 #include <base/string_util.h>
9 #include <base/utf_string_conversions.h>
10 #include <ibus.h>
11
12 namespace chromeos {
13
14 namespace {
15
16 // Checks the attribute if this indicates annotation.
17 gboolean IsAnnotation(IBusAttribute *attr) {
18 g_return_val_if_fail(attr, FALSE);
19
20 // Define annotation text color.
21 static const guint kAnnotationColor = 0x888888;
22
23 // Currently, we can discriminate annotation by specific value |attr->value|
24 // TODO(nhiroki): We should change the way when iBus supports annotations.
25 if (attr->type == IBUS_ATTR_TYPE_FOREGROUND &&
26 attr->value == kAnnotationColor) {
27 return TRUE;
28 }
29 return FALSE;
30 }
31
32 // Returns an string representation of |table| for debugging.
33 std::string IBusLookupTableToString(IBusLookupTable* table) {
34 std::stringstream stream;
35 stream << "page_size: " << table->page_size << "\n";
36 stream << "cursor_pos: " << table->cursor_pos << "\n";
37 stream << "cursor_visible: " << table->cursor_visible << "\n";
38 stream << "round: " << table->round << "\n";
39 stream << "orientation: " << table->orientation << "\n";
40 stream << "candidates:";
41 for (int i = 0; ; i++) {
42 IBusText *text = ibus_lookup_table_get_candidate(table, i);
43 if (!text) {
44 break;
45 }
46 stream << " " << text->text;
47 }
48 return stream.str();
49 }
50
51 } // namespace
52
53 // A thin wrapper for IBusPanelService.
54 class InputMethodUiStatusConnection {
55 public:
56 InputMethodUiStatusConnection(
57 const InputMethodUiStatusMonitorFunctions& monitor_functions,
58 void* input_method_library)
59 : monitor_functions_(monitor_functions),
60 connection_change_handler_(NULL),
61 input_method_library_(input_method_library),
62 ibus_(NULL),
63 ibus_panel_service_(NULL) {
64 }
65
66 ~InputMethodUiStatusConnection() {
67 // ibus_panel_service_ depends on ibus_, thus unref it first.
68 if (ibus_panel_service_) {
69 DisconnectPanelServiceSignals();
70 g_object_unref(ibus_panel_service_);
71 }
72 if (ibus_) {
73 DisconnectIBusSignals();
74 g_object_unref(ibus_);
75 }
76 }
77
78 // Creates IBusBus object if it's not created yet, and tries to connect to
79 // ibus-daemon. Returns true if IBusBus is successfully connected to the
80 // daemon.
81 bool ConnectToIBus() {
82 if (ibus_) {
83 return true;
84 }
85 ibus_init();
86 ibus_ = ibus_bus_new();
87 CHECK(ibus_) << "ibus_bus_new() failed. Out of memory?";
88
89 bool result = false;
90 // Check the IBus connection status.
91 if (ibus_bus_is_connected(ibus_)) {
92 LOG(INFO) << "ibus_bus_is_connected(). IBus connection is ready.";
93 if (connection_change_handler_) {
94 connection_change_handler_(input_method_library_, true);
95 }
96 result = true;
97 }
98
99 // Start listening the gobject signals regardless of the bus connection
100 // status.
101 ConnectIBusSignals();
102 return result;
103 }
104
105 // Creates IBusPanelService object if |ibus_| is already connected.
106 bool MaybeRestorePanelService() {
107 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
108 return false;
109 }
110
111 if (ibus_panel_service_) {
112 LOG(ERROR) << "IBusPanelService is already available. Remove it first.";
113 g_object_set_data(G_OBJECT(ibus_), kPanelObjectKey, NULL);
114 g_object_unref(ibus_panel_service_);
115 ibus_panel_service_ = NULL;
116 }
117
118 // Create an IBusPanelService object.
119 GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus_);
120 if (!ibus_connection) {
121 LOG(ERROR) << "ibus_bus_get_connection() failed";
122 return false;
123 }
124 ibus_panel_service_ = ibus_panel_service_new(ibus_connection);
125 if (!ibus_panel_service_) {
126 LOG(ERROR) << "ibus_chromeos_panel_service_new() failed";
127 return false;
128 }
129 ConnectPanelServiceSignals();
130 g_object_set_data(G_OBJECT(ibus_), kPanelObjectKey, ibus_panel_service_);
131 LOG(INFO) << "IBusPanelService object is successfully (re-)created.";
132
133 // Request the well-known name *asynchronously*.
134 ibus_bus_request_name_async(ibus_,
135 IBUS_SERVICE_PANEL,
136 0 /* flags */,
137 -1 /* timeout */,
138 NULL /* cancellable */,
139 RequestNameCallback,
140 g_object_ref(ibus_));
141 return true;
142 }
143
144 // A function called when a user clicks the candidate_window.
145 bool NotifyCandidateClicked(int index, int button, int flags) {
146 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
147 LOG(ERROR) << "NotifyCandidateClicked: bus is not connected.";
148 return false;
149 }
150 if (!ibus_panel_service_) {
151 LOG(ERROR) << "NotifyCandidateClicked: panel service is not available.";
152 return false;
153 }
154
155 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */
156 ibus_panel_service_candidate_clicked(ibus_panel_service_,
157 index,
158 button,
159 flags);
160 return true;
161 }
162
163 // A function called when a user clicks the cursor up button.
164 bool NotifyCursorUp() {
165 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
166 LOG(ERROR) << "NotifyCursorUp: bus is not connected.";
167 return false;
168 }
169 if (!ibus_panel_service_) {
170 LOG(ERROR) << "NotifyCursorUp: panel service is not available.";
171 return false;
172 }
173
174 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */
175 ibus_panel_service_cursor_up(ibus_panel_service_);
176 return true;
177 }
178
179 // A function called when a user clicks the cursor down button.
180 bool NotifyCursorDown() {
181 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
182 LOG(ERROR) << "NotifyCursorDown: bus is not connected.";
183 return false;
184 }
185 if (!ibus_panel_service_) {
186 LOG(ERROR) << "NotifyCursorDown: panel service is not available.";
187 return false;
188 }
189 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */
190 ibus_panel_service_cursor_down(ibus_panel_service_);
191 return true;
192 }
193
194 // A function called when a user clicks the page up button.
195 bool NotifyPageUp() {
196 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
197 LOG(ERROR) << "NotifyPageUp: bus is not connected.";
198 return false;
199 }
200 if (!ibus_panel_service_) {
201 LOG(ERROR) << "NotifyPageUp: panel service is not available.";
202 return false;
203 }
204
205 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */
206 ibus_panel_service_page_up(ibus_panel_service_);
207 return true;
208 }
209
210 // A function called when a user clicks the page down button.
211 bool NotifyPageDown() {
212 if (!ibus_ || !ibus_bus_is_connected(ibus_)) {
213 LOG(ERROR) << "NotifyPageDown: bus is not connected.";
214 return false;
215 }
216 if (!ibus_panel_service_) {
217 LOG(ERROR) << "NotifyPageDown: panel service is not available.";
218 return false;
219 }
220
221 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */
222 ibus_panel_service_page_down(ibus_panel_service_);
223 return true;
224 }
225
226
227 // Registers a callback function which is called when IBusBus connection
228 // status is changed.
229 void MonitorInputMethodConnection(
230 InputMethodConnectionChangeMonitorFunction connection_change_handler) {
231 connection_change_handler_ = connection_change_handler;
232 }
233
234 private:
235 // Installs gobject signal handlers to |ibus_|.
236 void ConnectIBusSignals() {
237 if (!ibus_) {
238 return;
239 }
240 g_signal_connect(ibus_,
241 "connected",
242 G_CALLBACK(IBusBusConnectedCallback),
243 this);
244 g_signal_connect(ibus_,
245 "disconnected",
246 G_CALLBACK(IBusBusDisconnectedCallback),
247 this);
248 }
249
250 // Removes gobject signal handlers from |ibus_|.
251 void DisconnectIBusSignals() {
252 if (!ibus_) {
253 return;
254 }
255 g_signal_handlers_disconnect_by_func(
256 ibus_,
257 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusConnectedCallback)),
258 this);
259 g_signal_handlers_disconnect_by_func(
260 ibus_,
261 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusDisconnectedCallback)),
262 this);
263 }
264
265 // Installs gobject signal handlers to |ibus_panel_service_|.
266 void ConnectPanelServiceSignals() {
267 if (!ibus_panel_service_) {
268 return;
269 }
270 g_signal_connect(ibus_panel_service_,
271 "hide-auxiliary-text",
272 G_CALLBACK(HideAuxiliaryTextCallback),
273 this);
274 g_signal_connect(ibus_panel_service_,
275 "hide-lookup-table",
276 G_CALLBACK(HideLookupTableCallback),
277 this);
278 g_signal_connect(ibus_panel_service_,
279 "update-auxiliary-text",
280 G_CALLBACK(UpdateAuxiliaryTextCallback),
281 this);
282 g_signal_connect(ibus_panel_service_,
283 "set-cursor-location",
284 G_CALLBACK(SetCursorLocationCallback),
285 this);
286 g_signal_connect(ibus_panel_service_,
287 "update-lookup-table",
288 G_CALLBACK(UpdateLookupTableCallback),
289 this);
290 }
291
292 // Removes gobject signal handlers from |ibus_panel_service_|.
293 void DisconnectPanelServiceSignals() {
294 if (!ibus_panel_service_) {
295 return;
296 }
297 g_signal_handlers_disconnect_by_func(
298 ibus_panel_service_,
299 reinterpret_cast<gpointer>(HideAuxiliaryTextCallback),
300 this);
301 g_signal_handlers_disconnect_by_func(
302 ibus_panel_service_,
303 reinterpret_cast<gpointer>(HideLookupTableCallback),
304 this);
305 g_signal_handlers_disconnect_by_func(
306 ibus_panel_service_,
307 reinterpret_cast<gpointer>(UpdateAuxiliaryTextCallback),
308 this);
309 g_signal_handlers_disconnect_by_func(
310 ibus_panel_service_,
311 reinterpret_cast<gpointer>(SetCursorLocationCallback),
312 this);
313 g_signal_handlers_disconnect_by_func(
314 ibus_panel_service_,
315 reinterpret_cast<gpointer>(UpdateLookupTableCallback),
316 this);
317 }
318
319 // Handles "connected" signal from ibus-daemon.
320 static void IBusBusConnectedCallback(IBusBus* bus, gpointer user_data) {
321 LOG(WARNING) << "IBus connection is recovered.";
322 g_return_if_fail(user_data);
323 InputMethodUiStatusConnection* self
324 = static_cast<InputMethodUiStatusConnection*>(user_data);
325 if (!self->MaybeRestorePanelService()) {
326 LOG(ERROR) << "MaybeRestorePanelService() failed";
327 return;
328 }
329 if (self->connection_change_handler_) {
330 self->connection_change_handler_(self->input_method_library_, true);
331 }
332 }
333
334 // Handles "disconnected" signal from ibus-daemon. Releases the
335 // |ibus_panel_service_| object since the connection the service has will be
336 // destroyed soon.
337 static void IBusBusDisconnectedCallback(IBusBus* bus, gpointer user_data) {
338 LOG(WARNING) << "IBus connection is terminated.";
339 g_return_if_fail(user_data);
340 InputMethodUiStatusConnection* self
341 = static_cast<InputMethodUiStatusConnection*>(user_data);
342 if (self->ibus_panel_service_) {
343 self->DisconnectPanelServiceSignals();
344 // Since the connection being disconnected is currently mutex-locked,
345 // we can't unref the panel service object directly here. Because when the
346 // service object is deleted, the connection, which the service also has,
347 // will be locked again. To avoid deadlock, we use g_idle_add instead.
348 g_object_set_data(G_OBJECT(self->ibus_), kPanelObjectKey, NULL);
349 g_idle_add(ReleasePanelService, self->ibus_panel_service_);
350 self->ibus_panel_service_ = NULL;
351 }
352
353 if (self->connection_change_handler_) {
354 self->connection_change_handler_(self->input_method_library_, false);
355 }
356 }
357
358 // Releases |ibus_panel_service_|. See the comment above.
359 static gboolean ReleasePanelService(gpointer user_data) {
360 g_return_val_if_fail(IBUS_IS_PANEL_SERVICE(user_data), FALSE);
361 g_object_unref(user_data);
362 return FALSE; // stop the idle timer.
363 }
364
365 // Handles IBusPanelService's |HideAuxiliaryText| method call.
366 // Calls |hide_auxiliary_text| in |monitor_functions|.
367 static void HideAuxiliaryTextCallback(IBusPanelService *panel,
368 gpointer user_data) {
369 g_return_if_fail(user_data);
370 InputMethodUiStatusConnection* self
371 = static_cast<InputMethodUiStatusConnection*>(user_data);
372 g_return_if_fail(self->monitor_functions_.hide_auxiliary_text);
373 self->monitor_functions_.hide_auxiliary_text(self->input_method_library_);
374 }
375
376 // Handles IBusPanelService's |HideLookupTable| method call.
377 // Calls |hide_lookup_table| in |monitor_functions|.
378 static void HideLookupTableCallback(IBusPanelService *panel,
379 gpointer user_data) {
380 g_return_if_fail(user_data);
381 InputMethodUiStatusConnection* self
382 = static_cast<InputMethodUiStatusConnection*>(user_data);
383 g_return_if_fail(self->monitor_functions_.hide_lookup_table);
384 self->monitor_functions_.hide_lookup_table(self->input_method_library_);
385 }
386
387 // Handles IBusPanelService's |UpdateAuxiliaryText| method call.
388 // Converts IBusText to a std::string, and calls |update_auxiliary_text| in
389 // |monitor_functions|
390 static void UpdateAuxiliaryTextCallback(IBusPanelService *panel,
391 IBusText *text,
392 gboolean visible,
393 gpointer user_data) {
394 g_return_if_fail(text);
395 g_return_if_fail(text->text);
396 g_return_if_fail(user_data);
397 InputMethodUiStatusConnection* self
398 = static_cast<InputMethodUiStatusConnection*>(user_data);
399 g_return_if_fail(self->monitor_functions_.update_auxiliary_text);
400 // Convert IBusText to a std::string. IBusText is an attributed text,
401 const std::string simple_text = text->text;
402 self->monitor_functions_.update_auxiliary_text(
403 self->input_method_library_, simple_text, visible == TRUE);
404 }
405
406 // Handles IBusPanelService's |SetCursorLocation| method call.
407 // Calls |set_cursor_location| in |monitor_functions|.
408 static void SetCursorLocationCallback(IBusPanelService *panel,
409 gint x,
410 gint y,
411 gint width,
412 gint height,
413 gpointer user_data) {
414 g_return_if_fail(user_data);
415 InputMethodUiStatusConnection* self
416 = static_cast<InputMethodUiStatusConnection*>(user_data);
417 g_return_if_fail(self->monitor_functions_.set_cursor_location);
418 self->monitor_functions_.set_cursor_location(
419 self->input_method_library_, x, y, width, height);
420 }
421
422 // Handles IBusPanelService's |UpdateLookupTable| method call.
423 // Creates an InputMethodLookupTable object and calls |update_lookup_table| in
424 // |monitor_functions|
425 static void UpdateLookupTableCallback(IBusPanelService *panel,
426 IBusLookupTable *table,
427 gboolean visible,
428 gpointer user_data) {
429 g_return_if_fail(table);
430 g_return_if_fail(user_data);
431 InputMethodUiStatusConnection* self
432 = static_cast<InputMethodUiStatusConnection*>(user_data);
433 g_return_if_fail(self->monitor_functions_.update_lookup_table);
434
435 InputMethodLookupTable lookup_table;
436 lookup_table.visible = (visible == TRUE);
437
438 // Copy the orientation information.
439 const gint orientation = ibus_lookup_table_get_orientation(table);
440 if (orientation == IBUS_ORIENTATION_VERTICAL) {
441 lookup_table.orientation = InputMethodLookupTable::kVertical;
442 } else if (orientation == IBUS_ORIENTATION_HORIZONTAL) {
443 lookup_table.orientation = InputMethodLookupTable::kHorizontal;
444 }
445
446 // Copy candidates and annotations to |lookup_table|.
447 for (int i = 0; ; i++) {
448 IBusText *text = ibus_lookup_table_get_candidate(table, i);
449 if (!text) {
450 break;
451 }
452
453 if (!text->attrs || !text->attrs->attributes) {
454 lookup_table.candidates.push_back(text->text);
455 lookup_table.annotations.push_back("");
456 continue;
457 }
458
459 // Divide candidate and annotation by specific attribute.
460 const guint length = text->attrs->attributes->len;
461 for (int j = 0; ; j++) {
462 IBusAttribute *attr = ibus_attr_list_get(text->attrs, j);
463
464 // The candidate does not have annotation.
465 if (!attr) {
466 lookup_table.candidates.push_back(text->text);
467 lookup_table.annotations.push_back("");
468 break;
469 }
470
471 // Check that the attribute indicates annotation.
472 if (IsAnnotation(attr) && j + 1 == static_cast<int>(length)) {
473 const std::wstring candidate_word =
474 UTF8ToWide(text->text).substr(0, attr->start_index);
475 lookup_table.candidates.push_back(WideToUTF8(candidate_word));
476
477 const std::wstring annotation_word =
478 UTF8ToWide(text->text).substr(attr->start_index, attr->end_index);
479 lookup_table.annotations.push_back(WideToUTF8(annotation_word));
480
481 break;
482 }
483 }
484 }
485 DCHECK_EQ(lookup_table.candidates.size(),
486 lookup_table.annotations.size());
487
488 // Copy labels to |lookup_table|.
489 for (int i = 0; ; i++) {
490 IBusText *text = ibus_lookup_table_get_label(table, i);
491 if (!text) {
492 break;
493 }
494 lookup_table.labels.push_back(text->text);
495 }
496
497 lookup_table.cursor_absolute_index =
498 ibus_lookup_table_get_cursor_pos(table);
499 lookup_table.page_size = ibus_lookup_table_get_page_size(table);
500 // Ensure that the page_size is non-zero to avoid div-by-zero error.
501 if (lookup_table.page_size <= 0) {
502 LOG(DFATAL) << "Invalid page size: " << lookup_table.page_size;
503 lookup_table.page_size = 1;
504 }
505
506 self->monitor_functions_.update_lookup_table(
507 self->input_method_library_, lookup_table);
508 }
509
510 // A callback function that will be called when ibus_bus_request_name_async()
511 // request is finished.
512 static void RequestNameCallback(GObject* source_object,
513 GAsyncResult* res,
514 gpointer user_data) {
515 IBusBus* bus = IBUS_BUS(user_data);
516 g_return_if_fail(bus);
517
518 GError* error = NULL;
519 const guint service_id =
520 ibus_bus_request_name_async_finish(bus, res, &error);
521
522 if (!service_id) {
523 std::string message = "(unknown error)";
524 if (error && error->message) {
525 message = error->message;
526 }
527 LOG(ERROR) << "Failed to register the panel service: " << message;
528 } else {
529 LOG(INFO) << "The panel service is registered: ID=" << service_id;
530 }
531
532 if (error) {
533 g_error_free(error);
534 }
535 g_object_unref(bus);
536 }
537
538 InputMethodUiStatusMonitorFunctions monitor_functions_;
539 InputMethodConnectionChangeMonitorFunction connection_change_handler_;
540 void* input_method_library_;
541 IBusBus* ibus_;
542 IBusPanelService* ibus_panel_service_;
543 };
544
545 //
546 // cros APIs
547 //
548
549 // The function will be bound to chromeos::MonitorInputMethodUiStatus with
550 // dlsym() in load.cc so it needs to be in the C linkage, so the symbol
551 // name does not get mangled.
552 extern "C"
553 InputMethodUiStatusConnection* ChromeOSMonitorInputMethodUiStatus(
554 const InputMethodUiStatusMonitorFunctions& monitor_functions,
555 void* input_method_library) {
556 DLOG(INFO) << "MonitorInputMethodUiStatus";
557
558 InputMethodUiStatusConnection* connection =
559 new InputMethodUiStatusConnection(monitor_functions,
560 input_method_library);
561
562 // It's totally fine if ConnectToIBus() fails here, as we'll get "connected"
563 // gobject signal once the connection becomes ready.
564 if (connection->ConnectToIBus()) {
565 connection->MaybeRestorePanelService();
566 }
567 return connection;
568 }
569
570 extern "C"
571 void ChromeOSDisconnectInputMethodUiStatus(
572 InputMethodUiStatusConnection* connection) {
573 DLOG(INFO) << "DisconnectInputMethodUiStatus";
574 delete connection;
575 }
576
577 extern "C"
578 void ChromeOSNotifyCandidateClicked(InputMethodUiStatusConnection* connection,
579 int index, int button, int flags) {
580 DLOG(INFO) << "NotifyCandidateClicked";
581 DCHECK(connection);
582 if (connection) {
583 connection->NotifyCandidateClicked(index, button, flags);
584 }
585 }
586
587 extern "C"
588 void ChromeOSNotifyCursorUp(InputMethodUiStatusConnection* connection) {
589 DLOG(INFO) << "NotifyCursorUp";
590 DCHECK(connection);
591 if (connection) {
592 connection->NotifyCursorUp();
593 }
594 }
595
596 extern "C"
597 void ChromeOSNotifyCursorDown(InputMethodUiStatusConnection* connection) {
598 DLOG(INFO) << "NotifyCursorDown";
599 DCHECK(connection);
600 if (connection) {
601 connection->NotifyCursorDown();
602 }
603 }
604
605 extern "C"
606 void ChromeOSNotifyPageUp(InputMethodUiStatusConnection* connection) {
607 DLOG(INFO) << "NotifyPageUp";
608 DCHECK(connection);
609 if (connection) {
610 connection->NotifyPageUp();
611 }
612 }
613
614 extern "C"
615 void ChromeOSNotifyPageDown(InputMethodUiStatusConnection* connection) {
616 DLOG(INFO) << "NotifyPageDown";
617 DCHECK(connection);
618 if (connection) {
619 connection->NotifyPageDown();
620 }
621 }
622
623 extern "C"
624 void ChromeOSMonitorInputMethodConnection(
625 InputMethodUiStatusConnection* connection,
626 InputMethodConnectionChangeMonitorFunction connection_change_handler) {
627 DLOG(INFO) << "MonitorInputMethodConnection";
628 DCHECK(connection);
629 if (connection) {
630 connection->MonitorInputMethodConnection(connection_change_handler);
631 }
632 }
633
634 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/input_method/ibus_ui_connection.h ('k') | chrome/browser/chromeos/input_method/ibus_ui_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698