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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « chromeos_language.h ('k') | cros_api.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 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_language.h"
6
7 #include <base/logging.h>
8 #include <ibus.h>
9
10 #include <algorithm> // for std::sort.
11
12 #include "chromeos/dbus/dbus.h"
13 #include "chromeos/glib/object.h"
14
15 namespace {
16
17 const char kCandidateWindowService[] = "org.freedesktop.IBus.Panel";
18 const char kCandidateWindowObjectPath[] = "/org/chromium/Chrome/LanguageBar";
19 const char kCandidateWindowInterface[] = "org.freedesktop.IBus.Panel";
20
21 // Copies IME names in |engines| to |out|.
22 void AddIMELanguages(const GList* engines, chromeos::InputLanguageList* out) {
23 DCHECK(out);
24 for (; engines; engines = g_list_next(engines)) {
25 IBusEngineDesc *engine_desc = IBUS_ENGINE_DESC(engines->data);
26 out->push_back(chromeos::InputLanguage(
27 chromeos::LANGUAGE_CATEGORY_IME,
28 engine_desc->name, engine_desc->longname, engine_desc->icon));
29 g_object_unref(engine_desc);
30 }
31 }
32
33 // Copies XKB layout names in (TBD) to |out|.
34 void AddXKBLayouts(chromeos::InputLanguageList* out) {
35 DCHECK(out);
36 // TODO(yusukes): implement this.
37 out->push_back(chromeos::InputLanguage(
38 chromeos::LANGUAGE_CATEGORY_XKB,
39 kFallbackXKBId,
40 kFallbackXKBDisplayName,
41 "" /* no icon */)); // mock
42 }
43
44 } // namespace
45
46 namespace chromeos {
47
48 // A class that holds IBus and DBus connections.
49 class LanguageStatusConnection {
50 public:
51 LanguageStatusConnection(LanguageStatusMonitorFunction monitor_function,
52 void* language_library)
53 : monitor_function_(monitor_function),
54 language_library_(language_library),
55 ibus_(NULL),
56 dbus_focus_in_(NULL),
57 dbus_focus_out_(NULL),
58 dbus_state_changed_(NULL),
59 input_context_path_("") {
60 DCHECK(monitor_function_);
61 DCHECK(language_library_);
62 }
63
64 ~LanguageStatusConnection() {
65 // Close IBus and DBus connections.
66 if (ibus_) {
67 g_object_unref(ibus_);
68 }
69 if (dbus_focus_in_) {
70 dbus::Disconnect(dbus_focus_in_);
71 }
72 if (dbus_focus_out_) {
73 dbus::Disconnect(dbus_focus_out_);
74 }
75 if (dbus_state_changed_) {
76 dbus::Disconnect(dbus_state_changed_);
77 }
78 }
79
80 // Initializes IBus and DBus connections.
81 bool Init() {
82 // Establish IBus connection between ibus-daemon to retrieve the list of
83 // available IME engines, change the current IME engine, and so on.
84 ibus_init();
85 ibus_ = ibus_bus_new();
86
87 // Check the IBus connection status.
88 if (!ibus_) {
89 LOG(ERROR) << "ibus_bus_new() failed";
90 return false;
91 }
92 if (!ibus_bus_is_connected(ibus_)) {
93 LOG(ERROR) << "ibus_bus_is_connected() failed";
94 return false;
95 }
96
97 // Establish a DBus connection between the candidate_window process for
98 // Chromium OS to handle "FocusIn", "FocusOut", and "StateChanged" signals
99 // from the process.
100 const char* address = ibus_get_address();
101 dbus::BusConnection dbus(dbus::GetPrivateBusConnection(address));
102 LOG(INFO) << "Established private D-Bus connection to: '" << address << "'";
103
104 const bool kConnectToNameOwner = true;
105 // TODO(yusukes): dbus::Proxy instantiation might fail (and abort due to
106 // DCHECK failure) when candidate_window process does not exist yet.
107 // Would be better to add "bool dbus::Proxy::Init()" or something like that
108 // to handle such case?
109 dbus::Proxy candidate_window(dbus,
110 kCandidateWindowService,
111 kCandidateWindowObjectPath,
112 kCandidateWindowInterface,
113 kConnectToNameOwner);
114
115 if (!candidate_window) {
116 LOG(ERROR) << "Can't construct proxy for the candidate window. "
117 << "candidate window is not running?";
118 return false;
119 }
120
121 dbus_focus_in_ = dbus::Monitor(candidate_window,
122 "FocusIn",
123 &LanguageStatusConnection::FocusIn,
124 this);
125 dbus_focus_out_ = dbus::Monitor(candidate_window,
126 "FocusOut",
127 &LanguageStatusConnection::FocusOut,
128 this);
129 dbus_state_changed_ = dbus::Monitor(candidate_window,
130 "StateChanged",
131 &LanguageStatusConnection::StateChanged,
132 this);
133
134 // TODO(yusukes): Investigate what happens if IBus/DBus connections are
135 // suddenly closed.
136 // TODO(yusukes): Investigate what happens if candidate_window process is
137 // restarted. I'm not sure but we should use dbus_g_proxy_new_for_name(),
138 // not dbus_g_proxy_new_for_name_owner()?
139
140 return true;
141 }
142
143 // Called by cros API ChromeOSGetLanguages() and returns a list of IMEs and
144 // XKB layouts that are currently available.
145 InputLanguageList* GetLanguages() {
146 GList* engines = ibus_bus_list_active_engines(ibus_);
147 if (!engines) {
148 // IBus connection is broken?
149 LOG(ERROR) << "ibus_bus_list_active_engines() failed.";
150 return NULL;
151 }
152 InputLanguageList* language_list = new InputLanguageList;
153 AddIMELanguages(engines, language_list);
154 AddXKBLayouts(language_list);
155 std::sort(language_list->begin(), language_list->end());
156
157 g_list_free(engines);
158 return language_list;
159 }
160
161 // Called by cros API ChromeOSChangeLanguage().
162 void SwitchXKB(const char* name) {
163 IBusInputContext* context
164 = ibus_input_context_get_input_context(input_context_path_.c_str(),
165 ibus_bus_get_connection(ibus_));
166 ibus_input_context_disable(context);
167 g_object_unref(context);
168 // TODO(yusukes): implement XKB switching.
169 UpdateUI();
170 }
171
172 // Called by cros API ChromeOSChangeLanguage() as well.
173 void SwitchIME(const char* name) {
174 if (input_context_path_.empty()) {
175 LOG(ERROR) << "Input context is unknown";
176 return;
177 }
178
179 IBusInputContext* context
180 = ibus_input_context_get_input_context(input_context_path_.c_str(),
181 ibus_bus_get_connection(ibus_));
182 ibus_input_context_set_engine(context, name);
183 g_object_unref(context);
184 UpdateUI();
185 }
186
187 private:
188 // Handles "FocusIn" signal from candidate_window.
189 static void FocusIn(void* object, const char* input_context_path) {
190 DCHECK(input_context_path) << "NULL context passed";
191 DLOG(INFO) << "FocusIn: " << input_context_path;
192 DCHECK(object);
193
194 // Remember the current ic path.
195 LanguageStatusConnection* self
196 = static_cast<LanguageStatusConnection*>(object);
197 self->input_context_path_ = input_context_path;
198 self->UpdateUI(); // This is necessary since IME status is held per ic.
199 }
200
201 // Handles "FocusOut" signal from candidate_window.
202 static void FocusOut(void* object, const char* input_context_path) {
203 DCHECK(input_context_path) << "NULL context passed";
204 DLOG(INFO) << "FocusOut: " << input_context_path;
205 DCHECK(object);
206 }
207
208 // Handles "StateChanged" signal from candidate_window process.
209 static void StateChanged(void* object, const char* dummy) {
210 // TODO(yusukes): Modify common/chromeos/dbus/dbus.h so that we can handle
211 // signals without argument. Then remove the |dummy|.
212 DLOG(INFO) << "StateChanged";
213 DCHECK(object);
214 LanguageStatusConnection* self
215 = static_cast<LanguageStatusConnection*>(object);
216 self->UpdateUI();
217 }
218
219 // Retrieve IME/XKB status and notify them to the UI.
220 void UpdateUI() {
221 if (input_context_path_.empty()) {
222 LOG(ERROR) << "Input context is unknown";
223 return;
224 }
225
226 IBusInputContext* context
227 = ibus_input_context_get_input_context(
228 input_context_path_.c_str(), ibus_bus_get_connection(ibus_));
229
230 InputLanguage current_language;
231 const bool ime_is_enabled = ibus_input_context_is_enabled(context);
232 if (ime_is_enabled) {
233 DLOG(INFO) << "IME is active";
234 // Set IME name on current_language.
235 const IBusEngineDesc* engine_desc
236 = ibus_input_context_get_engine(context);
237 DCHECK(engine_desc);
238 if (!engine_desc) {
239 return;
240 }
241 current_language = InputLanguage(LANGUAGE_CATEGORY_IME,
242 engine_desc->name,
243 engine_desc->longname,
244 engine_desc->icon);
245 } else {
246 DLOG(INFO) << "IME is not active";
247 // Set XKB layout name on current_languages.
248 current_language = InputLanguage(LANGUAGE_CATEGORY_XKB,
249 kFallbackXKBId,
250 kFallbackXKBDisplayName,
251 "" /* no icon */); // mock
252 // TODO(yusukes): implemente this.
253 }
254 DLOG(INFO) << "Updating the UI. ID:" << current_language.id
255 << ", display_name:" << current_language.display_name;
256
257 // Notify the change to update UI.
258 monitor_function_(language_library_, current_language);
259 g_object_unref(context);
260 }
261
262 // A function pointer which points LanguageLibrary::LanguageChangedHandler
263 // function. |monitor_funcion_| is called when Chrome UI needs to be updated.
264 LanguageStatusMonitorFunction monitor_function_;
265 // Points to a chromeos::LanguageLibrary object. |language_library_| is used
266 // as the first argument of the |monitor_function_| function.
267 void* language_library_;
268
269 // Connections to IBus and DBus.
270 typedef dbus::MonitorConnection<void (const char*)>* DBusConnectionType;
271 IBusBus* ibus_;
272 DBusConnectionType dbus_focus_in_;
273 DBusConnectionType dbus_focus_out_;
274 DBusConnectionType dbus_state_changed_;
275
276 // Current input context path.
277 std::string input_context_path_;
278 };
279
280 //
281 // cros APIs
282 //
283
284 // The function will be bound to chromeos::MonitorLanguageStatus with dlsym()
285 // in load.cc so it needs to be in the C linkage, so the symbol name does not
286 // get mangled.
287 extern "C"
288 LanguageStatusConnection* ChromeOSMonitorLanguageStatus(
289 LanguageStatusMonitorFunction monitor_function, void* language_library) {
290 LOG(INFO) << "MonitorLanguageStatus";
291 LanguageStatusConnection* connection
292 = new LanguageStatusConnection(monitor_function, language_library);
293 if (!connection->Init()) {
294 LOG(WARNING) << "Failed to Init() LanguageStatusConnection. "
295 << "Returning NULL";
296 delete connection;
297 connection = NULL;
298 }
299 return connection;
300 }
301
302 extern "C"
303 void ChromeOSDisconnectLanguageStatus(LanguageStatusConnection* connection) {
304 LOG(INFO) << "DisconnectLanguageStatus";
305 delete connection;
306 }
307
308 extern "C"
309 InputLanguageList* ChromeOSGetLanguages(LanguageStatusConnection* connection) {
310 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for
311 // Chrome OS gets ready.
312 if (!connection) {
313 LOG(WARNING) << "LanguageStatusConnection is NULL";
314 return NULL;
315 }
316 // Pass ownership to a caller. Note: GetLanguages() might return NULL.
317 return connection->GetLanguages();
318 }
319
320 extern "C"
321 void ChromeOSChangeLanguage(LanguageStatusConnection* connection,
322 LanguageCategory category,
323 const char* name) {
324 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for
325 // Chrome OS gets ready.
326 if (!connection) {
327 LOG(WARNING) << "LanguageStatusConnection is NULL";
328 return;
329 }
330 DCHECK(name);
331 DLOG(INFO) << "ChangeLanguage: " << name << " [category " << category << "]";
332 switch (category) {
333 case LANGUAGE_CATEGORY_XKB:
334 connection->SwitchXKB(name);
335 break;
336 case LANGUAGE_CATEGORY_IME:
337 connection->SwitchIME(name);
338 break;
339 }
340 }
341
342 } // namespace chromeos
OLDNEW
« 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