/*
* Copyright © 2008, 2010, 2011, 2021, 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 .
*/
#include "config.h"
#include
#include
#include
#include
#include "terminal-app.hh"
#include "terminal-debug.hh"
#include "terminal-i18n.hh"
#include "terminal-defines.hh"
#include "terminal-libgsystem.hh"
#include "terminal-settings-bridge-backend.hh"
#include "terminal-settings-bridge-generated.h"
// Reduce the default timeout to something that should still always work,
// but not hang the process for long periods of time if something does
// go wrong. See issue #7935.
#define BRIDGE_TIMEOUT 5000 /* ms */
static char* arg_profile_uuid = nullptr;
static char* arg_hint = nullptr;
static int arg_bus_fd = -1;
static int arg_timestamp = -1;
static const GOptionEntry options[] = {
{"profile", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &arg_profile_uuid, "Profile", "UUID"},
{"hint", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &arg_hint, "Hint", "HINT"},
{"bus-fd", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &arg_bus_fd, "Bus FD", "FD"},
{"timestamp", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &arg_timestamp, "Timestamp", "VALUE"},
{nullptr}
};
static GSettings*
profile_from_uuid(TerminalApp* app,
char const* uuid_str) noexcept
{
if (!uuid_str)
return nullptr;
auto const profiles_list = terminal_app_get_profiles_list(app);
GSettings* profile = nullptr;
if (g_str_equal(uuid_str, "default"))
profile = terminal_settings_list_ref_default_child(profiles_list);
else if (terminal_settings_list_valid_uuid(uuid_str))
profile = terminal_settings_list_ref_child(profiles_list, uuid_str);
return profile;
}
static void
preferences_cb(GSimpleAction* action,
GVariant* parameter,
void* user_data)
{
auto const app = terminal_app_get();
gs_free char* uuid_str = nullptr;
g_variant_lookup(parameter, "profile", "s", &uuid_str);
gs_unref_object auto profile = profile_from_uuid(app, uuid_str);
gs_free char* hint_str = nullptr;
g_variant_lookup(parameter, "hint", "s", &hint_str);
guint32 ts = 0;
g_variant_lookup(parameter, "timestamp", "u", &ts);
terminal_app_edit_preferences(app, profile, hint_str, ts);
}
static void
connection_closed_cb(GDBusConnection* connection,
gboolean peer_vanished,
GError* error,
void* user_data)
{
auto ptr = reinterpret_cast(user_data);
if (error)
g_printerr("D-Bus connection closed: %s\n", error->message);
// As per glib docs, unref the connection at this point
g_object_unref(g_steal_pointer(ptr));
// Exit cleanly
auto const app = g_application_get_default();
if (app)
g_application_quit(app);
}
int
main(int argc,
char* argv[])
{
// Sanitise environment
g_unsetenv("CHARSET");
g_unsetenv("DBUS_STARTER_BUS_TYPE");
// Not interested in silly debug spew polluting the journal, bug #749195
if (g_getenv("G_ENABLE_DIAGNOSTIC") == nullptr)
g_setenv("G_ENABLE_DIAGNOSTIC", "0", true);
// Maybe: g_setenv("GSETTINGS_BACKEND", "bridge", true);
if (setlocale(LC_ALL, "") == nullptr) {
g_printerr("Locale not supported.\n");
return _EXIT_FAILURE_UNSUPPORTED_LOCALE;
}
terminal_i18n_init(true);
char const* charset = nullptr;
if (!g_get_charset(&charset)) {
g_printerr("Non UTF-8 locale (%s) is not supported!\n", charset);
return _EXIT_FAILURE_NO_UTF8;
}
_terminal_debug_init();
auto const home_dir = g_get_home_dir();
if (home_dir == nullptr || chdir(home_dir) < 0)
(void) chdir("/");
g_set_prgname("gnome-terminal-preferences");
g_set_application_name(_("Preferences"));
gs_free_error GError *error = nullptr;
if (!gtk_init_with_args(&argc, &argv, nullptr, options, nullptr, &error)) {
g_printerr("Failed to parse arguments: %s\n", error->message);
return _EXIT_FAILURE_GTK_INIT;
}
gs_unref_object GDBusConnection* connection = nullptr;
gs_unref_object GSettingsBackend* backend = nullptr;
gs_unref_object GSimpleActionGroup* action_group = nullptr;
auto export_id = 0u;
if (arg_bus_fd != -1) {
gs_unref_object auto socket = g_socket_new_from_fd(arg_bus_fd, &error);
if (!socket) {
g_printerr("Failed to create bridge socket: %s\n", error->message);
close(arg_bus_fd);
return EXIT_FAILURE;
}
gs_unref_object auto sockconn =
g_socket_connection_factory_create_connection(socket);
if (!G_IS_IO_STREAM(sockconn)) {
g_printerr("Bridge socket has incorrect type %s\n", G_OBJECT_TYPE_NAME(sockconn));
return EXIT_FAILURE;
}
connection =
g_dbus_connection_new_sync(G_IO_STREAM(sockconn),
nullptr, // guid=nullptr for the client
GDBusConnectionFlags(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
nullptr, // auth observer,
nullptr, // cancellable,
&error);
if (!connection) {
g_printerr("Failed to create bus: %s\n", error->message);
return EXIT_FAILURE;
}
GActionEntry const action_entries[] = {
{ "preferences", preferences_cb, "a{sv}", nullptr, nullptr },
};
action_group = g_simple_action_group_new();
g_action_map_add_action_entries(G_ACTION_MAP(action_group),
action_entries, G_N_ELEMENTS(action_entries),
nullptr);
export_id = g_dbus_connection_export_action_group(connection,
TERMINAL_PREFERENCES_OBJECT_PATH,
G_ACTION_GROUP(action_group),
&error);
if (export_id == 0) {
g_printerr("Failed to export actions: %s\n", error->message);
return EXIT_FAILURE;
}
g_dbus_connection_start_message_processing(connection);
gs_unref_object auto bridge =
terminal_settings_bridge_proxy_new_sync
(connection,
GDBusProxyFlags(
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
nullptr, // no name
TERMINAL_SETTINGS_BRIDGE_OBJECT_PATH,
nullptr, // cancellable
&error);
if (!bridge) {
g_printerr("Failed to create settings bridge proxy: %s\n", error->message);
return EXIT_FAILURE;
}
if (!_terminal_debug_on(TERMINAL_DEBUG_BRIDGE)) {
g_dbus_proxy_set_default_timeout(G_DBUS_PROXY(bridge), BRIDGE_TIMEOUT);
}
backend = terminal_settings_bridge_backend_new(bridge);
g_dbus_connection_set_exit_on_close(connection, false);
g_signal_connect(connection, "closed", G_CALLBACK(connection_closed_cb), &connection);
}
gs_unref_object auto app =
terminal_app_new(TERMINAL_PREFERENCES_APPLICATION_ID,
GApplicationFlags(G_APPLICATION_NON_UNIQUE |
G_APPLICATION_IS_SERVICE),
backend);
// Need to startup the application now, otherwise we can't use
// gtk_application_add_window() before g_application_run() below.
// This should always succeed.
if (!g_application_register(G_APPLICATION(app), nullptr, &error)) {
g_printerr("Failed to register application: %s\n", error->message);
return EXIT_FAILURE;
}
// If started from gnome-terminal-server, the "preferences" action
// will be activated to actually show the preferences dialogue. However
// if started directly, need to show the dialogue right now.
if (!connection) {
gs_unref_object GSettings* profile = profile_from_uuid(TERMINAL_APP(app),
arg_profile_uuid);
if (arg_profile_uuid && !profile) {
g_printerr("No profile with UUID \"%s\": %s\n", arg_profile_uuid, error->message);
return EXIT_FAILURE;
}
terminal_app_edit_preferences(TERMINAL_APP(app),
profile,
arg_hint,
unsigned(arg_timestamp));
}
auto const r = g_application_run(app, 0, nullptr);
if (connection && export_id != 0) {
g_dbus_connection_unexport_action_group(connection, export_id);
export_id = 0;
}
if (connection &&
!g_dbus_connection_flush_sync(connection, nullptr, &error)) {
g_printerr("Failed to flush D-Bus connection: %s\n", error->message);
}
return r;
}