Create a proper GtkApplication subclass for Pidgin and use it.

Mon, 12 Oct 2020 22:10:27 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Mon, 12 Oct 2020 22:10:27 -0500
changeset 40553
892459990bb7
parent 40552
eb0f386cea6b
child 40554
e910eacdf9d9

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/

pidgin/libpidgin.c file | annotate | diff | comparison | revisions
pidgin/meson.build file | annotate | diff | comparison | revisions
pidgin/pidginapplication.c file | annotate | diff | comparison | revisions
pidgin/pidginapplication.h file | annotate | diff | comparison | revisions
pidgin/pidgincore.h file | annotate | diff | comparison | revisions
--- 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 */
 

mercurial