diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index ae0a88a98e..ed82d097f8 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -1,75 +1,81 @@ # Build Linux -*Last Updated on December 1, 2020* +*Last Updated on January 6, 2022* Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file. You can use the [Vircadia Builder](https://github.com/vircadia/vircadia-builder) to build on Linux more easily. Alternatively, you can follow the manual steps below. -## Ubuntu 16.04/18.04 specific build guide -### Ubuntu 16.04 only -Add the following line to *.bash_profile* -`export QT_QPA_FONTDIR=/usr/share/fonts/truetype/dejavu/` -### Ubuntu 18.04 server only +## Ubuntu 18.04 + +### Ubuntu 18.04 Server only Add the universe repository: -_(This is not enabled by default on the server edition)_ +_(This is not enabled by default on the server edition.)_ ```bash sudo add-apt-repository universe sudo apt-get update ``` -#### Install build tools: + +### Install build tools: - First update the repositories: ```bash sudo apt-get update -y sudo apt-get upgrade -y ``` -- git + +- Install git ```bash sudo apt-get install git -y ``` -Verify by git --version -- g++ +Verify git was installed by running `git --version`. + +- Install g++ ```bash sudo apt-get install g++ -y ``` -Verify by g++ --version -- *Ubuntu 18.04* cmake +Verify g++ was installed by running `g++ --version`. + +- **Ubuntu 18.04** CMake ```bash sudo apt-get install cmake -y ``` -Verify by cmake --version -- *Ubuntu 16.04* cmake -```bash -wget https://cmake.org/files/v3.14/cmake-3.14.2-Linux-x86_64.sh -sudo sh cmake-3.14.2-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir -``` -#### Install build dependencies: +Verify CMake was installed by running `cmake --version`. + +### Install build dependencies: - OpenSSL: ```bash sudo apt-get install libssl-dev ``` -Verify with `openssl version` +Verify OpenSSL was installed by running `openssl version`. + - OpenGL: ```bash sudo apt-get install libgl1-mesa-dev -y -sudo ln -s /usr/lib/x86_64-linux-gnu/libGL.so.346.35 /usr/lib/x86_64-linux-gnu/libGL.so.1.2.0 ``` -- Verify OpenGL: - - First install mesa-utils with the command `sudo apt install mesa-utils -y` - - Then run `glxinfo | grep "OpenGL version"` -#### To compile interface in a server you must install: +Verify OpenGL: + - First install mesa-utils with the command `sudo apt install mesa-utils -y`. + - Then run `glxinfo | grep "OpenGL version"`. + + +### Extra dependencies to compile Interface on a server + + +- Install the following: ```bash sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1 ``` + - Misc dependencies: ```bash sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev ``` + - Install Python 3 and required packages: ```bash sudo apt-get install python python3 python3-distro ``` -- Install node, required to build the jsdoc documentation: + +- Install Node.js as it is required to build the jsdoc documentation: ```bash sudo apt-get install nodejs ``` @@ -81,16 +87,17 @@ Clone this repository: git clone https://github.com/vircadia/vircadia.git ``` -To compile a DEV version checkout the branch you need. To get a list of all tags: -```bash -git fetch -a -``` - -Then checkout the main branch with: +Then checkout the master branch with: ```bash git checkout master ``` +If you need a different branch, you can get a list of all tags with: +```bash +git fetch --tags +git tag +``` + ### Using a custom Qt build Qt binaries are only provided for Ubuntu. In order to build on other distributions, a Qt5 install @@ -104,6 +111,18 @@ The system's Qt can be used, if the development packages are installed, by setti also the last version available in the Qt 5 branch. It is expected that Linux distributions will have Qt 5.15.2 available for a long time. +### Architecture support + +If the build is intended to be packaged for distribution, the `VIRCADIA_CPU_ARCHITECTURE` +CMake variable needs to be set to an architecture specific value. + +By default, it is set to `-march=native -mtune=native`, which yields builds optimized for a particular +machine, but these builds will not work on machines lacking same CPU instructions. + +For packaging, it is recommended to set it to a different value, for example `-msse3`. This will help ensure that the build will run on all reasonably modern CPUs. + +Setting `VIRCADIA_CPU_ARCHITECTURE` to an empty string will use the default compiler settings and yield maximum compatibility. + ### Compiling Create the build directory: @@ -118,33 +137,34 @@ Prepare makefiles: cmake .. ``` -- If cmake fails with a vcpkg error - delete /tmp/hifi/vcpkg. +If cmake fails with a vcpkg error, then delete `~/vircadia-files/vcpkg/`. -Start compilation of the server and get a cup of coffee: +#### Server + +To compile the Domain server: ```bash make domain-server assignment-client ``` -To compile interface: +*Note: For a server, it is not necessary to compile the Interface.* + +#### Interface + +To compile the Interface client: ```bash make interface ``` -The commands above will compile with a single thread. If you have enough memory, -you can decrease your build time using the `-j` flag. Since most x64 CPUs -support two threads per core, this works out to CPU_COUNT*2. As an example, if -you have a 2 core machine, you could use: -``` +The commands above will compile with a single thread. If you have enough memory, you can decrease your build time using the `-j` flag. Since most x64 CPUs support two threads per core, this works out to CPU_COUNT*2. As an example, if you have a 2 core machine, you could use: +```bash make -j4 interface ``` -In a server, it does not make sense to compile interface. - ### Running the software #### Domain server -Running domain server: +Running Domain server: ```bash ./domain-server/domain-server ``` @@ -158,24 +178,16 @@ Running assignment client: #### Interface -Running interface: +Running Interface: ```bash ./interface/interface ``` -Go to localhost in the running interface. +Go to "localhost" in the running Interface to visit your newly launched Domain server. -#### Notes +### Notes -If your goal is to set up a development environment, it is desirable to set the -directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable. +If your goal is to set up a development environment, it is desirable to set the directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable. For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`. -By default, vcpkg will build in the system `/tmp` directory. -If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE` -CMake variable needs to be set to architecture specific value. -It defaults to `-march=native -mtune=native`, which yields builds optimized for particular -machine, but builds will not work on machines lacking same CPU instructions. -For packaging and AppImage it is recommended to set it to different value, for example `-msse3`. -Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield -maximum compatibility. +By default, vcpkg will build in the `~/vircadia-files/vcpkg/` directory. diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 03106b9354..b041768dce 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,6 +1,6 @@ -# Build MacOS +# Build macOS -*Last Updated on October 19, 2021* +*Last Updated on December 1, 2021* Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. This will include the necessary environment variables to customize your build. Only macOS specific instructions are found in this document. @@ -21,9 +21,9 @@ brew install cmake openssl npm Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). Execute the `Update Shell Profile.command` script that is provided with the installer. -### MacOS SDK +### macOS SDK -You will need version `10.12` of the macOS SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on macOS. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g. +You will need version `10.12` of the macOS SDK for building, otherwise you may experience crashing or other unintended issues due to the deprecation of OpenGL on macOS. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g. ```bash cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ @@ -64,14 +64,17 @@ You can append `-j4` to assign more threads to build with. The number indicates To package the installation, you can simply run `make package` afterwards. -## Notes +## Architecture Support -If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE` -CMake variable needs to be set to architecture specific value. -It defaults to `-march=native -mtune=native`, which yields builds optimized for particular -machine, but builds will not work on machines lacking same CPU instructions. -For packaging and AppImage it is recommended to set it to different value, for example `-msse3`. -Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield +If the build is intended to be packaged for distribution, the `VIRCADIA_CPU_ARCHITECTURE` +CMake variable needs to be set to an architecture specific value. + +By default, it is set to `-march=native -mtune=native`, which yields builds optimized for a particular +machine, but these builds will not work on machines lacking same CPU instructions. + +For packaging, it is recommended to set it to a different value, for example `-msse3`. This will help ensure that the build will run on all reasonably modern CPUs. + +Setting `VIRCADIA_CPU_ARCHITECTURE` to an empty string will use the default compiler settings and yield maximum compatibility. ## FAQ diff --git a/README.md b/README.md index e5d3c68b2f..28e82735f0 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,6 @@ Vircadia™ is a 3D social software project seeking to incrementally bring about - [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) - [For Mac - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) - [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb) -- [For Linux - Server .rpm - Vircadia Builder](INSTALLER.md#amazon-linux-2--rpm) - [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) ### Boot to Metaverse: [The Goal](https://vircadia.com/vision/) @@ -94,10 +93,8 @@ Keep in mind that Vircadia consists of multiple smaller projects that might have #### Supporters of the Vircadia Project -| [ksuprynowicz (74hc595)](https://github.com/ksuprynowicz) | +One (1) anonymous, three (3) total sponsors through GitHub. ❤️ + +| [Daichi Shimabukuro](https://github.com/mshlomd) | | --- | -|

[ksuprynowicz](https://github.com/ksuprynowicz)

- -#### Sponsors of Open Source - -CrossBrowserTesting +|

[Daichi Shimabukuro](https://github.com/mshlomd)

diff --git a/hifi_qt.py b/hifi_qt.py index eb3e945ddb..412eb798ce 100644 --- a/hifi_qt.py +++ b/hifi_qt.py @@ -144,12 +144,16 @@ endif() if 'x86_64' == cpu_architecture: u_major = int( distro.major_version() ) u_minor = int( distro.minor_version() ) - if (distro.id() == 'ubuntu' and u_major == 18) or distro.id() == 'linuxmint' and u_major == 19: - self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-ubuntu-18.04-amd64.tar.xz' - elif (distro.id() == 'ubuntu' and u_major > 18) or (distro.id() == 'linuxmint' and u_major > 19): - self.__no_qt_package_error() + if distro.id() == 'ubuntu' or distro.id() == 'linuxmint': + if (distro.id() == 'ubuntu' and u_major == 18) or distro.id() == 'linuxmint' and u_major == 19: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-ubuntu-18.04-amd64.tar.xz' + elif (distro.id() == 'ubuntu' and u_major > 18) or (distro.id() == 'linuxmint' and u_major > 19): + self.__no_qt_package_error() + else: + self.__unsupported_error() else: - self.__unsupported_error() + self.__no_qt_package_error() + elif 'aarch64' == cpu_architecture: if distro.id() == 'ubuntu': diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 0670252406..8808dc34ad 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -182,7 +182,7 @@ void runTimingTests() { const int EXTRA_JUNK_SIZE = 200; extraJunk.append((unsigned char)255); for (int i = 0; i < EXTRA_JUNK_SIZE; i++) { - extraJunk.append(QString("junk")); + extraJunk.append(QString("junk").toUtf8()); } { @@ -257,17 +257,17 @@ void runUnitTests() { quint64 LAST_TEST = 10; quint64 SKIP_BY = 1; - + for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) { qDebug() << "value:" << value; ByteCountCoded codedValue = value; - + QByteArray codedValueBuffer = codedValue; - + codedValueBuffer.append((unsigned char)255); - codedValueBuffer.append(QString("junk")); - + codedValueBuffer.append(QString("junk").toUtf8()); + qDebug() << "codedValueBuffer:"; outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size()); @@ -276,7 +276,7 @@ void runUnitTests() { quint64 valueDecoded = valueDecoder; qDebug() << "valueDecoded:" << valueDecoded; qDebug() << "bytesConsumed:" << bytesConsumed; - + if (value == valueDecoded) { qDebug() << "SUCCESS!"; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 95533b0fb9..7dfb908844 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -525,7 +525,7 @@ bool Wallet::readSecurityImage(const QString& inputFilePath, unsigned char** out } else { foundFooter = (line == IMAGE_FOOTER); if (!foundFooter) { - base64EncryptedBuffer.append(line); + base64EncryptedBuffer.append(line.toUtf8()); } } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 7ac6414fe7..03a463c82a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -844,7 +844,7 @@ glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const { uvec2 result; auto window = _container->getPrimaryWidget(); if (window) { - result = toGlm(window->geometry().size() * window->devicePixelRatio()); + result = toGlm(window->geometry().size() * window->devicePixelRatioF()); } return result; } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 514efb431f..ce449a66c3 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -239,7 +239,7 @@ QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth:: } else { requestURL.setPath(getMetaverseServerURLPath(true) + path.left(queryStringLocation)); } - + // qCDebug(networking) << "Creating request path" << requestURL; // qCDebug(networking) << "requestURL.isValid()" << requestURL.isValid(); // qCDebug(networking) << "requestURL.errorString()" << requestURL.errorString(); @@ -573,7 +573,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas postData.append("grant_type=password&"); postData.append("username=" + QUrl::toPercentEncoding(login) + "&"); postData.append("password=" + QUrl::toPercentEncoding(password) + "&"); - postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8()); request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -594,9 +594,9 @@ void AccountManager::requestAccessTokenWithAuthCode(const QString& authCode, con QByteArray postData; postData.append("grant_type=authorization_code&"); - postData.append("client_id=" + clientId + "&"); - postData.append("client_secret=" + clientSecret + "&"); - postData.append("code=" + authCode + "&"); + postData.append("client_id=" + clientId.toUtf8() + "&"); + postData.append("client_secret=" + clientSecret.toUtf8() + "&"); + postData.append("code=" + authCode.toUtf8() + "&"); postData.append("redirect_uri=" + QUrl::toPercentEncoding(redirectUri)); request.setUrl(grantURL); @@ -618,7 +618,7 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { QByteArray postData; postData.append("grant_type=password&"); postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); - postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8()); request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -639,9 +639,9 @@ void AccountManager::requestAccessTokenWithOculus(const QString& nonce, const QS QByteArray postData; postData.append("grant_type=password&"); - postData.append("oculus_nonce=" + nonce + "&"); - postData.append("oculus_id=" + oculusID + "&"); - postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + postData.append("oculus_nonce=" + nonce.toUtf8() + "&"); + postData.append("oculus_id=" + oculusID.toUtf8() + "&"); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8()); request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -671,7 +671,7 @@ void AccountManager::refreshAccessToken() { QByteArray postData; postData.append("grant_type=refresh_token&"); postData.append("refresh_token=" + QUrl::toPercentEncoding(_accountInfo.getAccessToken().refreshToken) + "&"); - postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8()); request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); diff --git a/libraries/networking/src/DomainAccountManager.cpp b/libraries/networking/src/DomainAccountManager.cpp index 23c52143f6..9ec5badc91 100644 --- a/libraries/networking/src/DomainAccountManager.cpp +++ b/libraries/networking/src/DomainAccountManager.cpp @@ -57,7 +57,7 @@ void DomainAccountManager::setAuthURL(const QUrl& authURL) { } _currentAuth.authURL = authURL; - qCDebug(networking) << "DomainAccountManager URL for authenticated requests has been changed to" + qCDebug(networking) << "DomainAccountManager URL for authenticated requests has been changed to" << qPrintable(_currentAuth.authURL.toString()); _currentAuth.accessToken = ""; @@ -70,7 +70,7 @@ bool DomainAccountManager::hasLogIn() { return !_currentAuth.authURL.isEmpty(); } -bool DomainAccountManager::isLoggedIn() { +bool DomainAccountManager::isLoggedIn() { return !_currentAuth.authURL.isEmpty() && hasValidAccessToken(); } @@ -92,7 +92,7 @@ void DomainAccountManager::requestAccessToken(const QString& username, const QSt formData.append("grant_type=password&"); formData.append("username=" + QUrl::toPercentEncoding(username) + "&"); formData.append("password=" + QUrl::toPercentEncoding(password) + "&"); - formData.append("client_id=" + _currentAuth.clientID); + formData.append("client_id=" + _currentAuth.clientID.toUtf8()); request.setUrl(_currentAuth.authURL); @@ -168,7 +168,7 @@ bool DomainAccountManager::hasValidAccessToken() { } // ####### TODO - + // if (!_isWaitingForTokenRefresh && needsToRefreshToken()) { // refreshAccessToken(); // } @@ -184,18 +184,18 @@ void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, cons bool DomainAccountManager::checkAndSignalForAccessToken() { bool hasToken = hasValidAccessToken(); - // ####### TODO: Handle hasToken == true. - // It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server + // ####### TODO: Handle hasToken == true. + // It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server // gets error when trying to use it then user should be prompted to login). hasToken = false; - + if (!hasToken) { // Emit a signal so somebody can call back to us and request an access token given a user name and password. // Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed. auto domain = _currentAuth.authURL.host(); QTimer::singleShot(500, this, [this, domain] { - emit this->authRequired(domain); + emit this->authRequired(domain); }); } diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 94d6371ba4..4454648564 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -52,7 +52,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall // Adding the action name QHttpPart actionPart; actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); - actionPart.setBody(QByteArray().append(action)); + actionPart.setBody(QByteArray().append(action.toUtf8())); multipart->append(actionPart); // Log the local-time that this event was logged diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index b861904255..81e49d4cff 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -63,11 +63,11 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) { QByteArray OctreeUtils::RawOctreeData::toByteArray() { QByteArray jsonString; - jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion); + jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion).toUtf8(); writeSubclassData(jsonString); - jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version); + jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version).toUtf8(); return jsonString; } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d5fa6609d4..07376cf761 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -38,14 +38,14 @@ *

This API currently has no effect and is not used.

* * @namespace OffscreenFlags - * + * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {boolean} navigationFocused - true if UI has joystick navigation focus, false if it + * @property {boolean} navigationFocused - true if UI has joystick navigation focus, false if it * doesn't. - * @property {boolean} navigationFocusDisabled - true if UI joystick navigation focus is disabled, + * @property {boolean} navigationFocusDisabled - true if UI joystick navigation focus is disabled, * false if it isn't. */ @@ -75,7 +75,7 @@ public: emit navigationFocusDisabledChanged(); } } - + signals: /*@jsdoc @@ -99,9 +99,9 @@ private: static OffscreenFlags* offscreenFlags { nullptr }; -// This hack allows the QML UI to work with keys that are also bound as -// shortcuts at the application level. However, it seems as though the -// bound actions are still getting triggered. At least for backspace. +// This hack allows the QML UI to work with keys that are also bound as +// shortcuts at the application level. However, it seems as though the +// bound actions are still getting triggered. At least for backspace. // Not sure why. // // However, the problem may go away once we switch to the new menu system, @@ -168,7 +168,7 @@ void OffscreenUi::onRootContextCreated(QQmlContext* qmlContext) { qmlContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); #ifdef DEBUG qmlContext->setContextProperty("DebugQML", QVariant(true)); -#else +#else qmlContext->setContextProperty("DebugQML", QVariant(false)); #endif @@ -284,7 +284,7 @@ QQuickItem* OffscreenUi::createMessageBox(Icon icon, const QString& title, const map.insert("buttons", buttons.operator int()); map.insert("defaultButton", defaultButton); QVariant result; - bool invokeResult; + bool invokeResult = false; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); if (tablet->getToolbarMode() && _desktop) { @@ -309,7 +309,7 @@ int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) { if (!messageBox) { return QMessageBox::NoButton; } - + return MessageBoxListener(messageBox).waitForButtonResult(); } @@ -424,8 +424,8 @@ QString OffscreenUi::getText(const Icon icon, const QString& title, const QStrin QString OffscreenUi::getItem(const Icon icon, const QString& title, const QString& label, const QStringList& items, int current, bool editable, bool* ok) { - if (ok) { - *ok = false; + if (ok) { + *ok = false; } auto offscreenUi = DependencyManager::get(); @@ -580,7 +580,7 @@ QQuickItem* OffscreenUi::createInputDialog(const Icon icon, const QString& title auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - bool invokeResult; + bool invokeResult = false; if (tablet->getToolbarMode() && _desktop) { invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog", Q_RETURN_ARG(QVariant, result), @@ -608,7 +608,7 @@ QQuickItem* OffscreenUi::createCustomInputDialog(const Icon icon, const QString& auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - bool invokeResult; + bool invokeResult = false; if (tablet->getToolbarMode() && _desktop) { invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog", Q_RETURN_ARG(QVariant, result), @@ -619,7 +619,7 @@ QQuickItem* OffscreenUi::createCustomInputDialog(const Icon icon, const QString& Q_ARG(QVariant, QVariant::fromValue(map))); emit tabletScriptingInterface->tabletNotification(); } - + if (!invokeResult) { qWarning() << "Failed to create custom message box"; return nullptr; @@ -648,13 +648,13 @@ void OffscreenUi::setNavigationFocused(bool focused) { // FIXME HACK.... // This hack is an attempt to work around the 'offscreen UI can't gain keyboard focus' bug // https://app.asana.com/0/27650181942747/83176475832393 -// The problem seems related to https://bugreports.qt.io/browse/QTBUG-50309 +// The problem seems related to https://bugreports.qt.io/browse/QTBUG-50309 // // The workaround seems to be to give some other window (same process or another process doesn't seem to matter) -// focus and then put focus back on the interface main window. +// focus and then put focus back on the interface main window. // -// If I could reliably reproduce this bug I could eventually track down what state change is occuring -// during the process of the main window losing and then gaining focus, but failing that, here's a +// If I could reliably reproduce this bug I could eventually track down what state change is occuring +// during the process of the main window losing and then gaining focus, but failing that, here's a // brute force way of triggering that state change at application start in a way that should be nearly // imperceptible to the user. class KeyboardFocusHack : public QObject { @@ -754,7 +754,7 @@ private slots: QString OffscreenUi::fileDialog(const QVariantMap& properties) { QVariant buildDialogResult; - bool invokeResult; + bool invokeResult = false; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); if (tablet->getToolbarMode() && _desktop) { @@ -783,7 +783,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) { QVariant buildDialogResult; - bool invokeResult; + bool invokeResult = false; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); if (tablet->getToolbarMode() && _desktop) { @@ -1003,7 +1003,7 @@ class AssetDialogListener : public ModalDialogListener { QString OffscreenUi::assetDialog(const QVariantMap& properties) { // ATP equivalent of fileDialog(). QVariant buildDialogResult; - bool invokeResult; + bool invokeResult = false; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); if (tablet->getToolbarMode() && _desktop) { @@ -1033,7 +1033,7 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) { ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) { // ATP equivalent of fileDialog(). QVariant buildDialogResult; - bool invokeResult; + bool invokeResult = false; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); if (tablet->getToolbarMode() && _desktop) { @@ -1165,7 +1165,7 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { } // QML input elements absorb key press, but apparently not key release. - // therefore we want to ensure that key release events for key presses that were + // therefore we want to ensure that key release events for key presses that were // accepted by the QML layer are suppressed if (type == QEvent::KeyRelease && pressed) { pressed = false; @@ -1175,10 +1175,6 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { return result; } -unsigned int OffscreenUi::getMenuUserDataId() const { - return _vrMenu->_userDataId; -} - ModalDialogListener::ModalDialogListener(QQuickItem *dialog) : _dialog(dialog) { if (!dialog) { _finished = true; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 7e20a0b09d..994a579f44 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -74,7 +74,7 @@ public: // Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag void setPinned(bool pinned = true); - + void togglePinned(); void setConstrainToolbarToCenterX(bool constrained); @@ -237,7 +237,6 @@ public: static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); - unsigned int getMenuUserDataId() const; QList &getModalDialogListeners(); signals: @@ -270,7 +269,7 @@ private: QList _modalDialogListeners; std::unordered_map _pressedKeys; VrMenu* _vrMenu { nullptr }; - QQueue> _queuedMenuInitializers; + QQueue> _queuedMenuInitializers; }; #endif diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 8003337eac..6ba01165d5 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -17,139 +17,116 @@ #include "OffscreenUi.h" #include "ui/Logging.h" -static unsigned int USER_DATA_ID = 0; -// Binds together a Qt Action or Menu with the QML Menu or MenuItem -// -// TODO On reflection, it may be pointless to use the UUID. Perhaps -// simply creating the bidirectional link pointing to both the widget -// and qml object and inject the pointer into both objects -class MenuUserData : public QObjectUserData { -public: - MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent) { - if (!USER_DATA_ID) { - USER_DATA_ID = DependencyManager::get()->getMenuUserDataId(); - } - _action = action; - _qml = qmlObject; - _qmlParent = qmlParent; - action->setUserData(USER_DATA_ID, this); - qmlObject->setUserData(USER_DATA_ID, this); - qmlObject->setObjectName(uuid.toString()); - // Make sure we can find it again in the future +MenuUserData::MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent) { + _action = action; + _qml = qmlObject; + _qmlParent = qmlParent; + + action->setProperty(USER_DATA, QVariant::fromValue(this)); + qmlObject->setProperty(USER_DATA, QVariant::fromValue(this)); + qmlObject->setObjectName(uuid.toString()); + // Make sure we can find it again in the future + updateQmlItemFromAction(); + _changedConnection = QObject::connect(action, &QAction::changed, [=] { updateQmlItemFromAction(); - _changedConnection = QObject::connect(action, &QAction::changed, [=] { - updateQmlItemFromAction(); - }); - _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { - QObject::disconnect(_changedConnection); - }); - - class ExclusionGroupSetter : public QObject { - public: - ExclusionGroupSetter(QObject* from, QObject* to, QObject* qmlParent) : QObject(from), _from(from), _to(to), _qmlParent(qmlParent) { - _from->installEventFilter(this); - } - - ~ExclusionGroupSetter() { - _from->removeEventFilter(this); - } - protected: - virtual bool eventFilter(QObject* o, QEvent* e) override { - if (e->type() == QEvent::DynamicPropertyChange) { - QDynamicPropertyChangeEvent* dpc = static_cast(e); - if (dpc->propertyName() == "exclusionGroup") - { - // unfortunately Qt doesn't support passing dynamic properties between C++ / QML, so we have to use this ugly helper function - QMetaObject::invokeMethod(_qmlParent, - "addExclusionGroup", - Qt::DirectConnection, - Q_ARG(QVariant, QVariant::fromValue(_to)), - Q_ARG(QVariant, _from->property(dpc->propertyName()))); - } - } - - return QObject::eventFilter(o, e); - } - - private: - QObject* _from; - QObject* _to; - QObject* _qmlParent; - }; - - new ExclusionGroupSetter(action, qmlObject, qmlParent); - } - - ~MenuUserData() { + }); + _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { QObject::disconnect(_changedConnection); - QObject::disconnect(_shutdownConnection); - _action->setUserData(USER_DATA_ID, nullptr); - _qml->setUserData(USER_DATA_ID, nullptr); - } + }); - void updateQmlItemFromAction() { - _qml->setProperty("checkable", _action->isCheckable()); - _qml->setProperty("enabled", _action->isEnabled()); - QString text = _action->text(); - _qml->setProperty("text", text); - _qml->setProperty("shortcut", _action->shortcut().toString()); - _qml->setProperty("checked", _action->isChecked()); - _qml->setProperty("visible", _action->isVisible()); - } - - void clear() { - _qml->setProperty("checkable", 0); - _qml->setProperty("enabled", 0); - _qml->setProperty("text", 0); - _qml->setProperty("shortcut", 0); - _qml->setProperty("checked", 0); - _qml->setProperty("visible", 0); - - _action->setUserData(USER_DATA_ID, nullptr); - _qml->setUserData(USER_DATA_ID, nullptr); - } - - - const QUuid uuid{ QUuid::createUuid() }; - - static bool hasData(QAction* object) { - if (!object) { - qWarning() << "Attempted to fetch MenuUserData for null object"; - return false; + class ExclusionGroupSetter : public QObject { + public: + ExclusionGroupSetter(QObject* from, QObject* to, QObject* qmlParent) : QObject(from), _from(from), _to(to), _qmlParent(qmlParent) { + _from->installEventFilter(this); } - return (nullptr != static_cast(object->userData(USER_DATA_ID))); - } - static MenuUserData* forObject(QAction* object) { - if (!object) { - qWarning() << "Attempted to fetch MenuUserData for null object"; - return nullptr; + ~ExclusionGroupSetter() { + _from->removeEventFilter(this); } - auto result = static_cast(object->userData(USER_DATA_ID)); - if (!result) { - qWarning() << "Unable to find MenuUserData for object " << object; - if (auto action = dynamic_cast(object)) { - qWarning() << action->text(); - } else if (auto menu = dynamic_cast(object)) { - qWarning() << menu->title(); + protected: + virtual bool eventFilter(QObject* o, QEvent* e) override { + if (e->type() == QEvent::DynamicPropertyChange) { + QDynamicPropertyChangeEvent* dpc = static_cast(e); + if (dpc->propertyName() == "exclusionGroup") { + // unfortunately Qt doesn't support passing dynamic properties between C++ / QML, so we have to use this ugly helper function + QMetaObject::invokeMethod(_qmlParent, + "addExclusionGroup", + Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(_to)), + Q_ARG(QVariant, _from->property(dpc->propertyName()))); + } } - return nullptr; + + return QObject::eventFilter(o, e); } - return result; + + private: + QObject* _from; + QObject* _to; + QObject* _qmlParent; + }; + + new ExclusionGroupSetter(action, qmlObject, qmlParent); +} + +MenuUserData::~MenuUserData() { + QObject::disconnect(_changedConnection); + QObject::disconnect(_shutdownConnection); + _action->setProperty(USER_DATA, QVariant()); + _qml->setProperty(USER_DATA, QVariant()); +} + +void MenuUserData::updateQmlItemFromAction() { + _qml->setProperty("checkable", _action->isCheckable()); + _qml->setProperty("enabled", _action->isEnabled()); + QString text = _action->text(); + _qml->setProperty("text", text); + _qml->setProperty("shortcut", _action->shortcut().toString()); + _qml->setProperty("checked", _action->isChecked()); + _qml->setProperty("visible", _action->isVisible()); +} + +void MenuUserData::clear() { + _qml->setProperty("checkable", 0); + _qml->setProperty("enabled", 0); + _qml->setProperty("text", 0); + _qml->setProperty("shortcut", 0); + _qml->setProperty("checked", 0); + _qml->setProperty("visible", 0); + + _action->setProperty(USER_DATA, QVariant()); + _qml->setProperty(USER_DATA, QVariant()); +} + + + +bool MenuUserData::hasData(QAction* object) { + if (!object) { + qWarning() << "Attempted to fetch MenuUserData for null object"; + return false; } + return (nullptr != object->property(USER_DATA).value()); +} -private: - Q_DISABLE_COPY(MenuUserData); - - QMetaObject::Connection _shutdownConnection; - QMetaObject::Connection _changedConnection; - QAction* _action { nullptr }; - QObject* _qml { nullptr }; - QObject* _qmlParent{ nullptr }; -}; - +MenuUserData* MenuUserData::forObject(QAction* object) { + if (!object) { + qWarning() << "Attempted to fetch MenuUserData for null object"; + return nullptr; + } + auto result = object->property(USER_DATA).value(); + if (!result) { + qWarning() << "Unable to find MenuUserData for object " << object; + if (auto action = dynamic_cast(object)) { + qWarning() << action->text(); + } else if (auto menu = dynamic_cast(object)) { + qWarning() << menu->title(); + } + return nullptr; + } + return result; +} VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) { _rootMenu = parent->getRootItem()->findChild("rootMenu"); diff --git a/libraries/ui/src/VrMenu.h b/libraries/ui/src/VrMenu.h index c2f9fd830b..4e7031f650 100644 --- a/libraries/ui/src/VrMenu.h +++ b/libraries/ui/src/VrMenu.h @@ -18,8 +18,44 @@ #include #include #include +#include #include "OffscreenUi.h" + + +// Binds together a Qt Action or Menu with the QML Menu or MenuItem +// +// TODO On reflection, it may be pointless to use the UUID. Perhaps +// simply creating the bidirectional link pointing to both the widget +// and qml object and inject the pointer into both objects +class MenuUserData : public QObject { + Q_OBJECT +public: + MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent); + ~MenuUserData(); + void updateQmlItemFromAction(); + void clear(); + + const QUuid uuid{ QUuid::createUuid() }; + + static bool hasData(QAction* object); + + static MenuUserData* forObject(QAction* object); + +private: + Q_DISABLE_COPY(MenuUserData); + + static constexpr const char *USER_DATA{"user_data"}; + + QMetaObject::Connection _shutdownConnection; + QMetaObject::Connection _changedConnection; + QAction* _action { nullptr }; + QObject* _qml { nullptr }; + QObject* _qmlParent{ nullptr }; +}; + + + // FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML class VrMenu : public QObject { Q_OBJECT @@ -37,7 +73,6 @@ protected: friend class MenuUserData; friend class OffscreenUi; - const unsigned int _userDataId { QObject::registerUserData() }; }; #endif // hifi_VrMenu_h diff --git a/scripts/system/inventory/package-lock.json b/scripts/system/inventory/package-lock.json index 807d100bae..79ee53cdd2 100644 --- a/scripts/system/inventory/package-lock.json +++ b/scripts/system/inventory/package-lock.json @@ -6488,9 +6488,9 @@ } }, "follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "for-in": { @@ -10662,9 +10662,9 @@ "dev": true }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "requires": { "glob": "^7.0.0",