/* This file is part of GNUnet. Copyright (C) 2021 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /* * @author Tobias Frisch * @file application.c */ #include "application.h" static void _load_ui_stylesheets(void) { GdkScreen* screen = gdk_screen_get_default(); GtkCssProvider* provider = gtk_css_provider_new(); gtk_css_provider_load_from_path( provider, "resources/css/style.css", NULL ); gtk_style_context_add_provider_for_screen( screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION ); } static void _application_activate(UNUSED GtkApplication* application, gpointer user_data) { MESSENGER_Application *app = (MESSENGER_Application*) user_data; ui_messenger_init(app, &(app->ui.messenger)); } void application_init(MESSENGER_Application *app, int argc, char **argv) { memset(app, 0, sizeof(*app)); app->argc = argc; app->argv = argv; gtk_init(&argc, &argv); hdy_init(); app->application = gtk_application_new( "org.gnunet.MESSENGER-GTK", G_APPLICATION_NON_UNIQUE ); notify_init("Messenger-GTK"); _load_ui_stylesheets(); app->chat.status = EXIT_FAILURE; app->chat.tid = 0; app->chat.signal = MESSENGER_NONE; pthread_mutex_init(&(app->chat.mutex), NULL); app->ui.mobile = FALSE; app->ui.bindings = g_hash_table_new(g_direct_hash, g_direct_equal); g_application_add_main_option( G_APPLICATION(app->application), "mobile", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, "Optimize UI spacing for mobile devices", NULL ); g_application_add_main_option( G_APPLICATION(app->application), "ego", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, "Identity to select for messaging", "IDENTITY" ); g_signal_connect( app->application, "activate", G_CALLBACK(_application_activate), app ); } static void* _application_chat_thread(void *args) { MESSENGER_Application *app = (MESSENGER_Application*) args; struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_flag ( 'm', "mobile", "Optimize UI spacing for mobile devices", &(app->ui.mobile) ), GNUNET_GETOPT_option_string ( 'e', "ego", "IDENTITY", "Identity to select for messaging", &(app->chat.identity) ), GNUNET_GETOPT_OPTION_END }; app->chat.status = (GNUNET_PROGRAM_run( app->argc, app->argv, "messenger-gtk", gettext_noop("A GTK based GUI for the Messenger service of GNUnet."), options, &chat_messenger_run, app ) == GNUNET_OK? EXIT_SUCCESS : EXIT_FAILURE); return NULL; } void application_run(MESSENGER_Application *app) { pthread_create(&(app->chat.tid), NULL, _application_chat_thread, app); app->ui.status = g_application_run( G_APPLICATION(app->application), app->argc, app->argv ); pthread_join(app->chat.tid, NULL); g_hash_table_destroy(app->ui.bindings); pthread_mutex_destroy(&(app->chat.mutex)); notify_uninit(); g_object_unref(app->application); } typedef struct MESSENGER_ApplicationEventCall { MESSENGER_Application *app; MESSENGER_ApplicationEvent event; } MESSENGER_ApplicationEventCall; static gboolean _application_event_call(gpointer user_data) { MESSENGER_ApplicationEventCall *call; call = (MESSENGER_ApplicationEventCall*) user_data; call->event(call->app); GNUNET_free(call); return FALSE; } void application_call_event(MESSENGER_Application *app, MESSENGER_ApplicationEvent event) { MESSENGER_ApplicationEventCall *call; call = (MESSENGER_ApplicationEventCall*) GNUNET_malloc( sizeof(MESSENGER_ApplicationEventCall) ); call->app = app; call->event = event; g_idle_add(G_SOURCE_FUNC(_application_event_call), call); } typedef struct MESSENGER_ApplicationMessageEventCall { MESSENGER_Application *app; MESSENGER_ApplicationMessageEvent event; struct GNUNET_CHAT_Context *context; const struct GNUNET_CHAT_Message *message; } MESSENGER_ApplicationMessageEventCall; static gboolean _application_message_event_call(gpointer user_data) { MESSENGER_ApplicationMessageEventCall *call; call = (MESSENGER_ApplicationMessageEventCall*) user_data; pthread_mutex_lock(&(call->app->chat.mutex)); call->event(call->app, call->context, call->message); pthread_mutex_unlock(&(call->app->chat.mutex)); GNUNET_free(call); return FALSE; } void application_call_message_event(MESSENGER_Application *app, MESSENGER_ApplicationMessageEvent event, struct GNUNET_CHAT_Context *context, const struct GNUNET_CHAT_Message *message) { MESSENGER_ApplicationMessageEventCall *call; if (!event) return; call = (MESSENGER_ApplicationMessageEventCall*) GNUNET_malloc( sizeof(MESSENGER_ApplicationMessageEventCall) ); call->app = app; call->event = event; call->context = context; call->message = message; g_idle_add(G_SOURCE_FUNC(_application_message_event_call), call); } void application_exit(MESSENGER_Application *app, MESSENGER_ApplicationSignal signal) { app->chat.signal = signal; } int application_status(MESSENGER_Application *app) { if (EXIT_SUCCESS != app->chat.status) return app->chat.status; return app->ui.status; }