1 /*
2  * Copyright © 2001, 2002 Havoc Pennington
3  * Copyright © 2002 Red Hat, Inc.
4  * Copyright © 2002 Sun Microsystems
5  * Copyright © 2003 Mariano Suarez-Alvarez
6  * Copyright © 2008, 2010, 2011, 2015, 2017 Christian Persch
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gio/gio.h>
27 
28 #define G_SETTINGS_ENABLE_BACKEND
29 #include <gio/gsettingsbackend.h>
30 
31 #include "terminal-intl.hh"
32 #include "terminal-debug.hh"
33 #include "terminal-app.hh"
34 #include "terminal-accels.hh"
35 #include "terminal-client-utils.hh"
36 #include "terminal-screen.hh"
37 #include "terminal-screen-container.hh"
38 #include "terminal-window.hh"
39 #include "terminal-profiles-list.hh"
40 #include "terminal-util.hh"
41 #include "profile-editor.hh"
42 #include "terminal-schemas.hh"
43 #include "terminal-gdbus.hh"
44 #include "terminal-defines.hh"
45 #include "terminal-prefs.hh"
46 #include "terminal-libgsystem.hh"
47 
48 #ifdef ENABLE_SEARCH_PROVIDER
49 #include "terminal-search-provider.hh"
50 #endif /* ENABLE_SEARCH_PROVIDER */
51 
52 #include <sys/wait.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <stdlib.h>
56 #include <time.h>
57 
58 #ifdef GDK_WINDOWING_X11
59 #include <gdk/gdkx.h>
60 #endif
61 
62 #define DESKTOP_INTERFACE_SETTINGS_SCHEMA       "org.gnome.desktop.interface"
63 
64 #define SYSTEM_PROXY_SETTINGS_SCHEMA            "org.gnome.system.proxy"
65 #define SYSTEM_HTTP_PROXY_SETTINGS_SCHEMA       "org.gnome.system.proxy.http"
66 #define SYSTEM_HTTPS_PROXY_SETTINGS_SCHEMA      "org.gnome.system.proxy.https"
67 #define SYSTEM_FTP_PROXY_SETTINGS_SCHEMA        "org.gnome.system.proxy.ftp"
68 #define SYSTEM_SOCKS_PROXY_SETTINGS_SCHEMA      "org.gnome.system.proxy.socks"
69 
70 #define GTK_SETTING_PREFER_DARK_THEME           "gtk-application-prefer-dark-theme"
71 
72 #define GTK_DEBUG_SETTING_SCHEMA                "org.gtk.Settings.Debug"
73 
74 #ifdef DISUNIFY_NEW_TERMINAL_SECTION
75 #error Use a gsettings override instead
76 #endif
77 
78 /*
79  * Session state is stored entirely in the RestartCommand command line.
80  *
81  * The number one rule: all stored information is EITHER per-session,
82  * per-profile, or set from a command line option. THERE CAN BE NO
83  * OVERLAP. The UI and implementation totally break if you overlap
84  * these categories. See gnome-terminal 1.x for why.
85  */
86 
87 struct _TerminalAppClass {
88   GtkApplicationClass parent_class;
89 
90   void (* clipboard_targets_changed) (TerminalApp *app,
91                                       GtkClipboard *clipboard);
92 };
93 
94 struct _TerminalApp
95 {
96   GtkApplication parent_instance;
97 
98   GDBusObjectManagerServer *object_manager;
99 
100   TerminalSettingsList *profiles_list;
101 
102   GHashTable *screen_map;
103 
104   GSettingsSchemaSource* schema_source;
105   GSettings *global_settings;
106   GSettings *desktop_interface_settings;
107   GSettings *system_proxy_settings;
108   GSettings* system_proxy_protocol_settings[4];
109   GSettings *gtk_debug_settings;
110 
111 #ifdef ENABLE_SEARCH_PROVIDER
112   TerminalSearchProvider *search_provider;
113 #endif /* ENABLE_SEARCH_PROVIDER */
114 
115   GMenuModel *menubar;
116   GMenu *menubar_new_terminal_section;
117   GMenu *menubar_set_profile_section;
118 
119   GMenuModel *profilemenu;
120   GMenuModel *headermenu;
121   GMenu *headermenu_set_profile_section;
122 
123   GMenu *set_profile_menu;
124 
125   GtkClipboard *clipboard;
126   GdkAtom *clipboard_targets;
127   int n_clipboard_targets;
128 
129   gboolean unified_menu;
130   gboolean use_headerbar;
131 };
132 
133 enum
134 {
135   CLIPBOARD_TARGETS_CHANGED,
136   LAST_SIGNAL
137 };
138 
139 static guint signals[LAST_SIGNAL];
140 
141 /* Debugging helper */
142 
143 static void
terminal_app_init_debug(void)144 terminal_app_init_debug (void)
145 {
146 #ifdef ENABLE_DEBUG
147   const char *env = g_getenv ("GTK_TEXT_DIR");
148   if (env != nullptr) {
149     if (g_str_equal (env, "help")) {
150       g_printerr ("Usage: GTK_TEXT_DIR=ltr|rtl\n");
151     } else {
152       GtkTextDirection dir;
153       if (g_str_equal (env, "rtl"))
154         dir = GTK_TEXT_DIR_RTL;
155       else
156         dir = GTK_TEXT_DIR_LTR;
157 
158       gtk_widget_set_default_direction (dir);
159     }
160   }
161 
162   env = g_getenv ("GTK_SETTINGS");
163   if (env == nullptr)
164     return;
165 
166   GObject *settings = G_OBJECT (gtk_settings_get_default ());
167   GObjectClass *settings_class = G_OBJECT_GET_CLASS (settings);
168 
169   if (g_str_equal (env, "help")) {
170     g_printerr ("Usage: GTK_SETTINGS=setting[,setting…] where 'setting' is one of these:\n");
171 
172     guint n_props;
173     GParamSpec **props = g_object_class_list_properties (settings_class, &n_props);
174     for (guint i = 0; i < n_props; i++) {
175       if (G_PARAM_SPEC_VALUE_TYPE (props[i]) != G_TYPE_BOOLEAN)
176         continue;
177 
178       GValue value = { 0, };
179       g_value_init (&value, G_TYPE_BOOLEAN);
180       g_object_get_property (settings, props[i]->name, &value);
181       g_printerr ("  %s (%s)\n", props[i]->name, g_value_get_boolean (&value) ? "true" : "false");
182       g_value_unset (&value);
183     }
184     g_printerr ("  Use 'setting' to set to true, "
185                 "'~setting' to set to false, "
186                 "and '!setting' to invert.\n");
187   } else {
188     gs_strfreev char **tokens = g_strsplit (env, ",", -1);
189     for (guint i = 0; tokens[i] != nullptr; i++) {
190       const char *prop = tokens[i];
191       char c = prop[0];
192       if (c == '~' || c == '!')
193         prop++;
194 
195       GParamSpec *pspec = g_object_class_find_property (settings_class, prop);
196       if (pspec == nullptr) {
197         g_printerr ("Setting \"%s\" does not exist.\n", prop);
198       } else if (G_PARAM_SPEC_VALUE_TYPE (pspec) != G_TYPE_BOOLEAN) {
199         g_printerr ("Setting \"%s\" is not boolean.\n", prop);
200       } else {
201         GValue value = { 0, };
202         g_value_init (&value, G_TYPE_BOOLEAN);
203         if (c == '!') {
204           g_object_get_property (settings, pspec->name, &value);
205           g_value_set_boolean (&value, !g_value_get_boolean (&value));
206         } else if (c == '~') {
207           g_value_set_boolean (&value, FALSE);
208         } else {
209           g_value_set_boolean (&value, TRUE);
210         }
211         g_object_set_property (settings, pspec->name, &value);
212         g_value_unset (&value);
213       }
214     }
215   }
216 #endif
217 }
218 
219 /* Helper functions */
220 
221 static gboolean
strv_contains_gnome(char ** strv)222 strv_contains_gnome (char **strv)
223 {
224   if (strv == nullptr)
225     return FALSE;
226 
227   for (int i = 0; strv[i] != nullptr; i++) {
228     if (g_ascii_strcasecmp (strv[i], "gnome") == 0 ||
229         g_ascii_strcasecmp (strv[i], "gnome-classic") == 0)
230       return TRUE;
231   }
232 
233   return FALSE;
234 }
235 
236 /*
237  * terminal_app_should_use_headerbar:
238  *
239  * Determines if the app should use headerbars. This is determined
240  * * If the pref is set, the pref value is used
241  * * Otherwise, if XDG_CURRENT_DESKTOP contains GNOME or GNOME-Classic,
242  *   headerbar is used
243  * * Otherwise, headerbar is not used.
244  */
245 static gboolean
terminal_app_should_use_headerbar(TerminalApp * app)246 terminal_app_should_use_headerbar (TerminalApp *app)
247 {
248   gboolean set, use;
249   g_settings_get (app->global_settings, TERMINAL_SETTING_HEADERBAR_KEY, "mb", &set, &use);
250   if (set)
251     return use;
252 
253   const char *desktop = g_getenv ("XDG_CURRENT_DESKTOP");
254   if (desktop == nullptr)
255     return FALSE;
256 
257   char **desktops = g_strsplit (desktop, G_SEARCHPATH_SEPARATOR_S, -1);
258   use = strv_contains_gnome (desktops);
259   g_strfreev (desktops);
260 
261   return use;
262 }
263 
264 static gboolean
load_css_from_resource(GApplication * application,GtkCssProvider * provider,gboolean theme)265 load_css_from_resource (GApplication *application,
266                         GtkCssProvider *provider,
267                         gboolean theme)
268 {
269   const char *base_path;
270   gs_free char *uri;
271   gs_unref_object GFile *file;
272   gs_free_error GError *error = nullptr;
273 
274   base_path = g_application_get_resource_base_path (application);
275 
276   if (theme) {
277     gs_free char *str, *theme_name;
278 
279     g_object_get (gtk_settings_get_default (), "gtk-theme-name", &str, nullptr);
280     theme_name = g_ascii_strdown (str, -1);
281     uri = g_strdup_printf ("resource://%s/css/%s/terminal.css", base_path, theme_name);
282   } else {
283     uri = g_strdup_printf ("resource://%s/css/terminal.css", base_path);
284   }
285 
286   file = g_file_new_for_uri (uri);
287   if (!g_file_query_exists (file, nullptr /* cancellable */))
288     return FALSE;
289 
290   if (!gtk_css_provider_load_from_file (provider, file, &error))
291     g_assert_no_error (error);
292 
293   return TRUE;
294 }
295 
296 static void
add_css_provider(GApplication * application,gboolean theme)297 add_css_provider (GApplication *application,
298                   gboolean theme)
299 {
300   gs_unref_object GtkCssProvider *provider;
301 
302   provider = gtk_css_provider_new ();
303   if (!load_css_from_resource (application, provider, theme))
304     return;
305 
306   gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
307                                              GTK_STYLE_PROVIDER (provider),
308                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
309 }
310 
311 static void
app_load_css(GApplication * application)312 app_load_css (GApplication *application)
313 {
314   add_css_provider (application, FALSE);
315   add_css_provider (application, TRUE);
316 }
317 
318 char *
terminal_app_new_profile(TerminalApp * app,GSettings * base_profile,const char * name)319 terminal_app_new_profile (TerminalApp *app,
320                           GSettings   *base_profile,
321                           const char  *name)
322 {
323   char *uuid;
324 
325   if (base_profile) {
326     gs_free char *base_uuid;
327 
328     base_uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, base_profile);
329     uuid = terminal_settings_list_clone_child (app->profiles_list, base_uuid, name);
330   } else {
331     uuid = terminal_settings_list_add_child (app->profiles_list, name);
332   }
333 
334   return uuid;
335 }
336 
337 void
terminal_app_remove_profile(TerminalApp * app,GSettings * profile)338 terminal_app_remove_profile (TerminalApp *app,
339                              GSettings *profile)
340 {
341   g_return_if_fail (TERMINAL_IS_APP (app));
342   g_return_if_fail (G_IS_SETTINGS (profile));
343 
344   gs_unref_object GSettings *default_profile = terminal_settings_list_ref_default_child (app->profiles_list);
345   if (default_profile == profile)
346     return;
347 
348   /* First, we need to switch any screen using this profile to the default profile */
349   gs_free_list GList *screens = g_hash_table_get_values (app->screen_map);
350   for (GList *l = screens; l != nullptr; l = l->next) {
351     TerminalScreen *screen = TERMINAL_SCREEN (l->data);
352     if (terminal_screen_get_profile (screen) != profile)
353       continue;
354 
355     terminal_screen_set_profile (screen, default_profile);
356   }
357 
358   /* Now we can safely remove the profile */
359   gs_free char *uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, profile);
360   terminal_settings_list_remove_child (app->profiles_list, uuid);
361 }
362 
363 static void
terminal_app_theme_variant_changed_cb(GSettings * settings,const char * key,GtkSettings * gtk_settings)364 terminal_app_theme_variant_changed_cb (GSettings   *settings,
365                                        const char  *key,
366                                        GtkSettings *gtk_settings)
367 {
368   TerminalThemeVariant theme;
369 
370   theme = TerminalThemeVariant(g_settings_get_enum (settings, key));
371   if (theme == TERMINAL_THEME_VARIANT_SYSTEM)
372     gtk_settings_reset_property (gtk_settings, GTK_SETTING_PREFER_DARK_THEME);
373   else
374     g_object_set (gtk_settings,
375                   GTK_SETTING_PREFER_DARK_THEME,
376                   theme == TERMINAL_THEME_VARIANT_DARK,
377                   nullptr);
378 }
379 
380 /* Submenus for New Terminal per profile, and to change profiles */
381 
382 static void terminal_app_update_profile_menus (TerminalApp *app);
383 
384 typedef struct {
385   char *uuid;
386   char *label;
387 } ProfileData;
388 
389 static void
profile_data_clear(ProfileData * data)390 profile_data_clear (ProfileData *data)
391 {
392   g_free (data->uuid);
393   g_free (data->label);
394 }
395 
396 typedef struct {
397   GArray *array;
398   TerminalApp *app;
399 } ProfilesForeachData;
400 
401 static void
foreach_profile_cb(TerminalSettingsList * list,const char * uuid,GSettings * profile,ProfilesForeachData * user_data)402 foreach_profile_cb (TerminalSettingsList *list,
403                     const char *uuid,
404                     GSettings *profile,
405                     ProfilesForeachData *user_data)
406 {
407   ProfileData data;
408   data.uuid = g_strdup (uuid);
409   data.label = g_settings_get_string (profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY);
410 
411   g_array_append_val (user_data->array, data);
412 
413   /* only connect if we haven't seen this profile before */
414   if (g_signal_handler_find (profile,
415 			     GSignalMatchType(G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA),
416                              0, 0, nullptr,
417 			     (void*)terminal_app_update_profile_menus, user_data->app) == 0)
418     g_signal_connect_swapped (profile, "changed::" TERMINAL_PROFILE_VISIBLE_NAME_KEY,
419                               G_CALLBACK (terminal_app_update_profile_menus), user_data->app);
420 }
421 
422 static int
compare_profile_label_cb(gconstpointer ap,gconstpointer bp)423 compare_profile_label_cb (gconstpointer ap,
424                           gconstpointer bp)
425 {
426   const ProfileData *a = (ProfileData const*)ap;
427   const ProfileData *b = (ProfileData const*)bp;
428 
429   return g_utf8_collate (a->label, b->label);
430 }
431 
432 static void
menu_append_numbered(GMenu * menu,const char * label,int num,const char * action_name,GVariant * target)433 menu_append_numbered (GMenu *menu,
434                       const char *label,
435                       int num,
436                       const char *action_name,
437                       GVariant *target /* floating, consumed */)
438 {
439   gs_free_gstring GString *str;
440   gs_unref_object GMenuItem *item;
441   const char *p;
442 
443   /* Who'd use more that 4 underscores in a profile name... */
444   str = g_string_sized_new (strlen (label) + 4 + 1 + 8);
445 
446   if (num < 10)
447     g_string_append_printf (str, "_%Id. ", num);
448   else if (num < 36)
449     g_string_append_printf (str, "_%c. ",  (char)('A' + num - 10));
450 
451   /* Append the label with underscores elided */
452   for (p = label; *p; p++) {
453     if (*p == '_')
454       g_string_append (str, "__");
455     else
456       g_string_append_c (str, *p);
457   }
458 
459   item = g_menu_item_new (str->str, nullptr);
460   g_menu_item_set_action_and_target_value (item, action_name, target);
461   g_menu_append_item (menu, item);
462 }
463 
464 static void
append_new_terminal_item(GMenu * section,const char * label,const char * target,ProfileData * data,guint n_profiles)465 append_new_terminal_item (GMenu *section,
466                           const char *label,
467                           const char *target,
468                           ProfileData *data,
469                           guint n_profiles)
470 {
471   gs_unref_object GMenuItem *item = g_menu_item_new (label, nullptr);
472 
473   if (n_profiles > 1) {
474     gs_unref_object GMenu *submenu = g_menu_new ();
475 
476     for (guint i = 0; i < n_profiles; i++) {
477       menu_append_numbered (submenu, data[i].label, i + 1,
478                             "win.new-terminal",
479                             g_variant_new ("(ss)", target, data[i].uuid));
480     }
481 
482     g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
483   } else {
484     g_menu_item_set_action_and_target (item, "win.new-terminal",
485                                        "(ss)", target, "current");
486   }
487   g_menu_append_item (section, item);
488 }
489 
490 static void
fill_header_new_terminal_menu(GMenuModel * menu,ProfileData * data,guint n_profiles)491 fill_header_new_terminal_menu (GMenuModel *menu,
492                                ProfileData *data,
493                                guint n_profiles)
494 {
495   gs_unref_object GMenu *section = nullptr;
496 
497   if (n_profiles <= 1)
498     return;
499 
500   section = g_menu_new ();
501 
502   for (guint i = 0; i < n_profiles; i++) {
503     menu_append_numbered (section, data[i].label, i + 1,
504                           "win.new-terminal",
505                           g_variant_new ("(ss)", "default", data[i].uuid));
506   }
507 
508   g_menu_append_section (G_MENU (menu), _("New Terminal"), G_MENU_MODEL (section));
509 }
510 
511 static void
fill_new_terminal_section(TerminalApp * app,GMenu * section,ProfileData * profiles,guint n_profiles)512 fill_new_terminal_section (TerminalApp *app,
513                            GMenu *section,
514                            ProfileData *profiles,
515                            guint n_profiles)
516 {
517   if (terminal_app_get_menu_unified (app)) {
518     append_new_terminal_item (section, _("New _Terminal"), "default", profiles, n_profiles);
519   } else {
520     append_new_terminal_item (section, _("New _Tab"), "tab", profiles, n_profiles);
521     append_new_terminal_item (section, _("New _Window"), "window", profiles, n_profiles);
522   }
523 }
524 
525 static GMenu *
set_profile_submenu_new(ProfileData * data,guint n_profiles)526 set_profile_submenu_new (ProfileData *data,
527                          guint n_profiles)
528 {
529   /* No submenu if there's only one profile */
530   if (n_profiles <= 1)
531     return nullptr;
532 
533   GMenu *menu = g_menu_new ();
534   for (guint i = 0; i < n_profiles; i++) {
535     menu_append_numbered (menu, data[i].label, i + 1,
536                           "win.profile",
537                           g_variant_new_string (data[i].uuid));
538   }
539 
540   return menu;
541 }
542 
543 static void
terminal_app_update_profile_menus(TerminalApp * app)544 terminal_app_update_profile_menus (TerminalApp *app)
545 {
546   g_clear_object (&app->set_profile_menu);
547 
548   /* Get profiles list and sort by label */
549   gs_unref_array GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (ProfileData),
550                                                     terminal_settings_list_get_n_children (app->profiles_list));
551   g_array_set_clear_func (array, (GDestroyNotify) profile_data_clear);
552 
553   ProfilesForeachData data = { array, app };
554   terminal_settings_list_foreach_child (app->profiles_list,
555                                         (TerminalSettingsListForeachFunc) foreach_profile_cb,
556                                         &data);
557   g_array_sort (array, compare_profile_label_cb);
558 
559   ProfileData *profiles = (ProfileData*) array->data;
560   guint n_profiles = array->len;
561 
562   app->set_profile_menu = set_profile_submenu_new (profiles, n_profiles);
563 
564   if (app->menubar != nullptr) {
565     g_menu_remove_all (G_MENU (app->menubar_new_terminal_section));
566     fill_new_terminal_section (app, app->menubar_new_terminal_section, profiles, n_profiles);
567 
568     g_menu_remove_all (G_MENU (app->menubar_set_profile_section));
569     if (app->set_profile_menu != nullptr) {
570       g_menu_append_submenu (app->menubar_set_profile_section, _("Change _Profile"),
571                              G_MENU_MODEL (app->set_profile_menu));
572     }
573   }
574 
575   if (app->profilemenu != nullptr) {
576     g_menu_remove_all (G_MENU (app->profilemenu));
577     fill_header_new_terminal_menu (app->profilemenu, profiles, n_profiles);
578   }
579 
580   if (app->headermenu != nullptr) {
581     g_menu_remove_all (G_MENU (app->headermenu_set_profile_section));
582     if (app->set_profile_menu != nullptr) {
583       g_menu_append_submenu (app->headermenu_set_profile_section, _("_Profile"),
584                              G_MENU_MODEL (app->set_profile_menu));
585     }
586   }
587 }
588 
589 static GMenuModel *
terminal_app_create_menubar(TerminalApp * app,gboolean shell_shows_menubar)590 terminal_app_create_menubar (TerminalApp *app,
591                              gboolean shell_shows_menubar)
592 {
593   /* If the menubar is shown by the shell, omit mnemonics for the submenus. This is because Alt+F etc.
594    * are more important to be usable in the terminal, the menu cannot be replaced runtime (to toggle
595    * between mnemonic and non-mnemonic versions), gtk-enable-mnemonics or gtk_window_set_mnemonic_modifier()
596    * don't effect the menubar either, so there wouldn't be a way to disable Alt+F for File etc. otherwise.
597    * Furthermore, the menu would even grab mnemonics from the File and Preferences windows.
598    * In Unity, Alt+F10 opens the menubar, this should be good enough for keyboard navigation.
599    * If the menubar is shown by the app, toggling mnemonics is handled in terminal-window.c using
600    * gtk_window_set_mnemonic_modifier().
601    * See bug 792978 for details. */
602   terminal_util_load_objects_resource (shell_shows_menubar ? "/org/gnome/terminal/ui/menubar-without-mnemonics.ui"
603                                                            : "/org/gnome/terminal/ui/menubar-with-mnemonics.ui",
604                                        "menubar", &app->menubar,
605                                        "new-terminal-section", &app->menubar_new_terminal_section,
606                                        "set-profile-section", &app->menubar_set_profile_section,
607                                        nullptr);
608 
609   /* Install profile sections */
610   terminal_app_update_profile_menus (app);
611 
612   return app->menubar;
613 }
614 
615 static void
terminal_app_create_headermenu(TerminalApp * app)616 terminal_app_create_headermenu (TerminalApp *app)
617 {
618   terminal_util_load_objects_resource ("/org/gnome/terminal/ui/headerbar-menu.ui",
619                                        "headermenu", &app->headermenu,
620                                        "set-profile-section", &app->headermenu_set_profile_section,
621                                        nullptr);
622 
623   /* Install profile sections */
624   terminal_app_update_profile_menus (app);
625 }
626 
627 static void
terminal_app_create_profilemenu(TerminalApp * app)628 terminal_app_create_profilemenu (TerminalApp *app)
629 {
630   app->profilemenu = G_MENU_MODEL (g_menu_new ());
631 
632   /* Install profile sections */
633   terminal_app_update_profile_menus (app);
634 }
635 
636 /* Clipboard */
637 
638 static void
free_clipboard_targets(TerminalApp * app)639 free_clipboard_targets (TerminalApp *app)
640 {
641   g_free (app->clipboard_targets);
642   app->clipboard_targets = nullptr;
643   app->n_clipboard_targets = 0;
644 }
645 
646 static void
update_clipboard_targets(TerminalApp * app,GdkAtom * targets,int n_targets)647 update_clipboard_targets (TerminalApp *app,
648                           GdkAtom *targets,
649                           int n_targets)
650 {
651   free_clipboard_targets (app);
652 
653   /* Sometimes we receive targets == nullptr but n_targets == -1 */
654   if (targets != nullptr && n_targets < 255) {
655     app->clipboard_targets = reinterpret_cast<GdkAtom*>
656       (g_memdup (targets, sizeof (targets[0]) * n_targets));
657     app->n_clipboard_targets = n_targets;
658   }
659 }
660 
661 static void
clipboard_targets_received_cb(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,TerminalApp * app)662 clipboard_targets_received_cb (GtkClipboard *clipboard,
663                                GdkAtom *targets,
664                                int n_targets,
665                                TerminalApp *app)
666 {
667   update_clipboard_targets (app, targets, n_targets);
668 
669   _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_CLIPBOARD) {
670     g_printerr ("Clipboard has %d targets:", app->n_clipboard_targets);
671 
672     int i;
673     for (i = 0; i < app->n_clipboard_targets; i++) {
674       gs_free char *atom_name = gdk_atom_name (app->clipboard_targets[i]);
675       g_printerr (" %s", atom_name);
676     }
677     g_printerr ("\n");
678   }
679 
680   g_signal_emit (app, signals[CLIPBOARD_TARGETS_CHANGED], 0, clipboard);
681 }
682 
683 static void
clipboard_owner_change_cb(GtkClipboard * clipboard,GdkEvent * event G_GNUC_UNUSED,TerminalApp * app)684 clipboard_owner_change_cb (GtkClipboard *clipboard,
685                            GdkEvent *event G_GNUC_UNUSED,
686                            TerminalApp *app)
687 {
688   _terminal_debug_print (TERMINAL_DEBUG_CLIPBOARD,
689                          "Clipboard owner changed\n");
690 
691   clipboard_targets_received_cb (clipboard, nullptr, 0, app); /* clear */
692 
693   /* We can do this without holding a reference to @app since
694    * the app lives as long as the process.
695    */
696   gtk_clipboard_request_targets (clipboard,
697                                  (GtkClipboardTargetsReceivedFunc) clipboard_targets_received_cb,
698                                  app);
699 }
700 
701 /* Callbacks from former app menu.
702  * The preferences one is still used with the "--preferences" cmdline option. */
703 
704 static void
app_menu_preferences_cb(GSimpleAction * action,GVariant * parameter,gpointer user_data)705 app_menu_preferences_cb (GSimpleAction *action,
706                          GVariant      *parameter,
707                          gpointer       user_data)
708 {
709   TerminalApp *app = (TerminalApp*)user_data;
710 
711   terminal_app_edit_preferences (app, nullptr, nullptr);
712 }
713 
714 static void
app_menu_help_cb(GSimpleAction * action,GVariant * parameter,gpointer user_data)715 app_menu_help_cb (GSimpleAction *action,
716                   GVariant      *parameter,
717                   gpointer       user_data)
718 {
719   terminal_util_show_help (nullptr);
720 }
721 
722 static void
app_menu_about_cb(GSimpleAction * action,GVariant * parameter,gpointer user_data)723 app_menu_about_cb (GSimpleAction *action,
724                    GVariant      *parameter,
725                    gpointer       user_data)
726 {
727   terminal_util_show_about ();
728 }
729 
730 static void
app_menu_quit_cb(GSimpleAction * action,GVariant * parameter,gpointer user_data)731 app_menu_quit_cb (GSimpleAction *action,
732                   GVariant      *parameter,
733                   gpointer       user_data)
734 {
735   GtkApplication *application = (GtkApplication*)user_data;
736   GtkWindow *window;
737 
738   window = gtk_application_get_active_window (application);
739   if (TERMINAL_IS_WINDOW (window))
740     terminal_window_request_close (TERMINAL_WINDOW (window));
741   else /* a dialogue */
742     gtk_widget_destroy (GTK_WIDGET (window));
743 }
744 
745 /* Class implementation */
746 
G_DEFINE_TYPE(TerminalApp,terminal_app,GTK_TYPE_APPLICATION)747 G_DEFINE_TYPE (TerminalApp, terminal_app, GTK_TYPE_APPLICATION)
748 
749 /* GApplicationClass impl */
750 
751 static void
752 terminal_app_activate (GApplication *application)
753 {
754   /* No-op required because GApplication is stupid */
755 }
756 
757 static void
terminal_app_startup(GApplication * application)758 terminal_app_startup (GApplication *application)
759 {
760   TerminalApp *app = TERMINAL_APP (application);
761   const GActionEntry action_entries[] = {
762     { "preferences", app_menu_preferences_cb,   nullptr, nullptr, nullptr },
763     { "help",        app_menu_help_cb,          nullptr, nullptr, nullptr },
764     { "about",       app_menu_about_cb,         nullptr, nullptr, nullptr },
765     { "quit",        app_menu_quit_cb,          nullptr, nullptr, nullptr }
766   };
767 
768   g_application_set_resource_base_path (application, TERMINAL_RESOURCES_PATH_PREFIX);
769 
770   G_APPLICATION_CLASS (terminal_app_parent_class)->startup (application);
771 
772   /* Need to set the WM class (bug #685742) */
773   gdk_set_program_class("Gnome-terminal");
774 
775   g_action_map_add_action_entries (G_ACTION_MAP (application),
776                                    action_entries, G_N_ELEMENTS (action_entries),
777                                    application);
778 
779   app_load_css (application);
780 
781   /* Figure out whether the shell shows the menubar */
782   gboolean shell_shows_menubar;
783   g_object_get (gtk_settings_get_default (),
784                 "gtk-shell-shows-menubar", &shell_shows_menubar,
785                 nullptr);
786 
787   /* Create menubar */
788   terminal_app_create_menubar (app, shell_shows_menubar);
789 
790   /* Keep dynamic menus updated */
791   g_signal_connect_swapped (app->profiles_list, "children-changed",
792                             G_CALLBACK (terminal_app_update_profile_menus), app);
793 
794   /* Show/hide the menubar as appropriate: If the shell wants to show the menubar, make it available. */
795   if (shell_shows_menubar)
796     gtk_application_set_menubar (GTK_APPLICATION (app),
797                                  terminal_app_get_menubar (app));
798 
799   _terminal_debug_print (TERMINAL_DEBUG_SERVER, "Startup complete\n");
800 }
801 
802 /* GObjectClass impl */
803 
804 static void
terminal_app_init(TerminalApp * app)805 terminal_app_init (TerminalApp *app)
806 {
807   terminal_app_init_debug ();
808 
809   gtk_window_set_default_icon_name (GNOME_TERMINAL_ICON_NAME);
810 
811   app->schema_source = terminal_g_settings_schema_source_get_default();
812 
813   /* Desktop proxy settings */
814   app->system_proxy_settings = terminal_g_settings_new(app->schema_source,
815                                                        SYSTEM_PROXY_SETTINGS_SCHEMA);
816 
817   /* Since there is no way to get the schema ID of a child schema, we cannot
818    * verify that the installed schemas are correct. Also, due to a glib bug
819    * (https://gitlab.gnome.org/GNOME/glib/-/issues/1884) g_settings_get_child()
820    * doesn't work with non-default schema sources.
821    * So instead of using g_settings_get_child() on the SYSTEM_PROXY_SETTINGS_SCHEMA,
822    * we construct the child GSettings directly.
823    */
824   app->system_proxy_protocol_settings[TERMINAL_PROXY_HTTP] =
825     terminal_g_settings_new(app->schema_source,
826                             SYSTEM_HTTP_PROXY_SETTINGS_SCHEMA);
827   app->system_proxy_protocol_settings[TERMINAL_PROXY_HTTPS] =
828     terminal_g_settings_new(app->schema_source,
829                             SYSTEM_HTTPS_PROXY_SETTINGS_SCHEMA);
830   app->system_proxy_protocol_settings[TERMINAL_PROXY_FTP] =
831     terminal_g_settings_new(app->schema_source,
832                             SYSTEM_FTP_PROXY_SETTINGS_SCHEMA);
833   app->system_proxy_protocol_settings[TERMINAL_PROXY_SOCKS] =
834     terminal_g_settings_new(app->schema_source,
835                             SYSTEM_SOCKS_PROXY_SETTINGS_SCHEMA);
836 
837   /* Desktop Interface settings */
838   app->desktop_interface_settings = terminal_g_settings_new(app->schema_source,
839                                                             DESKTOP_INTERFACE_SETTINGS_SCHEMA);
840 
841   /* Terminal global settings */
842   app->global_settings = terminal_g_settings_new(app->schema_source,
843                                                  TERMINAL_SETTING_SCHEMA);
844 
845   /* Gtk debug settings */
846   app->gtk_debug_settings = terminal_g_settings_new(app->schema_source,
847                                                     GTK_DEBUG_SETTING_SCHEMA);
848 
849   /* These are internal settings that exists only for distributions
850    * to override, so we cache them on startup and don't react to changes.
851    */
852   app->unified_menu = g_settings_get_boolean (app->global_settings, TERMINAL_SETTING_UNIFIED_MENU_KEY);
853   app->use_headerbar = terminal_app_should_use_headerbar (app);
854 
855   GtkSettings *gtk_settings = gtk_settings_get_default ();
856   terminal_app_theme_variant_changed_cb (app->global_settings,
857                                          TERMINAL_SETTING_THEME_VARIANT_KEY, gtk_settings);
858   g_signal_connect (app->global_settings,
859                     "changed::" TERMINAL_SETTING_THEME_VARIANT_KEY,
860                     G_CALLBACK (terminal_app_theme_variant_changed_cb),
861                     gtk_settings);
862 
863   /* Clipboard targets */
864   GdkDisplay *display = gdk_display_get_default ();
865   app->clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
866   clipboard_owner_change_cb (app->clipboard, nullptr, app);
867   g_signal_connect (app->clipboard, "owner-change",
868                     G_CALLBACK (clipboard_owner_change_cb), app);
869 
870 #ifdef GDK_WINDOWING_X11
871   if (GDK_IS_X11_DISPLAY(display) &&
872       !gdk_display_supports_selection_notification (display))
873     g_printerr ("Display does not support owner-change; copy/paste will be broken!\n");
874 #endif
875 
876   /* Get the profiles */
877   app->profiles_list = terminal_profiles_list_new(app->schema_source);
878 
879   app->screen_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr);
880 
881   gs_unref_object GSettings *settings =
882     terminal_g_settings_new_with_path(app->schema_source,
883                                       TERMINAL_KEYBINDINGS_SCHEMA,
884                                       TERMINAL_KEYBINDINGS_SCHEMA_PATH);
885   terminal_accels_init (G_APPLICATION (app), settings, app->use_headerbar);
886 }
887 
888 static void
terminal_app_finalize(GObject * object)889 terminal_app_finalize (GObject *object)
890 {
891   TerminalApp *app = TERMINAL_APP (object);
892 
893   g_signal_handlers_disconnect_by_func (app->clipboard,
894                                         (void*)clipboard_owner_change_cb,
895                                         app);
896   free_clipboard_targets (app);
897 
898   g_signal_handlers_disconnect_by_func (app->profiles_list,
899                                         (void*)terminal_app_update_profile_menus,
900                                         app);
901   g_hash_table_destroy (app->screen_map);
902 
903   g_object_unref (app->global_settings);
904   g_object_unref (app->desktop_interface_settings);
905   g_object_unref (app->system_proxy_settings);
906   for (int i = 0; i < 4; ++i)
907     g_object_unref(app->system_proxy_protocol_settings[i]);
908   g_clear_object (&app->gtk_debug_settings);
909   g_settings_schema_source_unref(app->schema_source);
910 
911   g_clear_object (&app->menubar);
912   g_clear_object (&app->menubar_new_terminal_section);
913   g_clear_object (&app->menubar_set_profile_section);
914   g_clear_object (&app->profilemenu);
915   g_clear_object (&app->headermenu);
916   g_clear_object (&app->headermenu_set_profile_section);
917   g_clear_object (&app->set_profile_menu);
918 
919   terminal_accels_shutdown ();
920 
921   G_OBJECT_CLASS (terminal_app_parent_class)->finalize (object);
922 }
923 
924 static gboolean
terminal_app_dbus_register(GApplication * application,GDBusConnection * connection,const gchar * object_path,GError ** error)925 terminal_app_dbus_register (GApplication    *application,
926                             GDBusConnection *connection,
927                             const gchar     *object_path,
928                             GError         **error)
929 {
930   TerminalApp *app = TERMINAL_APP (application);
931   gs_unref_object TerminalObjectSkeleton *object = nullptr;
932   gs_unref_object TerminalFactory *factory = nullptr;
933 
934   if (!G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_register (application,
935                                                                        connection,
936                                                                        object_path,
937                                                                        error))
938     return FALSE;
939 
940 #ifdef ENABLE_SEARCH_PROVIDER
941   if (g_settings_get_boolean (app->global_settings, TERMINAL_SETTING_SHELL_INTEGRATION_KEY)) {
942     gs_unref_object TerminalSearchProvider *search_provider;
943 
944     search_provider = terminal_search_provider_new ();
945 
946     if (!terminal_search_provider_dbus_register (search_provider,
947                                                  connection,
948                                                  TERMINAL_SEARCH_PROVIDER_PATH,
949                                                  error))
950       return FALSE;
951 
952     gs_transfer_out_value (&app->search_provider, &search_provider);
953   }
954 #endif /* ENABLE_SEARCH_PROVIDER */
955 
956   object = terminal_object_skeleton_new (TERMINAL_FACTORY_OBJECT_PATH);
957   factory = terminal_factory_impl_new ();
958   terminal_object_skeleton_set_factory (object, factory);
959 
960   app->object_manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX);
961   g_dbus_object_manager_server_export (app->object_manager, G_DBUS_OBJECT_SKELETON (object));
962 
963   /* And export the object */
964   g_dbus_object_manager_server_set_connection (app->object_manager, connection);
965   return TRUE;
966 }
967 
968 static void
terminal_app_dbus_unregister(GApplication * application,GDBusConnection * connection,const gchar * object_path)969 terminal_app_dbus_unregister (GApplication    *application,
970                               GDBusConnection *connection,
971                               const gchar     *object_path)
972 {
973   TerminalApp *app = TERMINAL_APP (application);
974 
975   if (app->object_manager) {
976     g_dbus_object_manager_server_unexport (app->object_manager, TERMINAL_FACTORY_OBJECT_PATH);
977     g_object_unref (app->object_manager);
978     app->object_manager = nullptr;
979   }
980 
981 #ifdef ENABLE_SEARCH_PROVIDER
982   if (app->search_provider) {
983     terminal_search_provider_dbus_unregister (app->search_provider, connection, TERMINAL_SEARCH_PROVIDER_PATH);
984     g_object_unref (app->search_provider);
985     app->search_provider = nullptr;
986   }
987 #endif /* ENABLE_SEARCH_PROVIDER */
988 
989   G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_unregister (application,
990                                                                     connection,
991                                                                     object_path);
992 }
993 
994 static void
terminal_app_class_init(TerminalAppClass * klass)995 terminal_app_class_init (TerminalAppClass *klass)
996 {
997   GObjectClass *object_class = G_OBJECT_CLASS (klass);
998   GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
999 
1000   object_class->finalize = terminal_app_finalize;
1001 
1002   g_application_class->activate = terminal_app_activate;
1003   g_application_class->startup = terminal_app_startup;
1004   g_application_class->dbus_register = terminal_app_dbus_register;
1005   g_application_class->dbus_unregister = terminal_app_dbus_unregister;
1006 
1007   signals[CLIPBOARD_TARGETS_CHANGED] =
1008     g_signal_new (I_("clipboard-targets-changed"),
1009                   G_OBJECT_CLASS_TYPE (object_class),
1010                   G_SIGNAL_RUN_LAST,
1011                   G_STRUCT_OFFSET (TerminalAppClass, clipboard_targets_changed),
1012                   nullptr, nullptr,
1013                   g_cclosure_marshal_VOID__OBJECT,
1014                   G_TYPE_NONE, 1, G_TYPE_OBJECT);
1015 }
1016 
1017 /* Public API */
1018 
1019 GApplication *
terminal_app_new(const char * app_id)1020 terminal_app_new (const char *app_id)
1021 {
1022   const GApplicationFlags flags = G_APPLICATION_IS_SERVICE;
1023 
1024   return reinterpret_cast<GApplication*>
1025     (g_object_new (TERMINAL_TYPE_APP,
1026 		   "application-id", app_id ? app_id : TERMINAL_APPLICATION_ID,
1027 		   "flags", flags,
1028 		   nullptr));
1029 }
1030 
1031 TerminalScreen *
terminal_app_get_screen_by_uuid(TerminalApp * app,const char * uuid)1032 terminal_app_get_screen_by_uuid (TerminalApp *app,
1033                                  const char  *uuid)
1034 {
1035   g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr);
1036 
1037   return reinterpret_cast<TerminalScreen*>(g_hash_table_lookup (app->screen_map, uuid));
1038 }
1039 
1040 char *
terminal_app_dup_screen_object_path(TerminalApp * app,TerminalScreen * screen)1041 terminal_app_dup_screen_object_path (TerminalApp *app,
1042                                      TerminalScreen *screen)
1043 {
1044   char *object_path = g_strdup_printf (TERMINAL_RECEIVER_OBJECT_PATH_FORMAT,
1045                                        terminal_screen_get_uuid (screen));
1046   object_path = g_strdelimit (object_path,  "-", '_');
1047   g_assert (g_variant_is_object_path (object_path));
1048   return object_path;
1049 }
1050 
1051 /**
1052  * terminal_app_get_receiver_impl_by_object_path:
1053  * @app:
1054  * @object_path:
1055  *
1056  * Returns: (transfer full): the #TerminalReceiverImpl for @object_path, or %nullptr
1057  */
1058 static TerminalReceiverImpl *
terminal_app_get_receiver_impl_by_object_path(TerminalApp * app,const char * object_path)1059 terminal_app_get_receiver_impl_by_object_path (TerminalApp *app,
1060                                                const char *object_path)
1061 {
1062   gs_unref_object GDBusObject *skeleton =
1063     g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (app->object_manager),
1064                                       object_path);
1065   if (skeleton == nullptr || !TERMINAL_IS_OBJECT_SKELETON (skeleton))
1066     return nullptr;
1067 
1068   TerminalReceiverImpl *impl = nullptr;
1069   g_object_get (skeleton, "receiver", &impl, nullptr);
1070   if (impl == nullptr)
1071     return nullptr;
1072 
1073   g_assert (TERMINAL_IS_RECEIVER_IMPL (impl));
1074   return impl;
1075 }
1076 
1077 /**
1078  * terminal_app_get_screen_by_object_path:
1079  * @app:
1080  * @object_path:
1081  *
1082  * Returns: (transfer full): the #TerminalScreen for @object_path, or %nullptr
1083  */
1084 TerminalScreen *
terminal_app_get_screen_by_object_path(TerminalApp * app,const char * object_path)1085 terminal_app_get_screen_by_object_path (TerminalApp *app,
1086                                         const char *object_path)
1087 {
1088   gs_unref_object TerminalReceiverImpl *impl =
1089     terminal_app_get_receiver_impl_by_object_path (app, object_path);
1090   if (impl == nullptr)
1091     return nullptr;
1092 
1093   return terminal_receiver_impl_get_screen (impl);
1094 }
1095 
1096 void
terminal_app_register_screen(TerminalApp * app,TerminalScreen * screen)1097 terminal_app_register_screen (TerminalApp *app,
1098                               TerminalScreen *screen)
1099 {
1100   const char *uuid = terminal_screen_get_uuid (screen);
1101   g_hash_table_insert (app->screen_map, g_strdup (uuid), screen);
1102 
1103   gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen);
1104   TerminalObjectSkeleton *skeleton = terminal_object_skeleton_new (object_path);
1105 
1106   TerminalReceiverImpl *impl = terminal_receiver_impl_new (screen);
1107   terminal_object_skeleton_set_receiver (skeleton, TERMINAL_RECEIVER (impl));
1108   g_object_unref (impl);
1109 
1110   g_dbus_object_manager_server_export (app->object_manager,
1111                                        G_DBUS_OBJECT_SKELETON (skeleton));
1112 }
1113 
1114 void
terminal_app_unregister_screen(TerminalApp * app,TerminalScreen * screen)1115 terminal_app_unregister_screen (TerminalApp *app,
1116                                 TerminalScreen *screen)
1117 {
1118   const char *uuid = terminal_screen_get_uuid (screen);
1119   gboolean found = g_hash_table_remove (app->screen_map, uuid);
1120   g_warn_if_fail (found);
1121   if (!found)
1122     return; /* repeat unregistering */
1123 
1124   gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen);
1125   gs_unref_object TerminalReceiverImpl *impl =
1126     terminal_app_get_receiver_impl_by_object_path (app, object_path);
1127   g_warn_if_fail (impl != nullptr);
1128 
1129   if (impl != nullptr)
1130     terminal_receiver_impl_unset_screen (impl);
1131 
1132   g_dbus_object_manager_server_unexport (app->object_manager, object_path);
1133 }
1134 
1135 GdkAtom *
terminal_app_get_clipboard_targets(TerminalApp * app,GtkClipboard * clipboard,int * n_targets)1136 terminal_app_get_clipboard_targets (TerminalApp *app,
1137                                     GtkClipboard *clipboard,
1138                                     int *n_targets)
1139 {
1140   g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr);
1141   g_return_val_if_fail (n_targets != nullptr, nullptr);
1142 
1143   if (clipboard != app->clipboard) {
1144     *n_targets = 0;
1145     return nullptr;
1146   }
1147 
1148   *n_targets = app->n_clipboard_targets;
1149   return app->clipboard_targets;
1150 }
1151 
1152 void
terminal_app_edit_preferences(TerminalApp * app,GSettings * profile,const char * widget_name)1153 terminal_app_edit_preferences (TerminalApp     *app,
1154                                GSettings       *profile,
1155                                const char      *widget_name)
1156 {
1157   terminal_prefs_show_preferences (profile, widget_name);
1158 }
1159 
1160 /**
1161  * terminal_app_get_profiles_list:
1162  *
1163  * Returns: (transfer none): returns the singleton profiles list #TerminalSettingsList
1164  */
1165 TerminalSettingsList *
terminal_app_get_profiles_list(TerminalApp * app)1166 terminal_app_get_profiles_list (TerminalApp *app)
1167 {
1168   return app->profiles_list;
1169 }
1170 
1171 /**
1172  * terminal_app_get_menubar:
1173  * @app: a #TerminalApp
1174  *
1175  * Returns: (tranfer none): the main window menu bar as a #GMenuModel
1176  */
1177 GMenuModel *
terminal_app_get_menubar(TerminalApp * app)1178 terminal_app_get_menubar (TerminalApp *app)
1179 {
1180   return app->menubar;
1181 }
1182 
1183 /**
1184  * terminal_app_get_headermenu:
1185  * @app: a #TerminalApp
1186  *
1187  * Returns: (tranfer none): the main window headerbar menu bar as a #GMenuModel
1188  */
1189 GMenuModel *
terminal_app_get_headermenu(TerminalApp * app)1190 terminal_app_get_headermenu (TerminalApp *app)
1191 {
1192   if (app->headermenu == nullptr)
1193     terminal_app_create_headermenu (app);
1194 
1195   return app->headermenu;
1196 }
1197 
1198 /**
1199  * terminal_app_get_profilemenu:
1200  * @app: a #TerminalApp
1201  *
1202  * Returns: (tranfer none): the main window headerbar profile menu as a #GMenuModel
1203  */
1204 GMenuModel *
terminal_app_get_profilemenu(TerminalApp * app)1205 terminal_app_get_profilemenu (TerminalApp *app)
1206 {
1207   if (app->profilemenu == nullptr)
1208     terminal_app_create_profilemenu (app);
1209 
1210   return app->profilemenu;
1211 }
1212 
1213 /**
1214  * terminal_app_get_profile_section:
1215  * @app: a #TerminalApp
1216  *
1217  * Returns: (tranfer none): the main window's menubar's profiles section as a #GMenuModel
1218  */
1219 GMenuModel *
terminal_app_get_profile_section(TerminalApp * app)1220 terminal_app_get_profile_section (TerminalApp *app)
1221 {
1222   return G_MENU_MODEL (app->set_profile_menu);
1223 }
1224 
1225 /**
1226  * terminal_app_get_schema_source:
1227  * @app: a #TerminalApp
1228  *
1229  * Returns: (tranfer none): the #GSettingsSchemaSource to use for all #GSettings instances
1230  */
1231 GSettingsSchemaSource*
terminal_app_get_schema_source(TerminalApp * app)1232 terminal_app_get_schema_source(TerminalApp *app)
1233 {
1234   return app->schema_source;
1235 }
1236 
1237 /**
1238  * terminal_app_get_global_settings:
1239  * @app: a #TerminalApp
1240  *
1241  * Returns: (tranfer none): the cached #GSettings object for the org.gnome.Terminal.Preferences schema
1242  */
1243 GSettings *
terminal_app_get_global_settings(TerminalApp * app)1244 terminal_app_get_global_settings (TerminalApp *app)
1245 {
1246   return app->global_settings;
1247 }
1248 
1249 /**
1250  * terminal_app_get_desktop_interface_settings:
1251  * @app: a #TerminalApp
1252  *
1253  * Returns: (tranfer none): the cached #GSettings object for the org.gnome.interface schema
1254  */
1255 GSettings *
terminal_app_get_desktop_interface_settings(TerminalApp * app)1256 terminal_app_get_desktop_interface_settings (TerminalApp *app)
1257 {
1258   return app->desktop_interface_settings;
1259 }
1260 
1261 /**
1262  * terminal_app_get_proxy_settings:
1263  * @app: a #TerminalApp
1264  *
1265  * Returns: (tranfer none): the cached #GSettings object for the org.gnome.system.proxy schema
1266  */
1267 GSettings *
terminal_app_get_proxy_settings(TerminalApp * app)1268 terminal_app_get_proxy_settings (TerminalApp *app)
1269 {
1270   return app->system_proxy_settings;
1271 }
1272 
1273 /**
1274  * terminal_app_get_proxy_settings_for_protocol:
1275  * @app: a #TerminalApp
1276  * @protocol: a #TerminalProxyProtocol
1277  *
1278  * Returns: (tranfer none): the cached #GSettings object for the org.gnome.system.proxy.@protocol schema
1279  */
1280 GSettings*
terminal_app_get_proxy_settings_for_protocol(TerminalApp * app,TerminalProxyProtocol protocol)1281 terminal_app_get_proxy_settings_for_protocol(TerminalApp *app,
1282                                              TerminalProxyProtocol protocol)
1283 {
1284   return app->system_proxy_protocol_settings[(int)protocol];
1285 }
1286 
1287 GSettings *
terminal_app_get_gtk_debug_settings(TerminalApp * app)1288 terminal_app_get_gtk_debug_settings (TerminalApp *app)
1289 {
1290   return app->gtk_debug_settings;
1291 }
1292 
1293 /**
1294  * terminal_app_get_system_font:
1295  * @app:
1296  *
1297  * Creates a #PangoFontDescription for the system monospace font.
1298  *
1299  * Returns: (transfer full): a new #PangoFontDescription
1300  */
1301 PangoFontDescription *
terminal_app_get_system_font(TerminalApp * app)1302 terminal_app_get_system_font (TerminalApp *app)
1303 {
1304   gs_free char *font = nullptr;
1305 
1306   g_return_val_if_fail (TERMINAL_IS_APP (app), nullptr);
1307 
1308   font = g_settings_get_string (app->desktop_interface_settings, MONOSPACE_FONT_KEY_NAME);
1309 
1310   return pango_font_description_from_string (font);
1311 }
1312 
1313 /**
1314  * FIXME
1315  */
1316 GDBusObjectManagerServer *
terminal_app_get_object_manager(TerminalApp * app)1317 terminal_app_get_object_manager (TerminalApp *app)
1318 {
1319   g_warn_if_fail (app->object_manager != nullptr);
1320   return app->object_manager;
1321 }
1322 
1323 gboolean
terminal_app_get_menu_unified(TerminalApp * app)1324 terminal_app_get_menu_unified (TerminalApp *app)
1325 {
1326   g_return_val_if_fail (TERMINAL_IS_APP (app), TRUE);
1327 
1328   return app->unified_menu;
1329 }
1330 
1331 gboolean
terminal_app_get_use_headerbar(TerminalApp * app)1332 terminal_app_get_use_headerbar (TerminalApp *app)
1333 {
1334   g_return_val_if_fail (TERMINAL_IS_APP (app), FALSE);
1335 
1336   return app->use_headerbar;
1337 }
1338 
1339 gboolean
terminal_app_get_dialog_use_headerbar(TerminalApp * app)1340 terminal_app_get_dialog_use_headerbar (TerminalApp *app)
1341 {
1342   g_return_val_if_fail (TERMINAL_IS_APP (app), FALSE);
1343 
1344   gboolean dialog_use_header;
1345   g_object_get (gtk_settings_get_default (),
1346                 "gtk-dialogs-use-header", &dialog_use_header,
1347                 nullptr);
1348 
1349   return dialog_use_header && app->use_headerbar;
1350 }
1351