935 lines
27 KiB
C
935 lines
27 KiB
C
|
/* ide-terminal-launcher.c
|
||
|
*
|
||
|
* Copyright 2019 Christian Hergert <unknown@domain.org>
|
||
|
*
|
||
|
* 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/>.
|
||
|
*
|
||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
*/
|
||
|
|
||
|
#define G_LOG_DOMAIN "ide-terminal-launcher"
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <glib/gi18n.h>
|
||
|
#include <libide-foundry.h>
|
||
|
#include <libide-threading.h>
|
||
|
|
||
|
#include "ide-private.h"
|
||
|
|
||
|
#include "ide-terminal-launcher.h"
|
||
|
#include "ide-terminal-util.h"
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
LAUNCHER_KIND_HOST = 0,
|
||
|
LAUNCHER_KIND_DEBUG,
|
||
|
LAUNCHER_KIND_RUNTIME,
|
||
|
LAUNCHER_KIND_RUNNER,
|
||
|
LAUNCHER_KIND_LAUNCHER,
|
||
|
LAUNCHER_KIND_CONFIG,
|
||
|
} LauncherKind;
|
||
|
|
||
|
struct _IdeTerminalLauncher
|
||
|
{
|
||
|
GObject parent_instance;
|
||
|
gchar *cwd;
|
||
|
gchar *shell;
|
||
|
gchar *title;
|
||
|
gchar **args;
|
||
|
IdeRuntime *runtime;
|
||
|
IdeContext *context;
|
||
|
IdeConfig *config;
|
||
|
IdeSubprocessLauncher *launcher;
|
||
|
LauncherKind kind;
|
||
|
};
|
||
|
|
||
|
G_DEFINE_FINAL_TYPE (IdeTerminalLauncher, ide_terminal_launcher, G_TYPE_OBJECT)
|
||
|
|
||
|
enum {
|
||
|
PROP_0,
|
||
|
PROP_ARGS,
|
||
|
PROP_CWD,
|
||
|
PROP_SHELL,
|
||
|
PROP_TITLE,
|
||
|
N_PROPS
|
||
|
};
|
||
|
|
||
|
static GParamSpec *properties [N_PROPS];
|
||
|
static const struct {
|
||
|
const gchar *key;
|
||
|
const gchar *value;
|
||
|
} default_environment[] = {
|
||
|
{ "INSIDE_GNOME_BUILDER", PACKAGE_VERSION },
|
||
|
{ "TERM", "xterm-256color" },
|
||
|
};
|
||
|
|
||
|
static gboolean
|
||
|
shell_supports_login (const gchar *shell)
|
||
|
{
|
||
|
g_autofree gchar *name = NULL;
|
||
|
|
||
|
/* Shells that support --login */
|
||
|
static const gchar *supported[] = {
|
||
|
"sh", "bash",
|
||
|
};
|
||
|
|
||
|
if (shell == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
if (!(name = g_path_get_basename (shell)))
|
||
|
return FALSE;
|
||
|
|
||
|
for (guint i = 0; i < G_N_ELEMENTS (supported); i++)
|
||
|
{
|
||
|
if (g_str_equal (name, supported[i]))
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
copy_envvars (gpointer instance)
|
||
|
{
|
||
|
static const gchar *copy_env[] = {
|
||
|
"AT_SPI_BUS_ADDRESS",
|
||
|
"COLORTERM",
|
||
|
"DBUS_SESSION_BUS_ADDRESS",
|
||
|
"DBUS_SYSTEM_BUS_ADDRESS",
|
||
|
"DESKTOP_SESSION",
|
||
|
"DISPLAY",
|
||
|
"LANG",
|
||
|
"SHELL",
|
||
|
"SSH_AUTH_SOCK",
|
||
|
"USER",
|
||
|
"WAYLAND_DISPLAY",
|
||
|
"XAUTHORITY",
|
||
|
"XDG_CURRENT_DESKTOP",
|
||
|
#if 0
|
||
|
/* Can't copy these as they could mess up Flatpak */
|
||
|
"XDG_DATA_DIRS",
|
||
|
"XDG_RUNTIME_DIR",
|
||
|
#endif
|
||
|
"XDG_MENU_PREFIX",
|
||
|
"XDG_SEAT",
|
||
|
"XDG_SESSION_DESKTOP",
|
||
|
"XDG_SESSION_ID",
|
||
|
"XDG_SESSION_TYPE",
|
||
|
"XDG_VTNR",
|
||
|
};
|
||
|
const gchar * const *host_environ;
|
||
|
IdeEnvironment *env = NULL;
|
||
|
|
||
|
g_assert (IDE_IS_SUBPROCESS_LAUNCHER (instance) || IDE_IS_RUNNER (instance));
|
||
|
|
||
|
if (IDE_IS_RUNNER (instance))
|
||
|
env = ide_runner_get_environment (instance);
|
||
|
|
||
|
host_environ = _ide_host_environ ();
|
||
|
|
||
|
for (guint i = 0; i < G_N_ELEMENTS (copy_env); i++)
|
||
|
{
|
||
|
const gchar *val = g_environ_getenv ((gchar **)host_environ, copy_env[i]);
|
||
|
|
||
|
if (val != NULL)
|
||
|
{
|
||
|
if (IDE_IS_SUBPROCESS_LAUNCHER (instance))
|
||
|
ide_subprocess_launcher_setenv (instance, copy_env[i], val, FALSE);
|
||
|
else
|
||
|
ide_environment_setenv (env, copy_env[i], val);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
apply_pipeline_info (gpointer instance,
|
||
|
IdeObject *object)
|
||
|
{
|
||
|
g_autoptr(GFile) workdir = NULL;
|
||
|
IdeEnvironment *env = NULL;
|
||
|
IdeContext *context;
|
||
|
|
||
|
g_assert (IDE_IS_SUBPROCESS_LAUNCHER (instance) || IDE_IS_RUNNER (instance));
|
||
|
g_assert (IDE_IS_OBJECT (object));
|
||
|
|
||
|
context = ide_object_get_context (object);
|
||
|
workdir = ide_context_ref_workdir (context);
|
||
|
|
||
|
if (IDE_IS_RUNNER (instance))
|
||
|
env = ide_runner_get_environment (instance);
|
||
|
|
||
|
if (IDE_IS_SUBPROCESS_LAUNCHER (instance))
|
||
|
ide_subprocess_launcher_setenv (instance, "SRCDIR", g_file_peek_path (workdir), FALSE);
|
||
|
else
|
||
|
ide_environment_setenv (env, "SRCDIR", g_file_peek_path (workdir));
|
||
|
|
||
|
if (ide_context_has_project (context))
|
||
|
{
|
||
|
IdeBuildManager *build_manager = ide_build_manager_from_context (context);
|
||
|
IdePipeline *pipeline = ide_build_manager_get_pipeline (build_manager);
|
||
|
|
||
|
if (pipeline != NULL)
|
||
|
{
|
||
|
const gchar *builddir = ide_pipeline_get_builddir (pipeline);
|
||
|
|
||
|
if (IDE_IS_SUBPROCESS_LAUNCHER (instance))
|
||
|
ide_subprocess_launcher_setenv (instance, "BUILDDIR", builddir, FALSE);
|
||
|
else
|
||
|
ide_environment_setenv (env, "BUILDDIR", builddir);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_wait_check_cb (GObject *object,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
IdeSubprocess *subprocess = (IdeSubprocess *)object;
|
||
|
g_autoptr(IdeTask) task = user_data;
|
||
|
g_autoptr(GError) error = NULL;
|
||
|
|
||
|
g_assert (IDE_IS_SUBPROCESS (subprocess));
|
||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
|
||
|
if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
|
||
|
ide_task_return_error (task, g_steal_pointer (&error));
|
||
|
else
|
||
|
ide_task_return_boolean (task, TRUE);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
spawn_host_launcher (IdeTerminalLauncher *self,
|
||
|
IdeTask *task,
|
||
|
gint pty_fd,
|
||
|
gboolean run_on_host)
|
||
|
{
|
||
|
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
|
||
|
g_autoptr(IdeSubprocess) subprocess = NULL;
|
||
|
g_autoptr(GError) error = NULL;
|
||
|
const gchar *shell;
|
||
|
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
g_assert (pty_fd >= 0);
|
||
|
|
||
|
if (!(shell = ide_terminal_launcher_get_shell (self)))
|
||
|
shell = ide_get_user_shell ();
|
||
|
|
||
|
/* We only have sh/bash in our flatpak */
|
||
|
if (self->kind == LAUNCHER_KIND_DEBUG && ide_is_flatpak ())
|
||
|
shell = "/bin/bash";
|
||
|
|
||
|
launcher = ide_subprocess_launcher_new (0);
|
||
|
ide_subprocess_launcher_set_run_on_host (launcher, run_on_host);
|
||
|
ide_subprocess_launcher_set_cwd (launcher, self->cwd ? self->cwd : g_get_home_dir ());
|
||
|
ide_subprocess_launcher_set_clear_env (launcher, FALSE);
|
||
|
|
||
|
ide_subprocess_launcher_push_argv (launcher, shell);
|
||
|
if (shell_supports_login (shell))
|
||
|
ide_subprocess_launcher_push_argv (launcher, "--login");
|
||
|
|
||
|
ide_subprocess_launcher_take_stdin_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stdout_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stderr_fd (launcher, dup (pty_fd));
|
||
|
|
||
|
g_assert (ide_subprocess_launcher_get_needs_tty (launcher));
|
||
|
|
||
|
for (guint i = 0; i < G_N_ELEMENTS (default_environment); i++)
|
||
|
ide_subprocess_launcher_setenv (launcher,
|
||
|
default_environment[i].key,
|
||
|
default_environment[i].value,
|
||
|
FALSE);
|
||
|
|
||
|
ide_subprocess_launcher_setenv (launcher, "SHELL", shell, TRUE);
|
||
|
|
||
|
if (self->context != NULL)
|
||
|
{
|
||
|
g_autoptr(GFile) workdir = ide_context_ref_workdir (self->context);
|
||
|
|
||
|
ide_subprocess_launcher_setenv (launcher,
|
||
|
"SRCDIR",
|
||
|
g_file_peek_path (workdir),
|
||
|
FALSE);
|
||
|
}
|
||
|
|
||
|
if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
|
||
|
ide_task_return_error (task, g_steal_pointer (&error));
|
||
|
else
|
||
|
ide_subprocess_wait_check_async (subprocess,
|
||
|
ide_task_get_cancellable (task),
|
||
|
ide_terminal_launcher_wait_check_cb,
|
||
|
g_object_ref (task));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
spawn_launcher (IdeTerminalLauncher *self,
|
||
|
IdeTask *task,
|
||
|
IdeSubprocessLauncher *launcher,
|
||
|
gint pty_fd)
|
||
|
{
|
||
|
g_autoptr(IdeSubprocess) subprocess = NULL;
|
||
|
g_autoptr(GError) error = NULL;
|
||
|
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
g_assert (!launcher || IDE_IS_SUBPROCESS_LAUNCHER (launcher));
|
||
|
g_assert (pty_fd >= 0);
|
||
|
|
||
|
if (launcher == NULL)
|
||
|
{
|
||
|
ide_task_return_new_error (task,
|
||
|
G_IO_ERROR,
|
||
|
G_IO_ERROR_FAILED,
|
||
|
"process may only be spawned once");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ide_subprocess_launcher_set_flags (launcher, 0);
|
||
|
|
||
|
ide_subprocess_launcher_take_stdin_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stdout_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stderr_fd (launcher, dup (pty_fd));
|
||
|
|
||
|
if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
|
||
|
ide_task_return_error (task, g_steal_pointer (&error));
|
||
|
else
|
||
|
ide_subprocess_wait_check_async (subprocess,
|
||
|
ide_task_get_cancellable (task),
|
||
|
ide_terminal_launcher_wait_check_cb,
|
||
|
g_object_ref (task));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
spawn_runtime_launcher (IdeTerminalLauncher *self,
|
||
|
IdeTask *task,
|
||
|
IdeRuntime *runtime,
|
||
|
IdeConfig *config,
|
||
|
gint pty_fd)
|
||
|
{
|
||
|
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
|
||
|
g_autoptr(IdeSubprocess) subprocess = NULL;
|
||
|
g_autoptr(GError) error = NULL;
|
||
|
const gchar *shell;
|
||
|
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
g_assert (!runtime || IDE_IS_RUNTIME (runtime));
|
||
|
g_assert (pty_fd >= 0);
|
||
|
|
||
|
if (runtime == NULL)
|
||
|
{
|
||
|
ide_task_return_new_error (task,
|
||
|
G_IO_ERROR,
|
||
|
G_IO_ERROR_FAILED,
|
||
|
_("Requested runtime is not installed"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(shell = ide_terminal_launcher_get_shell (self)))
|
||
|
shell = ide_get_user_shell ();
|
||
|
|
||
|
if (!(launcher = ide_runtime_create_launcher (runtime, NULL)))
|
||
|
{
|
||
|
ide_task_return_new_error (task,
|
||
|
G_IO_ERROR,
|
||
|
G_IO_ERROR_FAILED,
|
||
|
_("Failed to create shell within runtime “%s”"),
|
||
|
ide_runtime_get_display_name (runtime));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ide_subprocess_launcher_set_flags (launcher, 0);
|
||
|
|
||
|
if (!ide_runtime_contains_program_in_path (runtime, shell, NULL))
|
||
|
shell = "/bin/sh";
|
||
|
|
||
|
ide_subprocess_launcher_set_cwd (launcher, self->cwd ? self->cwd : g_get_home_dir ());
|
||
|
|
||
|
ide_subprocess_launcher_push_argv (launcher, shell);
|
||
|
if (shell_supports_login (shell))
|
||
|
ide_subprocess_launcher_push_argv (launcher, "--login");
|
||
|
|
||
|
ide_subprocess_launcher_take_stdin_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stdout_fd (launcher, dup (pty_fd));
|
||
|
ide_subprocess_launcher_take_stderr_fd (launcher, dup (pty_fd));
|
||
|
|
||
|
g_assert (ide_subprocess_launcher_get_needs_tty (launcher));
|
||
|
|
||
|
for (guint i = 0; i < G_N_ELEMENTS (default_environment); i++)
|
||
|
ide_subprocess_launcher_setenv (launcher,
|
||
|
default_environment[i].key,
|
||
|
default_environment[i].value,
|
||
|
FALSE);
|
||
|
|
||
|
apply_pipeline_info (launcher, IDE_OBJECT (self->runtime));
|
||
|
copy_envvars (launcher);
|
||
|
|
||
|
ide_subprocess_launcher_setenv (launcher, "SHELL", shell, TRUE);
|
||
|
|
||
|
if (config != NULL)
|
||
|
ide_config_apply_path (config, launcher);
|
||
|
|
||
|
if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
|
||
|
ide_task_return_error (task, g_steal_pointer (&error));
|
||
|
else
|
||
|
ide_subprocess_wait_check_async (subprocess,
|
||
|
ide_task_get_cancellable (task),
|
||
|
ide_terminal_launcher_wait_check_cb,
|
||
|
g_object_ref (task));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_run_cb (GObject *object,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
IdeRunner *runner = (IdeRunner *)object;
|
||
|
g_autoptr(IdeTask) task = user_data;
|
||
|
g_autoptr(GError) error = NULL;
|
||
|
|
||
|
g_assert (IDE_IS_RUNNER (runner));
|
||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
|
||
|
if (!ide_runner_run_finish (runner, result, &error))
|
||
|
ide_task_return_error (task, g_steal_pointer (&error));
|
||
|
else
|
||
|
ide_task_return_boolean (task, TRUE);
|
||
|
|
||
|
ide_object_destroy (IDE_OBJECT (runner));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
spawn_runner_launcher (IdeTerminalLauncher *self,
|
||
|
IdeTask *task,
|
||
|
IdeRuntime *runtime,
|
||
|
gint pty_fd)
|
||
|
{
|
||
|
g_autoptr(IdeSimpleBuildTarget) build_target = NULL;
|
||
|
g_autoptr(IdeRunner) runner = NULL;
|
||
|
g_autoptr(GPtrArray) argv = NULL;
|
||
|
IdeEnvironment *env;
|
||
|
const gchar *shell;
|
||
|
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (IDE_IS_TASK (task));
|
||
|
g_assert (!runtime || IDE_IS_RUNTIME (runtime));
|
||
|
g_assert (pty_fd >= 0);
|
||
|
|
||
|
if (runtime == NULL)
|
||
|
{
|
||
|
ide_task_return_new_error (task,
|
||
|
G_IO_ERROR,
|
||
|
G_IO_ERROR_FAILED,
|
||
|
_("Requested runtime is not installed"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(shell = ide_terminal_launcher_get_shell (self)))
|
||
|
shell = ide_get_user_shell ();
|
||
|
|
||
|
if (!ide_runtime_contains_program_in_path (runtime, shell, NULL))
|
||
|
shell = "/bin/sh";
|
||
|
|
||
|
argv = g_ptr_array_new ();
|
||
|
g_ptr_array_add (argv, (gchar *)shell);
|
||
|
|
||
|
if (self->args == NULL)
|
||
|
{
|
||
|
if (shell_supports_login (shell))
|
||
|
g_ptr_array_add (argv, (gchar *)"--login");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (guint i = 0; self->args[i]; i++)
|
||
|
g_ptr_array_add (argv, self->args[i]);
|
||
|
}
|
||
|
|
||
|
g_ptr_array_add (argv, NULL);
|
||
|
|
||
|
build_target = ide_simple_build_target_new (NULL);
|
||
|
ide_simple_build_target_set_argv (build_target, (const gchar * const *)argv->pdata);
|
||
|
ide_simple_build_target_set_cwd (build_target, self->cwd ? self->cwd : g_get_home_dir ());
|
||
|
|
||
|
/* Creating runner should always succeed, but run_async() may fail */
|
||
|
runner = ide_runtime_create_runner (runtime, IDE_BUILD_TARGET (build_target));
|
||
|
env = ide_runner_get_environment (runner);
|
||
|
ide_runner_take_tty_fd (runner, dup (pty_fd));
|
||
|
|
||
|
for (guint i = 0; i < G_N_ELEMENTS (default_environment); i++)
|
||
|
ide_environment_setenv (env,
|
||
|
default_environment[i].key,
|
||
|
default_environment[i].value);
|
||
|
|
||
|
apply_pipeline_info (runner, IDE_OBJECT (self->runtime));
|
||
|
copy_envvars (runner);
|
||
|
|
||
|
ide_environment_setenv (env, "SHELL", shell);
|
||
|
|
||
|
ide_runner_run_async (runner,
|
||
|
ide_task_get_cancellable (task),
|
||
|
ide_terminal_launcher_run_cb,
|
||
|
g_object_ref (task));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_terminal_launcher_spawn_async (IdeTerminalLauncher *self,
|
||
|
VtePty *pty,
|
||
|
GCancellable *cancellable,
|
||
|
GAsyncReadyCallback callback,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
g_autoptr(IdeTask) task = NULL;
|
||
|
gint pty_fd = -1;
|
||
|
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (VTE_IS_PTY (pty));
|
||
|
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||
|
|
||
|
task = ide_task_new (self, cancellable, callback, user_data);
|
||
|
ide_task_set_source_tag (task, ide_terminal_launcher_spawn_async);
|
||
|
|
||
|
if ((pty_fd = ide_vte_pty_create_slave (pty)) == -1)
|
||
|
{
|
||
|
int errsv = errno;
|
||
|
ide_task_return_new_error (task,
|
||
|
G_IO_ERROR,
|
||
|
g_io_error_from_errno (errsv),
|
||
|
"%s", g_strerror (errsv));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (self->kind)
|
||
|
{
|
||
|
case LAUNCHER_KIND_RUNTIME:
|
||
|
spawn_runtime_launcher (self, task, self->runtime, NULL, pty_fd);
|
||
|
break;
|
||
|
|
||
|
case LAUNCHER_KIND_CONFIG:
|
||
|
spawn_runtime_launcher (self, task, self->runtime, self->config, pty_fd);
|
||
|
break;
|
||
|
|
||
|
case LAUNCHER_KIND_RUNNER:
|
||
|
spawn_runner_launcher (self, task, self->runtime, pty_fd);
|
||
|
break;
|
||
|
|
||
|
case LAUNCHER_KIND_LAUNCHER:
|
||
|
spawn_launcher (self, task, self->launcher, pty_fd);
|
||
|
g_clear_object (&self->launcher);
|
||
|
break;
|
||
|
|
||
|
case LAUNCHER_KIND_DEBUG:
|
||
|
case LAUNCHER_KIND_HOST:
|
||
|
default:
|
||
|
spawn_host_launcher (self, task, pty_fd, self->kind == LAUNCHER_KIND_HOST);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pty_fd != -1)
|
||
|
close (pty_fd);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_spawn_finish:
|
||
|
* @self: a #IdeTerminalLauncher
|
||
|
*
|
||
|
* Completes a request to ide_terminal_launcher_spawn_async()
|
||
|
*
|
||
|
* Returns: %TRUE if the process executed successfully; otherwise %FALSE
|
||
|
* and @error is set.
|
||
|
*
|
||
|
* Since: 3.34
|
||
|
*/
|
||
|
gboolean
|
||
|
ide_terminal_launcher_spawn_finish (IdeTerminalLauncher *self,
|
||
|
GAsyncResult *result,
|
||
|
GError **error)
|
||
|
{
|
||
|
g_assert (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
g_assert (IDE_IS_TASK (result));
|
||
|
|
||
|
return ide_task_propagate_boolean (IDE_TASK (result), error);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_finalize (GObject *object)
|
||
|
{
|
||
|
IdeTerminalLauncher *self = (IdeTerminalLauncher *)object;
|
||
|
|
||
|
g_clear_pointer (&self->args, g_strfreev);
|
||
|
g_clear_pointer (&self->cwd, g_free);
|
||
|
g_clear_pointer (&self->shell, g_free);
|
||
|
g_clear_pointer (&self->title, g_free);
|
||
|
g_clear_object (&self->launcher);
|
||
|
g_clear_object (&self->runtime);
|
||
|
g_clear_object (&self->config);
|
||
|
|
||
|
G_OBJECT_CLASS (ide_terminal_launcher_parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_get_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
IdeTerminalLauncher *self = IDE_TERMINAL_LAUNCHER (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_ARGS:
|
||
|
g_value_set_boxed (value, ide_terminal_launcher_get_args (self));
|
||
|
break;
|
||
|
|
||
|
case PROP_CWD:
|
||
|
g_value_set_string (value, ide_terminal_launcher_get_cwd (self));
|
||
|
break;
|
||
|
|
||
|
case PROP_SHELL:
|
||
|
g_value_set_string (value, ide_terminal_launcher_get_shell (self));
|
||
|
break;
|
||
|
|
||
|
case PROP_TITLE:
|
||
|
g_value_set_string (value, ide_terminal_launcher_get_title (self));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_set_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
IdeTerminalLauncher *self = IDE_TERMINAL_LAUNCHER (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_ARGS:
|
||
|
ide_terminal_launcher_set_args (self, g_value_get_boxed (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_CWD:
|
||
|
ide_terminal_launcher_set_cwd (self, g_value_get_string (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_SHELL:
|
||
|
ide_terminal_launcher_set_shell (self, g_value_get_string (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_TITLE:
|
||
|
ide_terminal_launcher_set_title (self, g_value_get_string (value));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_class_init (IdeTerminalLauncherClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
|
||
|
object_class->finalize = ide_terminal_launcher_finalize;
|
||
|
object_class->get_property = ide_terminal_launcher_get_property;
|
||
|
object_class->set_property = ide_terminal_launcher_set_property;
|
||
|
|
||
|
properties [PROP_ARGS] =
|
||
|
g_param_spec_boxed ("args",
|
||
|
"Args",
|
||
|
"Arguments to shell",
|
||
|
G_TYPE_STRV,
|
||
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
properties [PROP_CWD] =
|
||
|
g_param_spec_string ("cwd",
|
||
|
"Cwd",
|
||
|
"The cwd to spawn in the subprocess",
|
||
|
NULL,
|
||
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
properties [PROP_SHELL] =
|
||
|
g_param_spec_string ("shell",
|
||
|
"Shell",
|
||
|
"The shell to spawn in the subprocess",
|
||
|
NULL,
|
||
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
properties [PROP_TITLE] =
|
||
|
g_param_spec_string ("title",
|
||
|
"Title",
|
||
|
"The title for the subprocess launcher",
|
||
|
NULL,
|
||
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_terminal_launcher_init (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
self->cwd = NULL;
|
||
|
self->shell = NULL;
|
||
|
self->title = g_strdup (_("Untitled Terminal"));
|
||
|
}
|
||
|
|
||
|
const gchar *
|
||
|
ide_terminal_launcher_get_cwd (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
g_return_val_if_fail (IDE_IS_TERMINAL_LAUNCHER (self), NULL);
|
||
|
|
||
|
return self->cwd;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_terminal_launcher_set_cwd (IdeTerminalLauncher *self,
|
||
|
const gchar *cwd)
|
||
|
{
|
||
|
g_return_if_fail (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
|
||
|
if (g_strcmp0 (self->cwd, cwd) != 0)
|
||
|
{
|
||
|
g_free (self->cwd);
|
||
|
self->cwd = g_strdup (cwd);
|
||
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CWD]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const gchar *
|
||
|
ide_terminal_launcher_get_shell (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
g_return_val_if_fail (IDE_IS_TERMINAL_LAUNCHER (self), NULL);
|
||
|
|
||
|
return self->shell;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_terminal_launcher_set_shell (IdeTerminalLauncher *self,
|
||
|
const gchar *shell)
|
||
|
{
|
||
|
g_return_if_fail (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
|
||
|
if (g_strcmp0 (self->shell, shell) != 0)
|
||
|
{
|
||
|
g_free (self->shell);
|
||
|
self->shell = g_strdup (shell);
|
||
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHELL]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const gchar *
|
||
|
ide_terminal_launcher_get_title (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
g_return_val_if_fail (IDE_IS_TERMINAL_LAUNCHER (self), NULL);
|
||
|
|
||
|
return self->title;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_terminal_launcher_set_title (IdeTerminalLauncher *self,
|
||
|
const gchar *title)
|
||
|
{
|
||
|
g_return_if_fail (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
|
||
|
if (g_strcmp0 (self->title, title) != 0)
|
||
|
{
|
||
|
g_free (self->title);
|
||
|
self->title = g_strdup (title);
|
||
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new:
|
||
|
*
|
||
|
* Create a new #IdeTerminalLauncher that will spawn a terminal on the host.
|
||
|
*
|
||
|
* Returns: (transfer full): a newly created #IdeTerminalLauncher
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new (IdeContext *context)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
g_autoptr(GFile) workdir = NULL;
|
||
|
|
||
|
g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
|
||
|
|
||
|
workdir = ide_context_ref_workdir (context);
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->kind = LAUNCHER_KIND_HOST;
|
||
|
self->cwd = g_file_get_path (workdir);
|
||
|
self->context = g_object_ref (context);
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new_for_launcher:
|
||
|
* @launcher: an #IdeSubprocessLauncher
|
||
|
*
|
||
|
* Creates a new #IdeTerminalLauncher that can be used to launch a process
|
||
|
* using the provided #IdeSubprocessLauncher.
|
||
|
*
|
||
|
* Returns: (transfer full): an #IdeTerminalLauncher
|
||
|
*
|
||
|
* Since: 3.34
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new_for_launcher (IdeSubprocessLauncher *launcher)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
|
||
|
g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (launcher), NULL);
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->kind = LAUNCHER_KIND_LAUNCHER;
|
||
|
self->launcher = g_object_ref (launcher);
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new_for_debug
|
||
|
*
|
||
|
* Create a new #IdeTerminalLauncher that will spawn a terminal on the host.
|
||
|
*
|
||
|
* Returns: (transfer full): a newly created #IdeTerminalLauncher
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new_for_debug (void)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->kind = LAUNCHER_KIND_DEBUG;
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new_for_config:
|
||
|
* @config: an #IdeConfig
|
||
|
*
|
||
|
* Create a new #IdeTerminalLauncher that will spawn a terminal in the runtime
|
||
|
* of the configuration with various build options applied.
|
||
|
*
|
||
|
* Returns: (transfer full): a newly created #IdeTerminalLauncher
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new_for_config (IdeConfig *config)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
IdeRuntime *runtime;
|
||
|
|
||
|
g_return_val_if_fail (IDE_IS_CONFIG (config), NULL);
|
||
|
|
||
|
runtime = ide_config_get_runtime (config);
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->runtime = g_object_ref (runtime);
|
||
|
self->config = g_object_ref (config);
|
||
|
self->kind = LAUNCHER_KIND_CONFIG;
|
||
|
|
||
|
ide_terminal_launcher_set_title (self, ide_runtime_get_name (runtime));
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new_for_runtime:
|
||
|
* @runtime: an #IdeRuntime
|
||
|
*
|
||
|
* Create a new #IdeTerminalLauncher that will spawn a terminal in the runtime.
|
||
|
*
|
||
|
* Returns: (transfer full): a newly created #IdeTerminalLauncher
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new_for_runtime (IdeRuntime *runtime)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
|
||
|
g_return_val_if_fail (IDE_IS_RUNTIME (runtime), NULL);
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->runtime = g_object_ref (runtime);
|
||
|
self->kind = LAUNCHER_KIND_RUNTIME;
|
||
|
|
||
|
ide_terminal_launcher_set_title (self, ide_runtime_get_name (runtime));
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_terminal_launcher_new_for_runner:
|
||
|
* @runtime: an #IdeRuntime
|
||
|
*
|
||
|
* Create a new #IdeTerminalLauncher that will spawn a terminal in the runtime
|
||
|
* but with a "runner" context similar to how the application would execute.
|
||
|
*
|
||
|
* Returns: (transfer full): a newly created #IdeTerminalLauncher
|
||
|
*/
|
||
|
IdeTerminalLauncher *
|
||
|
ide_terminal_launcher_new_for_runner (IdeRuntime *runtime)
|
||
|
{
|
||
|
IdeTerminalLauncher *self;
|
||
|
|
||
|
g_return_val_if_fail (IDE_IS_RUNTIME (runtime), NULL);
|
||
|
|
||
|
self = g_object_new (IDE_TYPE_TERMINAL_LAUNCHER, NULL);
|
||
|
self->runtime = g_object_ref (runtime);
|
||
|
self->kind = LAUNCHER_KIND_RUNNER;
|
||
|
|
||
|
return g_steal_pointer (&self);
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
ide_terminal_launcher_can_respawn (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
g_return_val_if_fail (IDE_IS_TERMINAL_LAUNCHER (self), FALSE);
|
||
|
|
||
|
return self->kind != LAUNCHER_KIND_LAUNCHER;
|
||
|
}
|
||
|
|
||
|
const gchar * const *
|
||
|
ide_terminal_launcher_get_args (IdeTerminalLauncher *self)
|
||
|
{
|
||
|
g_return_val_if_fail (IDE_IS_TERMINAL_LAUNCHER (self), NULL);
|
||
|
|
||
|
return (const gchar * const *)self->args;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_terminal_launcher_set_args (IdeTerminalLauncher *self,
|
||
|
const gchar * const *args)
|
||
|
{
|
||
|
g_return_if_fail (IDE_IS_TERMINAL_LAUNCHER (self));
|
||
|
|
||
|
if ((gchar **)args != self->args)
|
||
|
{
|
||
|
gchar **freeme = g_steal_pointer (&self->args);
|
||
|
self->args = g_strdupv ((gchar **)args);
|
||
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ARGS]);
|
||
|
g_strfreev (freeme);
|
||
|
}
|
||
|
}
|