[vte] Add VtePty and adapt the VteTerminal APIs to it
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] Add VtePty and adapt the VteTerminal APIs to it
- Date: Wed, 17 Mar 2010 17:23:21 +0000 (UTC)
commit dd08c50c6a6dd4349d3cbce271ddf4b741db8861
Author: Christian Persch <chpe gnome org>
Date: Thu Jan 14 18:08:33 2010 +0100
Add VtePty and adapt the VteTerminal APIs to it
Add VtePty as a GObject holding the info about the PTY. Add new API to
VteTerminal to set a VtePty, and deprecate the old API that takes a FD
to the PTY. Also deprecate the whole of the undocumented _vte_pty_*()
APIs.
Add vte_terminal_fork_command_full() variant that allow providing a
custom child setup function and that returns a GError on failure.
Bug #585841, bug #320128, bug #514447, bug #588871.
Makefile.am | 2 +-
doc/reference/vte-docs.xml | 3 +
doc/reference/vte-sections.txt | 50 ++-
doc/reference/vte.types | 5 +-
gnome-pty-helper/configure.in | 5 +-
src/Makefile.am | 14 +-
src/pty.c | 1652 ++++++++++++++++++++++++++++++----------
src/pty.h | 6 +-
src/vte-private.h | 3 +-
src/vte.c | 775 +++++++++++++------
src/vte.h | 40 +-
src/vteapp.c | 32 +-
src/vtepty-private.h | 43 +
src/vtepty.h | 121 +++
14 files changed, 2058 insertions(+), 693 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 2163351..dd622c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,7 +19,7 @@ endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = vte.pc
-DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
+DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --disable-silent-rules
MAINTAINERCLEANFILES = \
ChangeLog \
diff --git a/doc/reference/vte-docs.xml b/doc/reference/vte-docs.xml
index 254ff14..4453760 100644
--- a/doc/reference/vte-docs.xml
+++ b/doc/reference/vte-docs.xml
@@ -56,6 +56,9 @@
<xi:include href="xml/vte-terminal.xml"/>
</chapter>
<chapter>
+ <xi:include href="xml/vte-pty.xml"/>
+ </chapter>
+ <chapter>
<xi:include href="xml/vte-version.xml"/>
</chapter>
diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index 87d13ae..0b9dfa0 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -10,10 +10,6 @@ VteTerminalWriteFlags
VteSelectionFunc
vte_terminal_new
vte_terminal_im_append_menuitems
-vte_terminal_fork_command
-vte_terminal_forkpty
-vte_terminal_set_pty
-vte_terminal_get_pty
vte_terminal_feed
vte_terminal_feed_child
vte_terminal_feed_child_binary
@@ -88,6 +84,18 @@ vte_terminal_get_encoding
vte_terminal_get_status_line
vte_terminal_get_padding
vte_terminal_write_contents
+
+<SUBSECTION>
+vte_terminal_fork_command
+vte_terminal_fork_command_full
+vte_terminal_forkpty
+vte_terminal_get_pty
+vte_terminal_get_pty_object
+vte_terminal_pty_new
+vte_terminal_set_pty
+vte_terminal_set_pty_object
+vte_terminal_watch_child
+
<SUBSECTION Standard>
VTE_TYPE_TERMINAL_CURSOR_BLINK_MODE
vte_terminal_cursor_blink_mode_get_type
@@ -126,6 +134,40 @@ VteTerminalPrivate
</SECTION>
<SECTION>
+<FILE>vte-pty</FILE>
+<TITLE>Vte PTY</TITLE>
+
+VtePtyFlags
+VtePtyError
+VtePty
+vte_pty_new
+vte_pty_new_foreign
+vte_pty_close
+vte_pty_child_setup
+vte_pty_get_fd
+vte_pty_set_size
+vte_pty_get_size
+vte_pty_set_term
+vte_pty_set_utf8
+
+<SUBSECTION Standard>
+vte_pty_flags_get_type
+VTE_TYPE_PTY_FLAGS
+vte_pty_error_get_type
+VTE_TYPE_PTY_ERROR
+vte_pty_error_quark
+VTE_PTY_ERROR
+vte_pty_get_type
+VTE_TYPE_PTY
+VTE_PTY
+VTE_PTY_CLASS
+VTE_IS_PTY
+VTE_IS_PTY_CLASS
+VTE_PTY_GET_CLASS
+VtePtyClass
+</SECTION>
+
+<SECTION>
<FILE>vte-version</FILE>
<TITLE>Version Information</TITLE>
diff --git a/doc/reference/vte.types b/doc/reference/vte.types
index 94d16d9..83b1aec 100644
--- a/doc/reference/vte.types
+++ b/doc/reference/vte.types
@@ -5,15 +5,18 @@
#include "vte.h"
#include "vteversion.h"
#include "vteaccess.h"
+#include "vtepty.h"
#include "vtetypebuiltins.h"
#include "reaper.h"
+vte_pty_get_type
vte_reaper_get_type
-
vte_terminal_get_type
vte_terminal_accessible_get_type
vte_terminal_accessible_factory_get_type
+vte_pty_error_get_type
+vte_pty_flags_get_type
vte_terminal_anti_alias_get_type
vte_terminal_cursor_blink_mode_get_type
vte_terminal_cursor_shape_get_type
diff --git a/gnome-pty-helper/configure.in b/gnome-pty-helper/configure.in
index dd14b71..73a400e 100644
--- a/gnome-pty-helper/configure.in
+++ b/gnome-pty-helper/configure.in
@@ -10,7 +10,10 @@ AC_PROG_CC
AC_STDC_HEADERS
AM_PROG_CC_STDC
-AM_MAINTAINER_MODE
+if test -z "$enable_maintainer_mode"; then
+ enable_maintainer_mode=yes
+fi
+AM_MAINTAINER_MODE([enable])
AC_CHECK_HEADERS(sys/syslimits.h sys/time.h sys/types.h sys/un.h alloca.h lastlog.h libutil.h paths.h pty.h stropts.h termios.h ttyent.h util.h utmp.h utmpx.h pty.h util.h libutil.h ttyent.h)
have_openpty=0
diff --git a/src/Makefile.am b/src/Makefile.am
index b980e7b..cd45e75 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@ EXTRA_DIST = iso2022.txt
# The library
-pkginclude_HEADERS = pty.h reaper.h vte.h vteaccess.h vtetypebuiltins.h vteversion.h
+pkginclude_HEADERS = pty.h reaper.h vte.h vteaccess.h vtepty.h vtetypebuiltins.h vteversion.h
lib_LTLIBRARIES = libvte.la
@@ -65,6 +65,8 @@ libvte_la_SOURCES = \
vtedraw.c \
vtedraw.h \
vteint.h \
+ vtepty.h \
+ vtepty-private.h \
vteregex.c \
vteregex.h \
vterowdata.c \
@@ -96,7 +98,7 @@ libvte_la_CFLAGS = $(VTE_CFLAGS)
libvte_la_LIBADD = $(VTE_LIBS)
libvte_la_LDFLAGS = \
-version-info $(LIBVTE_LTVERSION) \
- -export-symbols-regex "^vte_terminal_.*|^_vte_pty_.*|^vte_reaper_.*|_vte_debug_.*" \
+ -export-symbols-regex "^vte_terminal_.*|^vte_pty_.*|^_vte_pty_.*|^vte_reaper_.*|_vte_debug_.*" \
@LIBTOOL_EXPORT_OPTIONS@ @LIBTOOL_FLAGS@
@@ -116,14 +118,14 @@ marshal.h: marshal.list
vtetypebuiltins.h: stamp-vtetypebuiltins.h
@true
-stamp-vtetypebuiltins.h: vtetypebuiltins.h.template vte.h
- $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-vtbh \
+stamp-vtetypebuiltins.h: vtetypebuiltins.h.template vte.h vtepty.h
+ $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter %.h,$^) > xgen-vtbh \
&& (cmp -s xgen-vtbh vtetypebuiltins.h || cp xgen-vtbh vtetypebuiltins.h ) \
&& rm -f xgen-vtbh \
&& echo timestamp > $(@F)
-vtetypebuiltins.c: vtetypebuiltins.c.template vte.h
- $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-vtbc \
+vtetypebuiltins.c: vtetypebuiltins.c.template vte.h vtepty.h
+ $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter %.h,$^) > xgen-vtbc \
&& (cmp -s xgen-vtbc vtetypebuiltins.c || cp xgen-vtbc vtetypebuiltins.c ) \
&& rm -f xgen-vtbc
diff --git a/src/pty.c b/src/pty.c
index 4968e6a..d50d12b 100644
--- a/src/pty.c
+++ b/src/pty.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2001,2002 Red Hat, Inc.
+ * Copyright © 2009, 2010 Christian Persch
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU Library General Public License as published by
@@ -16,7 +17,23 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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.
+ *
+ * Since: 0.24
+ */
+
#include <config.h>
+
+#include "vtepty.h"
+#include "vtepty-private.h"
+#include "vte.h"
+
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -41,6 +58,7 @@
#include <stropts.h>
#endif
#include <glib.h>
+#include <gio/gio.h>
#include "debug.h"
#include "pty.h"
@@ -61,7 +79,6 @@
static gboolean _vte_pty_helper_started = FALSE;
static pid_t _vte_pty_helper_pid = -1;
static int _vte_pty_helper_tunnel = -1;
-static GTree *_vte_pty_helper_map = NULL;
#endif
/* Reset the handlers for all known signals to their defaults. The parent
@@ -144,7 +161,9 @@ _vte_pty_reset_signal_handlers(void)
#endif
}
-struct vte_pty_child_setup_data {
+typedef struct _VtePtyPrivate VtePtyPrivate;
+
+typedef struct {
enum {
TTY_OPEN_BY_NAME,
TTY_OPEN_BY_FD
@@ -153,31 +172,90 @@ struct vte_pty_child_setup_data {
const char *name;
int fd;
} tty;
+} VtePtyChildSetupData;
+
+/**
+ * VtePty:
+ *
+ * Since: 0.24
+ */
+struct _VtePty {
+ GObject parent_instance;
+
+ /* <private> */
+ VtePtyPrivate *priv;
};
-static void
-vte_pty_child_setup (gpointer arg)
+
+struct _VtePtyPrivate {
+ VtePtyFlags flags;
+ int pty_fd;
+
+ const char *term;
+ VtePtyChildSetupData child_setup_data;
+
+ gpointer helper_tag; /* only use when using_helper is TRUE */
+
+ guint utf8 : 1;
+ guint foreign : 1;
+ guint using_helper : 1;
+};
+
+struct _VtePtyClass {
+ GObjectClass parent_class;
+};
+
+/**
+ * vte_pty_child_setup:
+ * @pty: a #VtePty
+ *
+ * FIXMEchpe
+ *
+ * Since: 0.24
+ */
+void
+vte_pty_child_setup (VtePty *pty)
{
- struct vte_pty_child_setup_data *data = arg;
+ VtePtyPrivate *priv = pty->priv;
+ VtePtyChildSetupData *data = &priv->child_setup_data;
int fd = -1;
const char *tty = NULL;
+ if (priv->foreign) {
+ fd = priv->pty_fd;
+ } else {
+ /* Save the name of the pty -- we'll need it later to acquire
+ * it as our controlling terminal.
+ */
+ switch (data->mode) {
+ case TTY_OPEN_BY_NAME:
+ tty = data->tty.name;
+ break;
+ case TTY_OPEN_BY_FD:
+ fd = data->tty.fd;
+ tty = ttyname(fd);
+ break;
+ }
+
+ _vte_debug_print (VTE_DEBUG_PTY,
+ "Setting up child pty: name = %s, fd = %d\n",
+ tty ? tty : "(none)", fd);
+
+
+ /* Try to reopen the pty to acquire it as our controlling terminal. */
+ /* FIXMEchpe: why not just use the passed fd in TTY_OPEN_BY_FD mode? */
+ if (tty != NULL) {
+ int i = open(tty, O_RDWR);
+ if (i != -1) {
+ if (fd != -1){
+ close(fd);
+ }
+ fd = i;
+ }
+ }
+ }
- /* Save the name of the pty -- we'll need it later to acquire
- * it as our controlling terminal. */
- switch (data->mode) {
- case TTY_OPEN_BY_NAME:
- tty = data->tty.name;
- break;
- case TTY_OPEN_BY_FD:
- fd = data->tty.fd;
- tty = ttyname(fd);
- break;
- }
-
- _vte_debug_print (VTE_DEBUG_PTY,
- "Setting up child pty: name = %s, fd = %d\n",
- tty ? tty : "(none)", fd);
-
+ if (fd == -1)
+ _exit (127);
/* Start a new session and become process-group leader. */
#if defined(HAVE_SETSID) && defined(HAVE_SETPGID)
@@ -186,20 +264,6 @@ vte_pty_child_setup (gpointer arg)
setpgid(0, 0);
#endif
- /* Try to reopen the pty to acquire it as our controlling terminal. */
- if (tty != NULL) {
- int i = open(tty, O_RDWR);
- if (i != -1) {
- if (fd != -1){
- close(fd);
- }
- fd = i;
- }
- }
-
- if (fd == -1)
- _exit (127);
-
#ifdef TIOCSCTTY
/* TIOCSCTTY is defined? Let's try that, too. */
ioctl(fd, TIOCSCTTY, fd);
@@ -248,29 +312,73 @@ vte_pty_child_setup (gpointer arg)
close(fd);
}
-
/* Reset our signals -- our parent may have done any number of
* weird things to them. */
_vte_pty_reset_signal_handlers();
+
+ /* Now set the TERM environment variable */
+ if (priv->term != NULL) {
+ g_setenv("TERM", priv->term, TRUE);
+ }
}
/* TODO: clean up the spawning
* - replace current env rather than adding!
- * - allow user control over flags (eg DO_NOT_CLOSE)
- * - additional user callback for child setup
*/
-static void
-collect_variables (char *name, char *value, GPtrArray *array)
+/*
+ * __vte_pty_get_argv:
+ * @command: the command to run
+ * @argv: the argument vector
+ * @flags: (inout) flags from #GSpawnFlags
+ *
+ * Creates an argument vector to pass to g_spawn_async() from @command and
+ * @argv, modifying * flags if necessary.
+ *
+ * Returns: a newly allocated array of strings. Free with g_strfreev()
+ */
+char **
+__vte_pty_get_argv (const char *command,
+ char **argv,
+ GSpawnFlags *flags /* inout */)
{
- g_ptr_array_add (array,
- g_strconcat (name, "=", value, NULL));
+ char **argv2;
+ int i, argc;
+
+ g_return_val_if_fail(command != NULL, NULL);
+
+ /* push the command into argv[0] */
+ argc = argv ? g_strv_length (argv) : 0;
+ argv2 = g_new (char *, argc + 2);
+
+ argv2[0] = g_strdup (command);
+
+ for (i = 0; i < argc; i++) {
+ argv2[i+1] = g_strdup (argv[i]);
+ }
+ argv2[i+1] = NULL;
+
+ if (argv) {
+ *flags |= G_SPAWN_FILE_AND_ARGV_ZERO;
+ }
+
+ return argv2;
}
+/*
+ * __vte_pty_merge_environ:
+ * @envp: environment vector
+ *
+ * Merges @envp to the parent environment, and returns a new environment vector.
+ *
+ * Returns: a newly allocated string array. Free using g_strfreev()
+ */
static gchar **
-merge_environ (char **envp)
+__vte_pty_merge_environ (char **envp)
{
GHashTable *table;
+ GHashTableIter iter;
+ char *name, *value;
gchar **parent_environ;
GPtrArray *array;
gint i;
@@ -287,8 +395,8 @@ merge_environ (char **envp)
if (envp != NULL) {
for (i = 0; envp[i] != NULL; i++) {
- gchar *name = g_strdup (envp[i]);
- gchar *value = strchr (name, '=');
+ name = g_strdup (envp[i]);
+ value = strchr (name, '=');
if (value) {
*value = '\0';
value = g_strdup (value + 1);
@@ -298,175 +406,221 @@ merge_environ (char **envp)
}
array = g_ptr_array_sized_new (g_hash_table_size (table) + 1);
- g_hash_table_foreach (table, (GHFunc) collect_variables, array);
+ g_hash_table_iter_init(&iter, table);
+ while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &value)) {
+ g_ptr_array_add (array, g_strconcat (name, "=", value, NULL));
+ }
+ g_assert(g_hash_table_size(table) == array->len);
g_hash_table_destroy (table);
g_ptr_array_add (array, NULL);
+
return (gchar **) g_ptr_array_free (array, FALSE);
}
-/* Run the given command (if specified) */
-static gboolean
-_vte_pty_run_on_pty (struct vte_pty_child_setup_data *data,
- const char *command, char **argv, char **envp,
- const char *directory,
- GPid *pid, GError **error)
+/*
+ * __vte_pty_get_pty_flags:
+ * @lastlog: %TRUE if the session should be logged to the lastlog
+ * @utmp: %TRUE if the session should be logged to the utmp/utmpx log
+ * @wtmp: %TRUE if the session should be logged to the wtmp/wtmpx log
+ *
+ * Combines the @lastlog, @utmp, @wtmp arguments into the coresponding
+ * #VtePtyFlags flags.
+ *
+ * Returns: flags from #VtePtyFlags
+ */
+VtePtyFlags
+__vte_pty_get_pty_flags(gboolean lastlog,
+ gboolean utmp,
+ gboolean wtmp)
{
- gboolean ret = TRUE;
- GError *local_error = NULL;
-
- if (command != NULL) {
- gchar **arg2, **envp2;
- gint i, argc;
-
- /* push the command into argv[0] */
- argc = argv ? g_strv_length (argv) : 0;
- arg2 = g_new (char *, argc + 2);
- arg2[0] = g_strdup (command);
- for (i = 0; i < argc; i++) {
- arg2[i+1] = g_strdup (argv[i]);
- }
- arg2[i+1] = NULL;
-
- /* add the given environment to the childs */
- envp2 = merge_environ (envp);
-
- _VTE_DEBUG_IF (VTE_DEBUG_MISC) {
- g_printerr ("Spawing command '%s'\n", command);
- for (i = 0; arg2[i] != NULL; i++) {
- g_printerr (" argv[%d] = %s\n", i, arg2[i]);
- }
- for (i = 0; envp2[i] != NULL; i++) {
- g_printerr (" env[%d] = %s\n", i, envp2[i]);
- }
- g_printerr (" directory: %s\n",
- directory ? directory : "(none)");
- }
+ VtePtyFlags flags = VTE_PTY_DEFAULT;
- ret = g_spawn_async_with_pipes (directory,
- arg2, envp2,
- G_SPAWN_CHILD_INHERITS_STDIN |
- G_SPAWN_SEARCH_PATH |
- G_SPAWN_DO_NOT_REAP_CHILD |
- (argv ? G_SPAWN_FILE_AND_ARGV_ZERO : 0),
- vte_pty_child_setup, data,
- pid,
- NULL, NULL, NULL,
- &local_error);
- if (ret == FALSE) {
- if (g_error_matches (local_error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_CHDIR)) {
- /* try spawning in our working directory */
- g_clear_error (&local_error);
- ret = g_spawn_async_with_pipes (NULL,
- arg2, envp2,
- G_SPAWN_CHILD_INHERITS_STDIN |
- G_SPAWN_SEARCH_PATH |
- G_SPAWN_DO_NOT_REAP_CHILD |
- (argv ? G_SPAWN_FILE_AND_ARGV_ZERO : 0),
- vte_pty_child_setup, data,
- pid,
- NULL, NULL, NULL,
- &local_error);
- }
- }
- g_strfreev (arg2);
- g_strfreev (envp2);
-
- _vte_debug_print (VTE_DEBUG_MISC,
- "Spawn result: %s%s\n",
- ret?"Success":"Failure - ",
- ret?"":local_error->message);
- if (local_error)
- g_propagate_error (error, local_error);
- }
-#ifdef HAVE_FORK
- else {
- *pid = fork();
- switch (*pid) {
- case -1:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- "Unable to fork: %s",
- g_strerror (errno));
- ret = FALSE;
- case 0: /* child */
- vte_pty_child_setup (data);
- break;
- default: /* parent */
- break;
- }
- }
-#endif
+ if (!lastlog)
+ flags |= VTE_PTY_NO_LASTLOG;
+ if (!utmp)
+ flags |= VTE_PTY_NO_UTMP;
+ if (!wtmp)
+ flags |= VTE_PTY_NO_WTMP;
- return ret;
+ return flags;
}
-/* Open the named PTY slave, fork off a child (storing its PID in child),
- * and exec the named command in its own session as a process group leader */
-static gboolean
-_vte_pty_fork_on_pty_name (const char *path, int parent_fd, char **envp,
- const char *command, char **argv,
- const char *directory,
- int columns, int rows, GPid *child)
+/*
+ * __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
+ * @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 */,
+ GError **error)
{
- struct vte_pty_child_setup_data data;
-
- data.mode = TTY_OPEN_BY_NAME;
- data.tty.name = path;
-
- if (!_vte_pty_run_on_pty(&data,
- command, argv, envp, directory,
- child, NULL)) {
- /* XXX propagate the error */
- return FALSE;
- }
-
- _vte_pty_set_size(parent_fd, columns, rows);
- return TRUE;
+ gboolean ret = TRUE;
+ char **envp2;
+ gint i;
+ GError *err = NULL;
+
+ 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.
+ */
+ spawn_flags &= ~G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
+
+ /* add the given environment to the childs */
+ envp2 = __vte_pty_merge_environ (envv);
+
+ _VTE_DEBUG_IF (VTE_DEBUG_MISC) {
+ g_printerr ("Spawing command:\n");
+ for (i = 0; argv[i] != NULL; i++) {
+ g_printerr (" argv[%d] = %s\n", i, argv[i]);
+ }
+ for (i = 0; envp2[i] != NULL; i++) {
+ g_printerr (" env[%d] = %s\n", i, envp2[i]);
+ }
+ g_printerr (" directory: %s\n",
+ directory ? directory : "(none)");
+ }
+
+ ret = g_spawn_async_with_pipes(directory,
+ argv, envp2,
+ spawn_flags,
+ child_setup ? child_setup
+ : (GSpawnChildSetupFunc) vte_pty_child_setup,
+ child_setup ? child_setup_data : pty,
+ child_pid,
+ NULL, NULL, NULL,
+ &err);
+ if (!ret &&
+ directory != NULL &&
+ g_error_matches(err, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR)) {
+ /* try spawning in our working directory */
+ g_clear_error(&err);
+ ret = g_spawn_async_with_pipes(NULL,
+ argv, envp2,
+ spawn_flags,
+ child_setup ? child_setup
+ : (GSpawnChildSetupFunc) vte_pty_child_setup,
+ child_setup ? child_setup_data : pty,
+ child_pid,
+ NULL, NULL, NULL,
+ &err);
+ }
+
+ g_strfreev (envp2);
+
+ if (ret)
+ return TRUE;
+
+ g_propagate_error (error, err);
+ return FALSE;
}
-/* Fork off a child (storing its PID in child), and exec the named command
- * in its own session as a process group leader using the given terminal. */
-static gboolean
-_vte_pty_fork_on_pty_fd (int fd, char **envp,
- const char *command, char **argv,
- const char *directory,
- int columns, int rows, GPid *child)
+/*
+ * __vte_pty_fork:
+ * @pty: a #VtePty
+ * @pid: (out) a location to store a #GPid, or %NULL
+ * @error: a location to store a #GError, or %NULL
+ *
+ * Forks and calls vte_pty_child_setup() in the child.
+ *
+ * Returns: %TRUE on success, or %FALSE on failure with @error filled in
+ */
+gboolean
+__vte_pty_fork(VtePty *pty,
+ GPid *pid,
+ GError **error)
{
- struct vte_pty_child_setup_data data;
-
- data.mode = TTY_OPEN_BY_FD;
- data.tty.fd = fd;
-
- if (!_vte_pty_run_on_pty(&data,
- command, argv, envp, directory,
- child, NULL)) {
- /* XXX propagate the error */
- return FALSE;
- }
+#ifdef HAVE_FORK
+ gboolean ret = TRUE;
+
+ *pid = fork();
+ switch (*pid) {
+ case -1:
+ g_set_error(error,
+ G_SPAWN_ERROR,
+ G_SPAWN_ERROR_FAILED,
+ "Unable to fork: %s",
+ g_strerror(errno));
+ ret = FALSE;
+ case 0: /* child */
+ vte_pty_child_setup(pty);
+ break;
+ default: /* parent */
+ break;
+ }
- _vte_pty_set_size(fd, columns, rows);
- return TRUE;
+ return ret;
+#else /* !HAVE_FORK */
+ g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ "No fork implementation");
+ return FALSE;
+#endif /* HAVE_FORK */
}
/**
* vte_pty_set_size:
- * @master: the file descriptor of the pty master
- * @columns: the desired number of columns
+ * @pty: a #VtePty
* @rows: the desired number of rows
+ * @columns: the desired number of columns
+ * @error: 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.
*
- * Returns: 0 on success, -1 on failure.
+ * If setting the window size failed, @error will be set to a #GIOError.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ *
+ * Since: 0.24
*/
-int
-_vte_pty_set_size(int master, int columns, int rows)
+gboolean
+vte_pty_set_size(VtePty *pty,
+ int rows,
+ int columns,
+ GError **error)
{
+ VtePtyPrivate *priv;
struct winsize size;
+ int master;
int ret;
+
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+
+ priv = pty->priv;
+
+ master = vte_pty_get_fd(pty);
+
memset(&size, 0, sizeof(size));
size.ws_row = rows ? rows : 24;
size.ws_col = columns ? columns : 80;
@@ -475,28 +629,57 @@ _vte_pty_set_size(int master, int columns, int rows)
master, columns, rows);
ret = ioctl(master, TIOCSWINSZ, &size);
if (ret != 0) {
+ int errsv = errno;
+
+ g_set_error(error, G_IO_ERROR,
+ g_io_error_from_errno(errsv),
+ "Failed to set window size: %s",
+ g_strerror(errno));
+
_vte_debug_print(VTE_DEBUG_PTY,
"Failed to set size on %d: %s.\n",
- master, strerror(errno));
+ master, g_strerror(errsv));
+
+ errno = errsv;
+
+ return FALSE;
}
- return ret;
+
+ return TRUE;
}
/**
* vte_pty_get_size:
- * @master: the file descriptor of the pty master
- * @columns: a place to store the number of columns
+ * @pty: a #VtePty
* @rows: a place to store the number of rows
+ * @columns: a place to store the number of columns
+ * @error: return location to store a #GError, or %NULL
*
- * Attempts to read the pseudo terminal's window size.
+ * Reads the pseudo terminal's window size.
*
- * Returns: 0 on success, -1 on failure.
+ * If getting the window size failed, @error will be set to a #GIOError.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ *
+ * Since: 0.24
*/
-int
-_vte_pty_get_size(int master, int *columns, int *rows)
+gboolean
+vte_pty_get_size(VtePty *pty,
+ int *rows,
+ int *columns,
+ GError **error)
{
+ VtePtyPrivate *priv;
struct winsize size;
+ int master;
int ret;
+
+ g_return_val_if_fail(VTE_IS_PTY(pty), FALSE);
+
+ priv = pty->priv;
+
+ master = vte_pty_get_fd(pty);
+
memset(&size, 0, sizeof(size));
ret = ioctl(master, TIOCGWINSZ, &size);
if (ret == 0) {
@@ -509,14 +692,32 @@ _vte_pty_get_size(int master, int *columns, int *rows)
_vte_debug_print(VTE_DEBUG_PTY,
"Size on fd %d is (%d,%d).\n",
master, size.ws_col, size.ws_row);
+ return TRUE;
} else {
+ int errsv = errno;
+
+ g_set_error(error, G_IO_ERROR,
+ g_io_error_from_errno(errsv),
+ "Failed to get window size: %s",
+ g_strerror(errno));
+
_vte_debug_print(VTE_DEBUG_PTY,
- "Failed to read size from fd %d.\n",
- master);
+ "Failed to read size from fd %d: %s\n",
+ master, g_strerror(errsv));
+
+ errno = errsv;
+
+ return FALSE;
}
- return ret;
}
+/*
+ * _vte_pty_ptsname:
+ * @master: file descriptor to the PTY master
+ *
+ * Returns: a newly allocated string containing the file name of the
+ * PTY slave device, or %NULL on failure
+ */
static char *
_vte_pty_ptsname(int master)
{
@@ -558,6 +759,14 @@ _vte_pty_ptsname(int master)
return NULL;
}
+/*
+ * _vte_pty_getpt:
+ *
+ * Opens a file descriptor for the next available PTY master.
+ * Sets the descriptor to blocking mode!
+ *
+ * Returns: a new file descriptor, or %-1 on failure
+ */
static int
_vte_pty_getpt(void)
{
@@ -572,7 +781,11 @@ _vte_pty_getpt(void)
fd = open("/dev/ptc", O_RDWR | O_NOCTTY); /* AIX */
}
#endif
+ if (fd == -1)
+ return fd;
+
/* Set it to blocking. */
+ /* FIXMEchpe: why?? vte_terminal_set_pty does the inverse... */
flags = fcntl(fd, F_GETFL);
flags &= ~(O_NONBLOCK);
fcntl(fd, F_SETFL, flags);
@@ -602,40 +815,58 @@ _vte_pty_unlockpt(int fd)
#endif
}
-static int
-_vte_pty_open_unix98(GPid *child, char **env_add,
- const char *command, char **argv,
- const char *directory, int columns, int rows)
+/*
+ * _vte_pty_open_unix98:
+ * @pty: a #VtePty
+ * @error: a location to store a #GError, or %NULL
+ *
+ * Opens a new file descriptor to a new PTY master.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ */
+static gboolean
+_vte_pty_open_unix98(VtePty *pty,
+ GError **error)
{
+ VtePtyPrivate *priv = pty->priv;
int fd;
char *buf;
/* Attempt to open the master. */
fd = _vte_pty_getpt();
_vte_debug_print(VTE_DEBUG_PTY, "Allocated pty on fd %d.\n", fd);
- if (fd != -1) {
- /* Read the slave number and unlock it. */
- if (((buf = _vte_pty_ptsname(fd)) == NULL) ||
- (_vte_pty_grantpt(fd) != 0) ||
- (_vte_pty_unlockpt(fd) != 0)) {
- _vte_debug_print(VTE_DEBUG_PTY,
- "PTY setup failed, bailing.\n");
- close(fd);
- fd = -1;
- } else {
- /* Start up a child process with the given command. */
- if (!_vte_pty_fork_on_pty_name(buf, fd,
- env_add, command,
- argv, directory,
- columns, rows,
- child)) {
- close(fd);
- fd = -1;
- }
- g_free(buf);
- }
- }
- return fd;
+ if (fd == -1) {
+ g_set_error (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY98_FAILED,
+ "getpt failed: %s", g_strerror(errno));
+ return FALSE;
+ }
+
+ /* Read the slave number and unlock it. */
+ if (((buf = _vte_pty_ptsname(fd)) == NULL) ||
+ (_vte_pty_grantpt(fd) != 0) ||
+ (_vte_pty_unlockpt(fd) != 0)) {
+ int errsv = errno;
+
+ g_set_error(error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY98_FAILED,
+ "PTY setup failed: %s",
+ g_strerror(errsv));
+
+ _vte_debug_print(VTE_DEBUG_PTY,
+ "PTY setup failed, bailing.\n");
+ close(fd);
+ errno = errsv;
+
+ return FALSE;
+ }
+
+ priv->pty_fd = fd;
+ priv->child_setup_data.mode = TTY_OPEN_BY_NAME;
+ priv->child_setup_data.tty.name = buf;
+ priv->using_helper = FALSE;
+
+ return TRUE;
}
#ifdef VTE_USE_GNOME_PTY_HELPER
@@ -799,14 +1030,15 @@ n_write(int fd, const void *buffer, size_t count)
return n;
}
-
-
+/*
+ * _vte_pty_stop_helper:
+ *
+ * Terminates the running GNOME PTY helper.
+ */
static void
_vte_pty_stop_helper(void)
{
if (_vte_pty_helper_started) {
- g_tree_destroy(_vte_pty_helper_map);
- _vte_pty_helper_map = NULL;
close(_vte_pty_helper_tunnel);
_vte_pty_helper_tunnel = -1;
kill(_vte_pty_helper_pid, SIGTERM);
@@ -815,43 +1047,55 @@ _vte_pty_stop_helper(void)
}
}
-static gint
-_vte_direct_compare(gconstpointer a, gconstpointer b)
-{
- return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
-}
-
+/*
+ * _vte_pty_start_helper:
+ * @error: a location to store a #GError, or %NULL
+ *
+ * Starts the GNOME PTY helper process, if it is not already running.
+ *
+ * Returns: %TRUE if the helper was already started, or starting it succeeded,
+ * %FALSE on failure with @error filled in
+ */
static gboolean
-_vte_pty_start_helper(void)
+_vte_pty_start_helper(GError **error)
{
- int i, tmp[2], tunnel;
+ int i, tunnel;
+ int tmp[2] = { -1, -1 };
+
+ if (_vte_pty_helper_started)
+ return TRUE;
+
/* Sanity check. */
if (access(LIBEXECDIR "/gnome-pty-helper", X_OK) != 0) {
/* Give the user some clue as to why session logging is not
* going to work (assuming we can open a pty using some other
* method). */
g_warning(_("can not run %s"), LIBEXECDIR "/gnome-pty-helper");
+ g_set_error(error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ _("can not run %s"), LIBEXECDIR "/gnome-pty-helper");
return FALSE;
}
/* Create a communication link for use with the helper. */
tmp[0] = open("/dev/null", O_RDONLY);
if (tmp[0] == -1) {
- return FALSE;
+ goto failure;
}
tmp[1] = open("/dev/null", O_RDONLY);
if (tmp[1] == -1) {
- close(tmp[0]);
- return FALSE;
+ goto failure;
}
if (_vte_pty_pipe_open(&_vte_pty_helper_tunnel, &tunnel) != 0) {
- return FALSE;
+ goto failure;
}
close(tmp[0]);
close(tmp[1]);
+ tmp[0] = tmp[1] = -1;
+
/* Now fork and start the helper. */
_vte_pty_helper_pid = fork();
if (_vte_pty_helper_pid == -1) {
- return FALSE;
+ goto failure;
}
if (_vte_pty_helper_pid == 0) {
/* Child. Close descriptors. No need to close all,
@@ -871,49 +1115,125 @@ _vte_pty_start_helper(void)
_exit(1);
}
close(tunnel);
- _vte_pty_helper_map = g_tree_new(_vte_direct_compare);
atexit(_vte_pty_stop_helper);
+
+ _vte_pty_helper_started = TRUE;
return TRUE;
+
+failure:
+ g_set_error(error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "Failed to start gnome-pty-helper: %s",
+ g_strerror (errno));
+
+ if (tmp[0] != -1)
+ close(tmp[0]);
+ if (tmp[1] != -1)
+ close(tmp[1]);
+
+ return FALSE;
}
+/*
+ * _vte_pty_helper_ops_from_flags:
+ * @flags: flags from #VtePtyFlags
+ *
+ * Translates @flags into the corresponding op code for the
+ * GNOME PTY helper.
+ *
+ * Returns: the #GnomePtyOps corresponding to @flags
+ */
static int
-_vte_pty_open_with_helper(GPid *child, char **env_add,
- const char *command, char **argv,
- const char *directory,
- int columns, int rows, int op)
+_vte_pty_helper_ops_from_flags (VtePtyFlags flags)
{
+ int op = 0;
+ static const int opmap[8] = {
+ GNOME_PTY_OPEN_NO_DB_UPDATE, /* 0 0 0 */
+ GNOME_PTY_OPEN_PTY_LASTLOG, /* 0 0 1 */
+ GNOME_PTY_OPEN_PTY_UTMP, /* 0 1 0 */
+ GNOME_PTY_OPEN_PTY_LASTLOGUTMP, /* 0 1 1 */
+ GNOME_PTY_OPEN_PTY_WTMP, /* 1 0 0 */
+ GNOME_PTY_OPEN_PTY_LASTLOGWTMP, /* 1 0 1 */
+ GNOME_PTY_OPEN_PTY_UWTMP, /* 1 1 0 */
+ GNOME_PTY_OPEN_PTY_LASTLOGUWTMP, /* 1 1 1 */
+ };
+ if ((flags & VTE_PTY_NO_LASTLOG) == 0) {
+ op += 1;
+ }
+ if ((flags & VTE_PTY_NO_UTMP) == 0) {
+ op += 2;
+ }
+ if ((flags & VTE_PTY_NO_WTMP) == 0) {
+ op += 4;
+ }
+ g_assert(op >= 0 && op < (int) G_N_ELEMENTS(opmap));
+
+ return opmap[op];
+}
+
+/*
+ * _vte_pty_open_with_helper:
+ * @pty: a #VtePty
+ * @error: a location to store a #GError, or %NULL
+ *
+ * Opens a new file descriptor to a new PTY master using the
+ * GNOME PTY helper.
+ *
+ * Returns: %TRUE on success, %FALSE on failure with @error filled in
+ */
+static gboolean
+_vte_pty_open_with_helper(VtePty *pty,
+ GError **error)
+{
+ VtePtyPrivate *priv = pty->priv;
GnomePtyOps ops;
int ret;
int parentfd = -1, childfd = -1;
gpointer tag;
+
/* We have to use the pty helper here. */
- if (!_vte_pty_helper_started) {
- _vte_pty_helper_started = _vte_pty_start_helper();
- }
+ if (!_vte_pty_start_helper(error))
+ return FALSE;
+
/* Try to open a new descriptor. */
- if (_vte_pty_helper_started) {
- ops = op;
+ {
+ ops = _vte_pty_helper_ops_from_flags(priv->flags);
/* Send our request. */
if (n_write(_vte_pty_helper_tunnel,
&ops, sizeof(ops)) != sizeof(ops)) {
- return -1;
+ g_set_error (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "Failed to send request to gnome-pty-helper: %s",
+ g_strerror(errno));
+ return FALSE;
}
_vte_debug_print(VTE_DEBUG_PTY, "Sent request to helper.\n");
/* Read back the response. */
if (n_read(_vte_pty_helper_tunnel,
&ret, sizeof(ret)) != sizeof(ret)) {
- return -1;
+ g_set_error (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "Failed to read response from gnome-pty-helper: %s",
+ g_strerror(errno));
+ return FALSE;
}
_vte_debug_print(VTE_DEBUG_PTY,
"Received response from helper.\n");
if (ret == 0) {
- return -1;
+ g_set_error_literal (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "gnome-pty-helper failed to open pty");
+ return FALSE;
}
_vte_debug_print(VTE_DEBUG_PTY, "Helper returns success.\n");
/* Read back a tag. */
if (n_read(_vte_pty_helper_tunnel,
&tag, sizeof(tag)) != sizeof(tag)) {
- return -1;
+ g_set_error (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "Failed to read tag from gnome-pty-helper: %s",
+ g_strerror(errno));
+ return FALSE;
}
_vte_debug_print(VTE_DEBUG_PTY, "Tag = %p.\n", tag);
/* Receive the master and slave ptys. */
@@ -921,152 +1241,101 @@ _vte_pty_open_with_helper(GPid *child, char **env_add,
&parentfd, &childfd);
if ((parentfd == -1) || (childfd == -1)) {
+ int errsv = errno;
+
close(parentfd);
close(childfd);
- return -1;
+
+ g_set_error (error, VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED,
+ "Failed to read master or slave pty from gnome-pty-helper: %s",
+ g_strerror(errsv));
+ errno = errsv;
+ return FALSE;
}
+
_vte_debug_print(VTE_DEBUG_PTY,
"Got master pty %d and slave pty %d.\n",
parentfd, childfd);
- /* Add the parent and the tag to our map. */
- g_tree_insert(_vte_pty_helper_map,
- GINT_TO_POINTER(parentfd),
- tag);
- /* Start up a child process with the given command. */
- if (!_vte_pty_fork_on_pty_fd(childfd, env_add, command,
- argv, directory,
- columns, rows, child)) {
- close(parentfd);
- close(childfd);
- return -1;
- }
- close(childfd);
- return parentfd;
- }
- return -1;
-}
-#endif
+ priv->using_helper = TRUE;
+ priv->helper_tag = tag;
+ priv->pty_fd = parentfd;
-/**
- * _vte_pty_open:
- * @child: location to store the new process's ID
- * @env_add: a list of environment variables to add to the child's environment
- * @command: name of the binary to run
- * @argv: arguments to pass to @command
- * @directory: directory to start the new command in, or %NULL
- * @columns: desired window columns
- * @rows: desired window rows
- * @lastlog: %TRUE if the lastlog should be updated
- * @utmp: %TRUE if the utmp or utmpx log should be updated
- * @wtmp: %TRUE if the wtmp or wtmpx log should be updated
- *
- * Starts a new copy of @command running under a psuedo-terminal, optionally in
- * the supplied @directory, with window size set to @rows x @columns
- * and variables in @env_add added to its environment. If any combination of
- * @lastlog, @utmp, and @wtmp is set, then the session is logged in the
- * corresponding system files.
- *
- * Returns: an open file descriptor for the pty master, -1 on failure
- */
-int
-_vte_pty_open(pid_t *child_pid, char **env_add,
- const char *command, char **argv, const char *directory,
- int columns, int rows,
- gboolean lastlog, gboolean utmp, gboolean wtmp)
-{
- GPid child;
- int ret = -1;
-#ifdef VTE_USE_GNOME_PTY_HELPER
- int op = 0;
- int opmap[8] = {
- GNOME_PTY_OPEN_NO_DB_UPDATE, /* 0 0 0 */
- GNOME_PTY_OPEN_PTY_LASTLOG, /* 0 0 1 */
- GNOME_PTY_OPEN_PTY_UTMP, /* 0 1 0 */
- GNOME_PTY_OPEN_PTY_LASTLOGUTMP, /* 0 1 1 */
- GNOME_PTY_OPEN_PTY_WTMP, /* 1 0 0 */
- GNOME_PTY_OPEN_PTY_LASTLOGWTMP, /* 1 0 1 */
- GNOME_PTY_OPEN_PTY_UWTMP, /* 1 1 0 */
- GNOME_PTY_OPEN_PTY_LASTLOGUWTMP, /* 1 1 1 */
- };
- if (lastlog) {
- op += 1;
- }
- if (utmp) {
- op += 2;
- }
- if (wtmp) {
- op += 4;
- }
- g_assert(op >= 0 && op < (int) G_N_ELEMENTS(opmap));
- if (ret == -1 && op != 0) {
- ret = _vte_pty_open_with_helper(&child, env_add, command, argv,
- directory,
- columns, rows, opmap[op]);
- }
-#endif
- if (ret == -1) {
- ret = _vte_pty_open_unix98(&child, env_add, command, argv,
- directory, columns, rows);
- }
- if (ret != -1) {
- *child_pid = (pid_t) child;
+ priv->child_setup_data.mode = TTY_OPEN_BY_FD;
+ priv->child_setup_data.tty.fd = childfd;
+
+ // FIXMEchpe do this later
+// _vte_pty_set_size(fd, columns, rows);
+ return TRUE;
}
- _vte_debug_print(VTE_DEBUG_PTY,
- "Returning ptyfd = %d, child = %ld.\n",
- ret, (long) child);
- return ret;
}
+#endif /* VTE_USE_GNOME_PTY_HELPER */
+
/**
- * _vte_pty_set_utf8:
- * @pty: The pty master descriptor.
- * @utf8: Whether or not the pty is in UTF-8 mode.
+ * vte_pty_set_utf8:
+ * @pty: a #VtePty
+ * @utf8: Whether or not the pty is in UTF-8 mode
*
* 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.
+ *
+ * Since: 0.24
*/
void
-_vte_pty_set_utf8(int pty, gboolean utf8)
+vte_pty_set_utf8(VtePty *pty,
+ gboolean utf8)
{
#if defined(HAVE_TCSETATTR) && defined(IUTF8)
+ VtePtyPrivate *priv;
struct termios tio;
tcflag_t saved_cflag;
- if (pty != -1) {
- if (tcgetattr(pty, &tio) != -1) {
+
+ g_return_if_fail(VTE_IS_PTY(pty));
+ priv = pty->priv;
+
+ priv->utf8 = utf8 != FALSE;
+
+ if (priv->pty_fd != -1) {
+ if (tcgetattr(priv->pty_fd, &tio) != -1) {
saved_cflag = tio.c_iflag;
tio.c_iflag &= ~IUTF8;
if (utf8) {
tio.c_iflag |= IUTF8;
}
if (saved_cflag != tio.c_iflag) {
- tcsetattr(pty, TCSANOW, &tio);
+ tcsetattr(priv->pty_fd, TCSANOW, &tio);
}
}
}
#endif
+
+ g_object_notify(G_OBJECT(pty), "utf-8");
}
/**
- * _vte_pty_close:
- * @pty: the pty master descriptor.
+ * vte_pty_close:
+ * @pty: a #VtePty
*
- * Cleans up the PTY associated with the descriptor, specifically any logging
- * performed for the session. The descriptor itself remains open.
+ * Cleans up the PTY, specifically any logging performed for the session.
+ * The file descriptor to the PTY master remains open.
+ *
+ * Since: 0.24
*/
void
-_vte_pty_close(int pty)
+vte_pty_close (VtePty *pty)
{
#ifdef VTE_USE_GNOME_PTY_HELPER
+ VtePtyPrivate *priv = pty->priv;
gpointer tag;
GnomePtyOps ops;
- if (_vte_pty_helper_map != NULL) {
- if (g_tree_lookup(_vte_pty_helper_map, GINT_TO_POINTER(pty))) {
+
+ if (priv->using_helper) {
/* Signal the helper that it needs to close its
* connection. */
- tag = g_tree_lookup(_vte_pty_helper_map,
- GINT_TO_POINTER(pty));
+ tag = priv->helper_tag;
ops = GNOME_PTY_CLOSE_PTY;
if (n_write(_vte_pty_helper_tunnel,
@@ -1085,73 +1354,626 @@ _vte_pty_close(int pty)
}
n_read(_vte_pty_helper_tunnel, &ops, 1);
- /* Remove the item from the map. */
- g_tree_remove(_vte_pty_helper_map,
- GINT_TO_POINTER(pty));
- }
+ priv->helper_tag = NULL;
+ priv->using_helper = FALSE;
}
#endif
}
-#ifdef PTY_MAIN
-int fd;
+/* VTE PTY class */
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_FD,
+ PROP_UTF8,
+ PROP_TERM
+};
+
+/* GInitable impl */
+
+static gboolean
+vte_pty_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ VtePty *pty = VTE_PTY (initable);
+ VtePtyPrivate *priv = pty->priv;
+ gboolean ret = FALSE;
+ GError *err = NULL;
+
+ if (cancellable != NULL) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Cancellable initialisation not supported");
+ return FALSE;
+ }
+
+ /* If we already have a (foreign) FD, we're done. */
+ if (priv->foreign) {
+ g_assert(priv->pty_fd != -1);
+ // FIXMEchpe fill in the child_setup_data struct
+ return TRUE;
+ }
+
+#ifdef VTE_USE_GNOME_PTY_HELPER
+ if ((priv->flags & VTE_PTY_NO_HELPER) == 0) {
+ ret = _vte_pty_open_with_helper(pty, &err);
+ g_assert(ret || err != NULL);
+
+ if (ret)
+ goto out;
+
+ /* Only do fallback if gnome-pty-helper failed! */
+ if ((priv->flags & VTE_PTY_NO_FALLBACK) ||
+ !g_error_matches(err,
+ VTE_PTY_ERROR,
+ VTE_PTY_ERROR_PTY_HELPER_FAILED)) {
+ g_propagate_error (error, err);
+ goto out;
+ }
+
+ g_error_free(err);
+ /* Fall back to unix98 PTY */
+ }
+#endif /* VTE_USE_GNOME_PTY_HELPER */
+
+ ret = _vte_pty_open_unix98(pty, error);
+
+ out:
+ _vte_debug_print(VTE_DEBUG_PTY,
+ "vte_pty_initable_init returning %s with ptyfd = %d\n",
+ ret ? "TRUE" : "FALSE", priv->pty_fd);
+
+ return ret;
+}
+
+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_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, vte_pty_initable_iface_init))
static void
-sigchld_handler(int signum)
+vte_pty_init (VtePty *pty)
{
- /* This is very unsafe. Never do it in production code. */
- _vte_pty_close(fd);
+ VtePtyPrivate *priv;
+
+ priv = pty->priv = G_TYPE_INSTANCE_GET_PRIVATE (pty, VTE_TYPE_PTY, VtePtyPrivate);
+
+ priv->flags = VTE_PTY_DEFAULT;
+ priv->pty_fd = -1;
+ priv->foreign = FALSE;
+ priv->using_helper = FALSE;
+ priv->helper_tag = NULL;
+ priv->term = vte_terminal_get_default_emulation(NULL /* that's ok, this function is just retarded */); /* already interned */
}
+static void
+vte_pty_finalize (GObject *object)
+{
+ VtePty *pty = VTE_PTY (object);
+ VtePtyPrivate *priv = pty->priv;
+
+ if (priv->child_setup_data.mode == TTY_OPEN_BY_FD &&
+ priv->child_setup_data.tty.fd != -1) {
+ /* Close the child FD */
+ close(priv->child_setup_data.tty.fd);
+ }
+
+ vte_pty_close(pty);
+
+ /* 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;
+
+ case PROP_UTF8:
+ g_value_set_boolean(value, priv->utf8);
+ break;
+
+ case PROP_TERM:
+ g_value_set_string(value, priv->term);
+ 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 = g_value_get_flags(value);
+ break;
+
+ case PROP_FD:
+ priv->pty_fd = g_value_get_int(value);
+ priv->foreign = (priv->pty_fd != -1);
+ break;
+
+ case PROP_UTF8:
+ vte_pty_set_utf8 (pty, g_value_get_boolean (value));
+ break;
+
+ case PROP_TERM:
+ vte_pty_set_term(pty, g_value_get_string(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);
+
+ g_type_class_add_private(object_class, sizeof(VtePtyPrivate));
+
+ object_class->set_property = vte_pty_set_property;
+ object_class->get_property = vte_pty_get_property;
+ object_class->finalize = vte_pty_finalize;
+
+ /**
+ * VtePty:flags:
+ *
+ * Controls how the session is recorded in lastlog, utmp, and wtmp,
+ * and whether to use the GNOME PTY helper.
+ *
+ * Since: 0.24
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_FLAGS,
+ g_param_spec_flags ("flags", NULL, NULL,
+ VTE_TYPE_PTY_FLAGS,
+ VTE_PTY_DEFAULT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * VtePty:fd:
+ *
+ * The file descriptor of the PTY master.
+ *
+ * Since: 0.24
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_FD,
+ g_param_spec_int ("fd", NULL, NULL,
+ -1, G_MAXINT, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * VtePty:utf-8:
+ *
+ * Whether the PTY is in UTF-8 mode.
+ *
+ * Since: 0.24
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_UTF8,
+ g_param_spec_boolean ("utf-8", NULL, NULL,
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * VtePty:term:
+ *
+ * The value to set for the TERM environment variable
+ * in vte_pty_child_setup().
+ *
+ * Since: 0.24
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_UTF8,
+ g_param_spec_string ("term", NULL, NULL,
+ "xterm",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/* 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
+ *
+ * Since: 0.24
+ */
+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:
+ * @flags: flags from #VtePtyFlags
+ * @error: return location for a #GError, or %NULL
+ *
+ * Allocates a new pseudo-terminal.
+ *
+ * You can later use fork() or the g_spawn_async() familty 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.
+ *
+ * Also, you MUST pass the %G_SPAWN_DO_NOT_REAP_CHILD flag.
+ *
+ * When using the GNOME PTY Helper,
+ * unless some of the %VTE_PTY_NO_LASTLOG, %VTE_PTY_NO_UTMP or
+ * %VTE_PTY_NO_WTMP flags are passed in @flags, the
+ * session is logged in the corresponding lastlog, utmp or wtmp
+ * system files.
+ *
+ * When passing %VTE_PTY_NO_HELPER in @flags, the
+ * GNOME PTY Helper bypassed entirely.
+ *
+ * When passing %VTE_PTY_NO_FALLBACK in @flags,
+ * and opening a PTY using the PTY helper fails, there will
+ * be no fallback to allocate a PTY using Unix98 PTY functions.
+ *
+ * Returns: a new #VtePty, or %NULL on error with @error filled in
+ *
+ * Since: 0.24
+ */
+VtePty *
+vte_pty_new (VtePtyFlags flags,
+ GError **error)
+{
+ return g_initable_new (VTE_TYPE_PTY,
+ NULL /* cancellable */,
+ error,
+ "flags", flags,
+ NULL);
+}
+
+/**
+ * vte_pty_new_foreign:
+ * @fd: a file descriptor to the PTY
+ *
+ * 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: a new #VtePty for @fd
+ *
+ * Since: 0.24
+ */
+VtePty *
+vte_pty_new_foreign (int fd)
+{
+ VtePty *pty;
+ GError *error = NULL;
+
+ g_return_val_if_fail(fd >= 0, NULL);
+
+ pty = g_initable_new (VTE_TYPE_PTY,
+ NULL /* cancellable */,
+ &error,
+ "fd", fd,
+ NULL);
+ g_assert(error == NULL);
+
+ return pty;
+}
+
+/**
+ * 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
+ */
int
-main(int argc, char **argv)
+vte_pty_get_fd (VtePty *pty)
{
- GPid child = 0;
- char c;
- int ret;
- signal(SIGCHLD, sigchld_handler);
- _vte_debug_init();
- fd = _vte_pty_open(&child, NULL,
- (argc > 1) ? argv[1] : NULL,
- (argc > 1) ? argv + 1 : NULL,
- NULL,
- 0, 0,
- TRUE, TRUE, TRUE);
- if (child == 0) {
- int i;
- for (i = 0; ; i++) {
- switch (i % 3) {
- case 0:
- case 1:
- g_print("%d\n", i);
- break;
- case 2:
- g_printerr("%d\n", i);
- break;
- default:
- g_assert_not_reached();
- break;
- }
- sleep(1);
- }
- }
- g_print("Child pid is %d.\n", (int)child);
- do {
- ret = n_read(fd, &c, 1);
- if (ret == 0) {
- break;
- }
- if ((ret == -1) && (errno != EAGAIN) && (errno != EINTR)) {
- break;
- }
- if (argc < 2) {
- n_write(STDOUT_FILENO, "[", 1);
- }
- n_write(STDOUT_FILENO, &c, 1);
- if (argc < 2) {
- n_write(STDOUT_FILENO, "]", 1);
- }
- } while (TRUE);
- return 0;
+ 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;
}
-#endif
+
+/**
+ * vte_pty_set_term:
+ * @pty: a #VtePty
+ * @emulation: the name of a terminal description
+ *
+ * Sets what value of the TERM environment variable to set
+ * when using vte_pty_child_setup().
+ *
+ * Since: 0.24
+ */
+void
+vte_pty_set_term (VtePty *pty,
+ const char *emulation)
+{
+ VtePtyPrivate *priv;
+
+ g_return_if_fail(VTE_IS_PTY(pty));
+ g_return_if_fail(emulation != NULL);
+
+ priv = pty->priv;
+ emulation = g_intern_string(emulation);
+ if (emulation == priv->term)
+ return;
+
+ priv->term = emulation;
+ g_object_notify(G_OBJECT(pty), "term");
+}
+
+/* Reimplementation of the ugly deprecated APIs _vte_pty_*() */
+
+#ifndef VTE_DISABLE_DEPRECATED_SOURCE
+
+static GHashTable *fd_to_pty_hash = NULL;
+
+static VtePty *
+get_vte_pty_for_fd (int fd)
+{
+ VtePty *pty;
+
+ if (fd_to_pty_hash != NULL &&
+ (pty = g_hash_table_lookup(fd_to_pty_hash, &fd)) != NULL)
+ return pty;
+
+ g_warning("No VtePty found for fd %d!\n", fd);
+ return NULL;
+}
+
+/**
+ * _vte_pty_open:
+ * @child: location to store the new process's ID
+ * @env_add: a list of environment variables to add to the child's environment
+ * @command: name of the binary to run
+ * @argv: arguments to pass to @command
+ * @directory: directory to start the new command in, or %NULL
+ * @columns: desired window columns
+ * @rows: desired window rows
+ * @lastlog: %TRUE if the lastlog should be updated
+ * @utmp: %TRUE if the utmp or utmpx log should be updated
+ * @wtmp: %TRUE if the wtmp or wtmpx log should be updated
+ *
+ * Starts a new copy of @command running under a psuedo-terminal, optionally in
+ * the supplied @directory, with window size set to @rows x @columns
+ * and variables in @env_add added to its environment. If any combination of
+ * @lastlog, @utmp, and @wtmp is set, then the session is logged in the
+ * corresponding system files.
+ *
+ * Returns: an open file descriptor for the pty master, -1 on failure
+ *
+ * Deprecated: 0.24: Use #VtePty together with fork() or the g_spawn_async() family of functions instead
+ */
+int
+_vte_pty_open(pid_t *child,
+ char **env_add,
+ const char *command,
+ char **argv,
+ const char *directory,
+ int columns,
+ int rows,
+ gboolean lastlog,
+ gboolean utmp,
+ gboolean wtmp)
+{
+ VtePty *pty;
+ GPid pid;
+ gboolean ret;
+
+ pty = vte_pty_new(__vte_pty_get_pty_flags (lastlog, utmp, wtmp), NULL);
+ if (pty == NULL)
+ return -1;
+
+ if (command != NULL) {
+ char **real_argv;
+ GSpawnFlags spawn_flags;
+
+ spawn_flags = G_SPAWN_CHILD_INHERITS_STDIN |
+ G_SPAWN_SEARCH_PATH;
+ real_argv = __vte_pty_get_argv(command, argv, &spawn_flags);
+ ret = __vte_pty_spawn(pty,
+ directory,
+ real_argv,
+ env_add,
+ spawn_flags,
+ NULL, NULL,
+ &pid,
+ NULL);
+ g_strfreev(real_argv);
+ } else {
+ ret = __vte_pty_fork(pty, &pid, NULL);
+ }
+
+ if (!ret) {
+ g_object_unref(pty);
+ return -1;
+ }
+
+ vte_pty_set_size(pty, rows, columns, NULL);
+
+ /* Stash the pty in the hash so we can later retrieve it by FD */
+ if (fd_to_pty_hash == NULL) {
+ fd_to_pty_hash = g_hash_table_new_full(g_int_hash,
+ g_int_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+ }
+
+ g_hash_table_insert(fd_to_pty_hash, &pty->priv->pty_fd, pty /* adopt refcount */);
+
+ if (child)
+ *child = (pid_t) pid;
+
+ return vte_pty_get_fd(pty);
+}
+
+/**
+ * _vte_pty_get_size:
+ * @master: the file descriptor of the PTY master
+ * @columns: a place to store the number of columns
+ * @rows: a place to store the number of rows
+ *
+ * Attempts to read the pseudo terminal's window size.
+ *
+ * Returns: 0 on success, -1 on failure.
+ *
+ * Deprecated: 0.24: Use #VtePty and vte_pty_get_size() instead
+ */
+int
+_vte_pty_get_size(int master,
+ int *columns,
+ int *rows)
+{
+ VtePty *pty;
+
+ if ((pty = get_vte_pty_for_fd(master)) == NULL)
+ return -1;
+
+ if (vte_pty_get_size(pty, rows, columns, NULL))
+ return 0;
+
+ return -1;
+}
+
+/**
+ * _vte_pty_set_size:
+ * @master: the file descriptor of the PTY master
+ * @columns: the desired number of columns
+ * @rows: the desired number of rows
+ *
+ * Attempts to resize the pseudo terminal's window size. If successful, the
+ * OS kernel will send #SIGWINCH to the child process group.
+ *
+ * Returns: 0 on success, -1 on failure.
+ *
+ * Deprecated: 0.24: Use #VtePty and vte_pty_set_size() instead
+ */
+int
+_vte_pty_set_size(int master,
+ int columns,
+ int rows)
+{
+ VtePty *pty;
+
+ if ((pty = get_vte_pty_for_fd(master)) == NULL)
+ return -1;
+
+ if (vte_pty_set_size(pty, rows, columns, NULL))
+ return 0;
+
+ return -1;
+}
+
+/**
+ * _vte_pty_set_utf8:
+ * @pty: The pty master descriptor.
+ * @utf8: Whether or not the pty is in UTF-8 mode.
+ *
+ * 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.
+ *
+ * Deprecated: 0.24: Use #VtePty and vte_pty_set_utf8() instead
+ */
+void _vte_pty_set_utf8(int master,
+ gboolean utf8)
+{
+ VtePty *pty;
+
+ if ((pty = get_vte_pty_for_fd(master)) == NULL)
+ return;
+
+ vte_pty_set_utf8(pty, utf8);
+}
+
+/**
+ * _vte_pty_close:
+ * @pty: the pty master descriptor.
+ *
+ * Cleans up the PTY associated with the descriptor, specifically any logging
+ * performed for the session. The descriptor itself remains open.
+ *
+ * Deprecated: 0.24: Use #VtePty and vte_pty_close() instead
+ */
+void _vte_pty_close(int master)
+{
+ VtePty *pty;
+
+ if ((pty = get_vte_pty_for_fd(master)) == NULL)
+ return;
+
+ /* Prevent closing the FD */
+ pty->priv->pty_fd = -1;
+
+ g_hash_table_remove(fd_to_pty_hash, &master);
+
+ if (g_hash_table_size(fd_to_pty_hash) == 0) {
+ g_hash_table_destroy(fd_to_pty_hash);
+ fd_to_pty_hash = NULL;
+ }
+}
+
+#endif /* !VTE_DISABLE_DEPRECATED_SOURCE */
diff --git a/src/pty.h b/src/pty.h
index 063f92d..726d639 100644
--- a/src/pty.h
+++ b/src/pty.h
@@ -16,6 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifndef VTE_DISABLE_DEPRECATED
+
#ifndef vte_pty_h_included
#define vte_pty_h_included
@@ -45,4 +47,6 @@ void _vte_pty_close(int pty);
G_END_DECLS
-#endif
+#endif /* vte_pty_h_included */
+
+#endif /* !VTE_DISABLE_DEPRECATED */
diff --git a/src/vte-private.h b/src/vte-private.h
index 5681e12..ba0e432 100644
--- a/src/vte-private.h
+++ b/src/vte-private.h
@@ -169,8 +169,7 @@ struct _VteTerminalPrivate {
int default_column_count, default_row_count; /* default sizes */
/* PTY handling data. */
- const char *shell; /* shell we started */
- int pty_master; /* pty master descriptor */
+ VtePty *pty;
GIOChannel *pty_channel; /* master channel */
guint pty_input_source;
guint pty_output_source;
diff --git a/src/vte.c b/src/vte.c
index 1f2003d..7030478 100644
--- a/src/vte.c
+++ b/src/vte.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004,2009,2010 Red Hat, Inc.
- * Copyright © 2009, 2010 Christian Persch
+ * Copyright © 2008, 2009, 2010 Christian Persch
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU Library General Public License as published by
@@ -54,6 +54,8 @@
#include "pty.h"
#include "vteaccess.h"
#include "vteint.h"
+#include "vtepty.h"
+#include "vtepty-private.h"
#include "vteregex.h"
#include "vtetc.h"
@@ -158,6 +160,7 @@ enum {
PROP_ICON_TITLE,
PROP_MOUSE_POINTER_AUTOHIDE,
PROP_PTY,
+ PROP_PTY_OBJECT,
PROP_SCROLL_BACKGROUND,
PROP_SCROLLBACK_LINES,
PROP_SCROLL_ON_KEYSTROKE,
@@ -1255,7 +1258,7 @@ vte_terminal_cursor_new(VteTerminal *terminal, GdkCursorType cursor_type)
*
* Returns: an integer associated with this expression
*
- * @Deprecated: 0.17.1
+ * Deprecated: 0.17.1
*/
int
vte_terminal_match_add(VteTerminal *terminal, const char *match)
@@ -2180,8 +2183,10 @@ vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
static void
_vte_terminal_setup_utf8 (VteTerminal *terminal)
{
- _vte_pty_set_utf8(terminal->pvt->pty_master,
- (strcmp(terminal->pvt->encoding, "UTF-8") == 0));
+ VteTerminalPrivate *pvt = terminal->pvt;
+
+ vte_pty_set_utf8(pvt->pty,
+ strcmp(terminal->pvt->encoding, "UTF-8") == 0);
}
/**
@@ -3225,34 +3230,7 @@ vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status,
terminal->pvt->pty_pid = -1;
/* Close out the PTY. */
- _vte_terminal_disconnect_pty_read(terminal);
- _vte_terminal_disconnect_pty_write(terminal);
- if (terminal->pvt->pty_channel != NULL) {
- g_io_channel_unref (terminal->pvt->pty_channel);
- terminal->pvt->pty_channel = NULL;
- }
- if (terminal->pvt->pty_master != -1) {
- _vte_pty_close(terminal->pvt->pty_master);
- close(terminal->pvt->pty_master);
- terminal->pvt->pty_master = -1;
-
- g_object_notify(object, "pty");
- }
-
- /* 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 (terminal->pvt->incoming != NULL) {
- vte_terminal_process_incoming(terminal);
- _vte_incoming_chunks_release (terminal->pvt->incoming);
- terminal->pvt->incoming = NULL;
- terminal->pvt->input_bytes = 0;
- }
- g_array_set_size(terminal->pvt->pending, 0);
- vte_terminal_stop_processing (terminal);
-
- /* Clear the outgoing buffer as well. */
- _vte_buffer_clear(terminal->pvt->outgoing);
+ vte_terminal_set_pty_object(terminal, NULL);
/* Tell observers what's happened. */
terminal->pvt->child_exit_status = status;
@@ -3297,9 +3275,12 @@ static void mark_output_source_invalid(VteTerminal *terminal)
static void
_vte_terminal_connect_pty_write(VteTerminal *terminal)
{
+ VteTerminalPrivate *pvt = terminal->pvt;
+
+ g_assert(pvt->pty != NULL);
if (terminal->pvt->pty_channel == NULL) {
- terminal->pvt->pty_channel =
- g_io_channel_unix_new(terminal->pvt->pty_master);
+ pvt->pty_channel =
+ g_io_channel_unix_new(vte_pty_get_fd(pvt->pty));
}
if (terminal->pvt->pty_output_source == 0) {
@@ -3340,102 +3321,187 @@ _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
}
}
-/* Basic wrapper around _vte_pty_open(), which handles the pipefitting. */
-static pid_t
-_vte_terminal_fork_basic(VteTerminal *terminal, const char *command,
- char **argv, char **envv,
- const char *directory,
- gboolean lastlog, gboolean utmp, gboolean wtmp)
+/**
+ * vte_terminal_pty_new:
+ * @terminal: a #VteTerminal
+ * @flags: flags from #VtePtyFlags
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a new #VtePty, and sets the emulation property
+ * from #VteTerminal:emulation.
+ *
+ * See vte_pty_new() for more information.
+ *
+ * Since: 0.24
+ */
+VtePty *
+vte_terminal_pty_new(VteTerminal *terminal,
+ VtePtyFlags flags,
+ GError **error)
{
- GObject *object = G_OBJECT(terminal);
- char **env_add;
- int i, fd;
- pid_t pid;
- VteReaper *reaper;
+ VteTerminalPrivate *pvt;
+ VtePty *pty;
- g_object_freeze_notify(object);
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
- /* Duplicate the environment, and add one more variable. */
- i = envv ? g_strv_length(envv) : 0;
- env_add = g_new(char *, i + 2);
- env_add[0] = g_strdup_printf("TERM=%s", terminal->pvt->emulation);
- for (i = 0; (envv != NULL) && (envv[i] != NULL); i++) {
- env_add[i + 1] = g_strdup(envv[i]);
- }
- env_add[i + 1] = NULL;
+ pvt = terminal->pvt;
- /* Close any existing ptys. */
- if (terminal->pvt->pty_channel != NULL) {
- g_io_channel_unref (terminal->pvt->pty_channel);
- terminal->pvt->pty_channel = NULL;
- }
- if (terminal->pvt->pty_master != -1) {
- _vte_pty_close(terminal->pvt->pty_master);
- close(terminal->pvt->pty_master);
- terminal->pvt->pty_master = -1;
+ pty = vte_pty_new(flags, error);
+ if (pty == NULL)
+ return NULL;
- g_object_notify(object, "pty");
+ vte_pty_set_term(pty, vte_terminal_get_emulation(terminal));
- }
+ return pty;
+}
- terminal->pvt->child_exit_status = 0;
+/**
+ * vte_terminal_watch_child:
+ * @terminal: a #VteTerminal
+ * @child_pid: a #GPid
+ *
+ * Watches @child_pid. When the process exists, the #VteReaper::child-exited
+ * signal will be called. Use vte_terminal_get_child_exit_status() to
+ * retrieve the child's exit status.
+ *
+ * Prior to calling this function, a #VtePty must have been set in @terminal
+ * using vte_terminal_set_pty_object().
+ * When the child exits, the terminal's #VtePty will be set to %NULL.
+ *
+ * Note: g_child_watch_add() or g_child_watch_add_full() must not have
+ * been called for @child_pid, nor a #GSource for it been created with
+ * g_child_watch_source_new().
+ *
+ * Note: when using the g_spawn_async() family of functions,
+ * the %G_SPAWN_DO_NOT_REAP_CHILD flag MUST have been passed.
+ *
+ * Since: 0.24
+ */
+void
+vte_terminal_watch_child (VteTerminal *terminal,
+ GPid child_pid)
+{
+ VteTerminalPrivate *pvt;
+ GObject *object;
+ VteReaper *reaper;
- /* Open the new pty. */
- pid = -1;
- fd = _vte_pty_open(&pid, env_add, command, argv, directory,
- terminal->column_count, terminal->row_count,
- lastlog, utmp, wtmp);
- if (pid == -1 || fd == -1) {
- g_strfreev(env_add);
+ g_return_if_fail(VTE_IS_TERMINAL(terminal));
+ g_return_if_fail(child_pid != -1);
- g_object_thaw_notify(object);
+ pvt = terminal->pvt;
+ g_return_if_fail(pvt->pty != NULL);
- return -1;
- }
+ // FIXMEchpe: support passing child_pid = -1 to remove the wathch
- /* If we successfully started the process, set up to listen for its
- * output. */
- if (pid != 0) {
- /* Set this as the child's pid. */
- terminal->pvt->pty_pid = pid;
+ object = G_OBJECT(terminal);
- vte_terminal_set_pty (terminal, fd);
+ g_object_freeze_notify(object);
- /* Catch a child-exited signal from the child pid. */
- reaper = vte_reaper_get();
- vte_reaper_add_child(pid);
- if (reaper != terminal->pvt->pty_reaper) {
- if (terminal->pvt->pty_reaper != NULL) {
- g_signal_handlers_disconnect_by_func(terminal->pvt->pty_reaper,
- vte_terminal_catch_child_exited,
- terminal);
- g_object_unref(terminal->pvt->pty_reaper);
- }
- g_signal_connect(reaper, "child-exited",
- G_CALLBACK(vte_terminal_catch_child_exited),
- terminal);
- terminal->pvt->pty_reaper = reaper;
- } else
- g_object_unref(reaper);
+ /* Set this as the child's pid. */
+ pvt->pty_pid = child_pid;
+ pvt->child_exit_status = 0;
+
+ /* Catch a child-exited signal from the child pid. */
+ reaper = vte_reaper_get();
+ vte_reaper_add_child(child_pid);
+ if (reaper != pvt->pty_reaper) {
+ if (terminal->pvt->pty_reaper != NULL) {
+ g_signal_handlers_disconnect_by_func(pvt->pty_reaper,
+ vte_terminal_catch_child_exited,
+ terminal);
+ g_object_unref(pvt->pty_reaper);
+ }
+ g_signal_connect(reaper, "child-exited",
+ G_CALLBACK(vte_terminal_catch_child_exited),
+ terminal);
+ pvt->pty_reaper = reaper;
+ } else {
+ g_object_unref(reaper);
}
- /* Clean up. */
- g_strfreev(env_add);
+ /* FIXMEchpe: call vte_terminal_set_size here? */
g_object_thaw_notify(object);
+}
+
+/*
+ * _vte_terminal_get_user_shell:
+ *
+ * Uses getpwd() to determine the user's shell. If that fails, falls back
+ * to using the SHELL environment variable. As last-ditch fallback, returns
+ * "/bin/sh".
+ *
+ * Returns: a newly allocated string containing the command to run the
+ * user's shell
+ */
+static char *
+_vte_terminal_get_user_shell (void)
+{
+ struct passwd *pwd;
+ char *command;
- /* Return the pid to the caller. */
- return pid;
+ pwd = getpwuid(getuid());
+ if (pwd != NULL) {
+ command = g_strdup (pwd->pw_shell);
+ _vte_debug_print(VTE_DEBUG_MISC,
+ "Using user's shell (%s).\n",
+ command ? command : "(null)");
+ }
+ if (command == NULL) {
+ if (g_getenv ("SHELL")) {
+ command = g_strdup (g_getenv ("SHELL"));
+ _vte_debug_print(VTE_DEBUG_MISC,
+ "Using $SHELL shell (%s).\n",
+ command);
+ } else {
+ command = g_strdup ("/bin/sh");
+ _vte_debug_print(VTE_DEBUG_MISC,
+ "Using default shell (%s).\n",
+ command);
+ }
+ }
+
+ g_assert (command != NULL);
+
+ return command;
+}
+
+/*
+ * _vte_terminal_get_argv:
+ * @command: the command to run
+ * @argv: the argument vector
+ * @flags: (inout) flags from #GSpawnFlags
+ *
+ * Creates an argument vector to pass to g_spawn_async() from @command and
+ * @argv, modifying * flags if necessary.
+ * Like __vte_pty_get_argv(), but returns the argument vector to spawn
+ * the user's shell if @command is %NULL.
+ *
+ * Returns: a newly allocated array of strings. Free with g_strfreev()
+ */
+static char **
+_vte_terminal_get_argv (const char *command,
+ char **argv,
+ GSpawnFlags *flags /* inout */)
+{
+ char **argv2;
+ char *shell = NULL;
+
+ argv2 = __vte_pty_get_argv(command ? command : (shell = _vte_terminal_get_user_shell()),
+ argv,
+ flags);
+ g_free(shell);
+ return argv2;
}
/**
* vte_terminal_fork_command:
* @terminal: a #VteTerminal
- * @command: the name of a binary to run, or %NULL to get user's shell
+ * @command: the name of a binary to run, or %NULL to spawn the user's shell
* @argv: the argument list to be passed to @command, or %NULL
* @envv: a list of environment variables to be added to the environment before
* starting @command, or %NULL
- * @directory: the name of a directory the command should start in, or %NULL
+ * @working_directory: the name of a directory the command should start in, or %NULL
* @lastlog: %TRUE if the session should be logged to the lastlog
* @utmp: %TRUE if the session should be logged to the utmp/utmpx log
* @wtmp: %TRUE if the session should be logged to the wtmp/wtmpx log
@@ -3449,49 +3515,142 @@ _vte_terminal_fork_basic(VteTerminal *terminal, const char *command,
*
* Note that all file descriptors except stdin/stdout/stderr will be closed
* before calling exec() in the child.
- *
- * Returns: the ID of the new process
+ *
+ * Returns: the PID of the new process
+ *
+ * Deprecated: 0.24: Use vte_terminal_fork_command_full()
*/
pid_t
vte_terminal_fork_command(VteTerminal *terminal,
- const char *command, char **argv, char **envv,
- const char *directory,
- gboolean lastlog, gboolean utmp, gboolean wtmp)
-{
- g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
+ const char *command,
+ char **argv,
+ char **envv,
+ const char *working_directory,
+ gboolean lastlog,
+ gboolean utmp,
+ gboolean wtmp)
+{
+ char **real_argv;
+ GSpawnFlags spawn_flags;
+ GPid child_pid;
+ gboolean ret;
+#ifdef VTE_DEBUG
+ GError *error = NULL;
+ GError **err = &error;
+#else
+ GError **err = NULL;
+#endif
- /* Make the user's shell the default command. */
- if (command == NULL) {
- if (terminal->pvt->shell == NULL) {
- struct passwd *pwd;
-
- pwd = getpwuid(getuid());
- if (pwd != NULL) {
- terminal->pvt->shell = pwd->pw_shell;
- _vte_debug_print(VTE_DEBUG_MISC,
- "Using user's shell (%s).\n",
- terminal->pvt->shell);
- }
- }
- if (terminal->pvt->shell == NULL) {
- if (getenv ("SHELL")) {
- terminal->pvt->shell = getenv ("SHELL");
- _vte_debug_print(VTE_DEBUG_MISC,
- "Using $SHELL shell (%s).\n",
- terminal->pvt->shell);
- } else {
- terminal->pvt->shell = "/bin/sh";
- _vte_debug_print(VTE_DEBUG_MISC,
- "Using default shell (%s).\n",
- terminal->pvt->shell);
- }
- }
- command = terminal->pvt->shell;
- }
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
+
+ spawn_flags = G_SPAWN_CHILD_INHERITS_STDIN |
+ G_SPAWN_SEARCH_PATH;
+ real_argv = _vte_terminal_get_argv (command, argv, &spawn_flags);
+
+ ret = vte_terminal_fork_command_full(terminal,
+ __vte_pty_get_pty_flags(lastlog, utmp, wtmp),
+ working_directory,
+ real_argv,
+ envv,
+ spawn_flags,
+ NULL, NULL,
+ &child_pid,
+ err);
+ g_strfreev (real_argv);
+
+#ifdef VTE_DEBUG
+ if (error) {
+ _vte_debug_print(VTE_DEBUG_MISC,
+ "vte_terminal_fork_command failed: %s\n", error->message);
+ g_error_free(error);
+ }
+#endif
+
+ if (!ret)
+ return -1;
+
+ return (pid_t) child_pid;
+}
+
+/**
+ * vte_terminal_fork_command_full:
+ * @terminal: a #VteTerminal
+ * @pty_flags: flags from #VtePtyFlags
+ * @argv: child's argument vector
+ * @envv: a list of environment variables to be added to the environment before
+ * starting the process, or %NULL
+ * @working_directory: the name of a directory the command should start in, or %NULL
+ * to use the cwd
+ * @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
+ * @error: 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 reflect the
+ * terminal widget's emulation setting.
+ * @vte_spawn_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 unless @flags contains %G_SPAWN_LEAVE_DESCRIPTORS_OPEN, all file
+ * descriptors except stdin/stdout/stderr will be closed before calling exec()
+ * in the child.
+ *
+ * See vte_terminal_FIXMEchpe() and g_spawn_async() for more information.
+ *
+ * Returns: %TRUE on success, or %FALSE on error with @error filled in
+ *
+ * Since: 0.24
+ */
+gboolean
+vte_terminal_fork_command_full(VteTerminal *terminal,
+ 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 */,
+ GError **error)
+{
+ VtePty *pty;
+ GPid pid;
+
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
+ g_return_val_if_fail(argv != NULL, FALSE);
+ g_return_val_if_fail(child_setup_data == NULL || child_setup, FALSE);
+ g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
+
+ pty = vte_pty_new(pty_flags, error);
+ if (pty == NULL)
+ return FALSE;
+
+ /* FIXMEchpe: is this flag needed */
+ spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN;
+
+ if (!__vte_pty_spawn(pty,
+ working_directory,
+ argv,
+ envv,
+ spawn_flags,
+ child_setup, child_setup_data,
+ &pid,
+ error)) {
+ g_object_unref(pty);
+ return FALSE;
+ }
+
+ vte_terminal_set_pty_object(terminal, pty);
+ vte_terminal_watch_child(terminal, pid);
+
+ if (child_pid)
+ *child_pid = pid;
- /* Start up the command and get the PTY of the master. */
- return _vte_terminal_fork_basic(terminal, command, argv, envv,
- directory, lastlog, utmp, wtmp);
+ return TRUE;
}
/**
@@ -3499,7 +3658,7 @@ vte_terminal_fork_command(VteTerminal *terminal,
* @terminal: a #VteTerminal
* @envv: a list of environment variables to be added to the environment before
* starting returning in the child process, or %NULL
- * @directory: the name of a directory the child process should change to, or
+ * @working_directory: the name of a directory the child process should change to, or
* %NULL
* @lastlog: %TRUE if the session should be logged to the lastlog
* @utmp: %TRUE if the session should be logged to the utmp/utmpx log
@@ -3510,20 +3669,48 @@ vte_terminal_fork_command(VteTerminal *terminal,
* emulation setting. If @lastlog, @utmp, or @wtmp are %TRUE, logs the session
* to the specified system log files.
*
+ * Note that all file descriptors except stdin/stdout/stderr will be closed
+ * in the child.
+ *
+ * Note that @envv and @working_directory are silently ignored.
+ *
* Returns: the ID of the new process in the parent, 0 in the child, and -1 if
* there was an error
*
* Since: 0.11.11
+ *
+ * Deprecated: 0.24: Use #VtePty and fork() instead
*/
pid_t
vte_terminal_forkpty(VteTerminal *terminal,
- char **envv, const char *directory,
+ char **envv, const char *working_directory,
gboolean lastlog, gboolean utmp, gboolean wtmp)
{
- g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
+#ifdef HAVE_FORK
+ VtePty *pty;
+ GPid pid;
+
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
+
+ pty = vte_pty_new(__vte_pty_get_pty_flags(lastlog, utmp, wtmp), NULL);
+ if (pty == NULL)
+ return FALSE;
- return _vte_terminal_fork_basic(terminal, NULL, NULL, envv,
- directory, lastlog, utmp, wtmp);
+ if (!__vte_pty_fork(pty,
+ &pid,
+ NULL)) {
+ g_object_unref(pty);
+ return FALSE;
+ }
+
+ vte_terminal_set_pty_object(terminal, pty);
+ // FIXMEchpe is that really right?
+ vte_terminal_watch_child(terminal, pid);
+
+ return pid;
+#else
+ return -1;
+#endif
}
/* Handle an EOF from the client. */
@@ -3534,36 +3721,7 @@ vte_terminal_eof(GIOChannel *channel, VteTerminal *terminal)
g_object_freeze_notify(object);
-
- /* Close the connections to the child -- note that the source channel
- * has already been dereferenced. */
-
- _vte_terminal_disconnect_pty_read(terminal);
- _vte_terminal_disconnect_pty_write(terminal);
- if (terminal->pvt->pty_channel != NULL) {
- g_io_channel_unref (terminal->pvt->pty_channel);
- terminal->pvt->pty_channel = NULL;
- }
- if (terminal->pvt->pty_master != -1) {
- _vte_pty_close(terminal->pvt->pty_master);
- close(terminal->pvt->pty_master);
- terminal->pvt->pty_master = -1;
-
- g_object_notify(object, "pty");
- }
-
- /* 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. */
- vte_terminal_stop_processing (terminal);
- if (terminal->pvt->incoming) {
- vte_terminal_process_incoming(terminal);
- terminal->pvt->input_bytes = 0;
- }
- g_array_set_size(terminal->pvt->pending, 0);
-
- /* Clear the outgoing buffer as well. */
- _vte_buffer_clear(terminal->pvt->outgoing);
+ vte_terminal_set_pty_object(terminal, NULL);
/* Emit a signal that we read an EOF. */
vte_terminal_queue_eof(terminal);
@@ -4379,7 +4537,7 @@ vte_terminal_send(VteTerminal *terminal, const char *encoding,
}
/* If there's a place for it to go, add the data to the
* outgoing buffer. */
- if ((cooked_length > 0) && (terminal->pvt->pty_master != -1)) {
+ if ((cooked_length > 0) && (terminal->pvt->pty != NULL)) {
_vte_buffer_append(terminal->pvt->outgoing,
cooked, cooked_length);
_VTE_DEBUG_IF(VTE_DEBUG_KEYBOARD) {
@@ -4453,7 +4611,7 @@ vte_terminal_feed_child_binary(VteTerminal *terminal, const char *data, glong le
/* If there's a place for it to go, add the data to the
* outgoing buffer. */
- if (terminal->pvt->pty_master != -1) {
+ if (terminal->pvt->pty != NULL) {
_vte_buffer_append(terminal->pvt->outgoing,
data, length);
/* If we need to start waiting for the child pty to
@@ -4877,8 +5035,8 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
suppress_meta_esc = TRUE;
break;
case VTE_ERASE_TTY:
- if (terminal->pvt->pty_master != -1 &&
- tcgetattr(terminal->pvt->pty_master, &tio) != -1)
+ if (terminal->pvt->pty != NULL &&
+ tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
normal_length = 1;
@@ -4890,8 +5048,8 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
#ifndef _POSIX_VDISABLE
#define _POSIX_VDISABLE '\0'
#endif
- if (terminal->pvt->pty_master != -1 &&
- tcgetattr(terminal->pvt->pty_master, &tio) != -1 &&
+ if (terminal->pvt->pty != NULL &&
+ tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1 &&
tio.c_cc[VERASE] != _POSIX_VDISABLE)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
@@ -4920,8 +5078,8 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
normal_length = 1;
break;
case VTE_ERASE_TTY:
- if (terminal->pvt->pty_master != -1 &&
- tcgetattr(terminal->pvt->pty_master, &tio) != -1)
+ if (terminal->pvt->pty != NULL &&
+ tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1)
{
normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
normal_length = 1;
@@ -7554,18 +7712,19 @@ vte_terminal_get_font(VteTerminal *terminal)
static void
vte_terminal_refresh_size(VteTerminal *terminal)
{
+ VteTerminalPrivate *pvt = terminal->pvt;
int rows, columns;
- if (terminal->pvt->pty_master != -1) {
- /* Use an ioctl to read the size of the terminal. */
- if (_vte_pty_get_size(terminal->pvt->pty_master, &columns, &rows) != 0) {
- int errsv = errno;
+ GError *error = NULL;
- g_warning(_("Error reading PTY size, using defaults: "
- "%s."), g_strerror(errsv));
- } else {
- terminal->row_count = rows;
- terminal->column_count = columns;
- }
+ if (pvt->pty == NULL)
+ return;
+
+ if (vte_pty_get_size(pvt->pty, &rows, &columns, &error)) {
+ terminal->row_count = rows;
+ terminal->column_count = columns;
+ } else {
+ g_warning(_("Error reading PTY size, using defaults: %s\n"), error->message);
+ g_error_free(error);
}
}
@@ -7593,13 +7752,16 @@ vte_terminal_set_size(VteTerminal *terminal, glong columns, glong rows)
old_rows = terminal->row_count;
old_columns = terminal->column_count;
- if (terminal->pvt->pty_master != -1) {
- /* Try to set the terminal size. */
- if (_vte_pty_set_size(terminal->pvt->pty_master, columns, rows) != 0) {
- g_warning("Error setting PTY size: %s.",
- g_strerror(errno));
+ if (terminal->pvt->pty != NULL) {
+ GError *error = NULL;
+
+ /* Try to set the terminal size, and read it back,
+ * in case something went awry.
+ */
+ if (!vte_pty_set_size(terminal->pvt->pty, rows, columns, &error)) {
+ g_warning("%s\n", error->message);
+ g_error_free(error);
}
- /* Read the terminal size, in case something went awry. */
vte_terminal_refresh_size(terminal);
} else {
terminal->row_count = rows;
@@ -7774,6 +7936,7 @@ vte_terminal_set_emulation(VteTerminal *terminal, const char *emulation)
g_object_thaw_notify(object);
}
+/* FIXMEchpe deprecate this function, it's wrong */
/**
* vte_terminal_get_default_emulation:
* @terminal: a #VteTerminal
@@ -7781,14 +7944,15 @@ vte_terminal_set_emulation(VteTerminal *terminal, const char *emulation)
* Queries the terminal for its default emulation, which is attempted if the
* terminal type passed to vte_terminal_set_emulation() is %NULL.
*
- * Returns: the name of the default terminal type the widget attempts to emulate
+ * Returns: an interned string containing the name of the default terminal
+ * type the widget attempts to emulate
*
* Since: 0.11.11
*/
const char *
vte_terminal_get_default_emulation(VteTerminal *terminal)
{
- return VTE_DEFAULT_EMULATION;
+ return g_intern_static_string(VTE_DEFAULT_EMULATION);
}
/**
@@ -7798,7 +7962,8 @@ vte_terminal_get_default_emulation(VteTerminal *terminal)
* Queries the terminal for its current emulation, as last set by a call to
* vte_terminal_set_emulation().
*
- * Returns: the name of the terminal type the widget is attempting to emulate
+ * Returns: an interned string containing the name of the terminal type the
+ * widget is attempting to emulate
*/
const char *
vte_terminal_get_emulation(VteTerminal *terminal)
@@ -7948,12 +8113,11 @@ vte_terminal_init(VteTerminal *terminal)
/* Setting the terminal type and size requires the PTY master to
* be set up properly first. */
- pvt->pty_master = -1;
+ pvt->pty = NULL;
vte_terminal_set_emulation(terminal, NULL);
vte_terminal_set_size(terminal,
pvt->default_column_count,
pvt->default_row_count);
- g_assert (pvt->pty_master == -1);
pvt->pty_input_source = 0;
pvt->pty_output_source = 0;
pvt->pty_pid = -1;
@@ -8038,7 +8202,7 @@ vte_terminal_size_request(GtkWidget *widget, GtkRequisition *requisition)
vte_terminal_ensure_font (terminal);
- if (terminal->pvt->pty_master != -1) {
+ if (terminal->pvt->pty != NULL) {
vte_terminal_refresh_size(terminal);
requisition->width = terminal->char_width *
terminal->column_count;
@@ -8060,10 +8224,10 @@ vte_terminal_size_request(GtkWidget *widget, GtkRequisition *requisition)
"[Terminal %p] Size request is %dx%d for %ldx%ld cells.\n",
terminal,
requisition->width, requisition->height,
- (terminal->pvt->pty_master != -1) ?
+ (terminal->pvt->pty != NULL) ?
terminal->column_count :
terminal->pvt->default_column_count,
- (terminal->pvt->pty_master != -1) ?
+ (terminal->pvt->pty != NULL) ?
terminal->row_count :
terminal->pvt->default_row_count);
}
@@ -8449,9 +8613,9 @@ vte_terminal_finalize(GObject *object)
if (terminal->pvt->pty_channel != NULL) {
g_io_channel_unref (terminal->pvt->pty_channel);
}
- if (terminal->pvt->pty_master != -1) {
- _vte_pty_close(terminal->pvt->pty_master);
- close(terminal->pvt->pty_master);
+ if (terminal->pvt->pty != NULL) {
+ vte_pty_close(terminal->pvt->pty);
+ g_object_unref(terminal->pvt->pty);
}
/* Remove hash tables. */
@@ -10932,7 +11096,10 @@ vte_terminal_get_property (GObject *object,
g_value_set_boolean (value, vte_terminal_get_mouse_autohide (terminal));
break;
case PROP_PTY:
- g_value_set_int (value, pvt->pty_master);
+ g_value_set_int (value, pvt->pty != NULL ? vte_pty_get_fd(pvt->pty) : -1);
+ break;
+ case PROP_PTY_OBJECT:
+ g_value_set_object (value, vte_terminal_get_pty_object(terminal));
break;
case PROP_SCROLL_BACKGROUND:
g_value_set_boolean (value, pvt->scroll_background);
@@ -11024,6 +11191,9 @@ vte_terminal_set_property (GObject *object,
case PROP_PTY:
vte_terminal_set_pty (terminal, g_value_get_int (value));
break;
+ case PROP_PTY_OBJECT:
+ vte_terminal_set_pty_object (terminal, g_value_get_object (value));
+ break;
case PROP_SCROLL_BACKGROUND:
vte_terminal_set_scroll_background (terminal, g_value_get_boolean (value));
break;
@@ -11191,7 +11361,7 @@ vte_terminal_class_init(VteTerminalClass *klass)
*
* Emitted when the terminal receives an end-of-file from a child which
* is running in the terminal. This signal is frequently (but not
- * always) emitted with a "child-exited" signal.
+ * always) emitted with a #VteTerminal::child-exited signal.
*/
klass->eof_signal =
g_signal_new(I_("eof"),
@@ -11780,7 +11950,7 @@ vte_terminal_class_init(VteTerminalClass *klass)
* VteTerminal:background-saturation:
*
* If a background image has been set using #VteTerminal:background-image-file: or
- * #VteTermina:background-image-pixbuf:, or #VteTerminal:background-transparent:,
+ * #VteTerminal:background-image-pixbuf:, or #VteTerminal:background-transparent:,
* and the saturation value is less
* than 1.0, the terminal will adjust the colors of the image before drawing
* the image. To do so, the terminal will create a copy of the background
@@ -11800,7 +11970,7 @@ vte_terminal_class_init(VteTerminalClass *klass)
* VteTerminal:background-tint-color:
*
* If a background image has been set using #VteTerminal:background-image-file: or
- * #VteTermina:background-image-pixbuf:, or #VteTerminal:background-transparent:, and
+ * #VteTerminal:background-image-pixbuf:, or #VteTerminal:background-transparent:, and
* and the value set by VteTerminal:background-saturation: is less than 1.0,
* the terminal
* will adjust the color of the image before drawing the image. To do so,
@@ -11986,6 +12156,8 @@ vte_terminal_class_init(VteTerminalClass *klass)
* The file descriptor of the master end of the terminal's PTY.
*
* Since: 0.20
+ *
+ * Deprecated: 0.24: Use the #VteTerminal:pty-object property instead
*/
g_object_class_install_property
(gobject_class,
@@ -11996,6 +12168,21 @@ vte_terminal_class_init(VteTerminalClass *klass)
G_PARAM_READWRITE | STATIC_PARAMS));
/**
+ * VteTerminal:pty-object:
+ *
+ * The PTY object for the terminal.
+ *
+ * Since: 0.24
+ */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PTY_OBJECT,
+ g_param_spec_object ("pty-object", NULL, NULL,
+ VTE_TYPE_PTY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* VteTerminal:scroll-background:
*
* Controls whether or not the terminal will scroll the background image (if
@@ -13443,7 +13630,7 @@ vte_terminal_get_status_line(VteTerminal *terminal)
* size. The values returned in @xpad and @ypad are the total padding used in
* each direction, and do not need to be doubled.
*
- * @Deprecated: 0.24: Get the VteTerminal:inner-border style property instead
+ * Deprecated: 0.24: Get the #VteTerminal:inner-border style property instead
*/
void
vte_terminal_get_padding(VteTerminal *terminal, int *xpad, int *ypad)
@@ -13601,58 +13788,123 @@ vte_terminal_get_icon_title(VteTerminal *terminal)
/**
* vte_terminal_set_pty:
* @terminal: a #VteTerminal
- * @pty_master: a file descriptor of the master end of a PTY
+ * @pty_master: a file descriptor of the master end of a PTY, or %-1
*
* Attach an existing PTY master side to the terminal widget. Use
* instead of vte_terminal_fork_command() or vte_terminal_forkpty().
*
* Since: 0.12.1
+ *
+ * Deprecated: 0.24: Use vte_pty_new_foreign() and vte_terminal_set_pty_object()
*/
void
vte_terminal_set_pty(VteTerminal *terminal, int pty_master)
{
- long flags;
+ VtePty *pty;
+
+ if (pty_master == -1) {
+ vte_terminal_set_pty_object(terminal, NULL);
+ return;
+ }
+
+ pty = vte_pty_new_foreign(pty_master);
+ g_assert(pty != NULL);
+
+ vte_terminal_set_pty_object(terminal, pty);
+ g_object_unref(pty);
+}
+
+/**
+ * vte_terminal_set_pty_object:
+ * @terminal: a #VteTerminal
+ * @pty: a #VtePty, or %NULL
+ *
+ * Sets @pty as the PTY to use in @terminal.
+ * Use %NULL to unset the PTY.
+ *
+ * Since: 0.24.
+ */
+void
+vte_terminal_set_pty_object(VteTerminal *terminal,
+ VtePty *pty)
+{
VteTerminalPrivate *pvt;
GObject *object;
+ long flags;
+ int pty_master;
g_return_if_fail(VTE_IS_TERMINAL(terminal));
+ g_return_if_fail(pty == NULL || VTE_IS_PTY(pty));
- object = G_OBJECT(terminal);
pvt = terminal->pvt;
- if (pty_master == pvt->pty_master) {
+ if (pvt->pty == pty)
return;
- }
+
+ object = G_OBJECT(terminal);
g_object_freeze_notify(object);
- if (terminal->pvt->pty_channel != NULL) {
- g_io_channel_unref (terminal->pvt->pty_channel);
- }
- if (terminal->pvt->pty_master != -1) {
- _vte_pty_close (terminal->pvt->pty_master);
- close (terminal->pvt->pty_master);
- }
- terminal->pvt->pty_master = pty_master;
- terminal->pvt->pty_channel = g_io_channel_unix_new (pty_master);
- g_io_channel_set_close_on_unref (terminal->pvt->pty_channel, FALSE);
-
-
- /* Set the pty to be non-blocking. */
- flags = fcntl (terminal->pvt->pty_master, F_GETFL);
- if ((flags & O_NONBLOCK) == 0) {
- fcntl (terminal->pvt->pty_master, F_SETFL, flags | O_NONBLOCK);
- }
-
- vte_terminal_set_size (terminal,
+ if (pvt->pty != NULL) {
+ _vte_terminal_disconnect_pty_read(terminal);
+ _vte_terminal_disconnect_pty_write(terminal);
+
+ if (terminal->pvt->pty_channel != NULL) {
+ g_io_channel_unref (terminal->pvt->pty_channel);
+ pvt->pty_channel = NULL;
+ }
+
+ /* 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 (terminal->pvt->incoming != NULL) {
+ vte_terminal_process_incoming(terminal);
+ _vte_incoming_chunks_release (terminal->pvt->incoming);
+ terminal->pvt->incoming = NULL;
+ terminal->pvt->input_bytes = 0;
+ }
+ g_array_set_size(terminal->pvt->pending, 0);
+ vte_terminal_stop_processing (terminal);
+
+ /* Clear the outgoing buffer as well. */
+ _vte_buffer_clear(terminal->pvt->outgoing);
+
+ vte_pty_close(pvt->pty);
+ g_object_unref(pvt->pty);
+ pvt->pty = NULL;
+ }
+
+ if (pty == NULL) {
+ pvt->pty = NULL;
+ g_object_notify(object, "pty");
+ g_object_notify(object, "pty-object");
+ g_object_thaw_notify(object);
+ return;
+ }
+
+ pvt->pty = g_object_ref(pty);
+ pty_master = vte_pty_get_fd(pvt->pty);
+
+ pvt->pty_channel = g_io_channel_unix_new (pty_master);
+ g_io_channel_set_close_on_unref (pvt->pty_channel, FALSE);
+
+ /* FIXMEchpe: vte_pty_open_unix98 does the inverse ... */
+ /* Set the pty to be non-blocking. */
+ flags = fcntl(pty_master, F_GETFL);
+ if ((flags & O_NONBLOCK) == 0) {
+ fcntl(pty_master, F_SETFL, flags | O_NONBLOCK);
+ }
+
+ vte_terminal_set_size(terminal,
terminal->column_count,
terminal->row_count);
- _vte_terminal_setup_utf8 (terminal);
+ _vte_terminal_setup_utf8 (terminal);
- /* Open channels to listen for input on. */
- _vte_terminal_connect_pty_read (terminal);
+ /* Open channels to listen for input on. */
+ _vte_terminal_connect_pty_read (terminal);
g_object_notify(object, "pty");
+ g_object_notify(object, "pty-object");
g_object_thaw_notify(object);
}
@@ -13666,12 +13918,39 @@ vte_terminal_set_pty(VteTerminal *terminal, int pty_master)
* Return value: the file descriptor, or -1 if the terminal has no PTY.
*
* Since: 0.20
+ *
+ * Deprecated: 0.24: Use vte_terminal_get_pty_object() and vte_pty_get_fd()
+ */
+int
+vte_terminal_get_pty(VteTerminal *terminal)
+{
+ VteTerminalPrivate *pvt;
+
+ g_return_val_if_fail (VTE_IS_TERMINAL (terminal), -1);
+
+ pvt = terminal->pvt;
+ if (pvt->pty != NULL)
+ return vte_pty_get_fd(pvt->pty);
+
+ return -1;
+}
+
+/**
+ * vte_terminal_get_pty_object:
+ * @terminal: a #VteTerminal
+ *
+ * Returns the #VtePty of @terminal.
+ *
+ * Return value: a #VtePty, or %NULL
+ *
+ * Since: 0.24
*/
-int vte_terminal_get_pty(VteTerminal *terminal)
+VtePty *
+vte_terminal_get_pty_object(VteTerminal *terminal)
{
- g_return_val_if_fail (VTE_IS_TERMINAL (terminal), -1);
+ g_return_val_if_fail (VTE_IS_TERMINAL (terminal), NULL);
- return terminal->pvt->pty_master;
+ return terminal->pvt->pty;
}
/**
@@ -13683,7 +13962,7 @@ int vte_terminal_get_pty(VteTerminal *terminal)
* exit status.
*
* Note that this function may only be called from the signal handler of
- * the "child-exited" signal.
+ * the #VteTerminal::child-exited signal.
*
* Returns: the child's exit status
*
diff --git a/src/vte.h b/src/vte.h
index 8cd4372..53fb2d8 100644
--- a/src/vte.h
+++ b/src/vte.h
@@ -28,6 +28,7 @@
#define __VTE_VTE_H_INSIDE__ 1
+#include "vtepty.h"
#include "vtetypebuiltins.h"
#include "vteversion.h"
@@ -264,21 +265,39 @@ GType vte_terminal_get_type(void);
VTE_TYPE_TERMINAL)
#define VTE_TERMINAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_TERMINAL, VteTerminalClass))
-/* You can get by with just these two functions. */
GtkWidget *vte_terminal_new(void);
+
+VtePty *vte_terminal_pty_new (VteTerminal *terminal,
+ VtePtyFlags flags,
+ GError **error);
+
+void vte_terminal_watch_child (VteTerminal *terminal,
+ GPid child_pid);
+
+#ifndef VTE_DISABLE_DEPRECATED
pid_t vte_terminal_fork_command(VteTerminal *terminal,
const char *command, char **argv,
- char **envv, const char *directory,
+ char **envv, const char *working_directory,
gboolean lastlog,
gboolean utmp,
- gboolean wtmp);
-
-/* Users of libzvt may find this useful. */
+ gboolean wtmp) G_GNUC_DEPRECATED;
pid_t vte_terminal_forkpty(VteTerminal *terminal,
- char **envv, const char *directory,
+ char **envv, const char *working_directory,
gboolean lastlog,
gboolean utmp,
- gboolean wtmp);
+ gboolean wtmp) G_GNUC_DEPRECATED;
+#endif /* VTE_DISABLE_DEPRECATED */
+
+gboolean vte_terminal_fork_command_full(VteTerminal *terminal,
+ 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 */,
+ GError **error);
/* Send data to the terminal to display, or to the terminal's forked command
* to handle in some way. If it's 'cat', they should be the same. */
@@ -453,10 +472,13 @@ const char *vte_terminal_get_status_line(VteTerminal *terminal);
void vte_terminal_get_padding(VteTerminal *terminal, int *xpad, int *ypad) G_GNUC_DEPRECATED;
#endif
-/* Attach an existing PTY master side to the terminal widget. Use
- * instead of vte_terminal_fork_command(). */
+#ifndef VTE_DISABLE_DEPRECATED
void vte_terminal_set_pty(VteTerminal *terminal, int pty_master);
int vte_terminal_get_pty(VteTerminal *terminal);
+#endif
+
+void vte_terminal_set_pty_object(VteTerminal *terminal, VtePty *pty);
+VtePty *vte_terminal_get_pty_object(VteTerminal *terminal);
/* Accessors for bindings. */
GtkAdjustment *vte_terminal_get_adjustment(VteTerminal *terminal);
diff --git a/src/vteapp.c b/src/vteapp.c
index 26955b5..5c7375c 100644
--- a/src/vteapp.c
+++ b/src/vteapp.c
@@ -881,13 +881,35 @@ main(int argc, char **argv)
if (!console) {
if (shell) {
- /* Launch a shell. */
+ GError *err = NULL;
+ char **command_argv = NULL;
+ int command_argc;
+ GPid pid = -1;
+
_VTE_DEBUG_IF(VTE_DEBUG_MISC)
vte_terminal_feed(terminal, message, -1);
- vte_terminal_fork_command(terminal,
- command, NULL, env_add,
- working_directory,
- TRUE, TRUE, TRUE);
+
+ if (command == NULL)
+ command = "/bin/sh"; // FIXMEchpe
+
+ if (command != NULL) {
+ if (!g_shell_parse_argv(command, &command_argc, &command_argv, &err) ||
+ !vte_terminal_fork_command_full(terminal,
+ VTE_PTY_DEFAULT,
+ NULL,
+ command_argv,
+ env_add,
+ 0,
+ NULL, NULL,
+ &pid,
+ &err)) {
+ g_warning("Failed to fork: %s\n", err->message);
+ g_error_free(err);
+ } else {
+ g_print("Fork succeeded, PID %d\n", pid);
+ }
+ }
+ g_strfreev(command_argv);
#ifdef VTE_DEBUG
if (command == NULL) {
vte_terminal_feed_child(terminal,
diff --git a/src/vtepty-private.h b/src/vtepty-private.h
new file mode 100644
index 0000000..70347ee
--- /dev/null
+++ b/src/vtepty-private.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2009, 2010 Christian Persch
+ *
+ * This 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 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+G_BEGIN_DECLS
+
+VtePtyFlags __vte_pty_get_pty_flags(gboolean lastlog,
+ gboolean utmp,
+ gboolean wtmp);
+
+char **__vte_pty_get_argv (const char *command,
+ char **argv,
+ GSpawnFlags *flags /* inout */);
+
+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 */,
+ GError **error);
+
+gboolean __vte_pty_fork(VtePty *pty,
+ GPid *pid,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/vtepty.h b/src/vtepty.h
new file mode 100644
index 0000000..a705f94
--- /dev/null
+++ b/src/vtepty.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2009, 2010 Christian Persch
+ *
+ * This 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 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined (__VTE_VTE_H_INSIDE__) && !defined (VTE_COMPILATION)
+#error "Only <vte/vte.h> can be included directly."
+#endif
+
+#ifndef VTE_PTY_H
+#define VTE_PTY_H
+
+#include <glib-object.h>
+
+#include <sys/types.h> /* for pid_t */
+
+G_BEGIN_DECLS
+
+/**
+ * VtePtyFlags:
+ * @VTE_PTY_NO_LASTLOG: don't record the session in lastlog
+ * @VTE_PTY_NO_UTMP: don't record the session in utmp
+ * @VTE_PTY_NO_WTMP: don't record the session in wtmp
+ * @VTE_PTY_NO_HELPER: don't use the GNOME PTY helper to allocate the PTY
+ * @VTE_PTY_NO_FALLBACK: when allocating the PTY with the PTY helper fails,
+ * don't fall back to try using PTY98
+ * @VTE_PTY_DEFAULT: the default flags
+ *
+ * Since: 0.24
+ */
+typedef enum {
+ VTE_PTY_NO_LASTLOG = 1 << 0,
+ VTE_PTY_NO_UTMP = 1 << 1,
+ VTE_PTY_NO_WTMP = 1 << 2,
+ VTE_PTY_NO_HELPER = 1 << 3,
+ VTE_PTY_NO_FALLBACK = 1 << 4,
+ VTE_PTY_DEFAULT = 0
+} VtePtyFlags;
+
+/**
+ * VtePtyError:
+ * @VTE_PTY_ERROR_PTY_HELPER_FAILED: failure when using the GNOME PTY helper to
+ * allocate the PTY
+ * @VTE_PTY_ERROR_PTY98_FAILED: failure when using PTY98 to allocate the PTY
+ *
+ * Since: 0.24
+ */
+typedef enum {
+ VTE_PTY_ERROR_PTY_HELPER_FAILED = 0,
+ VTE_PTY_ERROR_PTY98_FAILED
+} VtePtyError;
+
+GQuark vte_pty_error_quark (void);
+
+/**
+ * VTE_PTY_ERROR:
+ *
+ * Error domain for VTE PTY errors. Errors in this domain will be from the #VtePtyError
+ * enumeration. See #GError for more information on error domains.
+ *
+ * Since: 0.24
+ */
+#define VTE_PTY_ERROR (vte_pty_error_quark ())
+
+/* VTE PTY object */
+
+#define VTE_TYPE_PTY (vte_pty_get_type())
+#define VTE_PTY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VTE_TYPE_PTY, VtePty))
+#define VTE_PTY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VTE_TYPE_PTY, VtePtyClass))
+#define VTE_IS_PTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VTE_TYPE_PTY))
+#define VTE_IS_PTY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VTE_TYPE_PTY))
+#define VTE_PTY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_PTY, VtePtyClass))
+
+typedef struct _VtePty VtePty;
+typedef struct _VtePtyClass VtePtyClass;
+
+GType vte_pty_get_type (void);
+
+VtePty *vte_pty_new (VtePtyFlags flags,
+ GError **error);
+
+VtePty *vte_pty_new_foreign (int fd);
+
+int vte_pty_get_fd (VtePty *pty);
+
+void vte_pty_close (VtePty *pty);
+
+void vte_pty_child_setup (VtePty *pty);
+
+gboolean vte_pty_get_size (VtePty *pty,
+ int *rows,
+ int *columns,
+ GError **error);
+
+gboolean vte_pty_set_size (VtePty *pty,
+ int rows,
+ int columns,
+ GError **error);
+
+void vte_pty_set_utf8 (VtePty *pty,
+ gboolean utf8);
+
+void vte_pty_set_term (VtePty *pty,
+ const char *emulation);
+
+G_END_DECLS
+
+#endif /* VTE_PTY_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]