#include #include #include "zero-pinyin-service.h" #include "../sqlite3_util.h" #include typedef struct { GApplication *app; GDBusNodeInfo *introspection_data; guint owner_id; sqlite3 *db; gboolean init_done; } AppData; static AppData appdata; static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " ""; static void handle_method_get_candidates (GDBusMethodInvocation *invocation, AppData *appdata, GVariant *parameters) { gchar *preedit_str = NULL; guint fetch_size = 0; GVariant *result = NULL; GVariantBuilder *candidates_builder = NULL; GVariantBuilder *matched_lengths_builder = NULL; g_variant_get (parameters, "(su)", &preedit_str, &fetch_size); g_return_if_fail (preedit_str != NULL); g_return_if_fail (fetch_size > 0); if (preedit_str == NULL || fetch_size == 0) { g_dbus_method_invocation_return_dbus_error ( invocation, "org.gtk.GDBus.Failed", "Bad param"); if (preedit_str) g_free (preedit_str); return; } g_message ("get_candidates for preedit_str=%s fetch_size=%u", preedit_str, fetch_size); candidates_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); matched_lengths_builder = g_variant_builder_new (G_VARIANT_TYPE ("au")); /* test data */ /* get_candidates_test (preedit_str, fetch_size, candidates_builder, matched_lengths_builder); */ get_candidates (appdata->db, preedit_str, fetch_size, candidates_builder, matched_lengths_builder); result = g_variant_new ("(asau)", candidates_builder, matched_lengths_builder); g_assert_nonnull (result); /* result is a GVarient tuple of two dbus arrays */ g_dbus_method_invocation_return_value (invocation, result); g_variant_builder_unref (candidates_builder); g_variant_builder_unref (matched_lengths_builder); g_free (preedit_str); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { AppData *appdata; gchar *errmsg = NULL; appdata = (AppData*) user_data; g_message ("got method call: %s", method_name); if (g_strcmp0 (method_name, "GetCandidates") == 0) { handle_method_get_candidates (invocation, appdata, parameters); } else if (g_strcmp0 (method_name, "Quit") == 0) { g_application_quit (appdata->app); } else { g_warning ("method not found: %s", method_name); errmsg = g_strdup_printf ("Method not found: %s", method_name); g_dbus_method_invocation_return_dbus_error ( invocation, "org.gtk.GDBus.Failed", errmsg); g_free (errmsg); } } static GVariant * handle_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { return NULL; } static gboolean handle_set_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data) { return TRUE; } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, handle_get_property, handle_set_property }; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { AppData *appdata = NULL; GError *err = NULL; guint registration_id = 0; static const gchar* object_path = "/com/emacsos/zero/ZeroPinyinService"; g_message ("on_bus_acquired() name=%s", name); appdata = (AppData*) user_data; registration_id = g_dbus_connection_register_object ( connection, object_path, appdata->introspection_data->interfaces[0], &interface_vtable, appdata, /* user_data */ NULL, /* user_data_free_func */ &err); /* GError** */ if (err != NULL) { g_warning ("object register failed: %s. exiting now", err->message); g_error_free (err); g_application_quit (appdata->app); return; } if (registration_id > 0) { g_message ("object registered at %s", object_path); } g_assert_cmpint (registration_id, >, 0); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_message ("on_name_acquired() name=%s", name); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_message ("on_name_lost() name=%s", name); } static void config_dbus_service (AppData *appdata) { appdata->introspection_data = g_dbus_node_info_new_for_xml ( introspection_xml, NULL); g_assert (appdata->introspection_data != NULL); appdata->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, "com.emacsos.zero.ZeroPinyinService", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT|G_BUS_NAME_OWNER_FLAGS_REPLACE, on_bus_acquired, on_name_acquired, on_name_lost, appdata, NULL); g_assert_cmpint (appdata->owner_id, >, 0); } /** * handle SIGTERM gracefully. */ static gboolean on_sigterm_received (gpointer user_data) { AppData *appdata = (AppData*) user_data; g_application_quit (appdata->app); return G_SOURCE_REMOVE; } static void config_db (AppData* appdata) { gint ri = 0; gboolean rb = FALSE; static const char* SQLITE3_MEMORY_DB = ":memory:"; sqlite3* db = NULL; gchar* sql = NULL; ri = sqlite3_open (SQLITE3_MEMORY_DB, &db); if (ri != SQLITE_OK) { g_warning ("sqlite3_open :memory: db failed, query will not work."); goto db_fail; } g_assert_nonnull (db); /* TODO make db path configurable */ /* TODO remove user name in db path */ sql = sqlite3_mprintf ("ATTACH %Q AS maindb", "/home/sylecn/.cache/ibus/pinyin/main.db"); rb = sqlite3_exec_simple (db, sql); if (! rb) { g_warning ("attach maindb failed, query will not work."); goto attach_fail; } sqlite3_free (sql); /* TODO make db path configurable */ sql = sqlite3_mprintf ("ATTACH %Q AS userdb", "/home/sylecn/.cache/ibus/pinyin/user-1.0.db"); rb = sqlite3_exec_simple (db, sql); if (! rb) { g_warning ("attach userdb failed, query will not work."); goto attach_fail; } appdata->db = db; return; attach_fail: sqlite3_free (sql); sqlite3_close (db); db_fail: appdata->db = NULL; } static void zero_pinyin_service_init (GApplication *app, AppData *appdata) { appdata->introspection_data = NULL; appdata->owner_id = 0; appdata->db = NULL; config_dbus_service (appdata); config_db (appdata); appdata->init_done = TRUE; } /** * allow graceful shutdown by Ctrl-C and SIGTERM. */ static void setup_sigint_sigterm_handler (AppData *appdata) { GSource *source = NULL; source = g_unix_signal_source_new (SIGTERM); g_source_set_callback (source, on_sigterm_received, appdata, NULL); g_source_attach (source, NULL); g_source_unref (source); source = g_unix_signal_source_new (SIGINT); g_source_set_callback (source, on_sigterm_received, appdata, NULL); g_source_attach (source, NULL); g_source_unref (source); } static void startup (GApplication* app, AppData* appdata) { g_message ("zero-pinyin-service startup()"); setup_sigint_sigterm_handler (appdata); g_assert_false (appdata->init_done); zero_pinyin_service_init (app, appdata); g_application_hold (app); } static void activate (GApplication *app, AppData *appdata) { g_message ("zero-pinyin-service activate()"); /* as a pure service, activate is a no-op. */ } static void shutdown (GApplication *app, AppData *appdata) { g_message ("zero-pinyin-service shutdown()"); if (appdata->owner_id > 0) { g_bus_unown_name (appdata->owner_id); appdata->owner_id = 0; } if (appdata->introspection_data != NULL) { g_dbus_node_info_unref (appdata->introspection_data); appdata->introspection_data = NULL; } if (appdata->db != NULL) { sqlite3_close (appdata->db); appdata->db = NULL; } } /** * provide zero-pinyin-service dbus service. * it's a console app based on glib and gio. */ int main (int argc, char *argv[]) { GApplication *app = NULL; int status = 0; app = g_application_new ("com.emacsos.zero.ZeroPinyinServiceApp", G_APPLICATION_FLAGS_NONE); g_assert_nonnull (app); appdata.app = app; appdata.init_done = FALSE; g_signal_connect (app, "startup", G_CALLBACK (startup), &appdata); g_signal_connect (app, "activate", G_CALLBACK (activate), &appdata); g_signal_connect (app, "shutdown", G_CALLBACK (shutdown), &appdata); status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); return status; }