Check for pangoxft with Xft1 or Xft2, hopefully getting things right for vte_0_3_26
authorNalin Dahyabhai <nalin@src.gnome.org>
Fri, 7 Jun 2002 16:58:50 +0000 (16:58 +0000)
committerNalin Dahyabhai <nalin@src.gnome.org>
Fri, 7 Jun 2002 16:58:50 +0000 (16:58 +0000)
* configure.in: Check for pangoxft with Xft1 or Xft2, hopefully getting
things right for either case.  Patches by andersca and otaylor.
* src/reaper.c, src/reaper.h: Add a singleton object to watch for
SIGCHLD when child processes quit.
* src/vte.c, src/vte.h: Add get_cursor_position(). Remove get_snapshot()
and free_shapshot(). Emit a "child-exited" signal when a child started
with fork_command() exits.  Fix a logic bug that caused us to scroll
to the bottom even if the user just pressed and released a modifier
key.  Fix saving of the font in set_font() in most cases, where we
weren't saving the new font before.  Scroll-to-bottom on input method
commits, which are also the result of keystrokes, when
scroll-on-keystroke is enabled.
* src/vte.c: Use FcNameUnparse() instead of XftNameUnparse in Xft2.  Patch from
otaylor.
* src/vte.defs: Add.  Not very useful yet.
* src/vteaccess.c: Rework to use get_text() and get_cursor_position()
instead of get_snapshot(), so that selection and accessibility both
agree on what's visible (or "visible").

13 files changed:
ChangeLog
README
configure.in
src/Makefile.am
src/reaper.c [new file with mode: 0644]
src/reaper.h [new file with mode: 0644]
src/termcap.c
src/vte.c
src/vte.defs [new file with mode: 0644]
src/vte.h
src/vteaccess.c
src/vteapp.c
vte.spec

index 595bc2d..cf49345 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2002-06-06 nalin
+       * src/vte.c, src/vte.h: Add get_cursor_position(). Remove get_snapshot()
+       and free_shapshot().
+       * src/vte.c: Fix a logic bug that caused us to scroll to the bottom
+       even if the user just pressed and released a modifier key.  Fix saving
+       of the font in set_font() in most cases, where we weren't saving the
+       new font before.  Scroll-to-bottom on input method commits, which are
+       also the result of keystrokes, when scroll-on-keystroke is enabled.
+       * src/vteaccess.c: Rework to use get_text() and get_cursor_position()
+       instead of get_snapshot(), so that selection and accessibility both
+       agree on what's visible (or "visible").
+2002-06-05 nalin
+       * configure.in: Check for pangoxft with Xft1 or Xft2, hopefully getting
+       things right for either case.  Originally from patch by andersca.
+       * src/reaper.c, src/reaper.h: Add a singleton object to watch for
+       SIGCHLD when child processes quit.
+       * src/vte.c: Emit a "child-exited" signal when a child started with
+       fork_command() exits.
+       * src/vte.c: Make a better guess at font metrics when using Xft by
+       measuring the extents for a string of representative characters.
+       * src/vte.defs: Add.  Not very useful yet.
 2002-05-31 nalin
        * src/vte.c: Implement font setting using PangoX to convert Pango
        font descriptions to core font xlfds for use when drawing with Xlib.
diff --git a/README b/README
index af4eff6..23f99fe 100644 (file)
--- a/README
+++ b/README
   implemented (ff, fs, i1, i3, is, iP, LF, LO, MC, mh, ML, mm, mo, nw, pf,
   pk, pl, pf, po, pO, ps, px, r1, r2, r3, RA, RF, rp, rs, RX, SA, SX, wi,
   several more from the XTerm set).
-- Currently doesn't handle children exiting quite right (if the child spawned
-  a background process which keeps its stdio open, we don't close because we
-  don't get an EOF).
 - Sequence matching is greedy, so that C=AB matches C and not A and then B.
 - Bold doesn't work right if the default foreground color isn't gray.  Need
   to move to 20-color palette to fix this right.
-- Matching doesn't always work right.
+- Matching doesn't always work right:  if two cells which are adjacent
+  (vertically) would both be hilited due to matching, the hilite doesn't
+  get cleared properly when the pointer moves.
 - I'm not sure the widget implementation itself is correct.  There are many
   changes in going from GTK+ 1.2 to 2.0, and examples of the proper way to do
   things is currently scarce, so some of it's guesswork.
index 4cb0ad1..d65d79a 100644 (file)
@@ -14,25 +14,37 @@ AC_EGREP_CPP(glibc,
 AC_PATH_XTRA
 PKG_CHECK_MODULES(GLIB,[glib-2.0 gobject-2.0])
 PKG_CHECK_MODULES(GTK, [glib-2.0 gobject-2.0 gdk-pixbuf-2.0 gtk+-2.0 pangox])
+if pkg-config --exists pangoxft '>=' 1.1.0 ; then
+       havexft=1
+       AC_DEFINE(HAVE_XFT2,1,[Whether we have Xft version 2])
+       PKG_CHECK_MODULES(XFT,[xft])
+else
+       savelibs="$LIBS"
+       LIBS="$GTK_LIBS"
+       AC_CHECK_FUNC(XftDrawString32,
+                     [AC_CHECK_HEADER(X11/Xft/Xft.h, havexft=1)])
+       LIBS="$savelibs"
+fi
 AC_DEFINE(G_DISABLE_DEPRECATED,1,[Disable deprecated glib features.])
 AC_DEFINE(GDK_DISABLE_DEPRECATED,1,[Disable deprecated gdk features.])
 AC_DEFINE(GDK_PIXBUF_DISABLE_DEPRECATED,1,[Disable deprecated gdk-pixbuf features.])
 AC_DEFINE(GTK_DISABLE_DEPRECATED,1,[Disable deprecated gtk features.])
+AC_DEFINE(PANGO_DISABLE_DEPRECATED,1,[Disable deprecated pango features.])
 AC_DEFINE(VTE_UTF8_BPC,6,[Maximum number of bytes used per UTF-8 character.])
 AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE",[Package name.])
 AC_CHECK_FUNCS(getpt grantpt unlockpt ptsname ptsname_r)
 
+# Double-check that the GTK libraries pulled in Xft functionality, then use
+# the GTK CFLAGS to find the Xft headers.
 savelibs="$LIBS"
 LIBS="$GTK_LIBS"
 havexft=0
 AC_CHECK_FUNC(XftDrawString32,havexft=1)
 LIBS="$savelibs"
 if test x$havexft = x1 ; then
-       AC_CHECK_HEADER(X11/Xft/Xft.h,
-                       [AC_DEFINE(HAVE_XFT,
-                                  1,
-                                  [Whether not Xft is available.])])
+       AC_DEFINE(HAVE_XFT, 1, [Whether not Xft is available.])
 fi
+
 AM_MAINTAINER_MODE
 
 if test x$USE_MAINTAINER_MODE != x ; then
@@ -51,7 +63,7 @@ if test x$USE_MAINTAINER_MODE != x ; then
        fi
 fi
 if test x$VTE_DEBUG = x1 ; then
-       AC_DEFINE(VTE_DEBUG,,[Enable debugging of unrecognized sequences.])
+       AC_DEFINE(VTE_DEBUG,,[Enable debugging messages.])
 fi
 
 mydatadir=`eval echo $datadir`
index 9661cd1..846b6f5 100644 (file)
@@ -1,11 +1,11 @@
 bin_PROGRAMS = vte
 pkginclude_HEADERS = caps.h pty.h ring.h termcap.h trie.h vte.h vteaccess.h
 lib_LTLIBRARIES = libvte.la
-noinst_PROGRAMS = interpret utf8echo utf8mode iso8859mode
+noinst_PROGRAMS = interpret utf8echo utf8mode iso8859mode reaper
 EXTRA_PROGRAMS = pty ring termcap trie
-EXTRA_DIST = marshal.list
+EXTRA_DIST = marshal.list vte.defs
 
-CFLAGS = @CFLAGS@ @X_CFLAGS@ @GTK_CFLAGS@
+CFLAGS = @CFLAGS@ @XFT_CFLAGS@ @GTK_CFLAGS@ @X_CFLAGS@
 
 libvte_la_SOURCES = \
        caps.c \
@@ -16,6 +16,8 @@ libvte_la_SOURCES = \
        marshal.h \
        pty.c \
        pty.h \
+       reaper.c \
+       reaper.h \
        ring.c \
        ring.h \
        termcap.c \
@@ -35,7 +37,7 @@ marshal.c marshal.h: marshal.list
 
 vte_SOURCES = \
        vteapp.c
-vte_LDADD = libvte.la @GTK_LIBS@ @X_LIBS@
+vte_LDADD = libvte.la @LIBS@ @XFT_LIBS@ @GTK_LIBS@ @X_LIBS@
 
 interpret_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DINTERPRET_MAIN
 interpret_SOURCES = \
@@ -52,7 +54,7 @@ interpret_LDADD = @GLIB_LIBS@
 
 utf8echo_SOURCES = \
        utf8echo.c
-utf8echo_LDADD = @GLIB_LIBS@
+utf8echo_LDADD = @LIBS@ @GLIB_LIBS@
 
 ring_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DRING_MAIN
 ring_SOURCES = \
@@ -60,7 +62,7 @@ ring_SOURCES = \
        debug.h \
        ring.c \
        ring.h
-ring_LDADD = @GLIB_LIBS@
+ring_LDADD = @LIBS@ @GLIB_LIBS@
 
 trie_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DTRIE_MAIN
 trie_SOURCES = \
@@ -70,7 +72,7 @@ trie_SOURCES = \
        termcap.h \
        trie.c \
        trie.h
-trie_LDADD = @GLIB_LIBS@
+trie_LDADD = @LIBS@ @GLIB_LIBS@
 
 termcap_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DTERMCAP_MAIN
 termcap_SOURCES = \
@@ -78,7 +80,7 @@ termcap_SOURCES = \
        debug.h \
        termcap.c \
        termcap.h
-termcap_LDADD = @GLIB_LIBS@
+termcap_LDADD = @LIBS@ @GLIB_LIBS@
 
 pty_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DPTY_MAIN
 pty_SOURCES = \
@@ -86,4 +88,12 @@ pty_SOURCES = \
        debug.h \
        pty.c \
        pty.h
-pty_LDADD = @GLIB_LIBS@
+pty_LDADD = @LIBS@ @GLIB_LIBS@
+
+reaper_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DREAPER_MAIN
+reaper_SOURCES = \
+       marshal.c \
+       marshal.h \
+       reaper.c \
+       reaper.h
+reaper_LDADD = @LIBS@ @GLIB_LIBS@
diff --git a/src/reaper.c b/src/reaper.c
new file mode 100644 (file)
index 0000000..dbde87d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ident "$Id$"
+#include "../config.h"
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "debug.h"
+#include "marshal.h"
+#include "reaper.h"
+
+static VteReaper *singleton_reaper = NULL;
+struct reaper_info {
+       int signum;
+       pid_t pid;
+       guint status;
+};
+
+static void
+vte_reaper_signal_handler(int signum)
+{
+       struct reaper_info info;
+       int status;
+
+       /* This might become more general-purpose in the future, but for now
+        * just forget about signals other than SIGCHLD. */
+       info.signum = signum;
+       if (signum != SIGCHLD) {
+               return;
+       }
+
+       if ((singleton_reaper != NULL) && (singleton_reaper->iopipe[0] != -1)) {
+               info.pid = waitpid(-1, &status, WNOHANG);
+               if ((info.pid != -1) && WIFEXITED(status)) {
+                       info.status = WEXITSTATUS(status);
+                       if (write(singleton_reaper->iopipe[1], "", 0) == 0) {
+                               write(singleton_reaper->iopipe[1],
+                                     &info, sizeof(info));
+                       }
+               }
+       }
+}
+
+static gboolean
+vte_reaper_emit_signal(GIOChannel *channel, GIOCondition condition,
+                      gpointer data)
+{
+       struct reaper_info info;
+       if (condition != G_IO_IN) {
+               return FALSE;
+       }
+       g_assert(data == singleton_reaper);
+       read(singleton_reaper->iopipe[0], &info, sizeof(info));
+       if (info.signum == SIGCHLD) {
+               g_signal_emit_by_name(data, "child-exited",
+                                     info.pid, info.status);
+       }
+       return TRUE;
+}
+
+static void
+vte_reaper_channel_destroyed(gpointer data)
+{
+       g_assert_not_reached();
+}
+
+static void
+vte_reaper_init(VteReaper *reaper, gpointer *klass)
+{
+       struct sigaction action, old_action;
+       int ret;
+       ret = pipe(reaper->iopipe);
+       if (ret == -1) {
+               g_error("Error creating signal pipe.");
+       }
+       action.sa_handler = vte_reaper_signal_handler;
+       sigemptyset(&action.sa_mask);
+       action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+       sigaction(SIGCHLD, &action, &old_action);
+       reaper->channel = g_io_channel_unix_new(reaper->iopipe[0]);
+       g_io_add_watch_full(reaper->channel,
+                           G_PRIORITY_HIGH,
+                           G_IO_IN,
+                           vte_reaper_emit_signal,
+                           reaper,
+                           vte_reaper_channel_destroyed);
+}
+
+static void
+vte_reaper_class_init(VteReaperClass *klass, gpointer data)
+{
+       klass->child_exited_signal = g_signal_new("child-exited",
+                                                 G_OBJECT_CLASS_TYPE(klass),
+                                                 G_SIGNAL_RUN_LAST,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 _vte_marshal_VOID__UINT_UINT,
+                                                 G_TYPE_NONE,
+                                                 2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+GType
+vte_reaper_get_type(void)
+{
+       static GType reaper_type = 0;
+       static GTypeInfo reaper_type_info = {
+               sizeof(VteReaperClass),
+
+               (GBaseInitFunc)NULL,
+               (GBaseFinalizeFunc)NULL,
+
+               (GClassInitFunc)vte_reaper_class_init,
+               (GClassFinalizeFunc)NULL,
+               NULL,
+
+               sizeof(VteReaper),
+               0,
+               (GInstanceInitFunc) vte_reaper_init,
+
+               (const GTypeValueTable *) NULL,
+       };
+       if (reaper_type == 0) {
+               reaper_type = g_type_register_static(G_TYPE_OBJECT,
+                                                    "VteReaper",
+                                                    &reaper_type_info,
+                                                    0);
+       }
+       return reaper_type;
+}
+
+VteReaper *
+vte_reaper_get(void)
+{
+       if (!VTE_IS_REAPER(singleton_reaper)) {
+               singleton_reaper = g_object_new(VTE_TYPE_REAPER, NULL);
+       }
+       return singleton_reaper;
+}
+
+#ifdef REAPER_MAIN
+GMainContext *context;
+GMainLoop *loop;
+pid_t child;
+
+static void
+child_exited(GObject *object, guint pid, guint status, gpointer data)
+{
+       g_print("[parent] Child with pid %d exited with code %d, "
+               "was waiting for %d.\n", pid, status, GPOINTER_TO_INT(data));
+       if (child == pid) {
+               g_print("[parent] Quitting.\n");
+               g_main_loop_quit(loop);
+       }
+}
+
+int
+main(int argc, char **argv)
+{
+       VteReaper *reaper;
+       pid_t p, q;
+
+       g_type_init();
+       context = g_main_context_default();
+       loop = g_main_loop_new(context, FALSE);
+       reaper = vte_reaper_get();
+
+       g_print("[parent] Forking.\n");
+       p = fork();
+       switch (p) {
+               case -1:
+                       g_print("[parent] Fork failed.\n");
+                       g_assert_not_reached();
+                       break;
+               case 0:
+                       g_print("[child]  Going to sleep.\n");
+                       sleep(10);
+                       g_print("[child]  Quitting.\n");
+                       _exit(30);
+                       break;
+               default:
+                       g_print("[parent] Starting to wait for %d.\n", p);
+                       child = p;
+                       g_signal_connect(G_OBJECT(reaper),
+                                        "child-exited",
+                                        G_CALLBACK(child_exited),
+                                        GINT_TO_POINTER(child));
+                       break;
+       }
+
+       g_print("[parent] Forking.\n");
+       q = fork();
+       switch (q) {
+               case -1:
+                       g_print("[parent] Fork failed.\n");
+                       g_assert_not_reached();
+                       break;
+               case 0:
+                       g_print("[child]  Going to sleep.\n");
+                       sleep(5);
+                       _exit(5);
+                       break;
+               default:
+                       g_print("[parent] Not waiting for %d.\n", q);
+                       break;
+       }
+
+
+       g_main_loop_run(loop);
+
+       return 0;
+}
+#endif
diff --git a/src/reaper.h b/src/reaper.h
new file mode 100644 (file)
index 0000000..6a63da6
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef vte_reaper_h_included
+#define vte_reaper_h_included
+
+G_BEGIN_DECLS
+
+#ident "$Id$"
+#include "../config.h"
+#include <sys/wait.h>
+#include <signal.h>
+#include <glib.h>
+#include <glib-object.h>
+
+struct _VteReaper {
+       GObject object;
+       GIOChannel *channel;
+       int iopipe[2];
+};
+typedef struct _VteReaper VteReaper;
+
+struct _VteReaperClass {
+       GObjectClass parent_class;
+       guint child_exited_signal;
+};
+typedef struct _VteReaperClass VteReaperClass;
+
+GType vte_reaper_get_type(void);
+
+#define VTE_TYPE_REAPER                        (vte_reaper_get_type())
+#define VTE_REAPER(obj)                        (GTK_CHECK_CAST((obj), \
+                                                       VTE_TYPE_REAPER, \
+                                                       VteReaper))
+#define VTE_REAPER_CLASS(klass)                GTK_CHECK_CLASS_CAST((klass), \
+                                                            VTE_TYPE_REAPER, \
+                                                            VteReaperClass)
+#define VTE_IS_REAPER(obj)             GTK_CHECK_TYPE((obj), VTE_TYPE_REAPER)
+#define VTE_IS_REAPER_CLASS(klass)     GTK_CHECK_CLASS_TYPE((klass), \
+                                                            VTE_TYPE_REAPER)
+#define VTE_REAPER_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS((obj), \
+                                                                  VTE_TYPE_REAPER, \
+                                                                  VteReaperClass))
+
+VteReaper *vte_reaper_get(void);
+
+G_END_DECLS
+
+#endif
index 4c8f317..beb2e0d 100644 (file)
@@ -386,16 +386,21 @@ vte_termcap_free(struct vte_termcap *termcap)
        for (entry = termcap->entries; entry != NULL; entry = nextentry) {
                nextentry = entry->next;
                g_free(entry->comment);
+               entry->comment = NULL;
                g_free(entry->string);
+               entry->string = NULL;
                g_free(entry);
        }
        for (alias = termcap->names; alias != NULL; alias = nextalias) {
                nextalias = alias->next;
                g_free(alias->name);
+               alias->name = NULL;
                g_free(alias);
        }
        g_tree_destroy(termcap->nametree);
+       termcap->nametree = NULL;
        g_free(termcap->comment);
+       termcap->comment = NULL;
        g_free(termcap);
 }
 
index 28e90b1..fa2dd7a 100644 (file)
--- a/src/vte.c
+++ b/src/vte.c
 #include "debug.h"
 #include "marshal.h"
 #include "pty.h"
-#include "termcap.h"
+#include "reaper.h"
 #include "ring.h"
+#include "termcap.h"
 #include "trie.h"
 #include "vte.h"
 #include "vteaccess.h"
 #include <X11/Xlib.h>
 #ifdef HAVE_XFT
-#include <X11/extensions/Xrender.h>
 #include <X11/Xft/Xft.h>
 #endif
 
-#define VTE_TAB_WIDTH  8
-#define VTE_LINE_WIDTH 1
-#define VTE_DEF_FG     16
-#define VTE_DEF_BG     17
-#define VTE_SATURATION_MAX 10000
-#define VTE_SCROLLBACK_MIN 100
-#define VTE_DEFAULT_EMULATION "xterm"
-#define VTE_DEFAULT_CURSOR GDK_XTERM
-#define VTE_MOUSING_CURSOR GDK_LEFT_PTR
-#define VTE_TAB_MAX    999
+#define VTE_TAB_WIDTH                  8
+#define VTE_LINE_WIDTH                 1
+#define VTE_COLOR_SET_SIZE             8
+#define VTE_COLOR_PLAIN_OFFSET         0
+#define VTE_COLOR_BRIGHT_OFFSET                8
+#define VTE_DEF_FG                     16
+#define VTE_DEF_BG                     (VTE_DEF_FG + 1)
+#define VTE_BOLD_FG                    (VTE_DEF_BG + 1)
+#define VTE_SATURATION_MAX             10000
+#define VTE_SCROLLBACK_MIN             100
+#define VTE_DEFAULT_EMULATION          "xterm"
+#define VTE_DEFAULT_CURSOR             GDK_XTERM
+#define VTE_MOUSING_CURSOR             GDK_LEFT_PTR
+#define VTE_TAB_MAX                    999
 #define VTE_REPRESENTATIVE_CHARACTERS  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                                        "abcdefgjijklmnopqrstuvwxyz" \
                                        "0123456789./+"
+#define VTE_DEFAULT_FONT               "Luxi Mono 12"
 
 /* The structure we use to hold characters we're supposed to display -- this
  * includes any supported visible attributes. */
@@ -132,9 +137,7 @@ struct _VteTerminalPrivate {
        const char *shell;              /* shell we started */
        int pty_master;                 /* pty master descriptor */
        GIOChannel *pty_input;          /* master input watch */
-       int pty_input_tag;
        GIOChannel *pty_output;         /* master output watch */
-       int pty_output_tag;
        pid_t pty_pid;                  /* pid of child using pty slave */
        const char *encoding;           /* the pty's encoding */
        const char *gxencoding[4];      /* alternate encodings */
@@ -160,7 +163,7 @@ struct _VteTerminalPrivate {
                XRenderColor rcolor;
                XftColor ftcolor;
 #endif
-       } palette[18];
+       } palette[VTE_BOLD_FG + 1];
        XFontSet fontset;
 #ifdef HAVE_XFT
        XftFont *ftfont;
@@ -512,6 +515,13 @@ vte_terminal_emit_selection_changed(VteTerminal *terminal)
        g_signal_emit_by_name(terminal, "selection-changed");
 }
 
+/* Emit a "child-exited" signal. */
+static void
+vte_terminal_emit_child_exited(VteTerminal *terminal)
+{
+       g_signal_emit_by_name(terminal, "child-exited");
+}
+
 /* Emit a "contents_changed" signal. */
 static void
 vte_terminal_emit_contents_changed(VteTerminal *terminal)
@@ -1004,10 +1014,9 @@ vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
        destination = floor(gtk_adjustment_get_value(terminal->adjustment));
        destination += (pages * terminal->row_count);
        /* Can't scroll past data we have. */
-       destination = MIN(destination,
-                         terminal->adjustment->upper - terminal->row_count);
-       /* Can't scroll up past zero. */
-       destination = MAX(terminal->adjustment->lower, destination);
+       destination = CLAMP(destination,
+                           terminal->adjustment->lower,
+                           terminal->adjustment->upper - terminal->row_count);
        /* Tell the scrollbar to adjust itself. */
        gtk_adjustment_set_value(terminal->adjustment, destination);
        gtk_adjustment_changed(terminal->adjustment);
@@ -1479,10 +1488,8 @@ vte_sequence_handler_cm(VteTerminal *terminal,
                    G_VALUE_HOLDS_LONG(col)) {
                        rowval = g_value_get_long(row);
                        colval = g_value_get_long(col);
-                       rowval = MAX(0, rowval);
-                       rowval = MIN(rowval, terminal->row_count - 1);
-                       colval = MAX(0, colval);
-                       colval = MIN(colval, terminal->column_count - 1);
+                       rowval = CLAMP(rowval, 0, terminal->row_count - 1);
+                       colval = CLAMP(colval, 0, terminal->column_count - 1);
                        screen->cursor_current.row = rowval +
                                                     screen->insert_delta;
                        screen->cursor_current.col = colval;
@@ -2665,7 +2672,7 @@ vte_sequence_handler_vb(VteTerminal *terminal,
                gc = XCreateGC(display, drawable, 0, NULL);
 
                XSetForeground(display, gc,
-                              terminal->pvt->palette[VTE_DEF_FG].pixel);
+                              terminal->pvt->palette[VTE_BOLD_FG].pixel);
                XFillRectangle(display, drawable, gc,
                               x_offs, y_offs,
                               terminal->column_count * terminal->char_width,
@@ -4495,19 +4502,21 @@ vte_terminal_set_colors(VteTerminal *terminal,
                        size_t palette_size)
 {
        int i;
-       GdkColor color;
+       GdkColor color, proposed;
        GtkWidget *widget;
        Display *display;
        GdkColormap *gcolormap;
        Colormap colormap;
        GdkVisual *gvisual;
        Visual *visual;
-       const GdkColor *proposed;
        int bright;
 
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
-       g_return_if_fail(palette_size >= 8);
-       g_return_if_fail((palette_size == 8) || (palette_size == 16));
+       g_return_if_fail((palette_size == 8)  ||
+                        (palette_size == 16) ||
+                        (palette_size == 24));
+
+       /* Accept NULL as the default foreground and background colors. */
        if (foreground == NULL) {
                foreground = &palette[7];
        }
@@ -4517,54 +4526,51 @@ vte_terminal_set_colors(VteTerminal *terminal,
 
        memset(&color, 0, sizeof(color));
 
-       widget = NULL;
-       display = NULL;
-       gcolormap = NULL;
-       colormap = 0;
-       gvisual = NULL;
-       visual = NULL;
+       /* Get X11 attributes used by GDK for the widget. */
+       widget = GTK_WIDGET(terminal);
+       display = GDK_DISPLAY();
+       gcolormap = gtk_widget_get_colormap(widget);
+       colormap = gdk_x11_colormap_get_xcolormap(gcolormap);
+       gvisual = gtk_widget_get_visual(widget);
+       visual = gdk_x11_visual_get_xvisual(gvisual);
 
        /* Initialize each item in the palette. */
        for (i = 0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
+               /* Default foreground and background. */
                if (i == VTE_DEF_FG) {
-                       proposed = foreground;
+                       proposed = *foreground;
                } else
                if (i == VTE_DEF_BG) {
-                       proposed = background;
+                       proposed = *background;
+               } else
+               /* Use a supplied color. */
+               if (i < palette_size) {
+                       proposed = palette[i];
                } else {
-                       proposed = &palette[i % palette_size];
-               }
-
-               /* Get X11 attributes used by GDK for the widget. */
-               if (widget == NULL) {
-                       widget = GTK_WIDGET(terminal);
-                       display = GDK_DISPLAY();
-                       gcolormap = gtk_widget_get_colormap(widget);
-                       colormap = gdk_x11_colormap_get_xcolormap(gcolormap);
-                       gvisual = gtk_widget_get_visual(widget);
-                       visual = gdk_x11_visual_get_xvisual(gvisual);
-               }
-
-               /* We need a temporary space to hold the pixel value. */
-               color = *proposed;
-
-               /* If we're guessing about the second half, check how much
-                * brighter we could make this entry. */
-               if ((i != VTE_DEF_FG) &&
-                   (i != VTE_DEF_BG) &&
-                   (i >= palette_size)) {
-                       bright = 0xffff;
-                       bright = MIN(bright, 0xffff - color.red);
-                       bright = MIN(bright, 0xffff - color.green);
-                       bright = MIN(bright, 0xffff - color.blue);
-                       bright = MIN(bright, 0xc000);
-                       color.red += bright;
-                       color.green += bright;
-                       color.blue += bright;
-               }
-
-               /* Get an Xlib color. */
-               gdk_rgb_find_color(gcolormap, &color); /* fill in pixel */
+                       /* We have to guess at the rest, the bolder
+                        * colors, where "bold" means "less like the
+                        * default background color". */
+                       if (i == VTE_BOLD_FG) {
+                               color = *foreground;
+                       } else {
+                               color = palette[i % VTE_COLOR_SET_SIZE];
+                       }
+                       bright = 0;
+                       bright = MAX(bright, 0xffff - color.red);
+                       bright = MAX(bright, 0xffff - color.green);
+                       bright = MAX(bright, 0xffff - color.blue);
+                       bright = MIN(bright, 0x6000);
+                       proposed.red = CLAMP(color.red + bright, 0, 0xffff);
+                       proposed.green = CLAMP(color.green + bright, 0, 0xffff);
+                       proposed.blue = CLAMP(color.blue + bright, 0, 0xffff);
+               }
+
+               /* Create a working copy of what we want, which GDK will
+                * adjust below when it fills in the pixel value. */
+               color = proposed;
+
+               /* Get a GDK color. */
+               gdk_rgb_find_color(gcolormap, &color); /* fills in pixel */
                terminal->pvt->palette[i].red = color.red;
                terminal->pvt->palette[i].green = color.green;
                terminal->pvt->palette[i].blue = color.blue;
@@ -4572,27 +4578,31 @@ vte_terminal_set_colors(VteTerminal *terminal,
 
 #ifdef HAVE_XFT
                if (terminal->pvt->use_xft) {
-                       /* Get an Xft color. */
-                       terminal->pvt->palette[i].rcolor.red = color.red;
-                       terminal->pvt->palette[i].rcolor.green = color.green;
-                       terminal->pvt->palette[i].rcolor.blue = color.blue;
-                       terminal->pvt->palette[i].rcolor.alpha = 0xffff;
+                       XRenderColor *rcolor;
+                       XftColor *ftcolor;
+
+                       rcolor = &terminal->pvt->palette[i].rcolor;
+                       ftcolor = &terminal->pvt->palette[i].ftcolor;
+
+                       /* Fill the render color in with what we got from GDK,
+                        * hopefully so that they match. */
+                       rcolor->red = color.red;
+                       rcolor->green = color.green;
+                       rcolor->blue = color.blue;
+                       rcolor->alpha = 0xffff;
 
                        /* FIXME this should probably use a color from the
                         * color cube. */
-                       if (!XftColorAllocValue(display,
-                                               visual,
-                                               colormap,
-                                               &terminal->pvt->palette[i].rcolor,
-                                               &terminal->pvt->palette[i].ftcolor)) {
+                       if (!XftColorAllocValue(display, visual, colormap,
+                                               rcolor, ftcolor)) {
                                terminal->pvt->use_xft = FALSE;
                        }
                }
 #endif
        }
 
-       /* This may have changed the default background color, so trigger
-        * a repaint. */
+       /* We may just have chnged the default background color, so queue
+        * a repaint of the entire viewable area. */
        vte_invalidate_all(terminal);
 }
 
@@ -4616,11 +4626,9 @@ vte_terminal_set_default_colors(VteTerminal *terminal)
                colors[i].red = red;
        }
 
-       /* Set the default background to look the same as color 7, and the
-        * default background to look like color 0. */
-       fg = colors[7];
-       bg = colors[0];
-
+       /* Set the default color set. */
+       fg.red = fg.green = fg.blue = 0xc000;
+       bg.red = bg.green = bg.blue = 0x0000;
        vte_terminal_set_colors(terminal, &fg, &bg,
                                colors, G_N_ELEMENTS(colors));
 }
@@ -4863,6 +4871,20 @@ vte_terminal_handle_sequence(GtkWidget *widget,
        gdk_window_thaw_updates(widget->window);
 }
 
+/* Catch a VteReaper child-exited signal, and if it matches the one we're
+ * looking for, emit one of our own. */
+static void
+vte_terminal_catch_child_exited(VteReaper *reaper, guint pid, guint status,
+                               VteTerminal *data)
+{
+       VteTerminal *terminal;
+       g_return_if_fail(VTE_IS_TERMINAL(data));
+       terminal = VTE_TERMINAL(data);
+       if (pid == terminal->pvt->pty_pid) {
+               vte_terminal_emit_child_exited(terminal);
+       }
+}
+
 /* Start up a command in a slave PTY. */
 pid_t
 vte_terminal_fork_command(VteTerminal *terminal, const char *command,
@@ -4900,6 +4922,11 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command,
                /* Set this as the child's pid. */
                terminal->pvt->pty_pid = pid;
 
+               /* Catch a child-exited signal from the child pid. */
+               g_signal_connect(G_OBJECT(vte_reaper_get()), "child-exited",
+                                G_CALLBACK(vte_terminal_catch_child_exited),
+                                terminal);
+
                /* Set the pty to be non-blocking. */
                i = fcntl(terminal->pvt->pty_master, F_GETFL);
                fcntl(terminal->pvt->pty_master, F_SETFL, i | O_NONBLOCK);
@@ -4908,13 +4935,12 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command,
                terminal->pvt->pty_input =
                        g_io_channel_unix_new(terminal->pvt->pty_master);
                terminal->pvt->pty_output = NULL;
-               terminal->pvt->pty_input_tag =
-                       g_io_add_watch_full(terminal->pvt->pty_input,
-                                           G_PRIORITY_LOW,
-                                           G_IO_IN | G_IO_HUP,
-                                           vte_terminal_io_read,
-                                           terminal,
-                                           NULL);
+               g_io_add_watch_full(terminal->pvt->pty_input,
+                                   G_PRIORITY_LOW,
+                                   G_IO_IN | G_IO_HUP,
+                                   vte_terminal_io_read,
+                                   terminal,
+                                   NULL);
                g_io_channel_unref(terminal->pvt->pty_input);
        }
 
@@ -4935,8 +4961,6 @@ vte_terminal_eof(GIOChannel *channel, gpointer data)
         * has already been dereferenced. */
        if (channel == terminal->pvt->pty_input) {
                terminal->pvt->pty_input = NULL;
-               g_source_remove(terminal->pvt->pty_input_tag);
-               terminal->pvt->pty_input_tag = -1;
        }
 
        /* Emit a signal that we read an EOF. */
@@ -5590,8 +5614,6 @@ vte_terminal_io_write(GIOChannel *channel,
        if (terminal->pvt->n_outgoing == 0) {
                if (channel == terminal->pvt->pty_output) {
                        terminal->pvt->pty_output = NULL;
-                       g_source_remove(terminal->pvt->pty_output_tag);
-                       terminal->pvt->pty_output_tag = -1;
                }
                leave_open = FALSE;
        } else {
@@ -5648,13 +5670,12 @@ vte_terminal_send(VteTerminal *terminal, const char *encoding,
                if (terminal->pvt->pty_output == NULL) {
                        terminal->pvt->pty_output =
                                g_io_channel_unix_new(terminal->pvt->pty_master);
-                       terminal->pvt->pty_output_tag =
-                               g_io_add_watch_full(terminal->pvt->pty_output,
-                                                   G_PRIORITY_HIGH,
-                                                   G_IO_OUT,
-                                                   vte_terminal_io_write,
-                                                   terminal,
-                                                   NULL);
+                       g_io_add_watch_full(terminal->pvt->pty_output,
+                                           G_PRIORITY_HIGH,
+                                           G_IO_OUT,
+                                           vte_terminal_io_write,
+                                           terminal,
+                                           NULL);
                        g_io_channel_unref(terminal->pvt->pty_output);
                }
        }
@@ -5678,13 +5699,21 @@ vte_terminal_feed_child(VteTerminal *terminal, const char *text, size_t length)
 static void
 vte_terminal_im_commit(GtkIMContext *im_context, gchar *text, gpointer data)
 {
+       VteTerminal *terminal;
+
        g_return_if_fail(VTE_IS_TERMINAL(data));
 #ifdef VTE_DEBUG
        if (vte_debug_on(VTE_DEBUG_EVENTS)) {
                fprintf(stderr, "Input method committed `%s'.\n", text);
        }
 #endif
-       vte_terminal_send(VTE_TERMINAL(data), "UTF-8", text, strlen(text));
+       terminal = VTE_TERMINAL(data);
+       vte_terminal_send(terminal, "UTF-8", text, strlen(text));
+       /* Committed text was committed because the user pressed a key, so
+        * we need to obey the scroll-on-keystroke setting. */
+       if (terminal->pvt->scroll_on_keystroke) {
+               vte_terminal_scroll_to_bottom(terminal);
+       }
 }
 
 /* We've started pre-editing. */
@@ -5848,24 +5877,6 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
                                event->string);
                }
 #endif
-               /* Determine if we want to steal this keysym from the input
-                * method.  Ideally the answer would be "no" always. */
-               steal = FALSE;
-               if (modifiers & GDK_SHIFT_MASK) {
-                       switch (event->keyval) {
-                               case GDK_KP_Add:
-                               case GDK_KP_Subtract:
-                                       steal = TRUE;
-#ifdef VTE_DEBUG
-                                       if (vte_debug_on(VTE_DEBUG_EVENTS)) {
-                                               fprintf(stderr, "Hiding key from input method.\n");
-                                       }
-#endif
-                                       break;
-                               default:
-                                       steal = FALSE;
-                       }
-               }
                /* Determine if this is just a modifier key. */
                switch (event->keyval) {
                        case GDK_Caps_Lock:
@@ -5889,13 +5900,26 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
                        case GDK_Shift_Lock:
                        case GDK_Shift_R:
                                modifier = TRUE;
+                               break;
                        default:
                                modifier = FALSE;
+                               break;
                }
                /* Unless it's a modifier key, hide the pointer. */
                if (!modifier) {
                        vte_terminal_set_pointer_visible(terminal, FALSE);
                }
+               /* Determine if this is a key we want to steal. */
+               switch (event->keyval) {
+                       case GDK_KP_Add:
+                       case GDK_KP_Subtract:
+                               if (modifiers & GDK_SHIFT_MASK) {
+                                       steal = TRUE;
+                               }
+                               break;
+                       default:
+                               break;
+               }
        }
 
        /* Let the input method at this one first. */
@@ -6071,10 +6095,23 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
                                                        i = MAX(PANGO_SCALE,
                                                                i - PANGO_SCALE);
                                                }
+#ifdef VTE_DEBUG
+                                               if (vte_debug_on(VTE_DEBUG_MISC)) {
+                                                       fprintf(stderr, "Changing font size from %d to %d.\n",
+                                                               pango_font_description_get_size(fontdesc), i);
+                                               }
+#endif
                                                pango_font_description_set_size(fontdesc, i);
                                                vte_terminal_set_font(terminal, fontdesc);
                                                pango_font_description_free(fontdesc);
                                        }
+#ifdef VTE_DEBUG
+                                       if (vte_debug_on(VTE_DEBUG_MISC)) {
+                                               if (rofontdesc == NULL) {
+                                                       fprintf(stderr, "Font can't be modified.\n");
+                                               }
+                                       }
+#endif
                                        break;
                                }
                        /* The default is to just send the string. */
@@ -6125,7 +6162,6 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
                }
                /* Keep the cursor on-screen. */
                if (!scrolled && !modifier &&
-                   ((normal != NULL) || (special != NULL)) &&
                    terminal->pvt->scroll_on_keystroke) {
                        vte_terminal_scroll_to_bottom(terminal);
                }
@@ -6813,6 +6849,16 @@ vte_terminal_get_text(VteTerminal *terminal,
        }
        return g_string_free(string, FALSE);
 }
+/* Tell the caller where the cursor is, in screen coordinates. */
+void
+vte_terminal_get_cursor_position(VteTerminal *terminal,
+                                long *column, long *row)
+{
+       g_return_if_fail(VTE_IS_TERMINAL(terminal));
+       *column = terminal->pvt->screen->cursor_current.col;
+       *row    = terminal->pvt->screen->cursor_current.row -
+                 terminal->pvt->screen->scroll_delta;
+}
 
 /* Place the selected text onto the clipboard.  Do this asynchronously so that
  * we get notified when the selection we placed on the clipboard is replaced. */
@@ -7266,6 +7312,24 @@ xlfd_from_pango_font_description(GtkWidget *widget,
        return xlfd;
 }
 
+#ifdef HAVE_XFT
+/* Convert an Xft pattern to a font name. */
+static char *
+vte_unparse_xft_pattern(XftPattern *pattern)
+{
+#ifdef HAVE_XFT2
+       return FcNameUnparse(pattern);
+#else /* !HAVE_XFT2 */
+       char buf[256];
+       if (!XftNameUnparse(pattern, buf, sizeof(buf)-1)) {
+               buf[0] = '\0';
+       }
+       buf[sizeof(buf) - 1] = '\0';
+       return strdup(buf);
+#endif /* HAVE_XFT2 */
+}
+#endif /* HAVE_XFT */
+
 /* Set the fontset used for rendering text into the widget. */
 void
 vte_terminal_set_font(VteTerminal *terminal,
@@ -7289,54 +7353,58 @@ vte_terminal_set_font(VteTerminal *terminal,
        descent = 0;
        ascent = height - descent;
 
-       if (terminal->pvt->use_pango) {
-               /* Create the layout if we don't have one yet. */
-               if (terminal->pvt->layout == NULL) {
-                       terminal->pvt->layout = pango_layout_new(gdk_pango_context_get());
-               }
-               /* Free the old font description. */
-               if (terminal->pvt->fontdesc != NULL) {
-                       pango_font_description_free(terminal->pvt->fontdesc);
-                       terminal->pvt->fontdesc = NULL;
-               }
-
-               /* Create the new font description. */
+       /* Free the old font description. */
+       if (terminal->pvt->fontdesc != NULL) {
+               pango_font_description_free(terminal->pvt->fontdesc);
+               terminal->pvt->fontdesc = NULL;
+       }
 #ifdef VTE_DEBUG
-               if (vte_debug_on(VTE_DEBUG_MISC)) {
-                       if (font_desc != NULL) {
-                               char *tmp;
-                               tmp = pango_font_description_to_string(font_desc);
-                               fprintf(stderr, "Using pango font \"%s\".\n",
-                                       tmp);
-                               g_free (tmp);
-                       } else {
-                               fprintf(stderr, "Using default pango font.\n");
-                       }
+       if (vte_debug_on(VTE_DEBUG_MISC)) {
+               if (font_desc) {
+                       char *tmp;
+                       tmp = pango_font_description_to_string(font_desc);
+                       fprintf(stderr, "Using pango font \"%s\".\n",
+                               tmp);
+                       g_free (tmp);
+               } else {
+                       fprintf(stderr, "Using default pango font.\n");
                }
+       }
 #endif
-               if (font_desc != NULL) {
-                       terminal->pvt->fontdesc = pango_font_description_copy(font_desc);
-               } else {
-                       terminal->pvt->fontdesc = pango_font_description_from_string("Luxi Mono 8");
+       /* Set up the normal font description. */
+       if (font_desc != NULL) {
+               terminal->pvt->fontdesc =
+                       pango_font_description_copy(font_desc);
+       } else {
+               terminal->pvt->fontdesc =
+                       pango_font_description_from_string(VTE_DEFAULT_FONT);
+       }
+
+       /* Set the parameter we were passed to point to a known-usable
+        * PangoFontDescription. */
+       font_desc = terminal->pvt->fontdesc;
+
+       if (terminal->pvt->use_pango) {
+               /* Create the layout if we don't have one yet. */
+               if (terminal->pvt->layout == NULL) {
+                       terminal->pvt->layout =
+                               pango_layout_new(gdk_pango_context_get());
                }
 
                /* Try to load the described font. */
-               if (terminal->pvt->fontdesc != NULL) {
+               if (font_desc != NULL) {
                        PangoFont *font = NULL;
                        PangoFontDescription *desc = NULL;
                        PangoContext *pcontext = NULL;
                        PangoFontMetrics *pmetrics = NULL;
                        PangoLanguage *lang = NULL;
                        pcontext = gdk_pango_context_get();
-                       font = pango_context_load_font(pcontext,
-                                                      terminal->pvt->fontdesc);
+                       font = pango_context_load_font(pcontext, font_desc);
                        if (PANGO_IS_FONT(font)) {
-                               /* We got a font, reset the description so that
-                                * it describes this font, and read its metrics. */
+                               /* We got a font, now reset the description so
+                                * that it describes this font, and read its
+                                * metrics. */
                                desc = pango_font_describe(font);
-                               pango_font_description_free(terminal->pvt->fontdesc);
-                               terminal->pvt->fontdesc = desc;
-
                                pango_layout_set_font_description(terminal->pvt->layout, desc);
                                lang = pango_context_get_language(pcontext);
                                pmetrics = pango_font_get_metrics(font, lang);
@@ -7350,6 +7418,10 @@ vte_terminal_set_font(VteTerminal *terminal,
                                height = ascent + descent;
                                pango_font_metrics_unref(pmetrics);
                        }
+                       /* Remove the actual description. */
+                       if (desc != NULL) {
+                               pango_font_description_free(desc);
+                       }
                }
        }
 
@@ -7360,23 +7432,9 @@ vte_terminal_set_font(VteTerminal *terminal,
                XftPattern *matched_pattern;
                XftResult result;
                XGlyphInfo glyph_info;
-               char buf[256];
-
-#ifdef VTE_DEBUG
-               if (vte_debug_on(VTE_DEBUG_MISC)) {
-                       if (font_desc) {
-                               char *tmp;
-                               tmp = pango_font_description_to_string(font_desc);
-                               fprintf(stderr, "Using pango font \"%s\".\n",
-                                       tmp);
-                               g_free (tmp);
-                       } else {
-                               fprintf(stderr, "Using default pango font.\n");
-                       }
-               }
-#endif
+               char *name;
 
-               pattern = xft_pattern_from_pango_font_description(font_desc);
+               pattern = xft_pattern_from_pango_font_description(terminal->pvt->fontdesc);
 
                /* Xft is on a lot of crack here - it fills in "result" when it
                 * feels like it, and leaves it uninitialized the rest of the
@@ -7390,14 +7448,10 @@ vte_terminal_set_font(VteTerminal *terminal,
 #ifdef VTE_DEBUG
                if (vte_debug_on(VTE_DEBUG_MISC)) {
                        if (matched_pattern != NULL) {
-                               char buf[256];
-                               if (!XftNameUnparse(matched_pattern,
-                                                   buf, sizeof(buf)-1)) {
-                                       buf[0] = '\0';
-                               }
-                               buf[sizeof(buf) - 1] = '\0';
+                               name = vte_unparse_xft_pattern(matched_pattern);
                                fprintf(stderr, "Matched pattern \"%s\".\n",
-                                       buf);
+                                       name);
+                               free(name);
                        }
 
                        switch (result) {
@@ -7434,13 +7488,10 @@ vte_terminal_set_font(VteTerminal *terminal,
                }
 
                if (new_font == NULL) {
-                       if (!XftNameUnparse(matched_pattern, buf, sizeof(buf)-1)) {
-                               buf[0] = '\0';
-                       }
-                       buf[sizeof(buf) - 1] = '\0';
-
+                       name = vte_unparse_xft_pattern(matched_pattern);
                        g_warning("Failed to load Xft font pattern \"%s\", "
-                                 "falling back to default font.", buf);
+                                 "falling back to default font.", name);
+                       free(name);
 
                        /* Try to use the default font. */
                        new_font = XftFontOpen(GDK_DISPLAY(),
@@ -7462,15 +7513,11 @@ vte_terminal_set_font(VteTerminal *terminal,
                }
 
                if (new_font) {
-                       char buf[256];
-                       if (!XftNameUnparse (new_font->pattern, buf, sizeof(buf)-1)) {
-                               buf[0] = '\0';
-                       }
-                       buf[sizeof(buf) - 1] = '\0';
-
 #ifdef VTE_DEBUG
                        if (vte_debug_on(VTE_DEBUG_MISC)) {
-                               fprintf(stderr, "Opened new font `%s'.\n", buf);
+                               name = vte_unparse_xft_pattern(new_font->pattern);
+                               fprintf(stderr, "Opened new font `%s'.\n", name);
+                               free(name);
                        }
 #endif
 
@@ -7502,26 +7549,8 @@ vte_terminal_set_font(VteTerminal *terminal,
 #endif
 
        if (!terminal->pvt->use_xft && !terminal->pvt->use_pango) {
-               /* Load the font set, freeing another one if we loaded one
-                * before. */
-#ifdef VTE_DEBUG
-               if (vte_debug_on(VTE_DEBUG_MISC)) {
-                       if (font_desc) {
-                               char *tmp;
-                               tmp = pango_font_description_to_string(font_desc);
-                               fprintf(stderr, "Using pango font \"%s\".\n",
-                                       tmp);
-                               g_free(tmp);
-                       } else {
-                               fprintf(stderr, "Using default pango font.\n");
-                       }
-               }
-#endif
-               xlfds = NULL;
-               if (font_desc) {
-                       xlfds = xlfd_from_pango_font_description(GTK_WIDGET(widget),
-                                                                font_desc);
-               }
+               xlfds = xlfd_from_pango_font_description(GTK_WIDGET(widget),
+                                                        terminal->pvt->fontdesc);
                if (xlfds == NULL) {
                        xlfds = g_strdup("-misc-fixed-medium-r-normal--12-*-*-*-*-*-*-*");
                }
@@ -8344,6 +8373,11 @@ vte_terminal_finalize(GObject *object)
        }
        terminal->pvt->pty_pid = 0;
 
+       /* Stop listening for child-exited signals. */
+       g_signal_handlers_disconnect_by_func(vte_reaper_get(),
+                                            vte_terminal_catch_child_exited,
+                                            terminal);
+
        /* Clear some of our strings. */
        terminal->pvt->termcap_path = NULL;
        terminal->pvt->shell = NULL;
@@ -8353,14 +8387,10 @@ vte_terminal_finalize(GObject *object)
        if (terminal->pvt->pty_input != NULL) {
                g_io_channel_unref(terminal->pvt->pty_input);
                terminal->pvt->pty_input = NULL;
-               g_source_remove(terminal->pvt->pty_input_tag);
-               terminal->pvt->pty_input_tag = -1;
        }
        if (terminal->pvt->pty_output != NULL) {
                g_io_channel_unref(terminal->pvt->pty_output);
                terminal->pvt->pty_output = NULL;
-               g_source_remove(terminal->pvt->pty_output_tag);
-               terminal->pvt->pty_output_tag = -1;
        }
 
        /* Discard any pending data. */
@@ -8529,17 +8559,16 @@ vte_terminal_determine_colors(VteTerminal *terminal,
                *fore = *back;
        }
        if (cell && cell->bold) {
-               if ((*fore != VTE_DEF_FG) &&
-                   (*fore != VTE_DEF_BG) &&
-                   (*fore < 8)) {
-                       *fore += 8;
+               if (*fore == VTE_DEF_FG) {
+                       *fore = VTE_BOLD_FG;
+               } else
+               if ((*fore != VTE_DEF_BG) && (*fore < VTE_COLOR_SET_SIZE)) {
+                       *fore += VTE_COLOR_BRIGHT_OFFSET;
                }
        }
        if (cell && cell->standout) {
-               if ((*back != VTE_DEF_FG) &&
-                   (*back != VTE_DEF_BG) &&
-                   (*back < 8)) {
-                       *back += 8;
+               if (*back < VTE_COLOR_SET_SIZE) {
+                       *back += VTE_COLOR_BRIGHT_OFFSET;
                }
        }
 }
@@ -9274,13 +9303,12 @@ vte_terminal_draw_char(VteTerminal *terminal,
        /* If we haven't drawn anything, try to draw the text using Xft. */
        if (!drawn && terminal->pvt->use_xft) {
                XftChar32 ftc;
-               ftc = vte_terminal_xft_remap_char(display,
-                                                 terminal->pvt->ftfont,
-                                                 cell->c);
+               XftFont *font;
+               font = terminal->pvt->ftfont;
+               ftc = vte_terminal_xft_remap_char(display, font, cell->c);
                XftDrawString32(ftdraw,
                                &terminal->pvt->palette[fore].ftcolor,
-                               terminal->pvt->ftfont,
-                               x, y + ascent, &ftc, 1);
+                               font, x, y + ascent, &ftc, 1);
                drawn = TRUE;
        }
 #endif
@@ -9845,6 +9873,15 @@ vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data)
                             NULL,
                             _vte_marshal_VOID__VOID,
                             G_TYPE_NONE, 0);
+       klass->child_exited_signal =
+               g_signal_new("child-exited",
+                            G_OBJECT_CLASS_TYPE(klass),
+                            G_SIGNAL_RUN_LAST,
+                            0,
+                            NULL,
+                            NULL,
+                            _vte_marshal_VOID__VOID,
+                            G_TYPE_NONE, 0);
        klass->window_title_changed_signal =
                g_signal_new("window-title-changed",
                             G_OBJECT_CLASS_TYPE(klass),
@@ -9970,7 +10007,7 @@ vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data)
                             NULL,
                             NULL,
                             _vte_marshal_VOID__UINT_UINT,
-                            G_TYPE_NONE, 0);
+                            G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
        klass->move_window_signal =
                g_signal_new("move-window",
                             G_OBJECT_CLASS_TYPE(klass),
@@ -9979,7 +10016,7 @@ vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data)
                             NULL,
                             NULL,
                             _vte_marshal_VOID__UINT_UINT,
-                            G_TYPE_NONE, 0);
+                            G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
 
 #ifdef VTE_DEBUG
        /* Turn on debugging if we were asked to. */
@@ -10598,104 +10635,6 @@ vte_terminal_set_scrollback_lines(VteTerminal *terminal, long lines)
        vte_invalidate_all(terminal);
 }
 
-/* Get a snapshot of what's in the visible part of the window. */
-VteTerminalSnapshot *
-vte_terminal_get_snapshot(VteTerminal *terminal)
-{
-       VteTerminalSnapshot *ret;
-       int row, column, x;
-       struct vte_charcell *cell;
-       int fore, back;
-
-       g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
-
-       ret = g_malloc0(sizeof(VteTerminalSnapshot));
-
-       /* Save the cursor position and visibility. */
-       ret->cursor.x = terminal->pvt->screen->cursor_current.col;
-       ret->cursor.y = terminal->pvt->screen->cursor_current.row -
-                       terminal->pvt->screen->insert_delta;
-       ret->cursor_visible = terminal->pvt->screen->cursor_visible;
-
-       /* Save the window size. */
-       ret->rows = terminal->row_count;
-       ret->columns = terminal->column_count;
-
-       /* Save the window contents. */
-       ret->contents = g_malloc0(sizeof(struct VteTerminalSnapshotCell*) *
-                                 (ret->rows + 1));
-       for (row = 0; row < ret->rows; row++) {
-               ret->contents[row] = g_malloc0(sizeof(struct VteTerminalSnapshotCell) *
-                                              (ret->columns + 1));
-               column = x = 0;
-               while (column < ret->columns) {
-                       cell = vte_terminal_find_charcell(terminal,
-                                                         x++,
-                                                         row + terminal->pvt->screen->scroll_delta);
-                       if (cell == NULL) {
-                               break;
-                       }
-                       if (cell->columns == 0) {
-                               continue;
-                       }
-
-                       /* Get the text. FIXME: convert from wchar_t to
-                        * gunichar when they're not interchangeable. */
-#ifdef VTE_DEBUG
-                       if (vte_debug_on(VTE_DEBUG_MISC)) {
-                               fprintf(stderr, "%lc", (wint_t) cell->c);
-                       }
-#endif
-                       ret->contents[row][column].c = cell->c;
-
-                       /* Get text attributes which aren't represented as
-                        * colors. */
-                       ret->contents[row][column].attributes.underline =
-                               cell->underline;
-                       ret->contents[row][column].attributes.alternate =
-                               cell->alternate;
-
-                       /* Get text colors. */
-                       vte_terminal_determine_colors(terminal, cell, FALSE,
-                                                     &fore, &back);
-
-                       ret->contents[row][column].attributes.foreground.red =
-                               terminal->pvt->palette[fore].red;
-                       ret->contents[row][column].attributes.foreground.green =
-                               terminal->pvt->palette[fore].green;
-                       ret->contents[row][column].attributes.foreground.blue =
-                               terminal->pvt->palette[fore].blue;
-
-                       ret->contents[row][column].attributes.background.red =
-                               terminal->pvt->palette[back].red;
-                       ret->contents[row][column].attributes.background.green =
-                               terminal->pvt->palette[back].green;
-                       ret->contents[row][column].attributes.background.blue =
-                               terminal->pvt->palette[back].blue;
-
-                       column++;
-               }
-       }
-       ret->contents[row] = NULL;
-
-       return ret;
-}
-
-void
-vte_terminal_free_snapshot(VteTerminalSnapshot *snapshot)
-{
-       int row;
-       g_return_if_fail(snapshot != NULL);
-       for (row = 0; snapshot->contents[row] != NULL; row++) {
-               memset(snapshot->contents[row], 0,
-                      sizeof(snapshot->contents[row][0]) * snapshot->columns);
-               g_free(snapshot->contents[row]);
-       }
-       g_free(snapshot->contents);
-       memset(snapshot, 0, sizeof(*snapshot));
-       g_free(snapshot);
-}
-
 /* Set the list of characters we consider to be parts of words.  Everything
  * else will be a non-word character, and we'll use transitions between the
  * two sets when doing selection-by-words. */
diff --git a/src/vte.defs b/src/vte.defs
new file mode 100644 (file)
index 0000000..60004e5
--- /dev/null
@@ -0,0 +1,38 @@
+;; -*- scheme -*-
+
+; object definitions ...
+
+(define-object Terminal
+  (in-module "Vte")
+  (parent "Gtk.Widget")
+  (c-name "VteTerminal")
+  (gtype-id "VTE_TYPE_TERMINAL")
+  (fields
+    '("glong" "char_width")
+    '("glong" "char_height")
+    '("glong" "char_ascent")
+    '("glong" "char_descent")
+    '("glong" "row_count")
+    '("glong" "column_count")
+    '("gchar*" "window_title")
+    '("gchar*" "icon_title")
+  )
+)
+
+(define-method fork_command
+  (of-object "VteTerminal")
+  (c-name "vte_terminal_fork_command")
+  (return-type "pid_t")
+  (parameters
+    '("const-gchar*" "command")
+    '("gchar**" "env_add")
+  )
+)
+
+(define-method set_emulation
+  (of-object "VteTerminal")
+  (c-name "vte_terminal_set_emulation")
+  (parameters
+    '("const-gchar*" "emulation")
+  )
+)
index 7b79ad9..f387661 100644 (file)
--- a/src/vte.h
+++ b/src/vte.h
@@ -64,6 +64,7 @@ typedef struct _VteTerminalClass {
        /*< private > */
        /* Signals we might emit. */
        guint eof_signal;
+       guint child_exited_signal;
        guint char_size_changed_signal;
        guint window_title_changed_signal;
        guint icon_title_changed_signal;
@@ -82,26 +83,6 @@ typedef struct _VteTerminalClass {
        guint move_window_signal;
 } VteTerminalClass;
 
-/* A snapshot of the screen contents. */
-typedef struct _VteTerminalSnapshot {
-       struct {
-               int x, y;                       /* Location of the cursor. */
-       } cursor;
-       int rows, columns;                      /* Size of the screen[shot]. */
-       gboolean cursor_visible;
-       struct VteTerminalSnapshotCell {
-               gunichar c;                     /* The character itself. */
-               struct {
-                       /* Colors of this character. */
-                       GdkColor foreground, background;
-                       /* Is it underlined? */
-                       gboolean underline;
-                       /* Is it a graphic character? */
-                       gboolean alternate;
-               } attributes;
-       } **contents;
-} VteTerminalSnapshot;
-
 /* Values for "what should happen when the user hits backspace/delete".  Use
  * AUTO unless the user can cause them to be overridden. */
 typedef enum {
@@ -225,6 +206,8 @@ char *vte_terminal_get_text(VteTerminal *terminal,
                            gboolean(*is_selected)(VteTerminal * terminal,
                                                    long column, long row),
                            GArray *attributes);
+void vte_terminal_get_cursor_position(VteTerminal *terminal,
+                                     long *column, long *row);
 
 /* Display string matching:  clear all matching expressions. */
 void vte_terminal_match_clear_all(VteTerminal *terminal);
index 31f9b38..2232d6f 100644 (file)
@@ -20,7 +20,6 @@
  * accessibility code. */
 
 #include "../config.h"
-#include <iconv.h>
 #include <atk/atk.h>
 #include <gtk/gtk.h>
 #include "debug.h"
 
 #define VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA "VteTerminalAccessiblePrivateData"
 typedef struct _VteTerminalAccessiblePrivate {
-       gboolean snapshot_invalid;
-       VteTerminalSnapshot *snapshot;
-       GArray *snapshot_cells;
-       GArray *snapshot_linebreaks;
-       gint snapshot_caret;
+       gboolean snapshot_invalid;      /* This data needs to be refreshed. */
+       gchar *snapshot_text;           /* Pointer to UTF-8 text. */
+       GArray *snapshot_characters;    /* Offsets to character begin points. */
+       GArray *snapshot_attributes;    /* Attributes, per byte. */
+       GArray *snapshot_linebreaks;    /* Offsets to line breaks. */
+       gint snapshot_caret;            /* Location of the cursor. */
 } VteTerminalAccessiblePrivate;
 
+enum direction {
+       direction_previous = -1,
+       direction_current = 0,
+       direction_next = 1,
+};
+
+static gunichar vte_terminal_accessible_get_character_at_offset(AtkText *text,
+                                                               gint offset);
+
 static VteTerminalAccessiblePrivate *
 vte_terminal_accessible_new_private_data(void)
 {
        VteTerminalAccessiblePrivate *priv;
        priv = g_malloc0(sizeof(*priv));
-       priv->snapshot = NULL;
-       priv->snapshot_cells = NULL;
+       priv->snapshot_text = NULL;
+       priv->snapshot_characters = NULL;
+       priv->snapshot_attributes = NULL;
        priv->snapshot_linebreaks = NULL;
        priv->snapshot_caret = 0;
        priv->snapshot_invalid = TRUE;
        return priv;
 }
 
+/* "Oh yeah, that's selected.  Sure."  */
+static gboolean
+all_selected(VteTerminal *terminal, long column, long row)
+{
+       return TRUE;
+}
+
 static void
 vte_terminal_accessible_update_private_data_if_needed(AtkObject *text)
 {
        VteTerminal *terminal;
        VteTerminalAccessiblePrivate *priv;
-       struct VteTerminalSnapshotCell cell;
-       int row, col, i, caret;
+       struct vte_char_attributes attrs;
+       char *next;
+       int row, i, offset, caret;
+       long ccol, crow;
 
        g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text));
 
@@ -69,87 +88,124 @@ vte_terminal_accessible_update_private_data_if_needed(AtkObject *text)
                return;
        }
 
-       /* Free the possibly-outdated snapshot. */
-       if (priv->snapshot != NULL) {
-               vte_terminal_free_snapshot(priv->snapshot);
-               priv->snapshot = NULL;
+       /* Free the possibly-outdated snapshot data. */
+       if (priv->snapshot_text != NULL) {
+               g_free(priv->snapshot_text);
+               priv->snapshot_text = NULL;
+       }
+
+       if (priv->snapshot_characters != NULL) {
+               g_array_free(priv->snapshot_characters, TRUE);
+               priv->snapshot_characters = NULL;
        }
-       if (priv->snapshot_cells != NULL) {
-               g_array_free(priv->snapshot_cells, TRUE);
-               priv->snapshot_cells = NULL;
+       priv->snapshot_characters = g_array_new(FALSE, TRUE, sizeof(int));
+
+       if (priv->snapshot_attributes != NULL) {
+               g_array_free(priv->snapshot_attributes, TRUE);
+               priv->snapshot_attributes = NULL;
        }
+       priv->snapshot_attributes = g_array_new(FALSE, TRUE,
+                                               sizeof(struct vte_char_attributes));
+
        if (priv->snapshot_linebreaks != NULL) {
                g_array_free(priv->snapshot_linebreaks, TRUE);
                priv->snapshot_linebreaks = NULL;
        }
+       priv->snapshot_linebreaks = g_array_new(FALSE, TRUE, sizeof(int));
+
        priv->snapshot_caret = 0;
 
-       /* Get a new snapshot, and munge it into something that might be
-        * mistaken for a continuous-text display widget. */
+       /* Get a new view of the uber-label. */
        terminal = VTE_TERMINAL((GTK_ACCESSIBLE(text))->widget);
-       priv->snapshot = vte_terminal_get_snapshot(terminal);
-       if (priv->snapshot == NULL) {
+       priv->snapshot_text = vte_terminal_get_text(terminal, all_selected,
+                                                   priv->snapshot_attributes);
+       if (priv->snapshot_text == NULL) {
                /* Aaargh!  We're screwed. */
                return;
        }
+       vte_terminal_get_cursor_position(terminal, &ccol, &crow);
+
+       /* Get the offsets to the beginnings of each character. */
+       i = 0;
+       while (i < priv->snapshot_attributes->len) {
+               g_array_append_val(priv->snapshot_characters, i);
+               next = g_utf8_find_next_char(priv->snapshot_text + i, NULL);
+               if (next == NULL) {
+                       break;
+               } else {
+                       i = next - priv->snapshot_text;
+               }
+       }
 
-       /* Get the addresses of each of the cells, and add them to a linear
-        * array of characters, tracking where line breaks occur, and setting
-        * the caret to point at the location where the cursor is. */
-       priv->snapshot_cells = g_array_new(FALSE, TRUE, sizeof(cell));
-       priv->snapshot_linebreaks = g_array_new(FALSE, TRUE, sizeof(int));
+       /* Get the offsets to the beginnings of each line. */
        caret = -1;
-       for (row = 0;
-             (row < priv->snapshot->rows) &&
-            (priv->snapshot->contents[row] != NULL);
-            row++) {
-               for (col = 0;
-                    (col < priv->snapshot->columns) &&
-                    (priv->snapshot->contents[row][col].c != 0);
-                    col++) {
-                       if ((row == priv->snapshot->cursor.y) &&
-                           (col == priv->snapshot->cursor.x)) {
-                               caret = priv->snapshot_cells->len;
-                       }
-                       cell = priv->snapshot->contents[row][col];
+       for (i = 0, row = 0; i < priv->snapshot_characters->len; i++) {
+               /* Get the attributes for the current cell. */
+               offset = g_array_index(priv->snapshot_characters, int, i);
+               attrs = g_array_index(priv->snapshot_attributes,
+                                     struct vte_char_attributes, offset);
+               /* If this cell is "before" the cursor, move the caret to
+                * be "here". */
+               if ((attrs.row < crow) ||
+                   ((attrs.row == crow) && (attrs.column <= ccol))) {
 #ifdef VTE_DEBUG
                        if (vte_debug_on(VTE_DEBUG_MISC)) {
-                               fprintf(stderr, "%lc", cell.c);
+                               fprintf(stderr, "Caret is after (%d/%d).\n",
+                                       attrs.column, attrs.row);
                        }
 #endif
-                       g_array_append_val(priv->snapshot_cells, cell);
-
+                       caret = i;
                }
-               if ((row == priv->snapshot->cursor.y) && (caret == -1)) {
-                       caret = priv->snapshot_cells->len;
+               /* If this character is on a row different from the row the
+                * character we looked at previously was on, then it's a new
+                * line and we need to keep track of where it is. */
+               if ((i == 0) || (attrs.row != row)) {
+#ifdef VTE_DEBUG
+                       if (vte_debug_on(VTE_DEBUG_MISC)) {
+                               fprintf(stderr, "Row %d/%d begins at %ld.\n",
+                                       priv->snapshot_linebreaks->len,
+                                       attrs.row,
+                                       i);
+                               fprintf(stderr, "Cursor at (%d, %d).\n",
+                                       ccol, crow);
+                       }
+#endif
+                       g_array_append_val(priv->snapshot_linebreaks, i);
                }
-               i = row;
-               g_array_append_val(priv->snapshot_linebreaks, i);
+               row = attrs.row;
        }
+       /* Add the final line break. */
+       g_array_append_val(priv->snapshot_linebreaks, i);
+       /* Store the caret position. */
        if (caret == -1) {
-               caret = priv->snapshot_cells->len;
+               caret = priv->snapshot_attributes->len;
        }
        priv->snapshot_caret = caret;
 #ifdef VTE_DEBUG
        if (vte_debug_on(VTE_DEBUG_MISC)) {
                fprintf(stderr, "Refreshed accessibility snapshot, "
-                       "%ld cells.\n", (long)priv->snapshot_cells->len);
+                       "%ld cells.\n", (long)priv->snapshot_attributes->len);
        }
 #endif
        priv->snapshot_invalid = FALSE;
 }
 
+/* Free snapshot private data. */
 static void
 vte_terminal_accessible_free_private_data(VteTerminalAccessiblePrivate *priv)
 {
        g_return_if_fail(priv != NULL);
-       if (priv->snapshot != NULL) {
-               vte_terminal_free_snapshot(priv->snapshot);
-               priv->snapshot = NULL;
+       if (priv->snapshot_text != NULL) {
+               g_free(priv->snapshot_text);
+               priv->snapshot_text = NULL;
+       }
+       if (priv->snapshot_characters != NULL) {
+               g_array_free(priv->snapshot_characters, TRUE);
+               priv->snapshot_characters = NULL;
        }
-       if (priv->snapshot_cells != NULL) {
-               g_array_free(priv->snapshot_cells, TRUE);
-               priv->snapshot_cells = NULL;
+       if (priv->snapshot_attributes != NULL) {
+               g_array_free(priv->snapshot_attributes, TRUE);
+               priv->snapshot_attributes = NULL;
        }
        if (priv->snapshot_linebreaks != NULL) {
                g_array_free(priv->snapshot_linebreaks, TRUE);
@@ -165,7 +221,6 @@ vte_terminal_accessible_invalidate(VteTerminal *terminal, gpointer data)
        VteTerminalAccessiblePrivate *priv;
 
        g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(data));
-       g_return_if_fail(G_IS_OBJECT(data));
 
        priv = g_object_get_data(G_OBJECT(data),
                                 VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA);
@@ -184,16 +239,18 @@ vte_terminal_accessible_invalidate(VteTerminal *terminal, gpointer data)
 static void
 vte_terminal_accessible_hierarchy_changed(VteTerminal *terminal, gpointer data)
 {
-       AtkObject *atk, *parent;
+       AtkObject *object, *parent;
+
        g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(data));
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
-       atk = ATK_OBJECT(data);
+
+       object = ATK_OBJECT(data);
        if (GTK_IS_WIDGET((GTK_WIDGET(terminal))->parent)) {
                parent = gtk_widget_get_accessible((GTK_WIDGET(terminal))->parent);
        } else {
                parent = NULL;
        }
-       atk_object_set_parent(atk, parent);
+       atk_object_set_parent(object, parent);
 }
 
 /* Handle title changes by resetting the parent object. */
@@ -227,13 +284,13 @@ vte_terminal_accessible_new(VteTerminal *terminal)
                          vte_terminal_accessible_new_private_data());
        g_signal_connect(G_OBJECT(terminal), "contents-changed",
                         GTK_SIGNAL_FUNC(vte_terminal_accessible_invalidate),
-                        access);
+                        object);
        g_signal_connect(G_OBJECT(terminal), "cursor-moved",
                         GTK_SIGNAL_FUNC(vte_terminal_accessible_invalidate),
-                        access);
+                        object);
         g_signal_connect(G_OBJECT(terminal), "hierarchy-changed",
                         GTK_SIGNAL_FUNC(vte_terminal_accessible_hierarchy_changed),
-                        access);
+                        object);
         g_signal_connect(G_OBJECT(terminal), "window-title-changed",
                         GTK_SIGNAL_FUNC(vte_terminal_accessible_title_changed),
                         access);
@@ -260,41 +317,23 @@ vte_terminal_accessible_finalize(GObject *object)
        gobject_class = g_type_class_peek_parent(VTE_TERMINAL_ACCESSIBLE_GET_CLASS(object));
 
        g_signal_handlers_disconnect_matched(G_OBJECT(accessible->widget),
-                                            (G_SIGNAL_MATCH_ID |
-                                             G_SIGNAL_MATCH_DATA),
-                                            g_signal_lookup("contents-changed",
-                                                            VTE_TYPE_TERMINAL),
-                                            0,
-                                            NULL,
-                                            NULL,
-                                            accessible);
+                                            G_SIGNAL_MATCH_FUNC |
+                                            G_SIGNAL_MATCH_DATA,
+                                            0, 0, NULL,
+                                            vte_terminal_accessible_invalidate,
+                                            object);
        g_signal_handlers_disconnect_matched(G_OBJECT(accessible->widget),
-                                            (G_SIGNAL_MATCH_ID |
-                                             G_SIGNAL_MATCH_DATA),
-                                            g_signal_lookup("cursor-moved",
-                                                            VTE_TYPE_TERMINAL),
-                                            0,
-                                            NULL,
-                                            NULL,
-                                            accessible);
+                                            G_SIGNAL_MATCH_FUNC |
+                                            G_SIGNAL_MATCH_DATA,
+                                            0, 0, NULL,
+                                            vte_terminal_accessible_title_changed,
+                                            object);
        g_signal_handlers_disconnect_matched(G_OBJECT(accessible->widget),
-                                            (G_SIGNAL_MATCH_ID |
-                                             G_SIGNAL_MATCH_DATA),
-                                            g_signal_lookup("hierarchy-changed",
-                                                            VTE_TYPE_TERMINAL),
-                                            0,
-                                            NULL,
-                                            NULL,
-                                            accessible);
-       g_signal_handlers_disconnect_matched(G_OBJECT(accessible->widget),
-                                            (G_SIGNAL_MATCH_ID |
-                                             G_SIGNAL_MATCH_DATA),
-                                            g_signal_lookup("window-title-changed",
-                                                            VTE_TYPE_TERMINAL),
-                                            0,
-                                            NULL,
-                                            NULL,
-                                            accessible);
+                                            G_SIGNAL_MATCH_FUNC |
+                                            G_SIGNAL_MATCH_DATA,
+                                            0, 0, NULL,
+                                            vte_terminal_accessible_hierarchy_changed,
+                                            object);
        if (gobject_class->finalize != NULL) {
                gobject_class->finalize(object);
        }
@@ -305,8 +344,7 @@ vte_terminal_accessible_get_text(AtkText *text,
                                 gint start_offset, gint end_offset)
 {
        VteTerminalAccessiblePrivate *priv;
-       gchar *buf, *p;
-       int i;
+       int start, end;
 
        g_return_val_if_fail(end_offset >= start_offset, g_strdup(""));
 
@@ -317,58 +355,58 @@ vte_terminal_accessible_get_text(AtkText *text,
 #ifdef VTE_DEBUG
        if (vte_debug_on(VTE_DEBUG_MISC)) {
                fprintf(stderr, "Getting text from %d to %d of %d.\n",
-                       start_offset, end_offset, priv->snapshot_cells->len);
+                       start_offset, end_offset,
+                       priv->snapshot_characters->len);
        }
 #endif
        g_return_val_if_fail(ATK_IS_TEXT(text), NULL);
 
        /* If the requested area is after all of the text, just return an
         * empty string. */
-       if (start_offset >= priv->snapshot_cells->len) {
-               return g_strdup("");
-       }
-       if (end_offset > priv->snapshot_cells->len) {
+       if (start_offset >= priv->snapshot_characters->len) {
                return g_strdup("");
        }
 
-       /* Allocate space to hold as many UTF-8 characters as we have
-        * unicode characters. */
-       p = buf = g_malloc((end_offset - start_offset) * VTE_UTF8_BPC + 1);
-       for (i = start_offset;
-            (i < end_offset) && (i < priv->snapshot_cells->len);
-            i++) {
-               p += g_unichar_to_utf8(g_array_index(priv->snapshot_cells,
-                                                    struct VteTerminalSnapshotCell,
-                                                    i).c,
-                                      p);
+       /* Map the offsets to, er, offsets. */
+       start = g_array_index(priv->snapshot_characters, int, start_offset);
+       if (end_offset >= priv->snapshot_attributes->len) {
+               /* Get everything up to the end of the buffer. */
+               end = priv->snapshot_attributes->len;
+       } else {
+               /* Map the stopping point. */
+               end = g_array_index(priv->snapshot_characters, int, end_offset);
        }
-       *p = '\0';
-       return buf;
+       return g_strndup(priv->snapshot_text + start, end - start);
 }
 
+/* Map a subsection of the text with before/at/after char/word/line specs
+ * into a run of Unicode characters.  (The interface is specifying characters,
+ * not bytes, plus that saves us from having to deal with parts of multibyte
+ * characters, which are icky.) */
 static gchar *
 vte_terminal_accessible_get_text_somewhere(AtkText *text,
                                           gint offset,
                                           AtkTextBoundary boundary_type,
-                                          gint direction,
+                                          enum direction direction,
                                           gint *start_offset,
                                           gint *end_offset)
 {
        VteTerminalAccessiblePrivate *priv;
-       gunichar c;
-       gboolean word, in_word;
-       int i, line;
+       VteTerminal *terminal;
+       gunichar current, prev, next;
+       int line;
 
        vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text));
 
        priv = g_object_get_data(G_OBJECT(text),
                                 VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA);
+       terminal = VTE_TERMINAL((GTK_ACCESSIBLE(text))->widget);
 
 #ifdef VTE_DEBUG
        if (vte_debug_on(VTE_DEBUG_MISC)) {
                fprintf(stderr, "Getting %s %s at %d of %d.\n",
-                       (direction == 0) ? "this" :
-                       ((direction == 1) ? "next" : "previous"),
+                       (direction == direction_current) ? "this" :
+                       ((direction == direction_next) ? "next" : "previous"),
                        (boundary_type == ATK_TEXT_BOUNDARY_CHAR) ? "char" :
                        ((boundary_type == ATK_TEXT_BOUNDARY_LINE_START) ? "line (start)" :
                        ((boundary_type == ATK_TEXT_BOUNDARY_LINE_END) ? "line (end)" :
@@ -376,11 +414,12 @@ vte_terminal_accessible_get_text_somewhere(AtkText *text,
                        ((boundary_type == ATK_TEXT_BOUNDARY_WORD_END) ? "word (end)" :
                        ((boundary_type == ATK_TEXT_BOUNDARY_SENTENCE_START) ? "sentence (start)" :
                        ((boundary_type == ATK_TEXT_BOUNDARY_SENTENCE_END) ? "sentence (end)" : "unknown")))))),
-                       offset, priv->snapshot_cells->len);
+                       offset, priv->snapshot_attributes->len);
        }
 #endif
-       g_return_val_if_fail(priv->snapshot_cells != NULL, g_strdup(""));
-       g_return_val_if_fail(offset < priv->snapshot_cells->len, g_strdup(""));
+       g_return_val_if_fail(priv->snapshot_text != NULL, g_strdup(""));
+       g_return_val_if_fail(offset < priv->snapshot_characters->len,
+                            g_strdup(""));
        g_return_val_if_fail(offset >= 0, g_strdup(""));
 
        switch (boundary_type) {
@@ -390,114 +429,117 @@ vte_terminal_accessible_get_text_somewhere(AtkText *text,
                        offset += direction;
                        *start_offset = MAX(offset, 0);
                        *end_offset = MIN(offset + 1,
-                                         priv->snapshot_cells->len);
+                                         priv->snapshot_attributes->len);
                        break;
                case ATK_TEXT_BOUNDARY_WORD_START:
                case ATK_TEXT_BOUNDARY_WORD_END:
-                       /* Find the wordstart before the requested point.
-                        * FIXME: use pango_break or g_unichar_break_type to
-                        * find word boundaries. For now, this should work
-                        * only for some locales. */
-                       c = g_array_index(priv->snapshot_cells,
-                                         struct VteTerminalSnapshotCell,
-                                         offset).c;
-                       word = in_word = !g_unichar_isspace(c);
-                       *start_offset = offset;
-                       for (i = offset; i >= 0; i--) {
-                               c = g_array_index(priv->snapshot_cells,
-                                                 struct VteTerminalSnapshotCell,
-                                                 i).c;
-                               if (word && g_unichar_isspace(c)) {
-                                       *start_offset = i + 1;
+                       /* Back up to the previous non-word-word transition. */
+                       while (offset > 0) {
+                               prev = vte_terminal_accessible_get_character_at_offset(text, offset - 1);
+                               if (vte_terminal_is_word_char(terminal, prev)) {
+                                       offset--;
+                               } else {
                                        break;
                                }
-                               if (i == 0) {
-                                       *start_offset = 0;
-                                       break;
-                               }
-                               word = g_unichar_isspace(c);
                        }
+                       *start_offset = offset;
                        /* If we started in a word and we're looking for the
-                        * word before this one, keep searching. */
-                       if (in_word && (direction == -1)) {
-                               word = !g_unichar_isspace(c);
-                               for (i = *start_offset; i >= 0; i--) {
-                                       c = g_array_index(priv->snapshot_cells,
-                                                         struct VteTerminalSnapshotCell,
-                                                         i).c;
-                                       if (word && g_unichar_isspace(c)) {
-                                               *start_offset = i + 1;
+                        * word before this one, keep searching by backing up
+                        * to the previous non-word character and then searching
+                        * for the word-start before that. */
+                       if (direction == direction_previous) {
+                               while (offset > 0) {
+                                       prev = vte_terminal_accessible_get_character_at_offset(text, offset - 1);
+                                       if (!vte_terminal_is_word_char(terminal, prev)) {
+                                               offset--;
+                                       } else {
                                                break;
                                        }
-                                       if (i == 0) {
-                                               *start_offset = 0;
+                               }
+                               while (offset > 0) {
+                                       prev = vte_terminal_accessible_get_character_at_offset(text, offset - 1);
+                                       if (vte_terminal_is_word_char(terminal, prev)) {
+                                               offset--;
+                                       } else {
                                                break;
                                        }
                                }
+                               *start_offset = offset;
                        }
                        /* If we're looking for the word after this one,
-                        * search forward. */
-                       if (direction == 1) {
-                               word = g_unichar_isspace(c);
-                               for (i = *start_offset; i < priv->snapshot_cells->len; i--) {
-                                       c = g_array_index(priv->snapshot_cells,
-                                                         struct VteTerminalSnapshotCell,
-                                                         i).c;
-                                       if (!word && !g_unichar_isspace(c)) {
-                                               *start_offset = i;
+                        * search forward by scanning forward for the next
+                        * non-word character, then the next word character
+                        * after that. */
+                       if (direction == direction_next) {
+                               while (offset < priv->snapshot_characters->len) {
+                                       next = vte_terminal_accessible_get_character_at_offset(text, offset);
+                                       if (vte_terminal_is_word_char(terminal, next)) {
+                                               offset++;
+                                       } else {
                                                break;
                                        }
-                                       if (i == priv->snapshot_cells->len - 1) {
-                                               *start_offset = i + 1;
+                               }
+                               while (offset < priv->snapshot_characters->len) {
+                                       next = vte_terminal_accessible_get_character_at_offset(text, offset);
+                                       if (!vte_terminal_is_word_char(terminal, next)) {
+                                               offset++;
+                                       } else {
                                                break;
                                        }
                                }
+                               *start_offset = offset;
                        }
                        /* Now find the end of this word. */
-                       word = TRUE;
-                       *end_offset = *start_offset;
-                       for (i = *start_offset; i < priv->snapshot_cells->len; i--) {
-                               c = g_array_index(priv->snapshot_cells,
-                                                 struct VteTerminalSnapshotCell,
-                                                 i).c;
-                               if (!word && !g_unichar_isspace(c)) {
-                                       *end_offset = i;
-                                       break;
-                               }
-                               if (i == priv->snapshot_cells->len - 1) {
-                                       *end_offset = i + 1;
+                       while (offset < priv->snapshot_characters->len) {
+                               current = vte_terminal_accessible_get_character_at_offset(text, offset);
+                               if (vte_terminal_is_word_char(terminal, current)) {
+                                       offset++;
+                               } else {
                                        break;
                                }
+
                        }
+                       *end_offset = offset;
                        break;
                case ATK_TEXT_BOUNDARY_LINE_START:
                case ATK_TEXT_BOUNDARY_LINE_END:
-                       /* Figure out which line we're on.  If the end of the
-                        * i'th line is after the offset, then i is the line
-                        * we're looking at. */
+                       /* Figure out which line we're on.  If the start of the
+                        * i'th line is before the offset, then i could be the
+                        * line we're looking for. */
                        line = 0;
-                       for (i = 0; i < priv->snapshot_cells->len; i++) {
+                       for (line = 0;
+                            line < priv->snapshot_linebreaks->len;
+                            line++) {
                                if (g_array_index(priv->snapshot_linebreaks,
-                                                 int, i) > offset) {
-                                       line = i;
+                                                 int, line) > offset) {
+                                       line--;
                                        break;
                                }
                        }
+#ifdef VTE_DEBUG
+                       if (vte_debug_on(VTE_DEBUG_MISC)) {
+                               fprintf(stderr, "Character %d is on line %d.\n",
+                                       offset, line);
+                       }
+#endif
                        /* Perturb the line number to handle before/at/after. */
                        line += direction;
-                       line = MAX(0, line);
-                       line = MIN(line, priv->snapshot_linebreaks->len - 1);
+                       line = CLAMP(line,
+                                    0, priv->snapshot_linebreaks->len - 1);
                        /* Read the offsets for this line. */
-                       if (line == 0) {
-                               *start_offset = 0;
-                       } else {
-                               *start_offset = g_array_index(priv->snapshot_linebreaks,
-                                                             int,
-                                                             line - 1);
-                       }
+                       *start_offset = g_array_index(priv->snapshot_linebreaks,
+                                                     int, line);
+                       line++;
+                       line = CLAMP(line,
+                                    0, priv->snapshot_linebreaks->len - 1);
                        *end_offset = g_array_index(priv->snapshot_linebreaks,
-                                                   int,
-                                                   line);
+                                                   int, line);
+#ifdef VTE_DEBUG
+                       if (vte_debug_on(VTE_DEBUG_MISC)) {
+                               fprintf(stderr, "Line runs from %d to %d.\n",
+                                       *start_offset, *end_offset);
+                       }
+#endif
                        break;
                case ATK_TEXT_BOUNDARY_SENTENCE_START:
                case ATK_TEXT_BOUNDARY_SENTENCE_END:
@@ -506,9 +548,9 @@ vte_terminal_accessible_get_text_somewhere(AtkText *text,
                        *start_offset = *end_offset = 0;
                        break;
        }
-       *start_offset = MAX(*start_offset, priv->snapshot_cells->len - 1);
-       *end_offset = MAX(*start_offset, *end_offset);
-       *end_offset = MIN(*end_offset, priv->snapshot_cells->len);
+       *start_offset = MIN(*start_offset, priv->snapshot_characters->len - 1);
+       *end_offset = CLAMP(*end_offset, *start_offset,
+                           priv->snapshot_characters->len);
        return vte_terminal_accessible_get_text(text,
                                                *start_offset,
                                                *end_offset);
@@ -566,15 +608,24 @@ static gunichar
 vte_terminal_accessible_get_character_at_offset(AtkText *text, gint offset)
 {
        VteTerminalAccessiblePrivate *priv;
+       int mapped;
+       char *unichar;
+       gunichar ret;
 
        vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text));
 
        priv = g_object_get_data(G_OBJECT(text),
                                 VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA);
 
-       return g_array_index(priv->snapshot_cells,
-                            struct VteTerminalSnapshotCell,
-                            offset).c;
+       g_return_val_if_fail(offset < priv->snapshot_characters->len, 0);
+
+       mapped = g_array_index(priv->snapshot_characters, int, offset);
+
+       unichar = vte_terminal_accessible_get_text(text, offset, offset + 1);
+       ret = g_utf8_get_char(unichar);
+       g_free(unichar);
+
+       return ret;
 }
 
 static gint
@@ -630,7 +681,7 @@ vte_terminal_accessible_get_character_count(AtkText *text)
        priv = g_object_get_data(G_OBJECT(text),
                                 VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA);
 
-       return priv->snapshot_cells->len;
+       return priv->snapshot_attributes->len;
 }
 
 static gint
index a43199a..5810efc 100644 (file)
@@ -71,6 +71,18 @@ destroy_and_quit(GtkWidget *widget, gpointer data)
        }
        gtk_main_quit();
 }
+static void
+destroy_and_quit_eof(GtkWidget *widget, gpointer data)
+{
+       g_print("Detected EOF.\n");
+       destroy_and_quit(widget, data);
+}
+static void
+destroy_and_quit_exited(GtkWidget *widget, gpointer data)
+{
+       g_print("Detected child exit.\n");
+       destroy_and_quit(widget, data);
+}
 
 int
 main(int argc, char **argv)
@@ -130,7 +142,9 @@ main(int argc, char **argv)
 
        /* Connect to the "eof" signal to quit when the session ends. */
        g_signal_connect(G_OBJECT(widget), "eof",
-                        G_CALLBACK(destroy_and_quit), widget);
+                        G_CALLBACK(destroy_and_quit_eof), widget);
+       g_signal_connect(G_OBJECT(widget), "child-exited",
+                        G_CALLBACK(destroy_and_quit_exited), widget);
 
        /* Create the scrollbar for the widget. */
        scrollbar = gtk_vscrollbar_new((VTE_TERMINAL(widget))->adjustment);
index 94bc55b..d011bb3 100644 (file)
--- a/vte.spec
+++ b/vte.spec
@@ -1,5 +1,5 @@
 Name: vte
-Version: 0.3.25
+Version: 0.3.26
 Release: 1
 Summary: An experimental terminal emulator.
 License: LGPL
@@ -41,7 +41,6 @@ make install DESTDIR=$RPM_BUILD_ROOT
 %files
 %defattr(-,root,root)
 %doc ChangeLog COPYING HACKING NEWS README
-%{_bindir}/*
 %{_libdir}/*.so.*.*
 %{_datadir}/*
 
@@ -53,6 +52,11 @@ make install DESTDIR=$RPM_BUILD_ROOT
 %{_libdir}/pkgconfig/*
 
 %changelog
+* Thu Jun  6 2002 Nalin Dahyabhai <nalin@redhat.com> 0.3.26-1
+- don't package up the test program
+- emit "child-exited" signals properly
+- try to allow building with either pangoxft-with-Xft1 or pangoxft-with-Xft2
+
 * Wed Jun  5 2002 Nalin Dahyabhai <nalin@redhat.com> 0.3.25-1
 - compute font widths better