2004-01-18 Marcus Brinkmann <marcus@g10code.de>
[pinentry.git] / qt / secqinternal.cpp
1 /****************************************************************************
2 ** $Id$
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 "secqinternal_p.h"
39 #include "qwidget.h"
40 #include "qpixmap.h"
41 #include "qpainter.h"
42 #include "qcleanuphandler.h"
43
44 static QPixmap* qdb_shared_pixmap = 0;
45 static QPixmap *qdb_force_pixmap = 0;
46 static SecQSharedDoubleBuffer* qdb_owner = 0;
47
48 static QCleanupHandler<QPixmap> qdb_pixmap_cleanup;
49
50 #ifdef Q_WS_MACX
51 bool SecQSharedDoubleBuffer::dblbufr = FALSE;
52 #else
53 bool SecQSharedDoubleBuffer::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  */
67 static const int hardLimitWidth = -1;
68 static const int hardLimitHeight = -1;
69 #if defined( Q_WS_QWS ) || defined( Q_WS_MAC9 )
70 // Small in Qt/Embedded / Mac9 - 5K on 32bpp
71 static const int sharedLimitWidth = 64;
72 static const int sharedLimitHeight = 20;
73 #else
74 // 240K on 32bpp
75 static const int sharedLimitWidth = 640;
76 static const int sharedLimitHeight = 100;
77 #endif
78
79 // *******************************************************************
80 // SecQSharedDoubleBufferCleaner declaration and implementation
81 // *******************************************************************
82
83 /* \internal
84    This class is responsible for cleaning up the pixmaps created by the
85    SecQSharedDoubleBuffer class.  When SecQSharedDoubleBuffer 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
96 static const int shared_double_buffer_cleanup_timeout = 30000; // 30 seconds
97
98 // declaration
99
100 class SecQSharedDoubleBufferCleaner : public QObject
101 {
102 public:
103     SecQSharedDoubleBufferCleaner( void );
104
105     void start( void );
106     void stop( void );
107
108     void doCleanup( void );
109
110     bool event( QEvent *e );
111
112 private:
113     int timer_id;
114 };
115
116 // implementation
117
118 /* \internal
119    Creates a SecQSharedDoubleBufferCleaner object. The timer is not
120    started when creating the object.
121 */
122 SecQSharedDoubleBufferCleaner::SecQSharedDoubleBufferCleaner( 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 */
131 void SecQSharedDoubleBufferCleaner::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 */
140 void SecQSharedDoubleBufferCleaner::stop( void )
141 {
142     if ( timer_id != -1 )
143         killTimer( timer_id );
144     timer_id = -1;
145 }
146
147 /* \internal
148  */
149 void SecQSharedDoubleBufferCleaner::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 */
160 bool SecQSharedDoubleBufferCleaner::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( "SecQSharedDoubleBufferCleaner::event: invalid timer event received." );
173         return FALSE;
174     }
175 #endif // QT_CHECK_STATE
176
177     return TRUE;
178 }
179
180 // static instance
181 static SecQSharedDoubleBufferCleaner *static_cleaner = 0;
182 QSingleCleanupHandler<SecQSharedDoubleBufferCleaner> cleanup_static_cleaner;
183
184 inline static SecQSharedDoubleBufferCleaner *staticCleaner()
185 {
186     if ( ! static_cleaner ) {
187         static_cleaner = new SecQSharedDoubleBufferCleaner();
188         cleanup_static_cleaner.set( &static_cleaner );
189     }
190     return static_cleaner;
191 }
192
193
194 // *******************************************************************
195 // SecQSharedDoubleBuffer 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 SecQSharedDoubleBuffer.
218 */
219
220 /* \internal
221    \class SecQSharedDoubleBuffer
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 SecQSharedDoubleBuffer with flags \f.
234
235    \sa DoubleBufferFlags
236 */
237 SecQSharedDoubleBuffer::SecQSharedDoubleBuffer( 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 SecQSharedDoubleBuffer 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 */
249 SecQSharedDoubleBuffer::SecQSharedDoubleBuffer( 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 SecQSharedDoubleBuffer 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 */
264 SecQSharedDoubleBuffer::SecQSharedDoubleBuffer( 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 SecQSharedDoubleBuffer with flags \f. The \a widget and
275    \a r arguments are passed to begin().
276
277    \sa DoubleBufferFlags begin()
278 */
279 SecQSharedDoubleBuffer::SecQSharedDoubleBuffer( 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 SecQSharedDoubleBuffer with flags \f. The \a painter and
288    \a r arguments are passed to begin().
289
290    \sa DoubleBufferFlags begin()
291 */
292 SecQSharedDoubleBuffer::SecQSharedDoubleBuffer( 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 SecQSharedDoubleBuffer and calls end() if the buffer is
301    active.
302
303    \sa isActive() end()
304 */
305 SecQSharedDoubleBuffer::~SecQSharedDoubleBuffer()
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 SecQSharedDoubleBuffer::painter().
315
316    The double buffered area will be updated when calling end().
317
318    \sa painter() isActive() end()
319 */
320 bool SecQSharedDoubleBuffer::begin( QPainter* painter, int x, int y, int w, int h )
321 {
322     if ( isActive() ) {
323 #if defined(QT_CHECK_STATE)
324         qWarning( "SecQSharedDoubleBuffer::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 SecQSharedDoubleBuffer::painter().
370
371    The double buffered area will be updated when calling end().
372
373    \sa painter() isActive() end()
374 */
375 bool SecQSharedDoubleBuffer::begin( QWidget* widget, int x, int y, int w, int h )
376 {
377     if ( isActive() ) {
378 #if defined(QT_CHECK_STATE)
379         qWarning( "SecQSharedDoubleBuffer::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 */
432 bool SecQSharedDoubleBuffer::end()
433 {
434     if ( ! isActive() ) {
435 #if defined(QT_CHECK_STATE)
436         qWarning( "SecQSharedDoubleBuffer::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 */
474 void SecQSharedDoubleBuffer::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 SecQSharedDoubleBuffer
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 */
506 QPixmap *SecQSharedDoubleBuffer::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 */
562 void SecQSharedDoubleBuffer::releasePixmap()
563 {
564     if ( qdb_owner != this ) {
565         // sanity check
566
567 #ifdef QT_CHECK_STATE
568         qWarning( "SecQSharedDoubleBuffer::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 SecQSharedDoubleBuffer::isDisabled()
581
582    Returns TRUE is double buffering is disabled globally, FALSE otherwise.
583 */
584
585 /* \internal
586    \fn void SecQSharedDoubleBuffer::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 */
597 void SecQSharedDoubleBuffer::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 SecQSharedDoubleBuffer::begin( QWidget *widget, const QRect &r )
610    \overload
611 */
612
613 /* \internal
614    \fn bool SecQSharedDoubleBuffer::begin( QPainter *painter, const QRect &r )
615    \overload
616 */
617
618 /* \internal
619    \fn QPainter *SecQSharedDoubleBuffer::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 SecQSharedDoubleBuffer::isActive() const
627
628    Returns TRUE if double buffered painting is active, FALSE otherwise.
629 */
630
631 /* \internal
632    \fn bool SecQSharedDoubleBuffer::isBuffered() const
633
634    Returns TRUE if painting is double buffered, FALSE otherwise.
635 */