Mon, 12 Oct 2020 22:10:27 -0500
Create a proper GtkApplication subclass for Pidgin and use it.
Testing Done:
Compiled and ran, tested irc and bonjour.
Reviewed at https://reviews.imfreedom.org/r/155/
--- a/pidgin/libpidgin.c Mon Oct 12 21:03:57 2020 -0500 +++ b/pidgin/libpidgin.c Mon Oct 12 22:10:27 2020 -0500 @@ -37,8 +37,6 @@ #include "gtkaccount.h" #include "gtkblist.h" #include "gtkconn.h" -#include "gtkconv.h" -#include "gtkdialogs.h" #include "gtkxfer.h" #include "gtkidle.h" #include "gtkmedia.h" @@ -52,6 +50,7 @@ #include "gtksmiley-theme.h" #include "gtkutils.h" #include "gtkwhiteboard.h" +#include "pidginapplication.h" #include "pidgincore.h" #include "pidgindebug.h" #include "pidginlog.h" @@ -83,34 +82,6 @@ }; #endif /* !_WIN32 */ -static void -dologin_named(const char *name) -{ - PurpleAccount *account; - char **names; - int i; - - if (name != NULL) { /* list of names given */ - names = g_strsplit(name, ",", 64); - for (i = 0; names[i] != NULL; i++) { - account = purple_accounts_find(names[i], NULL); - if (account != NULL) { /* found a user */ - purple_account_set_enabled(account, PIDGIN_UI, TRUE); - } - } - g_strfreev(names); - } else { /* no name given, use the first account */ - GList *accounts; - - accounts = purple_accounts_get_all(); - if (accounts != NULL) - { - account = (PurpleAccount *)accounts->data; - purple_account_set_enabled(account, PIDGIN_UI, TRUE); - } - } -} - #ifndef _WIN32 static char *segfault_message; @@ -184,16 +155,6 @@ } #endif /* !_WIN32 */ -static int -ui_main(void) -{ - pidgin_blist_setup_sort_methods(); - - gtk_window_set_default_icon_name("pidgin"); - - return 0; -} - static void debug_init(void) { @@ -276,98 +237,12 @@ pidgin_ui_get_info, }; -static PurpleCoreUiOps * +PurpleCoreUiOps * pidgin_core_get_ui_ops(void) { return &core_ops; } -static gint -pidgin_handle_local_options_cb(GApplication *app, GVariantDict *options, - gpointer user_data) -{ - if (g_variant_dict_contains(options, "version")) { - printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION, - purple_core_get_version()); - return 0; - } - - return -1; -} - -static void -pidgin_activate_cb(GApplication *application, gpointer user_data) -{ - PidginBuddyList *blist = pidgin_blist_get_default_gtk_blist(); - - if (blist != NULL && blist->window != NULL) { - gtk_window_present(GTK_WINDOW(blist->window)); - } -} - -static gint -pidgin_command_line_cb(GApplication *application, - GApplicationCommandLine *cmdline, gpointer user_data) -{ - gchar **argv; - int argc; - int i; - - argv = g_application_command_line_get_arguments(cmdline, &argc); - - if (argc == 1) { - /* No arguments, just activate */ - g_application_activate(application); - } - - /* Start at 1 to skip the executable name */ - for (i = 1; i < argc; ++i) { - purple_got_protocol_handler_uri(argv[i]); - } - - g_strfreev(argv); - - return 0; -} - -static gchar *opt_config_dir_arg = NULL; -static gboolean opt_nologin = FALSE; -static gboolean opt_login = FALSE; -static gchar *opt_login_arg = NULL; - -static gboolean -login_opt_arg_func(const gchar *option_name, const gchar *value, - gpointer data, GError **error) -{ - opt_login = TRUE; - - g_free(opt_login_arg); - opt_login_arg = g_strdup(value); - - return TRUE; -} - -static GOptionEntry option_entries[] = { - {"config", 'c', 0, - G_OPTION_ARG_FILENAME, &opt_config_dir_arg, - N_("use DIR for config files"), N_("DIR")}, - {"login", 'l', G_OPTION_FLAG_OPTIONAL_ARG, - G_OPTION_ARG_CALLBACK, &login_opt_arg_func, - N_("enable specified account(s) (optional argument NAME\n" - " " - "specifies account(s) to use, separated by commas.\n" - " " - "Without this only the first account will be enabled)"), - N_("[NAME]")}, - {"nologin", 'n', 0, - G_OPTION_ARG_NONE, &opt_nologin, - N_("don't automatically login"), NULL}, - {"version", 'v', 0, - G_OPTION_ARG_NONE, NULL, - N_("display the current version and exit"), NULL}, - {NULL} -}; - #ifndef _WIN32 static void pidgin_setup_error_handler(void) @@ -473,156 +348,6 @@ } #endif /* !_WIN32 */ -static void -pidgin_startup_cb(GApplication *app, gpointer user_data) -{ - char *search_path; - GtkCssProvider *provider; - GdkScreen *screen; - GList *accounts; - gboolean gui_check; - GList *active_accounts; - GStatBuf st; - GError *error = NULL; - - /* set a user-specified config directory */ - if (opt_config_dir_arg != NULL) { - if (g_path_is_absolute(opt_config_dir_arg)) { - purple_util_set_user_dir(opt_config_dir_arg); - } else { - /* Make an absolute (if not canonical) path */ - char *cwd = g_get_current_dir(); - char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL); - purple_util_set_user_dir(path); - g_free(path); - g_free(cwd); - } - } - - search_path = g_build_filename(purple_config_dir(), "gtk-3.0.css", NULL); - - provider = gtk_css_provider_new(); - gui_check = gtk_css_provider_load_from_path(provider, search_path, &error); - - if (gui_check && !error) { - screen = gdk_screen_get_default(); - gtk_style_context_add_provider_for_screen(screen, - GTK_STYLE_PROVIDER(provider), - GTK_STYLE_PROVIDER_PRIORITY_USER); - } else { - purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n", - error ? error->message : "(unknown error)"); - g_clear_error(&error); - } - - g_free(search_path); - -#ifdef _WIN32 - winpidgin_init(); -#endif - - purple_core_set_ui_ops(pidgin_core_get_ui_ops()); - - if (!purple_core_init(PIDGIN_UI)) { - fprintf(stderr, - "Initialization of the libpurple core failed. Dumping core.\n" - "Please report this!\n"); -#ifndef _WIN32 - g_free(segfault_message); -#endif - abort(); - } - - if (!g_getenv("PURPLE_PLUGINS_SKIP")) { - search_path = g_build_filename(purple_data_dir(), - "plugins", NULL); - if (!g_stat(search_path, &st)) - g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR); - purple_plugins_add_search_path(search_path); - g_free(search_path); - - purple_plugins_add_search_path(PIDGIN_LIBDIR); - } else { - purple_debug_info("gtk", - "PURPLE_PLUGINS_SKIP environment variable " - "set, skipping normal Pidgin plugin paths"); - } - - purple_plugins_refresh(); - - /* load plugins we had when we quit */ - purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded"); - - ui_main(); - - g_free(opt_config_dir_arg); - opt_config_dir_arg = NULL; - - /* - * We want to show the blist early in the init process so the - * user feels warm and fuzzy (not cold and prickley). - */ - purple_blist_show(); - - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) - pidgin_debug_window_show(); - - if (opt_login) { - /* disable all accounts */ - for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) { - PurpleAccount *account = accounts->data; - purple_account_set_enabled(account, PIDGIN_UI, FALSE); - } - /* honor the startup status preference */ - if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) - purple_savedstatus_activate(purple_savedstatus_get_startup()); - /* now enable the requested ones */ - dologin_named(opt_login_arg); - g_free(opt_login_arg); - opt_login_arg = NULL; - } else if (opt_nologin) { - /* Set all accounts to "offline" */ - PurpleSavedStatus *saved_status; - - /* If we've used this type+message before, lookup the transient status */ - saved_status = purple_savedstatus_find_transient_by_type_and_message( - PURPLE_STATUS_OFFLINE, NULL); - - /* If this type+message is unique then create a new transient saved status */ - if (saved_status == NULL) - saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE); - - /* Set the status for each account */ - purple_savedstatus_activate(saved_status); - } else { - /* Everything is good to go--sign on already */ - if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) - purple_savedstatus_activate(purple_savedstatus_get_startup()); - purple_accounts_restore_current_statuses(); - } - - if ((active_accounts = purple_accounts_get_all_active()) == NULL) - { - pidgin_accounts_window_show(); - } - else - { - g_list_free(active_accounts); - } - - /* GTK clears the notification for us when opening the first window, - * but we may have launched with only a status icon, so clear the it - * just in case. */ - gdk_notify_startup_complete(); - -#ifdef _WIN32 - winpidgin_post_init(); -#endif - - /* TODO: Use GtkApplicationWindow or add a window instead */ - g_application_hold(app); -} - int pidgin_start(int argc, char *argv[]) { GApplication *app; @@ -643,24 +368,7 @@ pidgin_setup_error_handler(); #endif - app = G_APPLICATION(gtk_application_new("im.pidgin.Pidgin3", - G_APPLICATION_CAN_OVERRIDE_APP_ID | - G_APPLICATION_HANDLES_COMMAND_LINE)); - - g_application_add_main_option_entries(app, option_entries); - g_application_add_option_group(app, purple_get_option_group()); - g_application_add_option_group(app, gplugin_get_option_group()); - - g_object_set(app, "register-session", TRUE, NULL); - - g_signal_connect(app, "handle-local-options", - G_CALLBACK(pidgin_handle_local_options_cb), NULL); - g_signal_connect(app, "startup", - G_CALLBACK(pidgin_startup_cb), NULL); - g_signal_connect(app, "activate", - G_CALLBACK(pidgin_activate_cb), NULL); - g_signal_connect(app, "command-line", - G_CALLBACK(pidgin_command_line_cb), NULL); + app = pidgin_application_new(); ret = g_application_run(app, argc, argv);
--- a/pidgin/meson.build Mon Oct 12 21:03:57 2020 -0500 +++ b/pidgin/meson.build Mon Oct 12 22:10:27 2020 -0500 @@ -40,6 +40,7 @@ 'pidginaccountsmenu.c', 'pidginaccountstore.c', 'pidginactiongroup.c', + 'pidginapplication.c', 'pidginattachment.c', 'pidginbuddylistmenu.c', 'pidginclosebutton.c', @@ -105,6 +106,7 @@ 'pidginaccountsmenu.h', 'pidginaccountstore.h', 'pidginactiongroup.h', + 'pidginapplication.h', 'pidginattachment.h', 'pidginbuddylistmenu.h', 'pidginclosebutton.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginapplication.c Mon Oct 12 22:10:27 2020 -0500 @@ -0,0 +1,290 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> + +#include <gplugin.h> +#include <purple.h> + +#include "pidginapplication.h" + +#include "gtkaccount.h" +#include "gtkblist.h" +#include "pidgincore.h" +#include "pidgindebug.h" + +struct _PidginApplication { + GtkApplication parent; +}; + +/****************************************************************************** + * Globals + *****************************************************************************/ +static gchar *opt_config_dir_arg = NULL; +static gboolean opt_nologin = FALSE; + +static GOptionEntry option_entries[] = { + { + "config", 'c', 0, G_OPTION_ARG_FILENAME, &opt_config_dir_arg, + N_("use DIR for config files"), N_("DIR") + }, { + "nologin", 'n', 0, G_OPTION_ARG_NONE, &opt_nologin, + N_("don't automatically login"), NULL + }, + { + "version", 'v', 0, G_OPTION_ARG_NONE, NULL, + N_("display the current version and exit"), NULL + }, { + NULL + } +}; + +G_DEFINE_TYPE(PidginApplication, pidgin_application, GTK_TYPE_APPLICATION) + +/****************************************************************************** + * GApplication Implementation + *****************************************************************************/ +static void +pidgin_application_startup(GApplication *application) { + GtkCssProvider *provider = NULL; + GError *error = NULL; + GList *active_accounts = NULL; + gchar *search_path = NULL; + + G_APPLICATION_CLASS(pidgin_application_parent_class)->startup(application); + + /* set a user-specified config directory */ + if (opt_config_dir_arg != NULL) { + if (g_path_is_absolute(opt_config_dir_arg)) { + purple_util_set_user_dir(opt_config_dir_arg); + } else { + /* Make an absolute (if not canonical) path */ + gchar *cwd = g_get_current_dir(); + gchar *path = g_build_filename(cwd, opt_config_dir_arg, NULL); + + purple_util_set_user_dir(path); + + g_free(cwd); + g_free(path); + } + } + + provider = gtk_css_provider_new(); + + search_path = g_build_filename(purple_config_dir(), "gtk-3.0.css", NULL); + gtk_css_provider_load_from_path(provider, search_path, &error); + if(error != NULL) { + purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n", + error->message); + g_clear_error(&error); + } else { + GdkScreen *screen = gdk_screen_get_default(); + gtk_style_context_add_provider_for_screen(screen, + GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + } + + g_free(search_path); + +#ifdef _WIN32 + winpidgin_init(); +#endif + + purple_core_set_ui_ops(pidgin_core_get_ui_ops()); + + if(!purple_core_init(PIDGIN_UI)) { + fprintf(stderr, + _("Initialization of the libpurple core failed. Aborting!\n" + "Please report this!\n")); + g_abort(); + } + + if(g_getenv("PURPLE_PLUGINS_SKIP")) { + purple_debug_info("gtk", + "PURPLE_PLUGINS_SKIP environment variable " + "set, skipping normal Pidgin plugin paths"); + } else { + search_path = g_build_filename(purple_data_dir(), "plugins", NULL); + + if(!g_file_test(search_path, G_FILE_TEST_IS_DIR)) { + g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR); + } + + purple_plugins_add_search_path(search_path); + g_free(search_path); + + purple_plugins_add_search_path(PIDGIN_LIBDIR); + } + + purple_plugins_refresh(); + + /* load plugins we had when we quit */ + purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded"); + + /* gk 20201008: this needs to be moved to the buddy list initialization. */ + pidgin_blist_setup_sort_methods(); + + gtk_window_set_default_icon_name("pidgin"); + + g_free(opt_config_dir_arg); + opt_config_dir_arg = NULL; + + /* + * We want to show the blist early in the init process so the + * user feels warm and fuzzy. + */ + purple_blist_show(); + + if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) { + pidgin_debug_window_show(); + } + + if(opt_nologin) { + /* Set all accounts to "offline" */ + PurpleSavedStatus *saved_status; + + /* If we've used this type+message before, lookup the transient status */ + saved_status = purple_savedstatus_find_transient_by_type_and_message( + PURPLE_STATUS_OFFLINE, NULL); + + /* If this type+message is unique then create a new transient saved status */ + if(saved_status == NULL) { + saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE); + } + + /* Set the status for each account */ + purple_savedstatus_activate(saved_status); + } else { + /* Everything is good to go--sign on already */ + if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) { + purple_savedstatus_activate(purple_savedstatus_get_startup()); + } + + purple_accounts_restore_current_statuses(); + } + + if((active_accounts = purple_accounts_get_all_active()) == NULL) { + pidgin_accounts_window_show(); + } else { + g_list_free(active_accounts); + } + + /* GTK clears the notification for us when opening the first window, but we + * may have launched with only a status icon, so clear it just in case. + */ + gdk_notify_startup_complete(); + +#ifdef _WIN32 + winpidgin_post_init(); +#endif + + /* TODO: Use GtkApplicationWindow or add a window instead */ + g_application_hold(application); +} + +static void +pidgin_application_activate(GApplication *application) { + PidginBuddyList *blist = pidgin_blist_get_default_gtk_blist(); + + if(blist != NULL && blist->window != NULL) { + gtk_window_present(GTK_WINDOW(blist->window)); + } +} + +static gint +pidgin_application_command_line(GApplication *application, + GApplicationCommandLine *cmdline) +{ + gchar **argv = NULL; + gint argc = 0, i = 0; + + argv = g_application_command_line_get_arguments(cmdline, &argc); + + if(argc == 1) { + /* No arguments, just activate */ + g_application_activate(application); + } + + /* Start at 1 to skip the executable name */ + for (i = 1; i < argc; i++) { + purple_got_protocol_handler_uri(argv[i]); + } + + g_strfreev(argv); + + return 0; +} + +static gint +pidgin_application_handle_local_options(GApplication *application, + GVariantDict *options) +{ + if (g_variant_dict_contains(options, "version")) { + printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION, + purple_core_get_version()); + + return 0; + } + + return -1; +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +static void +pidgin_application_init(PidginApplication *application) { + GApplication *gapp = G_APPLICATION(application); + + g_application_add_main_option_entries(gapp, option_entries); + g_application_add_option_group(gapp, purple_get_option_group()); + g_application_add_option_group(gapp, gplugin_get_option_group()); +} + +static void +pidgin_application_class_init(PidginApplicationClass *klass) { + GApplicationClass *app_class = G_APPLICATION_CLASS(klass); + + app_class->startup = pidgin_application_startup; + app_class->activate = pidgin_application_activate; + app_class->command_line = pidgin_application_command_line; + app_class->handle_local_options = pidgin_application_handle_local_options; +} + +/****************************************************************************** + * Public API + *****************************************************************************/ +GApplication * +pidgin_application_new(void) { + return g_object_new( + PIDGIN_TYPE_APPLICATION, + "flags", G_APPLICATION_CAN_OVERRIDE_APP_ID | + G_APPLICATION_HANDLES_COMMAND_LINE, + "register-session", TRUE, + NULL); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginapplication.h Mon Oct 12 22:10:27 2020 -0500 @@ -0,0 +1,62 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#endif + +#ifndef PIDGIN_APPLICATION_H +#define PIDGIN_APPLICATION_H + +/** + * SECTION:pidginapplication + * @section_id: pidgin-application + * @short_description: A #GtkApplication subclass for Pidgin. + * @title: Pidgin Application + * + * #PidginApplication is a subclass of #GtkApplication that holds all of the + * application wide actions. + */ + +#include <glib.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define PIDGIN_TYPE_APPLICATION (pidgin_application_get_type()) +G_DECLARE_FINAL_TYPE(PidginApplication, pidgin_application, PIDGIN, + APPLICATION, GtkApplication) + +/** + * pidgin_application_new: + * + * Creates a new #PidginApplication instance. + * + * Returns: (transfer full): The new #PidginApplication instance. + * + * Since: 3.0.0 + */ +GApplication *pidgin_application_new(void); + +G_END_DECLS + +#endif /* PIDGIN_APPLICATION_H */
--- a/pidgin/pidgincore.h Mon Oct 12 21:03:57 2020 -0500 +++ b/pidgin/pidgincore.h Mon Oct 12 22:10:27 2020 -0500 @@ -34,6 +34,8 @@ #include <glib.h> +#include <purple.h> + #ifdef _WIN32 # include "win32/gtkwin32dep.h" #endif @@ -66,5 +68,17 @@ */ int pidgin_start(int argc, char *argv[]); +/** + * pidgin_core_get_ui_ops: + * + * Gets the #PurpleCoreUiOps that Pidgin sets up. You probably don't want to + * call this. + * + * Returns: The #PurpleCoreUiOps for Pidgin. + * + * Since: 3.0.0 + */ +PurpleCoreUiOps *pidgin_core_get_ui_ops(void); + #endif /* PIDGIN_CORE_H */