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 <assert.h>
6 #include <glib.h>
7 #include <ibus.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string>
11
12 namespace {
13
14 const gchar kDummySection[] = "aaa/bbb";
15 const gchar kDummyConfigName[] = "ccc";
16
17 const gboolean kDummyValueBoolean = TRUE;
18 const gint kDummyValueInt = 12345;
19 const gdouble kDummyValueDouble = 2345.5432;
20 const gchar kDummyValueString[] = "dummy value";
21
22 const size_t kArraySize = 3;
23 const gboolean kDummyValueBooleanArray[kArraySize] = { FALSE, TRUE, FALSE };
24 const gint kDummyValueIntArray[kArraySize] = { 123, 234, 345 };
25 const gdouble kDummyValueDoubleArray[kArraySize] = { 111.22, 333.44, 555.66 };
26 const gchar* kDummyValueStringArray[kArraySize] = {
27 "DUMMY_VALUE 1", "DUMMY_VALUE 2", "DUMMY_VALUE 3",
28 };
29
30 const char kGeneralSectionName[] = "general";
31 const char kPreloadEnginesConfigName[] = "preload_engines";
32
33 // Converts |list_type_string| into its element type (e.g. "int_list" to "int").
GetElementType(const std::string & list_type_string)34 std::string GetElementType(const std::string& list_type_string) {
35 const std::string suffix = "_list";
36 if (list_type_string.length() > suffix.length()) {
37 return list_type_string.substr(
38 0, list_type_string.length() - suffix.length());
39 }
40 return list_type_string;
41 }
42
43 // Converts |type_string| into GVariantClass.
GetGVariantClassFromStringOrDie(const std::string & type_string)44 GVariantClass GetGVariantClassFromStringOrDie(const std::string& type_string) {
45 if (type_string == "boolean") {
46 return G_VARIANT_CLASS_BOOLEAN;
47 } else if (type_string == "int") {
48 return G_VARIANT_CLASS_INT32;
49 } else if (type_string == "double") {
50 return G_VARIANT_CLASS_DOUBLE;
51 } else if (type_string == "string") {
52 return G_VARIANT_CLASS_STRING;
53 } else if (GetElementType(type_string) != type_string) {
54 return G_VARIANT_CLASS_ARRAY;
55 }
56 printf("FAIL (unknown type: %s)\n", type_string.c_str());
57 abort();
58 }
59
60 // Unsets a dummy value from ibus config service.
UnsetConfigAndPrintResult(IBusConfig * ibus_config)61 void UnsetConfigAndPrintResult(IBusConfig* ibus_config) {
62 if (ibus_config_unset(ibus_config, kDummySection, kDummyConfigName)) {
63 printf("OK\n");
64 } else {
65 printf("FAIL\n");
66 }
67 }
68
69 // Sets a dummy value to ibus config service. You can specify a type of the
70 // dummy value by |type_string|. "boolean", "int", "double", or "string" are
71 // allowed.
SetConfigAndPrintResult(IBusConfig * ibus_config,const std::string & type_string)72 void SetConfigAndPrintResult(
73 IBusConfig* ibus_config, const std::string& type_string) {
74 GVariant* variant = NULL;
75 GVariantClass klass = GetGVariantClassFromStringOrDie(type_string);
76
77 switch (klass) {
78 case G_VARIANT_CLASS_BOOLEAN:
79 variant = g_variant_new_boolean(kDummyValueBoolean);
80 break;
81 case G_VARIANT_CLASS_INT32:
82 variant = g_variant_new_int32(kDummyValueInt);
83 break;
84 case G_VARIANT_CLASS_DOUBLE:
85 variant = g_variant_new_double(kDummyValueDouble);
86 break;
87 case G_VARIANT_CLASS_STRING:
88 variant = g_variant_new_string(kDummyValueString);
89 break;
90 case G_VARIANT_CLASS_ARRAY: {
91 const GVariantClass element_klass
92 = GetGVariantClassFromStringOrDie(GetElementType(type_string));
93 g_assert(element_klass != G_VARIANT_CLASS_ARRAY);
94
95 GVariantBuilder variant_builder;
96 for (size_t i = 0; i < kArraySize; ++i) {
97 switch (element_klass) {
98 case G_VARIANT_CLASS_BOOLEAN:
99 if (i == 0) {
100 g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ab"));
101 }
102 g_variant_builder_add(
103 &variant_builder, "b", kDummyValueBooleanArray[i]);
104 break;
105 case G_VARIANT_CLASS_INT32:
106 if (i == 0) {
107 g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ai"));
108 }
109 g_variant_builder_add(
110 &variant_builder, "i", kDummyValueIntArray[i]);
111 break;
112 case G_VARIANT_CLASS_DOUBLE:
113 if (i == 0) {
114 g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ad"));
115 }
116 g_variant_builder_add(
117 &variant_builder, "d", kDummyValueDoubleArray[i]);
118 break;
119 case G_VARIANT_CLASS_STRING:
120 if (i == 0) {
121 g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
122 }
123 g_variant_builder_add(
124 &variant_builder, "s", kDummyValueStringArray[i]);
125 break;
126 default:
127 printf("FAIL\n");
128 return;
129 }
130 }
131 variant = g_variant_builder_end(&variant_builder);
132 break;
133 }
134 default:
135 printf("FAIL\n");
136 return;
137 }
138 if (!variant) {
139 printf("FAIL\n");
140 return;
141 }
142 if (ibus_config_set_value(
143 ibus_config, kDummySection, kDummyConfigName, variant)) {
144 printf("OK\n");
145 return;
146 }
147 printf("FAIL\n");
148 }
149
150 // Gets a dummy value from ibus config service. This function checks if the
151 // dummy value is |type_string| type.
GetConfigAndPrintResult(IBusConfig * ibus_config,const std::string & type_string)152 void GetConfigAndPrintResult(
153 IBusConfig* ibus_config, const std::string& type_string) {
154 GVariant* variant = ibus_config_get_value(
155 ibus_config, kDummySection, kDummyConfigName);
156 if (!variant) {
157 printf("FAIL (not found)\n");
158 return;
159 }
160 switch(g_variant_classify(variant)) {
161 case G_VARIANT_CLASS_BOOLEAN: {
162 if (g_variant_get_boolean(variant) != kDummyValueBoolean) {
163 printf("FAIL (value mismatch)\n");
164 return;
165 }
166 break;
167 }
168 case G_VARIANT_CLASS_INT32: {
169 if (g_variant_get_int32(variant) != kDummyValueInt) {
170 printf("FAIL (value mismatch)\n");
171 return;
172 }
173 break;
174 }
175 case G_VARIANT_CLASS_DOUBLE: {
176 if (g_variant_get_double(variant) != kDummyValueDouble) {
177 printf("FAIL (value mismatch)\n");
178 return;
179 }
180 break;
181 }
182 case G_VARIANT_CLASS_STRING: {
183 const char* value = g_variant_get_string(variant, NULL);
184 if (value == NULL ||
185 value != std::string(kDummyValueString)) {
186 printf("FAIL (value mismatch)\n");
187 return;
188 }
189 break;
190 }
191 case G_VARIANT_CLASS_ARRAY: {
192 const GVariantType* variant_element_type
193 = g_variant_type_element(g_variant_get_type(variant));
194 GVariantIter iter;
195 g_variant_iter_init(&iter, variant);
196
197 size_t i;
198 GVariant* element = g_variant_iter_next_value(&iter);
199 for (i = 0; element; ++i) {
200 bool match = false;
201 if (g_variant_type_equal(
202 variant_element_type, G_VARIANT_TYPE_BOOLEAN)) {
203 const gboolean value = g_variant_get_boolean(element);
204 match = (value == kDummyValueBooleanArray[i]);
205 } else if (g_variant_type_equal(
206 variant_element_type, G_VARIANT_TYPE_INT32)) {
207 const gint32 value = g_variant_get_int32(element);
208 match = (value == kDummyValueIntArray[i]);
209 } else if (g_variant_type_equal(
210 variant_element_type, G_VARIANT_TYPE_DOUBLE)) {
211 const gdouble value = g_variant_get_double(element);
212 match = (value == kDummyValueDoubleArray[i]);
213 } else if (g_variant_type_equal(
214 variant_element_type, G_VARIANT_TYPE_STRING)) {
215 const char* value = g_variant_get_string(element, NULL);
216 match = (value && (value == std::string(kDummyValueStringArray[i])));
217 } else {
218 printf("FAIL (list type mismatch)\n");
219 return;
220 }
221 if (!match) {
222 printf("FAIL (value mismatch)\n");
223 return;
224 }
225 g_variant_unref(element);
226 element = g_variant_iter_next_value(&iter);
227 }
228 if (i != kArraySize) {
229 printf("FAIL (invalid array)\n");
230 return;
231 }
232 break;
233 }
234 default:
235 printf("FAIL (unknown type)\n");
236 return;
237 }
238 printf("OK\n");
239 }
240
241 // Prints out the array. It is assumed that the array contains STRING values.
242 // On success, returns true
243 // On failure, prints out "FAIL (error message)" and returns false
PrintArray(GVariant * variant)244 bool PrintArray(GVariant* variant) {
245 if (g_variant_classify(variant) != G_VARIANT_CLASS_ARRAY) {
246 printf("FAIL (Not an array)\n");
247 return false;
248 }
249 const GVariantType* variant_element_type
250 = g_variant_type_element(g_variant_get_type(variant));
251 if (!g_variant_type_equal(variant_element_type, G_VARIANT_TYPE_STRING)) {
252 printf("FAIL (Array element type is not STRING)\n");
253 return false;
254 }
255 GVariantIter iter;
256 g_variant_iter_init(&iter, variant);
257 GVariant* element = g_variant_iter_next_value(&iter);
258 while(element) {
259 const char* value = g_variant_get_string(element, NULL);
260 if (!value) {
261 printf("FAIL (Array element type is NULL)\n");
262 return false;
263 }
264 printf("%s\n", value);
265 element = g_variant_iter_next_value(&iter);
266 }
267 return true;
268 }
269
270 // Print out the list of unused config variables from ibus.
271 // On failure, prints out "FAIL (error message)" instead.
PrintUnused(IBusConfig * ibus_config)272 void PrintUnused(IBusConfig* ibus_config) {
273 GVariant* unread = NULL;
274 GVariant* unwritten = NULL;
275 if (!ibus_config_get_unused(ibus_config, &unread, &unwritten)) {
276 printf("FAIL (get_unused failed)\n");
277 return;
278 }
279
280 if (g_variant_classify(unread) != G_VARIANT_CLASS_ARRAY) {
281 printf("FAIL (unread is not an array)\n");
282 g_variant_unref(unread);
283 g_variant_unref(unwritten);
284 return;
285 }
286
287 if (g_variant_classify(unwritten) != G_VARIANT_CLASS_ARRAY) {
288 printf("FAIL (unwritten is not an array)\n");
289 g_variant_unref(unread);
290 g_variant_unref(unwritten);
291 return;
292 }
293
294 printf("Unread:\n");
295 if (!PrintArray(unread)) {
296 g_variant_unref(unread);
297 g_variant_unref(unwritten);
298 return;
299 }
300
301 printf("Unwritten:\n");
302 if (!PrintArray(unwritten)) {
303 g_variant_unref(unread);
304 g_variant_unref(unwritten);
305 return;
306 }
307
308 g_variant_unref(unread);
309 g_variant_unref(unwritten);
310 }
311
312 // Set the preload engines to those named in the array |engines| of size
313 // |num_engines| and prints the result.
314 //
315 // Note that this only fails if it can't set the config value; it does not check
316 // that the names of the engines are valid.
PreloadEnginesAndPrintResult(IBusConfig * ibus_config,int num_engines,char ** engines)317 void PreloadEnginesAndPrintResult(IBusConfig* ibus_config, int num_engines,
318 char** engines) {
319 GVariant* variant = NULL;
320 GVariantBuilder variant_builder;
321 g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
322 for (int i = 0; i < num_engines; ++i) {
323 g_variant_builder_add(&variant_builder, "s", engines[i]);
324 }
325 variant = g_variant_builder_end(&variant_builder);
326
327 if (ibus_config_set_value(ibus_config, kGeneralSectionName,
328 kPreloadEnginesConfigName, variant)) {
329 printf("OK\n");
330 } else {
331 printf("FAIL\n");
332 }
333 g_variant_unref(variant);
334 }
335
336 // Sets |engine_name| as the active IME engine.
ActivateEngineAndPrintResult(IBusBus * ibus,const char * engine_name)337 void ActivateEngineAndPrintResult(IBusBus* ibus, const char* engine_name) {
338 if (!ibus_bus_set_global_engine(ibus, engine_name)) {
339 printf("FAIL (could not start engine)\n");
340 } else {
341 printf("OK\n");
342 }
343 }
344
345 // Prints the name of the active IME engine.
PrintActiveEngine(IBusBus * ibus)346 void PrintActiveEngine(IBusBus* ibus) {
347 IBusEngineDesc* engine_desc = ibus_bus_get_global_engine(ibus);
348 if (engine_desc) {
349 printf("%s\n", ibus_engine_desc_get_name(engine_desc));
350 g_object_unref(engine_desc);
351 } else {
352 printf("FAIL (Could not get active engine)\n");
353 }
354 }
355
356 // Prints the names of the given engines. Takes the ownership of |engines|.
PrintEngineNames(GList * engines)357 void PrintEngineNames(GList* engines) {
358 for (GList* cursor = engines; cursor; cursor = g_list_next(cursor)) {
359 IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(cursor->data);
360 assert(engine_desc);
361 printf("%s\n", ibus_engine_desc_get_name(engine_desc));
362 g_object_unref(IBUS_ENGINE_DESC(cursor->data));
363 }
364 g_list_free(engines);
365 }
366
PrintUsage(const char * argv0)367 void PrintUsage(const char* argv0) {
368 printf("Usage: %s COMMAND\n", argv0);
369 printf("check_reachable Check if ibus-daemon is reachable\n");
370 printf("list_engines List engine names (all engines)\n");
371 printf("list_active_engines List active engine names\n");
372 // TODO(yusukes): Add 2 parameters, config_key and config_value, to
373 // set_config and get_config commands.
374 printf("set_config (boolean|int|double|string|\n"
375 " boolean_list|int_list|double_list|string_list)\n"
376 " Set a dummy value to ibus config service\n");
377 printf("get_config (boolean|int|double|string\n"
378 " boolean_list|int_list|double_list|string_list)\n"
379 " Get a dummy value from ibus config service\n");
380 // TODO(yusukes): Add config_key parameter to unset_config.
381 printf("unset_config Unset a dummy value from ibus config service\n");
382 printf("get_unused List all keys that never were used.\n");
383 printf("preload_engines Preload the listed engines.\n");
384 printf("activate_engine Activate the specified engine.\n");
385 printf("get_active_engine Print the name of the current active engine.\n");
386 }
387
388 } // namespace
389
main(int argc,char ** argv)390 int main(int argc, char **argv) {
391 if (argc == 1) {
392 PrintUsage(argv[0]);
393 return 1;
394 }
395
396 ibus_init();
397 bool connected = false;
398 IBusBus* ibus = ibus_bus_new();
399 if (ibus) {
400 connected = ibus_bus_is_connected(ibus);
401 }
402
403 const std::string command = argv[1];
404 if (command == "check_reachable") {
405 printf("%s\n", connected ? "YES" : "NO");
406 return 0;
407 } else if (!connected) {
408 printf("FAIL (Not connected)\n");
409 return 0;
410 }
411
412 // Other commands need the bus to be connected.
413 assert(ibus);
414 assert(connected);
415 GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus);
416 assert(ibus_connection);
417 IBusConfig* ibus_config = ibus_config_new(ibus_connection, NULL, NULL);
418 assert(ibus_config);
419
420 if (command == "list_engines") {
421 PrintEngineNames(ibus_bus_list_engines(ibus));
422 } else if (command == "list_active_engines") {
423 PrintEngineNames(ibus_bus_list_active_engines(ibus));
424 } else if (command == "set_config") {
425 if (argc != 3) {
426 PrintUsage(argv[0]);
427 return 1;
428 }
429 SetConfigAndPrintResult(ibus_config, argv[2]);
430 } else if (command == "get_config") {
431 if (argc != 3) {
432 PrintUsage(argv[0]);
433 return 1;
434 }
435 GetConfigAndPrintResult(ibus_config, argv[2]);
436 } else if (command == "unset_config") {
437 UnsetConfigAndPrintResult(ibus_config);
438 } else if (command == "get_unused") {
439 PrintUnused(ibus_config);
440 } else if (command == "preload_engines") {
441 if (argc < 3) {
442 PrintUsage(argv[0]);
443 return 1;
444 }
445 PreloadEnginesAndPrintResult(ibus_config, argc-2, &(argv[2]));
446 } else if (command == "activate_engine") {
447 if (argc != 3) {
448 PrintUsage(argv[0]);
449 return 1;
450 }
451 ActivateEngineAndPrintResult(ibus, argv[2]);
452 } else if (command == "get_active_engine") {
453 PrintActiveEngine(ibus);
454 } else {
455 PrintUsage(argv[0]);
456 return 1;
457 }
458
459 return 0;
460 }
461