1/*
2 * This file is a part of QTerminal - http://gitorious.org/qterminal
3 *
4 * This file was un-linked from KDE and modified
5 * by Maxim Bourmistrov <maxim@unixconn.com>
6 *
7 */
8
9/* This file is part of the KDE libraries
10
11 Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Library General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Library General Public License for more details.
22
23 You should have received a copy of the GNU Library General Public License
24 along with this library; see the file COPYING.LIB. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 Boston, MA 02110-1301, USA.
27*/
28
29#ifndef kptydev_h
30#define kptydev_h
31
32#include "kpty_p.h"
33
34#include <QIODevice>
35
36#define KMAXINT ((int)(~0U >> 1))
37
38struct KPtyDevicePrivate;
39class QSocketNotifier;
40
41#define Q_DECLARE_PRIVATE_MI(Class, SuperClass) \
42 inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(SuperClass::d_ptr); } \
43 inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(SuperClass::d_ptr); } \
44 friend struct Class##Private;
45
46/**
47 * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
48 */
49class KPtyDevice : public QIODevice, public KPty {
50 Q_OBJECT
51 Q_DECLARE_PRIVATE_MI(KPtyDevice, KPty)
52
53public:
54
55 /**
56 * Constructor
57 */
58 KPtyDevice(QObject *parent = 0);
59
60 /**
61 * Destructor:
62 *
63 * If the pty is still open, it will be closed. Note, however, that
64 * an utmp registration is @em not undone.
65 */
66 virtual ~KPtyDevice();
67
68 /**
69 * Create a pty master/slave pair.
70 *
71 * @return true if a pty pair was successfully opened
72 */
73 virtual bool open(OpenMode mode = ReadWrite | Unbuffered);
74
75 /**
76 * Open using an existing pty master. The ownership of the fd
77 * remains with the caller, i.e., close() will not close the fd.
78 *
79 * This is useful if you wish to attach a secondary "controller" to an
80 * existing pty device such as a terminal widget.
81 * Note that you will need to use setSuspended() on both devices to
82 * control which one gets the incoming data from the pty.
83 *
84 * @param fd an open pty master file descriptor.
85 * @param mode the device mode to open the pty with.
86 * @return true if a pty pair was successfully opened
87 */
88 bool open(int fd, OpenMode mode = ReadWrite | Unbuffered);
89
90 /**
91 * Close the pty master/slave pair.
92 */
93 virtual void close();
94
95 /**
96 * Sets whether the KPtyDevice monitors the pty for incoming data.
97 *
98 * When the KPtyDevice is suspended, it will no longer attempt to buffer
99 * data that becomes available from the pty and it will not emit any
100 * signals.
101 *
102 * Do not use on closed ptys.
103 * After a call to open(), the pty is not suspended. If you need to
104 * ensure that no data is read, call this function before the main loop
105 * is entered again (i.e., immediately after opening the pty).
106 */
107 void setSuspended(bool suspended);
108
109 /**
110 * Returns true if the KPtyDevice is not monitoring the pty for incoming
111 * data.
112 *
113 * Do not use on closed ptys.
114 *
115 * See setSuspended()
116 */
117 bool isSuspended() const;
118
119 /**
120 * @return always true
121 */
122 virtual bool isSequential() const;
123
124 /**
125 * @reimp
126 */
127 bool canReadLine() const;
128
129 /**
130 * @reimp
131 */
132 bool atEnd() const;
133
134 /**
135 * @reimp
136 */
137 qint64 bytesAvailable() const;
138
139 /**
140 * @reimp
141 */
142 qint64 bytesToWrite() const;
143
144 bool waitForBytesWritten(int msecs = -1);
145 bool waitForReadyRead(int msecs = -1);
146
147
148Q_SIGNALS:
149 /**
150 * Emitted when EOF is read from the PTY.
151 *
152 * Data may still remain in the buffers.
153 */
154 void readEof();
155
156protected:
157 virtual qint64 readData(char *data, qint64 maxSize);
158 virtual qint64 readLineData(char *data, qint64 maxSize);
159 virtual qint64 writeData(const char *data, qint64 maxSize);
160
161private:
162 Q_PRIVATE_SLOT(d_func(), bool _k_canRead())
163 Q_PRIVATE_SLOT(d_func(), bool _k_canWrite())
164};
165
166/////////////////////////////////////////////////////
167// Helper. Remove when QRingBuffer becomes public. //
168/////////////////////////////////////////////////////
169
170#include <QByteArray>
171#include <QLinkedList>
172
173#define CHUNKSIZE 4096
174
175class KRingBuffer
176{
177public:
178 KRingBuffer()
179 {
180 clear();
181 }
182
183 void clear()
184 {
185 buffers.clear();
186 QByteArray tmp;
187 tmp.resize(CHUNKSIZE);
188 buffers << tmp;
189 head = tail = 0;
190 totalSize = 0;
191 }
192
193 inline bool isEmpty() const
194 {
195 return buffers.count() == 1 && !tail;
196 }
197
198 inline int size() const
199 {
200 return totalSize;
201 }
202
203 inline int readSize() const
204 {
205 return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
206 }
207
208 inline const char *readPointer() const
209 {
210 Q_ASSERT(totalSize > 0);
211 return buffers.first().constData() + head;
212 }
213
214 void free(int bytes)
215 {
216 totalSize -= bytes;
217 Q_ASSERT(totalSize >= 0);
218
219 forever {
220 int nbs = readSize();
221
222 if (bytes < nbs) {
223 head += bytes;
224 if (head == tail && buffers.count() == 1) {
225 buffers.first().resize(CHUNKSIZE);
226 head = tail = 0;
227 }
228 break;
229 }
230
231 bytes -= nbs;
232 if (buffers.count() == 1) {
233 buffers.first().resize(CHUNKSIZE);
234 head = tail = 0;
235 break;
236 }
237
238 buffers.removeFirst();
239 head = 0;
240 }
241 }
242
243 char *reserve(int bytes)
244 {
245 totalSize += bytes;
246
247 char *ptr;
248 if (tail + bytes <= buffers.last().size()) {
249 ptr = buffers.last().data() + tail;
250 tail += bytes;
251 } else {
252 buffers.last().resize(tail);
253 QByteArray tmp;
254 tmp.resize(qMax(CHUNKSIZE, bytes));
255 ptr = tmp.data();
256 buffers << tmp;
257 tail = bytes;
258 }
259 return ptr;
260 }
261
262 // release a trailing part of the last reservation
263 inline void unreserve(int bytes)
264 {
265 totalSize -= bytes;
266 tail -= bytes;
267 }
268
269 inline void write(const char *data, int len)
270 {
271 memcpy(reserve(len), data, len);
272 }
273
274 // Find the first occurrence of c and return the index after it.
275 // If c is not found until maxLength, maxLength is returned, provided
276 // it is smaller than the buffer size. Otherwise -1 is returned.
277 int indexAfter(char c, int maxLength = KMAXINT) const
278 {
279 int index = 0;
280 int start = head;
281 QLinkedList<QByteArray>::ConstIterator it = buffers.constBegin();
282 forever {
283 if (!maxLength)
284 return index;
285 if (index == size())
286 return -1;
287 const QByteArray &buf = *it;
288 ++it;
289 int len = qMin((it == buffers.end() ? tail : buf.size()) - start,
290 maxLength);
291 const char *ptr = buf.data() + start;
292 if (const char *rptr = (const char *)memchr(ptr, c, len))
293 return index + (rptr - ptr) + 1;
294 index += len;
295 maxLength -= len;
296 start = 0;
297 }
298 }
299
300 inline int lineSize(int maxLength = KMAXINT) const
301 {
302 return indexAfter('\n', maxLength);
303 }
304
305 inline bool canReadLine() const
306 {
307 return lineSize() != -1;
308 }
309
310 int read(char *data, int maxLength)
311 {
312 int bytesToRead = qMin(size(), maxLength);
313 int readSoFar = 0;
314 while (readSoFar < bytesToRead) {
315 const char *ptr = readPointer();
316 int bs = qMin(bytesToRead - readSoFar, readSize());
317 memcpy(data + readSoFar, ptr, bs);
318 readSoFar += bs;
319 free(bs);
320 }
321 return readSoFar;
322 }
323
324 int readLine(char *data, int maxLength)
325 {
326 return read(data, lineSize(qMin(maxLength, size())));
327 }
328
329private:
330 QLinkedList<QByteArray> buffers;
331 int head, tail;
332 int totalSize;
333};
334
335struct KPtyDevicePrivate : public KPtyPrivate {
336
337 Q_DECLARE_PUBLIC(KPtyDevice)
338
339 KPtyDevicePrivate(KPty* parent) :
340 KPtyPrivate(parent),
341 emittedReadyRead(false), emittedBytesWritten(false),
342 readNotifier(0), writeNotifier(0)
343 {
344 }
345
346 bool _k_canRead();
347 bool _k_canWrite();
348
349 bool doWait(int msecs, bool reading);
350 void finishOpen(QIODevice::OpenMode mode);
351
352 bool emittedReadyRead;
353 bool emittedBytesWritten;
354 QSocketNotifier *readNotifier;
355 QSocketNotifier *writeNotifier;
356 KRingBuffer readBuffer;
357 KRingBuffer writeBuffer;
358};
359
360#endif
361
362