diff --git a/ssb-gtk/sbot.c b/ssb-gtk/sbot.c index 4e96dd6..b77e10a 100644 --- a/ssb-gtk/sbot.c +++ b/ssb-gtk/sbot.c @@ -92,9 +92,16 @@ enum { SIGNAL_LAST }; -static guint ssb_scuttler_signals[SIGNAL_LAST] = {0 +enum { + PROP_0, + PROP_INITIALISED, + PROP_CONNECTED, + PROP_COUNT }; +static guint ssb_scuttler_signals[SIGNAL_LAST] = {0}; +static GParamSpec *ssb_scuttler_properties[PROP_COUNT]; + GMainLoop *scuttle_loop = NULL; SsbScuttler *singleton = NULL; @@ -232,7 +239,7 @@ emit_initialised(SsbScuttler *scuttler, gboolean success) } static void -initialise(SsbScuttler *scuttler, gchar *ssb_dir) +initialise(SsbScuttler *scuttler, const gchar *ssb_dir) { gchar *config_data = NULL; GError *err = NULL; @@ -319,13 +326,13 @@ initialise(SsbScuttler *scuttler, gchar *ssb_dir) } /** - * ensure_scuttler(): + * ssb_scuttler_ensure(): * @ssb_dir: (nullable): the SSB directory * * Ensure the scuttler singleton is initialised with @ssb_dir as its base directory. */ -static inline gboolean -ensure_scuttler(gchar *ssb_dir) +gboolean +ssb_scuttler_ensure(const gchar *ssb_dir) { if (singleton != NULL) { if ((ssb_dir != NULL) && g_strcmp0(ssb_dir, singleton->ssb_dir)) { @@ -344,14 +351,6 @@ ensure_scuttler(gchar *ssb_dir) return TRUE; } -static gboolean -second_hook_cb() -{ - g_print("Scuttling…\n"); - - return TRUE; -} - // was: write_all static gboolean ssb_scuttler_send_plain(SsbScuttler *scuttler, gpointer data, gsize data_len, GError **error) @@ -597,6 +596,8 @@ ssb_scuttler_shs_connect(SsbScuttler *scuttler, GError **error) scuttler->noauth = FALSE; scuttler->wrote_goodbye = FALSE; + g_debug("SHS succeeded"); + return TRUE; } @@ -606,14 +607,62 @@ emit_connected(SsbScuttler *scuttler, gboolean success) g_signal_emit(scuttler, ssb_scuttler_signals[SIGNAL_CONNECTED], 0, success); } -// extracted from main -static gboolean -ssb_scuttler_connect(SsbScuttler *scuttler, GError **error) +static void +connection_finished(GObject *source, GAsyncResult *result, gpointer user_data) { + GSocketClient *socket_client = G_SOCKET_CLIENT(source); + GTask *task = user_data; + SsbScuttler *scuttler = g_task_get_task_data(task); + GError *err = NULL; + GSocketConnection *connection = g_socket_client_connect_finish(socket_client, result, &err); + + if (connection == NULL) { + g_task_return_error(task, err); + g_object_unref(task); + + return; + } + + g_clear_object(&(scuttler->connection)); + scuttler->connection = connection; + + // TODO: This is only required if noauth is not set + if (!ssb_scuttler_shs_connect(scuttler, &err)) { + g_task_return_error(task, err); + g_object_unref(task); + + return; + } + + g_task_return_boolean(task, TRUE); + emit_connected(scuttler, TRUE); + g_object_unref(task); +} + +void +ssb_scuttler_connect_async(SsbScuttler *scuttler, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; GInetAddress *address; GSocketAddress *destination; - GError *err = NULL; + g_return_if_fail(SSB_IS_SCUTTLER(scuttler)); + g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); + + task = g_task_new(scuttler, cancellable, callback, user_data); + + // TODO: Maybe there should be a better condition here… + if (scuttler->connection) { + g_task_return_boolean(task, TRUE); + g_object_unref(task); + + return; + } + + g_clear_object(&(scuttler->socket_client)); scuttler->socket_client = g_socket_client_new(); // TODO: Implement the UNIX socket version, too! Also, read this stuff from the @@ -622,20 +671,19 @@ ssb_scuttler_connect(SsbScuttler *scuttler, GError **error) destination = g_inet_socket_address_new(address, 8008); g_object_unref(address); - scuttler->connection = g_socket_client_connect(scuttler->socket_client, - G_SOCKET_CONNECTABLE(destination), - NULL, - &err); + g_task_set_task_data(task, g_object_ref(scuttler), (GDestroyNotify)g_object_unref); + + g_socket_client_connect_async(scuttler->socket_client, G_SOCKET_CONNECTABLE(destination), cancellable, connection_finished, task); g_object_unref(destination); +} - // TODO: This is only required if noauth is not set - if (!ssb_scuttler_shs_connect(scuttler, &err)) { - g_propagate_error(error, err); +gboolean +ssb_scuttler_connect_finish(SsbScuttler *scuttler, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(SSB_IS_SCUTTLER(scuttler), FALSE); + g_return_val_if_fail(g_task_is_valid(result, scuttler), FALSE); - return FALSE; - } - - return TRUE; + return g_task_propagate_boolean(G_TASK(result), error); } static void @@ -747,7 +795,22 @@ ssb_scuttler_send_packet(SsbScuttler *scuttler, } // was: muxrpc_call -static void +/** + * ssb_scuttler_muxrpc_call: + * @scuttler: an #SsbScuttler instance + * @method: the name of the RPC method to call + * @argument: the argument for the method + * @type: the type of the RPC call + * @typestr: TODO + * @req_id: the request ID to use in the RPC call + * @error: (nullable): placeholder for a #GError, or %NULL + * + * Start an RPC call to SBOT. + * + * This is a low-level call and unless a specific RPC method is not exposed via other #SsbScuttler + * method, it should not be called explicitly. + */ +void ssb_scuttler_muxrpc_call(SsbScuttler *scuttler, const gchar *method, const gchar *argument, @@ -821,6 +884,12 @@ ssb_scuttler_class_init(SsbScuttlerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + if (sodium_init() < 0) { + g_critical("Can not initialise sodium"); + + return; + } + gobject_class->dispose = ssb_scuttler_dispose; gobject_class->finalize = ssb_scuttler_finalize; @@ -851,6 +920,23 @@ ssb_scuttler_class_init(SsbScuttlerClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + /** + * SsbScuttler:initialised: + * + * If %TRUE, the scuttler is connected + */ + ssb_scuttler_properties[PROP_INITIALISED] = g_param_spec_boolean( + "initialised", "initialised", "initialised", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + + ssb_scuttler_properties[PROP_CONNECTED] = g_param_spec_boolean( + "connected", "connected", "connected", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + + g_object_class_install_properties(gobject_class, PROP_COUNT, ssb_scuttler_properties); } static void @@ -865,68 +951,12 @@ ssb_scuttler_init(SsbScuttler *scuttler) scuttler->encrypt_key = NULL; } -gpointer -scuttle(gchar *ssb_dir) +SsbScuttler * +ssb_scuttler_get(void) { - GMainContext *scuttle_context; - GSource *second_hook; - GError *err = NULL; - - if (sodium_init() < 0) { - g_critical("Can not initialise sodium"); - + if (singleton == NULL) { return NULL; } - // Since scuttler is a singleton global to this file, we don’t need the return value of this - // function now. - if (G_UNLIKELY(!ensure_scuttler(ssb_dir))) { - g_critical("Can not reinitialise scuttler with a new SSB directory."); - - return NULL; - } - - g_object_ref_sink(singleton); - - scuttle_context = g_main_context_new(); - scuttle_loop = g_main_loop_new(scuttle_context, FALSE); - - // TODO: This is for debugging purposes only, let’s remove it as soon as the scuttler works - second_hook = g_timeout_source_new_seconds(1); - g_source_set_callback(second_hook, - G_SOURCE_FUNC(second_hook_cb), - g_main_loop_ref(scuttle_loop), - (GDestroyNotify)g_main_loop_unref); - g_source_attach(second_hook, scuttle_context); - - // Set scuttle_context as the default context for this thread - g_main_context_push_thread_default(scuttle_context); - - if (!ssb_scuttler_connect(singleton, &err)) - { - g_critical("Could not connect: %s", err->message); - - return NULL; - } - - g_debug("Starting scuttle"); - - g_main_loop_run(scuttle_loop); - g_main_context_pop_thread_default(scuttle_context); - g_main_loop_unref(scuttle_loop); - g_object_unref(singleton); - - g_debug("Scuttling stopped"); - - scuttle_loop = NULL; - - return NULL; -} - -void -stop_scuttling(void) -{ - if (scuttle_loop) { - g_main_loop_quit(scuttle_loop); - } + return g_object_ref(singleton); } diff --git a/ssb-gtk/sbot.h b/ssb-gtk/sbot.h index b333199..4fa8230 100644 --- a/ssb-gtk/sbot.h +++ b/ssb-gtk/sbot.h @@ -2,6 +2,7 @@ # define __SBOT_H__ # include +# include # define SSB_TYPE_SCUTTLER ssb_scuttler_get_type() G_DECLARE_FINAL_TYPE(SsbScuttler, ssb_scuttler, SSB, SCUTTLER, GInitiallyUnowned) @@ -22,6 +23,15 @@ void stop_scuttling(void); # define SSB_SCUTTLER_ERROR (ssb_scuttler_error_quark()) GQuark ssb_scuttler_error_quark(void); +gboolean ssb_scuttler_ensure(const gchar *ssb_dir); +SsbScuttler *ssb_scuttler_get(void); +void ssb_scuttler_connect_async(SsbScuttler *scuttler, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean ssb_scuttler_connect_finish(SsbScuttler *scuttler, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/ssb-gtk/ssb-app.c b/ssb-gtk/ssb-app.c index 1663e8d..a13e746 100644 --- a/ssb-gtk/ssb-app.c +++ b/ssb-gtk/ssb-app.c @@ -7,7 +7,7 @@ struct _SsbApp { gchar *ssb_dir; SsbWindow *window; - GThread *scuttle_thread; + SsbScuttler *scuttler; }; G_DEFINE_TYPE(SsbApp, ssb_app, GTK_TYPE_APPLICATION); @@ -78,19 +78,42 @@ ssb_app_dispose(GObject *gobject) SsbApp *app = SSB_APP(gobject); g_clear_object(&(app->window)); + g_clear_object(&(app->scuttler)); + G_OBJECT_CLASS(ssb_app_parent_class)->dispose(gobject); } +void +connect_finished(GObject *source, GAsyncResult *res, gpointer user_data) +{ + SsbScuttler *scuttler = SSB_SCUTTLER(source); + SsbApp *app = user_data; + GError *err = NULL; + gboolean connect_successful = ssb_scuttler_connect_finish(scuttler, res, &err); +} + static void ssb_app_startup(GApplication *gapp) { SsbApp *app = SSB_APP(gapp); + SsbScuttler *scuttler = NULL; G_APPLICATION_CLASS(ssb_app_parent_class)->startup(gapp); - if (app->scuttle_thread == NULL) { - app->scuttle_thread = g_thread_new("scuttler", (GThreadFunc)scuttle, app->ssb_dir); + if (G_UNLIKELY(!ssb_scuttler_ensure(app->ssb_dir))) { + g_critical("Can not reinitialise scuttler with a new SSB directory."); + + return; } + + // Wait until the scuttler instance comes up + if ((scuttler = ssb_scuttler_get()) == NULL) { + g_critical("Can not get scuttler object"); + } + + app->scuttler = g_object_ref_sink(ssb_scuttler_get()); + + ssb_scuttler_connect_async(app->scuttler, NULL, connect_finished, app); } static SsbWindow * @@ -113,12 +136,6 @@ ssb_app_activate(GApplication *app) static void ssb_app_shutdown(GApplication *gapp) { - SsbApp *app = SSB_APP(gapp); - - stop_scuttling(); - - (void)g_thread_join(app->scuttle_thread); - G_APPLICATION_CLASS(ssb_app_parent_class)->shutdown(gapp); } @@ -130,7 +147,7 @@ ssb_app_init(SsbApp *app) app->ssb_dir = NULL; app->window = NULL; - app->scuttle_thread = NULL; + app->scuttler = NULL; if (app_dir_path != NULL) { app->ssb_dir = g_strdup(app_dir_path);