[vte] pty: Use packet mode on the PTY



commit 06e794c1961195633ba80951ff4771be8e794cf4
Author: Egmont Koblinger <egmont gmail com>
Date:   Wed Nov 25 20:40:17 2015 +0100

    pty: Use packet mode on the PTY
    
    This allows us to get informed when scroll lock changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=755371

 src/pty.cc         |   14 +++++++++++++
 src/vte.cc         |   56 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/vteinternal.hh |    6 ++++-
 3 files changed, 72 insertions(+), 4 deletions(-)
---
diff --git a/src/pty.cc b/src/pty.cc
index 82f7771..efdc076 100644
--- a/src/pty.cc
+++ b/src/pty.cc
@@ -759,6 +759,20 @@ _vte_pty_getpt(GError **error)
                 return -1;
         }
 
+        /* tty_ioctl(4) -> every read() gives an extra byte at the beginning
+         * notifying us of stop/start (^S/^Q) events. */
+        int one = 1;
+        rv = ioctl(fd, TIOCPKT, &one);
+        if (rv < 0) {
+                int errsv = errno;
+                g_set_error(error, VTE_PTY_ERROR,
+                            VTE_PTY_ERROR_PTY98_FAILED,
+                            "%s failed: %s", "ioctl(TIOCPKT)", g_strerror(errno));
+                close(fd);
+                errno = errsv;
+                return -1;
+        }
+
        return fd;
 }
 
diff --git a/src/vte.cc b/src/vte.cc
index 39f9567..e8f0fcf 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -53,6 +53,12 @@
 
 #include <new> /* placement new */
 
+/* Some sanity checks */
+/* FIXMEchpe: move this to there when splitting _vte_incoming_chunk into its own file */
+static_assert(sizeof(struct _vte_incoming_chunk) <= VTE_INPUT_CHUNK_SIZE, "_vte_incoming_chunk too large");
+static_assert(offsetof(struct _vte_incoming_chunk, data) == offsetof(struct _vte_incoming_chunk, 
dataminusone) + 1, "_vte_incoming_chunk layout wrong");
+
+
 #ifndef HAVE_ROUND
 static inline double round(double x) {
        if(x - floor(x) < 0.5) {
@@ -3424,7 +3430,7 @@ VteTerminalPrivate::connect_pty_read()
                m_pty_input_source =
                        g_io_add_watch_full(m_pty_channel,
                                            VTE_CHILD_INPUT_PRIORITY,
-                                           (GIOCondition)(G_IO_IN | G_IO_HUP),
+                                           (GIOCondition)(G_IO_IN | G_IO_PRI | G_IO_HUP),
                                            (GIOFunc) vte_terminal_io_read,
                                            m_terminal,
                                            (GDestroyNotify) mark_input_source_invalid);
@@ -3487,6 +3493,20 @@ VteTerminalPrivate::disconnect_pty_write()
        }
 }
 
+void
+VteTerminalPrivate::pty_termios_changed()
+{
+        _vte_debug_print(VTE_DEBUG_IO, "Termios changed\n");
+}
+
+void
+VteTerminalPrivate::pty_scroll_lock_changed(bool locked)
+{
+        _vte_debug_print(VTE_DEBUG_IO, "Output %s (^%c)\n",
+                         locked ? "stopped" : "started",
+                         locked ? 'Q' : 'S');
+}
+
 /*
  * VteTerminalPrivate::watch_child:
  * @child_pid: a #GPid
@@ -4098,7 +4118,7 @@ vte_terminal_io_read(GIOChannel *channel,
        eof = condition & G_IO_HUP;
 
        /* Read some data in from this channel. */
-       if (condition & G_IO_IN) {
+       if (condition & (G_IO_IN | G_IO_PRI)) {
                struct _vte_incoming_chunk *chunk, *chunks = NULL;
                const int fd = g_io_channel_unix_get_fd (channel);
                guchar *bp;
@@ -4133,7 +4153,16 @@ vte_terminal_io_read(GIOChannel *channel,
                        bp = chunk->data + chunk->len;
                        len = 0;
                        do {
-                               int ret = read (fd, bp, rem);
+                                /* We'd like to read (fd, bp, rem); but due to TIOCPKT mode
+                                 * there's an extra input byte returned at the beginning.
+                                 * We need to see what that byte is, but otherwise drop it
+                                 * and write continuously to chunk->data.
+                                 */
+                                char pkt_header;
+                                char save = bp[-1];
+                                int ret = read (fd, bp - 1, rem + 1);
+                                pkt_header = bp[-1];
+                                bp[-1] = save;
                                switch (ret){
                                        case -1:
                                                err = errno;
@@ -4142,6 +4171,27 @@ vte_terminal_io_read(GIOChannel *channel,
                                                eof = TRUE;
                                                goto out;
                                        default:
+                                                ret--;
+
+                                                if (pkt_header & TIOCPKT_IOCTL) {
+                                                        /* We'd like to always be informed when the termios 
change,
+                                                         * so we can e.g. detect when no-echo is en/disabled 
and
+                                                         * change the cursor/input method/etc., but 
unfortunately
+                                                         * the kernel only sends this flag when (old or new) 
'local flags'
+                                                         * include EXTPROC, which is not used often, and due 
to its side
+                                                         * effects, cannot be enabled by vte by default.
+                                                         *
+                                                         * FIXME: improve the kernel! see discussion in bug 
755371
+                                                         * starting at comment 12
+                                                         */
+                                                        terminal->pvt->pty_termios_changed();
+                                                }
+                                                if (pkt_header & TIOCPKT_STOP) {
+                                                        terminal->pvt->pty_scroll_lock_changed(true);
+                                                } else if (pkt_header & TIOCPKT_START) {
+                                                        terminal->pvt->pty_scroll_lock_changed(false);
+                                                }
+
                                                bp += ret;
                                                rem -= ret;
                                                len += ret;
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index ca268b1..59e8b65 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -125,7 +125,8 @@ typedef struct _vte_incoming_chunk _vte_incoming_chunk_t;
 struct _vte_incoming_chunk{
         _vte_incoming_chunk_t *next;
         guint len;
-        guchar data[VTE_INPUT_CHUNK_SIZE - 2 * sizeof(void *)];
+        guchar dataminusone;    /* Hack: Keep it right before data, so that data[-1] is valid and usable */
+        guchar data[VTE_INPUT_CHUNK_SIZE - 2 * sizeof(void *) - 1];
 };
 
 typedef struct _VteScreen VteScreen;
@@ -509,6 +510,9 @@ public:
         void connect_pty_write();
         void disconnect_pty_write();
 
+        void pty_termios_changed();
+        void pty_scroll_lock_changed(bool locked);
+
         void watch_child (GPid child_pid);
         bool spawn_sync(VtePtyFlags pty_flags,
                         const char *working_directory,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]