summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Persch <chpe@src.gnome.org>2022-11-01 20:35:28 +0100
committerChristian Persch <chpe@src.gnome.org>2022-11-01 20:35:28 +0100
commitf1814cb7ecfe6a5cb4f629dcea56db0dec10a242 (patch)
treec55db42ea586e99f3ac906a0b7131a69f77707fe /src
parent0493736250e2896568e5fa8b60b8e94c262a3470 (diff)
downloadgnome-terminal-f1814cb7ecfe6a5cb4f629dcea56db0dec10a242.tar.gz
all: Implement xdg-terminal-exec support
Add support to read and write the default terminal as determined by xdg-terminal-exec, as well as UI to make gnome-terminal the default terminal. Fixes: https://gitlab.gnome.org/GNOME/gnome-terminal/-/issues/7942
Diffstat (limited to 'src')
-rw-r--r--src/meson.build1
-rw-r--r--src/org.gnome.Terminal.gschema.xml13
-rw-r--r--src/preferences.ui33
-rw-r--r--src/terminal-app.cc120
-rw-r--r--src/terminal-app.hh8
-rw-r--r--src/terminal-debug.cc1
-rw-r--r--src/terminal-debug.hh1
-rw-r--r--src/terminal-libgsystem.hh29
-rw-r--r--src/terminal-prefs.cc25
-rw-r--r--src/terminal-schemas.hh1
-rw-r--r--src/terminal-util.cc358
-rw-r--r--src/terminal-util.hh6
-rw-r--r--src/terminal-window.cc74
13 files changed, 654 insertions, 16 deletions
diff --git a/src/meson.build b/src/meson.build
index 7347f3c0..86fa265a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -164,6 +164,7 @@ util_sources = files(
common_cxxflags = version_cxxflags + [
'-DTERMINAL_COMPILATION',
+ '-DTERM_PREFIX="@0@"'.format(gt_prefix),
'-DTERM_BINDIR="@0@"'.format(gt_prefix / gt_bindir),
'-DTERM_DATADIR="@0@"'.format(gt_prefix / gt_datadir),
'-DTERM_LIBEXECDIR="@0@"'.format(gt_prefix / gt_libexecdir),
diff --git a/src/org.gnome.Terminal.gschema.xml b/src/org.gnome.Terminal.gschema.xml
index 36a0230e..882b251a 100644
--- a/src/org.gnome.Terminal.gschema.xml
+++ b/src/org.gnome.Terminal.gschema.xml
@@ -704,6 +704,13 @@
<summary>Whether new tabs should open next to the current one or at the last position</summary>
</key>
+ <!-- Default terminal -->
+
+ <key name="always-check-default-terminal" type="b">
+ <default>true</default>
+ <summary>Always check whether GNOME Terminal is the default terminal</summary>
+ </key>
+
<!-- Note that changing the following settings will only take effect
when gnome-terminal-server is restarted.
-->
@@ -720,9 +727,9 @@
<child name="keybindings" schema="org.gnome.Terminal.Legacy.Keybindings" />
- <key name="schema-version" type="u">
- <default>3</default>
- </key>
+ <key name="schema-version" type="u">
+ <default>3</default>
+ </key>
</schema>
diff --git a/src/preferences.ui b/src/preferences.ui
index d1aca7b2..ca58c656 100644
--- a/src/preferences.ui
+++ b/src/preferences.ui
@@ -494,6 +494,39 @@
<property name="position">3</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="always-check-default-checkbutton">
+ <property name="label" translatable="yes">_Always check if default terminal</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="make-default-button">
+ <property name="label" translatable="yes">_Set as default terminal</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="halign">start</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/src/terminal-app.cc b/src/terminal-app.cc
index f672e4ab..70933a68 100644
--- a/src/terminal-app.cc
+++ b/src/terminal-app.cc
@@ -90,6 +90,8 @@
enum {
PROP_SETTINGS_BACKEND = 1,
+ PROP_IS_DEFAULT_TERMINAL,
+ PROP_ASK_DEFAULT_TERMINAL,
};
/*
@@ -145,8 +147,11 @@ struct _TerminalApp
int n_clipboard_targets;
GWeakRef prefs_process_ref;
+
#endif /* TERMINAL_SERVER */
+ gboolean ask_default;
+ gboolean xte_is_default;
gboolean unified_menu;
gboolean use_headerbar;
};
@@ -271,15 +276,8 @@ terminal_app_should_use_headerbar (TerminalApp *app)
if (set)
return use;
- const char *desktop = g_getenv ("XDG_CURRENT_DESKTOP");
- if (desktop == nullptr)
- return FALSE;
-
- char **desktops = g_strsplit (desktop, G_SEARCHPATH_SEPARATOR_S, -1);
- use = strv_contains_gnome (desktops);
- g_strfreev (desktops);
-
- return use;
+ gs_strfreev auto desktops = terminal_util_get_desktops();
+ return strv_contains_gnome(desktops);
}
static gboolean
@@ -402,11 +400,29 @@ terminal_app_theme_variant_changed_cb (GSettings *settings,
/* Submenus for New Terminal per profile, and to change profiles */
+static void
+terminal_app_check_default(TerminalApp* app)
+{
+ // 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;
+
+ // 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;
@@ -849,6 +865,8 @@ terminal_app_activate (GApplication *application)
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);
@@ -876,8 +894,6 @@ terminal_app_startup (GApplication *application)
action_entries, G_N_ELEMENTS (action_entries),
application);
- auto const app = TERMINAL_APP(application);
-
/* Figure out whether the shell shows the menubar */
gboolean shell_shows_menubar;
g_object_get (gtk_settings_get_default (),
@@ -898,6 +914,8 @@ terminal_app_startup (GApplication *application)
#endif /* TERMINAL_SERVER */
+ terminal_app_check_default(app);
+
_terminal_debug_print (TERMINAL_DEBUG_SERVER, "Startup complete\n");
}
@@ -1070,6 +1088,30 @@ terminal_app_finalize (GObject *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,
@@ -1081,6 +1123,10 @@ terminal_app_set_property(GObject* object,
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;
@@ -1169,6 +1215,7 @@ terminal_app_class_init (TerminalAppClass *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
@@ -1176,10 +1223,26 @@ terminal_app_class_init (TerminalAppClass *klass)
PROP_SETTINGS_BACKEND,
g_param_spec_object("settings-backend", nullptr, nullptr,
G_TYPE_SETTINGS_BACKEND,
- GParamFlags(G_PARAM_WRITABLE |
+ 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
@@ -1573,3 +1636,34 @@ terminal_app_get_dialog_use_headerbar (TerminalApp *app)
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");
+}
diff --git a/src/terminal-app.hh b/src/terminal-app.hh
index 0437a882..94098649 100644
--- a/src/terminal-app.hh
+++ b/src/terminal-app.hh
@@ -129,6 +129,14 @@ GSettings *terminal_app_get_gtk_debug_settings (TerminalApp *app);
PangoFontDescription *terminal_app_get_system_font (TerminalApp *app);
+gboolean terminal_app_is_default_terminal(TerminalApp* app);
+
+gboolean terminal_app_get_ask_default_terminal(TerminalApp* app);
+
+void terminal_app_unset_ask_default_terminal(TerminalApp* app);
+
+void terminal_app_make_default_terminal(TerminalApp* app);
+
G_END_DECLS
#endif /* !TERMINAL_APP_H */
diff --git a/src/terminal-debug.cc b/src/terminal-debug.cc
index 61814819..bf2db7e9 100644
--- a/src/terminal-debug.cc
+++ b/src/terminal-debug.cc
@@ -39,6 +39,7 @@ _terminal_debug_init(void)
{ "settings-list", TERMINAL_DEBUG_SETTINGS_LIST },
{ "search", TERMINAL_DEBUG_SEARCH },
{ "bridge", TERMINAL_DEBUG_BRIDGE },
+ { "default", TERMINAL_DEBUG_DEFAULT },
};
_terminal_debug_flags = TerminalDebugFlags(g_parse_debug_string (g_getenv ("GNOME_TERMINAL_DEBUG"),
diff --git a/src/terminal-debug.hh b/src/terminal-debug.hh
index c1a4113c..fedc9e4d 100644
--- a/src/terminal-debug.hh
+++ b/src/terminal-debug.hh
@@ -36,6 +36,7 @@ typedef enum {
TERMINAL_DEBUG_SETTINGS_LIST = 1 << 8,
TERMINAL_DEBUG_SEARCH = 1 << 9,
TERMINAL_DEBUG_BRIDGE = 1 << 10,
+ TERMINAL_DEBUG_DEFAULT = 1 << 11,
} TerminalDebugFlags;
void _terminal_debug_init(void);
diff --git a/src/terminal-libgsystem.hh b/src/terminal-libgsystem.hh
index e13c5a9c..86e3c1fd 100644
--- a/src/terminal-libgsystem.hh
+++ b/src/terminal-libgsystem.hh
@@ -52,6 +52,7 @@ GS_DEFINE_CLEANUP_FUNCTION0(GArray*, gs_local_array_unref, g_array_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GBytes*, gs_local_bytes_unref, g_bytes_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GChecksum*, gs_local_checksum_free, g_checksum_free)
GS_DEFINE_CLEANUP_FUNCTION0(GDateTime*, gs_local_date_time_unref, g_date_time_unref)
+GS_DEFINE_CLEANUP_FUNCTION0(GDir*, gs_local_dir_close, g_dir_close)
GS_DEFINE_CLEANUP_FUNCTION0(GError*, gs_local_free_error, g_error_free)
GS_DEFINE_CLEANUP_FUNCTION0(GHashTable*, gs_local_hashtable_unref, g_hash_table_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, gs_local_key_file_unref, g_key_file_unref)
@@ -274,6 +275,34 @@ static inline void gs_local_gstring_free (void *v) \
*/
#define gs_free_option_context __attribute__ ((cleanup(gs_local_option_context_free)))
+/**
+ * gs_close_dir:
+ *
+ * Call g_dir_close() on a variable location when it goes out of
+ * scope.
+
+ */
+#define gs_close_dir __attribute__ ((cleanup(gs_local_dir_close)))
+
+static inline void gs_local_fd_close (void *v)
+{
+ auto fd = *reinterpret_cast<int*>(v);
+ if (fd != -1) {
+ auto const errsv = errno;
+ close(fd);
+ errno = errsv;
+ }
+}
+
+/**
+ * gs_free_close:
+ *
+ * Call close() on a variable location when it goes out of
+ * scope.
+
+ */
+#define gs_close_fd __attribute__ ((cleanup(gs_local_fd_close)))
+
G_END_DECLS
#endif
diff --git a/src/terminal-prefs.cc b/src/terminal-prefs.cc
index 1163f739..437c58af 100644
--- a/src/terminal-prefs.cc
+++ b/src/terminal-prefs.cc
@@ -714,6 +714,13 @@ prefs_dialog_destroy_cb (GtkWidget *widget,
g_free (data);
}
+static void
+make_default_button_clicked_cb(GtkWidget* button,
+ PrefData* data)
+{
+ terminal_app_make_default_terminal(terminal_app_get());
+}
+
void
terminal_prefs_show_preferences(GSettings* profile,
char const* widget_name,
@@ -729,6 +736,7 @@ terminal_prefs_show_preferences(GSettings* profile,
GtkWidget *new_tab_position_combo;
GtkWidget *close_button, *help_button;
GtkWidget *content_box, *general_frame, *keybindings_frame;
+ GtkWidget *always_check_default_button, *make_default_button;
GSettings *settings;
const GActionEntry action_entries[] = {
@@ -764,6 +772,8 @@ terminal_prefs_show_preferences(GSettings* profile,
"disable-shortcuts-checkbutton", &disable_shortcuts_button,
"disable-menu-accel-checkbutton", &disable_menu_accel_button,
"new-tab-position-combobox", &new_tab_position_combo,
+ "always-check-default-checkbutton", &always_check_default_button,
+ "make-default-button", &make_default_button,
"accelerators-treeview", &tree_view,
"the-stack", &data->stack,
"the-listbox", &data->listbox,
@@ -856,6 +866,21 @@ terminal_prefs_show_preferences(GSettings* profile,
"active",
GSettingsBindFlags(G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET));
+ g_settings_bind(settings,
+ TERMINAL_SETTING_ALWAYS_CHECK_DEFAULT_KEY,
+ always_check_default_button,
+ "active",
+ GSettingsBindFlags(G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET));
+
+ g_signal_connect(make_default_button, "clicked",
+ G_CALLBACK(make_default_button_clicked_cb), data);
+
+ g_object_bind_property(app, "is-default-terminal",
+ make_default_button, "sensitive",
+ GBindingFlags(G_BINDING_DEFAULT |
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN));
+
/* Shortcuts page */
g_settings_bind (settings,
diff --git a/src/terminal-schemas.hh b/src/terminal-schemas.hh
index b146ba82..61f1305e 100644
--- a/src/terminal-schemas.hh
+++ b/src/terminal-schemas.hh
@@ -93,6 +93,7 @@ G_BEGIN_DECLS
#define TERMINAL_SETTING_TAB_POSITION_KEY "tab-position"
#define TERMINAL_SETTING_THEME_VARIANT_KEY "theme-variant"
#define TERMINAL_SETTING_UNIFIED_MENU_KEY "unified-menu"
+#define TERMINAL_SETTING_ALWAYS_CHECK_DEFAULT_KEY "always-check-default-terminal"
#define TERMINAL_SETTINGS_LIST_LIST_KEY "list"
#define TERMINAL_SETTINGS_LIST_DEFAULT_KEY "default"
diff --git a/src/terminal-util.cc b/src/terminal-util.cc
index b430efca..fcdd939a 100644
--- a/src/terminal-util.cc
+++ b/src/terminal-util.cc
@@ -21,10 +21,12 @@
#include "config.h"
+#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <langinfo.h>
#include <errno.h>
@@ -40,6 +42,8 @@
#include "terminal-accels.hh"
#include "terminal-app.hh"
#include "terminal-client-utils.hh"
+#include "terminal-debug.hh"
+#include "terminal-defines.hh"
#include "terminal-intl.hh"
#include "terminal-util.hh"
#include "terminal-version.hh"
@@ -1570,3 +1574,357 @@ terminal_util_check_envv(char const* const* strv)
return TRUE;
}
+
+char**
+terminal_util_get_desktops(void)
+{
+ auto const desktop = g_getenv("XDG_CURRENT_DESKTOP");
+ if (!desktop)
+ return nullptr;
+
+ return g_strsplit(desktop, G_SEARCHPATH_SEPARATOR_S, -1);
+}
+
+#define XTE_CONFIG_DIRNAME "xdg-terminals"
+#define XTE_CONFIG_FILENAME "xdg-terminals.list"
+
+#define NEWLINE '\n'
+#define DOT_DESKTOP ".desktop"
+#define TERMINAL_DESKTOP_FILENAME TERMINAL_APPLICATION_ID DOT_DESKTOP
+
+static bool
+xte_data_check_one(char const* file,
+ bool full)
+{
+ if (!g_file_test(file, G_FILE_TEST_EXISTS)) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Desktop file \"%s\" does not exist.\n",
+ file);
+ return false;
+ }
+
+ if (!full)
+ return true;
+
+ gs_free_error GError* error = nullptr;
+ gs_unref_key_file auto kf = g_key_file_new();
+ if (!g_key_file_load_from_file(kf,
+ file,
+ GKeyFileFlags(G_KEY_FILE_NONE),
+ &error)) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Failed to load \"%s\" as keyfile: %s\n",
+ file, error->message);
+
+ return false;
+ }
+
+ if (!g_key_file_has_group(kf, G_KEY_FILE_DESKTOP_GROUP)) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Keyfile file \"%s\" is not a desktop file.\n",
+ file);
+ return false;
+ }
+
+ // As per the XDG desktop entry spec, the TryExec key contains the name
+ // of an executable that can be used to determine if the programme is
+ // actually present.
+ gs_free auto try_exec = g_key_file_get_string(kf,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
+ nullptr);
+ if (!try_exec) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Desktop file \"%s\" has no TryExec field.\n",
+ file);
+
+ return false;
+ }
+
+ gs_free auto exec_path = g_find_program_in_path(try_exec);
+ auto const exists = exec_path != nullptr;
+
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Desktop file \"%s\" is %sinstalled.\n",
+ file, exists ? "" : "not ");
+
+ return exists;
+}
+
+static bool
+xte_data_check(char const* name,
+ bool full)
+{
+ gs_free auto user_path = g_build_filename(g_get_user_data_dir(),
+ XTE_CONFIG_DIRNAME,
+ name,
+ nullptr);
+ if (xte_data_check_one(user_path, full))
+ return true;
+
+ gs_free auto local_path = g_build_filename(TERM_PREFIX, "local", "share",
+ XTE_CONFIG_DIRNAME,
+ name,
+ nullptr);
+ if (xte_data_check_one(local_path, full))
+ return true;
+
+ gs_free auto sys_path = g_build_filename(TERM_DATADIR,
+ XTE_CONFIG_DIRNAME,
+ name,
+ nullptr);
+ if (xte_data_check_one(sys_path, full))
+ return true;
+
+ return false;
+}
+
+static bool
+xte_data_ensure(void)
+{
+ if (xte_data_check(TERMINAL_DESKTOP_FILENAME, false))
+ return true;
+
+ // If we get here, there wasn't a desktop file in any of the paths. Install
+ // a symlink to the system-installed desktop file into the user path.
+
+ gs_free auto user_dir = g_build_filename(g_get_user_data_dir(),
+ XTE_CONFIG_DIRNAME,
+ nullptr);
+ if (g_mkdir_with_parents(user_dir, 0700) != 0 &&
+ errno != EEXIST) {
+ auto const errsv = errno;
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Failed to create directory %s: %s\n",
+ user_dir, g_strerror(errsv));
+ return false;
+ }
+
+ gs_free auto link_path = g_build_filename(user_dir,
+ TERMINAL_DESKTOP_FILENAME,
+ nullptr);
+ gs_free auto target_path = g_build_filename(TERM_DATADIR,
+ "applications",
+ TERMINAL_DESKTOP_FILENAME,
+ nullptr);
+
+ auto const r = symlink(target_path, link_path);
+ if (r != -1) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Installed symlink %s -> %s\n",
+ link_path, target_path);
+
+ } else {
+ auto const errsv = errno;
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Failed to create symlink %s: %s\n",
+ link_path, g_strerror(errsv));
+ }
+
+ return r != -1;
+}
+
+static char**
+xte_config_read(char const* path,
+ GError** error)
+{
+ gs_close_fd auto fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (fd == -1)
+ return nullptr;
+
+ // This is a small config file, so shouldn't be any bigger than this.
+ // If it is bigger, we'll discard the rest. That's why we're not using
+ // g_file_get_contents() here.
+ char buf[8192];
+ auto r = ssize_t{};
+ do {
+ r = read(fd, buf, sizeof(buf) - 1); // reserve one byte in buf
+ } while (r == -1 && errno == EINTR);
+ if (r < 0)
+ return nullptr;
+
+ buf[r] = '\0'; // NUL terminator; note that r < sizeof(buf)
+
+ auto lines = g_strsplit_set(buf, "\r\n", -1);
+ if (!lines)
+ return nullptr;
+
+ for (auto i = 0; lines[i]; ++i)
+ lines[i] = g_strstrip(lines[i]);
+
+ return lines;
+}
+
+static bool
+xte_config_rewrite(char const* path)
+{
+ gs_free_gstring auto str = g_string_sized_new(1024);
+ g_string_append(str, TERMINAL_DESKTOP_FILENAME);
+ g_string_append_c(str, NEWLINE);
+
+ gs_strfreev auto lines = xte_config_read(path, nullptr);
+ if (lines) {
+ for (auto i = 0; lines[i]; ++i) {
+ if (lines[i][0] == '\0')
+ continue;
+ if (strcmp(lines[i], TERMINAL_DESKTOP_FILENAME) == 0)
+ continue;
+
+ g_string_append(str, lines[i]);
+ g_string_append_c(str, NEWLINE);
+ }
+ }
+
+ gs_free_error GError* error = nullptr;
+ auto const r = g_file_set_contents(path, str->str, str->len, &error);
+ if (!r) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Failed to rewrite XTE config %s: %s\n",
+ path, error->message);
+ }
+
+ return r;
+}
+
+static void
+xte_config_rewrite(void)
+{
+ auto const user_dir = g_get_user_config_dir();
+ if (g_mkdir_with_parents(user_dir, 0700) != 0 &&
+ errno != EEXIST) {
+ auto const errsv = errno;
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Failed to create directory %s: %s\n",
+ user_dir, g_strerror(errsv));
+ // Nothing to do if we can't even create the directory
+ return;
+ }
+
+ // Install as default for all current desktops
+ gs_strfreev auto desktops = terminal_util_get_desktops();
+ if (desktops) {
+ for (auto i = 0; desktops[i]; ++i) {
+ gs_free auto name = g_strdup_printf("%s-" XTE_CONFIG_FILENAME,
+ desktops[i]);
+ gs_free auto path = g_build_filename(user_dir, name, nullptr);
+
+ xte_config_rewrite(path);
+ }
+ }
+
+ // Install as non-desktop specific default too
+ gs_free auto path = g_build_filename(user_dir, XTE_CONFIG_FILENAME, nullptr);
+ xte_config_rewrite(path);
+}
+
+static bool
+xte_config_is_foreign(char const* name)
+{
+ return !g_str_equal(name, TERMINAL_DESKTOP_FILENAME);
+}
+
+static char*
+xte_config_get_default(char const* path)
+{
+ gs_strfreev auto lines = xte_config_read(path, nullptr);
+ if (!lines)
+ return nullptr;
+
+ // A terminal is the default if it's the first non-comment line in the file
+ for (auto i = 0; lines[i]; ++i) {
+ auto const line = lines[i];
+ if (!line[0] || line[0] == '#')
+ continue;
+
+ // If a foreign terminal is default, check whether it is actually installed.
+ // (We always ensure our own desktop file exists.)
+ if (xte_config_is_foreign(line) &&
+ !xte_data_check(line, true)) {
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "Default entry \"%s\" from config \"%s\" is not installed, skipping.\n",
+ line, path);
+ return nullptr;
+ }
+
+ return g_strdup(line);
+ }
+
+ return nullptr;
+}
+
+static char*
+xte_config_get_default(void)
+{
+ auto const user_dir = g_get_user_config_dir();
+ gs_strfreev auto desktops = terminal_util_get_desktops();
+ if (desktops) {
+ for (auto i = 0; desktops[i]; ++i) {
+ gs_free auto name = g_strdup_printf("%s-" XTE_CONFIG_FILENAME,
+ desktops[i]);
+ gs_free auto path = g_build_filename(user_dir, name, nullptr);
+ if (auto term = xte_config_get_default(path))
+ return term;
+ }
+ }
+
+ gs_free auto user_path = g_build_filename(user_dir, XTE_CONFIG_FILENAME, nullptr);
+ if (auto term = xte_config_get_default(user_path))
+ return term;
+
+ if (desktops) {
+ for (auto i = 0; desktops[i]; ++i) {
+ gs_free auto name = g_strdup_printf("%s-" XTE_CONFIG_FILENAME,
+ desktops[i]);
+ gs_free auto path = g_build_filename("/etc/xdg", name, nullptr);
+ if (auto term = xte_config_get_default(path))
+ return term;
+ }
+ }
+
+ gs_free auto sys_path = g_build_filename("/etc/xdg", XTE_CONFIG_FILENAME, nullptr);
+ if (auto term = xte_config_get_default(sys_path))
+ return term;
+
+ return nullptr;
+}
+
+static bool
+xte_config_is_default(bool* set = nullptr)
+{
+ gs_free auto term = xte_config_get_default();
+
+ auto const is_default = term && g_str_equal(term, TERMINAL_DESKTOP_FILENAME);
+ if (set)
+ *set = term != nullptr;
+ return is_default;
+}
+
+gboolean
+terminal_util_is_default_terminal(void)
+{
+ auto set = false;
+ auto const is_default = xte_config_is_default(&set);
+ if (!set) {
+ // No terminal is default yet, so we claim the default.
+ _terminal_debug_print(TERMINAL_DEBUG_DEFAULT,
+ "No default terminal, claiming default.\n");
+ return terminal_util_make_default_terminal();
+ }
+
+ if (is_default) {
+ // If we're the default terminal, ensure our desktop file is installed
+ // in the right location.
+ xte_data_ensure();
+ }
+
+ return is_default;
+}
+
+gboolean
+terminal_util_make_default_terminal(void)
+{
+ xte_config_rewrite();
+ xte_data_ensure();
+
+ return xte_config_is_default();
+}
diff --git a/src/terminal-util.hh b/src/terminal-util.hh
index d980725e..3770c854 100644
--- a/src/terminal-util.hh
+++ b/src/terminal-util.hh
@@ -109,6 +109,12 @@ char *terminal_util_find_program_in_path (const char *path,
gboolean terminal_util_check_envv(char const* const* strv);
+char** terminal_util_get_desktops(void);
+
+gboolean terminal_util_is_default_terminal(void);
+
+gboolean terminal_util_make_default_terminal(void);
+
G_END_DECLS
#endif /* TERMINAL_UTIL_H */
diff --git a/src/terminal-window.cc b/src/terminal-window.cc
index 78fcbc1a..36eb7ce7 100644
--- a/src/terminal-window.cc
+++ b/src/terminal-window.cc
@@ -56,6 +56,7 @@ struct _TerminalWindowPrivate
GtkWidget *menubar;
TerminalMdiContainer *mdi_container;
GtkWidget *main_vbox;
+ GtkWidget* ask_default_infobar;
TerminalScreen *active_screen;
/* Size of a character cell in pixels */
@@ -1938,6 +1939,44 @@ terminal_window_fill_notebook_action_box (TerminalWindow *window,
gtk_widget_show (tabs_menu_button);
}
+static void
+window_hide_ask_default_terminal(TerminalWindow* window)
+{
+ auto const priv = window->priv;
+
+ if (!priv->ask_default_infobar ||
+ !gtk_widget_get_visible(priv->ask_default_infobar))
+ return;
+
+ gtk_widget_hide(priv->ask_default_infobar);
+ terminal_window_update_size(window);
+}
+
+static void
+window_sync_ask_default_terminal_cb(TerminalApp* app,
+ GParamSpec* pspect,
+ TerminalWindow* window)
+{
+ window_hide_ask_default_terminal(window);
+}
+
+static void
+default_infobar_response_cb(GtkInfoBar* infobar,
+ int response,
+ TerminalWindow* window)
+{
+ auto const app = terminal_app_get();
+
+ if (response == GTK_RESPONSE_YES) {
+ terminal_app_make_default_terminal(app);
+ terminal_app_unset_ask_default_terminal(app);
+ } else if (response == GTK_RESPONSE_NO) {
+ terminal_app_unset_ask_default_terminal(app);
+ } else { // GTK_RESPONSE_CLOSE
+ window_hide_ask_default_terminal(window);
+ }
+}
+
/*****************************************/
static void
@@ -2243,6 +2282,31 @@ terminal_window_init (TerminalWindow *window)
priv->use_default_menubar_visibility = !use_headerbar;
}
+ /* Add "Set as default terminal" infobar */
+ if (terminal_app_get_ask_default_terminal(app)) {
+ auto const infobar = priv->ask_default_infobar = gtk_info_bar_new();
+ gtk_info_bar_set_show_close_button(GTK_INFO_BAR(infobar), true);
+ gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), GTK_MESSAGE_QUESTION);
+
+ auto const question = gtk_label_new (_("Set GNOME Terminal as your default terminal?"));
+ gtk_label_set_line_wrap(GTK_LABEL(question), true);
+ auto const box = gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar));
+ gtk_container_add(GTK_CONTAINER(box), question);
+ gtk_widget_show(question);
+
+ gtk_info_bar_add_button(GTK_INFO_BAR(infobar), _("_Yes"), GTK_RESPONSE_YES);
+ gtk_info_bar_add_button(GTK_INFO_BAR(infobar), _("_No"), GTK_RESPONSE_NO);
+
+ g_signal_connect (infobar, "response",
+ G_CALLBACK(default_infobar_response_cb), window);
+
+ gtk_box_pack_start(GTK_BOX(priv->main_vbox), infobar, false, true, 0);
+
+ gtk_widget_show(infobar);
+ g_signal_connect(app, "notify::ask-default-terminal",
+ G_CALLBACK(window_sync_ask_default_terminal_cb), window);
+ }
+
/* Maybe make Inspector available */
action = lookup_action (window, "inspector");
gtk_debug_settings = terminal_app_get_gtk_debug_settings (app);
@@ -2340,6 +2404,13 @@ terminal_window_dispose (GObject *object)
priv->clipboard = nullptr;
}
+ if (priv->ask_default_infobar) {
+ g_signal_handlers_disconnect_by_func(app,
+ (void*)window_sync_ask_default_terminal_cb,
+ window);
+ priv->ask_default_infobar = nullptr;
+ }
+
remove_popup_info (window);
if (priv->search_popover != nullptr)
@@ -2648,6 +2719,9 @@ terminal_window_update_size (TerminalWindow *window)
return;
}
+ if (!priv->active_screen)
+ return;
+
/* be sure our geometry is up-to-date */
terminal_window_update_geometry (window);