[vte] pty: Separate PTY code from its GObject wrapper
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] pty: Separate PTY code from its GObject wrapper
- Date: Sat, 12 Oct 2019 18:12:24 +0000 (UTC)
commit 8c72345d1df56287c8ffaa2186f5913ac587d837
Author: Christian Persch <chpe src gnome org>
Date: Sat Oct 12 20:10:55 2019 +0200
pty: Separate PTY code from its GObject wrapper
src/fwd.hh | 28 ++
src/meson.build | 11 +-
src/pty.cc | 770 +++++++++-----------------------------------------
src/pty.hh | 83 ++++++
src/vte.cc | 217 +++++---------
src/vtegtk.cc | 48 +++-
src/vteinternal.hh | 51 ++--
src/vtepty-private.h | 33 ---
src/vtepty.cc | 722 ++++++++++++++++++++++++++++++++++++++++++++++
src/vteptyinternal.hh | 35 +++
src/vtetypes.hh | 19 +-
src/widget.cc | 27 ++
src/widget.hh | 13 +-
13 files changed, 1182 insertions(+), 875 deletions(-)
---
diff --git a/src/fwd.hh b/src/fwd.hh
new file mode 100644
index 00000000..11525a98
--- /dev/null
+++ b/src/fwd.hh
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2019 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+namespace vte {
+
+namespace base {
+
+class Pty;
+
+} // namespace base
+
+} // namespace vte
diff --git a/src/meson.build b/src/meson.build
index 2921d838..efaf81f4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -49,6 +49,13 @@ parser_sources = files(
'parser.hh',
)
+pty_sources = files(
+ 'pty.cc',
+ 'pty.hh',
+ 'vtepty.cc',
+ 'vteptyinternal.hh',
+)
+
regex_sources = files(
'regex.cc',
'regex.hh'
@@ -59,7 +66,7 @@ utf8_sources = files(
'utf8.hh',
)
-libvte_common_sources = debug_sources + modes_sources + parser_sources + regex_sources + utf8_sources +
files(
+libvte_common_sources = debug_sources + modes_sources + parser_sources + pty_sources + regex_sources +
utf8_sources + files(
'attr.hh',
'bidi.cc',
'bidi.hh',
@@ -71,7 +78,6 @@ libvte_common_sources = debug_sources + modes_sources + parser_sources + regex_s
'color-triple.hh',
'keymap.cc',
'keymap.h',
- 'pty.cc',
'reaper.cc',
'reaper.hh',
'refptr.hh',
@@ -91,7 +97,6 @@ libvte_common_sources = debug_sources + modes_sources + parser_sources + regex_s
'vtegtk.hh',
'vteinternal.hh',
'vtepcre2.h',
- 'vtepty-private.h',
'vteregex.cc',
'vteregexinternal.hh',
'vterowdata.cc',
diff --git a/src/pty.cc b/src/pty.cc
index 87cc0dd1..df60c9f5 100644
--- a/src/pty.cc
+++ b/src/pty.cc
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2001,2002 Red Hat, Inc.
- * Copyright © 2009, 2010 Christian Persch
+ * Copyright © 2009, 2010, 2019 Christian Persch
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -28,8 +28,10 @@
#include <config.h>
+#include "pty.hh"
+
#include <vte/vte.h>
-#include "vtepty-private.h"
+#include "vteptyinternal.hh"
#include "vtetypes.hh"
#include "vtespawn.hh"
@@ -79,51 +81,25 @@
#define VTE_TERMINFO_NAME "xterm-256color"
-#define I_(string) (g_intern_static_string(string))
-
-typedef struct _VtePtyPrivate VtePtyPrivate;
-
-typedef struct {
- GSpawnChildSetupFunc extra_child_setup;
- gpointer extra_child_setup_data;
-} VtePtyChildSetupData;
-
-/**
- * VtePty:
- */
-struct _VtePty {
- GObject parent_instance;
-
- /* <private> */
- VtePtyPrivate *priv;
-};
+namespace vte::base {
-struct _VtePtyPrivate {
- VtePtyFlags flags;
- int pty_fd;
-
- VtePtyChildSetupData child_setup_data;
-
- guint utf8 : 1;
- guint foreign : 1;
-};
-
-struct _VtePtyClass {
- GObjectClass parent_class;
-};
+Pty*
+Pty::ref() noexcept
+{
+ g_atomic_int_inc(&m_refcount);
+ return this;
+}
-/**
- * vte_pty_child_setup:
- * @pty: a #VtePty
- *
- * FIXMEchpe
- */
void
-vte_pty_child_setup (VtePty *pty)
+Pty::unref() noexcept
{
- VtePtyPrivate *priv = pty->priv;
- VtePtyChildSetupData *data = &priv->child_setup_data;
+ if (g_atomic_int_dec_and_test(&m_refcount))
+ delete this;
+}
+void
+Pty::child_setup() const noexcept
+{
/* Unblock all signals */
sigset_t set;
sigemptyset(&set);
@@ -141,7 +117,7 @@ vte_pty_child_setup (VtePty *pty)
signal(n, SIG_DFL);
}
- auto masterfd = priv->pty_fd;
+ auto masterfd = fd();
if (masterfd == -1)
_exit(127);
@@ -156,7 +132,7 @@ vte_pty_child_setup (VtePty *pty)
}
/* Note: *not* O_CLOEXEC! */
- auto const fd_flags = int{O_RDWR | ((priv->flags & VTE_PTY_NO_CTTY) ? O_NOCTTY : 0)};
+ auto const fd_flags = int{O_RDWR | ((m_flags & VTE_PTY_NO_CTTY) ? O_NOCTTY : 0)};
auto fd = int{-1};
#ifdef __linux__
@@ -187,7 +163,7 @@ vte_pty_child_setup (VtePty *pty)
"Setting up child pty: master FD = %d name = %s\n",
masterfd, name);
- fd = open(name, fd_flags);
+ fd = ::open(name, fd_flags);
if (fd == -1) {
_vte_debug_print (VTE_DEBUG_PTY, "Failed to open PTY: %m\n");
_exit(127);
@@ -197,7 +173,7 @@ vte_pty_child_setup (VtePty *pty)
assert(fd != -1);
#if defined(HAVE_SETSID) && defined(HAVE_SETPGID)
- if (!(priv->flags & VTE_PTY_NO_SESSION)) {
+ if (!(m_flags & VTE_PTY_NO_SESSION)) {
/* Start a new session and become process-group leader. */
_vte_debug_print (VTE_DEBUG_PTY, "Starting new session\n");
setsid();
@@ -206,7 +182,7 @@ vte_pty_child_setup (VtePty *pty)
#endif
#ifdef TIOCSCTTY
- if (!(priv->flags & VTE_PTY_NO_CTTY)) {
+ if (!(m_flags & VTE_PTY_NO_CTTY)) {
ioctl(fd, TIOCSCTTY, fd);
}
#endif
@@ -263,8 +239,8 @@ vte_pty_child_setup (VtePty *pty)
g_setenv ("VTE_VERSION", version, TRUE);
/* Finally call an extra child setup */
- if (data->extra_child_setup) {
- data->extra_child_setup (data->extra_child_setup_data);
+ if (m_extra_child_setup.func) {
+ m_extra_child_setup.func(m_extra_child_setup.data);
}
}
@@ -344,6 +320,13 @@ __vte_pty_merge_environ (char **envp,
return (gchar **) g_ptr_array_free (array, FALSE);
}
+static void
+pty_child_setup_cb(void* data)
+{
+ vte::base::Pty* pty = reinterpret_cast<vte::base::Pty*>(data);
+ pty->child_setup();
+}
+
/*
* __vte_pty_spawn:
* @pty: a #VtePty
@@ -375,27 +358,24 @@ __vte_pty_merge_environ (char **envp,
*
* Returns: %TRUE on success, or %FALSE on failure with @error filled in
*/
-gboolean
-__vte_pty_spawn (VtePty *pty,
- const char *directory,
- char **argv,
- char **envv,
- GSpawnFlags spawn_flags_,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GPid *child_pid /* out */,
- int timeout,
- GCancellable *cancellable,
- GError **error)
+bool
+Pty::spawn(char const* directory,
+ char** argv,
+ char** envv,
+ GSpawnFlags spawn_flags_,
+ GSpawnChildSetupFunc child_setup_func,
+ gpointer child_setup_data,
+ GPid* child_pid /* out */,
+ int timeout,
+ GCancellable* cancellable,
+ GError** error) noexcept
{
- VtePtyPrivate *priv = pty->priv;
- VtePtyChildSetupData *data = &priv->child_setup_data;
guint spawn_flags = (guint) spawn_flags_;
- gboolean ret = TRUE;
- gboolean inherit_envv;
- char **envp2;
- gint i;
- GError *err = NULL;
+ bool ret{true};
+ bool inherit_envv;
+ char** envp2;
+ int i;
+ GError* err{nullptr};
GPollFD pollfd;
if (cancellable && !g_cancellable_make_pollfd(cancellable, &pollfd)) {
@@ -405,13 +385,14 @@ __vte_pty_spawn (VtePty *pty,
g_io_error_from_errno(errsv),
"Failed to make cancellable pollfd: %s",
g_strerror(errsv));
- return FALSE;
+ return false;
}
spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD;
- /* FIXMEchpe: Enforce this until I've checked our code to make sure
- * it doesn't leak out internal FDs into the child this way.
+ /* We do NOT support this flag. If you want to have some FD open in the child
+ * process, simply use a child setup function that unsets the CLOEXEC flag
+ * on that FD.
*/
spawn_flags &= ~G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
@@ -433,156 +414,123 @@ __vte_pty_spawn (VtePty *pty,
directory ? directory : "(none)");
}
- data->extra_child_setup = child_setup;
- data->extra_child_setup_data = child_setup_data;
+ m_extra_child_setup.func = child_setup_func;
+ m_extra_child_setup.data = child_setup_data;
ret = vte_spawn_async_with_pipes_cancellable(directory,
argv, envp2,
(GSpawnFlags)spawn_flags,
- (GSpawnChildSetupFunc)vte_pty_child_setup,
- pty,
+ (GSpawnChildSetupFunc)pty_child_setup_cb,
+ this,
child_pid,
- NULL, NULL, NULL,
+ nullptr, nullptr, nullptr,
timeout,
- cancellable ? &pollfd : NULL,
+ cancellable ? &pollfd : nullptr,
&err);
if (!ret &&
- directory != NULL &&
+ directory != nullptr &&
g_error_matches(err, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR)) {
/* try spawning in our working directory */
g_clear_error(&err);
- ret = vte_spawn_async_with_pipes_cancellable(NULL,
+ ret = vte_spawn_async_with_pipes_cancellable(nullptr,
argv, envp2,
(GSpawnFlags)spawn_flags,
- (GSpawnChildSetupFunc)vte_pty_child_setup,
- pty,
+ (GSpawnChildSetupFunc)pty_child_setup_cb,
+ this,
child_pid,
- NULL, NULL, NULL,
+ nullptr, nullptr, nullptr,
timeout,
- cancellable ? &pollfd : NULL,
+ cancellable ? &pollfd : nullptr,
&err);
}
g_strfreev (envp2);
- data->extra_child_setup = NULL;
- data->extra_child_setup_data = NULL;
+ m_extra_child_setup.func = nullptr;
+ m_extra_child_setup.data = nullptr;
if (cancellable)
g_cancellable_release_fd(cancellable);
if (ret)
- return TRUE;
+ return true;
- g_propagate_error (error, err);
- return FALSE;
+ g_propagate_error(error, err);
+ return false;
}
-/**
- * vte_pty_set_size:
- * @pty: a #VtePty
+/*
+ * Pty::set_size:
* @rows: the desired number of rows
* @columns: the desired number of columns
- * @error: (allow-none): return location to store a #GError, or %NULL
*
* Attempts to resize the pseudo terminal's window size. If successful, the
* OS kernel will send #SIGWINCH to the child process group.
*
- * If setting the window size failed, @error will be set to a #GIOError.
- *
- * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ * Returns: %true on success, or %false on error with errno set
*/
-gboolean
-vte_pty_set_size(VtePty *pty,
- int rows,
- int columns,
- GError **error)
+bool
+Pty::set_size(int rows,
+ int columns) const noexcept
{
- struct winsize size;
- int master;
- int ret;
-
- g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
-
- master = vte_pty_get_fd(pty);
+ auto master = fd();
+ struct winsize size;
memset(&size, 0, sizeof(size));
size.ws_row = rows > 0 ? rows : 24;
size.ws_col = columns > 0 ? columns : 80;
_vte_debug_print(VTE_DEBUG_PTY,
"Setting size on fd %d to (%d,%d).\n",
master, columns, rows);
- ret = ioctl(master, TIOCSWINSZ, &size);
- if (ret != 0) {
- vte::util::restore_errno errsv;
-
- g_set_error(error, G_IO_ERROR,
- g_io_error_from_errno(errsv),
- "Failed to set window size: %s",
- g_strerror(errsv));
+ auto ret = ioctl(master, TIOCSWINSZ, &size);
- _vte_debug_print(VTE_DEBUG_PTY,
- "Failed to set size on %d: %s.\n",
- master, g_strerror(errsv));
- return FALSE;
- }
+ if (ret != 0) {
+ vte::util::restore_errno errsv;
+ _vte_debug_print(VTE_DEBUG_PTY,
+ "Failed to set size on %d: %m\n", master);
+ }
- return TRUE;
+ return ret == 0;
}
-/**
- * vte_pty_get_size:
- * @pty: a #VtePty
+/*
+ * Pty::get_size:
* @rows: (out) (allow-none): a location to store the number of rows, or %NULL
* @columns: (out) (allow-none): a location to store the number of columns, or %NULL
- * @error: return location to store a #GError, or %NULL
*
* Reads the pseudo terminal's window size.
*
* If getting the window size failed, @error will be set to a #GIOError.
*
- * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ * Returns: %true on success, or %false on error with errno set
*/
-gboolean
-vte_pty_get_size(VtePty *pty,
- int *rows,
- int *columns,
- GError **error)
+bool
+Pty::get_size(int* rows,
+ int* columns) const noexcept
{
- struct winsize size;
- int master;
- int ret;
-
- g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
-
- master = vte_pty_get_fd(pty);
+ auto master = fd();
+ struct winsize size;
memset(&size, 0, sizeof(size));
- ret = ioctl(master, TIOCGWINSZ, &size);
- if (ret == 0) {
- if (columns != NULL) {
+ auto ret = ioctl(master, TIOCGWINSZ, &size);
+ if (ret == 0) {
+ if (columns != nullptr) {
*columns = size.ws_col;
}
- if (rows != NULL) {
+ if (rows != nullptr) {
*rows = size.ws_row;
}
_vte_debug_print(VTE_DEBUG_PTY,
"Size on fd %d is (%d,%d).\n",
master, size.ws_col, size.ws_row);
- return TRUE;
- } else {
- vte::util::restore_errno errsv;
+ return true;
+ }
- g_set_error(error, G_IO_ERROR,
- g_io_error_from_errno(errsv),
- "Failed to get window size: %s",
- g_strerror(errsv));
+ vte::util::restore_errno errsv;
+ _vte_debug_print(VTE_DEBUG_PTY,
+ "Failed to read size from fd %d: %m\n", master);
- _vte_debug_print(VTE_DEBUG_PTY,
- "Failed to read size from fd %d: %s\n",
- master, g_strerror(errsv));
- return FALSE;
- }
+ return false;
}
static int
@@ -722,521 +670,65 @@ _vte_pty_open_foreign(int masterfd /* consumed */)
return fd.steal();
}
-/**
- * vte_pty_set_utf8:
- * @pty: a #VtePty
+/*
+ * Pty::set_utf8:
* @utf8: whether or not the pty is in UTF-8 mode
- * @error: (allow-none): return location to store a #GError, or %NULL
*
* Tells the kernel whether the terminal is UTF-8 or not, in case it can make
* use of the info. Linux 2.6.5 or so defines IUTF8 to make the line
* discipline do multibyte backspace correctly.
*
- * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ * Returns: %true on success, or %false on error with errno set
*/
-gboolean
-vte_pty_set_utf8(VtePty *pty,
- gboolean utf8,
- GError **error)
+bool
+Pty::set_utf8(bool utf8) const noexcept
{
#if defined(HAVE_TCSETATTR) && defined(IUTF8)
- VtePtyPrivate *priv;
struct termios tio;
- tcflag_t saved_cflag;
-
- g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
-
- priv = pty->priv;
- g_return_val_if_fail (priv->pty_fd != -1, FALSE);
-
- if (tcgetattr(priv->pty_fd, &tio) == -1) {
+ if (tcgetattr(fd(), &tio) == -1) {
vte::util::restore_errno errsv;
- g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
- "%s failed: %s", "tcgetattr", g_strerror(errsv));
- return FALSE;
+ _vte_debug_print(VTE_DEBUG_PTY, "%s failed: %m", "tcgetattr");
+ return false;
}
- saved_cflag = tio.c_iflag;
+ auto saved_cflag = tio.c_iflag;
if (utf8) {
tio.c_iflag |= IUTF8;
} else {
- tio.c_iflag &= ~IUTF8;
+ tio.c_iflag &= ~IUTF8;
}
/* Only set the flag if it changes */
if (saved_cflag != tio.c_iflag &&
- tcsetattr(priv->pty_fd, TCSANOW, &tio) == -1) {
+ tcsetattr(fd(), TCSANOW, &tio) == -1) {
vte::util::restore_errno errsv;
- g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
- "%s failed: %s", "tcgetattr", g_strerror(errsv));
- return FALSE;
+ _vte_debug_print(VTE_DEBUG_PTY, "%s failed: %m", "tcsetattr");
+ return false;
}
#endif
- return TRUE;
-}
-
-/**
- * vte_pty_close:
- * @pty: a #VtePty
- *
- * Since 0.42 this is a no-op.
- *
- * Deprecated: 0.42
- */
-void
-vte_pty_close (VtePty *pty)
-{
-}
-
-/* VTE PTY class */
-
-enum {
- PROP_0,
- PROP_FLAGS,
- PROP_FD,
-};
-
-/* GInitable impl */
-
-static gboolean
-vte_pty_initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
-{
- VtePty *pty = VTE_PTY (initable);
- VtePtyPrivate *priv = pty->priv;
-
- if (cancellable != NULL) {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- "Cancellable initialisation not supported");
- return FALSE;
- }
-
- if (priv->foreign) {
- priv->pty_fd = _vte_pty_open_foreign(priv->pty_fd);
- } else {
- priv->pty_fd = _vte_pty_open_posix();
- }
-
- if (priv->pty_fd == -1) {
- vte::util::restore_errno errsv;
- g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
- "Failed to open PTY: %s", g_strerror(errsv));
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-vte_pty_initable_iface_init (GInitableIface *iface)
-{
- iface->init = vte_pty_initable_init;
-}
-
-/* GObjectClass impl */
-
-G_DEFINE_TYPE_WITH_CODE (VtePty, vte_pty, G_TYPE_OBJECT,
- G_ADD_PRIVATE (VtePty)
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, vte_pty_initable_iface_init))
-
-static void
-vte_pty_init (VtePty *pty)
-{
- VtePtyPrivate *priv;
-
- priv = pty->priv = (VtePtyPrivate *)vte_pty_get_instance_private (pty);
-
- priv->flags = VTE_PTY_DEFAULT;
- priv->pty_fd = -1;
- priv->foreign = FALSE;
-}
-
-static void
-vte_pty_finalize (GObject *object)
-{
- VtePty *pty = VTE_PTY (object);
- VtePtyPrivate *priv = pty->priv;
-
- /* Close the master FD */
- if (priv->pty_fd != -1) {
- close(priv->pty_fd);
- }
-
- G_OBJECT_CLASS (vte_pty_parent_class)->finalize (object);
-}
-
-static void
-vte_pty_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- VtePty *pty = VTE_PTY (object);
- VtePtyPrivate *priv = pty->priv;
-
- switch (property_id) {
- case PROP_FLAGS:
- g_value_set_flags(value, priv->flags);
- break;
-
- case PROP_FD:
- g_value_set_int(value, vte_pty_get_fd(pty));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
- }
-}
-
-static void
-vte_pty_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- VtePty *pty = VTE_PTY (object);
- VtePtyPrivate *priv = pty->priv;
-
- switch (property_id) {
- case PROP_FLAGS:
- priv->flags = (VtePtyFlags) g_value_get_flags(value);
- break;
-
- case PROP_FD:
- priv->pty_fd = g_value_get_int(value);
- priv->foreign = (priv->pty_fd != -1);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-vte_pty_class_init (VtePtyClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->set_property = vte_pty_set_property;
- object_class->get_property = vte_pty_get_property;
- object_class->finalize = vte_pty_finalize;
-
- /**
- * VtePty:flags:
- *
- * Flags.
- */
- g_object_class_install_property
- (object_class,
- PROP_FLAGS,
- g_param_spec_flags ("flags", NULL, NULL,
- VTE_TYPE_PTY_FLAGS,
- VTE_PTY_DEFAULT,
- (GParamFlags) (G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)));
-
- /**
- * VtePty:fd:
- *
- * The file descriptor of the PTY master.
- */
- g_object_class_install_property
- (object_class,
- PROP_FD,
- g_param_spec_int ("fd", NULL, NULL,
- -1, G_MAXINT, -1,
- (GParamFlags) (G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)));
-}
-
-/* public API */
-
-/**
- * vte_pty_error_quark:
- *
- * Error domain for VTE PTY errors. Errors in this domain will be from the #VtePtyError
- * enumeration. See #GError for more information on error domains.
- *
- * Returns: the error domain for VTE PTY errors
- */
-GQuark
-vte_pty_error_quark(void)
-{
- static GQuark quark = 0;
-
- if (G_UNLIKELY (quark == 0))
- quark = g_quark_from_static_string("vte-pty-error");
-
- return quark;
-}
-
-/**
- * vte_pty_new_sync: (constructor)
- * @flags: flags from #VtePtyFlags
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- * @error: (allow-none): return location for a #GError, or %NULL
- *
- * Allocates a new pseudo-terminal.
- *
- * You can later use fork() or the g_spawn_async() family of functions
- * to start a process on the PTY.
- *
- * If using fork(), you MUST call vte_pty_child_setup() in the child.
- *
- * If using g_spawn_async() and friends, you MUST either use
- * vte_pty_child_setup() directly as the child setup function, or call
- * vte_pty_child_setup() from your own child setup function supplied.
- *
- * When using vte_terminal_spawn_sync() with a custom child setup
- * function, vte_pty_child_setup() will be called before the supplied
- * function; you must not call it again.
- *
- * Also, you MUST pass the %G_SPAWN_DO_NOT_REAP_CHILD flag.
- *
- * Returns: (transfer full): a new #VtePty, or %NULL on error with @error filled in
- */
-VtePty *
-vte_pty_new_sync (VtePtyFlags flags,
- GCancellable *cancellable,
- GError **error)
-{
- return (VtePty *) g_initable_new (VTE_TYPE_PTY,
- cancellable,
- error,
- "flags", flags,
- NULL);
-}
-
-/**
- * vte_pty_new_foreign_sync: (constructor)
- * @fd: a file descriptor to the PTY
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- * @error: (allow-none): return location for a #GError, or %NULL
- *
- * Creates a new #VtePty for the PTY master @fd.
- *
- * No entry will be made in the lastlog, utmp or wtmp system files.
- *
- * Note that the newly created #VtePty will take ownership of @fd
- * and close it on finalize.
- *
- * Returns: (transfer full): a new #VtePty for @fd, or %NULL on error with @error filled in
- */
-VtePty *
-vte_pty_new_foreign_sync (int fd,
- GCancellable *cancellable,
- GError **error)
-{
- g_return_val_if_fail(fd >= 0, NULL);
-
- return (VtePty *) g_initable_new (VTE_TYPE_PTY,
- cancellable,
- error,
- "fd", fd,
- NULL);
+ return true;
}
-/**
- * vte_pty_get_fd:
- * @pty: a #VtePty
- *
- * Returns: the file descriptor of the PTY master in @pty. The
- * file descriptor belongs to @pty and must not be closed of have
- * its flags changed
- */
-int
-vte_pty_get_fd (VtePty *pty)
+Pty*
+Pty::create(VtePtyFlags flags)
{
- VtePtyPrivate *priv;
-
- g_return_val_if_fail(VTE_IS_PTY(pty), -1);
-
- priv = pty->priv;
- g_return_val_if_fail(priv->pty_fd != -1, -1);
-
- return priv->pty_fd;
-}
+ auto fd = _vte_pty_open_posix();
+ if (fd == -1)
+ return nullptr;
-typedef struct {
- VtePty* m_pty;
- char* m_working_directory;
- char** m_argv;
- char** m_envv;
- GSpawnFlags m_spawn_flags;
- GSpawnChildSetupFunc m_child_setup;
- gpointer m_child_setup_data;
- GDestroyNotify m_child_setup_data_destroy;
- int m_timeout;
-} AsyncSpawnData;
-
-static AsyncSpawnData*
-async_spawn_data_new (VtePty* pty,
- char const* working_directory,
- char** argv,
- char** envv,
- GSpawnFlags spawn_flags,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GDestroyNotify child_setup_data_destroy,
- int timeout)
-{
- auto data = g_new(AsyncSpawnData, 1);
-
- data->m_pty = (VtePty*)g_object_ref(pty);
- data->m_working_directory = g_strdup(working_directory);
- data->m_argv = g_strdupv(argv);
- data->m_envv = envv ? g_strdupv(envv) : nullptr;
- data->m_spawn_flags = spawn_flags;
- data->m_child_setup = child_setup;
- data->m_child_setup_data = child_setup_data;
- data->m_child_setup_data_destroy = child_setup_data_destroy;
- data->m_timeout = timeout;
-
- return data;
+ return new Pty{fd, flags};
}
-static void
-async_spawn_data_free(gpointer data_)
+Pty*
+Pty::create_foreign(int fd,
+ VtePtyFlags flags)
{
- AsyncSpawnData *data = reinterpret_cast<AsyncSpawnData*>(data_);
-
- g_free(data->m_working_directory);
- g_strfreev(data->m_argv);
- g_strfreev(data->m_envv);
- if (data->m_child_setup_data && data->m_child_setup_data_destroy)
- data->m_child_setup_data_destroy(data->m_child_setup_data);
- g_object_unref(data->m_pty);
+ fd = _vte_pty_open_foreign(fd);
+ if (fd == -1)
+ return nullptr;
- g_free(data);
+ return new Pty{fd, flags};
}
-static void
-async_spawn_run_in_thread(GTask *task,
- gpointer object,
- gpointer data_,
- GCancellable *cancellable)
-{
- AsyncSpawnData *data = reinterpret_cast<AsyncSpawnData*>(data_);
-
- GPid pid;
- GError *error = NULL;
- if (__vte_pty_spawn(data->m_pty,
- data->m_working_directory,
- data->m_argv,
- data->m_envv,
- (GSpawnFlags)data->m_spawn_flags,
- data->m_child_setup, data->m_child_setup_data,
- &pid,
- data->m_timeout,
- cancellable,
- &error))
- g_task_return_pointer(task, g_memdup(&pid, sizeof(pid)), g_free);
- else
- g_task_return_error(task, error);
-}
-
-/**
- * vte_pty_spawn_async:
- * @pty: a #VtePty
- * @working_directory: (allow-none): the name of a directory the command should start
- * in, or %NULL to use the current working directory
- * @argv: (array zero-terminated=1) (element-type filename): child's argument vector
- * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment
- * variables to be added to the environment before starting the process, or %NULL
- * @spawn_flags: flags from #GSpawnFlags
- * @child_setup: (allow-none) (scope async): an extra child setup function to run in the child just before
exec(), or %NULL
- * @child_setup_data: (closure child_setup): user data for @child_setup, or %NULL
- * @child_setup_data_destroy: (destroy child_setup_data): a #GDestroyNotify for @child_setup_data, or %NULL
- * @timeout: a timeout value in ms, or -1 to wait indefinitely
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- *
- * Starts the specified command under the pseudo-terminal @pty.
- * The @argv and @envv lists should be %NULL-terminated.
- * The "TERM" environment variable is automatically set to a default value,
- * but can be overridden from @envv.
- * @pty_flags controls logging the session to the specified system log files.
- *
- * Note that %G_SPAWN_DO_NOT_REAP_CHILD will always be added to @spawn_flags.
- *
- * Note that all open file descriptors will be closed in the child. If you want
- * to keep some file descriptor open for use in the child process, you need to
- * use a child setup function that unsets the FD_CLOEXEC flag on that file
- * descriptor.
- *
- * See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
- *
- * Since: 0.48
- */
-void
-vte_pty_spawn_async(VtePty *pty,
- const char *working_directory,
- char **argv,
- char **envv,
- GSpawnFlags spawn_flags,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GDestroyNotify child_setup_data_destroy,
- int timeout,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_return_if_fail(argv != nullptr);
- g_return_if_fail(!child_setup_data || child_setup);
- g_return_if_fail(!child_setup_data_destroy || child_setup_data);
- g_return_if_fail(cancellable == nullptr || G_IS_CANCELLABLE (cancellable));
- g_return_if_fail(callback);
-
- auto data = async_spawn_data_new(pty,
- working_directory, argv, envv,
- spawn_flags,
- child_setup, child_setup_data, child_setup_data_destroy,
- timeout);
-
- auto task = g_task_new(pty, cancellable, callback, user_data);
- g_task_set_source_tag(task, (void*)vte_pty_spawn_async);
- g_task_set_task_data(task, data, async_spawn_data_free);
- g_task_run_in_thread(task, async_spawn_run_in_thread);
- g_object_unref(task);
-}
-
-/**
- * vte_pty_spawn_finish:
- * @pty: a #VtePty
- * @result: a #GAsyncResult
- * @child_pid: (out) (allow-none) (transfer full): a location to store the child PID, or %NULL
- * @error: (allow-none): return location for a #GError, or %NULL
- *
- * Returns: %TRUE on success, or %FALSE on error with @error filled in
- *
- * Since: 0.48
- */
-gboolean
-vte_pty_spawn_finish(VtePty *pty,
- GAsyncResult *result,
- GPid *child_pid /* out */,
- GError **error)
-{
- g_return_val_if_fail (VTE_IS_PTY (pty), FALSE);
- g_return_val_if_fail (G_IS_TASK (result), FALSE);
- g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
-
- gpointer pidptr = g_task_propagate_pointer(G_TASK(result), error);
- if (pidptr == nullptr) {
- if (child_pid)
- *child_pid = -1;
- return FALSE;
- }
-
- if (child_pid)
- *child_pid = *(GPid*)pidptr;
- if (error)
- *error = nullptr;
-
- g_free(pidptr);
- return TRUE;
-}
+} // namespace vte::base
diff --git a/src/pty.hh b/src/pty.hh
new file mode 100644
index 00000000..f03d2791
--- /dev/null
+++ b/src/pty.hh
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2018, 2019 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "vte/vteenums.h"
+#include "vtetypes.hh"
+
+namespace vte::base {
+
+class Pty {
+private:
+ mutable volatile int m_refcount{1};
+
+ mutable struct {
+ GSpawnChildSetupFunc func{nullptr};
+ void* data{nullptr};
+ } m_extra_child_setup;
+
+ vte::util::smart_fd m_pty_fd{};
+
+ VtePtyFlags m_flags{VTE_PTY_DEFAULT};
+
+public:
+ constexpr Pty(int fd = -1,
+ VtePtyFlags flags = VTE_PTY_DEFAULT) noexcept
+ : m_pty_fd{fd},
+ m_flags{flags}
+ {
+ }
+
+ Pty(Pty const&) = delete;
+ Pty(Pty&&) = delete;
+ Pty operator=(Pty const&) = delete;
+ Pty operator=(Pty&&) = delete;
+
+ Pty* ref() noexcept;
+ void unref() noexcept;
+
+ inline constexpr int fd() const noexcept { return m_pty_fd; }
+ inline constexpr auto flags() const noexcept { return m_flags; }
+
+ void child_setup() const noexcept;
+
+ bool spawn(char const* directory,
+ char** argv,
+ char** envv,
+ GSpawnFlags spawn_flags_,
+ GSpawnChildSetupFunc child_setup,
+ void* child_setup_data,
+ GPid* child_pid /* out */,
+ int timeout,
+ GCancellable* cancellable,
+ GError** error) noexcept;
+
+ bool set_size(int rows,
+ int columns) const noexcept;
+ bool get_size(int* rows,
+ int* columns) const noexcept;
+ bool set_utf8(bool utf8) const noexcept;
+
+ static Pty* create(VtePtyFlags flags);
+ static Pty* create_foreign(int fd,
+ VtePtyFlags flags);
+};
+
+} // namespace vte::base
diff --git a/src/vte.cc b/src/vte.cc
index 45d480c8..abbead5a 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -63,7 +63,6 @@
#include "marshal.h"
#include "vteaccess.h"
#include "vtepty.h"
-#include "vtepty-private.h"
#include "vtegtk.hh"
#include <new> /* placement new */
@@ -130,6 +129,14 @@ vte_g_array_fill(GArray *array, gconstpointer item, guint final_size)
} while (--final_size);
}
+void
+Terminal::unset_widget() noexcept
+{
+ m_real_widget = nullptr;
+ m_terminal = nullptr;
+ m_widget = nullptr;
+}
+
// FIXMEchpe replace this with a method on VteRing
VteRowData*
Terminal::ring_insert(vte::grid::row_t position,
@@ -3111,7 +3118,7 @@ Terminal::child_watch_done(pid_t pid,
m_pty_pid = -1;
/* Close out the PTY. */
- set_pty(nullptr);
+ unset_pty();
/* Tell observers what's happened. */
if (m_real_widget)
@@ -3173,12 +3180,12 @@ io_write_cb(GIOChannel *channel,
void
Terminal::connect_pty_write()
{
- g_assert(m_pty != nullptr);
+ assert(pty());
g_warn_if_fail(m_input_enabled);
if (m_pty_channel == nullptr) {
m_pty_channel =
- g_io_channel_unix_new(vte_pty_get_fd(m_pty));
+ g_io_channel_unix_new(pty()->fd());
}
if (m_pty_output_source == 0) {
@@ -3255,7 +3262,8 @@ Terminal::watch_child (pid_t child_pid)
{
// FIXMEchpe: support passing child_pid = -1 to remove the wathch
g_assert(child_pid != -1);
- g_assert(m_pty != nullptr);
+ if (!pty())
+ return;
GObject *object = G_OBJECT(m_terminal);
g_object_freeze_notify(object);
@@ -3286,86 +3294,6 @@ Terminal::watch_child (pid_t child_pid)
g_object_thaw_notify(object);
}
-/*
- * Terminal::spawn_sync:
- * @pty_flags: flags from #VtePtyFlags
- * @working_directory: (allow-none): the name of a directory the command should start
- * in, or %NULL to use the current working directory
- * @argv: (array zero-terminated=1) (element-type filename): child's argument vector
- * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment
- * variables to be added to the environment before starting the process, or %NULL
- * @spawn_flags: flags from #GSpawnFlags
- * @child_setup: (allow-none) (scope call): an extra child setup function to run in the child just before
exec(), or %NULL
- * @child_setup_data: user data for @child_setup
- * @child_pid: (out) (allow-none) (transfer full): a location to store the child PID, or %NULL
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- * @error: (allow-none): return location for a #GError, or %NULL
- *
- * Starts the specified command under a newly-allocated controlling
- * pseudo-terminal. The @argv and @envv lists should be %NULL-terminated.
- * The "TERM" environment variable is automatically set to a default value,
- * but can be overridden from @envv.
- * @pty_flags controls logging the session to the specified system log files.
- *
- * Note that %G_SPAWN_DO_NOT_REAP_CHILD will always be added to @spawn_flags.
- *
- * See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
- *
- * Returns: %TRUE on success, or %FALSE on error with @error filled in
- */
-bool
-Terminal::spawn_sync(VtePtyFlags pty_flags,
- const char *working_directory,
- char **argv,
- char **envv,
- GSpawnFlags spawn_flags_,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GPid *child_pid /* out */,
- GCancellable *cancellable,
- GError **error)
-{
- guint spawn_flags = (guint)spawn_flags_;
- VtePty *new_pty;
- GPid pid;
-
- g_assert(argv != nullptr);
- g_assert(child_setup_data == nullptr || child_setup != nullptr);
- g_assert(error == nullptr || *error == nullptr);
-
- new_pty = vte_terminal_pty_new_sync(m_terminal, pty_flags, cancellable, error);
- if (new_pty == nullptr)
- return false;
-
- /* We do NOT support this flag. If you want to have some FD open in the child
- * process, simply use a child setup function that unsets the CLOEXEC flag
- * on that FD.
- */
- spawn_flags &= ~G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
-
- if (!__vte_pty_spawn(new_pty,
- working_directory,
- argv,
- envv,
- (GSpawnFlags)spawn_flags,
- child_setup, child_setup_data,
- &pid,
- -1 /* no timeout */, cancellable,
- error)) {
- g_object_unref(new_pty);
- return false;
- }
-
- set_pty(new_pty);
- g_object_unref (new_pty);
- watch_child(pid);
-
- if (child_pid)
- *child_pid = pid;
-
- return true;
-}
-
/* Handle an EOF from the client. */
void
Terminal::pty_channel_eof()
@@ -3374,7 +3302,7 @@ Terminal::pty_channel_eof()
g_object_freeze_notify(object);
- set_pty(nullptr);
+ unset_pty();
/* Emit a signal that we read an EOF. */
queue_eof();
@@ -4126,8 +4054,8 @@ Terminal::send_child(char const* data,
/* If there's a place for it to go, add the data to the
* outgoing buffer. */
- // FIXMEchpe: shouldn't require m_pty for this
- if ((cooked_length > 0) && (m_pty != NULL)) {
+ // FIXMEchpe: shouldn't require pty for this
+ if ((cooked_length > 0) && pty()) {
_vte_byte_array_append(m_outgoing, cooked, cooked_length);
_VTE_DEBUG_IF(VTE_DEBUG_KEYBOARD) {
for (i = 0; i < cooked_length; i++) {
@@ -4198,7 +4126,7 @@ Terminal::feed_child_binary(guint8 const* data,
/* If there's a place for it to go, add the data to the
* outgoing buffer. */
- if (m_pty != NULL) {
+ if (pty()) {
_vte_byte_array_append(m_outgoing,
data, length);
/* If we need to start waiting for the child pty to
@@ -4706,8 +4634,8 @@ Terminal::widget_key_press(GdkEventKey *event)
suppress_meta_esc = TRUE;
break;
case VTE_ERASE_TTY:
- if (m_pty != nullptr &&
- tcgetattr(vte_pty_get_fd(m_pty), &tio) != -1)
+ if (pty() &&
+ tcgetattr(pty()->fd(), &tio) != -1)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
normal_length = 1;
@@ -4719,8 +4647,8 @@ Terminal::widget_key_press(GdkEventKey *event)
#ifndef _POSIX_VDISABLE
#define _POSIX_VDISABLE '\0'
#endif
- if (m_pty != nullptr &&
- tcgetattr(vte_pty_get_fd(m_pty), &tio) != -1 &&
+ if (pty() &&
+ tcgetattr(pty()->fd(), &tio) != -1 &&
tio.c_cc[VERASE] != _POSIX_VDISABLE)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
@@ -4756,8 +4684,8 @@ Terminal::widget_key_press(GdkEventKey *event)
normal_length = 1;
break;
case VTE_ERASE_TTY:
- if (m_pty != nullptr &&
- tcgetattr(vte_pty_get_fd(m_pty), &tio) != -1)
+ if (pty() &&
+ tcgetattr(pty()->fd(), &tio) != -1)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
normal_length = 1;
@@ -7529,11 +7457,11 @@ Terminal::set_cell_height_scale(double scale)
void
Terminal::refresh_size()
{
- if (!m_pty)
+ if (!pty())
return;
int rows, columns;
- if (!vte_pty_get_size(m_pty, &rows, &columns, nullptr)) {
+ if (!pty()->get_size(&rows, &columns)) {
/* Error reading PTY size, use defaults */
rows = VTE_ROWS;
columns = VTE_COLUMNS;
@@ -7701,16 +7629,12 @@ Terminal::set_size(long columns,
old_rows = m_row_count;
old_columns = m_column_count;
- if (m_pty != NULL) {
- GError *error = NULL;
-
+ if (pty()) {
/* Try to set the terminal size, and read it back,
* in case something went awry.
*/
- if (!vte_pty_set_size(m_pty, rows, columns, &error)) {
- g_warning("%s\n", error->message);
- g_error_free(error);
- }
+ if (!pty()->set_size(rows, columns))
+ g_warning("Failed to set PTY size: %m\n");
refresh_size();
} else {
m_row_count = rows;
@@ -7914,7 +7838,6 @@ Terminal::Terminal(vte::platform::Widget* w,
/* Setting the terminal type and size requires the PTY master to
* be set up properly first. */
- m_pty = nullptr;
set_size(VTE_COLUMNS, VTE_ROWS);
m_pty_input_source = 0;
m_pty_output_source = 0;
@@ -8204,7 +8127,7 @@ Terminal::~Terminal()
int sel;
terminate_child();
- set_pty(nullptr, false /* don't process remaining data */);
+ unset_pty(false /* don't notify widget */, false /* don't process remaining data */);
remove_update_timeout(this);
/* Stop processing input. */
@@ -10197,50 +10120,61 @@ Terminal::reset(bool clear_tabstops,
g_object_thaw_notify(object);
}
-bool
-Terminal::set_pty(VtePty *new_pty,
- bool process_remaining)
+void
+Terminal::unset_pty(bool notify_widget,
+ bool process_remaining)
{
- if (new_pty == m_pty)
- return false;
+ /* This may be called from inside or from widget,
+ * and must notify the widget if not called from it.
+ */
- if (m_pty != nullptr) {
- disconnect_pty_read();
- disconnect_pty_write();
+ disconnect_pty_read();
+ disconnect_pty_write();
- if (m_pty_channel != nullptr) {
- g_io_channel_unref (m_pty_channel);
- m_pty_channel = nullptr;
- }
+ if (m_pty_channel != nullptr) {
+ g_io_channel_unref (m_pty_channel);
+ m_pty_channel = nullptr;
+ }
- /* Take one last shot at processing whatever data is pending,
- * then flush the buffers in case we're about to run a new
- * command, disconnecting the timeout. */
- if (!m_incoming_queue.empty() && process_remaining) {
- process_incoming();
- while (!m_incoming_queue.empty())
- m_incoming_queue.pop();
+ /* Take one last shot at processing whatever data is pending,
+ * then flush the buffers in case we're about to run a new
+ * command, disconnecting the timeout. */
+ if (!m_incoming_queue.empty() && process_remaining) {
+ process_incoming();
+ while (!m_incoming_queue.empty())
+ m_incoming_queue.pop();
- m_input_bytes = 0;
- }
- stop_processing(this);
+ m_input_bytes = 0;
+ }
+ stop_processing(this);
+
+ m_utf8_decoder.reset(); // FIXMEchpe necessary here?
+
+ /* Clear the outgoing buffer as well. */
+ _vte_byte_array_clear(m_outgoing);
- m_utf8_decoder.reset(); // FIXMEchpe necessary here?
+ m_pty.reset();
- /* Clear the outgoing buffer as well. */
- _vte_byte_array_clear(m_outgoing);
+ if (notify_widget && widget())
+ widget()->unset_pty();
+}
- g_object_unref(m_pty);
- m_pty = nullptr;
+bool
+Terminal::set_pty(vte::base::Pty *new_pty,
+ bool process_remaining)
+{
+ if (pty().get() == new_pty)
+ return false;
+
+ if (pty()) {
+ unset_pty(false /* don't notify widget */, process_remaining);
}
- if (new_pty == nullptr) {
- m_pty = nullptr;
+ m_pty = vte::base::make_ref(new_pty);
+ if (!new_pty)
return true;
- }
- m_pty = (VtePty *)g_object_ref(new_pty);
- int pty_master = vte_pty_get_fd(m_pty);
+ int pty_master = pty()->fd();
/* Ensure the FD is non-blocking */
int flags = fcntl(pty_master, F_GETFL);
@@ -10251,11 +10185,8 @@ Terminal::set_pty(VtePty *new_pty,
set_size(m_column_count, m_row_count);
- GError *error = nullptr;
- if (!vte_pty_set_utf8(m_pty, m_using_utf8, &error)) {
- g_warning ("Failed to set UTF8 mode: %s\n", error->message);
- g_error_free (error);
- }
+ if (!pty()->set_utf8(m_using_utf8))
+ g_warning ("Failed to set UTF8 mode: %m\n");
/* Open channels to listen for input on. */
connect_pty_read();
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index fe5d1c41..e3bff16b 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -56,6 +56,7 @@
#include "widget.hh"
#include "vtegtk.hh"
+#include "vteptyinternal.hh"
#include "vteregexinternal.hh"
#define I_(string) (g_intern_static_string(string))
@@ -2600,10 +2601,9 @@ vte_terminal_watch_child (VteTerminal *terminal,
g_return_if_fail(VTE_IS_TERMINAL(terminal));
g_return_if_fail(child_pid != -1);
- auto impl = IMPL(terminal);
- g_return_if_fail(impl->m_pty != NULL);
+ g_return_if_fail(WIDGET(terminal)->pty() != nullptr);
- impl->watch_child(child_pid);
+ IMPL(terminal)->watch_child(child_pid);
}
/**
@@ -2663,16 +2663,34 @@ vte_terminal_spawn_sync(VteTerminal *terminal,
g_return_val_if_fail(child_setup_data == NULL || child_setup, FALSE);
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
- return IMPL(terminal)->spawn_sync(pty_flags,
- working_directory,
- argv,
- envv,
- spawn_flags_,
- child_setup,
- child_setup_data,
- child_pid,
- cancellable,
- error);
+ auto new_pty = vte_terminal_pty_new_sync(terminal, pty_flags, cancellable, error);
+ if (new_pty == nullptr)
+ return false;
+
+ GPid pid;
+ if (!_vte_pty_spawn(new_pty,
+ working_directory,
+ argv,
+ envv,
+ spawn_flags_,
+ child_setup, child_setup_data,
+ &pid,
+ -1 /* no timeout */,
+ cancellable,
+ error)) {
+ g_object_unref(new_pty);
+ return false;
+ }
+
+ vte_terminal_set_pty(terminal, new_pty);
+ g_object_unref (new_pty);
+
+ vte_terminal_watch_child(terminal, pid);
+
+ if (child_pid)
+ *child_pid = pid;
+
+ return true;
}
typedef struct {
@@ -4232,7 +4250,7 @@ vte_terminal_set_pty(VteTerminal *terminal,
GObject *object = G_OBJECT(terminal);
g_object_freeze_notify(object);
- if (IMPL(terminal)->set_pty(pty))
+ if (WIDGET(terminal)->set_pty(pty))
g_object_notify_by_pspec(object, pspecs[PROP_PTY]);
g_object_thaw_notify(object);
@@ -4251,7 +4269,7 @@ vte_terminal_get_pty(VteTerminal *terminal)
{
g_return_val_if_fail (VTE_IS_TERMINAL (terminal), NULL);
- return IMPL(terminal)->m_pty;
+ return WIDGET(terminal)->pty();
}
/**
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 17d1c4db..216e268f 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -37,6 +37,7 @@
#include "vteregexinternal.hh"
#include "chunk.hh"
+#include "pty.hh"
#include "utf8.hh"
#include <list>
@@ -311,9 +312,16 @@ public:
~Terminal();
public:
- vte::platform::Widget* m_real_widget;
- VteTerminal *m_terminal;
- GtkWidget *m_widget;
+ vte::platform::Widget* m_real_widget{nullptr};
+ inline constexpr auto widget() const noexcept { return m_real_widget; }
+
+ VteTerminal *m_terminal{nullptr};
+ inline constexpr auto vte_terminal() const noexcept { return m_terminal; }
+
+ GtkWidget *m_widget{nullptr};
+ inline constexpr auto gtk_widget() const noexcept { return m_widget; }
+
+ void unset_widget() noexcept;
/* Metric and sizing data: dimensions of the window */
vte::grid::row_t m_row_count{VTE_ROWS};
@@ -327,7 +335,14 @@ public:
vte::terminal::modes::Private m_modes_private{};
/* PTY handling data. */
- VtePty *m_pty;
+ vte::base::RefPtr<vte::base::Pty> m_pty{};
+ inline constexpr auto& pty() const noexcept { return m_pty; }
+
+ void unset_pty(bool notify_widget = true,
+ bool process_remaining = true);
+ bool set_pty(vte::base::Pty* pty,
+ bool process_remaining = true);
+
GIOChannel *m_pty_channel; /* master channel */
guint m_pty_input_source;
guint m_pty_output_source;
@@ -928,32 +943,6 @@ public:
void im_reset();
void im_update_cursor();
- bool spawn_sync(VtePtyFlags pty_flags,
- const char *working_directory,
- char **argv,
- char **envv,
- GSpawnFlags spawn_flags_,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GPid *child_pid /* out */,
- GCancellable *cancellable,
- GError **error);
-#if 0
- void spawn_async(VtePtyFlags pty_flags,
- const char *working_directory,
- char **argv,
- char **envv,
- GSpawnFlags spawn_flags_,
- GSpawnChildSetupFunc child_setup,
- gpointer child_setup_data,
- GDestroyNotify child_setup_data_destroy,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- bool spawn_finish(GAsyncResult *result,
- GPid *child_pid /* out */);
-#endif
-
void reset(bool clear_tabstops,
bool clear_history,
bool from_api = false);
@@ -1251,8 +1240,6 @@ public:
bool set_font_scale(double scale);
bool set_input_enabled(bool enabled);
bool set_mouse_autohide(bool autohide);
- bool set_pty(VtePty *pty,
- bool proces_remaining = true);
bool set_rewrap_on_resize(bool rewrap);
bool set_scrollback_lines(long lines);
bool set_scroll_on_keystroke(bool scroll);
diff --git a/src/vtepty.cc b/src/vtepty.cc
new file mode 100644
index 00000000..78f89c44
--- /dev/null
+++ b/src/vtepty.cc
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2001,2002 Red Hat, Inc.
+ * Copyright © 2009, 2010, 2019 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION: vte-pty
+ * @short_description: Functions for starting a new process on a new pseudo-terminal and for
+ * manipulating pseudo-terminals
+ *
+ * The terminal widget uses these functions to start commands with new controlling
+ * pseudo-terminals and to resize pseudo-terminals.
+ */
+
+#include <config.h>
+
+#include <vte/vte.h>
+#include "vtetypes.hh"
+#include "vtespawn.hh"
+
+#include <errno.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include "debug.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "pty.hh"
+#include "vteptyinternal.hh"
+
+#if !GLIB_CHECK_VERSION(2, 42, 0)
+#define G_PARAM_EXPLICIT_NOTIFY 0
+#endif
+
+#define I_(string) (g_intern_static_string(string))
+
+typedef struct _VtePtyPrivate VtePtyPrivate;
+
+typedef struct {
+ GSpawnChildSetupFunc extra_child_setup;
+ gpointer extra_child_setup_data;
+} VtePtyChildSetupData;
+
+/**
+ * VtePty:
+ */
+struct _VtePty {
+ GObject parent_instance;
+
+ /* <private> */
+ VtePtyPrivate *priv;
+};
+
+struct _VtePtyPrivate {
+ vte::base::Pty* pty; /* owned */
+ int foreign_fd; /* foreign FD if != -1 */
+ VtePtyFlags flags;
+};
+
+struct _VtePtyClass {
+ GObjectClass parent_class;
+};
+
+vte::base::Pty*
+_vte_pty_get_impl(VtePty* pty)
+{
+ return pty->priv->pty;
+}
+
+#define IMPL(wrapper) (_vte_pty_get_impl(wrapper))
+
+/**
+ * vte_pty_child_setup:
+ * @pty: a #VtePty
+ *
+ * FIXMEchpe
+ */
+void
+vte_pty_child_setup (VtePty *pty)
+{
+ g_return_if_fail(pty != nullptr);
+ auto impl = IMPL(pty);
+ g_return_if_fail(impl != nullptr);
+
+ impl->child_setup();
+}
+
+/*
+ * __vte_pty_spawn:
+ * @pty: a #VtePty
+ * @directory: the name of a directory the command should start in, or %NULL
+ * to use the cwd
+ * @argv: child's argument vector
+ * @envv: a list of environment variables to be added to the environment before
+ * starting the process, or %NULL
+ * @spawn_flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @child_setup_data: user data for @child_setup
+ * @child_pid: a location to store the child PID, or %NULL
+ * @timeout: a timeout value in ms, or %NULL
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Uses g_spawn_async() to spawn the command in @argv. The child's environment will
+ * be the parent environment with the variables in @envv set afterwards.
+ *
+ * Enforces the vte_terminal_watch_child() requirements by adding
+ * %G_SPAWN_DO_NOT_REAP_CHILD to @spawn_flags.
+ *
+ * Note that the %G_SPAWN_LEAVE_DESCRIPTORS_OPEN flag is not supported;
+ * it will be cleared!
+ *
+ * If spawning the command in @working_directory fails because the child
+ * is unable to chdir() to it, falls back trying to spawn the command
+ * in the parent's working directory.
+ *
+ * Returns: %TRUE on success, or %FALSE on failure with @error filled in
+ */
+gboolean
+_vte_pty_spawn(VtePty *pty,
+ const char *directory,
+ char **argv,
+ char **envv,
+ GSpawnFlags spawn_flags_,
+ GSpawnChildSetupFunc child_setup,
+ gpointer child_setup_data,
+ GPid *child_pid /* out */,
+ int timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+
+ auto impl = IMPL(pty);
+ g_return_val_if_fail(impl != nullptr, FALSE);
+
+ return impl->spawn(directory,
+ argv,
+ envv,
+ spawn_flags_,
+ child_setup,
+ child_setup_data,
+ child_pid,
+ timeout,
+ cancellable,
+ error);
+}
+
+/**
+ * vte_pty_set_size:
+ * @pty: a #VtePty
+ * @rows: the desired number of rows
+ * @columns: the desired number of columns
+ * @error: (allow-none): return location to store a #GError, or %NULL
+ *
+ * Attempts to resize the pseudo terminal's window size. If successful, the
+ * OS kernel will send #SIGWINCH to the child process group.
+ *
+ * If setting the window size failed, @error will be set to a #GIOError.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ */
+gboolean
+vte_pty_set_size(VtePty *pty,
+ int rows,
+ int columns,
+ GError **error)
+{
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+ auto impl = IMPL(pty);
+ g_return_val_if_fail(impl != nullptr, FALSE);
+
+ if (impl->set_size(rows, columns))
+ return true;
+
+ vte::util::restore_errno errsv;
+ g_set_error(error, G_IO_ERROR,
+ g_io_error_from_errno(errsv),
+ "Failed to set window size: %s",
+ g_strerror(errsv));
+
+ return false;
+}
+
+/**
+ * vte_pty_get_size:
+ * @pty: a #VtePty
+ * @rows: (out) (allow-none): a location to store the number of rows, or %NULL
+ * @columns: (out) (allow-none): a location to store the number of columns, or %NULL
+ * @error: return location to store a #GError, or %NULL
+ *
+ * Reads the pseudo terminal's window size.
+ *
+ * If getting the window size failed, @error will be set to a #GIOError.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ */
+gboolean
+vte_pty_get_size(VtePty *pty,
+ int *rows,
+ int *columns,
+ GError **error)
+{
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+ auto impl = IMPL(pty);
+ g_return_val_if_fail(impl != nullptr, FALSE);
+
+ if (impl->get_size(rows, columns))
+ return true;
+
+ vte::util::restore_errno errsv;
+ g_set_error(error, G_IO_ERROR,
+ g_io_error_from_errno(errsv),
+ "Failed to get window size: %s",
+ g_strerror(errsv));
+ return false;
+}
+
+/**
+ * vte_pty_set_utf8:
+ * @pty: a #VtePty
+ * @utf8: whether or not the pty is in UTF-8 mode
+ * @error: (allow-none): return location to store a #GError, or %NULL
+ *
+ * Tells the kernel whether the terminal is UTF-8 or not, in case it can make
+ * use of the info. Linux 2.6.5 or so defines IUTF8 to make the line
+ * discipline do multibyte backspace correctly.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ */
+gboolean
+vte_pty_set_utf8(VtePty *pty,
+ gboolean utf8,
+ GError **error)
+{
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+ auto impl = IMPL(pty);
+ g_return_val_if_fail(impl != nullptr, FALSE);
+
+ if (impl->set_utf8(utf8))
+ return true;
+
+ vte::util::restore_errno errsv;
+ g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
+ "%s failed: %s", "tc[sg]etattr", g_strerror(errsv));
+ return false;
+}
+
+/**
+ * vte_pty_close:
+ * @pty: a #VtePty
+ *
+ * Since 0.42 this is a no-op.
+ *
+ * Deprecated: 0.42
+ */
+void
+vte_pty_close (VtePty *pty)
+{
+ /* impl->close(); */
+}
+
+/* VTE PTY class */
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_FD,
+};
+
+/* GInitable impl */
+
+static gboolean
+vte_pty_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ VtePty *pty = VTE_PTY (initable);
+ VtePtyPrivate *priv = pty->priv;
+
+ if (cancellable != NULL) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Cancellable initialisation not supported");
+ return FALSE;
+ }
+
+ if (priv->foreign_fd != -1) {
+ priv->pty = vte::base::Pty::create_foreign(priv->foreign_fd, priv->flags);
+ priv->foreign_fd = -1;
+ } else {
+ priv->pty = vte::base::Pty::create(priv->flags);
+ }
+
+ if (priv->pty == nullptr) {
+ vte::util::restore_errno errsv;
+ g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv),
+ "Failed to open PTY: %s", g_strerror(errsv));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+vte_pty_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = vte_pty_initable_init;
+}
+
+/* GObjectClass impl */
+
+G_DEFINE_TYPE_WITH_CODE (VtePty, vte_pty, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (VtePty)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, vte_pty_initable_iface_init))
+
+static void
+vte_pty_init (VtePty *pty)
+{
+ VtePtyPrivate *priv;
+
+ priv = pty->priv = (VtePtyPrivate *)vte_pty_get_instance_private (pty);
+
+ priv->pty = nullptr;
+ priv->foreign_fd = -1;
+ priv->flags = VTE_PTY_DEFAULT;
+}
+
+static void
+vte_pty_finalize (GObject *object)
+{
+ VtePty *pty = VTE_PTY (object);
+ VtePtyPrivate *priv = pty->priv;
+
+ if (priv->pty != nullptr)
+ priv->pty->unref();
+
+ G_OBJECT_CLASS (vte_pty_parent_class)->finalize (object);
+}
+
+static void
+vte_pty_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ VtePty *pty = VTE_PTY (object);
+ VtePtyPrivate *priv = pty->priv;
+
+ switch (property_id) {
+ case PROP_FLAGS:
+ g_value_set_flags(value, priv->flags);
+ break;
+
+ case PROP_FD:
+ g_value_set_int(value, vte_pty_get_fd(pty));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+vte_pty_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ VtePty *pty = VTE_PTY (object);
+ VtePtyPrivate *priv = pty->priv;
+
+ switch (property_id) {
+ case PROP_FLAGS:
+ priv->flags = (VtePtyFlags) g_value_get_flags(value);
+ break;
+
+ case PROP_FD:
+ priv->foreign_fd = g_value_get_int(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+vte_pty_class_init (VtePtyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = vte_pty_set_property;
+ object_class->get_property = vte_pty_get_property;
+ object_class->finalize = vte_pty_finalize;
+
+ /**
+ * VtePty:flags:
+ *
+ * Flags.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_FLAGS,
+ g_param_spec_flags ("flags", NULL, NULL,
+ VTE_TYPE_PTY_FLAGS,
+ VTE_PTY_DEFAULT,
+ (GParamFlags) (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)));
+
+ /**
+ * VtePty:fd:
+ *
+ * The file descriptor of the PTY master.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_FD,
+ g_param_spec_int ("fd", NULL, NULL,
+ -1, G_MAXINT, -1,
+ (GParamFlags) (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)));
+}
+
+/* public API */
+
+/**
+ * vte_pty_error_quark:
+ *
+ * Error domain for VTE PTY errors. Errors in this domain will be from the #VtePtyError
+ * enumeration. See #GError for more information on error domains.
+ *
+ * Returns: the error domain for VTE PTY errors
+ */
+GQuark
+vte_pty_error_quark(void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string("vte-pty-error");
+
+ return quark;
+}
+
+/**
+ * vte_pty_new_sync: (constructor)
+ * @flags: flags from #VtePtyFlags
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Allocates a new pseudo-terminal.
+ *
+ * You can later use fork() or the g_spawn_async() family of functions
+ * to start a process on the PTY.
+ *
+ * If using fork(), you MUST call vte_pty_child_setup() in the child.
+ *
+ * If using g_spawn_async() and friends, you MUST either use
+ * vte_pty_child_setup() directly as the child setup function, or call
+ * vte_pty_child_setup() from your own child setup function supplied.
+ *
+ * When using vte_terminal_spawn_sync() with a custom child setup
+ * function, vte_pty_child_setup() will be called before the supplied
+ * function; you must not call it again.
+ *
+ * Also, you MUST pass the %G_SPAWN_DO_NOT_REAP_CHILD flag.
+ *
+ * Returns: (transfer full): a new #VtePty, or %NULL on error with @error filled in
+ */
+VtePty *
+vte_pty_new_sync (VtePtyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return (VtePty *) g_initable_new (VTE_TYPE_PTY,
+ cancellable,
+ error,
+ "flags", flags,
+ NULL);
+}
+
+/**
+ * vte_pty_new_foreign_sync: (constructor)
+ * @fd: a file descriptor to the PTY
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Creates a new #VtePty for the PTY master @fd.
+ *
+ * No entry will be made in the lastlog, utmp or wtmp system files.
+ *
+ * Note that the newly created #VtePty will take ownership of @fd
+ * and close it on finalize.
+ *
+ * Returns: (transfer full): a new #VtePty for @fd, or %NULL on error with @error filled in
+ */
+VtePty *
+vte_pty_new_foreign_sync (int fd,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail(fd != -1, nullptr);
+
+ return (VtePty *) g_initable_new (VTE_TYPE_PTY,
+ cancellable,
+ error,
+ "fd", fd,
+ NULL);
+}
+
+/**
+ * vte_pty_get_fd:
+ * @pty: a #VtePty
+ *
+ * Returns: the file descriptor of the PTY master in @pty. The
+ * file descriptor belongs to @pty and must not be closed or have
+ * its flags changed
+ */
+int
+vte_pty_get_fd (VtePty *pty)
+{
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+ auto impl = IMPL(pty);
+ g_return_val_if_fail(impl != nullptr, FALSE);
+
+ return impl->fd();
+}
+
+typedef struct {
+ VtePty* m_pty;
+ char* m_working_directory;
+ char** m_argv;
+ char** m_envv;
+ GSpawnFlags m_spawn_flags;
+ GSpawnChildSetupFunc m_child_setup;
+ gpointer m_child_setup_data;
+ GDestroyNotify m_child_setup_data_destroy;
+ int m_timeout;
+} AsyncSpawnData;
+
+static AsyncSpawnData*
+async_spawn_data_new (VtePty* pty,
+ char const* working_directory,
+ char** argv,
+ char** envv,
+ GSpawnFlags spawn_flags,
+ GSpawnChildSetupFunc child_setup,
+ gpointer child_setup_data,
+ GDestroyNotify child_setup_data_destroy,
+ int timeout)
+{
+ auto data = g_new(AsyncSpawnData, 1);
+
+ data->m_pty = (VtePty*)g_object_ref(pty);
+ data->m_working_directory = g_strdup(working_directory);
+ data->m_argv = g_strdupv(argv);
+ data->m_envv = envv ? g_strdupv(envv) : nullptr;
+ data->m_spawn_flags = spawn_flags;
+ data->m_child_setup = child_setup;
+ data->m_child_setup_data = child_setup_data;
+ data->m_child_setup_data_destroy = child_setup_data_destroy;
+ data->m_timeout = timeout;
+
+ return data;
+}
+
+static void
+async_spawn_data_free(gpointer data_)
+{
+ AsyncSpawnData *data = reinterpret_cast<AsyncSpawnData*>(data_);
+
+ g_free(data->m_working_directory);
+ g_strfreev(data->m_argv);
+ g_strfreev(data->m_envv);
+ if (data->m_child_setup_data && data->m_child_setup_data_destroy)
+ data->m_child_setup_data_destroy(data->m_child_setup_data);
+ g_object_unref(data->m_pty);
+
+ g_free(data);
+}
+
+static void
+async_spawn_run_in_thread(GTask *task,
+ gpointer object,
+ gpointer data_,
+ GCancellable *cancellable)
+{
+ AsyncSpawnData *data = reinterpret_cast<AsyncSpawnData*>(data_);
+
+ GPid pid;
+ GError *error = NULL;
+ if (_vte_pty_spawn(data->m_pty,
+ data->m_working_directory,
+ data->m_argv,
+ data->m_envv,
+ (GSpawnFlags)data->m_spawn_flags,
+ data->m_child_setup, data->m_child_setup_data,
+ &pid,
+ data->m_timeout,
+ cancellable,
+ &error))
+ g_task_return_pointer(task, g_memdup(&pid, sizeof(pid)), g_free);
+ else
+ g_task_return_error(task, error);
+}
+
+/**
+ * vte_pty_spawn_async:
+ * @pty: a #VtePty
+ * @working_directory: (allow-none): the name of a directory the command should start
+ * in, or %NULL to use the current working directory
+ * @argv: (array zero-terminated=1) (element-type filename): child's argument vector
+ * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment
+ * variables to be added to the environment before starting the process, or %NULL
+ * @spawn_flags: flags from #GSpawnFlags
+ * @child_setup: (allow-none) (scope async): an extra child setup function to run in the child just before
exec(), or %NULL
+ * @child_setup_data: (closure child_setup): user data for @child_setup, or %NULL
+ * @child_setup_data_destroy: (destroy child_setup_data): a #GDestroyNotify for @child_setup_data, or %NULL
+ * @timeout: a timeout value in ms, or -1 to wait indefinitely
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ *
+ * Starts the specified command under the pseudo-terminal @pty.
+ * The @argv and @envv lists should be %NULL-terminated.
+ * The "TERM" environment variable is automatically set to a default value,
+ * but can be overridden from @envv.
+ * @pty_flags controls logging the session to the specified system log files.
+ *
+ * Note that %G_SPAWN_DO_NOT_REAP_CHILD will always be added to @spawn_flags.
+ *
+ * Note that all open file descriptors will be closed in the child. If you want
+ * to keep some file descriptor open for use in the child process, you need to
+ * use a child setup function that unsets the FD_CLOEXEC flag on that file
+ * descriptor.
+ *
+ * See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
+ *
+ * Since: 0.48
+ */
+void
+vte_pty_spawn_async(VtePty *pty,
+ const char *working_directory,
+ char **argv,
+ char **envv,
+ GSpawnFlags spawn_flags,
+ GSpawnChildSetupFunc child_setup,
+ gpointer child_setup_data,
+ GDestroyNotify child_setup_data_destroy,
+ int timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail(argv != nullptr);
+ g_return_if_fail(!child_setup_data || child_setup);
+ g_return_if_fail(!child_setup_data_destroy || child_setup_data);
+ g_return_if_fail(cancellable == nullptr || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail(callback);
+
+ auto data = async_spawn_data_new(pty,
+ working_directory, argv, envv,
+ spawn_flags,
+ child_setup, child_setup_data, child_setup_data_destroy,
+ timeout);
+
+ auto task = g_task_new(pty, cancellable, callback, user_data);
+ g_task_set_source_tag(task, (void*)vte_pty_spawn_async);
+ g_task_set_task_data(task, data, async_spawn_data_free);
+ g_task_run_in_thread(task, async_spawn_run_in_thread);
+ g_object_unref(task);
+}
+
+/**
+ * vte_pty_spawn_finish:
+ * @pty: a #VtePty
+ * @result: a #GAsyncResult
+ * @child_pid: (out) (allow-none) (transfer full): a location to store the child PID, or %NULL
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Returns: %TRUE on success, or %FALSE on error with @error filled in
+ *
+ * Since: 0.48
+ */
+gboolean
+vte_pty_spawn_finish(VtePty *pty,
+ GAsyncResult *result,
+ GPid *child_pid /* out */,
+ GError **error)
+{
+ g_return_val_if_fail (VTE_IS_PTY (pty), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
+
+ gpointer pidptr = g_task_propagate_pointer(G_TASK(result), error);
+ if (pidptr == nullptr) {
+ if (child_pid)
+ *child_pid = -1;
+ return FALSE;
+ }
+
+ if (child_pid)
+ *child_pid = *(GPid*)pidptr;
+ if (error)
+ *error = nullptr;
+
+ g_free(pidptr);
+ return TRUE;
+}
diff --git a/src/vteptyinternal.hh b/src/vteptyinternal.hh
new file mode 100644
index 00000000..12dcf38c
--- /dev/null
+++ b/src/vteptyinternal.hh
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2019 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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/>.
+ */
+
+#pragma once
+
+#include "vte/vtepty.h"
+#include "pty.hh"
+
+vte::base::Pty* _vte_pty_get_impl(VtePty* pty);
+
+gboolean _vte_pty_spawn(VtePty *pty,
+ const char *working_directory,
+ char **argv,
+ char **envv,
+ GSpawnFlags spawn_flags,
+ GSpawnChildSetupFunc child_setup,
+ gpointer child_setup_data,
+ GPid *child_pid /* out */,
+ int timeout,
+ GCancellable *cancellable,
+ GError **error);
diff --git a/src/vtetypes.hh b/src/vtetypes.hh
index 4092e43f..51dc424a 100644
--- a/src/vtetypes.hh
+++ b/src/vtetypes.hh
@@ -21,6 +21,7 @@
#include <gdk/gdk.h>
#include <errno.h>
+#include <cassert>
#include <cstdint>
#include <memory>
@@ -211,16 +212,16 @@ namespace util {
class smart_fd {
public:
- smart_fd() : m_fd(-1) { }
- explicit smart_fd(int fd) : m_fd(fd) { }
- ~smart_fd() { if (m_fd != -1) { restore_errno errsv; close(m_fd); } }
+ constexpr smart_fd() noexcept = default;
+ explicit constexpr smart_fd(int fd) noexcept : m_fd{fd} { }
+ ~smart_fd() noexcept { if (m_fd != -1) { restore_errno errsv; close(m_fd); } }
- inline smart_fd& operator = (int rhs) { if (m_fd != -1) { restore_errno errsv; close(m_fd);
} m_fd = rhs; return *this; }
- inline smart_fd& operator = (smart_fd& rhs) { if (&rhs != this) { if (m_fd != -1) {
restore_errno errsv; close(m_fd); } m_fd = rhs.m_fd; rhs.m_fd = -1; } return *this; }
- inline operator int () const { return m_fd; }
- inline operator int* () { g_assert(m_fd == -1); return &m_fd; }
+ inline smart_fd& operator = (int rhs) noexcept { if (m_fd != -1) { restore_errno errsv;
close(m_fd); } m_fd = rhs; return *this; }
+ inline smart_fd& operator = (smart_fd& rhs) noexcept { if (&rhs != this) { if (m_fd != -1) {
restore_errno errsv; close(m_fd); } m_fd = rhs.m_fd; rhs.m_fd = -1; } return *this; }
+ inline constexpr operator int () const noexcept { return m_fd; }
+ inline constexpr operator int* () noexcept { assert(m_fd == -1); return &m_fd; }
- int steal() { auto d = m_fd; m_fd = -1; return d; }
+ int steal() noexcept { auto d = m_fd; m_fd = -1; return d; }
/* Prevent accidents */
smart_fd(smart_fd const&) = delete;
@@ -229,7 +230,7 @@ namespace util {
smart_fd& operator = (smart_fd&&) = delete;
private:
- int m_fd;
+ int m_fd{-1};
};
} /* namespace util */
diff --git a/src/widget.cc b/src/widget.cc
index bbc0dadc..eef88311 100644
--- a/src/widget.cc
+++ b/src/widget.cc
@@ -26,6 +26,7 @@
#include <string>
#include "vtegtk.hh"
+#include "vteptyinternal.hh"
#include "debug.h"
using namespace std::literals;
@@ -355,6 +356,32 @@ Widget::set_cursor(CursorType type) noexcept
}
}
+bool
+Widget::set_pty(VtePty* pty_obj) noexcept
+{
+ if (pty() == pty_obj)
+ return false;
+
+ m_pty = vte::glib::make_ref(pty_obj);
+ terminal()->set_pty(_vte_pty_get_impl(pty()));
+
+ return true;
+}
+
+void
+Widget::unset_pty() noexcept
+{
+ if (!pty())
+ return;
+
+ /* This is only called from Terminal, so we need
+ * to explicitly notify the VteTerminal:pty property,
+ * but we do NOT need to call Terminal::set_pty(nullptr).
+ */
+ m_pty.reset();
+ g_object_notify_by_pspec(object(), pspecs[PROP_PTY]);
+}
+
void
Widget::size_allocate(GtkAllocation* allocation) noexcept
{
diff --git a/src/widget.hh b/src/widget.hh
index 6338cb70..e4b71869 100644
--- a/src/widget.hh
+++ b/src/widget.hh
@@ -22,8 +22,11 @@
#include <variant>
#include "vteterminal.h"
+#include "vtepty.h"
+
#include "vteinternal.hh"
+#include "fwd.hh"
#include "refptr.hh"
namespace vte {
@@ -52,7 +55,7 @@ public:
GtkWidget* gtk() const noexcept { return m_widget; }
VteTerminal* vte() const noexcept { return reinterpret_cast<VteTerminal*>(m_widget); }
- vte::terminal::Terminal* terminal() const noexcept { return m_terminal; }
+ inline constexpr vte::terminal::Terminal* terminal() const noexcept { return m_terminal; }
void constructed() noexcept { m_terminal->widget_constructed(); }
void dispose() noexcept;
@@ -108,6 +111,9 @@ public:
void emit_child_exited(int status) noexcept;
+ bool set_pty(VtePty* pty) noexcept;
+ inline auto pty() const noexcept { return m_pty.get(); }
+
protected:
enum class CursorType {
@@ -143,6 +149,8 @@ protected:
void im_set_cursor_location(cairo_rectangle_int_t const* rect) noexcept;
+ void unset_pty() noexcept;
+
public: // FIXMEchpe
void im_preedit_changed() noexcept;
@@ -162,6 +170,9 @@ private:
/* Input method */
vte::glib::RefPtr<GtkIMContext> m_im_context;
+
+ /* PTY */
+ vte::glib::RefPtr<VtePty> m_pty;
};
} // namespace platform
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]