diff options
Diffstat (limited to 'src/terminal-client-utils.cc')
-rw-r--r-- | src/terminal-client-utils.cc | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/terminal-client-utils.cc b/src/terminal-client-utils.cc new file mode 100644 index 0000000..cea473e --- /dev/null +++ b/src/terminal-client-utils.cc @@ -0,0 +1,484 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2011, 2013 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 <unistd.h> +#include "terminal-client-utils.hh" +#include "terminal-debug.hh" +#include "terminal-defines.hh" +#include "terminal-libgsystem.hh" + +#ifdef TERMINAL_PREFERENCES +#include "terminal-debug.hh" +#endif + +#include <string.h> + +#include <gio/gio.h> + +#ifndef TERMINAL_NAUTILUS +#include <gdk/gdk.h> +#if defined(TERMINAL_COMPILATION) && defined(GDK_WINDOWING_X11) +#include <gdk/gdkx.h> +#endif +#endif + +#ifdef ENABLE_DEBUG + +static char* +get_binary_path_if_uninstalled(char const* install_dir) noexcept +{ +#ifdef __linux__ + char buf[1024]; + auto const r = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (r < 0 || r >= ssize_t(sizeof(buf))) + return nullptr; + + buf[r] = '\0'; // nul terminate + + gs_free auto path = g_path_get_dirname(buf); + if (!path) + return nullptr; + + if (g_str_equal(path, install_dir)) + return nullptr; + + return reinterpret_cast<char*>(g_steal_pointer(&path)); +#else + return nullptr; +#endif /* __linux__ */ +} + +static char* +get_path_if_uninstalled(char const* exe_install_dir, + char const* file_name, + GFileTest tests) +{ + gs_free auto path = get_binary_path_if_uninstalled(exe_install_dir); + if (!path) + return nullptr; + + gs_free auto file = g_build_filename(path, file_name, nullptr); + if (!g_file_test(file, GFileTest(tests | G_FILE_TEST_EXISTS))) + return nullptr; + + return reinterpret_cast<char*>(g_steal_pointer(&path)); +} + +#endif /* ENABLE_DEBUG */ + +/** + * terminal_client_find_file_uninstalled: + * @exe_install_dir: the directory where the current exe is installed + * @file_install_dir: the directory where the file to locate is installed + * @file_name: the name of the file to locate + * @tests: extra tests from #GFileTest + * + * Tries to locate the directory that contains @file_name in a build directory, + * and returns the directory. If @file_name is not found, returns the + * installed location for it. + */ +char* +terminal_client_get_directory_uninstalled(char const* exe_install_dir, + char const *file_install_dir, + char const* file_name, + GFileTest tests) +{ +#ifdef ENABLE_DEBUG + auto path = get_path_if_uninstalled(exe_install_dir, file_name, tests); + if (path) + return path; +#endif /* ENABLE_DEBUG */ + + return g_strdup(file_install_dir); +} + +/** + * terminal_client_find_file_uninstalled: + * @exe_install_dir: the directory where the current exe is installed + * @file_install_dir: the directory where the file to locate is installed + * @file_name: the name of the file to locate + * @tests: extra tests from #GFileTest + * + * Tries to locate the file @file_name in a build directory, and + * returns a full path to it. If @file_name is not found, returns the + * installed location for it. + */ +char* +terminal_client_get_file_uninstalled(char const* exe_install_dir, + char const *file_install_dir, + char const* file_name, + GFileTest tests) +{ +#ifdef ENABLE_DEBUG + gs_free auto path = get_path_if_uninstalled(exe_install_dir, file_name, tests); + if (path) + return g_build_filename(path, file_name, nullptr); +#endif /* ENABLE_DEBUG */ + + return g_build_filename(file_install_dir, file_name, nullptr); +} + +/** + * terminal_client_append_create_instance_options: + * @builder: a #GVariantBuilder of #GVariantType "a{sv}" + * @display: (array element-type=guint8): + * @startup_id: + * @activation_token: + * @geometry: + * @role: + * @profile: + * @title: + * @maximise_window: + * @fullscreen_window: + * + * Appends common options to @builder. + */ +void +terminal_client_append_create_instance_options (GVariantBuilder *builder, + const char *display_name, + const char *startup_id, + const char *activation_token, + const char *geometry, + const char *role, + const char *profile, + const char *encoding, + const char *title, + gboolean active, + gboolean maximise_window, + gboolean fullscreen_window) +{ + /* Bytestring options */ + if (display_name != nullptr) + g_variant_builder_add (builder, "{sv}", + "display", g_variant_new_bytestring (display_name)); + if (startup_id) + g_variant_builder_add (builder, "{sv}", + "desktop-startup-id", g_variant_new_bytestring (startup_id)); + if (activation_token) + g_variant_builder_add (builder, "{sv}", + "activation-token", g_variant_new_string (activation_token)); + + /* String options */ + if (profile) + g_variant_builder_add (builder, "{sv}", + "profile", g_variant_new_string (profile)); + if (encoding) + g_variant_builder_add (builder, "{sv}", + "encoding", g_variant_new_string (encoding)); + if (title) + g_variant_builder_add (builder, "{sv}", + "title", g_variant_new_string (title)); + if (geometry) + g_variant_builder_add (builder, "{sv}", + "geometry", g_variant_new_string (geometry)); + if (role) + g_variant_builder_add (builder, "{sv}", + "role", g_variant_new_string (role)); + + /* Boolean options */ + if (active) + g_variant_builder_add (builder, "{sv}", + "active", g_variant_new_boolean (active)); + + if (maximise_window) + g_variant_builder_add (builder, "{sv}", + "maximize-window", g_variant_new_boolean (TRUE)); + if (fullscreen_window) + g_variant_builder_add (builder, "{sv}", + "fullscreen-window", g_variant_new_boolean (TRUE)); +} + +char const* const* +terminal_client_get_environment_filters (void) +{ + static char const* filters[] = { + "COLORFGBG", + "COLORTERM", + "COLUMNS", + "DEFAULT_COLORS", + "DESKTOP_STARTUP_ID", + "EXIT_CODE", + "EXIT_STATUS", + "GIO_LAUNCHED_DESKTOP_FILE", + "GIO_LAUNCHED_DESKTOP_FILE_PID", + "GJS_DEBUG_OUTPUT", + "GJS_DEBUG_TOPICS", + "GNOME_DESKTOP_ICON", + "INVOCATION_ID", + "JOURNAL_STREAM", + "LINES", + "LISTEN_FDNAMES", + "LISTEN_FDS", + "LISTEN_PID", + "MAINPID", + "MANAGERPID", + "NOTIFY_SOCKET", + "NOTIFY_SOCKET", + "PIDFILE", + "PWD", + "REMOTE_ADDR", + "REMOTE_PORT", + "SERVICE_RESULT", + "SHLVL", + "STY", + "TERM", + "TERMCAP", + "TMUX", + "TMUX_PANE", + "VTE_VERSION", + "WATCHDOG_PID", + "WATCHDOG_USEC", + "WCWIDTH_CJK_LEGACY", + "WINDOWID", + "XDG_ACTIVATION_TOKEN", + nullptr + }; + + return filters; +} + +char const* const* +terminal_client_get_environment_prefix_filters (void) +{ + static char const* filters[] = { + "GNOME_TERMINAL_", + // "VTE_", ? + + /* other terminals */ + "FOOT_", + "ITERM2_", + "MC_", + "MINTTY_", + "PUTTY_", + "RXVT_", + "TERM_", + "URXVT_", + "WEZTERM_", + "XTERM_", + nullptr + }; + + return filters; +} + +static char const* const* +terminal_client_get_environment_prefix_filters_excludes(void) +{ + static char const* filters[] = { + "MC_XDG_OPEN", + + nullptr, + }; + + return filters; +} + +bool +terminal_client_get_environment_prefix_filters_is_excluded(char const* env) +{ + auto const excludes = terminal_client_get_environment_prefix_filters_excludes(); + for (auto j = 0; excludes[j]; ++j) { + if (g_str_equal(excludes[j], env)) + return true; + } + + return false; +} + +static char** +terminal_environ_unsetenv_prefix (char** envv, + char const* prefix) +{ + if (!envv) + return envv; + + for (auto i = 0; envv[i]; ++i) { + if (!g_str_has_prefix (envv[i], prefix)) + continue; + + auto const equal = strchr(envv[i], '='); + g_assert(equal != nullptr); + gs_free char* env = g_strndup(envv[i], equal - envv[i]); + + if (terminal_client_get_environment_prefix_filters_is_excluded(env)) + continue; + + envv = g_environ_unsetenv (envv, env); + } + + return envv; +} + +/** + * terminal_client_filter_environment: + * @envv: (transfer full): the environment + * + * Filters unwanted variables from @envv, and returns it. + * + * Returns: (transfer full): the filtered environment + */ +char** +terminal_client_filter_environment (char** envv) +{ + if (envv == nullptr) + return nullptr; + + auto filters = terminal_client_get_environment_filters (); + for (auto i = 0; filters[i]; ++i) + envv = g_environ_unsetenv (envv, filters[i]); + + filters = terminal_client_get_environment_prefix_filters (); + for (auto i = 0; filters[i]; ++i) + envv = terminal_environ_unsetenv_prefix (envv, filters[i]); + + return envv; +} + +/** + * terminal_client_append_exec_options: + * @builder: a #GVariantBuilder of #GVariantType "a{sv}" + * @pass_environment: whether to pass the current environment + * @working_directory: (allow-none): the cwd, or %nullptr + * @fd_array: (array lenght=fd_array_len): + * @fd_array_len: + * @shell: + * + * Appends the environment and the working directory to @builder. + */ +void +terminal_client_append_exec_options (GVariantBuilder *builder, + gboolean pass_environment, + const char *working_directory, + PassFdElement *fd_array, + gsize fd_array_len, + gboolean shell) +{ + if (pass_environment) { + gs_strfreev char **envv; + + envv = g_get_environ (); + envv = terminal_client_filter_environment (envv); + + envv = g_environ_unsetenv (envv, TERMINAL_ENV_SERVICE_NAME); + envv = g_environ_unsetenv (envv, TERMINAL_ENV_SCREEN); + + g_variant_builder_add (builder, "{sv}", + "environ", + g_variant_new_bytestring_array ((const char * const *) envv, -1)); + } + + if (working_directory) + g_variant_builder_add (builder, "{sv}", + "cwd", g_variant_new_bytestring (working_directory)); + + if (shell) + g_variant_builder_add (builder, "{sv}", + "shell", + g_variant_new_boolean (TRUE)); + + if (fd_array_len) { + gsize i; + + g_variant_builder_open (builder, G_VARIANT_TYPE ("{sv}")); + g_variant_builder_add (builder, "s", "fd-set"); + + g_variant_builder_open (builder, G_VARIANT_TYPE ("v")); + g_variant_builder_open (builder, G_VARIANT_TYPE ("a(ih)")); + for (i = 0; i < fd_array_len; i++) { + g_variant_builder_add (builder, "(ih)", fd_array[i].fd, fd_array[i].index); + } + g_variant_builder_close (builder); /* a(ih) */ + g_variant_builder_close (builder); /* v */ + + g_variant_builder_close (builder); /* {sv} */ + } +} + +#ifndef TERMINAL_NAUTILUS + +/** + * terminal_client_get_fallback_startup_id: + * + * Returns: a fallback startup ID, or %nullptr + */ +char * +terminal_client_get_fallback_startup_id (void) +{ +#if defined(TERMINAL_COMPILATION) && defined(GDK_WINDOWING_X11) + GdkDisplay *display; + Display *xdisplay; + Window xwindow; + XEvent event; + + display = gdk_display_get_default (); + if (display == nullptr || !GDK_IS_X11_DISPLAY (display)) + goto out; + + xdisplay = GDK_DISPLAY_XDISPLAY (display); + + { + XSetWindowAttributes attrs; + Atom atom_name; + Atom atom_type; + const char *name; + + attrs.override_redirect = True; + attrs.event_mask = PropertyChangeMask | StructureNotifyMask; + + xwindow = + XCreateWindow (xdisplay, + RootWindow (xdisplay, 0), + -100, -100, 1, 1, + 0, + CopyFromParent, + CopyFromParent, + (Visual *)CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attrs); + + atom_name = XInternAtom (xdisplay, "WM_NAME", TRUE); + g_assert (atom_name != None); + atom_type = XInternAtom (xdisplay, "STRING", TRUE); + g_assert (atom_type != None); + + name = "Fake Window"; + XChangeProperty (xdisplay, + xwindow, atom_name, + atom_type, + 8, PropModeReplace, (unsigned char *)name, strlen (name)); + } + + XWindowEvent (xdisplay, + xwindow, + PropertyChangeMask, + &event); + + XDestroyWindow(xdisplay, xwindow); + + return g_strdup_printf ("_TIME%lu", event.xproperty.time); +out: +#endif + return nullptr; +} + +#endif /* !TERMINAL_NAUTILUS */ |