普通文本  |  461行  |  14.55 KB

// Copyright (c) 2010 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 <assert.h>
#include <glib.h>
#include <ibus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>

namespace {

const gchar kDummySection[] = "aaa/bbb";
const gchar kDummyConfigName[] = "ccc";

const gboolean kDummyValueBoolean = TRUE;
const gint kDummyValueInt = 12345;
const gdouble kDummyValueDouble = 2345.5432;
const gchar kDummyValueString[] = "dummy value";

const size_t kArraySize = 3;
const gboolean kDummyValueBooleanArray[kArraySize] = { FALSE, TRUE, FALSE };
const gint kDummyValueIntArray[kArraySize] = { 123, 234, 345 };
const gdouble kDummyValueDoubleArray[kArraySize] = { 111.22, 333.44, 555.66 };
const gchar* kDummyValueStringArray[kArraySize] = {
  "DUMMY_VALUE 1", "DUMMY_VALUE 2", "DUMMY_VALUE 3",
};

const char kGeneralSectionName[] = "general";
const char kPreloadEnginesConfigName[] = "preload_engines";

// Converts |list_type_string| into its element type (e.g. "int_list" to "int").
std::string GetElementType(const std::string& list_type_string) {
  const std::string suffix = "_list";
  if (list_type_string.length() > suffix.length()) {
    return list_type_string.substr(
        0, list_type_string.length() - suffix.length());
  }
  return list_type_string;
}

// Converts |type_string| into GVariantClass.
GVariantClass GetGVariantClassFromStringOrDie(const std::string& type_string) {
  if (type_string == "boolean") {
    return G_VARIANT_CLASS_BOOLEAN;
  } else if (type_string == "int") {
    return G_VARIANT_CLASS_INT32;
  } else if (type_string == "double") {
    return G_VARIANT_CLASS_DOUBLE;
  } else if (type_string == "string") {
    return G_VARIANT_CLASS_STRING;
  } else if (GetElementType(type_string) != type_string) {
    return G_VARIANT_CLASS_ARRAY;
  }
  printf("FAIL (unknown type: %s)\n", type_string.c_str());
  abort();
}

// Unsets a dummy value from ibus config service.
void UnsetConfigAndPrintResult(IBusConfig* ibus_config) {
  if (ibus_config_unset(ibus_config, kDummySection, kDummyConfigName)) {
    printf("OK\n");
  } else {
    printf("FAIL\n");
  }
}

// Sets a dummy value to ibus config service. You can specify a type of the
// dummy value by |type_string|. "boolean", "int", "double", or "string" are
// allowed.
void SetConfigAndPrintResult(
    IBusConfig* ibus_config, const std::string& type_string) {
  GVariant* variant = NULL;
  GVariantClass klass = GetGVariantClassFromStringOrDie(type_string);

  switch (klass) {
    case G_VARIANT_CLASS_BOOLEAN:
      variant = g_variant_new_boolean(kDummyValueBoolean);
      break;
    case G_VARIANT_CLASS_INT32:
      variant = g_variant_new_int32(kDummyValueInt);
      break;
    case G_VARIANT_CLASS_DOUBLE:
      variant = g_variant_new_double(kDummyValueDouble);
      break;
    case G_VARIANT_CLASS_STRING:
      variant = g_variant_new_string(kDummyValueString);
      break;
    case G_VARIANT_CLASS_ARRAY: {
      const GVariantClass element_klass
          = GetGVariantClassFromStringOrDie(GetElementType(type_string));
      g_assert(element_klass != G_VARIANT_CLASS_ARRAY);

      GVariantBuilder variant_builder;
      for (size_t i = 0; i < kArraySize; ++i) {
        switch (element_klass) {
          case G_VARIANT_CLASS_BOOLEAN:
            if (i == 0) {
              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ab"));
            }
            g_variant_builder_add(
                &variant_builder, "b", kDummyValueBooleanArray[i]);
            break;
          case G_VARIANT_CLASS_INT32:
            if (i == 0) {
              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ai"));
            }
            g_variant_builder_add(
                &variant_builder, "i", kDummyValueIntArray[i]);
            break;
          case G_VARIANT_CLASS_DOUBLE:
            if (i == 0) {
              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("ad"));
            }
            g_variant_builder_add(
                &variant_builder, "d", kDummyValueDoubleArray[i]);
            break;
          case G_VARIANT_CLASS_STRING:
            if (i == 0) {
              g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
            }
            g_variant_builder_add(
                &variant_builder, "s", kDummyValueStringArray[i]);
            break;
          default:
            printf("FAIL\n");
            return;
        }
      }
      variant = g_variant_builder_end(&variant_builder);
      break;
    }
    default:
      printf("FAIL\n");
      return;
  }
  if (!variant) {
    printf("FAIL\n");
    return;
  }
  if (ibus_config_set_value(
          ibus_config, kDummySection, kDummyConfigName, variant)) {
    printf("OK\n");
    return;
  }
  printf("FAIL\n");
}

// Gets a dummy value from ibus config service. This function checks if the
// dummy value is |type_string| type.
void GetConfigAndPrintResult(
    IBusConfig* ibus_config, const std::string& type_string) {
  GVariant* variant = ibus_config_get_value(
      ibus_config, kDummySection, kDummyConfigName);
  if (!variant) {
    printf("FAIL (not found)\n");
    return;
  }
  switch(g_variant_classify(variant)) {
    case G_VARIANT_CLASS_BOOLEAN: {
      if (g_variant_get_boolean(variant) != kDummyValueBoolean) {
        printf("FAIL (value mismatch)\n");
        return;
      }
      break;
    }
    case G_VARIANT_CLASS_INT32: {
      if (g_variant_get_int32(variant) != kDummyValueInt) {
        printf("FAIL (value mismatch)\n");
        return;
      }
      break;
    }
    case G_VARIANT_CLASS_DOUBLE: {
      if (g_variant_get_double(variant) != kDummyValueDouble) {
        printf("FAIL (value mismatch)\n");
        return;
      }
      break;
    }
    case G_VARIANT_CLASS_STRING: {
      const char* value = g_variant_get_string(variant, NULL);
      if (value == NULL ||
          value != std::string(kDummyValueString)) {
        printf("FAIL (value mismatch)\n");
        return;
      }
      break;
    }
    case G_VARIANT_CLASS_ARRAY: {
      const GVariantType* variant_element_type
          = g_variant_type_element(g_variant_get_type(variant));
      GVariantIter iter;
      g_variant_iter_init(&iter, variant);

      size_t i;
      GVariant* element = g_variant_iter_next_value(&iter);
      for (i = 0; element; ++i) {
        bool match = false;
        if (g_variant_type_equal(
                variant_element_type, G_VARIANT_TYPE_BOOLEAN)) {
          const gboolean value = g_variant_get_boolean(element);
          match = (value == kDummyValueBooleanArray[i]);
        } else if (g_variant_type_equal(
            variant_element_type, G_VARIANT_TYPE_INT32)) {
          const gint32 value = g_variant_get_int32(element);
          match = (value == kDummyValueIntArray[i]);
        } else if (g_variant_type_equal(
            variant_element_type, G_VARIANT_TYPE_DOUBLE)) {
          const gdouble value = g_variant_get_double(element);
          match = (value == kDummyValueDoubleArray[i]);
        } else if (g_variant_type_equal(
            variant_element_type, G_VARIANT_TYPE_STRING)) {
          const char* value = g_variant_get_string(element, NULL);
          match = (value && (value == std::string(kDummyValueStringArray[i])));
        } else {
          printf("FAIL (list type mismatch)\n");
          return;
        }
        if (!match) {
          printf("FAIL (value mismatch)\n");
          return;
        }
        g_variant_unref(element);
        element = g_variant_iter_next_value(&iter);
      }
      if (i != kArraySize) {
        printf("FAIL (invalid array)\n");
        return;
      }
      break;
    }
    default:
      printf("FAIL (unknown type)\n");
      return;
  }
  printf("OK\n");
}

// Prints out the array. It is assumed that the array contains STRING values.
// On success, returns true
// On failure, prints out "FAIL (error message)" and returns false
bool PrintArray(GVariant* variant) {
  if (g_variant_classify(variant) != G_VARIANT_CLASS_ARRAY) {
    printf("FAIL (Not an array)\n");
    return false;
  }
  const GVariantType* variant_element_type
      = g_variant_type_element(g_variant_get_type(variant));
  if (!g_variant_type_equal(variant_element_type, G_VARIANT_TYPE_STRING)) {
    printf("FAIL (Array element type is not STRING)\n");
    return false;
  }
  GVariantIter iter;
  g_variant_iter_init(&iter, variant);
  GVariant* element = g_variant_iter_next_value(&iter);
  while(element) {
    const char* value = g_variant_get_string(element, NULL);
    if (!value) {
      printf("FAIL (Array element type is NULL)\n");
      return false;
    }
    printf("%s\n", value);
    element = g_variant_iter_next_value(&iter);
  }
  return true;
}

// Print out the list of unused config variables from ibus.
// On failure, prints out "FAIL (error message)" instead.
void PrintUnused(IBusConfig* ibus_config) {
  GVariant* unread = NULL;
  GVariant* unwritten = NULL;
  if (!ibus_config_get_unused(ibus_config, &unread, &unwritten)) {
    printf("FAIL (get_unused failed)\n");
    return;
  }

  if (g_variant_classify(unread) != G_VARIANT_CLASS_ARRAY) {
    printf("FAIL (unread is not an array)\n");
    g_variant_unref(unread);
    g_variant_unref(unwritten);
    return;
  }

  if (g_variant_classify(unwritten) != G_VARIANT_CLASS_ARRAY) {
    printf("FAIL (unwritten is not an array)\n");
    g_variant_unref(unread);
    g_variant_unref(unwritten);
    return;
  }

  printf("Unread:\n");
  if (!PrintArray(unread)) {
    g_variant_unref(unread);
    g_variant_unref(unwritten);
    return;
  }

  printf("Unwritten:\n");
  if (!PrintArray(unwritten)) {
    g_variant_unref(unread);
    g_variant_unref(unwritten);
    return;
  }

  g_variant_unref(unread);
  g_variant_unref(unwritten);
}

// Set the preload engines to those named in the array |engines| of size
// |num_engines| and prints the result.
//
// Note that this only fails if it can't set the config value; it does not check
// that the names of the engines are valid.
void PreloadEnginesAndPrintResult(IBusConfig* ibus_config, int num_engines,
                                  char** engines) {
  GVariant* variant = NULL;
  GVariantBuilder variant_builder;
  g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
  for (int i = 0; i < num_engines; ++i) {
    g_variant_builder_add(&variant_builder, "s", engines[i]);
  }
  variant = g_variant_builder_end(&variant_builder);

  if (ibus_config_set_value(ibus_config, kGeneralSectionName,
                            kPreloadEnginesConfigName, variant)) {
    printf("OK\n");
  } else {
    printf("FAIL\n");
  }
  g_variant_unref(variant);
}

// Sets |engine_name| as the active IME engine.
void ActivateEngineAndPrintResult(IBusBus* ibus, const char* engine_name) {
  if (!ibus_bus_set_global_engine(ibus, engine_name)) {
    printf("FAIL (could not start engine)\n");
  } else {
    printf("OK\n");
  }
}

// Prints the name of the active IME engine.
void PrintActiveEngine(IBusBus* ibus) {
  IBusEngineDesc* engine_desc = ibus_bus_get_global_engine(ibus);
  if (engine_desc) {
    printf("%s\n", ibus_engine_desc_get_name(engine_desc));
    g_object_unref(engine_desc);
  } else {
    printf("FAIL (Could not get active engine)\n");
  }
}

// Prints the names of the given engines. Takes the ownership of |engines|.
void PrintEngineNames(GList* engines) {
  for (GList* cursor = engines; cursor; cursor = g_list_next(cursor)) {
    IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(cursor->data);
    assert(engine_desc);
    printf("%s\n", ibus_engine_desc_get_name(engine_desc));
    g_object_unref(IBUS_ENGINE_DESC(cursor->data));
  }
  g_list_free(engines);
}

void PrintUsage(const char* argv0) {
  printf("Usage: %s COMMAND\n", argv0);
  printf("check_reachable      Check if ibus-daemon is reachable\n");
  printf("list_engines         List engine names (all engines)\n");
  printf("list_active_engines  List active engine names\n");
  // TODO(yusukes): Add 2 parameters, config_key and config_value, to
  // set_config and get_config commands.
  printf("set_config (boolean|int|double|string|\n"
         "            boolean_list|int_list|double_list|string_list)\n"
         "                     Set a dummy value to ibus config service\n");
  printf("get_config (boolean|int|double|string\n"
         "            boolean_list|int_list|double_list|string_list)\n"
         "                     Get a dummy value from ibus config service\n");
  // TODO(yusukes): Add config_key parameter to unset_config.
  printf("unset_config         Unset a dummy value from ibus config service\n");
  printf("get_unused           List all keys that never were used.\n");
  printf("preload_engines      Preload the listed engines.\n");
  printf("activate_engine      Activate the specified engine.\n");
  printf("get_active_engine    Print the name of the current active engine.\n");
}

}  // namespace

int main(int argc, char **argv) {
  if (argc == 1) {
    PrintUsage(argv[0]);
    return 1;
  }

  ibus_init();
  bool connected = false;
  IBusBus* ibus = ibus_bus_new();
  if (ibus) {
    connected = ibus_bus_is_connected(ibus);
  }

  const std::string command = argv[1];
  if (command == "check_reachable") {
    printf("%s\n", connected ? "YES" : "NO");
    return 0;
  } else if (!connected) {
    printf("FAIL (Not connected)\n");
    return 0;
  }

  // Other commands need the bus to be connected.
  assert(ibus);
  assert(connected);
  GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus);
  assert(ibus_connection);
  IBusConfig* ibus_config = ibus_config_new(ibus_connection, NULL, NULL);
  assert(ibus_config);

  if (command == "list_engines") {
    PrintEngineNames(ibus_bus_list_engines(ibus));
  } else if (command == "list_active_engines") {
    PrintEngineNames(ibus_bus_list_active_engines(ibus));
  } else if (command == "set_config") {
    if (argc != 3) {
      PrintUsage(argv[0]);
      return 1;
    }
    SetConfigAndPrintResult(ibus_config, argv[2]);
  } else if (command == "get_config") {
    if (argc != 3) {
      PrintUsage(argv[0]);
      return 1;
    }
    GetConfigAndPrintResult(ibus_config, argv[2]);
  } else if (command == "unset_config") {
    UnsetConfigAndPrintResult(ibus_config);
  } else if (command == "get_unused") {
    PrintUnused(ibus_config);
  } else if (command == "preload_engines") {
    if (argc < 3) {
      PrintUsage(argv[0]);
      return 1;
    }
    PreloadEnginesAndPrintResult(ibus_config, argc-2, &(argv[2]));
  } else if (command == "activate_engine") {
    if (argc != 3) {
      PrintUsage(argv[0]);
      return 1;
    }
    ActivateEngineAndPrintResult(ibus, argv[2]);
  } else if (command == "get_active_engine") {
    PrintActiveEngine(ibus);
  } else {
    PrintUsage(argv[0]);
    return 1;
  }

  return 0;
}