source: vendor/trolltech/current/src/kernel/qinternal.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 20.1 KB
Line 
1/****************************************************************************
2** $Id: qinternal.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of some internal classes
5**
6** Created : 010427
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "private/qinternal_p.h"
39#include "qwidget.h"
40#include "qpixmap.h"
41#include "qpainter.h"
42#include "qcleanuphandler.h"
43
44static QPixmap* qdb_shared_pixmap = 0;
45static QPixmap *qdb_force_pixmap = 0;
46static QSharedDoubleBuffer* qdb_owner = 0;
47
48QCleanupHandler<QPixmap> qdb_pixmap_cleanup;
49
50#ifdef Q_WS_MACX
51bool QSharedDoubleBuffer::dblbufr = FALSE;
52#else
53bool QSharedDoubleBuffer::dblbufr = TRUE;
54#endif
55
56
57/*
58 hardLimitWidth/Height: if >= 0, the maximum number of pixels that
59 get double buffered.
60
61 sharedLimitWidth/Height: if >= 0, the maximum number of pixels the
62 shared double buffer can keep.
63
64 For x with sharedLimitSize < x <= hardLimitSize, temporary buffers
65 are constructed.
66 */
67static const int hardLimitWidth = -1;
68static const int hardLimitHeight = -1;
69#if defined( Q_WS_QWS ) || defined( Q_WS_MAC9 )
70// Small in Qt/Embedded / Mac9 - 5K on 32bpp
71static const int sharedLimitWidth = 64;
72static const int sharedLimitHeight = 20;
73#else
74// 240K on 32bpp
75static const int sharedLimitWidth = 640;
76static const int sharedLimitHeight = 100;
77#endif
78
79// *******************************************************************
80// QSharedDoubleBufferCleaner declaration and implementation
81// *******************************************************************
82
83/* \internal
84 This class is responsible for cleaning up the pixmaps created by the
85 QSharedDoubleBuffer class. When QSharedDoubleBuffer creates a
86 pixmap larger than the shared limits, this class deletes it after a
87 specified amount of time.
88
89 When the large pixmap is created/used, you must call start(). If the
90 large pixmap is ever deleted, you must call stop(). The start()
91 method always restarts the timer, so if the large pixmap is
92 constantly in use, the timer will never fire, and the pixmap will
93 not be constantly created and destroyed.
94*/
95
96static const int shared_double_buffer_cleanup_timeout = 30000; // 30 seconds
97
98// declaration
99
100class QSharedDoubleBufferCleaner : public QObject
101{
102public:
103 QSharedDoubleBufferCleaner( void );
104
105 void start( void );
106 void stop( void );
107
108 void doCleanup( void );
109
110 bool event( QEvent *e );
111
112private:
113 int timer_id;
114};
115
116// implementation
117
118/* \internal
119 Creates a QSharedDoubleBufferCleaner object. The timer is not
120 started when creating the object.
121*/
122QSharedDoubleBufferCleaner::QSharedDoubleBufferCleaner( void )
123 : QObject( 0, "internal shared double buffer cleanup object" ),
124 timer_id( -1 )
125{
126}
127
128/* \internal
129 Starts the cleanup timer. Any previously running timer is stopped.
130*/
131void QSharedDoubleBufferCleaner::start( void )
132{
133 stop();
134 timer_id = startTimer( shared_double_buffer_cleanup_timeout );
135}
136
137/* \internal
138 Stops the cleanup timer, if it is running.
139*/
140void QSharedDoubleBufferCleaner::stop( void )
141{
142 if ( timer_id != -1 )
143 killTimer( timer_id );
144 timer_id = -1;
145}
146
147/* \internal
148 */
149void QSharedDoubleBufferCleaner::doCleanup( void )
150{
151 qdb_pixmap_cleanup.remove( &qdb_force_pixmap );
152 delete qdb_force_pixmap;
153 qdb_force_pixmap = 0;
154}
155
156/* \internal
157 Event handler reimplementation. Calls doCleanup() when the timer
158 fires.
159*/
160bool QSharedDoubleBufferCleaner::event( QEvent *e )
161{
162 if ( e->type() != QEvent::Timer )
163 return FALSE;
164
165 QTimerEvent *event = (QTimerEvent *) e;
166 if ( event->timerId() == timer_id ) {
167 doCleanup();
168 stop();
169 }
170#ifdef QT_CHECK_STATE
171 else {
172 qWarning( "QSharedDoubleBufferCleaner::event: invalid timer event received." );
173 return FALSE;
174 }
175#endif // QT_CHECK_STATE
176
177 return TRUE;
178}
179
180// static instance
181static QSharedDoubleBufferCleaner *static_cleaner = 0;
182QSingleCleanupHandler<QSharedDoubleBufferCleaner> cleanup_static_cleaner;
183
184inline static QSharedDoubleBufferCleaner *staticCleaner()
185{
186 if ( ! static_cleaner ) {
187 static_cleaner = new QSharedDoubleBufferCleaner();
188 cleanup_static_cleaner.set( &static_cleaner );
189 }
190 return static_cleaner;
191}
192
193
194// *******************************************************************
195// QSharedDoubleBuffer implementation
196// *******************************************************************
197
198/* \internal
199 \enum DoubleBufferFlags
200
201 \value InitBG initialize the background of the double buffer.
202
203 \value Force disable shared buffer size limits.
204
205 \value Default InitBG and Force are used by default.
206*/
207
208/* \internal
209 \enum DoubleBufferState
210
211 \value Active indicates that the buffer may be used.
212
213 \value BufferActive indicates that painting with painter() will be
214 double buffered.
215
216 \value ExternalPainter indicates that painter() will return a
217 painter that was not created by QSharedDoubleBuffer.
218*/
219
220/* \internal
221 \class QSharedDoubleBuffer
222
223 This class provides a single, reusable double buffer. This class
224 is used internally by Qt widgets that need double buffering, which
225 prevents each individual widget form creating a double buffering
226 pixmap.
227
228 Using a single pixmap double buffer and sharing it across all
229 widgets is nicer on window system resources.
230*/
231
232/* \internal
233 Creates a QSharedDoubleBuffer with flags \f.
234
235 \sa DoubleBufferFlags
236*/
237QSharedDoubleBuffer::QSharedDoubleBuffer( DBFlags f )
238 : wid( 0 ), rx( 0 ), ry( 0 ), rw( 0 ), rh( 0 ), flags( f ), state( 0 ),
239 p( 0 ), external_p( 0 ), pix( 0 )
240{
241}
242
243/* \internal
244 Creates a QSharedDoubleBuffer with flags \f. The \a widget, \a x,
245 \a y, \a w and \a h arguments are passed to begin().
246
247 \sa DoubleBufferFlags begin()
248*/
249QSharedDoubleBuffer::QSharedDoubleBuffer( QWidget* widget,
250 int x, int y, int w, int h,
251 DBFlags f )
252 : wid( 0 ), rx( 0 ), ry( 0 ), rw( 0 ), rh( 0 ), flags( f ), state( 0 ),
253 p( 0 ), external_p( 0 ), pix( 0 )
254{
255 begin( widget, x, y, w, h );
256}
257
258/* \internal
259 Creates a QSharedDoubleBuffer with flags \f. The \a painter, \a x,
260 \a y, \a w and \a h arguments are passed to begin().
261
262 \sa DoubleBufferFlags begin()
263*/
264QSharedDoubleBuffer::QSharedDoubleBuffer( QPainter* painter,
265 int x, int y, int w, int h,
266 DBFlags f)
267 : wid( 0 ), rx( 0 ), ry( 0 ), rw( 0 ), rh( 0 ), flags( f ), state( 0 ),
268 p( 0 ), external_p( 0 ), pix( 0 )
269{
270 begin( painter, x, y, w, h );
271}
272
273/* \internal
274 Creates a QSharedDoubleBuffer with flags \f. The \a widget and
275 \a r arguments are passed to begin().
276
277 \sa DoubleBufferFlags begin()
278*/
279QSharedDoubleBuffer::QSharedDoubleBuffer( QWidget *widget, const QRect &r, DBFlags f )
280 : wid( 0 ), rx( 0 ), ry( 0 ), rw( 0 ), rh( 0 ), flags( f ), state( 0 ),
281 p( 0 ), external_p( 0 ), pix( 0 )
282{
283 begin( widget, r );
284}
285
286/* \internal
287 Creates a QSharedDoubleBuffer with flags \f. The \a painter and
288 \a r arguments are passed to begin().
289
290 \sa DoubleBufferFlags begin()
291*/
292QSharedDoubleBuffer::QSharedDoubleBuffer( QPainter *painter, const QRect &r, DBFlags f )
293 : wid( 0 ), rx( 0 ), ry( 0 ), rw( 0 ), rh( 0 ), flags( f ), state( 0 ),
294 p( 0 ), external_p( 0 ), pix( 0 )
295{
296 begin( painter, r );
297}
298
299/* \internal
300 Destructs the QSharedDoubleBuffer and calls end() if the buffer is
301 active.
302
303 \sa isActive() end()
304*/
305QSharedDoubleBuffer::~QSharedDoubleBuffer()
306{
307 if ( isActive() )
308 end();
309}
310
311/* \internal
312 Starts double buffered painting in the area specified by \a x,
313 \a y, \a w and \a h on \a painter. Painting should be done using the
314 QPainter returned by QSharedDoubleBuffer::painter().
315
316 The double buffered area will be updated when calling end().
317
318 \sa painter() isActive() end()
319*/
320bool QSharedDoubleBuffer::begin( QPainter* painter, int x, int y, int w, int h )
321{
322 if ( isActive() ) {
323#if defined(QT_CHECK_STATE)
324 qWarning( "QSharedDoubleBuffer::begin: Buffer is already active."
325 "\n\tYou must end() the buffer before a second begin()" );
326#endif // QT_CHECK_STATE
327 return FALSE;
328 }
329
330 external_p = painter;
331
332 if ( painter->device()->devType() == QInternal::Widget )
333 return begin( (QWidget *) painter->device(), x, y, w, h );
334
335 state = Active;
336
337 rx = x;
338 ry = y;
339 rw = w;
340 rh = h;
341
342 if ( ( pix = getPixmap() ) ) {
343#ifdef Q_WS_X11
344 if ( painter->device()->x11Screen() != pix->x11Screen() )
345 pix->x11SetScreen( painter->device()->x11Screen() );
346 QPixmap::x11SetDefaultScreen( pix->x11Screen() );
347#endif // Q_WS_X11
348
349 state |= BufferActive;
350 p = new QPainter( pix );
351 if ( p->isActive() ) {
352 p->setPen( external_p->pen() );
353 p->setBackgroundColor( external_p->backgroundColor() );
354 p->setFont( external_p->font() );
355 }
356 } else {
357 state |= ExternalPainter;
358 p = external_p;
359 }
360
361 return TRUE;
362}
363
364/* \internal
365
366
367 Starts double buffered painting in the area specified by \a x,
368 \a y, \a w and \a h on \a widget. Painting should be done using the
369 QPainter returned by QSharedDoubleBuffer::painter().
370
371 The double buffered area will be updated when calling end().
372
373 \sa painter() isActive() end()
374*/
375bool QSharedDoubleBuffer::begin( QWidget* widget, int x, int y, int w, int h )
376{
377 if ( isActive() ) {
378#if defined(QT_CHECK_STATE)
379 qWarning( "QSharedDoubleBuffer::begin: Buffer is already active."
380 "\n\tYou must end() the buffer before a second begin()" );
381#endif // QT_CHECK_STATE
382 return FALSE;
383 }
384
385 state = Active;
386
387 wid = widget;
388 rx = x;
389 ry = y;
390 rw = w <= 0 ? wid->width() : w;
391 rh = h <= 0 ? wid->height() : h;
392
393 if ( ( pix = getPixmap() ) ) {
394#ifdef Q_WS_X11
395 if ( wid->x11Screen() != pix->x11Screen() )
396 pix->x11SetScreen( wid->x11Screen() );
397 QPixmap::x11SetDefaultScreen( pix->x11Screen() );
398#endif // Q_WS_X11
399
400 state |= BufferActive;
401 if ( flags & InitBG ) {
402 pix->fill( wid, rx, ry );
403 }
404 p = new QPainter( pix, wid );
405 // newly created painters should be translated to the origin
406 // of the widget, so that paint methods can draw onto the double
407 // buffered painter in widget coordinates.
408 p->setBrushOrigin( -rx, -ry );
409 p->translate( -rx, -ry );
410 } else {
411 if ( external_p ) {
412 state |= ExternalPainter;
413 p = external_p;
414 } else {
415 p = new QPainter( wid );
416 }
417
418 if ( flags & InitBG ) {
419 wid->erase( rx, ry, rw, rh );
420 }
421 }
422 return TRUE;
423}
424
425/* \internal
426 Ends double buffered painting. The contents of the shared double
427 buffer pixmap are drawn onto the destination by calling flush(),
428 and ownership of the shared double buffer pixmap is released.
429
430 \sa begin() flush()
431*/
432bool QSharedDoubleBuffer::end()
433{
434 if ( ! isActive() ) {
435#if defined(QT_CHECK_STATE)
436 qWarning( "QSharedDoubleBuffer::end: Buffer is not active."
437 "\n\tYou must call begin() before calling end()." );
438#endif // QT_CHECK_STATE
439 return FALSE;
440 }
441
442 if ( ! ( state & ExternalPainter ) ) {
443 p->end();
444 delete p;
445 }
446
447 flush();
448
449 if ( pix ) {
450 releasePixmap();
451 }
452
453 wid = 0;
454 rx = ry = rw = rh = 0;
455 // do not reset flags!
456 state = 0;
457
458 p = external_p = 0;
459 pix = 0;
460
461 return TRUE;
462}
463
464/* \internal
465 Paints the contents of the shared double buffer pixmap onto the
466 destination. The destination is determined from the arguments
467 based to begin().
468
469 Note: You should not need to call this function, since it is called
470 from end().
471
472 \sa begin() end()
473*/
474void QSharedDoubleBuffer::flush()
475{
476 if ( ! isActive() || ! ( state & BufferActive ) )
477 return;
478
479 if ( external_p )
480 external_p->drawPixmap( rx, ry, *pix, 0, 0, rw, rh );
481 else if ( wid && wid->isVisible() )
482 bitBlt( wid, rx, ry, pix, 0, 0, rw, rh );
483}
484
485/* \internal
486 Aquire ownership of the shared double buffer pixmap, subject to the
487 following conditions:
488
489 \list 1
490 \i double buffering is enabled globally.
491 \i the shared double buffer pixmap is not in use.
492 \i the size specified in begin() is valid, and within limits.
493 \endlist
494
495 If all of these conditions are met, then this QSharedDoubleBuffer
496 object becomes the owner of the shared double buffer pixmap. The
497 shared double buffer pixmap is resize if necessary, and this
498 function returns a pointer to the pixmap. Ownership must later be
499 relinquished by calling releasePixmap().
500
501 If none of the above conditions are met, this function returns
502 zero.
503
504 \sa releasePixmap()
505*/
506QPixmap *QSharedDoubleBuffer::getPixmap()
507{
508 if ( isDisabled() ) {
509 // double buffering disabled globally
510 return 0;
511 }
512
513 if ( qdb_owner ) {
514 // shared pixmap already in use
515 return 0;
516 }
517
518 if ( rw <= 0 || rh <= 0 ||
519 ( hardLimitWidth > 0 && rw >= hardLimitWidth ) ||
520 ( hardLimitHeight > 0 && rh >= hardLimitHeight ) ) {
521 // invalid size, or hard limit reached
522 return 0;
523 }
524
525 if ( rw >= sharedLimitWidth || rh >= sharedLimitHeight ) {
526 if ( flags & Force ) {
527 rw = QMIN(rw, 8000);
528 rh = QMIN(rh, 8000);
529 // need to create a big pixmap and start the cleaner
530 if ( ! qdb_force_pixmap ) {
531 qdb_force_pixmap = new QPixmap( rw, rh );
532 qdb_pixmap_cleanup.add( &qdb_force_pixmap );
533 } else if ( qdb_force_pixmap->width () < rw ||
534 qdb_force_pixmap->height() < rh ) {
535 qdb_force_pixmap->resize( rw, rh );
536 }
537 qdb_owner = this;
538 staticCleaner()->start();
539 return qdb_force_pixmap;
540 }
541
542 // size is outside shared limit
543 return 0;
544 }
545
546 if ( ! qdb_shared_pixmap ) {
547 qdb_shared_pixmap = new QPixmap( rw, rh );
548 qdb_pixmap_cleanup.add( &qdb_shared_pixmap );
549 } else if ( qdb_shared_pixmap->width() < rw ||
550 qdb_shared_pixmap->height() < rh ) {
551 qdb_shared_pixmap->resize( rw, rh );
552 }
553 qdb_owner = this;
554 return qdb_shared_pixmap;
555}
556
557/* \internal
558 Releases ownership of the shared double buffer pixmap.
559
560 \sa getPixmap()
561*/
562void QSharedDoubleBuffer::releasePixmap()
563{
564 if ( qdb_owner != this ) {
565 // sanity check
566
567#ifdef QT_CHECK_STATE
568 qWarning( "QSharedDoubleBuffer::releasePixmap: internal error."
569 "\n\t%p does not own shared pixmap, %p does.",
570 (void*)this, (void*)qdb_owner );
571#endif // QT_CHECK_STATE
572
573 return;
574 }
575
576 qdb_owner = 0;
577}
578
579/* \internal
580 \fn bool QSharedDoubleBuffer::isDisabled()
581
582 Returns TRUE if double buffering is disabled globally, FALSE otherwise.
583*/
584
585/* \internal
586 \fn void QSharedDoubleBuffer::setDisabled( bool off )
587
588 Disables global double buffering \a off is TRUE, otherwise global
589 double buffering is enabled.
590*/
591
592/* \internal
593 Deletes the shared double buffer pixmap. You should not need to
594 call this function, since it is called from the QApplication
595 destructor.
596*/
597void QSharedDoubleBuffer::cleanup()
598{
599 qdb_pixmap_cleanup.remove( &qdb_shared_pixmap );
600 qdb_pixmap_cleanup.remove( &qdb_force_pixmap );
601 delete qdb_shared_pixmap;
602 delete qdb_force_pixmap;
603 qdb_shared_pixmap = 0;
604 qdb_force_pixmap = 0;
605 qdb_owner = 0;
606}
607
608/* \internal
609 \fn bool QSharedDoubleBuffer::begin( QWidget *widget, const QRect &r )
610 \overload
611*/
612
613/* \internal
614 \fn bool QSharedDoubleBuffer::begin( QPainter *painter, const QRect &r )
615 \overload
616*/
617
618/* \internal
619 \fn QPainter *QSharedDoubleBuffer::painter() const
620
621 Returns the active painter on the double buffered area,
622 or zero if double buffered painting is not active.
623*/
624
625/* \internal
626 \fn bool QSharedDoubleBuffer::isActive() const
627
628 Returns TRUE if double buffered painting is active, FALSE otherwise.
629*/
630
631/* \internal
632 \fn bool QSharedDoubleBuffer::isBuffered() const
633
634 Returns TRUE if painting is double buffered, FALSE otherwise.
635*/
636
637
638// *******************************************************************
639// QMembuf declaration and implementation
640// *******************************************************************
641
642/* \internal
643 This class implements an efficient buffering of data that is often used by
644 asynchronous IO classes like QSocket, QHttp and QProcess.
645*/
646
647QMembuf::QMembuf() : _size(0), _index(0)
648{
649 buf = new QPtrList<QByteArray>;
650 buf->setAutoDelete( TRUE );
651}
652
653QMembuf::~QMembuf()
654{
655 delete buf;
656}
657
658/*! \internal
659 This function consumes \a nbytes bytes of data from the
660 buffer and copies it into \a sink. If \a sink is a 0 pointer
661 the data goes into the nirvana.
662*/
663bool QMembuf::consumeBytes( Q_ULONG nbytes, char *sink )
664{
665 if ( nbytes <= 0 || nbytes > _size )
666 return FALSE;
667 _size -= nbytes;
668 for ( ;; ) {
669 QByteArray *a = buf->first();
670 if ( _index + nbytes >= a->size() ) {
671 // Here we skip the whole byte array and get the next later
672 int len = a->size() - _index;
673 if ( sink ) {
674 memcpy( sink, a->data()+_index, len );
675 sink += len;
676 }
677 nbytes -= len;
678 buf->remove();
679 _index = 0;
680 if ( nbytes == 0 )
681 break;
682 } else {
683 // Here we skip only a part of the first byte array
684 if ( sink )
685 memcpy( sink, a->data()+_index, nbytes );
686 _index += nbytes;
687 break;
688 }
689 }
690 return TRUE;
691}
692
693/*! \internal
694 Scans for any occurrence of '\n' in the buffer. If \a store
695 is not 0 the text up to the first '\n' (or terminating 0) is
696 written to \a store, and a terminating 0 is appended to \a store
697 if necessary. Returns TRUE if a '\n' was found; otherwise returns
698 FALSE.
699*/
700bool QMembuf::scanNewline( QByteArray *store )
701{
702 if ( _size == 0 )
703 return FALSE;
704 int i = 0; // index into 'store'
705 QByteArray *a = 0;
706 char *p;
707 int n;
708 for ( ;; ) {
709 if ( !a ) {
710 a = buf->first();
711 if ( !a || a->size() == 0 )
712 return FALSE;
713 p = a->data() + _index;
714 n = a->size() - _index;
715 } else {
716 a = buf->next();
717 if ( !a || a->size() == 0 )
718 return FALSE;
719 p = a->data();
720 n = a->size();
721 }
722 if ( store ) {
723 while ( n-- > 0 ) {
724 *(store->data()+i) = *p;
725 if ( ++i == (int)store->size() )
726 store->resize( store->size() < 256
727 ? 1024 : store->size()*4 );
728 switch ( *p ) {
729 case '\0':
730 store->resize( i );
731 return FALSE;
732 case '\n':
733 *(store->data()+i) = '\0';
734 store->resize( i );
735 return TRUE;
736 }
737 p++;
738 }
739 } else {
740 while ( n-- > 0 ) {
741 switch ( *p++ ) {
742 case '\0':
743 return FALSE;
744 case '\n':
745 return TRUE;
746 }
747 }
748 }
749 }
750}
751
752int QMembuf::ungetch( int ch )
753{
754 if ( buf->isEmpty() || _index==0 ) {
755 // we need a new QByteArray
756 QByteArray *ba = new QByteArray( 1 );
757 buf->insert( 0, ba );
758 _size++;
759 ba->at( 0 ) = ch;
760 } else {
761 // we can reuse a place in the buffer
762 QByteArray *ba = buf->first();
763 _index--;
764 _size++;
765 ba->at( _index ) = ch;
766 }
767 return ch;
768}
Note: See TracBrowser for help on using the repository browser.