ksuprynowiczFix UI input on Vulkan3c0a8391 year agoHistory
1029 lines · 37.9 KB · C++
RawDownload
////  Application_Events.cpp//  interface/src////  Split from Application.cpp by HifiExperiments on 3/30/24//  Created by Andrzej Kapolka on 5/10/13.//  Copyright 2013 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.//  Copyright 2022-2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "Application.h" #include <QtCore/QMimeData> #include <controllers/InputRecorder.h>#include <display-plugins/CompositorHelper.h>#include <graphics/RenderEventHandler.h>#include <input-plugins/KeyboardMouseDevice.h>#include <input-plugins/TouchscreenDevice.h>#include <input-plugins/TouchscreenVirtualPadDevice.h>#include <MainWindow.h>#include <OffscreenUi.h>#include <plugins/PluginManager.h>#include <ScriptEngines.h>#include <scripting/Audio.h>#include <scripting/ControllerScriptingInterface.h>#include <shared/FileUtils.h>#include <ui/DialogsManager.h> #include "AudioClient.h"#include "GLCanvas.h"#include "Menu.h" #if defined(Q_OS_ANDROID)#include "AndroidHelper.h"#endif Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") static const unsigned int THROTTLED_SIM_FRAMERATE = 15;static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; class LambdaEvent : public QEvent {    std::function<void()> _fun;public:    LambdaEvent(const std::function<void()> & fun) :        QEvent(static_cast<QEvent::Type>(ApplicationEvent::Lambda)), _fun(fun) {}    LambdaEvent(std::function<void()> && fun) :        QEvent(static_cast<QEvent::Type>(ApplicationEvent::Lambda)), _fun(fun) {}    void call() const { _fun(); }}; bool Application::notify(QObject* object, QEvent* event) {    if (thread() == QThread::currentThread()) {        PROFILE_RANGE_IF_LONGER(app, "notify", 2)        return QApplication::notify(object, event);    }     return QApplication::notify(object, event);} static inline bool isKeyEvent(QEvent::Type type) {    return type == QEvent::KeyPress || type == QEvent::KeyRelease;} bool Application::event(QEvent* event) {    if (_aboutToQuit) {        return false;    }     // This helps avoid deadlock issue early during Application initialization    if (!_isMenuInitialized) {        return QApplication::event(event);    }     if (!Menu::getInstance()) {        return false;    }     if ((event->type() == QEvent::InputMethod || event->type() == QEvent::InputMethodQuery) && handleInputMethodEventForFocusedEntity(event)) {        return true;    }     // Allow focused Entities to handle keyboard input    if (isKeyEvent(event->type()) && handleKeyEventForFocusedEntity(event)) {        return true;    }     int type = event->type();    switch (type) {        case ApplicationEvent::Lambda:            static_cast<LambdaEvent*>(event)->call();            return true;         // Explicit idle keeps the idle running at a lower interval, but without any rendering        // see (windowMinimizedChanged)        case ApplicationEvent::Idle:            idle(); #ifdef DEBUG_EVENT_QUEUE_DEPTH            // The event queue may very well grow beyond 400, so            // this code should only be enabled on local builds            {                int count = ::hifi::qt::getEventQueueSize(QThread::currentThread());                if (count > 400) {                    ::hifi::qt::dumpEventQueue(QThread::currentThread());                }            }#endif // DEBUG_EVENT_QUEUE_DEPTH             _pendingIdleEvent.store(false);             return true;         case QEvent::MouseMove:            mouseMoveEvent(static_cast<QMouseEvent*>(event));            return true;        case QEvent::MouseButtonPress:            mousePressEvent(static_cast<QMouseEvent*>(event));            return true;        case QEvent::MouseButtonDblClick:            mouseDoublePressEvent(static_cast<QMouseEvent*>(event));            return true;        case QEvent::MouseButtonRelease:            mouseReleaseEvent(static_cast<QMouseEvent*>(event));            return true;        case QEvent::KeyPress:            keyPressEvent(static_cast<QKeyEvent*>(event));            return true;        case QEvent::KeyRelease:            keyReleaseEvent(static_cast<QKeyEvent*>(event));            return true;        case QEvent::FocusOut:            focusOutEvent(static_cast<QFocusEvent*>(event));            return true;        case QEvent::FocusIn:        { //testing to see if we can set focus when focus is not set to root window.#ifdef USE_GL            _primaryWidget->activateWindow(); //VKTODO#endif            _primaryWidget->setFocus();            return true;        }         case QEvent::TouchBegin:            touchBeginEvent(static_cast<QTouchEvent*>(event));            event->accept();            return true;        case QEvent::TouchEnd:            touchEndEvent(static_cast<QTouchEvent*>(event));            return true;        case QEvent::TouchUpdate:            touchUpdateEvent(static_cast<QTouchEvent*>(event));            return true;        case QEvent::Gesture:            touchGestureEvent((QGestureEvent*)event);            return true;        case QEvent::Wheel:            wheelEvent(static_cast<QWheelEvent*>(event));            return true;        case QEvent::Drop:            dropEvent(static_cast<QDropEvent*>(event));            return true;         case QEvent::FileOpen:            if (handleFileOpenEvent(static_cast<QFileOpenEvent*>(event))) {                return true;            }            break;         default:            break;    }     return QApplication::event(event);} bool Application::eventFilter(QObject* object, QEvent* event) {    auto eventType = event->type();     if (_aboutToQuit && eventType != QEvent::DeferredDelete && eventType != QEvent::Destroy) {        return true;    } #if defined(Q_OS_MAC)    // On Mac OS, Cmd+LeftClick is treated as a RightClick (more specifically, it seems to    // be Cmd+RightClick without the modifier being dropped). Starting in Qt 5.12, only    // on Mac, the MouseButtonRelease event for these mouse presses is sent to the top    // level QWidgetWindow, but are not propagated further. This means that the Application    // will see a MouseButtonPress, but no MouseButtonRelease, causing the client to get    // stuck in "mouse-look." The cause of the problem is in the way QWidgetWindow processes    // events where QMouseEvent::button() is not equal to QMouseEvent::buttons(). In this case    // QMouseEvent::button() is Qt::RightButton, while QMouseEvent::buttons() is (correctly?)    // Qt::LeftButton.    //    // The change here gets around this problem by capturing these    // pseudo-RightClicks, and re-emitting them as "pure" RightClicks, where    // QMouseEvent::button() == QMouseEvent::buttons() == Qt::RightButton.    //    if (eventType == QEvent::MouseButtonPress) {        auto mouseEvent = static_cast<QMouseEvent*>(event);        if (mouseEvent->button() == Qt::RightButton            && mouseEvent->buttons() == Qt::LeftButton            && mouseEvent->modifiers() == Qt::MetaModifier) {             QMouseEvent* newEvent = new QMouseEvent(                    QEvent::MouseButtonPress, mouseEvent->localPos(), mouseEvent->windowPos(),                    mouseEvent->screenPos(), Qt::RightButton, Qt::MouseButtons(Qt::RightButton),                    mouseEvent->modifiers());            QApplication::postEvent(object, newEvent);            return true;        }    }#endif     if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease || eventType == QEvent::MouseMove) {        getRefreshRateManager().resetInactiveTimer();    }     if (event->type() == QEvent::Leave) {        getApplicationCompositor().handleLeaveEvent();    }     if (event->type() == QEvent::ShortcutOverride) {#if !defined(DISABLE_QML)        if (getOffscreenUI()->shouldSwallowShortcut(event)) {            event->accept();            return true;        }#endif         // Filter out captured keys before they're used for shortcut actions.        if (_controllerScriptingInterface->isKeyCaptured(static_cast<QKeyEvent*>(event))) {            event->accept();            return true;        }    }     if (event->type() == QEvent::WindowStateChange) {        if (getWindow()->windowState() & Qt::WindowMinimized) {            getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::MINIMIZED);        } else {            auto* windowStateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);            if (windowStateChangeEvent->oldState() & Qt::WindowMinimized) {                getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE);                getRefreshRateManager().resetInactiveTimer();            }        }    }     return false;} void Application::postLambdaEvent(const std::function<void()>& f) {    if (this->thread() == QThread::currentThread()) {        f();    } else {        QCoreApplication::postEvent(this, new LambdaEvent(f));    }} void Application::sendLambdaEvent(const std::function<void()>& f) {    if (this->thread() == QThread::currentThread()) {        f();    } else {        LambdaEvent event(f);        QCoreApplication::sendEvent(this, &event);    }} void Application::pushPostUpdateLambda(void* key, const std::function<void()>& func) {    std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);    _postUpdateLambdas[key] = func;} // thread-safevoid Application::onPresent(quint32 frameCount) {    bool expected = false;    if (_pendingIdleEvent.compare_exchange_strong(expected, true)) {        postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority);    }    expected = false;    if (_graphicsEngine->checkPendingRenderEvent() && !isAboutToQuit()) {        postEvent(_graphicsEngine->_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render));    }} void Application::activeChanged(Qt::ApplicationState state) {    switch (state) {        case Qt::ApplicationActive:            _isForeground = true;            if (!_aboutToQuit && _startUpFinished) {                getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE);            }            break;         case Qt::ApplicationSuspended:            break;        case Qt::ApplicationHidden:            break;        case Qt::ApplicationInactive:            if (!_aboutToQuit && _startUpFinished) {                getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::UNFOCUS);            }            break;        default:            _isForeground = false;            break;    }} void Application::windowMinimizedChanged(bool minimized) {    // initialize the _minimizedWindowTimer    static std::once_flag once;    std::call_once(once, [&] {        connect(&_minimizedWindowTimer, &QTimer::timeout, this, [] {            QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);        });    });     // avoid rendering to the display plugin but continue posting Idle events,    // so that physics continues to simulate and the deadlock watchdog knows we're alive    if (!minimized && !getActiveDisplayPlugin()->isActive()) {        _minimizedWindowTimer.stop();        getActiveDisplayPlugin()->activate();    } else if (minimized && getActiveDisplayPlugin()->isActive()) {        getActiveDisplayPlugin()->deactivate();        _minimizedWindowTimer.start(THROTTLED_SIM_FRAME_PERIOD_MS);    }} void Application::keyPressEvent(QKeyEvent* event) {    if (!event->isAutoRepeat()) {        _keysPressed.insert(event->key(), *event);    }     _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts    // if one of our scripts have asked to capture this event, then stop processing it    if (_controllerScriptingInterface->isKeyCaptured(event) || isInterstitialMode()) {        return;    }     if (hasFocus() && getLoginDialogPoppedUp()) {        if (_keyboardMouseDevice->isActive()) {            _keyboardMouseDevice->keyReleaseEvent(event);        }         bool isControlOrCommand = event->modifiers().testFlag(Qt::ControlModifier);        bool isOption = event->modifiers().testFlag(Qt::AltModifier);        switch (event->key()) {            case Qt::Key_4:            case Qt::Key_5:            case Qt::Key_6:            case Qt::Key_7:                if (isControlOrCommand || isOption) {                    unsigned int index = static_cast<unsigned int>(event->key() - Qt::Key_1);                    const auto& displayPlugins = PluginManager::getInstance()->getDisplayPlugins();                    if (index < displayPlugins.size()) {                        auto targetPlugin = displayPlugins.at(index);                        QString targetName = targetPlugin->getName();                        auto menu = Menu::getInstance();                        QAction* action = menu->getActionForOption(targetName);                        if (action && !action->isChecked()) {                            action->trigger();                        }                    }                }                break;        }    } else if (hasFocus()) {        if (_keyboardMouseDevice->isActive()) {            _keyboardMouseDevice->keyPressEvent(event);        }         bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);        bool isControlOrCommand = event->modifiers().testFlag(Qt::ControlModifier);        bool isMetaOrMacControl = event->modifiers().testFlag(Qt::MetaModifier);        bool isOption = event->modifiers().testFlag(Qt::AltModifier);        switch (event->key()) {            case Qt::Key_Enter:            case Qt::Key_Return:                if (isOption) {                    if (_window->isFullScreen()) {                        unsetFullscreen();                    } else {                        setFullscreen(nullptr);                    }                }                break;             case Qt::Key_1: {                Menu* menu = Menu::getInstance();                menu->triggerOption(MenuOption::FirstPersonLookAt);                break;            }            case Qt::Key_2: {                Menu* menu = Menu::getInstance();                menu->triggerOption(MenuOption::SelfieCamera);                break;            }            case Qt::Key_3: {                Menu* menu = Menu::getInstance();                menu->triggerOption(MenuOption::LookAtCamera);                break;            }            case Qt::Key_4:            case Qt::Key_5:            case Qt::Key_6:            case Qt::Key_7:                if (isControlOrCommand || isOption) {                    unsigned int index = static_cast<unsigned int>(event->key() - Qt::Key_1);                    const auto& displayPlugins = PluginManager::getInstance()->getDisplayPlugins();                    if (index < displayPlugins.size()) {                        auto targetPlugin = displayPlugins.at(index);                        QString targetName = targetPlugin->getName();                        auto menu = Menu::getInstance();                        QAction* action = menu->getActionForOption(targetName);                        if (action && !action->isChecked()) {                            action->trigger();                        }                    }                }                break;             case Qt::Key_G:                if (isShifted && isControlOrCommand && isOption && isMetaOrMacControl) {                    static const QString HIFI_FRAMES_FOLDER_VAR = "HIFI_FRAMES_FOLDER";                    static const QString GPU_FRAME_FOLDER = QProcessEnvironment::systemEnvironment().contains(HIFI_FRAMES_FOLDER_VAR)                        ? QProcessEnvironment::systemEnvironment().value(HIFI_FRAMES_FOLDER_VAR)                        : "hifiFrames";                    static QString GPU_FRAME_TEMPLATE = GPU_FRAME_FOLDER + "/{DATE}_{TIME}";                    QString fullPath = FileUtils::computeDocumentPath(FileUtils::replaceDateTimeTokens(GPU_FRAME_TEMPLATE));                    if (FileUtils::canCreateFile(fullPath)) {                        getActiveDisplayPlugin()->captureFrame(fullPath.toStdString());                    }                }                break;            case Qt::Key_X:                if (isShifted && isControlOrCommand) {                    auto offscreenUi = getOffscreenUI();                    offscreenUi->togglePinned();                    //offscreenUi->getSurfaceContext()->engine()->clearComponentCache();                    //OffscreenUi::information("Debugging", "Component cache cleared");                    // placeholder for dialogs being converted to QML.                }                break;             case Qt::Key_Y:                if (isShifted && isControlOrCommand) {                    getActiveDisplayPlugin()->cycleDebugOutput();                }                break;             case Qt::Key_B:                if (isOption) {                    controller::InputRecorder* inputRecorder = controller::InputRecorder::getInstance();                    inputRecorder->stopPlayback();                }                break;             case Qt::Key_L:                if (isShifted && isControlOrCommand) {                    Menu::getInstance()->triggerOption(MenuOption::Log);                } else if (isControlOrCommand) {                    auto dialogsManager = DependencyManager::get<DialogsManager>();                    dialogsManager->toggleAddressBar();                }                break;             case Qt::Key_R:                if (isControlOrCommand && !event->isAutoRepeat()) {                    DependencyManager::get<ScriptEngines>()->reloadAllScripts();                    getOffscreenUI()->clearCache();                }                break;             case Qt::Key_Asterisk:                Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);                break;             case Qt::Key_M:                if (isControlOrCommand) {                    auto audioClient = DependencyManager::get<AudioClient>();                    audioClient->setMuted(!audioClient->isMuted());                    QSharedPointer<scripting::Audio> audioScriptingInterface = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());                    if (audioScriptingInterface && audioScriptingInterface->getPTT()) {                       audioScriptingInterface->setPushingToTalk(!audioClient->isMuted());                    }                }                break;             case Qt::Key_S:                if (isShifted && isControlOrCommand && !isOption) {                    Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);                }

Lines 1–500 of 1029.