changeset 13667:9b74f97919e1

Fixed KPtyDevice.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Sat, 10 Sep 2011 15:36:02 +0200
parents 68c50b393f1d
children 421afeae929b
files gui/src/TerminalHighlighter.cpp gui/src/TerminalView.cpp gui/src/TerminalView.h gui/src/terminal/KPtyDevice.cpp gui/src/terminal/KPtyDevice.h
diffstat 5 files changed, 250 insertions(+), 79 deletions(-) [+]
line wrap: on
line diff
--- a/gui/src/TerminalHighlighter.cpp	Sat Sep 10 09:38:49 2011 +0200
+++ b/gui/src/TerminalHighlighter.cpp	Sat Sep 10 15:36:02 2011 +0200
@@ -27,7 +27,7 @@
   keywordFormat.setForeground(Qt::darkBlue);
   QStringList keywordPatterns
       = QString(ResourceManager::instance ()->octaveKeywords ()).split(" ", QString::SkipEmptyParts);
-  keywordPatterns << "GNU" << "Octave" << "OctaveGUI";
+   keywordPatterns << "GNU" << "Octave" << "OctaveGUI";
 
   for (int i = 0; i < keywordPatterns.size (); i++)
     keywordPatterns.replace(i, QString("\\b%1\\b").arg(keywordPatterns.at (i)));
@@ -71,7 +71,6 @@
   rule.pattern = QRegExp("^\\s+\\*\\*.+$");
   rule.format = captionFormat;
   highlightingRules.append(rule);
-
 }
 
 void TerminalHighlighter::highlightBlock(const QString &text)
--- a/gui/src/TerminalView.cpp	Sat Sep 10 09:38:49 2011 +0200
+++ b/gui/src/TerminalView.cpp	Sat Sep 10 15:36:02 2011 +0200
@@ -69,3 +69,17 @@
   // TODO: Pass mouse events to the terminal emulation.
   mouseEvent->accept();
 }
+
+void
+TerminalView::mouseDoubleClickEvent (QMouseEvent *mouseEvent)
+{
+  // TODO: Pass mouse events to the terminal emulation.
+  mouseEvent->accept();
+}
+
+void
+TerminalView::wheelEvent (QWheelEvent *wheelEvent)
+{
+  // TODO: Pass mouse events to the terminal emulation.
+  wheelEvent->accept();
+}
--- a/gui/src/TerminalView.h	Sat Sep 10 09:38:49 2011 +0200
+++ b/gui/src/TerminalView.h	Sat Sep 10 15:36:02 2011 +0200
@@ -43,6 +43,8 @@
 protected:
   void keyPressEvent (QKeyEvent *keyEvent);
   void mousePressEvent (QMouseEvent *mouseEvent);
+  void mouseDoubleClickEvent (QMouseEvent *mouseEvent);
+  void wheelEvent (QWheelEvent *wheelEvent);
 
 private:
   TerminalEmulation *m_terminalEmulation;
--- a/gui/src/terminal/KPtyDevice.cpp	Sat Sep 10 09:38:49 2011 +0200
+++ b/gui/src/terminal/KPtyDevice.cpp	Sat Sep 10 15:36:02 2011 +0200
@@ -22,6 +22,7 @@
 */
 
 #include "KPtyDevice.h"
+#define i18n
 
 #include <QtCore/QSocketNotifier>
 
@@ -31,8 +32,23 @@
 #include <termios.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 
+#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
+  // "the other end's output queue size" - kinda braindead, huh?
+#define PTY_BYTES_AVAILABLE TIOCOUTQ
+#elif defined(TIOCINQ)
+  // "our end's input queue size"
 #define PTY_BYTES_AVAILABLE TIOCINQ
+#else
+  // likewise. more generic ioctl (theoretically)
+#define PTY_BYTES_AVAILABLE FIONREAD
+#endif
 
 //////////////////
 // private data //
@@ -61,36 +77,75 @@
   Q_Q (KPtyDevice);
   qint64 readBytes = 0;
 
-
+#ifdef Q_OS_IRIX		// this should use a config define, but how to check it?
+  size_t available;
+#else
   int available;
+#endif
   if (!::ioctl (q->masterFd (), PTY_BYTES_AVAILABLE, (char *) &available))
     {
+#ifdef Q_OS_SOLARIS
+      // A Pty is a STREAMS module, and those can be activated
+      // with 0 bytes available. This happens either when ^C is
+      // pressed, or when an application does an explicit write(a,b,0)
+      // which happens in experiments fairly often. When 0 bytes are
+      // available, you must read those 0 bytes to clear the STREAMS
+      // module, but we don't want to hit the !readBytes case further down.
+      if (!available)
+        {
+          char c;
+          // Read the 0-byte STREAMS message
+          NO_INTR (readBytes, read (q->masterFd (), &c, 0));
+          // Should return 0 bytes read; -1 is error
+          if (readBytes < 0)
+            {
+              readNotifier->setEnabled (false);
+              emit q->readEof ();
+              return false;
+            }
+          return true;
+        }
+#endif
+
       char *ptr = readBuffer.reserve (available);
-	// Useless block braces except in Solaris
-	{
-	  NO_INTR (readBytes, read (q->masterFd (), ptr, available));
-	}
+#ifdef Q_OS_SOLARIS
+      // Even if available > 0, it is possible for read()
+      // to return 0 on Solaris, due to 0-byte writes in the stream.
+      // Ignore them and keep reading until we hit *some* data.
+      // In Solaris it is possible to have 15 bytes available
+      // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads.
+      // Because the stream is set to O_NONBLOCK in finishOpen(),
+      // an EOF read will return -1.
+      readBytes = 0;
+      while (!readBytes)
+#endif
+        // Useless block braces except in Solaris
+        {
+          NO_INTR (readBytes, read (q->masterFd (), ptr, available));
+        }
       if (readBytes < 0)
-	{
-	  readBuffer.unreserve (available);
-	  return false;
-	}
+        {
+          readBuffer.unreserve (available);
+          q->setErrorString (i18n ("Error reading from PTY"));
+          return false;
+        }
       readBuffer.unreserve (available - readBytes);	// *should* be a no-op
     }
 
   if (!readBytes)
     {
       readNotifier->setEnabled (false);
+      emit q->readEof ();
       return false;
     }
   else
     {
       if (!emittedReadyRead)
-	{
-	  emittedReadyRead = true;
-	  emit q->readyRead ();
-	  emittedReadyRead = false;
-	}
+        {
+          emittedReadyRead = true;
+          emit q->readyRead ();
+          emittedReadyRead = false;
+        }
       return true;
     }
 }
@@ -107,10 +162,11 @@
   qt_ignore_sigpipe ();
   int wroteBytes;
   NO_INTR (wroteBytes,
-	   write (q->masterFd (),
-		  writeBuffer.readPointer (), writeBuffer.readSize ()));
+           write (q->masterFd (),
+                  writeBuffer.readPointer (), writeBuffer.readSize ()));
   if (wroteBytes < 0)
     {
+      q->setErrorString (i18n ("Error writing to PTY"));
       return false;
     }
   writeBuffer.free (wroteBytes);
@@ -153,6 +209,9 @@
 KPtyDevicePrivate::doWait (int msecs, bool reading)
 {
   Q_Q (KPtyDevice);
+#ifndef __linux__
+  struct timeval etv;
+#endif
   struct timeval tv, *tvp;
 
   if (msecs < 0)
@@ -161,6 +220,10 @@
     {
       tv.tv_sec = msecs / 1000;
       tv.tv_usec = (msecs % 1000) * 1000;
+#ifndef __linux__
+      gettimeofday (&etv, 0);
+      timeradd (&tv, &etv, &etv);
+#endif
       tvp = &tv;
     }
 
@@ -173,33 +236,44 @@
       FD_ZERO (&wfds);
 
       if (readNotifier->isEnabled ())
-	FD_SET (q->masterFd (), &rfds);
+        FD_SET (q->masterFd (), &rfds);
       if (!writeBuffer.isEmpty ())
-	FD_SET (q->masterFd (), &wfds);
+        FD_SET (q->masterFd (), &wfds);
+
+#ifndef __linux__
+      if (tvp)
+        {
+          gettimeofday (&tv, 0);
+          timersub (&etv, &tv, &tv);
+          if (tv.tv_sec < 0)
+            tv.tv_sec = tv.tv_usec = 0;
+        }
+#endif
 
       switch (select (q->masterFd () + 1, &rfds, &wfds, 0, tvp))
-	{
-	case -1:
-	  if (errno == EINTR)
-	    break;
-	  return false;
-	case 0:
-	  return false;
-	default:
-	  if (FD_ISSET (q->masterFd (), &rfds))
-	    {
-	      bool canRead = _k_canRead ();
-	      if (reading && canRead)
-		return true;
-	    }
-	  if (FD_ISSET (q->masterFd (), &wfds))
-	    {
-	      bool canWrite = _k_canWrite ();
-	      if (!reading)
-		return canWrite;
-	    }
-	  break;
-	}
+        {
+        case -1:
+          if (errno == EINTR)
+            break;
+          return false;
+        case 0:
+          q->setErrorString (i18n ("PTY operation timed out"));
+          return false;
+        default:
+          if (FD_ISSET (q->masterFd (), &rfds))
+            {
+              bool canRead = _k_canRead ();
+              if (reading && canRead)
+                return true;
+            }
+          if (FD_ISSET (q->masterFd (), &wfds))
+            {
+              bool canWrite = _k_canWrite ();
+              if (!reading)
+                return canWrite;
+            }
+          break;
+        }
     }
   return false;
 }
@@ -217,12 +291,16 @@
   writeNotifier =
     new QSocketNotifier (q->masterFd (), QSocketNotifier::Write, q);
   QObject::connect (readNotifier, SIGNAL (activated (int)), q,
-		    SLOT (_k_canRead ()));
+                    SLOT (_k_canRead ()));
   QObject::connect (writeNotifier, SIGNAL (activated (int)), q,
-		    SLOT (_k_canWrite ()));
+                    SLOT (_k_canWrite ()));
   readNotifier->setEnabled (true);
 }
 
+/////////////////////////////
+// public member functions //
+/////////////////////////////
+
 KPtyDevice::KPtyDevice (QObject * parent):
 QIODevice (parent), KPty (new KPtyDevicePrivate (this))
 {
@@ -243,6 +321,7 @@
 
   if (!KPty::open ())
     {
+      setErrorString (i18n ("Error opening PTY"));
       return false;
     }
 
@@ -258,6 +337,7 @@
 
   if (!KPty::open (fd))
     {
+      setErrorString (i18n ("Error opening PTY"));
       return false;
     }
 
@@ -283,6 +363,12 @@
 }
 
 bool
+KPtyDevice::isSequential () const
+{
+  return true;
+}
+
+bool
 KPtyDevice::canReadLine () const
 {
   Q_D (const KPtyDevice);
@@ -310,6 +396,34 @@
   return d->writeBuffer.size ();
 }
 
+bool
+KPtyDevice::waitForReadyRead (int msecs)
+{
+  Q_D (KPtyDevice);
+  return d->doWait (msecs, true);
+}
+
+bool
+KPtyDevice::waitForBytesWritten (int msecs)
+{
+  Q_D (KPtyDevice);
+  return d->doWait (msecs, false);
+}
+
+void
+KPtyDevice::setSuspended (bool suspended)
+{
+  Q_D (KPtyDevice);
+  d->readNotifier->setEnabled (!suspended);
+}
+
+bool
+KPtyDevice::isSuspended () const
+{
+  Q_D (const KPtyDevice);
+  return !d->readNotifier->isEnabled ();
+}
+
 // protected
 qint64
 KPtyDevice::readData (char *data, qint64 maxlen)
@@ -324,7 +438,7 @@
 {
   Q_D (KPtyDevice);
   return d->readBuffer.readLine (data,
-				 (int) qMin < qint64 > (maxlen, KMAXINT));
+                                 (int) qMin < qint64 > (maxlen, KMAXINT));
 }
 
 // protected
--- a/gui/src/terminal/KPtyDevice.h	Sat Sep 10 09:38:49 2011 +0200
+++ b/gui/src/terminal/KPtyDevice.h	Sat Sep 10 15:36:02 2011 +0200
@@ -21,6 +21,7 @@
 #ifndef kptydev_h
 #define kptydev_h
 
+struct KPtyPrivate;
 struct KPtyDevicePrivate;
 
 #include "KPty.h"
@@ -36,7 +37,7 @@
  * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
  */
 class KPtyDevice:public QIODevice, public KPty
-{
+{				//krazy:exclude=dpointer (via macro)
 Q_OBJECT Q_DECLARE_PRIVATE_MI (KPtyDevice, KPty) public:
 
     /**
@@ -80,6 +81,35 @@
   virtual void close ();
 
     /**
+     * Sets whether the KPtyDevice monitors the pty for incoming data.
+     *
+     * When the KPtyDevice is suspended, it will no longer attempt to buffer
+     * data that becomes available from the pty and it will not emit any
+     * signals.
+     *
+     * Do not use on closed ptys.
+     * After a call to open(), the pty is not suspended. If you need to
+     * ensure that no data is read, call this function before the main loop
+     * is entered again (i.e., immediately after opening the pty).
+     */
+  void setSuspended (bool suspended);
+
+    /**
+     * Returns true if the KPtyDevice is not monitoring the pty for incoming
+     * data.
+     *
+     * Do not use on closed ptys.
+     *
+     * See setSuspended()
+     */
+  bool isSuspended () const;
+
+    /**
+     * @return always true
+     */
+  virtual bool isSequential () const;
+
+    /**
      * @reimp
      */
   bool canReadLine () const;
@@ -99,14 +129,26 @@
      */
   qint64 bytesToWrite () const;
 
+  bool waitForBytesWritten (int msecs = -1);
+  bool waitForReadyRead (int msecs = -1);
+
+
+    Q_SIGNALS:
+    /**
+     * Emitted when EOF is read from the PTY.
+     *
+     * Data may still remain in the buffers.
+     */
+  void readEof ();
+
 protected:
-  virtual qint64 readData (char *data, qint64 maxSize);
+    virtual qint64 readData (char *data, qint64 maxSize);
   virtual qint64 readLineData (char *data, qint64 maxSize);
   virtual qint64 writeData (const char *data, qint64 maxSize);
 
 private:
   Q_PRIVATE_SLOT (d_func (), bool _k_canRead ())
-  Q_PRIVATE_SLOT (d_func (), bool _k_canWrite ())};
+    Q_PRIVATE_SLOT (d_func (), bool _k_canWrite ())};
 
 #define KMAXINT ((int)(~0U >> 1))
 
@@ -168,23 +210,23 @@
       int nbs = readSize ();
 
       if (bytes < nbs)
-	{
-	  head += bytes;
-	  if (head == tail && buffers.count () == 1)
-	    {
-	      buffers.first ().resize (CHUNKSIZE);
-	      head = tail = 0;
-	    }
-	  break;
-	}
+        {
+          head += bytes;
+          if (head == tail && buffers.count () == 1)
+            {
+              buffers.first ().resize (CHUNKSIZE);
+              head = tail = 0;
+            }
+          break;
+        }
 
       bytes -= nbs;
       if (buffers.count () == 1)
-	{
-	  buffers.first ().resize (CHUNKSIZE);
-	  head = tail = 0;
-	  break;
-	}
+        {
+          buffers.first ().resize (CHUNKSIZE);
+          head = tail = 0;
+          break;
+        }
 
       buffers.removeFirst ();
       head = 0;
@@ -198,17 +240,17 @@
     char *ptr;
     if (tail + bytes <= buffers.last ().size ())
       {
-	ptr = buffers.last ().data () + tail;
-	tail += bytes;
+        ptr = buffers.last ().data () + tail;
+        tail += bytes;
       }
     else
       {
-	buffers.last ().resize (tail);
-	QByteArray tmp;
-	tmp.resize (qMax (CHUNKSIZE, bytes));
-	ptr = tmp.data ();
-	buffers << tmp;
-	tail = bytes;
+        buffers.last ().resize (tail);
+        QByteArray tmp;
+        tmp.resize (qMax (CHUNKSIZE, bytes));
+        ptr = tmp.data ();
+        buffers << tmp;
+        tail = bytes;
       }
     return ptr;
   }
@@ -236,16 +278,16 @@
       forever
     {
       if (!maxLength)
-	return index;
+        return index;
       if (index == size ())
-	return -1;
+        return -1;
       const QByteArray & buf = *it;
       ++it;
       int len = qMin ((it == buffers.end ()? tail : buf.size ()) - start,
-		      maxLength);
+                      maxLength);
       const char *ptr = buf.data () + start;
       if (const char *rptr = (const char *)memchr (ptr, c, len))
-	return index + (rptr - ptr) + 1;
+        return index + (rptr - ptr) + 1;
         index += len;
         maxLength -= len;
         start = 0;
@@ -268,11 +310,11 @@
     int readSoFar = 0;
     while (readSoFar < bytesToRead)
       {
-	const char *ptr = readPointer ();
-	int bs = qMin (bytesToRead - readSoFar, readSize ());
-	memcpy (data + readSoFar, ptr, bs);
-	readSoFar += bs;
-	free (bs);
+        const char *ptr = readPointer ();
+        int bs = qMin (bytesToRead - readSoFar, readSize ());
+        memcpy (data + readSoFar, ptr, bs);
+        readSoFar += bs;
+        free (bs);
       }
     return readSoFar;
   }