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

Unified Diff: chromeos_language.cc

Issue 460107: Adding IBus support to cros library. (Closed)
Patch Set: copied to writable tree Created 11 years 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chromeos_language.h ('k') | cros_api.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromeos_language.cc
diff --git a/chromeos_language.cc b/chromeos_language.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8aed34f10084f3dc5f5d032519ffd05a0b4b95b7
--- /dev/null
+++ b/chromeos_language.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos_language.h"
+
+#include <base/logging.h>
+#include <ibus.h>
+
+#include <algorithm> // for std::sort.
+
+#include "chromeos/dbus/dbus.h"
+#include "chromeos/glib/object.h"
+
+namespace {
+
+const char kCandidateWindowService[] = "org.freedesktop.IBus.Panel";
+const char kCandidateWindowObjectPath[] = "/org/chromium/Chrome/LanguageBar";
+const char kCandidateWindowInterface[] = "org.freedesktop.IBus.Panel";
+
+// Copies IME names in |engines| to |out|.
+void AddIMELanguages(const GList* engines, chromeos::InputLanguageList* out) {
+ DCHECK(out);
+ for (; engines; engines = g_list_next(engines)) {
+ IBusEngineDesc *engine_desc = IBUS_ENGINE_DESC(engines->data);
+ out->push_back(chromeos::InputLanguage(
+ chromeos::LANGUAGE_CATEGORY_IME,
+ engine_desc->name, engine_desc->longname, engine_desc->icon));
+ g_object_unref(engine_desc);
+ }
+}
+
+// Copies XKB layout names in (TBD) to |out|.
+void AddXKBLayouts(chromeos::InputLanguageList* out) {
+ DCHECK(out);
+ // TODO(yusukes): implement this.
+ out->push_back(chromeos::InputLanguage(
+ chromeos::LANGUAGE_CATEGORY_XKB,
+ kFallbackXKBId,
+ kFallbackXKBDisplayName,
+ "" /* no icon */)); // mock
+}
+
+} // namespace
+
+namespace chromeos {
+
+// A class that holds IBus and DBus connections.
+class LanguageStatusConnection {
+ public:
+ LanguageStatusConnection(LanguageStatusMonitorFunction monitor_function,
+ void* language_library)
+ : monitor_function_(monitor_function),
+ language_library_(language_library),
+ ibus_(NULL),
+ dbus_focus_in_(NULL),
+ dbus_focus_out_(NULL),
+ dbus_state_changed_(NULL),
+ input_context_path_("") {
+ DCHECK(monitor_function_);
+ DCHECK(language_library_);
+ }
+
+ ~LanguageStatusConnection() {
+ // Close IBus and DBus connections.
+ if (ibus_) {
+ g_object_unref(ibus_);
+ }
+ if (dbus_focus_in_) {
+ dbus::Disconnect(dbus_focus_in_);
+ }
+ if (dbus_focus_out_) {
+ dbus::Disconnect(dbus_focus_out_);
+ }
+ if (dbus_state_changed_) {
+ dbus::Disconnect(dbus_state_changed_);
+ }
+ }
+
+ // Initializes IBus and DBus connections.
+ bool Init() {
+ // Establish IBus connection between ibus-daemon to retrieve the list of
+ // available IME engines, change the current IME engine, and so on.
+ ibus_init();
+ ibus_ = ibus_bus_new();
+
+ // Check the IBus connection status.
+ if (!ibus_) {
+ LOG(ERROR) << "ibus_bus_new() failed";
+ return false;
+ }
+ if (!ibus_bus_is_connected(ibus_)) {
+ LOG(ERROR) << "ibus_bus_is_connected() failed";
+ return false;
+ }
+
+ // Establish a DBus connection between the candidate_window process for
+ // Chromium OS to handle "FocusIn", "FocusOut", and "StateChanged" signals
+ // from the process.
+ const char* address = ibus_get_address();
+ dbus::BusConnection dbus(dbus::GetPrivateBusConnection(address));
+ LOG(INFO) << "Established private D-Bus connection to: '" << address << "'";
+
+ const bool kConnectToNameOwner = true;
+ // TODO(yusukes): dbus::Proxy instantiation might fail (and abort due to
+ // DCHECK failure) when candidate_window process does not exist yet.
+ // Would be better to add "bool dbus::Proxy::Init()" or something like that
+ // to handle such case?
+ dbus::Proxy candidate_window(dbus,
+ kCandidateWindowService,
+ kCandidateWindowObjectPath,
+ kCandidateWindowInterface,
+ kConnectToNameOwner);
+
+ if (!candidate_window) {
+ LOG(ERROR) << "Can't construct proxy for the candidate window. "
+ << "candidate window is not running?";
+ return false;
+ }
+
+ dbus_focus_in_ = dbus::Monitor(candidate_window,
+ "FocusIn",
+ &LanguageStatusConnection::FocusIn,
+ this);
+ dbus_focus_out_ = dbus::Monitor(candidate_window,
+ "FocusOut",
+ &LanguageStatusConnection::FocusOut,
+ this);
+ dbus_state_changed_ = dbus::Monitor(candidate_window,
+ "StateChanged",
+ &LanguageStatusConnection::StateChanged,
+ this);
+
+ // TODO(yusukes): Investigate what happens if IBus/DBus connections are
+ // suddenly closed.
+ // TODO(yusukes): Investigate what happens if candidate_window process is
+ // restarted. I'm not sure but we should use dbus_g_proxy_new_for_name(),
+ // not dbus_g_proxy_new_for_name_owner()?
+
+ return true;
+ }
+
+ // Called by cros API ChromeOSGetLanguages() and returns a list of IMEs and
+ // XKB layouts that are currently available.
+ InputLanguageList* GetLanguages() {
+ GList* engines = ibus_bus_list_active_engines(ibus_);
+ if (!engines) {
+ // IBus connection is broken?
+ LOG(ERROR) << "ibus_bus_list_active_engines() failed.";
+ return NULL;
+ }
+ InputLanguageList* language_list = new InputLanguageList;
+ AddIMELanguages(engines, language_list);
+ AddXKBLayouts(language_list);
+ std::sort(language_list->begin(), language_list->end());
+
+ g_list_free(engines);
+ return language_list;
+ }
+
+ // Called by cros API ChromeOSChangeLanguage().
+ void SwitchXKB(const char* name) {
+ IBusInputContext* context
+ = ibus_input_context_get_input_context(input_context_path_.c_str(),
+ ibus_bus_get_connection(ibus_));
+ ibus_input_context_disable(context);
+ g_object_unref(context);
+ // TODO(yusukes): implement XKB switching.
+ UpdateUI();
+ }
+
+ // Called by cros API ChromeOSChangeLanguage() as well.
+ void SwitchIME(const char* name) {
+ if (input_context_path_.empty()) {
+ LOG(ERROR) << "Input context is unknown";
+ return;
+ }
+
+ IBusInputContext* context
+ = ibus_input_context_get_input_context(input_context_path_.c_str(),
+ ibus_bus_get_connection(ibus_));
+ ibus_input_context_set_engine(context, name);
+ g_object_unref(context);
+ UpdateUI();
+ }
+
+ private:
+ // Handles "FocusIn" signal from candidate_window.
+ static void FocusIn(void* object, const char* input_context_path) {
+ DCHECK(input_context_path) << "NULL context passed";
+ DLOG(INFO) << "FocusIn: " << input_context_path;
+ DCHECK(object);
+
+ // Remember the current ic path.
+ LanguageStatusConnection* self
+ = static_cast<LanguageStatusConnection*>(object);
+ self->input_context_path_ = input_context_path;
+ self->UpdateUI(); // This is necessary since IME status is held per ic.
+ }
+
+ // Handles "FocusOut" signal from candidate_window.
+ static void FocusOut(void* object, const char* input_context_path) {
+ DCHECK(input_context_path) << "NULL context passed";
+ DLOG(INFO) << "FocusOut: " << input_context_path;
+ DCHECK(object);
+ }
+
+ // Handles "StateChanged" signal from candidate_window process.
+ static void StateChanged(void* object, const char* dummy) {
+ // TODO(yusukes): Modify common/chromeos/dbus/dbus.h so that we can handle
+ // signals without argument. Then remove the |dummy|.
+ DLOG(INFO) << "StateChanged";
+ DCHECK(object);
+ LanguageStatusConnection* self
+ = static_cast<LanguageStatusConnection*>(object);
+ self->UpdateUI();
+ }
+
+ // Retrieve IME/XKB status and notify them to the UI.
+ void UpdateUI() {
+ if (input_context_path_.empty()) {
+ LOG(ERROR) << "Input context is unknown";
+ return;
+ }
+
+ IBusInputContext* context
+ = ibus_input_context_get_input_context(
+ input_context_path_.c_str(), ibus_bus_get_connection(ibus_));
+
+ InputLanguage current_language;
+ const bool ime_is_enabled = ibus_input_context_is_enabled(context);
+ if (ime_is_enabled) {
+ DLOG(INFO) << "IME is active";
+ // Set IME name on current_language.
+ const IBusEngineDesc* engine_desc
+ = ibus_input_context_get_engine(context);
+ DCHECK(engine_desc);
+ if (!engine_desc) {
+ return;
+ }
+ current_language = InputLanguage(LANGUAGE_CATEGORY_IME,
+ engine_desc->name,
+ engine_desc->longname,
+ engine_desc->icon);
+ } else {
+ DLOG(INFO) << "IME is not active";
+ // Set XKB layout name on current_languages.
+ current_language = InputLanguage(LANGUAGE_CATEGORY_XKB,
+ kFallbackXKBId,
+ kFallbackXKBDisplayName,
+ "" /* no icon */); // mock
+ // TODO(yusukes): implemente this.
+ }
+ DLOG(INFO) << "Updating the UI. ID:" << current_language.id
+ << ", display_name:" << current_language.display_name;
+
+ // Notify the change to update UI.
+ monitor_function_(language_library_, current_language);
+ g_object_unref(context);
+ }
+
+ // A function pointer which points LanguageLibrary::LanguageChangedHandler
+ // function. |monitor_funcion_| is called when Chrome UI needs to be updated.
+ LanguageStatusMonitorFunction monitor_function_;
+ // Points to a chromeos::LanguageLibrary object. |language_library_| is used
+ // as the first argument of the |monitor_function_| function.
+ void* language_library_;
+
+ // Connections to IBus and DBus.
+ typedef dbus::MonitorConnection<void (const char*)>* DBusConnectionType;
+ IBusBus* ibus_;
+ DBusConnectionType dbus_focus_in_;
+ DBusConnectionType dbus_focus_out_;
+ DBusConnectionType dbus_state_changed_;
+
+ // Current input context path.
+ std::string input_context_path_;
+};
+
+//
+// cros APIs
+//
+
+// The function will be bound to chromeos::MonitorLanguageStatus with dlsym()
+// in load.cc so it needs to be in the C linkage, so the symbol name does not
+// get mangled.
+extern "C"
+LanguageStatusConnection* ChromeOSMonitorLanguageStatus(
+ LanguageStatusMonitorFunction monitor_function, void* language_library) {
+ LOG(INFO) << "MonitorLanguageStatus";
+ LanguageStatusConnection* connection
+ = new LanguageStatusConnection(monitor_function, language_library);
+ if (!connection->Init()) {
+ LOG(WARNING) << "Failed to Init() LanguageStatusConnection. "
+ << "Returning NULL";
+ delete connection;
+ connection = NULL;
+ }
+ return connection;
+}
+
+extern "C"
+void ChromeOSDisconnectLanguageStatus(LanguageStatusConnection* connection) {
+ LOG(INFO) << "DisconnectLanguageStatus";
+ delete connection;
+}
+
+extern "C"
+InputLanguageList* ChromeOSGetLanguages(LanguageStatusConnection* connection) {
+ // TODO(yusukes): Add DCHECK(connection); here when candidate_window for
+ // Chrome OS gets ready.
+ if (!connection) {
+ LOG(WARNING) << "LanguageStatusConnection is NULL";
+ return NULL;
+ }
+ // Pass ownership to a caller. Note: GetLanguages() might return NULL.
+ return connection->GetLanguages();
+}
+
+extern "C"
+void ChromeOSChangeLanguage(LanguageStatusConnection* connection,
+ LanguageCategory category,
+ const char* name) {
+ // TODO(yusukes): Add DCHECK(connection); here when candidate_window for
+ // Chrome OS gets ready.
+ if (!connection) {
+ LOG(WARNING) << "LanguageStatusConnection is NULL";
+ return;
+ }
+ DCHECK(name);
+ DLOG(INFO) << "ChangeLanguage: " << name << " [category " << category << "]";
+ switch (category) {
+ case LANGUAGE_CATEGORY_XKB:
+ connection->SwitchXKB(name);
+ break;
+ case LANGUAGE_CATEGORY_IME:
+ connection->SwitchIME(name);
+ break;
+ }
+}
+
+} // namespace chromeos
« no previous file with comments | « chromeos_language.h ('k') | cros_api.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698