root/OpenSceneGraph/trunk/src/osgQt/GraphicsWindowQt.cpp @ 13130

Revision 13130, 29.1 kB (checked in by robert, 10 hours ago)

Changed the osgUI behaviour so that events are set to be handled by Widgets that have focus even if they don't directly use them.

  • Property svn:eol-style set to native
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 2009 Wang Rui
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osg/DeleteHandler>
15#include <osgQt/GraphicsWindowQt>
16#include <osgViewer/ViewerBase>
17#include <QtGui/QInputEvent>
18
19using namespace osgQt;
20
21
22class QtKeyboardMap
23{
24
25public:
26    QtKeyboardMap()
27    {
28        mKeyMap[Qt::Key_Escape     ] = osgGA::GUIEventAdapter::KEY_Escape;
29        mKeyMap[Qt::Key_Delete   ] = osgGA::GUIEventAdapter::KEY_Delete;
30        mKeyMap[Qt::Key_Home       ] = osgGA::GUIEventAdapter::KEY_Home;
31        mKeyMap[Qt::Key_Enter      ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
32        mKeyMap[Qt::Key_End        ] = osgGA::GUIEventAdapter::KEY_End;
33        mKeyMap[Qt::Key_Return     ] = osgGA::GUIEventAdapter::KEY_Return;
34        mKeyMap[Qt::Key_PageUp     ] = osgGA::GUIEventAdapter::KEY_Page_Up;
35        mKeyMap[Qt::Key_PageDown   ] = osgGA::GUIEventAdapter::KEY_Page_Down;
36        mKeyMap[Qt::Key_Left       ] = osgGA::GUIEventAdapter::KEY_Left;
37        mKeyMap[Qt::Key_Right      ] = osgGA::GUIEventAdapter::KEY_Right;
38        mKeyMap[Qt::Key_Up         ] = osgGA::GUIEventAdapter::KEY_Up;
39        mKeyMap[Qt::Key_Down       ] = osgGA::GUIEventAdapter::KEY_Down;
40        mKeyMap[Qt::Key_Backspace  ] = osgGA::GUIEventAdapter::KEY_BackSpace;
41        mKeyMap[Qt::Key_Tab        ] = osgGA::GUIEventAdapter::KEY_Tab;
42        mKeyMap[Qt::Key_Space      ] = osgGA::GUIEventAdapter::KEY_Space;
43        mKeyMap[Qt::Key_Delete     ] = osgGA::GUIEventAdapter::KEY_Delete;
44        mKeyMap[Qt::Key_Alt      ] = osgGA::GUIEventAdapter::KEY_Alt_L;
45        mKeyMap[Qt::Key_Shift    ] = osgGA::GUIEventAdapter::KEY_Shift_L;
46        mKeyMap[Qt::Key_Control  ] = osgGA::GUIEventAdapter::KEY_Control_L;
47        mKeyMap[Qt::Key_Meta     ] = osgGA::GUIEventAdapter::KEY_Meta_L;
48
49        mKeyMap[Qt::Key_F1             ] = osgGA::GUIEventAdapter::KEY_F1;
50        mKeyMap[Qt::Key_F2             ] = osgGA::GUIEventAdapter::KEY_F2;
51        mKeyMap[Qt::Key_F3             ] = osgGA::GUIEventAdapter::KEY_F3;
52        mKeyMap[Qt::Key_F4             ] = osgGA::GUIEventAdapter::KEY_F4;
53        mKeyMap[Qt::Key_F5             ] = osgGA::GUIEventAdapter::KEY_F5;
54        mKeyMap[Qt::Key_F6             ] = osgGA::GUIEventAdapter::KEY_F6;
55        mKeyMap[Qt::Key_F7             ] = osgGA::GUIEventAdapter::KEY_F7;
56        mKeyMap[Qt::Key_F8             ] = osgGA::GUIEventAdapter::KEY_F8;
57        mKeyMap[Qt::Key_F9             ] = osgGA::GUIEventAdapter::KEY_F9;
58        mKeyMap[Qt::Key_F10            ] = osgGA::GUIEventAdapter::KEY_F10;
59        mKeyMap[Qt::Key_F11            ] = osgGA::GUIEventAdapter::KEY_F11;
60        mKeyMap[Qt::Key_F12            ] = osgGA::GUIEventAdapter::KEY_F12;
61        mKeyMap[Qt::Key_F13            ] = osgGA::GUIEventAdapter::KEY_F13;
62        mKeyMap[Qt::Key_F14            ] = osgGA::GUIEventAdapter::KEY_F14;
63        mKeyMap[Qt::Key_F15            ] = osgGA::GUIEventAdapter::KEY_F15;
64        mKeyMap[Qt::Key_F16            ] = osgGA::GUIEventAdapter::KEY_F16;
65        mKeyMap[Qt::Key_F17            ] = osgGA::GUIEventAdapter::KEY_F17;
66        mKeyMap[Qt::Key_F18            ] = osgGA::GUIEventAdapter::KEY_F18;
67        mKeyMap[Qt::Key_F19            ] = osgGA::GUIEventAdapter::KEY_F19;
68        mKeyMap[Qt::Key_F20            ] = osgGA::GUIEventAdapter::KEY_F20;
69
70        mKeyMap[Qt::Key_hyphen         ] = '-';
71        mKeyMap[Qt::Key_Equal         ] = '=';
72
73        mKeyMap[Qt::Key_division      ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
74        mKeyMap[Qt::Key_multiply      ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
75        mKeyMap[Qt::Key_Minus         ] = '-';
76        mKeyMap[Qt::Key_Plus          ] = '+';
77        //mKeyMap[Qt::Key_H              ] = osgGA::GUIEventAdapter::KEY_KP_Home;
78        //mKeyMap[Qt::Key_                    ] = osgGA::GUIEventAdapter::KEY_KP_Up;
79        //mKeyMap[92                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
80        //mKeyMap[86                    ] = osgGA::GUIEventAdapter::KEY_KP_Left;
81        //mKeyMap[87                    ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
82        //mKeyMap[88                    ] = osgGA::GUIEventAdapter::KEY_KP_Right;
83        //mKeyMap[83                    ] = osgGA::GUIEventAdapter::KEY_KP_End;
84        //mKeyMap[84                    ] = osgGA::GUIEventAdapter::KEY_KP_Down;
85        //mKeyMap[85                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
86        mKeyMap[Qt::Key_Insert        ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
87        //mKeyMap[Qt::Key_Delete        ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
88    }
89
90    ~QtKeyboardMap()
91    {
92    }
93
94    int remapKey(QKeyEvent* event)
95    {
96        KeyMap::iterator itr = mKeyMap.find(event->key());
97        if (itr == mKeyMap.end())
98        {
99            return int(*(event->text().toAscii().data()));
100        }
101        else
102            return itr->second;
103    }
104
105    private:
106    typedef std::map<unsigned int, int> KeyMap;
107    KeyMap mKeyMap;
108};
109
110static QtKeyboardMap s_QtKeyboardMap;
111
112
113/// The object responsible for the scene re-rendering.
114class HeartBeat : public QObject {
115public:
116int _timerId;
117osg::Timer _lastFrameStartTime;
118osg::observer_ptr< osgViewer::ViewerBase > _viewer;
119
120HeartBeat();
121virtual ~HeartBeat();
122void init( osgViewer::ViewerBase *viewer );
123void stopTimer();
124void timerEvent( QTimerEvent *event );
125};
126
127static HeartBeat heartBeat;
128
129GLWidget::GLWidget( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f, bool forwardKeyEvents )
130: QGLWidget(parent, shareWidget, f),
131_gw( NULL ),
132_forwardKeyEvents( forwardKeyEvents )
133{
134}
135
136GLWidget::GLWidget( QGLContext* context, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f,
137                    bool forwardKeyEvents )
138: QGLWidget(context, parent, shareWidget, f),
139_gw( NULL ),
140_forwardKeyEvents( forwardKeyEvents )
141{
142}
143
144GLWidget::GLWidget( const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f,
145                    bool forwardKeyEvents )
146: QGLWidget(format, parent, shareWidget, f),
147_gw( NULL ),
148_forwardKeyEvents( forwardKeyEvents )
149{
150}
151
152GLWidget::~GLWidget()
153{
154    // close GraphicsWindowQt and remove the reference to us
155    if( _gw )
156    {
157        _gw->close();
158        _gw->_widget = NULL;
159        _gw = NULL;
160    }
161}
162
163void GLWidget::processDeferredEvents()
164{
165    QQueue<QEvent::Type> deferredEventQueueCopy;
166    {
167        QMutexLocker lock(&_deferredEventQueueMutex);
168        deferredEventQueueCopy = _deferredEventQueue;
169        _eventCompressor.clear();
170        _deferredEventQueue.clear();
171    }
172
173    while (!deferredEventQueueCopy.isEmpty())
174    {
175        QEvent event(deferredEventQueueCopy.dequeue());
176        QGLWidget::event(&event);
177    }
178}
179
180bool GLWidget::event( QEvent* event )
181{
182
183    // QEvent::Hide
184    //
185    // workaround "Qt-workaround" that does glFinish before hiding the widget
186    // (the Qt workaround was seen at least in Qt 4.6.3 and 4.7.0)
187    //
188    // Qt makes the context current, performs glFinish, and releases the context.
189    // This makes the problem in OSG multithreaded environment as the context
190    // is active in another thread, thus it can not be made current for the purpose
191    // of glFinish in this thread.
192
193    // QEvent::ParentChange
194    //
195    // Reparenting GLWidget may create a new underlying window and a new GL context.
196    // Qt will then call doneCurrent on the GL context about to be deleted. The thread
197    // where old GL context was current has no longer current context to render to and
198    // we cannot make new GL context current in this thread.
199
200    // We workaround above problems by deferring execution of problematic event requests.
201    // These events has to be enqueue and executed later in a main GUI thread (GUI operations
202    // outside the main thread are not allowed) just before makeCurrent is called from the
203    // right thread. The good place for doing that is right after swap in a swapBuffersImplementation.
204
205    if (event->type() == QEvent::Hide)
206    {
207        // enqueue only the last of QEvent::Hide and QEvent::Show
208        enqueueDeferredEvent(QEvent::Hide, QEvent::Show);
209        return true;
210    }
211    else if (event->type() == QEvent::Show)
212    {
213        // enqueue only the last of QEvent::Show or QEvent::Hide
214        enqueueDeferredEvent(QEvent::Show, QEvent::Hide);
215        return true;
216    }
217    else if (event->type() == QEvent::ParentChange)
218    {
219        // enqueue only the last QEvent::ParentChange
220        enqueueDeferredEvent(QEvent::ParentChange);
221        return true;
222    }
223
224    // perform regular event handling
225    return QGLWidget::event( event );
226}
227
228void GLWidget::setKeyboardModifiers( QInputEvent* event )
229{
230    int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier);
231    unsigned int mask = 0;
232    if ( modkey & Qt::ShiftModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT;
233    if ( modkey & Qt::ControlModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL;
234    if ( modkey & Qt::AltModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_ALT;
235    _gw->getEventQueue()->getCurrentEventState()->setModKeyMask( mask );
236}
237
238void GLWidget::resizeEvent( QResizeEvent* event )
239{
240    const QSize& size = event->size();
241    _gw->resized( x(), y(), size.width(), size.height() );
242    _gw->getEventQueue()->windowResize( x(), y(), size.width(), size.height() );
243    _gw->requestRedraw();
244}
245
246void GLWidget::moveEvent( QMoveEvent* event )
247{
248    const QPoint& pos = event->pos();
249    _gw->resized( pos.x(), pos.y(), width(), height() );
250    _gw->getEventQueue()->windowResize( pos.x(), pos.y(), width(), height() );
251}
252
253void GLWidget::glDraw()
254{
255    _gw->requestRedraw();
256}
257
258void GLWidget::keyPressEvent( QKeyEvent* event )
259{
260    setKeyboardModifiers( event );
261    int value = s_QtKeyboardMap.remapKey( event );
262    _gw->getEventQueue()->keyPress( value );
263
264    // this passes the event to the regular Qt key event processing,
265    // among others, it closes popup windows on ESC and forwards the event to the parent widgets
266    if( _forwardKeyEvents )
267        inherited::keyPressEvent( event );
268}
269
270void GLWidget::keyReleaseEvent( QKeyEvent* event )
271{
272    setKeyboardModifiers( event );
273    int value = s_QtKeyboardMap.remapKey( event );
274    _gw->getEventQueue()->keyRelease( value );
275
276    // this passes the event to the regular Qt key event processing,
277    // among others, it closes popup windows on ESC and forwards the event to the parent widgets
278    if( _forwardKeyEvents )
279        inherited::keyReleaseEvent( event );
280}
281
282void GLWidget::mousePressEvent( QMouseEvent* event )
283{
284    int button = 0;
285    switch ( event->button() )
286    {
287        case Qt::LeftButton: button = 1; break;
288        case Qt::MidButton: button = 2; break;
289        case Qt::RightButton: button = 3; break;
290        case Qt::NoButton: button = 0; break;
291        default: button = 0; break;
292    }
293    setKeyboardModifiers( event );
294    _gw->getEventQueue()->mouseButtonPress( event->x(), event->y(), button );
295}
296
297void GLWidget::mouseReleaseEvent( QMouseEvent* event )
298{
299    int button = 0;
300    switch ( event->button() )
301    {
302        case Qt::LeftButton: button = 1; break;
303        case Qt::MidButton: button = 2; break;
304        case Qt::RightButton: button = 3; break;
305        case Qt::NoButton: button = 0; break;
306        default: button = 0; break;
307    }
308    setKeyboardModifiers( event );
309    _gw->getEventQueue()->mouseButtonRelease( event->x(), event->y(), button );
310}
311
312void GLWidget::mouseDoubleClickEvent( QMouseEvent* event )
313{
314    int button = 0;
315    switch ( event->button() )
316    {
317        case Qt::LeftButton: button = 1; break;
318        case Qt::MidButton: button = 2; break;
319        case Qt::RightButton: button = 3; break;
320        case Qt::NoButton: button = 0; break;
321        default: button = 0; break;
322    }
323    setKeyboardModifiers( event );
324    _gw->getEventQueue()->mouseDoubleButtonPress( event->x(), event->y(), button );
325}
326
327void GLWidget::mouseMoveEvent( QMouseEvent* event )
328{
329    setKeyboardModifiers( event );
330    _gw->getEventQueue()->mouseMotion( event->x(), event->y() );
331}
332
333void GLWidget::wheelEvent( QWheelEvent* event )
334{
335    setKeyboardModifiers( event );
336    _gw->getEventQueue()->mouseScroll(
337        event->orientation() == Qt::Vertical ?
338            (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) :
339            (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_LEFT : osgGA::GUIEventAdapter::SCROLL_RIGHT) );
340}
341
342
343
344GraphicsWindowQt::GraphicsWindowQt( osg::GraphicsContext::Traits* traits, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f )
345:   _realized(false)
346{
347
348    _widget = NULL;
349    _traits = traits;
350    init( parent, shareWidget, f );
351}
352
353GraphicsWindowQt::GraphicsWindowQt( GLWidget* widget )
354:   _realized(false)
355{
356    _widget = widget;
357    _traits = _widget ? createTraits( _widget ) : new osg::GraphicsContext::Traits;
358    init( NULL, NULL, 0 );
359}
360
361GraphicsWindowQt::~GraphicsWindowQt()
362{
363    close();
364
365    // remove reference from GLWidget
366    if ( _widget )
367        _widget->_gw = NULL;
368}
369
370bool GraphicsWindowQt::init( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f )
371{
372    // update _widget and parent by WindowData
373    WindowData* windowData = _traits.get() ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : 0;
374    if ( !_widget )
375        _widget = windowData ? windowData->_widget : NULL;
376    if ( !parent )
377        parent = windowData ? windowData->_parent : NULL;
378
379    // create widget if it does not exist
380    _ownsWidget = _widget == NULL;
381    if ( !_widget )
382    {
383        // shareWidget
384        if ( !shareWidget ) {
385            GraphicsWindowQt* sharedContextQt = dynamic_cast<GraphicsWindowQt*>(_traits->sharedContext.get());
386            if ( sharedContextQt )
387                shareWidget = sharedContextQt->getGLWidget();
388        }
389
390        // WindowFlags
391        Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint;
392        if ( _traits->windowDecoration )
393            flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint
394#if (QT_VERSION_CHECK(4, 5, 0) <= QT_VERSION)
395                | Qt::WindowCloseButtonHint
396#endif
397                ;
398
399        // create widget
400        _widget = new GLWidget( traits2qglFormat( _traits.get() ), parent, shareWidget, flags );
401    }
402
403    // set widget name and position
404    // (do not set it when we inherited the widget)
405    if ( _ownsWidget )
406    {
407        _widget->setWindowTitle( _traits->windowName.c_str() );
408        _widget->move( _traits->x, _traits->y );
409        if ( !_traits->supportsResize ) _widget->setFixedSize( _traits->width, _traits->height );
410        else _widget->resize( _traits->width, _traits->height );
411    }
412
413    // initialize widget properties
414    _widget->setAutoBufferSwap( false );
415    _widget->setMouseTracking( true );
416    _widget->setFocusPolicy( Qt::WheelFocus );
417    _widget->setGraphicsWindow( this );
418    useCursor( _traits->useCursor );
419
420    // initialize State
421    setState( new osg::State );
422    getState()->setGraphicsContext(this);
423
424    // initialize contextID
425    if ( _traits.valid() && _traits->sharedContext.valid() )
426    {
427        getState()->setContextID( _traits->sharedContext->getState()->getContextID() );
428        incrementContextIDUsageCount( getState()->getContextID() );
429    }
430    else
431    {
432        getState()->setContextID( osg::GraphicsContext::createNewContextID() );
433    }
434
435    return true;
436}
437
438QGLFormat GraphicsWindowQt::traits2qglFormat( const osg::GraphicsContext::Traits* traits )
439{
440    QGLFormat format( QGLFormat::defaultFormat() );
441
442    format.setAlphaBufferSize( traits->alpha );
443    format.setRedBufferSize( traits->red );
444    format.setGreenBufferSize( traits->green );
445    format.setBlueBufferSize( traits->blue );
446    format.setDepthBufferSize( traits->depth );
447    format.setStencilBufferSize( traits->stencil );
448    format.setSampleBuffers( traits->sampleBuffers );
449    format.setSamples( traits->samples );
450
451    format.setAlpha( traits->alpha>0 );
452    format.setDepth( traits->depth>0 );
453    format.setStencil( traits->stencil>0 );
454    format.setDoubleBuffer( traits->doubleBuffer );
455    format.setSwapInterval( traits->vsync ? 1 : 0 );
456    format.setStereo( traits->quadBufferStereo ? 1 : 0 );
457
458    return format;
459}
460
461void GraphicsWindowQt::qglFormat2traits( const QGLFormat& format, osg::GraphicsContext::Traits* traits )
462{
463    traits->red = format.redBufferSize();
464    traits->green = format.greenBufferSize();
465    traits->blue = format.blueBufferSize();
466    traits->alpha = format.alpha() ? format.alphaBufferSize() : 0;
467    traits->depth = format.depth() ? format.depthBufferSize() : 0;
468    traits->stencil = format.stencil() ? format.stencilBufferSize() : 0;
469
470    traits->sampleBuffers = format.sampleBuffers() ? 1 : 0;
471    traits->samples = format.samples();
472
473    traits->quadBufferStereo = format.stereo();
474    traits->doubleBuffer = format.doubleBuffer();
475
476    traits->vsync = format.swapInterval() >= 1;
477}
478
479osg::GraphicsContext::Traits* GraphicsWindowQt::createTraits( const QGLWidget* widget )
480{
481    osg::GraphicsContext::Traits *traits = new osg::GraphicsContext::Traits;
482
483    qglFormat2traits( widget->format(), traits );
484
485    QRect r = widget->geometry();
486    traits->x = r.x();
487    traits->y = r.y();
488    traits->width = r.width();
489    traits->height = r.height();
490
491    traits->windowName = widget->windowTitle().toLocal8Bit().data();
492    Qt::WindowFlags f = widget->windowFlags();
493    traits->windowDecoration = ( f & Qt::WindowTitleHint ) &&
494                            ( f & Qt::WindowMinMaxButtonsHint ) &&
495                            ( f & Qt::WindowSystemMenuHint );
496    QSizePolicy sp = widget->sizePolicy();
497    traits->supportsResize = sp.horizontalPolicy() != QSizePolicy::Fixed ||
498                            sp.verticalPolicy() != QSizePolicy::Fixed;
499
500    return traits;
501}
502
503bool GraphicsWindowQt::setWindowRectangleImplementation( int x, int y, int width, int height )
504{
505    if ( _widget == NULL )
506        return false;
507
508    _widget->setGeometry( x, y, width, height );
509    return true;
510}
511
512void GraphicsWindowQt::getWindowRectangle( int& x, int& y, int& width, int& height )
513{
514    if ( _widget )
515    {
516        const QRect& geom = _widget->geometry();
517        x = geom.x();
518        y = geom.y();
519        width = geom.width();
520        height = geom.height();
521    }
522}
523
524bool GraphicsWindowQt::setWindowDecorationImplementation( bool windowDecoration )
525{
526    Qt::WindowFlags flags = Qt::Window|Qt::CustomizeWindowHint;//|Qt::WindowStaysOnTopHint;
527    if ( windowDecoration )
528        flags |= Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowSystemMenuHint;
529    _traits->windowDecoration = windowDecoration;
530
531    if ( _widget )
532    {
533        _widget->setWindowFlags( flags );
534
535        return true;
536    }
537
538    return false;
539}
540
541bool GraphicsWindowQt::getWindowDecoration() const
542{
543    return _traits->windowDecoration;
544}
545
546void GraphicsWindowQt::grabFocus()
547{
548    if ( _widget )
549        _widget->setFocus( Qt::ActiveWindowFocusReason );
550}
551
552void GraphicsWindowQt::grabFocusIfPointerInWindow()
553{
554    if ( _widget->underMouse() )
555        _widget->setFocus( Qt::ActiveWindowFocusReason );
556}
557
558void GraphicsWindowQt::raiseWindow()
559{
560    if ( _widget )
561        _widget->raise();
562}
563
564void GraphicsWindowQt::setWindowName( const std::string& name )
565{
566    if ( _widget )
567        _widget->setWindowTitle( name.c_str() );
568}
569
570std::string GraphicsWindowQt::getWindowName()
571{
572    return _widget ? _widget->windowTitle().toStdString() : "";
573}
574
575void GraphicsWindowQt::useCursor( bool cursorOn )
576{
577    if ( _widget )
578    {
579        _traits->useCursor = cursorOn;
580        if ( !cursorOn ) _widget->setCursor( Qt::BlankCursor );
581        else _widget->setCursor( _currentCursor );
582    }
583}
584
585void GraphicsWindowQt::setCursor( MouseCursor cursor )
586{
587    if ( cursor==InheritCursor && _widget )
588    {
589        _widget->unsetCursor();
590    }
591
592    switch ( cursor )
593    {
594    case NoCursor: _currentCursor = Qt::BlankCursor; break;
595    case RightArrowCursor: case LeftArrowCursor: _currentCursor = Qt::ArrowCursor; break;
596    case InfoCursor: _currentCursor = Qt::SizeAllCursor; break;
597    case DestroyCursor: _currentCursor = Qt::ForbiddenCursor; break;
598    case HelpCursor: _currentCursor = Qt::WhatsThisCursor; break;
599    case CycleCursor: _currentCursor = Qt::ForbiddenCursor; break;
600    case SprayCursor: _currentCursor = Qt::SizeAllCursor; break;
601    case WaitCursor: _currentCursor = Qt::WaitCursor; break;
602    case TextCursor: _currentCursor = Qt::IBeamCursor; break;
603    case CrosshairCursor: _currentCursor = Qt::CrossCursor; break;
604    case HandCursor: _currentCursor = Qt::OpenHandCursor; break;
605    case UpDownCursor: _currentCursor = Qt::SizeVerCursor; break;
606    case LeftRightCursor: _currentCursor = Qt::SizeHorCursor; break;
607    case TopSideCursor: case BottomSideCursor: _currentCursor = Qt::UpArrowCursor; break;
608    case LeftSideCursor: case RightSideCursor: _currentCursor = Qt::SizeHorCursor; break;
609    case TopLeftCorner: _currentCursor = Qt::SizeBDiagCursor; break;
610    case TopRightCorner: _currentCursor = Qt::SizeFDiagCursor; break;
611    case BottomRightCorner: _currentCursor = Qt::SizeBDiagCursor; break;
612    case BottomLeftCorner: _currentCursor = Qt::SizeFDiagCursor; break;
613    default: break;
614    };
615    if ( _widget ) _widget->setCursor( _currentCursor );
616}
617
618bool GraphicsWindowQt::valid() const
619{
620    return _widget && _widget->isValid();
621}
622
623bool GraphicsWindowQt::realizeImplementation()
624{
625    // save the current context
626    // note: this will save only Qt-based contexts
627    const QGLContext *savedContext = QGLContext::currentContext();
628
629    // initialize GL context for the widget
630    if ( !valid() )
631        _widget->glInit();
632
633    // make current
634    _realized = true;
635    bool result = makeCurrent();
636    _realized = false;
637
638    // fail if we do not have current context
639    if ( !result )
640    {
641        if ( savedContext )
642            const_cast< QGLContext* >( savedContext )->makeCurrent();
643
644        OSG_WARN << "Window realize: Can make context current." << std::endl;
645        return false;
646    }
647
648    _realized = true;
649
650    // make this window's context not current
651    // note: this must be done as we will probably make the context current from another thread
652    //       and it is not allowed to have one context current in two threads
653    if( !releaseContext() )
654        OSG_WARN << "Window realize: Can not release context." << std::endl;
655
656    // restore previous context
657    if ( savedContext )
658        const_cast< QGLContext* >( savedContext )->makeCurrent();
659
660    return true;
661}
662
663bool GraphicsWindowQt::isRealizedImplementation() const
664{
665    return _realized;
666}
667
668void GraphicsWindowQt::closeImplementation()
669{
670    if ( _widget )
671        _widget->close();
672    _realized = false;
673}
674
675void GraphicsWindowQt::runOperations()
676{
677    // While in graphics thread this is last chance to do something useful before
678    // graphics thread will execute its operations.
679    if (_widget->getNumDeferredEvents() > 0)
680        _widget->processDeferredEvents();
681
682    if (QGLContext::currentContext() != _widget->context())
683        _widget->makeCurrent();
684
685    GraphicsWindow::runOperations();
686}
687
688bool GraphicsWindowQt::makeCurrentImplementation()
689{
690    if (_widget->getNumDeferredEvents() > 0)
691        _widget->processDeferredEvents();
692
693    _widget->makeCurrent();
694
695    return true;
696}
697
698bool GraphicsWindowQt::releaseContextImplementation()
699{
700    _widget->doneCurrent();
701    return true;
702}
703
704void GraphicsWindowQt::swapBuffersImplementation()
705{
706    _widget->swapBuffers();
707
708    // FIXME: the processDeferredEvents should really be executed in a GUI (main) thread context but
709    // I couln't find any reliable way to do this. For now, lets hope non of *GUI thread only operations* will
710    // be executed in a QGLWidget::event handler. On the other hand, calling GUI only operations in the
711    // QGLWidget event handler is an indication of a Qt bug.
712    if (_widget->getNumDeferredEvents() > 0)
713        _widget->processDeferredEvents();
714
715    // We need to call makeCurrent here to restore our previously current context
716    // which may be changed by the processDeferredEvents function.
717    if (QGLContext::currentContext() != _widget->context())
718        _widget->makeCurrent();
719}
720
721void GraphicsWindowQt::requestWarpPointer( float x, float y )
722{
723    if ( _widget )
724        QCursor::setPos( _widget->mapToGlobal(QPoint((int)x,(int)y)) );
725}
726
727
728class QtWindowingSystem : public osg::GraphicsContext::WindowingSystemInterface
729{
730public:
731
732    QtWindowingSystem()
733    {
734        OSG_INFO << "QtWindowingSystemInterface()" << std::endl;
735    }
736
737    ~QtWindowingSystem()
738    {
739        if (osg::Referenced::getDeleteHandler())
740        {
741            osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
742            osg::Referenced::getDeleteHandler()->flushAll();
743        }
744    }
745
746    // Access the Qt windowing system through this singleton class.
747    static QtWindowingSystem* getInterface()
748    {
749        static QtWindowingSystem* qtInterface = new QtWindowingSystem;
750        return qtInterface;
751    }
752
753    // Return the number of screens present in the system
754    virtual unsigned int getNumScreens( const osg::GraphicsContext::ScreenIdentifier& si )
755    {
756        OSG_WARN << "osgQt: getNumScreens() not implemented yet." << std::endl;
757        return 0;
758    }
759
760    // Return the resolution of specified screen
761    // (0,0) is returned if screen is unknown
762    virtual void getScreenSettings( const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution )
763    {
764        OSG_WARN << "osgQt: getScreenSettings() not implemented yet." << std::endl;
765    }
766
767    // Set the resolution for given screen
768    virtual bool setScreenSettings( const osg::GraphicsContext::ScreenIdentifier& si, const osg::GraphicsContext::ScreenSettings & resolution )
769    {
770        OSG_WARN << "osgQt: setScreenSettings() not implemented yet." << std::endl;
771        return false;
772    }
773
774    // Enumerates available resolutions
775    virtual void enumerateScreenSettings( const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolution )
776    {
777        OSG_WARN << "osgQt: enumerateScreenSettings() not implemented yet." << std::endl;
778    }
779
780    // Create a graphics context with given traits
781    virtual osg::GraphicsContext* createGraphicsContext( osg::GraphicsContext::Traits* traits )
782    {
783        if (traits->pbuffer)
784        {
785            OSG_WARN << "osgQt: createGraphicsContext - pbuffer not implemented yet." << std::endl;
786            return NULL;
787        }
788        else
789        {
790            osg::ref_ptr< GraphicsWindowQt > window = new GraphicsWindowQt( traits );
791            if (window->valid()) return window.release();
792            else return NULL;
793        }
794    }
795
796private:
797
798    // No implementation for these
799    QtWindowingSystem( const QtWindowingSystem& );
800    QtWindowingSystem& operator=( const QtWindowingSystem& );
801};
802
803
804// declare C entry point for static compilation.
805extern "C" void OSGQT_EXPORT graphicswindow_Qt(void)
806{
807    osg::GraphicsContext::setWindowingSystemInterface(QtWindowingSystem::getInterface());
808}
809
810
811void osgQt::initQtWindowingSystem()
812{
813    graphicswindow_Qt();
814}
815
816
817
818void osgQt::setViewer( osgViewer::ViewerBase *viewer )
819{
820    heartBeat.init( viewer );
821}
822
823
824/// Constructor. Must be called from main thread.
825HeartBeat::HeartBeat() : _timerId( 0 )
826{
827}
828
829
830/// Destructor. Must be called from main thread.
831HeartBeat::~HeartBeat()
832{
833    stopTimer();
834}
835
836
837void HeartBeat::stopTimer()
838{
839    if ( _timerId != 0 )
840    {
841        killTimer( _timerId );
842        _timerId = 0;
843    }
844}
845
846
847/// Initializes the loop for viewer. Must be called from main thread.
848void HeartBeat::init( osgViewer::ViewerBase *viewer )
849{
850    if( _viewer == viewer )
851        return;
852
853    stopTimer();
854
855    _viewer = viewer;
856
857    if( viewer )
858    {
859        _timerId = startTimer( 0 );
860        _lastFrameStartTime.setStartTick( 0 );
861    }
862}
863
864
865void HeartBeat::timerEvent( QTimerEvent *event )
866{
867    osg::ref_ptr< osgViewer::ViewerBase > viewer;
868    if( !_viewer.lock( viewer ) )
869    {
870        // viewer has been deleted -> stop timer
871        stopTimer();
872        return;
873    }
874
875    // limit the frame rate
876    if( viewer->getRunMaxFrameRate() > 0.0)
877    {
878        double dt = _lastFrameStartTime.time_s();
879        double minFrameTime = 1.0 / viewer->getRunMaxFrameRate();
880        if (dt < minFrameTime)
881            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(minFrameTime-dt)));
882    }
883    else
884    {
885        // avoid excessive CPU loading when no frame is required in ON_DEMAND mode
886        if( viewer->getRunFrameScheme() == osgViewer::ViewerBase::ON_DEMAND )
887        {
888            double dt = _lastFrameStartTime.time_s();
889            if (dt < 0.01)
890                OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(0.01-dt)));
891        }
892
893        // record start frame time
894        _lastFrameStartTime.setStartTick();
895
896        // make frame
897        if( viewer->getRunFrameScheme() == osgViewer::ViewerBase::ON_DEMAND )
898        {
899            if( viewer->checkNeedToDoFrame() )
900            {
901                viewer->frame();
902            }
903        }
904        else
905        {
906            viewer->frame();
907        }
908    }
909}
Note: See TracBrowser for help on using the browser.