diff options
Diffstat (limited to '')
-rw-r--r-- | src/terminal-app.cc | 1671 |
1 files changed, 1671 insertions, 0 deletions
diff --git a/src/terminal-app.cc b/src/terminal-app.cc new file mode 100644 index 0000000..b6740e3 --- /dev/null +++ b/src/terminal-app.cc @@ -0,0 +1,1671 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008, 2010, 2011, 2015, 2017, 2022 Christian Persch + * + * 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 3 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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#define G_SETTINGS_ENABLE_BACKEND +#include <gio/gsettingsbackend.h> + +#include "terminal-intl.hh" +#include "terminal-debug.hh" +#include "terminal-app.hh" +#include "terminal-accels.hh" +#include "terminal-client-utils.hh" +#include "terminal-profiles-list.hh" +#include "terminal-util.hh" +#include "terminal-schemas.hh" +#include "terminal-settings-utils.hh" +#include "terminal-defines.hh" +#include "terminal-libgsystem.hh" + +#ifdef TERMINAL_SERVER +#include "terminal-gdbus.hh" +#include "terminal-prefs-process.hh" +#include "terminal-screen-container.hh" +#include "terminal-screen.hh" +#include "terminal-window.hh" +#endif + +#ifdef TERMINAL_PREFERENCES +#include "terminal-prefs.hh" +#endif + +#ifndef TERMINAL_SERVER +#undef ENABLE_SEARCH_PROVIDER +#endif + +#ifdef ENABLE_SEARCH_PROVIDER +#include "terminal-search-provider.hh" +#endif /* ENABLE_SEARCH_PROVIDER */ + +#include <sys/wait.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +#define GNOME_TERMINAL_PREFERENCES_ICON_NAME "org.gnome.Terminal.Preferences" + +#define DESKTOP_INTERFACE_SETTINGS_SCHEMA "org.gnome.desktop.interface" + +#define SYSTEM_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy" +#define SYSTEM_HTTP_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy.http" +#define SYSTEM_HTTPS_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy.https" +#define SYSTEM_FTP_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy.ftp" +#define SYSTEM_SOCKS_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy.socks" + +#define GTK_SETTING_PREFER_DARK_THEME "gtk-application-prefer-dark-theme" + +#define GTK_DEBUG_SETTING_SCHEMA "org.gtk.Settings.Debug" + +#ifdef DISUNIFY_NEW_TERMINAL_SECTION +#error Use a gsettings override instead +#endif + +enum { + PROP_SETTINGS_BACKEND = 1, + PROP_IS_DEFAULT_TERMINAL, + PROP_ASK_DEFAULT_TERMINAL, +}; + +/* + * Session state is stored entirely in the RestartCommand command line. + * + * The number one rule: all stored information is EITHER per-session, + * per-profile, or set from a command line option. THERE CAN BE NO + * OVERLAP. The UI and implementation totally break if you overlap + * these categories. See gnome-terminal 1.x for why. + */ + +struct _TerminalAppClass { + GtkApplicationClass parent_class; + + void (* clipboard_targets_changed) (TerminalApp *app, + GtkClipboard *clipboard); +}; + +struct _TerminalApp +{ + GtkApplication parent_instance; + + TerminalSettingsList *profiles_list; + + GSettingsBackend* settings_backend; + GSettingsSchemaSource* schema_source; + GSettings *global_settings; + GSettings *desktop_interface_settings; + GSettings *system_proxy_settings; + GSettings* system_proxy_protocol_settings[4]; + GSettings *gtk_debug_settings; + +#ifdef TERMINAL_SERVER + GDBusObjectManagerServer *object_manager; + GHashTable *screen_map; + +#ifdef ENABLE_SEARCH_PROVIDER + TerminalSearchProvider *search_provider; +#endif /* ENABLE_SEARCH_PROVIDER */ + + GMenuModel *menubar; + GMenu *menubar_new_terminal_section; + GMenu *menubar_set_profile_section; + + GMenuModel *profilemenu; + GMenuModel *headermenu; + GMenu *headermenu_set_profile_section; + + GMenu *set_profile_menu; + + GtkClipboard *clipboard; + GdkAtom *clipboard_targets; + int n_clipboard_targets; + + GWeakRef prefs_process_ref; + +#endif /* TERMINAL_SERVER */ + + gboolean ask_default; + gboolean xte_is_default; + gboolean unified_menu; + gboolean use_headerbar; +}; + +enum +{ + CLIPBOARD_TARGETS_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +/* Debugging helper */ + +static void +terminal_app_init_debug (void) +{ +#ifdef ENABLE_DEBUG + const char *env = g_getenv ("GTK_TEXT_DIR"); + if (env != nullptr) { + if (g_str_equal (env, "help")) { + g_printerr ("Usage: GTK_TEXT_DIR=ltr|rtl\n"); + } else { + GtkTextDirection dir; + if (g_str_equal (env, "rtl")) + dir = GTK_TEXT_DIR_RTL; + else + dir = GTK_TEXT_DIR_LTR; + + gtk_widget_set_default_direction (dir); + } + } + + env = g_getenv ("GTK_SETTINGS"); + if (env == nullptr) + return; + + GObject *settings = G_OBJECT (gtk_settings_get_default ()); + GObjectClass *settings_class = G_OBJECT_GET_CLASS (settings); + + if (g_str_equal (env, "help")) { + g_printerr ("Usage: GTK_SETTINGS=setting[,setting…] where 'setting' is one of these:\n"); + + guint n_props; + GParamSpec **props = g_object_class_list_properties (settings_class, &n_props); + for (guint i = 0; i < n_props; i++) { + if (G_PARAM_SPEC_VALUE_TYPE (props[i]) != G_TYPE_BOOLEAN) + continue; + + GValue value = { 0, }; + g_value_init (&value, G_TYPE_BOOLEAN); + g_object_get_property (settings, props[i]->name, &value); + g_printerr (" %s (%s)\n", props[i]->name, g_value_get_boolean (&value) ? "true" : "false"); + g_value_unset (&value); + } + g_printerr (" Use 'setting' to set to true, " + "'~setting' to set to false, " + "and '!setting' to invert.\n"); + } else { + gs_strfreev char **tokens = g_strsplit (env, ",", -1); + for (guint i = 0; tokens[i] != nullptr; i++) { + const char *prop = tokens[i]; + char c = prop[0]; + if (c == '~' || c == '!') + prop++; + + GParamSpec *pspec = g_object_class_find_property (settings_class, prop); + if (pspec == nullptr) { + g_printerr ("Setting \"%s\" does not exist.\n", prop); + } else if (G_PARAM_SPEC_VALUE_TYPE (pspec) != G_TYPE_BOOLEAN) { + g_printerr ("Setting \"%s\" is not boolean.\n", prop); + } else { + GValue value = { 0, }; + g_value_init (&value, G_TYPE_BOOLEAN); + if (c == '!') { + g_object_get_property (settings, pspec->name, &value); + g_value_set_boolean (&value, !g_value_get_boolean (&value)); + } else if (c == '~') { + g_value_set_boolean (&value, FALSE); + } else { + g_value_set_boolean (&value, TRUE); + } + g_object_set_property (settings, pspec->name, &value); + g_value_unset (&value); + } + } + } +#endif +} + +/* Helper functions */ + +static gboolean +strv_contains_gnome (char **strv) +{ + if (strv == nullptr) + return FALSE; + + for (int i = 0; strv[i] != nullptr; i++) { + if (g_ascii_strcasecmp (strv[i], "gnome") == 0 || + g_ascii_strcasecmp (strv[i], "gnome-classic") == 0) + return TRUE; + } + + return FALSE; +} + +/* + * terminal_app_should_use_headerbar: + * + * Determines if the app should use headerbars. This is determined + * * If the pref is set, the pref value is used + * * Otherwise, if XDG_CURRENT_DESKTOP contains GNOME or GNOME-Classic, + * headerbar is used + * * Otherwise, headerbar is not used. + */ +static gboolean +terminal_app_should_use_headerbar (TerminalApp *app) +{ + gboolean set, use; + g_settings_get (app->global_settings, TERMINAL_SETTING_HEADERBAR_KEY, "mb", &set, &use); + if (set) + return use; + + gs_strfreev auto desktops = terminal_util_get_desktops(); + return strv_contains_gnome(desktops); +} + +static gboolean +load_css_from_resource (GApplication *application, + GtkCssProvider *provider, + gboolean theme) +{ + const char *base_path; + gs_free char *uri; + gs_unref_object GFile *file; + gs_free_error GError *error = nullptr; + + base_path = g_application_get_resource_base_path (application); + + if (theme) { + gs_free char *str, *theme_name; + + g_object_get (gtk_settings_get_default (), "gtk-theme-name", &str, nullptr); + theme_name = g_ascii_strdown (str, -1); + uri = g_strdup_printf ("resource://%s/css/%s/terminal.css", base_path, theme_name); + } else { + uri = g_strdup_printf ("resource://%s/css/terminal.css", base_path); + } + + file = g_file_new_for_uri (uri); + if (!g_file_query_exists (file, nullptr /* cancellable */)) + return FALSE; + + if (!gtk_css_provider_load_from_file (provider, file, &error)) + g_assert_no_error (error); + + return TRUE; +} + +static void +add_css_provider (GApplication *application, + gboolean theme) +{ + gs_unref_object GtkCssProvider *provider; + + provider = gtk_css_provider_new (); + if (!load_css_from_resource (application, provider, theme)) + return; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +} + +static void +app_load_css (GApplication *application) +{ + add_css_provider (application, FALSE); + add_css_provider (application, TRUE); +} + +char * +terminal_app_new_profile (TerminalApp *app, + GSettings *base_profile, + const char *name) +{ + char *uuid; + + if (base_profile) { + gs_free char *base_uuid; + + base_uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, base_profile); + uuid = terminal_settings_list_clone_child (app->profiles_list, base_uuid, name); + } else { + uuid = terminal_settings_list_add_child (app->profiles_list, name); + } + + return uuid; +} + +void +terminal_app_remove_profile (TerminalApp *app, + GSettings *profile) +{ + g_return_if_fail (TERMINAL_IS_APP (app)); + g_return_if_fail (G_IS_SETTINGS (profile)); + + gs_unref_object GSettings *default_profile = terminal_settings_list_ref_default_child (app->profiles_list); + if (default_profile == profile) + return; + +#ifdef TERMINAL_SERVER + /* First, we need to switch any screen using this profile to the default profile */ + gs_free_list GList *screens = g_hash_table_get_values (app->screen_map); + for (GList *l = screens; l != nullptr; l = l->next) { + TerminalScreen *screen = TERMINAL_SCREEN (l->data); + if (terminal_screen_get_profile (screen) != profile) + continue; + + terminal_screen_set_profile (screen, default_profile); + } +#endif /* TERMINAL_SERVER */ + + /* Now we can safely remove the profile */ + gs_free char *uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, profile); + terminal_settings_list_remove_child (app->profiles_list, uuid); +} + +static void +terminal_app_theme_variant_changed_cb (GSettings *settings, + const char *key, + GtkSettings *gtk_settings) +{ + TerminalThemeVariant theme; + + theme = TerminalThemeVariant(g_settings_get_enum (settings, key)); + if (theme == TERMINAL_THEME_VARIANT_SYSTEM) + gtk_settings_reset_property (gtk_settings, GTK_SETTING_PREFER_DARK_THEME); + else + g_object_set (gtk_settings, + GTK_SETTING_PREFER_DARK_THEME, + theme == TERMINAL_THEME_VARIANT_DARK, + nullptr); +} + +/* Submenus for New Terminal per profile, and to change profiles */ + +static void +terminal_app_check_default(TerminalApp* app) +{ +#ifdef TERMINAL_SERVER + // Only do this for the default app ID + gs_free char* app_id = nullptr; + g_object_get(app, "application-id", &app_id, nullptr); + if (!_terminal_debug_on(TERMINAL_DEBUG_DEFAULT) && + !g_str_equal(app_id, TERMINAL_APPLICATION_ID)) + return; +#endif /* TERMINAL_SERVER */ + + // Check whether gnome-terminal is the default terminal + // as per XDG-Terminal-Exec. + app->xte_is_default = terminal_util_is_default_terminal(); + + gboolean ask = false; + g_settings_get(app->global_settings, TERMINAL_SETTING_ALWAYS_CHECK_DEFAULT_KEY, "b", &ask); + app->ask_default = (ask != false) && !app->xte_is_default; +} + +#ifdef TERMINAL_SERVER + +static void terminal_app_update_profile_menus (TerminalApp *app); + +typedef struct { + char *uuid; + char *label; +} ProfileData; + +static void +profile_data_clear (ProfileData *data) +{ + g_free (data->uuid); + g_free (data->label); +} + +typedef struct { + GArray *array; + TerminalApp *app; +} ProfilesForeachData; + +static void +foreach_profile_cb (TerminalSettingsList *list, + const char *uuid, + GSettings *profile, + ProfilesForeachData *user_data) +{ + ProfileData data; + data.uuid = g_strdup (uuid); + data.label = g_settings_get_string (profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY); + + g_array_append_val (user_data->array, data); + + /* only connect if we haven't seen this profile before */ + if (g_signal_handler_find (profile, + GSignalMatchType(G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), + 0, 0, nullptr, + (void*)terminal_app_update_profile_menus, user_data->app) == 0) + g_signal_connect_swapped (profile, "changed::" TERMINAL_PROFILE_VISIBLE_NAME_KEY, + G_CALLBACK (terminal_app_update_profile_menus), user_data->app); +} + +static int +compare_profile_label_cb (gconstpointer ap, + gconstpointer bp) +{ + const ProfileData *a = (ProfileData const*)ap; + const ProfileData *b = (ProfileData const*)bp; + + return g_utf8_collate (a->label, b->label); +} + +static void +menu_append_numbered (GMenu *menu, + const char *label, + int num, + const char *action_name, + GVariant *target /* floating, consumed */) +{ + gs_free_gstring GString *str; + gs_unref_object GMenuItem *item; + const char *p; + + /* Who'd use more that 4 underscores in a profile name... */ + str = g_string_sized_new (strlen (label) + 4 + 1 + 8); + + if (num < 10) + g_string_append_printf (str, "_%Id. ", num); + else if (num < 36) + g_string_append_printf (str, "_%c. ", (char)('A' + num - 10)); + + /* Append the label with underscores elided */ + for (p = label; *p; p++) { + if (*p == '_') + g_string_append (str, "__"); + else + g_string_append_c (str, *p); + } + + item = g_menu_item_new (str->str, nullptr); + g_menu_item_set_action_and_target_value (item, action_name, target); + g_menu_append_item (menu, item); +} + +static void +append_new_terminal_item (GMenu *section, + const char *label, + const char *target, + ProfileData *data, + guint n_profiles) +{ + gs_unref_object GMenuItem *item = g_menu_item_new (label, nullptr); + + if (n_profiles > 1) { + gs_unref_object GMenu *submenu = g_menu_new (); + + for (guint i = 0; i < n_profiles; i++) { + menu_append_numbered (submenu, data[i].label, i + 1, + "win.new-terminal", + g_variant_new ("(ss)", target, data[i].uuid)); + } + + g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu)); + } else { + g_menu_item_set_action_and_target (item, "win.new-terminal", + "(ss)", target, "current"); + } + g_menu_append_item (section, item); +} + +static void +fill_header_new_terminal_menu (GMenuModel *menu, + ProfileData *data, + guint n_profiles) +{ + gs_unref_object GMenu *section = nullptr; + + if (n_profiles <= 1) + return; + + section = g_menu_new (); + + for (guint i = 0; i < n_profiles; i++) { + menu_append_numbered (section, data[i].label, i + 1, + "win.new-terminal", + g_variant_new ("(ss)", "default", data[i].uuid)); + } + + g_menu_append_section (G_MENU (menu), _("New Terminal"), G_MENU_MODEL (section)); +} + +static void +fill_new_terminal_section (TerminalApp *app, + GMenu *section, + ProfileData *profiles, + guint n_profiles) +{ + if (terminal_app_get_menu_unified (app)) { + append_new_terminal_item (section, _("New _Terminal"), "default", profiles, n_profiles); + } else { + append_new_terminal_item (section, _("New _Tab"), "tab", profiles, n_profiles); + append_new_terminal_item (section, _("New _Window"), "window", profiles, n_profiles); + } +} + +static GMenu * +set_profile_submenu_new (ProfileData *data, + guint n_profiles) +{ + /* No submenu if there's only one profile */ + if (n_profiles <= 1) + return nullptr; + + GMenu *menu = g_menu_new (); + for (guint i = 0; i < n_profiles; i++) { + menu_append_numbered (menu, data[i].label, i + 1, + "win.profile", + g_variant_new_string (data[i].uuid)); + } + + return menu; +} + +static void +terminal_app_update_profile_menus (TerminalApp *app) +{ + g_clear_object (&app->set_profile_menu); + + /* Get profiles list and sort by label */ + gs_unref_array GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (ProfileData), + terminal_settings_list_get_n_children (app->profiles_list)); + g_array_set_clear_func (array, (GDestroyNotify) profile_data_clear); + + ProfilesForeachData data = { array, app }; + terminal_settings_list_foreach_child (app->profiles_list, + (TerminalSettingsListForeachFunc) foreach_profile_cb, + &data); + g_array_sort (array, compare_profile_label_cb); + + ProfileData *profiles = (ProfileData*) array->data; + guint n_profiles = array->len; + + app->set_profile_menu = set_profile_submenu_new (profiles, n_profiles); + + if (app->menubar != nullptr) { + g_menu_remove_all (G_MENU (app->menubar_new_terminal_section)); + fill_new_terminal_section (app, app->menubar_new_terminal_section, profiles, n_profiles); + + g_menu_remove_all (G_MENU (app->menubar_set_profile_section)); + if (app->set_profile_menu != nullptr) { + g_menu_append_submenu (app->menubar_set_profile_section, _("Change _Profile"), + G_MENU_MODEL (app->set_profile_menu)); + } + } + + if (app->profilemenu != nullptr) { + g_menu_remove_all (G_MENU (app->profilemenu)); + fill_header_new_terminal_menu (app->profilemenu, profiles, n_profiles); + } + + if (app->headermenu != nullptr) { + g_menu_remove_all (G_MENU (app->headermenu_set_profile_section)); + if (app->set_profile_menu != nullptr) { + g_menu_append_submenu (app->headermenu_set_profile_section, _("_Profile"), + G_MENU_MODEL (app->set_profile_menu)); + } + } +} + +static GMenuModel * +terminal_app_create_menubar (TerminalApp *app, + gboolean shell_shows_menubar) +{ + /* If the menubar is shown by the shell, omit mnemonics for the submenus. This is because Alt+F etc. + * are more important to be usable in the terminal, the menu cannot be replaced runtime (to toggle + * between mnemonic and non-mnemonic versions), gtk-enable-mnemonics or gtk_window_set_mnemonic_modifier() + * don't effect the menubar either, so there wouldn't be a way to disable Alt+F for File etc. otherwise. + * Furthermore, the menu would even grab mnemonics from the File and Preferences windows. + * In Unity, Alt+F10 opens the menubar, this should be good enough for keyboard navigation. + * If the menubar is shown by the app, toggling mnemonics is handled in terminal-window.c using + * gtk_window_set_mnemonic_modifier(). + * See bug 792978 for details. */ + terminal_util_load_objects_resource (shell_shows_menubar ? "/org/gnome/terminal/ui/menubar-without-mnemonics.ui" + : "/org/gnome/terminal/ui/menubar-with-mnemonics.ui", + "menubar", &app->menubar, + "new-terminal-section", &app->menubar_new_terminal_section, + "set-profile-section", &app->menubar_set_profile_section, + nullptr); + + /* Install profile sections */ + terminal_app_update_profile_menus (app); + + return app->menubar; +} + +static void +terminal_app_create_headermenu (TerminalApp *app) +{ + terminal_util_load_objects_resource ("/org/gnome/terminal/ui/headerbar-menu.ui", + "headermenu", &app->headermenu, + "set-profile-section", &app->headermenu_set_profile_section, + nullptr); + + /* Install profile sections */ + terminal_app_update_profile_menus (app); +} + +static void +terminal_app_create_profilemenu (TerminalApp *app) +{ + app->profilemenu = G_MENU_MODEL (g_menu_new ()); + + /* Install profile sections */ + terminal_app_update_profile_menus (app); +} + +/* Clipboard */ + +static void +free_clipboard_targets (TerminalApp *app) +{ + g_free (app->clipboard_targets); + app->clipboard_targets = nullptr; + app->n_clipboard_targets = 0; +} + +static void +update_clipboard_targets (TerminalApp *app, + GdkAtom *targets, + int n_targets) +{ + free_clipboard_targets (app); + + /* Sometimes we receive targets == nullptr but n_targets == -1 */ + if (targets != nullptr && n_targets < 255) { + app->clipboard_targets = reinterpret_cast<GdkAtom*> + (g_memdup (targets, sizeof (targets[0]) * n_targets)); + app->n_clipboard_targets = n_targets; + } +} + +static void +clipboard_targets_received_cb (GtkClipboard *clipboard, + GdkAtom *targets, + int n_targets, + TerminalApp *app) +{ + update_clipboard_targets (app, targets, n_targets); + + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_CLIPBOARD) { + g_printerr ("Clipboard has %d targets:", app->n_clipboard_targets); + + int i; + for (i = 0; i < app->n_clipboard_targets; i++) { + gs_free char *atom_name = gdk_atom_name (app->clipboard_targets[i]); + g_printerr (" %s", atom_name); + } + g_printerr ("\n"); + } + + g_signal_emit (app, signals[CLIPBOARD_TARGETS_CHANGED], 0, clipboard); +} + +static void +clipboard_owner_change_cb (GtkClipboard *clipboard, + GdkEvent *event G_GNUC_UNUSED, + TerminalApp *app) +{ + _terminal_debug_print (TERMINAL_DEBUG_CLIPBOARD, + "Clipboard owner changed\n"); + + clipboard_targets_received_cb (clipboard, nullptr, 0, app); /* clear */ + + /* We can do this without holding a reference to @app since + * the app lives as long as the process. + */ + gtk_clipboard_request_targets (clipboard, + (GtkClipboardTargetsReceivedFunc) clipboard_targets_received_cb, + app); +} + +/* Preferences */ + +struct PrefsLaunchData { + GWeakRef app_ref; + char* profile_uuid; + char* hint; + unsigned timestamp; +}; + +static auto +prefs_launch_data_new(TerminalApp* app, + char const* profile_uuid, + char const* hint, + unsigned timestamp) +{ + auto data = g_new(PrefsLaunchData, 1); + g_weak_ref_init(&data->app_ref, app); + data->profile_uuid = g_strdup(profile_uuid); + data->hint = g_strdup(hint); + data->timestamp = timestamp; + + return data; +} + +static void +prefs_launch_data_free(PrefsLaunchData* data) +{ + g_weak_ref_clear(&data->app_ref); + g_free(data->profile_uuid); + g_free(data->hint); + g_free(data); +} + +static void +launch_prefs_cb(GObject* source, + GAsyncResult* result, + void* user_data) +{ + auto const data = reinterpret_cast<PrefsLaunchData*>(user_data); + auto const app = reinterpret_cast<TerminalApp*>(g_weak_ref_get(&data->app_ref)); + + // @process holds a ref on itself via the g_subprocess_wait_async() call, + // so we only keep a weak ref that gets cleared when the process exits. + gs_free_error GError* error = nullptr; + gs_unref_object auto process = terminal_prefs_process_new_finish(result, &error); + if (app) + g_weak_ref_init(&app->prefs_process_ref, process); + + if (process) { + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Preferences process launched successfully.\n"); + + terminal_prefs_process_show(process, + data->profile_uuid, + data->hint, + data->timestamp); + } else { + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Failed to launch preferences process: %s\n", error->message); + } + + prefs_launch_data_free(data); +} + +/* Callbacks from former app menu. + * The preferences one is still used with the "--preferences" cmdline option. */ + +static void +app_menu_preferences_cb (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + TerminalApp *app = (TerminalApp*)user_data; + + terminal_app_edit_preferences (app, nullptr, nullptr, gtk_get_current_event_time()); +} + +static void +app_menu_help_cb (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + terminal_util_show_help (nullptr); +} + +static void +app_menu_about_cb (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + terminal_util_show_about (); +} + +static void +app_menu_quit_cb (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GtkApplication *application = (GtkApplication*)user_data; + GtkWindow *window; + + window = gtk_application_get_active_window (application); + if (TERMINAL_IS_WINDOW (window)) + terminal_window_request_close (TERMINAL_WINDOW (window)); + else /* a dialogue */ + gtk_widget_destroy (GTK_WIDGET (window)); +} + +#endif /* TERMINAL_SERVER */ + +/* Class implementation */ + +G_DEFINE_TYPE (TerminalApp, terminal_app, GTK_TYPE_APPLICATION) + +/* GApplicationClass impl */ + +static void +terminal_app_activate (GApplication *application) +{ + /* No-op required because GApplication is stupid */ +} + +static void +terminal_app_startup (GApplication *application) +{ + auto const app = TERMINAL_APP(application); + + g_application_set_resource_base_path (application, TERMINAL_RESOURCES_PATH_PREFIX); + + G_APPLICATION_CLASS (terminal_app_parent_class)->startup (application); + + /* Need to set the WM class (bug #685742) */ +#if defined(TERMINAL_SERVER) + gdk_set_program_class("Gnome-terminal"); +#elif defined(TERMINAL_PREFERENCES) + gdk_set_program_class("Gnome-terminal-preferences"); +#else +#error +#endif + + app_load_css (application); + +#ifdef TERMINAL_SERVER + GActionEntry const action_entries[] = { + { "preferences", app_menu_preferences_cb, nullptr, nullptr, nullptr }, + { "help", app_menu_help_cb, nullptr, nullptr, nullptr }, + { "about", app_menu_about_cb, nullptr, nullptr, nullptr }, + { "quit", app_menu_quit_cb, nullptr, nullptr, nullptr } + }; + + g_action_map_add_action_entries (G_ACTION_MAP (application), + action_entries, G_N_ELEMENTS (action_entries), + application); + + /* Figure out whether the shell shows the menubar */ + gboolean shell_shows_menubar; + g_object_get (gtk_settings_get_default (), + "gtk-shell-shows-menubar", &shell_shows_menubar, + nullptr); + + /* Create menubar */ + terminal_app_create_menubar (app, shell_shows_menubar); + + /* Keep dynamic menus updated */ + g_signal_connect_swapped (app->profiles_list, "children-changed", + G_CALLBACK (terminal_app_update_profile_menus), app); + + /* Show/hide the menubar as appropriate: If the shell wants to show the menubar, make it available. */ + if (shell_shows_menubar) + gtk_application_set_menubar (GTK_APPLICATION (app), + terminal_app_get_menubar (app)); + +#endif /* TERMINAL_SERVER */ + + terminal_app_check_default(app); + + _terminal_debug_print (TERMINAL_DEBUG_SERVER, "Startup complete\n"); +} + +/* GObjectClass impl */ + +static void +terminal_app_init (TerminalApp* app) +{ +#ifdef TERMINAL_SERVER + g_weak_ref_init(&app->prefs_process_ref, nullptr); + + app->screen_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, nullptr); +#endif +} + +static void +terminal_app_constructed(GObject *object) +{ + auto app = TERMINAL_APP(object); + + G_OBJECT_CLASS(terminal_app_parent_class)->constructed(object); + + terminal_app_init_debug (); + +#if defined(TERMINAL_SERVER) + gtk_window_set_default_icon_name (GNOME_TERMINAL_ICON_NAME); +#elif defined(TERMINAL_PREFERENCES) + gtk_window_set_default_icon_name(GNOME_TERMINAL_PREFERENCES_ICON_NAME); +#else +#error +#endif + + if (app->settings_backend == nullptr) + app->settings_backend = g_settings_backend_get_default (); + + app->schema_source = terminal_g_settings_schema_source_get_default(); + + /* Desktop proxy settings */ + app->system_proxy_settings = terminal_g_settings_new(app->settings_backend, + app->schema_source, + SYSTEM_PROXY_SETTINGS_SCHEMA); + + /* Since there is no way to get the schema ID of a child schema, we cannot + * verify that the installed schemas are correct. Also, due to a glib bug + * (https://gitlab.gnome.org/GNOME/glib/-/issues/1884) g_settings_get_child() + * doesn't work with non-default schema sources. + * So instead of using g_settings_get_child() on the SYSTEM_PROXY_SETTINGS_SCHEMA, + * we construct the child GSettings directly. + */ + app->system_proxy_protocol_settings[TERMINAL_PROXY_HTTP] = + terminal_g_settings_new(app->settings_backend, + app->schema_source, + SYSTEM_HTTP_PROXY_SETTINGS_SCHEMA); + app->system_proxy_protocol_settings[TERMINAL_PROXY_HTTPS] = + terminal_g_settings_new(app->settings_backend, + app->schema_source, + SYSTEM_HTTPS_PROXY_SETTINGS_SCHEMA); + app->system_proxy_protocol_settings[TERMINAL_PROXY_FTP] = + terminal_g_settings_new(app->settings_backend, + app->schema_source, + SYSTEM_FTP_PROXY_SETTINGS_SCHEMA); + app->system_proxy_protocol_settings[TERMINAL_PROXY_SOCKS] = + terminal_g_settings_new(app->settings_backend, + app->schema_source, + SYSTEM_SOCKS_PROXY_SETTINGS_SCHEMA); + + /* Desktop Interface settings */ + app->desktop_interface_settings = terminal_g_settings_new(app->settings_backend, + app->schema_source, + DESKTOP_INTERFACE_SETTINGS_SCHEMA); + + /* Terminal global settings */ + app->global_settings = terminal_g_settings_new(app->settings_backend, + app->schema_source, + TERMINAL_SETTING_SCHEMA); + + /* Gtk debug settings */ + app->gtk_debug_settings = terminal_g_settings_new(app->settings_backend, + app->schema_source, + GTK_DEBUG_SETTING_SCHEMA); + + /* These are internal settings that exists only for distributions + * to override, so we cache them on startup and don't react to changes. + */ + app->unified_menu = g_settings_get_boolean (app->global_settings, TERMINAL_SETTING_UNIFIED_MENU_KEY); + app->use_headerbar = terminal_app_should_use_headerbar (app); + + GtkSettings *gtk_settings = gtk_settings_get_default (); + terminal_app_theme_variant_changed_cb (app->global_settings, + TERMINAL_SETTING_THEME_VARIANT_KEY, gtk_settings); + g_signal_connect (app->global_settings, + "changed::" TERMINAL_SETTING_THEME_VARIANT_KEY, + G_CALLBACK (terminal_app_theme_variant_changed_cb), + gtk_settings); + +#ifdef TERMINAL_SERVER + /* Clipboard targets */ + GdkDisplay *display = gdk_display_get_default (); + app->clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD); + clipboard_owner_change_cb (app->clipboard, nullptr, app); + g_signal_connect (app->clipboard, "owner-change", + G_CALLBACK (clipboard_owner_change_cb), app); + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY(display) && + !gdk_display_supports_selection_notification (display)) + g_printerr ("Display does not support owner-change; copy/paste will be broken!\n"); +#endif +#endif /* TERMINAL_SERVER */ + + /* Get the profiles */ + app->profiles_list = terminal_profiles_list_new(app->settings_backend, + app->schema_source); + + gs_unref_object auto settings = + terminal_g_settings_new_with_path(app->settings_backend, + app->schema_source, + TERMINAL_KEYBINDINGS_SCHEMA, + TERMINAL_KEYBINDINGS_SCHEMA_PATH); + terminal_accels_init (G_APPLICATION (app), settings, app->use_headerbar); +} + +static void +terminal_app_finalize (GObject *object) +{ + auto app = TERMINAL_APP(object); + +#ifdef TERMINAL_SERVER + g_signal_handlers_disconnect_by_func (app->clipboard, + (void*)clipboard_owner_change_cb, + app); + free_clipboard_targets (app); + + g_signal_handlers_disconnect_by_func (app->profiles_list, + (void*)terminal_app_update_profile_menus, + app); + g_hash_table_destroy (app->screen_map); +#endif + + g_object_unref (app->global_settings); + g_object_unref (app->desktop_interface_settings); + g_object_unref (app->system_proxy_settings); + for (int i = 0; i < 4; ++i) + g_object_unref(app->system_proxy_protocol_settings[i]); + g_clear_object (&app->gtk_debug_settings); + g_settings_schema_source_unref(app->schema_source); + g_clear_object (&app->settings_backend); + +#ifdef TERMINAL_SERVER + g_clear_object (&app->menubar); + g_clear_object (&app->menubar_new_terminal_section); + g_clear_object (&app->menubar_set_profile_section); + g_clear_object (&app->profilemenu); + g_clear_object (&app->headermenu); + g_clear_object (&app->headermenu_set_profile_section); + g_clear_object (&app->set_profile_menu); + + { + gs_unref_object auto process = reinterpret_cast<TerminalPrefsProcess*>(g_weak_ref_get(&app->prefs_process_ref)); + if (process) + terminal_prefs_process_abort(process); + } + + g_weak_ref_clear(&app->prefs_process_ref); +#endif /* TERMINAL_SERVER */ + + terminal_accels_shutdown (); + + G_OBJECT_CLASS (terminal_app_parent_class)->finalize (object); +} + +static void +terminal_app_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + auto app = TERMINAL_APP(object); + + switch (prop_id) { + case PROP_SETTINGS_BACKEND: + g_value_set_object(value, app->settings_backend); + break; + case PROP_IS_DEFAULT_TERMINAL: + g_value_set_boolean(value, app->xte_is_default); + break; + case PROP_ASK_DEFAULT_TERMINAL: + g_value_set_boolean(value, app->ask_default); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_app_set_property(GObject* object, + guint prop_id, + GValue const* value, + GParamSpec* pspec) +{ + auto app = TERMINAL_APP(object); + + switch (prop_id) { + case PROP_SETTINGS_BACKEND: + app->settings_backend = G_SETTINGS_BACKEND(g_value_dup_object(value)); + break; + case PROP_ASK_DEFAULT_TERMINAL: + app->ask_default = g_value_get_boolean(value); + break; + case PROP_IS_DEFAULT_TERMINAL: // not writable + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#ifdef TERMINAL_SERVER + +static gboolean +terminal_app_dbus_register (GApplication *application, + GDBusConnection *connection, + const gchar *object_path, + GError **error) +{ + TerminalApp *app = TERMINAL_APP (application); + gs_unref_object TerminalObjectSkeleton *object = nullptr; + gs_unref_object TerminalFactory *factory = nullptr; + + if (!G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_register (application, + connection, + object_path, + error)) + return FALSE; + +#ifdef ENABLE_SEARCH_PROVIDER + if (g_settings_get_boolean (app->global_settings, TERMINAL_SETTING_SHELL_INTEGRATION_KEY)) { + gs_unref_object TerminalSearchProvider *search_provider; + + search_provider = terminal_search_provider_new (); + + if (!terminal_search_provider_dbus_register (search_provider, + connection, + TERMINAL_SEARCH_PROVIDER_PATH, + error)) + return FALSE; + + gs_transfer_out_value (&app->search_provider, &search_provider); + } +#endif /* ENABLE_SEARCH_PROVIDER */ + + object = terminal_object_skeleton_new (TERMINAL_FACTORY_OBJECT_PATH); + factory = terminal_factory_impl_new (); + terminal_object_skeleton_set_factory (object, factory); + + app->object_manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX); + g_dbus_object_manager_server_export (app->object_manager, G_DBUS_OBJECT_SKELETON (object)); + + /* And export the object */ + g_dbus_object_manager_server_set_connection (app->object_manager, connection); + return TRUE; +} + +static void +terminal_app_dbus_unregister (GApplication *application, + GDBusConnection *connection, + const gchar *object_path) +{ + TerminalApp *app = TERMINAL_APP (application); + + if (app->object_manager) { + g_dbus_object_manager_server_unexport (app->object_manager, TERMINAL_FACTORY_OBJECT_PATH); + g_object_unref (app->object_manager); + app->object_manager = nullptr; + } + +#ifdef ENABLE_SEARCH_PROVIDER + if (app->search_provider) { + terminal_search_provider_dbus_unregister (app->search_provider, connection, TERMINAL_SEARCH_PROVIDER_PATH); + g_object_unref (app->search_provider); + app->search_provider = nullptr; + } +#endif /* ENABLE_SEARCH_PROVIDER */ + + G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_unregister (application, + connection, + object_path); +} + +#endif /* TERMINAL_SERVER */ + +static void +terminal_app_class_init (TerminalAppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass); + + object_class->constructed = terminal_app_constructed; + object_class->finalize = terminal_app_finalize; + object_class->get_property = terminal_app_get_property; + object_class->set_property = terminal_app_set_property; + + g_object_class_install_property + (object_class, + PROP_SETTINGS_BACKEND, + g_param_spec_object("settings-backend", nullptr, nullptr, + G_TYPE_SETTINGS_BACKEND, + GParamFlags(G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property + (object_class, + PROP_IS_DEFAULT_TERMINAL, + g_param_spec_boolean("is-default-terminal", nullptr, nullptr, + false, + GParamFlags(G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property + (object_class, + PROP_ASK_DEFAULT_TERMINAL, + g_param_spec_boolean("ask-default-terminal", nullptr, nullptr, + false, + GParamFlags(G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + g_application_class->activate = terminal_app_activate; + g_application_class->startup = terminal_app_startup; +#ifdef TERMINAL_SERVER + g_application_class->dbus_register = terminal_app_dbus_register; + g_application_class->dbus_unregister = terminal_app_dbus_unregister; +#endif + + signals[CLIPBOARD_TARGETS_CHANGED] = + g_signal_new (I_("clipboard-targets-changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalAppClass, clipboard_targets_changed), + nullptr, nullptr, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); +} + +/* Public API */ + +GApplication* +terminal_app_new(char const* app_id, + GApplicationFlags flags, + GSettingsBackend* backend) +{ + return reinterpret_cast<GApplication*> + (g_object_new (TERMINAL_TYPE_APP, + "application-id", app_id ? app_id : TERMINAL_APPLICATION_ID, + "flags", flags, + "settings-backend", backend, + nullptr)); +} + +#ifdef TERMINAL_SERVER + +TerminalScreen * +terminal_app_get_screen_by_uuid (TerminalApp *app, + const char *uuid) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr); + + return reinterpret_cast<TerminalScreen*>(g_hash_table_lookup (app->screen_map, uuid)); +} + +char * +terminal_app_dup_screen_object_path (TerminalApp *app, + TerminalScreen *screen) +{ + char *object_path = g_strdup_printf (TERMINAL_RECEIVER_OBJECT_PATH_FORMAT, + terminal_screen_get_uuid (screen)); + object_path = g_strdelimit (object_path, "-", '_'); + g_assert (g_variant_is_object_path (object_path)); + return object_path; +} + +/** + * terminal_app_get_receiver_impl_by_object_path: + * @app: + * @object_path: + * + * Returns: (transfer full): the #TerminalReceiverImpl for @object_path, or %nullptr + */ +static TerminalReceiverImpl * +terminal_app_get_receiver_impl_by_object_path (TerminalApp *app, + const char *object_path) +{ + gs_unref_object GDBusObject *skeleton = + g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (app->object_manager), + object_path); + if (skeleton == nullptr || !TERMINAL_IS_OBJECT_SKELETON (skeleton)) + return nullptr; + + TerminalReceiverImpl *impl = nullptr; + g_object_get (skeleton, "receiver", &impl, nullptr); + if (impl == nullptr) + return nullptr; + + g_assert (TERMINAL_IS_RECEIVER_IMPL (impl)); + return impl; +} + +/** + * terminal_app_get_screen_by_object_path: + * @app: + * @object_path: + * + * Returns: (transfer full): the #TerminalScreen for @object_path, or %nullptr + */ +TerminalScreen * +terminal_app_get_screen_by_object_path (TerminalApp *app, + const char *object_path) +{ + gs_unref_object TerminalReceiverImpl *impl = + terminal_app_get_receiver_impl_by_object_path (app, object_path); + if (impl == nullptr) + return nullptr; + + return terminal_receiver_impl_get_screen (impl); +} + +void +terminal_app_register_screen (TerminalApp *app, + TerminalScreen *screen) +{ + const char *uuid = terminal_screen_get_uuid (screen); + g_hash_table_insert (app->screen_map, g_strdup (uuid), screen); + + gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen); + TerminalObjectSkeleton *skeleton = terminal_object_skeleton_new (object_path); + + TerminalReceiverImpl *impl = terminal_receiver_impl_new (screen); + terminal_object_skeleton_set_receiver (skeleton, TERMINAL_RECEIVER (impl)); + g_object_unref (impl); + + g_dbus_object_manager_server_export (app->object_manager, + G_DBUS_OBJECT_SKELETON (skeleton)); +} + +void +terminal_app_unregister_screen (TerminalApp *app, + TerminalScreen *screen) +{ + const char *uuid = terminal_screen_get_uuid (screen); + gboolean found = g_hash_table_remove (app->screen_map, uuid); + g_warn_if_fail (found); + if (!found) + return; /* repeat unregistering */ + + gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen); + gs_unref_object TerminalReceiverImpl *impl = + terminal_app_get_receiver_impl_by_object_path (app, object_path); + g_warn_if_fail (impl != nullptr); + + if (impl != nullptr) + terminal_receiver_impl_unset_screen (impl); + + g_dbus_object_manager_server_unexport (app->object_manager, object_path); +} + +GdkAtom * +terminal_app_get_clipboard_targets (TerminalApp *app, + GtkClipboard *clipboard, + int *n_targets) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr); + g_return_val_if_fail (n_targets != nullptr, nullptr); + + if (clipboard != app->clipboard) { + *n_targets = 0; + return nullptr; + } + + *n_targets = app->n_clipboard_targets; + return app->clipboard_targets; +} + +#endif /* TERMINAL_SERVER */ + +void +terminal_app_edit_preferences(TerminalApp* app, + GSettings* profile, + char const* hint, + unsigned timestamp) +{ +#ifdef TERMINAL_SERVER + gs_free char* uuid = nullptr; + if (profile) + uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, profile); + + gs_unref_object auto process = reinterpret_cast<TerminalPrefsProcess*>(g_weak_ref_get(&app->prefs_process_ref)); + if (process) { + terminal_prefs_process_show(process, + uuid, + hint, + timestamp); + } else { + terminal_prefs_process_new_async(nullptr, // cancellable, + GAsyncReadyCallback(launch_prefs_cb), + prefs_launch_data_new(app, uuid, hint, timestamp)); + } +#endif /* TERMINAL_SERVER */ +#ifdef TERMINAL_PREFERENCES + terminal_prefs_show_preferences(profile, hint, timestamp); +#endif +} + +/** + * terminal_app_get_profiles_list: + * + * Returns: (transfer none): returns the singleton profiles list #TerminalSettingsList + */ +TerminalSettingsList * +terminal_app_get_profiles_list (TerminalApp *app) +{ + return app->profiles_list; +} + +#ifdef TERMINAL_SERVER + +/** + * terminal_app_get_menubar: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the main window menu bar as a #GMenuModel + */ +GMenuModel * +terminal_app_get_menubar (TerminalApp *app) +{ + return app->menubar; +} + +/** + * terminal_app_get_headermenu: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the main window headerbar menu bar as a #GMenuModel + */ +GMenuModel * +terminal_app_get_headermenu (TerminalApp *app) +{ + if (app->headermenu == nullptr) + terminal_app_create_headermenu (app); + + return app->headermenu; +} + +/** + * terminal_app_get_profilemenu: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the main window headerbar profile menu as a #GMenuModel + */ +GMenuModel * +terminal_app_get_profilemenu (TerminalApp *app) +{ + if (app->profilemenu == nullptr) + terminal_app_create_profilemenu (app); + + return app->profilemenu; +} + +/** + * terminal_app_get_profile_section: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the main window's menubar's profiles section as a #GMenuModel + */ +GMenuModel * +terminal_app_get_profile_section (TerminalApp *app) +{ + return G_MENU_MODEL (app->set_profile_menu); +} + +#endif /* TERMINAL_SERVER */ + +/** + * terminal_app_get_settings_backend: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the #GSettingsBackend to use for all #GSettings instances + */ +GSettingsBackend* +terminal_app_get_settings_backend(TerminalApp *app) +{ + return app->settings_backend; +} + +/** + * terminal_app_get_schema_source: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the #GSettingsSchemaSource to use for all #GSettings instances + */ +GSettingsSchemaSource* +terminal_app_get_schema_source(TerminalApp *app) +{ + return app->schema_source; +} + +/** + * terminal_app_get_global_settings: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the cached #GSettings object for the org.gnome.Terminal.Preferences schema + */ +GSettings * +terminal_app_get_global_settings (TerminalApp *app) +{ + return app->global_settings; +} + +/** + * terminal_app_get_desktop_interface_settings: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the cached #GSettings object for the org.gnome.interface schema + */ +GSettings * +terminal_app_get_desktop_interface_settings (TerminalApp *app) +{ + return app->desktop_interface_settings; +} + +/** + * terminal_app_get_proxy_settings: + * @app: a #TerminalApp + * + * Returns: (tranfer none): the cached #GSettings object for the org.gnome.system.proxy schema + */ +GSettings * +terminal_app_get_proxy_settings (TerminalApp *app) +{ + return app->system_proxy_settings; +} + +/** + * terminal_app_get_proxy_settings_for_protocol: + * @app: a #TerminalApp + * @protocol: a #TerminalProxyProtocol + * + * Returns: (tranfer none): the cached #GSettings object for the org.gnome.system.proxy.@protocol schema + */ +GSettings* +terminal_app_get_proxy_settings_for_protocol(TerminalApp *app, + TerminalProxyProtocol protocol) +{ + return app->system_proxy_protocol_settings[(int)protocol]; +} + +GSettings * +terminal_app_get_gtk_debug_settings (TerminalApp *app) +{ + return app->gtk_debug_settings; +} + +/** + * terminal_app_get_system_font: + * @app: + * + * Creates a #PangoFontDescription for the system monospace font. + * + * Returns: (transfer full): a new #PangoFontDescription + */ +PangoFontDescription * +terminal_app_get_system_font (TerminalApp *app) +{ + gs_free char *font = nullptr; + + g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr); + + font = g_settings_get_string (app->desktop_interface_settings, MONOSPACE_FONT_KEY_NAME); + + return pango_font_description_from_string (font); +} + +#ifdef TERMINAL_SERVER + +GDBusObjectManagerServer * +terminal_app_get_object_manager (TerminalApp *app) +{ + g_warn_if_fail (app->object_manager != nullptr); + return app->object_manager; +} + +#endif /* TERMINAL_SERVER */ + +gboolean +terminal_app_get_menu_unified (TerminalApp *app) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), TRUE); + + return app->unified_menu; +} + +gboolean +terminal_app_get_use_headerbar (TerminalApp *app) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), FALSE); + + return app->use_headerbar; +} + +gboolean +terminal_app_get_dialog_use_headerbar (TerminalApp *app) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), FALSE); + + gboolean dialog_use_header; + g_object_get (gtk_settings_get_default (), + "gtk-dialogs-use-header", &dialog_use_header, + nullptr); + + return dialog_use_header && app->use_headerbar; +} + +gboolean +terminal_app_is_default_terminal(TerminalApp* app) +{ + g_return_val_if_fail(TERMINAL_IS_APP(app), false); + return app->xte_is_default; +} + +gboolean +terminal_app_get_ask_default_terminal(TerminalApp* app) +{ + g_return_val_if_fail(TERMINAL_IS_APP(app), false); + return app->ask_default; +} + +void +terminal_app_unset_ask_default_terminal(TerminalApp* app) +{ + g_return_if_fail(TERMINAL_IS_APP(app)); + app->ask_default = false; + g_object_notify(G_OBJECT(app), "ask-default-terminal"); +} + +void +terminal_app_make_default_terminal(TerminalApp* app) +{ + g_return_if_fail(TERMINAL_IS_APP(app)); + terminal_util_make_default_terminal(); + app->xte_is_default = terminal_util_is_default_terminal(); + g_object_notify(G_OBJECT(app), "is-default-terminal"); +} |