/*
* 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 .
*/
#include "config.h"
#include
#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
#include
#ifndef TERMINAL_NAUTILUS
#include
#if defined(TERMINAL_COMPILATION) && defined(GDK_WINDOWING_X11)
#include
#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(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(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 */