1 | /* |
2 | This file is part of Konsole, an X terminal. |
3 | Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; either version 2 of the License, or |
8 | (at your option) any later version. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | 02110-1301 USA. |
19 | */ |
20 | |
21 | #ifndef HISTORY_H |
22 | #define HISTORY_H |
23 | |
24 | // System |
25 | #include <sys/mman.h> |
26 | |
27 | // Qt |
28 | #include <QtCore/QList> |
29 | #include <QtCore/QVector> |
30 | #include <QtCore/QTemporaryFile> |
31 | |
32 | #include "konsole_export.h" |
33 | |
34 | // Konsole |
35 | #include "Character.h" |
36 | |
37 | namespace Konsole |
38 | { |
39 | /* |
40 | An extendable tmpfile(1) based buffer. |
41 | */ |
42 | |
43 | class HistoryFile |
44 | { |
45 | public: |
46 | HistoryFile(); |
47 | virtual ~HistoryFile(); |
48 | |
49 | virtual void add(const unsigned char* bytes, int len); |
50 | virtual void get(unsigned char* bytes, int len, int loc); |
51 | virtual int len() const; |
52 | |
53 | //mmaps the file in read-only mode |
54 | void map(); |
55 | //un-mmaps the file |
56 | void unmap(); |
57 | //returns true if the file is mmap'ed |
58 | bool isMapped() const; |
59 | |
60 | |
61 | private: |
62 | int _fd; |
63 | int _length; |
64 | QTemporaryFile _tmpFile; |
65 | |
66 | //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed |
67 | char* _fileMap; |
68 | |
69 | //incremented whenever 'add' is called and decremented whenever |
70 | //'get' is called. |
71 | //this is used to detect when a large number of lines are being read and processed from the history |
72 | //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). |
73 | int _readWriteBalance; |
74 | |
75 | //when _readWriteBalance goes below this threshold, the file will be mmap'ed automatically |
76 | static const int MAP_THRESHOLD = -1000; |
77 | }; |
78 | |
79 | ////////////////////////////////////////////////////////////////////// |
80 | |
81 | ////////////////////////////////////////////////////////////////////// |
82 | // Abstract base class for file and buffer versions |
83 | ////////////////////////////////////////////////////////////////////// |
84 | class HistoryType; |
85 | |
86 | class HistoryScroll |
87 | { |
88 | public: |
89 | explicit HistoryScroll(HistoryType*); |
90 | virtual ~HistoryScroll(); |
91 | |
92 | virtual bool hasScroll(); |
93 | |
94 | // access to history |
95 | virtual int getLines() = 0; |
96 | virtual int getLineLen(int lineno) = 0; |
97 | virtual void getCells(int lineno, int colno, int count, Character res[]) = 0; |
98 | virtual bool isWrappedLine(int lineno) = 0; |
99 | |
100 | // adding lines. |
101 | virtual void addCells(const Character a[], int count) = 0; |
102 | // convenience method - this is virtual so that subclasses can take advantage |
103 | // of QVector's implicit copying |
104 | virtual void addCellsVector(const QVector<Character>& cells) { |
105 | addCells(cells.data(), cells.size()); |
106 | } |
107 | |
108 | virtual void addLine(bool previousWrapped = false) = 0; |
109 | |
110 | // |
111 | // FIXME: Passing around constant references to HistoryType instances |
112 | // is very unsafe, because those references will no longer |
113 | // be valid if the history scroll is deleted. |
114 | // |
115 | const HistoryType& getType() const { |
116 | return *_historyType; |
117 | } |
118 | |
119 | protected: |
120 | HistoryType* _historyType; |
121 | }; |
122 | |
123 | ////////////////////////////////////////////////////////////////////// |
124 | // File-based history (e.g. file log, no limitation in length) |
125 | ////////////////////////////////////////////////////////////////////// |
126 | |
127 | class KONSOLEPRIVATE_EXPORT HistoryScrollFile : public HistoryScroll |
128 | { |
129 | public: |
130 | explicit HistoryScrollFile(const QString& logFileName); |
131 | virtual ~HistoryScrollFile(); |
132 | |
133 | virtual int getLines(); |
134 | virtual int getLineLen(int lineno); |
135 | virtual void getCells(int lineno, int colno, int count, Character res[]); |
136 | virtual bool isWrappedLine(int lineno); |
137 | |
138 | virtual void addCells(const Character a[], int count); |
139 | virtual void addLine(bool previousWrapped = false); |
140 | |
141 | private: |
142 | int startOfLine(int lineno); |
143 | |
144 | HistoryFile _index; // lines Row(int) |
145 | HistoryFile _cells; // text Row(Character) |
146 | HistoryFile _lineflags; // flags Row(unsigned char) |
147 | }; |
148 | |
149 | ////////////////////////////////////////////////////////////////////// |
150 | // Nothing-based history (no history :-) |
151 | ////////////////////////////////////////////////////////////////////// |
152 | class KONSOLEPRIVATE_EXPORT HistoryScrollNone : public HistoryScroll |
153 | { |
154 | public: |
155 | HistoryScrollNone(); |
156 | virtual ~HistoryScrollNone(); |
157 | |
158 | virtual bool hasScroll(); |
159 | |
160 | virtual int getLines(); |
161 | virtual int getLineLen(int lineno); |
162 | virtual void getCells(int lineno, int colno, int count, Character res[]); |
163 | virtual bool isWrappedLine(int lineno); |
164 | |
165 | virtual void addCells(const Character a[], int count); |
166 | virtual void addLine(bool previousWrapped = false); |
167 | }; |
168 | |
169 | ////////////////////////////////////////////////////////////////////// |
170 | // History using compact storage |
171 | // This implementation uses a list of fixed-sized blocks |
172 | // where history lines are allocated in (avoids heap fragmentation) |
173 | ////////////////////////////////////////////////////////////////////// |
174 | typedef QVector<Character> TextLine; |
175 | |
176 | class CharacterFormat |
177 | { |
178 | public: |
179 | bool equalsFormat(const CharacterFormat& other) const { |
180 | return (other.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && other.fgColor == fgColor && other.bgColor == bgColor; |
181 | } |
182 | |
183 | bool equalsFormat(const Character& c) const { |
184 | return (c.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && c.foregroundColor == fgColor && c.backgroundColor == bgColor; |
185 | } |
186 | |
187 | void setFormat(const Character& c) { |
188 | rendition = c.rendition; |
189 | fgColor = c.foregroundColor; |
190 | bgColor = c.backgroundColor; |
191 | isRealCharacter = c.isRealCharacter; |
192 | } |
193 | |
194 | CharacterColor fgColor, bgColor; |
195 | quint16 startPos; |
196 | quint8 rendition; |
197 | bool isRealCharacter; |
198 | }; |
199 | |
200 | class CompactHistoryBlock |
201 | { |
202 | public: |
203 | CompactHistoryBlock() { |
204 | _blockLength = 4096 * 64; // 256kb |
205 | _head = (quint8*) mmap(0, _blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
206 | //_head = (quint8*) malloc(_blockLength); |
207 | Q_ASSERT(_head != MAP_FAILED); |
208 | _tail = _blockStart = _head; |
209 | _allocCount = 0; |
210 | } |
211 | |
212 | virtual ~CompactHistoryBlock() { |
213 | //free(_blockStart); |
214 | munmap(_blockStart, _blockLength); |
215 | } |
216 | |
217 | virtual unsigned int remaining() { |
218 | return _blockStart + _blockLength - _tail; |
219 | } |
220 | virtual unsigned length() { |
221 | return _blockLength; |
222 | } |
223 | virtual void* allocate(size_t length); |
224 | virtual bool contains(void* addr) { |
225 | return addr >= _blockStart && addr < (_blockStart + _blockLength); |
226 | } |
227 | virtual void deallocate(); |
228 | virtual bool isInUse() { |
229 | return _allocCount != 0; |
230 | }; |
231 | |
232 | private: |
233 | size_t _blockLength; |
234 | quint8* _head; |
235 | quint8* _tail; |
236 | quint8* _blockStart; |
237 | int _allocCount; |
238 | }; |
239 | |
240 | class CompactHistoryBlockList |
241 | { |
242 | public: |
243 | CompactHistoryBlockList() {} |
244 | ~CompactHistoryBlockList(); |
245 | |
246 | void* allocate(size_t size); |
247 | void deallocate(void *); |
248 | int length() { |
249 | return list.size(); |
250 | } |
251 | private: |
252 | QList<CompactHistoryBlock*> list; |
253 | }; |
254 | |
255 | class CompactHistoryLine |
256 | { |
257 | public: |
258 | CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList); |
259 | virtual ~CompactHistoryLine(); |
260 | |
261 | // custom new operator to allocate memory from custom pool instead of heap |
262 | static void* operator new(size_t size, CompactHistoryBlockList& blockList); |
263 | static void operator delete(void *) { |
264 | /* do nothing, deallocation from pool is done in destructor*/ |
265 | }; |
266 | |
267 | virtual void getCharacters(Character* array, int length, int startColumn); |
268 | virtual void getCharacter(int index, Character& r); |
269 | virtual bool isWrapped() const { |
270 | return _wrapped; |
271 | }; |
272 | virtual void setWrapped(bool value) { |
273 | _wrapped = value; |
274 | }; |
275 | virtual unsigned int getLength() const { |
276 | return _length; |
277 | }; |
278 | |
279 | protected: |
280 | CompactHistoryBlockList& _blockListRef; |
281 | CharacterFormat* _formatArray; |
282 | quint16 _length; |
283 | quint16* _text; |
284 | quint16 _formatLength; |
285 | bool _wrapped; |
286 | }; |
287 | |
288 | class KONSOLEPRIVATE_EXPORT CompactHistoryScroll : public HistoryScroll |
289 | { |
290 | typedef QList<CompactHistoryLine*> HistoryArray; |
291 | |
292 | public: |
293 | explicit CompactHistoryScroll(unsigned int maxNbLines = 1000); |
294 | virtual ~CompactHistoryScroll(); |
295 | |
296 | virtual int getLines(); |
297 | virtual int getLineLen(int lineno); |
298 | virtual void getCells(int lineno, int colno, int count, Character res[]); |
299 | virtual bool isWrappedLine(int lineno); |
300 | |
301 | virtual void addCells(const Character a[], int count); |
302 | virtual void addCellsVector(const TextLine& cells); |
303 | virtual void addLine(bool previousWrapped = false); |
304 | |
305 | void setMaxNbLines(unsigned int nbLines); |
306 | |
307 | private: |
308 | bool hasDifferentColors(const TextLine& line) const; |
309 | HistoryArray _lines; |
310 | CompactHistoryBlockList _blockList; |
311 | |
312 | unsigned int _maxLineCount; |
313 | }; |
314 | |
315 | ////////////////////////////////////////////////////////////////////// |
316 | // History type |
317 | ////////////////////////////////////////////////////////////////////// |
318 | |
319 | class KONSOLEPRIVATE_EXPORT HistoryType |
320 | { |
321 | public: |
322 | HistoryType(); |
323 | virtual ~HistoryType(); |
324 | |
325 | /** |
326 | * Returns true if the history is enabled ( can store lines of output ) |
327 | * or false otherwise. |
328 | */ |
329 | virtual bool isEnabled() const = 0; |
330 | /** |
331 | * Returns the maximum number of lines which this history type |
332 | * can store or -1 if the history can store an unlimited number of lines. |
333 | */ |
334 | virtual int maximumLineCount() const = 0; |
335 | /** |
336 | * Converts from one type of HistoryScroll to another or if given the |
337 | * same type, returns it. |
338 | */ |
339 | virtual HistoryScroll* scroll(HistoryScroll *) const = 0; |
340 | /** |
341 | * Returns true if the history size is unlimited. |
342 | */ |
343 | bool isUnlimited() const { |
344 | return maximumLineCount() == -1; |
345 | } |
346 | }; |
347 | |
348 | class KONSOLEPRIVATE_EXPORT HistoryTypeNone : public HistoryType |
349 | { |
350 | public: |
351 | HistoryTypeNone(); |
352 | |
353 | virtual bool isEnabled() const; |
354 | virtual int maximumLineCount() const; |
355 | |
356 | virtual HistoryScroll* scroll(HistoryScroll *) const; |
357 | }; |
358 | |
359 | class KONSOLEPRIVATE_EXPORT HistoryTypeFile : public HistoryType |
360 | { |
361 | public: |
362 | explicit HistoryTypeFile(const QString& fileName = QString()); |
363 | |
364 | virtual bool isEnabled() const; |
365 | virtual int maximumLineCount() const; |
366 | |
367 | virtual HistoryScroll* scroll(HistoryScroll *) const; |
368 | |
369 | protected: |
370 | QString _fileName; |
371 | }; |
372 | |
373 | class KONSOLEPRIVATE_EXPORT CompactHistoryType : public HistoryType |
374 | { |
375 | public: |
376 | explicit CompactHistoryType(unsigned int size); |
377 | |
378 | virtual bool isEnabled() const; |
379 | virtual int maximumLineCount() const; |
380 | |
381 | virtual HistoryScroll* scroll(HistoryScroll *) const; |
382 | |
383 | protected: |
384 | unsigned int _maxLines; |
385 | }; |
386 | } |
387 | |
388 | #endif // HISTORY_H |
389 | |