// // GLCanvas.cpp // interface/src // // Created by Stephen Birarda on 8/14/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include #include "Application.h" #include "GLCanvas.h" #include "MainWindow.h" #include "devices/OculusManager.h" const int MSECS_PER_FRAME_WHEN_THROTTLED = 66; GLCanvas::GLCanvas() : QGLWidget(QGL::NoDepthBuffer | QGL::NoStencilBuffer), _throttleRendering(false), _idleRenderInterval(MSECS_PER_FRAME_WHEN_THROTTLED) { #ifdef Q_OS_LINUX // Cause GLCanvas::eventFilter to be called. // It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux. qApp->installEventFilter(this); #endif } void GLCanvas::stopFrameTimer() { _frameTimer.stop(); } bool GLCanvas::isThrottleRendering() const { return _throttleRendering || Application::getInstance()->getWindow()->isMinimized(); } int GLCanvas::getDeviceWidth() const { return width() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f); } int GLCanvas::getDeviceHeight() const { return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f); } void GLCanvas::initializeGL() { Application::getInstance()->initializeGL(); setAttribute(Qt::WA_AcceptTouchEvents); setAcceptDrops(true); connect(Application::getInstance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(activeChanged(Qt::ApplicationState))); connect(&_frameTimer, SIGNAL(timeout()), this, SLOT(throttleRender())); // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. setAutoBufferSwap(false); } void GLCanvas::paintGL() { if (!_throttleRendering && !Application::getInstance()->getWindow()->isMinimized()) { //Need accurate frame timing for the oculus rift if (OculusManager::isConnected()) { OculusManager::beginFrameTiming(); } Application::getInstance()->paintGL(); if (!OculusManager::isConnected()) { swapBuffers(); } else { if (OculusManager::allowSwap()) { swapBuffers(); } OculusManager::endFrameTiming(); } } } void GLCanvas::resizeGL(int width, int height) { Application::getInstance()->resizeGL(width, height); } void GLCanvas::keyPressEvent(QKeyEvent* event) { Application::getInstance()->keyPressEvent(event); } void GLCanvas::keyReleaseEvent(QKeyEvent* event) { Application::getInstance()->keyReleaseEvent(event); } void GLCanvas::focusOutEvent(QFocusEvent* event) { Application::getInstance()->focusOutEvent(event); } void GLCanvas::mouseMoveEvent(QMouseEvent* event) { Application::getInstance()->mouseMoveEvent(event); } void GLCanvas::mousePressEvent(QMouseEvent* event) { Application::getInstance()->mousePressEvent(event); } void GLCanvas::mouseReleaseEvent(QMouseEvent* event) { Application::getInstance()->mouseReleaseEvent(event); } void GLCanvas::activeChanged(Qt::ApplicationState state) { switch (state) { case Qt::ApplicationActive: // If we're active, stop the frame timer and the throttle. _frameTimer.stop(); _throttleRendering = false; break; case Qt::ApplicationSuspended: case Qt::ApplicationHidden: // If we're hidden or are about to suspend, don't render anything. _throttleRendering = false; _frameTimer.stop(); break; default: // Otherwise, throttle. if (!_throttleRendering) { _frameTimer.start(_idleRenderInterval); _throttleRendering = true; } break; } } void GLCanvas::throttleRender() { _frameTimer.start(_idleRenderInterval); if (!Application::getInstance()->getWindow()->isMinimized()) { //Need accurate frame timing for the oculus rift if (OculusManager::isConnected()) { OculusManager::beginFrameTiming(); } Application::getInstance()->paintGL(); swapBuffers(); if (OculusManager::isConnected()) { OculusManager::endFrameTiming(); } } } int updateTime = 0; bool GLCanvas::event(QEvent* event) { switch (event->type()) { case QEvent::TouchBegin: Application::getInstance()->touchBeginEvent(static_cast(event)); event->accept(); return true; case QEvent::TouchEnd: Application::getInstance()->touchEndEvent(static_cast(event)); return true; case QEvent::TouchUpdate: Application::getInstance()->touchUpdateEvent(static_cast(event)); return true; default: break; } return QGLWidget::event(event); } void GLCanvas::wheelEvent(QWheelEvent* event) { Application::getInstance()->wheelEvent(event); } void GLCanvas::dragEnterEvent(QDragEnterEvent* event) { const QMimeData* mimeData = event->mimeData(); foreach (QUrl url, mimeData->urls()) { auto urlString = url.toString(); if (Application::getInstance()->canAcceptURL(urlString)) { event->acceptProposedAction(); break; } } } void GLCanvas::dropEvent(QDropEvent* event) { Application::getInstance()->dropEvent(event); } // Pressing Alt (and Meta) key alone activates the menubar because its style inherits the // SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to // receive keyPress events for the Alt (and Meta) key in a reliable manner. // // This filter catches events before QMenuBar can steal the keyboard focus. // The idea was borrowed from // http://www.archivum.info/qt-interest@trolltech.com/2006-09/00053/Re-(Qt4)-Alt-key-focus-QMenuBar-(solved).html bool GLCanvas::eventFilter(QObject*, QEvent* event) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::ShortcutOverride: { QKeyEvent* keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta) { if (event->type() == QEvent::KeyPress) { keyPressEvent(keyEvent); } else if (event->type() == QEvent::KeyRelease) { keyReleaseEvent(keyEvent); } else { QGLWidget::event(event); } return true; } } default: break; } return false; }