view gui/src/terminal/KPtyDevice.h @ 13647:ac3d9480292d

Renamed file.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Tue, 23 Aug 2011 18:22:27 +0200
parents gui/src/terminal/kptydevice.h@2e1f54803758
children 9b74f97919e1
line wrap: on
line source

/* This file is part of the KDE libraries

    Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>

    This library 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 library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#ifndef kptydev_h
#define kptydev_h

struct KPtyDevicePrivate;

#include "KPty.h"
#include <QtCore/QIODevice>
#include <QSocketNotifier>

#define Q_DECLARE_PRIVATE_MI(Class, SuperClass) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(SuperClass::d_ptr); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(SuperClass::d_ptr); } \
    friend class Class##Private;

/**
 * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
 */
class KPtyDevice:public QIODevice, public KPty
{
Q_OBJECT Q_DECLARE_PRIVATE_MI (KPtyDevice, KPty) public:

    /**
     * Constructor
     */
  KPtyDevice (QObject * parent = 0);

    /**
     * Destructor:
     *
     *  If the pty is still open, it will be closed. Note, however, that
     *  an utmp registration is @em not undone.
     */
  virtual ~ KPtyDevice ();

    /**
     * Create a pty master/slave pair.
     *
     * @return true if a pty pair was successfully opened
     */
  virtual bool open (OpenMode mode = ReadWrite | Unbuffered);

    /**
     * Open using an existing pty master. The ownership of the fd
     * remains with the caller, i.e., close() will not close the fd.
     *
     * This is useful if you wish to attach a secondary "controller" to an
     * existing pty device such as a terminal widget.
     * Note that you will need to use setSuspended() on both devices to
     * control which one gets the incoming data from the pty.
     *
     * @param fd an open pty master file descriptor.
     * @param mode the device mode to open the pty with.
     * @return true if a pty pair was successfully opened
     */
  bool open (int fd, OpenMode mode = ReadWrite | Unbuffered);

    /**
     * Close the pty master/slave pair.
     */
  virtual void close ();

    /**
     * @reimp
     */
  bool canReadLine () const;

    /**
     * @reimp
     */
  bool atEnd () const;

    /**
     * @reimp
     */
  qint64 bytesAvailable () const;

    /**
     * @reimp
     */
  qint64 bytesToWrite () const;

protected:
  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 ())};

#define KMAXINT ((int)(~0U >> 1))

/////////////////////////////////////////////////////
// Helper. Remove when QRingBuffer becomes public. //
/////////////////////////////////////////////////////

#include <QtCore/qbytearray.h>
#include <QtCore/qlinkedlist.h>

#define CHUNKSIZE 4096

class KRingBuffer
{
public:
  KRingBuffer ()
  {
    clear ();
  }

  void clear ()
  {
    buffers.clear ();
    QByteArray tmp;
    tmp.resize (CHUNKSIZE);
    buffers << tmp;
    head = tail = 0;
    totalSize = 0;
  }

  inline bool isEmpty () const
  {
    return buffers.count () == 1 && !tail;
  }

  inline int size () const
  {
    return totalSize;
  }

  inline int readSize () const
  {
    return (buffers.count () == 1 ? tail : buffers.first ().size ()) - head;
  }

  inline const char *readPointer () const
  {
    Q_ASSERT (totalSize > 0);
    return buffers.first ().constData () + head;
  }

  void free (int bytes)
  {
    totalSize -= bytes;
    Q_ASSERT (totalSize >= 0);

    forever
    {
      int nbs = readSize ();

      if (bytes < nbs)
	{
	  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.removeFirst ();
      head = 0;
    }
  }

  char *reserve (int bytes)
  {
    totalSize += bytes;

    char *ptr;
    if (tail + bytes <= buffers.last ().size ())
      {
	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;
      }
    return ptr;
  }

  // release a trailing part of the last reservation
  inline void unreserve (int bytes)
  {
    totalSize -= bytes;
    tail -= bytes;
  }

  inline void write (const char *data, int len)
  {
    memcpy (reserve (len), data, len);
  }

  // Find the first occurrence of c and return the index after it.
  // If c is not found until maxLength, maxLength is returned, provided
  // it is smaller than the buffer size. Otherwise -1 is returned.
  int indexAfter (char c, int maxLength = KMAXINT) const
  {
    int index = 0;
    int start = head;
      QLinkedList < QByteArray >::ConstIterator it = buffers.begin ();
      forever
    {
      if (!maxLength)
	return index;
      if (index == size ())
	return -1;
      const QByteArray & buf = *it;
      ++it;
      int len = qMin ((it == buffers.end ()? tail : buf.size ()) - start,
		      maxLength);
      const char *ptr = buf.data () + start;
      if (const char *rptr = (const char *)memchr (ptr, c, len))
	return index + (rptr - ptr) + 1;
        index += len;
        maxLength -= len;
        start = 0;
    }
  }

  inline int lineSize (int maxLength = KMAXINT) const
  {
    return indexAfter ('\n', maxLength);
  }

  inline bool canReadLine () const
  {
    return lineSize () != -1;
  }

  int read (char *data, int maxLength)
  {
    int bytesToRead = qMin (size (), maxLength);
    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);
      }
    return readSoFar;
  }

  int readLine (char *data, int maxLength)
  {
    return read (data, lineSize (qMin (maxLength, size ())));
  }

private:
  QLinkedList < QByteArray > buffers;
  int head, tail;
  int totalSize;
};

struct KPtyDevicePrivate:public KPtyPrivate
{
  Q_DECLARE_PUBLIC (KPtyDevice)
    KPtyDevicePrivate (KPty * parent):KPtyPrivate (parent),
    emittedReadyRead (false), emittedBytesWritten (false),
    readNotifier (0), writeNotifier (0)
  {
  }

  bool _k_canRead ();
  bool _k_canWrite ();

  bool doWait (int msecs, bool reading);
  void finishOpen (QIODevice::OpenMode mode);

  bool emittedReadyRead;
  bool emittedBytesWritten;
  QSocketNotifier *readNotifier;
  QSocketNotifier *writeNotifier;
  KRingBuffer readBuffer;
  KRingBuffer writeBuffer;
};

#endif