mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 13:43:49 +02:00
Merge branch 'master' into Anim_memory_reduction
This commit is contained in:
commit
c6f72e0e77
58 changed files with 995 additions and 227 deletions
|
@ -34,7 +34,7 @@ if (WIN32)
|
|||
list(APPEND CMAKE_PREFIX_PATH "${WINDOW_SDK_PATH}")
|
||||
|
||||
# /wd4351 disables warning C4351: new behavior: elements of array will be default initialized
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4351")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP${HIFI_MAX_BUILD_CORES} /wd4351")
|
||||
# /LARGEADDRESSAWARE enables 32-bit apps to use more than 2GB of memory.
|
||||
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
|
||||
# TODO: Remove when building 64-bit.
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
|
||||
#include <shared/FileUtils.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <shared/PlatformHelper.h>
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <StatTracker.h>
|
||||
#include <Trace.h>
|
||||
|
@ -257,10 +258,6 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "MacHelper.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <android/log.h>
|
||||
#include "AndroidHelper.h"
|
||||
|
@ -552,13 +549,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
if (message->message == WM_POWERBROADCAST) {
|
||||
if (message->wParam == PBT_APMRESUMEAUTOMATIC) {
|
||||
qCInfo(interfaceapp) << "Waking up from sleep or hybernation.";
|
||||
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
if (message->message == WM_COPYDATA) {
|
||||
COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam);
|
||||
QUrl url = QUrl((const char*)(pcds->lpData));
|
||||
|
@ -964,9 +954,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<KeyboardScriptingInterface>();
|
||||
DependencyManager::set<GrabManager>();
|
||||
DependencyManager::set<AvatarPackager>();
|
||||
#ifdef Q_OS_MAC
|
||||
DependencyManager::set<MacHelper>();
|
||||
#endif
|
||||
PlatformHelper::setup();
|
||||
|
||||
QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
|
||||
});
|
||||
|
||||
|
||||
QString setBookmarkValue = getCmdOption(argc, constArgv, "--setBookmark");
|
||||
if (!setBookmarkValue.isEmpty()) {
|
||||
|
@ -1172,6 +1165,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId());
|
||||
deadlockWatchdogThread->start();
|
||||
|
||||
// Pause the deadlock watchdog when we sleep, or it might
|
||||
// trigger a false positive when we wake back up
|
||||
auto platformHelper = PlatformHelper::instance();
|
||||
|
||||
connect(platformHelper, &PlatformHelper::systemWillSleep, [] {
|
||||
DeadlockWatchdogThread::pause();
|
||||
});
|
||||
|
||||
connect(platformHelper, &PlatformHelper::systemWillWake, [] {
|
||||
DeadlockWatchdogThread::resume();
|
||||
});
|
||||
|
||||
// Main thread timer to keep the watchdog updated
|
||||
QTimer* watchdogUpdateTimer = new QTimer(this);
|
||||
|
@ -2868,9 +2872,7 @@ Application::~Application() {
|
|||
_gameWorkload.shutdown();
|
||||
|
||||
DependencyManager::destroy<Preferences>();
|
||||
#ifdef Q_OS_MAC
|
||||
DependencyManager::destroy<MacHelper>();
|
||||
#endif
|
||||
PlatformHelper::shutdown();
|
||||
|
||||
_entityClipboard->eraseAllOctreeElements();
|
||||
_entityClipboard.reset();
|
||||
|
@ -3562,6 +3564,9 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
|
|||
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
|
||||
surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance());
|
||||
// This `module` context property is blank for the QML scripting interface so that we don't get log errors when importing
|
||||
// certain JS files from both scripts (in the JS context) and QML (in the QML context).
|
||||
surfaceContext->setContextProperty("module", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// MacHelper.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Howard Stearns
|
||||
// Copyright 2019 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 "InterfaceLogging.h"
|
||||
#include "MacHelper.h"
|
||||
#include <NodeList.h>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
// The type definitions in these variables come from IOKit, which includes a definition of Duration that conflicts with ours.
|
||||
// So... we include these definitions here rather than in the .h, as the .h is included in Application.cpp which
|
||||
// uses Duration.
|
||||
static io_connect_t root_port;
|
||||
static IONotificationPortRef notifyPortRef;
|
||||
static io_object_t notifierObject;
|
||||
static void* refCon;
|
||||
|
||||
static void sleepHandler(void* refCon, io_service_t service, natural_t messageType, void* messageArgument) {
|
||||
if (messageType == kIOMessageSystemHasPoweredOn) {
|
||||
qCInfo(interfaceapp) << "Waking up from sleep or hybernation.";
|
||||
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MacHelper::MacHelper() {
|
||||
#ifdef Q_OS_MAC
|
||||
root_port = IORegisterForSystemPower(refCon, ¬ifyPortRef, sleepHandler, ¬ifierObject);
|
||||
if (root_port == 0) {
|
||||
qCWarning(interfaceapp) << "IORegisterForSystemPower failed";
|
||||
return;
|
||||
}
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(),
|
||||
IONotificationPortGetRunLoopSource(notifyPortRef),
|
||||
kCFRunLoopCommonModes);
|
||||
#endif
|
||||
}
|
||||
|
||||
MacHelper::~MacHelper() {
|
||||
#ifdef Q_OS_MAC
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
|
||||
IONotificationPortGetRunLoopSource(notifyPortRef),
|
||||
kCFRunLoopCommonModes);
|
||||
IODeregisterForSystemPower(¬ifierObject);
|
||||
IOServiceClose(root_port);
|
||||
IONotificationPortDestroy(notifyPortRef);
|
||||
#endif
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
//
|
||||
// MacHelper.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Howard Stearns
|
||||
// Copyright 2019 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
class MacHelper : public Dependency {
|
||||
public:
|
||||
MacHelper();
|
||||
~MacHelper();
|
||||
};
|
||||
|
|
@ -101,8 +101,8 @@ void Audio::setMutedDesktop(bool isMuted) {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (!isMuted && _settingsLoaded) {
|
||||
// Disable Push-To-Talk if muted is changed to false. Settings also need to be loaded.
|
||||
if (!isMuted && _settingsLoaded && !_pushingToTalk) {
|
||||
// If the user is not pushing to talk and muted is changed to false, disable Push-To-Talk. Settings also need to be loaded.
|
||||
setPTTDesktop(isMuted);
|
||||
}
|
||||
if (changed) {
|
||||
|
@ -132,8 +132,8 @@ void Audio::setMutedHMD(bool isMuted) {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (!isMuted && _settingsLoaded) {
|
||||
// Disable Push-To-Talk if muted is changed to false. Settings also need to be loaded.
|
||||
if (!isMuted && _settingsLoaded && !_pushingToTalk) {
|
||||
// If the user is not pushing to talk and muted is changed to false, disable Push-To-Talk. Settings also need to be loaded.
|
||||
setPTTHMD(isMuted);
|
||||
}
|
||||
if (changed) {
|
||||
|
|
|
@ -225,6 +225,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
QObject::connect(_dockWidget.get(), SIGNAL(onResizeEvent()), this, SLOT(emitMainWindowResizeEvent()));
|
||||
|
||||
_dockWidget->setSource(QUrl(sourceUrl));
|
||||
_dockWidget->setObjectName("DockedWidget");
|
||||
mainWindow->addDockWidget(dockArea, _dockWidget.get());
|
||||
} else {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
@ -283,6 +284,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
if (!KNOWN_SCHEMES.contains(sourceURL.scheme(), Qt::CaseInsensitive)) {
|
||||
sourceURL = QUrl::fromLocalFile(sourceURL.toString()).toString();
|
||||
}
|
||||
object->setObjectName("InteractiveWindow");
|
||||
object->setProperty(SOURCE_PROPERTY, sourceURL);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ set(src_files
|
|||
src/CustomUI.m
|
||||
src/NSTask+NSTaskExecveAdditions.h
|
||||
src/NSTask+NSTaskExecveAdditions.m
|
||||
src/HQDefaults.h
|
||||
src/HQDefaults.m
|
||||
src/main.mm
|
||||
nib/Window.xib
|
||||
nib/SplashScreen.xib
|
||||
|
@ -118,6 +120,10 @@ add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
|
|||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/images "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/data/HQDefaults.plist "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND updater
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
|
||||
|
@ -146,3 +152,17 @@ set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc"
|
|||
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
include(CPack)
|
||||
|
||||
include(FindXCTest)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
xctest_add_bundle(HQLauncherTests HQLauncher
|
||||
${CMAKE_SOURCE_DIR}/src/HQDefaults.m
|
||||
${CMAKE_SOURCE_DIR}/tests/HQDefaultsTests.m
|
||||
${CMAKE_SOURCE_DIR}/tests/Info.plist
|
||||
)
|
||||
|
||||
set_target_properties(HQLauncherTests PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/tests/Info.plist
|
||||
)
|
||||
|
|
14
launchers/darwin/data/HQDefaults.plist
Normal file
14
launchers/darwin/data/HQDefaults.plist
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>thunderURL</string>
|
||||
<key>defaultValue</key>
|
||||
<string>https://thunder.highfidelity.com</string>
|
||||
<key>environmentVariable</key>
|
||||
<string>HIFI_THUNDER_URL</string>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
|
@ -81,7 +81,7 @@
|
|||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xeX-qc-ccB" customClass="HFButton">
|
||||
<rect key="frame" x="205" y="111" width="104" height="42"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="square" title="LOG IN" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Ba-S9-5qW">
|
||||
<buttonCell key="cell" type="square" title="NEXT" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Ba-S9-5qW">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="systemBold" size="18"/>
|
||||
</buttonCell>
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<button focusRingType="exterior" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jKE-fV-Tjv" customClass="HFButton">
|
||||
<rect key="frame" x="197" y="57" width="110" height="35"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="square" title="NEXT" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" focusRingType="exterior" imageScaling="proportionallyUpOrDown" inset="2" id="RZM-vz-5eS">
|
||||
<buttonCell key="cell" type="square" title="LOG IN" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" focusRingType="exterior" imageScaling="proportionallyUpOrDown" inset="2" id="RZM-vz-5eS">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="systemBold" size="18"/>
|
||||
</buttonCell>
|
||||
|
|
46
launchers/darwin/src/HQDefaults.h
Normal file
46
launchers/darwin/src/HQDefaults.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Created by Matt Hardcastle <m@hardcastle.com>
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*
|
||||
* `HQDefaults` loads defaults from the `HQDefaults.plist` and allows
|
||||
* defaults in that plist to be overwritten using an environment variable.
|
||||
*
|
||||
* For example, the following `HQDefaults.plist` will set a default "foo"
|
||||
* with a value "bar". The "bar" value can be overwritten with the value of
|
||||
* the FOO environment variables, if the FOO environment variable is set.
|
||||
*
|
||||
* <?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
* <plist version="1.0">
|
||||
* <array>
|
||||
* <dict>
|
||||
* <key>name</key>
|
||||
* <string>foo</string>
|
||||
* <key>defaultValue</key>
|
||||
* <string>bar</string>
|
||||
* <key>environmentVariable</key>
|
||||
* <string>FOO</string>
|
||||
* </dict>
|
||||
* </array>
|
||||
* </plist>
|
||||
*/
|
||||
|
||||
@interface HQDefaults : NSObject
|
||||
|
||||
-(NSString *)defaultNamed:(NSString *)name;
|
||||
+(HQDefaults *)sharedDefaults;
|
||||
|
||||
@property (strong, readonly) NSDictionary<NSString *, NSString *> *defaults;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
74
launchers/darwin/src/HQDefaults.m
Normal file
74
launchers/darwin/src/HQDefaults.m
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Created by Matt Hardcastle <m@hardcastle.com>
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
#import "HQDefaults.h"
|
||||
|
||||
@implementation HQDefaults;
|
||||
|
||||
// This initializer is for testing purposes. See `init()` for normal use.
|
||||
-(id)initWithArray:(NSArray *)array environment:(NSDictionary<NSString *, NSString *> *)environment
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSMutableDictionary<NSString *, NSString *> *__defaults = [[NSMutableDictionary alloc] init];
|
||||
NSString *name, *defaultValue, *environmentVariable;
|
||||
|
||||
for (NSDictionary *obj in array) {
|
||||
NSMutableArray<NSString *> *missingKeys = [[NSMutableArray alloc] init];
|
||||
if ((name = [obj objectForKey:@"name"]) == nil) {
|
||||
[missingKeys addObject:@"name"];
|
||||
}
|
||||
if ((defaultValue = [obj objectForKey:@"defaultValue"]) == nil) {
|
||||
[missingKeys addObject:@"defaultValue"];
|
||||
}
|
||||
if ([missingKeys count] > 0) {
|
||||
@throw [NSException exceptionWithName:@"InvalidHQDefaults"
|
||||
reason:@"A required key is missing"
|
||||
userInfo:@{@"missingKeys": missingKeys}];
|
||||
}
|
||||
|
||||
environmentVariable = [obj objectForKey:@"environmentVariable"];
|
||||
if (environmentVariable == nil) {
|
||||
__defaults[name] = defaultValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *value = environment[environmentVariable];
|
||||
__defaults[name] = value == nil ? defaultValue : value;
|
||||
}
|
||||
|
||||
// Make the dictionary immutable.
|
||||
_defaults = __defaults;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Initialize an `HQLauncher` object using the bundles "HQDefaults.plist" and the current process's environment.
|
||||
-(id)init {
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSString *defaultsPath = [bundle pathForResource:@"HQDefaults" ofType:@"plist"];
|
||||
NSArray *array = [NSArray arrayWithContentsOfFile:defaultsPath];
|
||||
return [self initWithArray:array environment:NSProcessInfo.processInfo.environment];
|
||||
}
|
||||
|
||||
// Retrieve a default.
|
||||
-(NSString *)defaultNamed:(NSString *)name {
|
||||
return _defaults[name];
|
||||
}
|
||||
|
||||
// A singleton HQDefaults using the mainBundle's "HQDefaults.plist" and the environment.
|
||||
+(HQDefaults *)sharedDefaults {
|
||||
static HQDefaults *defaults = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
defaults = [[HQDefaults alloc] init];
|
||||
});
|
||||
return defaults;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +1,7 @@
|
|||
#import "LatestBuildRequest.h"
|
||||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
#import "HQDefaults.h"
|
||||
|
||||
@implementation LatestBuildRequest
|
||||
|
||||
|
@ -8,7 +9,13 @@
|
|||
NSString* buildsURL = [[[NSProcessInfo processInfo] environment] objectForKey:@"HQ_LAUNCHER_BUILDS_URL"];
|
||||
|
||||
if ([buildsURL length] == 0) {
|
||||
buildsURL = @"https://thunder.highfidelity.com/builds/api/tags/latest?format=json";
|
||||
NSString *thunderURL = [[HQDefaults sharedDefaults] defaultNamed:@"thunderURL"];
|
||||
if (thunderURL == nil) {
|
||||
@throw [NSException exceptionWithName:@"DefaultMissing"
|
||||
reason:@"The thunderURL default is missing"
|
||||
userInfo:nil];
|
||||
}
|
||||
buildsURL = [NSString stringWithFormat:@"%@/builds/api/tags/latest?format=json", thunderURL];
|
||||
}
|
||||
|
||||
NSLog(@"Making request for builds to: %@", buildsURL);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#import "Settings.h"
|
||||
#import "NSTask+NSTaskExecveAdditions.h"
|
||||
#import "Interface.h"
|
||||
#import "HQDefaults.h"
|
||||
|
||||
@interface Launcher ()
|
||||
|
||||
|
@ -483,6 +484,17 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
[self.window makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
-(void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
// Sanity check the HQDefaults so we fail early if there's an issue.
|
||||
HQDefaults *defaults = [HQDefaults sharedDefaults];
|
||||
if ([defaults defaultNamed:@"thunderURL"] == nil) {
|
||||
@throw [NSException exceptionWithName:@"DefaultsNotConfigured"
|
||||
reason:@"thunderURL is not configured"
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setDownloadFilename:(NSString *)aFilename
|
||||
{
|
||||
self.filename = aFilename;
|
||||
|
|
78
launchers/darwin/tests/HQDefaultsTests.m
Normal file
78
launchers/darwin/tests/HQDefaultsTests.m
Normal file
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Created by Matt Hardcastle <m@hardcastle.com>
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "HQDefaults.h"
|
||||
|
||||
// Expose the `initWithArray:environment` initializer for testing.
|
||||
@interface HQDefaults(HQDefaultsTesting)
|
||||
-(id)initWithArray:(NSArray *)array environment:(NSDictionary<NSString *, NSString *> *)environment;
|
||||
@end
|
||||
|
||||
@interface HQDefaultsTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation HQDefaultsTests
|
||||
|
||||
// If there is not environment the default value should be used.
|
||||
- (void)testNoEnvironmentReturnsDefaultValue {
|
||||
NSArray *array = @[@{@"name":@"foo",
|
||||
@"environmentVariable":@"FOO",
|
||||
@"defaultValue":@"bar"}];
|
||||
|
||||
HQDefaults *defaults = [[HQDefaults alloc] initWithArray:array
|
||||
environment:@{}];
|
||||
|
||||
XCTAssertEqual([defaults defaultNamed:@"foo"], @"bar");
|
||||
}
|
||||
|
||||
// The value from the environment should overwrite the default value.
|
||||
- (void)testEnvironmentOverWritesDefaultValue {
|
||||
NSArray *array = @[@{@"name":@"foo",
|
||||
@"environmentVariable":@"FOO",
|
||||
@"defaultValue":@"bar"}];
|
||||
NSDictionary *environment = @{@"FOO":@"FOO_VALUE_FROM_ENVIRONMENT"};
|
||||
|
||||
HQDefaults *defaults = [[HQDefaults alloc] initWithArray:array environment:environment];
|
||||
|
||||
NSString *value = [defaults defaultNamed:@"foo"];
|
||||
|
||||
XCTAssertEqual(value, @"FOO_VALUE_FROM_ENVIRONMENT");
|
||||
}
|
||||
|
||||
// An exception should be thrown if a defaults object is missing `name`.
|
||||
- (void)testMissingNameThrowsException {
|
||||
NSArray *array = @[@{@"defaultValue":@"bar"}];
|
||||
XCTAssertThrowsSpecificNamed([[HQDefaults alloc] initWithArray:array environment:@{}],
|
||||
NSException,
|
||||
@"InvalidHQDefaults");
|
||||
}
|
||||
|
||||
// An exception should be thrown if a defaults object is missing `defaultValue`.
|
||||
- (void)testMissingDefaultValueThrowsException {
|
||||
NSArray *array = @[@{@"name":@"foo" }];
|
||||
XCTAssertThrowsSpecificNamed([[HQDefaults alloc] initWithArray:array environment:@{}],
|
||||
NSException,
|
||||
@"InvalidHQDefaults");
|
||||
}
|
||||
|
||||
// An exception should **NOT** be thrown if a defaults object is missing `environmentVariable`.
|
||||
- (void)testMissingEnvironmentVariableDoesNotThrowException {
|
||||
NSArray *array = @[@{@"name":@"foo", @"defaultValue":@"bar"}];
|
||||
XCTAssertNoThrow([[HQDefaults alloc] initWithArray:array environment:@{}]);
|
||||
}
|
||||
|
||||
// A `nil` should be returned if a default is missing.
|
||||
- (void)testEmptyDefaultIsNil {
|
||||
HQDefaults *defaults = [[HQDefaults alloc] initWithArray:@[] environment:@{}];
|
||||
XCTAssertNil([defaults defaultNamed:@"foo"]);
|
||||
}
|
||||
|
||||
@end
|
22
launchers/darwin/tests/Info.plist
Normal file
22
launchers/darwin/tests/Info.plist
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -581,7 +581,7 @@ void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
|
|||
int xpan = 0;
|
||||
if (nIDCtl == IDC_BUTTON_NEXT) {
|
||||
if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) {
|
||||
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("NEXT") : _T("LOG IN");
|
||||
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("LOG IN") : _T("NEXT");
|
||||
int xpan = -20;
|
||||
defrect = CRect(rect.left - xpan, rect.top, rect.right + xpan, rect.bottom);
|
||||
} else if (_drawStep == DrawStep::DrawError) {
|
||||
|
|
|
@ -1225,8 +1225,8 @@ void LimitedNodeList::connectedForLocalSocketTest() {
|
|||
auto localHostAddress = localIPTestSocket->localAddress();
|
||||
|
||||
if (localHostAddress.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_hasTCPCheckedLocalSocket = true;
|
||||
setLocalSocket(HifiSockAddr { localHostAddress, _nodeSocket.localPort() });
|
||||
_hasTCPCheckedLocalSocket = true;
|
||||
}
|
||||
|
||||
localIPTestSocket->deleteLater();
|
||||
|
@ -1244,7 +1244,7 @@ void LimitedNodeList::errorTestingLocalSocket() {
|
|||
setLocalSocket(HifiSockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() });
|
||||
}
|
||||
|
||||
localIPTestSocket->deleteLater();;
|
||||
localIPTestSocket->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ void NetworkPeer::activatePublicSocket() {
|
|||
|
||||
void NetworkPeer::activateSymmetricSocket() {
|
||||
if (_activeSocket != &_symmetricSocket) {
|
||||
qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
qCDebug(networking) << "Activating symmetric socket (" << _symmetricSocket << ") for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
setActiveSocket(&_symmetricSocket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool fi
|
|||
if (filterCreate && _connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) {
|
||||
// the connection creation filter did not allow us to create a new connection
|
||||
#ifdef UDT_CONNECTION_DEBUG
|
||||
qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr
|
||||
qCDebug(networking) << "Socket::findOrCreateConnection refusing to create Connection class for" << sockAddr
|
||||
<< "due to connection creation filter";
|
||||
#endif // UDT_CONNECTION_DEBUG
|
||||
return nullptr;
|
||||
|
@ -279,7 +279,7 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool fi
|
|||
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
|
||||
this, &Socket::clientHandshakeRequestComplete);
|
||||
|
||||
qCDebug(networking) << "Creating new connection to" << sockAddr;
|
||||
qCDebug(networking) << "Creating new Connection class for" << sockAddr;
|
||||
|
||||
it = _connectionsHash.insert(it, std::make_pair(sockAddr, std::move(connection)));
|
||||
}
|
||||
|
|
|
@ -37,7 +37,11 @@ OctreeEditPacketSender::~OctreeEditPacketSender() {
|
|||
|
||||
|
||||
bool OctreeEditPacketSender::serversExist() const {
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (!nodeList) {
|
||||
return false;
|
||||
}
|
||||
auto node = nodeList->soloNodeOfType(getMyNodeType());
|
||||
return node && node->getActiveSocket();
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ CharacterController::CharacterController() {
|
|||
// ATM CharacterController is a singleton. When we want more we'll have to
|
||||
// overhaul the applyPairwiseFilter() logic to handle multiple instances.
|
||||
++_numCharacterControllers;
|
||||
assert(numCharacterControllers == 1);
|
||||
assert(_numCharacterControllers == 1);
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController() {
|
||||
|
@ -189,7 +189,7 @@ void CharacterController::addToWorld() {
|
|||
_rigidBody->setCollisionFlags(btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
|
||||
|
||||
// enable CCD
|
||||
_rigidBody->setCcdSweptSphereRadius(_radius);
|
||||
_rigidBody->setCcdSweptSphereRadius(2.0f * (_radius + _halfHeight));
|
||||
_rigidBody->setCcdMotionThreshold(_radius);
|
||||
|
||||
btCollisionShape* shape = _rigidBody->getCollisionShape();
|
||||
|
@ -225,6 +225,7 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
|||
float deepestDistance = 0.0f;
|
||||
float strongestImpulse = 0.0f;
|
||||
|
||||
_netCollisionImpulse = btVector3(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < numManifolds; i++) {
|
||||
btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i);
|
||||
if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) {
|
||||
|
@ -245,6 +246,7 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
|||
deepestDistance = distance;
|
||||
}
|
||||
float impulse = contact.getAppliedImpulse();
|
||||
_netCollisionImpulse += impulse * normal;
|
||||
if (impulse > strongestImpulse) {
|
||||
strongestImpulse = impulse;
|
||||
}
|
||||
|
@ -554,7 +556,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
|||
|
||||
if (_rigidBody) {
|
||||
// update CCD with new _radius
|
||||
_rigidBody->setCcdSweptSphereRadius(_radius);
|
||||
_rigidBody->setCcdSweptSphereRadius(2.0f * (_radius + _halfHeight));
|
||||
_rigidBody->setCcdMotionThreshold(_radius);
|
||||
}
|
||||
}
|
||||
|
@ -777,14 +779,50 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) {
|
|||
velocity += dt * _linearAcceleration;
|
||||
// Note the differences between these two variables:
|
||||
// _targetVelocity = ideal final velocity according to input
|
||||
// velocity = real final velocity after motors are applied to current velocity
|
||||
// velocity = new final velocity after motors are applied to currentVelocity
|
||||
|
||||
bool gettingStuck = !_isStuck && _stuckTransitionCount > 1 && _state == State::Hover;
|
||||
if (gettingStuck && velocity.length2() > currentVelocity.length2()) {
|
||||
// we are probably trying to fly fast into a mesh obstacle
|
||||
// which is causing us to tickle the "stuck" detection code
|
||||
// so we average our new velocity with currentVeocity to prevent a "safe landing" response
|
||||
velocity = 0.5f * (velocity + currentVelocity);
|
||||
// but we want to avoid getting stuck and tunelling through geometry so we perform
|
||||
// further checks and modify/abandon our velocity calculations
|
||||
|
||||
if (_isStuck || _stuckTransitionCount == 0) {
|
||||
// we are either definitely stuck, or definitely not --> nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const float SAFE_COLLISION_SPEED = glm::abs(STUCK_PENETRATION) * (float)NUM_SUBSTEPS_PER_SECOND;
|
||||
const float SAFE_COLLISION_SPEED_SQUARED = SAFE_COLLISION_SPEED * SAFE_COLLISION_SPEED;
|
||||
bool fast = velocity.length2() > SAFE_COLLISION_SPEED_SQUARED;
|
||||
|
||||
const float STRONG_IMPACT_IMPULSE_DOT = -1000.0f; // this tuned manually
|
||||
bool strongImpact = velocity.dot(_netCollisionImpulse) < STRONG_IMPACT_IMPULSE_DOT;
|
||||
|
||||
if (fast && strongImpact) {
|
||||
const float REFLECTION_COEFFICIENT = 1.5f;
|
||||
if (velocity.dot(currentVelocity) > 0.0f) {
|
||||
// our new velocity points in the same direction as our currentVelocity
|
||||
// but strongImpact means new velocity points against netImpulse
|
||||
if (currentVelocity.dot(_netCollisionImpulse) > 0.0f) {
|
||||
// currentVelocity points positively with netImpulse
|
||||
// so we will assume collisions will save us and use it for our new velocity
|
||||
velocity = currentVelocity;
|
||||
} else {
|
||||
// can't trust physical simulation --> reflect velocity against netImpulse
|
||||
btVector3 impulseDirection = _netCollisionImpulse.normalized();
|
||||
velocity -= (REFLECTION_COEFFICIENT * velocity.dot(impulseDirection)) * impulseDirection;
|
||||
// also attenuate the velocity to help slow down the character before its penetration gets worse
|
||||
const float ATTENUATION_COEFFICIENT = 0.8f;
|
||||
velocity *= ATTENUATION_COEFFICIENT;
|
||||
}
|
||||
} else {
|
||||
// currentVelocity points against new velocity, which means it is probably better but...
|
||||
// this doesn't mean it points in a good direction yet, so we must check
|
||||
if (currentVelocity.dot(_netCollisionImpulse) < 0.0f) {
|
||||
// currentVelocity points against netImpulse, so we reflect it
|
||||
btVector3 impulseDirection = _netCollisionImpulse.normalized();
|
||||
currentVelocity -= (REFLECTION_COEFFICIENT * currentVelocity.dot(impulseDirection)) * impulseDirection;
|
||||
}
|
||||
velocity = currentVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -215,6 +215,7 @@ protected:
|
|||
btVector3 _followLinearDisplacement;
|
||||
btQuaternion _followAngularDisplacement;
|
||||
btVector3 _linearAcceleration;
|
||||
btVector3 _netCollisionImpulse;
|
||||
|
||||
State _state;
|
||||
bool _isPushingUp;
|
||||
|
|
|
@ -56,7 +56,6 @@ static FilePersistThread* _persistThreadInstance;
|
|||
|
||||
QString getLogRollerFilename() {
|
||||
QString result = FileUtils::standardPath(LOGS_DIRECTORY);
|
||||
QHostAddress clientAddress = getGuessedLocalAddress();
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QString fileSessionID;
|
||||
|
||||
|
|
|
@ -9,9 +9,19 @@
|
|||
#include "NetworkUtils.h"
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
|
||||
namespace {
|
||||
const QString LINK_LOCAL_SUBNET {"169.254."};
|
||||
|
||||
// Is address local-subnet valid only (rfc 3927):
|
||||
bool isLinkLocalAddress(const QHostAddress& ip4Addr) {
|
||||
return ip4Addr.toString().startsWith(LINK_LOCAL_SUBNET);
|
||||
}
|
||||
}
|
||||
|
||||
QHostAddress getGuessedLocalAddress() {
|
||||
|
||||
QHostAddress localAddress;
|
||||
QHostAddress linkLocalAddress;
|
||||
|
||||
foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (networkInterface.flags() & QNetworkInterface::IsUp
|
||||
|
@ -20,12 +30,16 @@ QHostAddress getGuessedLocalAddress() {
|
|||
// we've decided that this is the active NIC
|
||||
// enumerate it's addresses to grab the IPv4 address
|
||||
foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
|
||||
const auto& addressCandidate = entry.ip();
|
||||
// make sure it's an IPv4 address that isn't the loopback
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
|
||||
|
||||
// set our localAddress and break out
|
||||
localAddress = entry.ip();
|
||||
break;
|
||||
if (addressCandidate.protocol() == QAbstractSocket::IPv4Protocol && !addressCandidate.isLoopback()) {
|
||||
if (isLinkLocalAddress(addressCandidate)) {
|
||||
linkLocalAddress = addressCandidate; // Last resort
|
||||
} else {
|
||||
// set our localAddress and break out
|
||||
localAddress = addressCandidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +50,5 @@ QHostAddress getGuessedLocalAddress() {
|
|||
}
|
||||
|
||||
// return the looked up local address
|
||||
return localAddress;
|
||||
return localAddress.isNull() ? linkLocalAddress : localAddress;
|
||||
}
|
||||
|
||||
|
||||
|
|
31
libraries/shared/src/shared/PlatformHelper.cpp
Normal file
31
libraries/shared/src/shared/PlatformHelper.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Copyright 2013-2019 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 "PlatformHelper.h"
|
||||
|
||||
void PlatformHelper::onSleep() {
|
||||
if (_awake.exchange(false)) {
|
||||
qInfo() << "Entering sleep or hibernation.";
|
||||
emit systemWillSleep();
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformHelper::onWake() {
|
||||
if (!_awake.exchange(true)) {
|
||||
qInfo() << "Waking up from sleep or hibernation.";
|
||||
emit systemWillWake();
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformHelper::shutdown() {
|
||||
DependencyManager::destroy<PlatformHelper>();
|
||||
}
|
||||
|
||||
PlatformHelper* PlatformHelper::instance() {
|
||||
return DependencyManager::get<PlatformHelper>().get();
|
||||
}
|
||||
|
43
libraries/shared/src/shared/PlatformHelper.h
Normal file
43
libraries/shared/src/shared/PlatformHelper.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Copyright 2013-2019 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_shared_PlatformHelper_h
|
||||
#define hifi_shared_PlatformHelper_h
|
||||
|
||||
#include <atomic>
|
||||
#include <QtCore/QtGlobal>
|
||||
#include "../DependencyManager.h"
|
||||
|
||||
class PlatformHelper : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PlatformHelper() {}
|
||||
virtual ~PlatformHelper() {}
|
||||
|
||||
void onSleep();
|
||||
void onWake();
|
||||
|
||||
signals:
|
||||
void systemWillSleep();
|
||||
void systemWillWake();
|
||||
|
||||
public:
|
||||
// Run the per-platform code to instantiate a platform-dependent PlatformHelper dependency object
|
||||
static void setup();
|
||||
// Run the per-platform code to cleanly shutdown a platform-dependent PlatformHelper dependency object
|
||||
static void shutdown();
|
||||
// Fetch the platform specific instance of the helper
|
||||
static PlatformHelper* instance();
|
||||
|
||||
std::atomic<bool> _awake{ true };
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
27
libraries/shared/src/shared/platform/AndroidHelper.cpp
Normal file
27
libraries/shared/src/shared/platform/AndroidHelper.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Copyright 2013-2019 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 "../PlatformHelper.h"
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
|
||||
// FIXME support sleep/wake notifications
|
||||
class AndroidHelper : public PlatformHelper {
|
||||
public:
|
||||
AndroidHelper() {
|
||||
}
|
||||
|
||||
~AndroidHelper() {
|
||||
}
|
||||
};
|
||||
|
||||
void PlatformHelper::setup() {
|
||||
DependencyManager::set<PlatformHelper, AndroidHelper>();
|
||||
}
|
||||
|
||||
#endif
|
27
libraries/shared/src/shared/platform/LinuxHelper.cpp
Normal file
27
libraries/shared/src/shared/platform/LinuxHelper.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Copyright 2013-2019 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 "../PlatformHelper.h"
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
|
||||
|
||||
// FIXME support sleep/wake notifications
|
||||
class LinuxHelper : public PlatformHelper {
|
||||
public:
|
||||
LinuxHelper() {
|
||||
}
|
||||
|
||||
~LinuxHelper() {
|
||||
}
|
||||
};
|
||||
|
||||
void PlatformHelper::setup() {
|
||||
DependencyManager::set<PlatformHelper, LinuxHelper>();
|
||||
}
|
||||
|
||||
#endif
|
70
libraries/shared/src/shared/platform/MacHelper.cpp
Normal file
70
libraries/shared/src/shared/platform/MacHelper.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Based on interface/src/MacHelper.cpp, created by Howard Stearns
|
||||
// Copyright 2013-2019 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 "../PlatformHelper.h"
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
class MacHelper : public PlatformHelper {
|
||||
public:
|
||||
MacHelper() {
|
||||
_rootPort = IORegisterForSystemPower(this, &_notifyPortRef, serviceInterestCallback, &_notifierObject);
|
||||
if (_rootPort == 0) {
|
||||
qWarning() << "IORegisterForSystemPower failed";
|
||||
return;
|
||||
}
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(_notifyPortRef), kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
~MacHelper() {
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(_notifyPortRef), kCFRunLoopCommonModes);
|
||||
IODeregisterForSystemPower(&_notifierObject);
|
||||
IOServiceClose(_rootPort);
|
||||
IONotificationPortDestroy(_notifyPortRef);
|
||||
}
|
||||
|
||||
private:
|
||||
void onServiceMessage(io_service_t, natural_t messageType, void* messageArgument) {
|
||||
switch (messageType) {
|
||||
case kIOMessageSystemHasPoweredOn:
|
||||
onWake();
|
||||
break;
|
||||
|
||||
case kIOMessageSystemWillSleep:
|
||||
onSleep();
|
||||
// explicit fallthrough
|
||||
|
||||
// Per the documentation for kIOMessageSystemWillSleep and kIOMessageCanSystemSleep, the receiver MUST respond
|
||||
// https://developer.apple.com/documentation/iokit/1557114-ioregisterforsystempower?language=objc
|
||||
case kIOMessageCanSystemSleep:
|
||||
IOAllowPowerChange(_rootPort, (long)messageArgument);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void serviceInterestCallback(void* refCon, io_service_t service, natural_t messageType, void* messageArgument) {
|
||||
static_cast<MacHelper*>(refCon)->onServiceMessage(service, messageType, messageArgument);
|
||||
}
|
||||
|
||||
io_connect_t _rootPort{ 0 };
|
||||
IONotificationPortRef _notifyPortRef{};
|
||||
io_object_t _notifierObject{};
|
||||
};
|
||||
|
||||
void PlatformHelper::setup() {
|
||||
DependencyManager::set<PlatformHelper, MacHelper>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
52
libraries/shared/src/shared/platform/WinHelper.cpp
Normal file
52
libraries/shared/src/shared/platform/WinHelper.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/08/22
|
||||
// Copyright 2013-2019 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 "../PlatformHelper.h"
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && defined(Q_OS_WIN)
|
||||
|
||||
#include <QtCore/QAbstractNativeEventFilter>
|
||||
#include <QtCore/QAbstractEventDispatcher>
|
||||
#include <Windows.h>
|
||||
|
||||
class WinHelper : public PlatformHelper, public QAbstractNativeEventFilter {
|
||||
public:
|
||||
WinHelper() {
|
||||
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
~WinHelper() {
|
||||
auto eventDispatcher = QAbstractEventDispatcher::instance();
|
||||
if (eventDispatcher) {
|
||||
eventDispatcher->removeNativeEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override {
|
||||
MSG* msg = static_cast<MSG*>(message);
|
||||
if (msg->message == WM_POWERBROADCAST) {
|
||||
switch (msg->wParam) {
|
||||
case PBT_APMRESUMEAUTOMATIC:
|
||||
case PBT_APMRESUMESUSPEND:
|
||||
onWake();
|
||||
break;
|
||||
|
||||
case PBT_APMSUSPEND:
|
||||
onSleep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void PlatformHelper::setup() {
|
||||
DependencyManager::set<PlatformHelper, WinHelper>();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,10 +8,7 @@ var customEmojiList = [
|
|||
]
|
||||
}
|
||||
]
|
||||
try {
|
||||
if (module) {
|
||||
module.exports = customEmojiList;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error exporting:\n", e);
|
||||
|
||||
if (module.exports) {
|
||||
module.exports = customEmojiList;
|
||||
}
|
|
@ -2987,8 +2987,7 @@ var emojiList = [
|
|||
"shortName": "unamused face",
|
||||
"keywords": [
|
||||
"face",
|
||||
"unamused",
|
||||
"unhappy"
|
||||
"unamused"
|
||||
],
|
||||
"mainCategory": "Smileys & Emotion",
|
||||
"subCategory": "face-neutral-skeptical",
|
||||
|
@ -38734,10 +38733,7 @@ var emojiList = [
|
|||
}
|
||||
}
|
||||
]
|
||||
try {
|
||||
if (module) {
|
||||
module.exports = emojiList;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error exporting:\n", e);
|
||||
|
||||
if (module.exports) {
|
||||
module.exports = emojiList;
|
||||
}
|
|
@ -102,7 +102,6 @@ function startTimeoutDelete() {
|
|||
|
||||
|
||||
// The QML has a property called archEnd on the pie chart that controls how much pie is showing
|
||||
var COUNT_DOWN_INTERVAL_MS = 100;
|
||||
function beginCountDownTimer() {
|
||||
_this._avimojiQMLWindow.sendToQml({
|
||||
"source": "simplifiedEmoji.js",
|
||||
|
@ -128,9 +127,9 @@ function resetEmojis() {
|
|||
clearCountDownTimerHandler();
|
||||
if (currentEmoji) {
|
||||
Entities.deleteEntity(currentEmoji);
|
||||
currentEmoji = false;
|
||||
selectedEmojiFilename = null;
|
||||
}
|
||||
currentEmoji = false;
|
||||
selectedEmojiFilename = null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -315,7 +314,7 @@ function playPopAnimation() {
|
|||
} else {
|
||||
// Start with the pop sound on the out
|
||||
currentPopScale = finalInPopScale ? finalInPopScale : MAX_POP_SCALE;
|
||||
playSound(emojiDestroySound);
|
||||
playSound(emojiDestroySound, DEFAULT_VOLUME, MyAvatar.position, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,11 +359,11 @@ function playPopAnimation() {
|
|||
Entities.editEntity(currentEmoji, {"dimensions": dimensions});
|
||||
}
|
||||
} else {
|
||||
// make sure there is a currentEmoji entity before trying to delete
|
||||
pruneOldAvimojis();
|
||||
if (currentEmoji) {
|
||||
Entities.deleteEntity(currentEmoji);
|
||||
currentEmoji = false;
|
||||
}
|
||||
currentEmoji = false;
|
||||
finalInPopScale = null;
|
||||
selectedEmojiFilename = null;
|
||||
clearCountDownTimerHandler();
|
||||
|
|
|
@ -31,6 +31,9 @@ Rectangle {
|
|||
// if this is true, then hovering doesn't allow showing other icons
|
||||
property bool isSelected: false
|
||||
|
||||
KeyNavigation.backtab: emojiSearchTextField
|
||||
KeyNavigation.tab: emojiSearchTextField
|
||||
|
||||
// Update the selected emoji image whenever the code property is changed.
|
||||
onCurrentCodeChanged: {
|
||||
mainEmojiImage.source = emojiBaseURL + currentCode;
|
||||
|
@ -209,6 +212,18 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this MouseArea is hit, the user is clicking on an area not handled
|
||||
// by any other MouseArea
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
// The grid will forward keypresses to the main Interface window
|
||||
grid.forceActiveFocus();
|
||||
// Necessary to get keyboard keyPress/keyRelease forwarding to work. See `Application::hasFocus()`;
|
||||
Window.setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: emojiIndicatorContainer
|
||||
|
@ -302,6 +317,7 @@ Rectangle {
|
|||
MouseArea {
|
||||
hoverEnabled: enabled
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: false
|
||||
onEntered: {
|
||||
grid.currentIndex = index
|
||||
// don't allow a hover image change of the main emoji image
|
||||
|
@ -313,6 +329,10 @@ Rectangle {
|
|||
}
|
||||
onClicked: {
|
||||
root.selectEmoji(model.code.utf);
|
||||
// The grid will forward keypresses to the main Interface window
|
||||
grid.forceActiveFocus();
|
||||
// Necessary to get keyboard keyPress/keyRelease forwarding to work. See `Application::hasFocus()`;
|
||||
Window.setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -330,6 +350,10 @@ Rectangle {
|
|||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
root.selectEmoji(grid.model.get(grid.currentIndex).code.utf);
|
||||
}
|
||||
root.keyPressEvent(event.key, event.modifiers);
|
||||
}
|
||||
Keys.onReleased: {
|
||||
root.keyReleaseEvent(event.key, event.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,6 +453,7 @@ Rectangle {
|
|||
id: attributionMouseArea
|
||||
hoverEnabled: enabled
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: false
|
||||
onClicked: {
|
||||
popupContainer.visible = true;
|
||||
}
|
||||
|
@ -458,8 +483,15 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
signal sendToScript(var message);
|
||||
signal sendToScript(var message)
|
||||
signal keyPressEvent(int key, int modifiers)
|
||||
signal keyReleaseEvent(int key, int modifiers)
|
||||
Keys.onPressed: {
|
||||
root.keyPressEvent(event.key, event.modifiers);
|
||||
}
|
||||
Keys.onReleased: {
|
||||
root.keyReleaseEvent(event.key, event.modifiers);
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
if (message.source !== "simplifiedEmoji.js") {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -127,14 +127,51 @@ function getSounds() {
|
|||
}
|
||||
|
||||
|
||||
// Returns the first valid joint position from the list of supplied test joint positions.
|
||||
// If none are valid, returns MyAvatar.position.
|
||||
function getValidJointPosition(jointsToTest) {
|
||||
var currentJointIndex;
|
||||
|
||||
for (var i = 0; i < jointsToTest.length; i++) {
|
||||
currentJointIndex = MyAvatar.getJointIndex(jointsToTest[i]);
|
||||
|
||||
if (currentJointIndex > -1) {
|
||||
return MyAvatar.getJointPosition(jointsToTest[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return Vec3.sum(MyAvatar.position, Vec3.multiply(0.25, Quat.getForward(MyAvatar.orientation)));
|
||||
}
|
||||
|
||||
|
||||
// Returns the world position halfway between the user's hands
|
||||
var HALF = 0.5;
|
||||
function getClapPosition() {
|
||||
var validLeftJoints = ["LeftHandMiddle2", "LeftHand", "LeftArm"];
|
||||
var leftPosition = getValidJointPosition(validLeftJoints);
|
||||
|
||||
var validRightJoints = ["RightHandMiddle2", "RightHand", "RightArm"];
|
||||
var rightPosition = getValidJointPosition(validRightJoints);
|
||||
|
||||
var centerPosition = Vec3.sum(leftPosition, rightPosition);
|
||||
centerPosition = Vec3.multiply(centerPosition, HALF);
|
||||
|
||||
return centerPosition;
|
||||
}
|
||||
|
||||
|
||||
var clapSoundInterval = false;
|
||||
var CLAP_SOUND_INTERVAL_MS = 260; // Must match the clap animation interval
|
||||
var CLAP_SOUND_INTERVAL_MS_FLOOR = 260;
|
||||
var CLAP_SOUND_INTERVAL_MS_CEIL = 320;
|
||||
function startClappingSounds() {
|
||||
maybeClearClapSoundInterval();
|
||||
|
||||
// Compute a random clap sound interval to avoid strange echos between many people clapping simultaneously
|
||||
var clapSoundIntervalMS = Math.floor(randomFloat(CLAP_SOUND_INTERVAL_MS_FLOOR, CLAP_SOUND_INTERVAL_MS_CEIL));
|
||||
|
||||
clapSoundInterval = Script.setInterval(function() {
|
||||
playSound(clapSounds[Math.floor(Math.random() * clapSounds.length)], MyAvatar.position, true);
|
||||
}, CLAP_SOUND_INTERVAL_MS);
|
||||
playSound(clapSounds[Math.floor(Math.random() * clapSounds.length)], getClapPosition(), true);
|
||||
}, clapSoundIntervalMS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,10 +188,15 @@ function toggleReaction(reaction) {
|
|||
|
||||
if (reactionEnding) {
|
||||
endReactionWrapper(reaction);
|
||||
updateEmoteIndicatorIcon("images/emote_Icon.svg");
|
||||
} else {
|
||||
beginReactionWrapper(reaction);
|
||||
updateEmoteIndicatorIcon("images/" + reaction + "_Icon.svg");
|
||||
}
|
||||
}
|
||||
|
||||
function maybeDeleteRemoteIndicatorTimeout() {
|
||||
if (restoreEmoteIndicatorTimeout) {
|
||||
Script.clearTimeout(restoreEmoteIndicatorTimeout);
|
||||
restoreEmoteIndicatorTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,6 +204,8 @@ var reactionsBegun = [];
|
|||
var pointReticle = null;
|
||||
var mouseMoveEventsConnected = false;
|
||||
function beginReactionWrapper(reaction) {
|
||||
maybeDeleteRemoteIndicatorTimeout();
|
||||
|
||||
reactionsBegun.forEach(function(react) {
|
||||
endReactionWrapper(react);
|
||||
});
|
||||
|
@ -169,6 +213,8 @@ function beginReactionWrapper(reaction) {
|
|||
if (MyAvatar.beginReaction(reaction)) {
|
||||
reactionsBegun.push(reaction);
|
||||
}
|
||||
|
||||
updateEmoteIndicatorIcon("images/" + reaction + "_Icon.svg");
|
||||
|
||||
// Insert reaction-specific logic here:
|
||||
switch (reaction) {
|
||||
|
@ -235,10 +281,10 @@ function mouseMoveEvent(event) {
|
|||
Entities.editEntity(pointReticle, { position: reticlePosition });
|
||||
} else if (reticlePosition) {
|
||||
pointReticle = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
type: "Box",
|
||||
name: "Point Reticle",
|
||||
position: reticlePosition,
|
||||
dimensions: { x: 0.1, y: 0.1, z: 0.1 },
|
||||
dimensions: { x: 0.075, y: 0.075, z: 0.075 },
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
collisionless: true,
|
||||
ignorePickIntersection: true,
|
||||
|
@ -250,13 +296,22 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
|
||||
|
||||
var WAIT_TO_RESTORE_EMOTE_INDICATOR_ICON_MS = 2000;
|
||||
var restoreEmoteIndicatorTimeout;
|
||||
function triggerReactionWrapper(reaction) {
|
||||
maybeDeleteRemoteIndicatorTimeout();
|
||||
|
||||
reactionsBegun.forEach(function(react) {
|
||||
endReactionWrapper(react);
|
||||
});
|
||||
|
||||
MyAvatar.triggerReaction(reaction);
|
||||
updateEmoteIndicatorIcon("images/" + reaction + "_Icon.svg");
|
||||
|
||||
restoreEmoteIndicatorTimeout = Script.setTimeout(function() {
|
||||
updateEmoteIndicatorIcon("images/emote_Icon.svg");
|
||||
restoreEmoteIndicatorTimeout = null;
|
||||
}, WAIT_TO_RESTORE_EMOTE_INDICATOR_ICON_MS);
|
||||
}
|
||||
|
||||
function maybeClearReticleUpdateLimiterTimeout() {
|
||||
|
@ -275,6 +330,8 @@ function endReactionWrapper(reaction) {
|
|||
reactionsBegun.splice(reactionsBegunIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
updateEmoteIndicatorIcon("images/emote_Icon.svg");
|
||||
|
||||
// Insert reaction-specific logic here:
|
||||
switch (reaction) {
|
||||
|
@ -287,7 +344,6 @@ function endReactionWrapper(reaction) {
|
|||
mouseMoveEventsConnected = false;
|
||||
}
|
||||
maybeClearReticleUpdateLimiterTimeout();
|
||||
intersectedEntityOrAvatarID = null;
|
||||
deleteOldReticles();
|
||||
break;
|
||||
}
|
||||
|
@ -301,19 +357,35 @@ function onMessageFromEmoteAppBar(message) {
|
|||
}
|
||||
switch (message.method) {
|
||||
case "positive":
|
||||
if (!message.data.isPressingAndHolding) {
|
||||
return;
|
||||
}
|
||||
triggerReactionWrapper("positive");
|
||||
updateEmoteIndicatorIcon("images/" + message.method + "_Icon.svg");
|
||||
break;
|
||||
case "negative":
|
||||
if (!message.data.isPressingAndHolding) {
|
||||
return;
|
||||
}
|
||||
triggerReactionWrapper("negative");
|
||||
updateEmoteIndicatorIcon("images/" + message.method + "_Icon.svg");
|
||||
break;
|
||||
case "raiseHand":
|
||||
case "applaud":
|
||||
if (message.data.isPressingAndHolding) {
|
||||
beginReactionWrapper(message.method);
|
||||
} else {
|
||||
endReactionWrapper(message.method);
|
||||
}
|
||||
break;
|
||||
case "point":
|
||||
case "raiseHand":
|
||||
if (!message.data.isPressingAndHolding) {
|
||||
return;
|
||||
}
|
||||
toggleReaction(message.method);
|
||||
break;
|
||||
case "toggleEmojiApp":
|
||||
if (!message.data.isPressingAndHolding) {
|
||||
return;
|
||||
}
|
||||
toggleEmojiApp();
|
||||
break;
|
||||
default:
|
||||
|
@ -363,8 +435,8 @@ function onWindowMinimizedChanged(isMinimized) {
|
|||
// for the tooltips to match the actual keys.
|
||||
var POSITIVE_KEY = "z";
|
||||
var NEGATIVE_KEY = "x";
|
||||
var RAISE_HAND_KEY = "c";
|
||||
var APPLAUD_KEY = "v";
|
||||
var APPLAUD_KEY = "c";
|
||||
var RAISE_HAND_KEY = "v";
|
||||
var POINT_KEY = "b";
|
||||
var EMOTE_WINDOW = "f";
|
||||
function keyPressHandler(event) {
|
||||
|
@ -383,7 +455,7 @@ function keyPressHandler(event) {
|
|||
toggleReaction("applaud");
|
||||
} else if (event.text === POINT_KEY) {
|
||||
toggleReaction("point");
|
||||
} else if (event.text === EMOTE_WINDOW) {
|
||||
} else if (event.text === EMOTE_WINDOW && !(Settings.getValue("io.highfidelity.isEditing", false))) {
|
||||
toggleEmojiApp();
|
||||
}
|
||||
}
|
||||
|
@ -392,18 +464,10 @@ function keyPressHandler(event) {
|
|||
|
||||
function keyReleaseHandler(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
if (event.text === RAISE_HAND_KEY) {
|
||||
if (reactionsBegun.indexOf("raiseHand") > -1) {
|
||||
toggleReaction("raiseHand");
|
||||
}
|
||||
} else if (event.text === APPLAUD_KEY) {
|
||||
if (event.text === APPLAUD_KEY) {
|
||||
if (reactionsBegun.indexOf("applaud") > -1) {
|
||||
toggleReaction("applaud");
|
||||
}
|
||||
} else if (event.text === POINT_KEY) {
|
||||
if (reactionsBegun.indexOf("point") > -1) {
|
||||
toggleReaction("point");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,6 +555,7 @@ var EmojiAPI = Script.require("./emojiApp/simplifiedEmoji.js");
|
|||
var emojiAPI = new EmojiAPI();
|
||||
var keyPressSignalsConnected = false;
|
||||
var emojiCodeMap;
|
||||
var customEmojiCodeMap;
|
||||
function init() {
|
||||
deleteOldReticles();
|
||||
|
||||
|
@ -552,6 +617,7 @@ function shutdown() {
|
|||
emojiAPI.unload();
|
||||
maybeClearClapSoundInterval();
|
||||
maybeClearReticleUpdateLimiterTimeout();
|
||||
maybeDeleteRemoteIndicatorTimeout();
|
||||
|
||||
Window.minimizedChanged.disconnect(onWindowMinimizedChanged);
|
||||
Window.geometryChanged.disconnect(onGeometryChanged);
|
||||
|
@ -657,7 +723,6 @@ var EMOJI_APP_WINDOW_FLAGS = 0x00000001 | // Qt::Window
|
|||
0x00008000 | // Qt::WindowMaximizeButtonHint
|
||||
0x00004000; // Qt::WindowMinimizeButtonHint
|
||||
var emojiAppWindow = false;
|
||||
var POPOUT_SAFE_MARGIN_X = 30;
|
||||
var POPOUT_SAFE_MARGIN_Y = 30;
|
||||
var emojiAppWindowSignalsConnected = false;
|
||||
function toggleEmojiApp() {
|
||||
|
@ -678,7 +743,7 @@ function toggleEmojiApp() {
|
|||
y: EMOJI_APP_HEIGHT_PX
|
||||
},
|
||||
position: {
|
||||
x: Math.max(Window.x + POPOUT_SAFE_MARGIN_X, Window.x + Window.innerWidth / 2 - EMOJI_APP_WIDTH_PX / 2),
|
||||
x: Window.x + EMOTE_APP_BAR_LEFT_MARGIN,
|
||||
y: Math.max(Window.y + POPOUT_SAFE_MARGIN_Y, Window.y + Window.innerHeight / 2 - EMOJI_APP_HEIGHT_PX / 2)
|
||||
},
|
||||
overrideFlags: EMOJI_APP_WINDOW_FLAGS
|
||||
|
|
|
@ -171,10 +171,10 @@ Rectangle {
|
|||
id: buttonsModel
|
||||
ListElement { imageURL: "images/positive_Icon.svg"; hotkey: "Z"; method: "positive" }
|
||||
ListElement { imageURL: "images/negative_Icon.svg"; hotkey: "X"; method: "negative" }
|
||||
ListElement { imageURL: "images/raiseHand_Icon.svg"; hotkey: "C"; method: "raiseHand" }
|
||||
ListElement { imageURL: "images/applaud_Icon.svg"; hotkey: "V"; method: "applaud" }
|
||||
ListElement { imageURL: "images/applaud_Icon.svg"; hotkey: "C"; method: "applaud" }
|
||||
ListElement { imageURL: "images/raiseHand_Icon.svg"; hotkey: "V"; method: "raiseHand" }
|
||||
ListElement { imageURL: "images/point_Icon.svg"; hotkey: "B"; method: "point" }
|
||||
ListElement { imageURL: "images/emote_Icon.svg"; hotkey: "F"; method: "toggleEmojiApp" }
|
||||
ListElement { imageURL: "images/emoji_Icon.svg"; hotkey: "F"; method: "toggleEmojiApp" }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -211,7 +211,7 @@ Rectangle {
|
|||
opacity: 0.8
|
||||
radius: 4
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
HifiStylesUit.GraphikSemiBold {
|
||||
id: toolTipText
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2
|
||||
|
@ -222,7 +222,7 @@ Rectangle {
|
|||
verticalAlignment: TextInput.AlignBottom
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
color: simplifiedUI.colors.text.white
|
||||
size: 22
|
||||
size: 20
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,17 +230,37 @@ Rectangle {
|
|||
id: emoteTrayMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
sendToScript({
|
||||
"source": "EmoteAppBar.qml",
|
||||
"method": model.method
|
||||
});
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
sendToScript({
|
||||
"source": "EmoteAppBar.qml",
|
||||
"method": model.method,
|
||||
"data": { "isPressingAndHolding": true }
|
||||
});
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
sendToScript({
|
||||
"source": "EmoteAppBar.qml",
|
||||
"method": model.method,
|
||||
"data": { "isPressingAndHolding": false }
|
||||
});
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (pressed) {
|
||||
sendToScript({
|
||||
"source": "EmoteAppBar.qml",
|
||||
"method": model.method,
|
||||
"data": { "isPressingAndHolding": false }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
|
||||
<path d="M64,0C28.7,0,0,28.7,0,64s28.7,64,64,64s64-28.7,64-64S99.3,0,64,0z M64,124.1C30.8,124.1,3.9,97.2,3.9,64S30.8,3.9,64,3.9
|
||||
s60.1,26.9,60.1,60.1S97.2,124.1,64,124.1z M88.8,63.5c-3.8,0-7,3.2-7,7c0,9.5-7.6,5.7-17.8,5.7c-10.2,0-17.8,3.8-17.8-5.7
|
||||
c0-3.8-3.2-7-7-7s-7,3.2-7,7c0,17.8,14,32.4,31.7,32.4s31.7-14.6,31.7-32.4C95.7,66.7,92.6,63.5,88.8,63.5z M40.5,41.9
|
||||
c5.1,0,9.5-1.9,9.5,3.2c0,1.9,1.9,3.8,3.8,3.8c2.5,0,4.4-1.9,4.4-3.8c0-9.5-7.6-17.1-17.1-17.1S24,35.6,24,45.1
|
||||
c0,1.9,1.3,3.8,3.8,3.8c1.9,0,3.8-1.9,3.8-3.8C31.6,40,35.4,41.9,40.5,41.9z M86.9,28c-9.5,0-17.1,7.6-17.1,17.1
|
||||
c0,1.9,1.9,3.8,3.8,3.8s3.8-1.9,3.8-3.8c0-5.1,4.4-3.2,9.5-3.2c5.1,0,9.5-1.9,9.5,3.2c0,1.9,1.9,3.8,3.8,3.8c2.5,0,3.8-1.9,3.8-3.8
|
||||
C104,35.6,96.4,28,86.9,28z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,29 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 94 128" style="enable-background:new 0 0 94 128;" xml:space="preserve">
|
||||
<path d="M47,74.3c25.3,0,45.9-16.6,45.9-37.2S72.3,0,47,0S1.1,16.6,1.1,37.2S21.6,74.3,47,74.3z M65.3,24.7c2.7,0,4.9,1.8,4.9,4
|
||||
s-2.2,4-4.9,4c-2.7,0-4.9-1.8-4.9-4S62.6,24.7,65.3,24.7z M36.4,43.5c2.6,2.6,6.5,4,10.6,4c4.1,0,8-1.5,10.6-4
|
||||
c0.9-0.9,2.6-1,3.7-0.3c1.1,0.7,1.3,2.1,0.3,3c-3.6,3.5-8.9,5.5-14.6,5.5c-5.7,0-11-2-14.6-5.5c-0.9-0.9-0.8-2.2,0.3-3
|
||||
C33.8,42.5,35.4,42.6,36.4,43.5z M28.6,24.7c2.7,0,4.9,1.8,4.9,4c0,2.2-2.2,4-4.9,4c-2.7,0-4.9-1.8-4.9-4
|
||||
C23.7,26.5,25.9,24.7,28.6,24.7z M47,101.1c21.2,0,39.4-10.9,46.8-26.4C83.2,87.1,66.2,95.1,47,95.1S10.7,87.1,0.2,74.7
|
||||
C7.6,90.2,25.7,101.1,47,101.1z M47,108.6c-19.2,0-36.3-8.1-46.8-20.4c7.4,15.5,25.5,26.4,46.8,26.4s39.4-10.9,46.8-26.4
|
||||
C83.2,100.5,66.2,108.6,47,108.6z M47,122c-19.2,0-36.3-8.1-46.8-20.4c1.8,3.9,4.4,7.4,7.4,10.6c0,0,0,0,0,0
|
||||
c0.4,0.4,0.7,0.7,1.1,1.1c0.1,0.1,0.1,0.1,0.2,0.2c1.1,1.1,2.3,2.1,3.5,3.1c0.1,0.1,0.1,0.1,0.2,0.2c0.4,0.3,0.7,0.6,1.1,0.8
|
||||
c0.2,0.1,0.3,0.2,0.5,0.3c0.4,0.3,0.7,0.5,1.1,0.8c0.1,0.1,0.3,0.2,0.5,0.3c0.7,0.4,1.4,0.9,2,1.3c0.2,0.1,0.4,0.2,0.6,0.4
|
||||
c0.3,0.2,0.7,0.4,1,0.6c0.3,0.1,0.5,0.3,0.8,0.4c0.3,0.2,0.7,0.3,1,0.5c0.3,0.2,0.6,0.3,1,0.5c0.3,0.2,0.7,0.3,1,0.5
|
||||
c0.5,0.2,1.1,0.5,1.6,0.7c0.3,0.1,0.6,0.2,0.9,0.3c0.4,0.1,0.8,0.3,1.2,0.4c0.3,0.1,0.6,0.2,0.9,0.3c0.4,0.1,0.9,0.3,1.3,0.4
|
||||
c0.2,0.1,0.5,0.2,0.7,0.2c0.7,0.2,1.3,0.4,2,0.6c0.2,0.1,0.4,0.1,0.6,0.2c0.5,0.1,1,0.2,1.5,0.4c0.3,0.1,0.5,0.1,0.8,0.2
|
||||
c0.5,0.1,1,0.2,1.5,0.3c0.2,0,0.5,0.1,0.7,0.1c0.7,0.1,1.5,0.2,2.2,0.3c0.1,0,0.3,0,0.4,0c0.6,0.1,1.3,0.1,1.9,0.2
|
||||
c0.2,0,0.5,0,0.7,0.1c0.6,0,1.1,0.1,1.7,0.1c0.2,0,0.5,0,0.7,0c0.8,0,1.6,0,2.3,0c0.8,0,1.6,0,2.3,0c0.2,0,0.5,0,0.7,0
|
||||
c0.6,0,1.1-0.1,1.7-0.1c0.2,0,0.5,0,0.7-0.1c0.6-0.1,1.3-0.1,1.9-0.2c0.1,0,0.3,0,0.4,0c0.7-0.1,1.5-0.2,2.2-0.3
|
||||
c0.2,0,0.5-0.1,0.7-0.1c0.5-0.1,1-0.2,1.5-0.3c0.3-0.1,0.5-0.1,0.8-0.2c0.5-0.1,1-0.2,1.5-0.4c0.2-0.1,0.4-0.1,0.6-0.2
|
||||
c0.7-0.2,1.3-0.4,2-0.6c0.2-0.1,0.5-0.2,0.7-0.2c0.4-0.1,0.9-0.3,1.3-0.4c0.3-0.1,0.6-0.2,0.9-0.3c0.4-0.1,0.8-0.3,1.2-0.4
|
||||
c0.3-0.1,0.6-0.2,0.9-0.3c0.6-0.2,1.1-0.5,1.6-0.7c0.3-0.2,0.7-0.3,1-0.5c0.3-0.2,0.6-0.3,1-0.5c0.3-0.2,0.7-0.3,1-0.5
|
||||
c0.3-0.1,0.5-0.3,0.8-0.4c0.3-0.2,0.7-0.4,1-0.6c0.2-0.1,0.4-0.2,0.6-0.4c0.7-0.4,1.4-0.8,2.1-1.3c0.1-0.1,0.3-0.2,0.4-0.3
|
||||
c0.4-0.2,0.7-0.5,1.1-0.8c0.2-0.1,0.3-0.2,0.5-0.3c0.4-0.3,0.7-0.5,1.1-0.8c0.1-0.1,0.1-0.1,0.2-0.2c1.2-1,2.4-2,3.5-3.1
|
||||
c0.1-0.1,0.1-0.1,0.2-0.2c0.4-0.4,0.7-0.7,1.1-1.1c0,0,0,0,0,0c3.1-3.2,5.6-6.8,7.4-10.6c0,0,0,0,0,0C83.2,113.9,66.2,122,47,122z
|
||||
M62,80c-0.3,0.1-0.7,0.2-1,0.2c-4.5,1-9.2,1.6-14,1.6c-4.8,0-9.5-0.5-14-1.5c-0.4-0.1-0.8-0.2-1.2-0.3c0,0-0.1,0-0.1,0
|
||||
c-11-2.6-20.8-7.9-28.1-15c-0.3,0.4-0.5,0.7-0.8,1.1c8.6,13,25.1,21.7,44.1,21.7s35.6-8.8,44.1-21.7c-0.2-0.4-0.5-0.7-0.8-1.1
|
||||
c-7.3,7.1-17,12.4-28,15C62.2,79.9,62.1,80,62,80z"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 93.7 127.8" style="enable-background:new 0 0 93.7 127.8;" xml:space="preserve">
|
||||
<path d="M91.9,97c-7.6,9.6-17.8,16.7-29.3,20.3h-0.1c-0.4,0.1-0.8,0.3-1.3,0.4c-4.6,1.3-9.5,2-14.4,2c-4.9,0-9.8-0.7-14.4-2
|
||||
c-0.4-0.1-0.8-0.3-1.3-0.4h-0.1C19.6,113.8,9.4,106.6,1.8,97c0,0-0.5-0.5-0.9,0.1C0.6,97.6,1,98.5,1,98.5
|
||||
C9.9,116,27,127.8,46.8,127.8l0,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l0,0c19.7-0.1,36.8-11.8,45.8-29.3c0,0,0.4-0.9,0.1-1.4
|
||||
C92.4,96.6,91.9,97,91.9,97z M91.9,78.8C84.3,88.4,74.1,95.5,62.6,99h-0.1c-0.4,0.1-0.8,0.3-1.3,0.4c-4.6,1.3-9.5,2-14.4,2
|
||||
c-4.9,0-9.8-0.7-14.4-2c-0.4-0.1-0.8-0.3-1.3-0.4h-0.1C19.6,95.5,9.4,88.4,1.8,78.8c0,0-0.5-0.5-0.9,0.1C0.6,79.3,1,80.2,1,80.2
|
||||
c8.9,17.5,26.1,29.2,45.8,29.3l0,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l0,0c19.7-0.1,36.8-11.8,45.8-29.3c0,0,0.4-0.9,0.1-1.4
|
||||
C92.4,78.3,91.9,78.8,91.9,78.8z M46.9,92.7c25.8,0,46.9-20.7,46.9-46.4S72.7,0,46.9,0S0,20.7,0,46.4S20.9,92.7,46.9,92.7z
|
||||
M65.5,30.8c2.8,0,5,2.2,5,5s-2.2,5-5,5c-2.8,0-5-2.2-5-5S62.8,30.8,65.5,30.8z M36,54.3c2.7,3.2,6.6,5,10.8,5s8.2-1.9,10.8-5
|
||||
c0.9-1.1,2.7-1.2,3.8-0.4c1.1,0.9,1.3,2.6,0.3,3.7c-3.7,4.4-9.1,6.9-14.9,6.9c-5.8,0-11.2-2.5-14.9-6.9c-0.9-1.1-0.8-2.7,0.3-3.7
|
||||
C33.4,53.1,35,53.2,36,54.3z M28.1,30.8c2.8,0,5,2.2,5,5s-2.2,5-5,5c-2.8,0-5-2.2-5-5S25.3,30.8,28.1,30.8z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -93,7 +93,7 @@ if (WIN32)
|
|||
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF")
|
||||
endif()
|
||||
|
||||
link_hifi_libraries(entities-renderer)
|
||||
link_hifi_libraries(entities-renderer platform)
|
||||
|
||||
# perform standard include and linking for found externals
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
|
|
|
@ -323,9 +323,11 @@ void TestCreator::startTestsEvaluation(
|
|||
|
||||
QString expectedImagePartialSourceDirectory = getExpectedImagePartialSourceDirectory(currentFilename);
|
||||
|
||||
// Images are stored on GitHub as ExpectedImage_ddddd.png
|
||||
// Extract the digits at the end of the filename (excluding the file extension)
|
||||
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
|
||||
// Images are stored on GitHub as ExpectedImage_ddddd.png or ExpectedImage_some_metadata_ddddd.png
|
||||
// Extract the part of the filename after "ExpectedImage_" and excluding the file extension
|
||||
QString expectedImageFilenameTail = currentFilename.left(currentFilename.lastIndexOf("."));
|
||||
int expectedImageStart = expectedImageFilenameTail.lastIndexOf(".") + 1;
|
||||
expectedImageFilenameTail.remove(0, expectedImageStart);
|
||||
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
|
||||
|
||||
QString imageURLString("https://raw.githubusercontent.com/" + user + "/" + GIT_HUB_REPOSITORY + "/" + branch + "/" +
|
||||
|
@ -495,6 +497,93 @@ void TestCreator::createTests(const QString& clientProfile) {
|
|||
QMessageBox::information(0, "Success", "Test images have been created");
|
||||
}
|
||||
|
||||
namespace TestProfile {
|
||||
std::vector<QString> tiers = [](){
|
||||
std::vector<QString> toReturn;
|
||||
for (int tier = (int)platform::Profiler::Tier::LOW; tier < (int)platform::Profiler::Tier::NumTiers; ++tier) {
|
||||
QString tierStringUpper = platform::Profiler::TierNames[tier];
|
||||
toReturn.push_back(tierStringUpper.toLower());
|
||||
}
|
||||
return toReturn;
|
||||
}();
|
||||
|
||||
std::vector<QString> operatingSystems = { "windows", "mac", "linux", "android" };
|
||||
|
||||
std::vector<QString> gpus = { "amd", "nvidia", "intel" };
|
||||
};
|
||||
|
||||
enum class ProfileCategory {
|
||||
TIER,
|
||||
OS,
|
||||
GPU
|
||||
};
|
||||
const std::map<QString, ProfileCategory> propertyToProfileCategory = [](){
|
||||
std::map<QString, ProfileCategory> toReturn;
|
||||
for (const auto& tier : TestProfile::tiers) {
|
||||
toReturn[tier] = ProfileCategory::TIER;
|
||||
}
|
||||
for (const auto& os : TestProfile::operatingSystems) {
|
||||
toReturn[os] = ProfileCategory::OS;
|
||||
}
|
||||
for (const auto& gpu : TestProfile::gpus) {
|
||||
toReturn[gpu] = ProfileCategory::GPU;
|
||||
}
|
||||
return toReturn;
|
||||
}();
|
||||
|
||||
TestFilter::TestFilter(const QString& filterString) {
|
||||
auto filterParts = filterString.split(".", QString::SkipEmptyParts);
|
||||
for (const auto& filterPart : filterParts) {
|
||||
QList<QString> allowedVariants = filterPart.split(",", QString::SkipEmptyParts);
|
||||
if (allowedVariants.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& referenceVariant = allowedVariants[0];
|
||||
auto foundCategoryIt = propertyToProfileCategory.find(referenceVariant);
|
||||
if (foundCategoryIt == propertyToProfileCategory.cend()) {
|
||||
error = "Invalid test filter property '" + referenceVariant + "'";
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileCategory selectedFilterCategory = foundCategoryIt->second;
|
||||
for (auto allowedVariantIt = ++(allowedVariants.cbegin()); allowedVariantIt != allowedVariants.cend(); ++allowedVariantIt) {
|
||||
auto& currentVariant = *allowedVariantIt;
|
||||
auto nextCategoryIt = propertyToProfileCategory.find(currentVariant);
|
||||
if (nextCategoryIt == propertyToProfileCategory.cend()) {
|
||||
error = "Invalid test filter property '" + referenceVariant + "'";
|
||||
return;
|
||||
}
|
||||
auto& currentCategory = nextCategoryIt->second;
|
||||
if (currentCategory != selectedFilterCategory) {
|
||||
error = "Mismatched comma-separated test filter properties '" + referenceVariant + "' and '" + currentVariant + "'";
|
||||
return;
|
||||
}
|
||||
// List of comma-separated test property variants is consistent so far
|
||||
}
|
||||
|
||||
switch (selectedFilterCategory) {
|
||||
case ProfileCategory::TIER:
|
||||
allowedTiers.insert(allowedTiers.cend(), allowedVariants.cbegin(), allowedVariants.cend());
|
||||
break;
|
||||
case ProfileCategory::OS:
|
||||
allowedOperatingSystems.insert(allowedOperatingSystems.cend(), allowedVariants.cbegin(), allowedVariants.cend());
|
||||
break;
|
||||
case ProfileCategory::GPU:
|
||||
allowedGPUs.insert(allowedGPUs.cend(), allowedVariants.cbegin(), allowedVariants.cend());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TestFilter::isValid() const {
|
||||
return error.isEmpty();
|
||||
}
|
||||
|
||||
QString TestFilter::getError() const {
|
||||
return error;
|
||||
}
|
||||
|
||||
ExtractedText TestCreator::getTestScriptLines(QString testFileName) {
|
||||
ExtractedText relevantTextFromTest;
|
||||
|
||||
|
@ -511,7 +600,7 @@ ExtractedText TestCreator::getTestScriptLines(QString testFileName) {
|
|||
QString line = stream.readLine();
|
||||
|
||||
// Name of test is the string in the following line:
|
||||
// nitpick.perform("Apply Material Entities to Avatars", Script.resolvePath("."), function(testType) {...
|
||||
// nitpick.perform("Apply Material Entities to Avatars", Script.resolvePath("."), "secondary", undefined, function(testType) {...
|
||||
const QString ws("\\h*"); //white-space character
|
||||
const QString functionPerformName(ws + "nitpick" + ws + "\\." + ws + "perform");
|
||||
const QString quotedString("\\\".+\\\"");
|
||||
|
@ -644,6 +733,17 @@ void TestCreator::createAllMDFiles() {
|
|||
QMessageBox::information(0, "Success", "MD files have been created");
|
||||
}
|
||||
|
||||
QString joinVector(const std::vector<QString>& qStringVector, const char* separator) {
|
||||
if (qStringVector.empty()) {
|
||||
return QString("");
|
||||
}
|
||||
QString joined = qStringVector[0];
|
||||
for (std::size_t i = 1; i < qStringVector.size(); ++i) {
|
||||
joined += separator + qStringVector[i];
|
||||
}
|
||||
return joined;
|
||||
}
|
||||
|
||||
bool TestCreator::createMDFile(const QString& directory) {
|
||||
// Verify folder contains test.js file
|
||||
QString testFileName(directory + "/" + TEST_FILENAME);
|
||||
|
@ -655,33 +755,73 @@ bool TestCreator::createMDFile(const QString& directory) {
|
|||
|
||||
ExtractedText testScriptLines = getTestScriptLines(testFileName);
|
||||
|
||||
QDir qDirectory(directory);
|
||||
|
||||
QString mdFilename(directory + "/" + "test.md");
|
||||
QFile mdFile(mdFilename);
|
||||
if (!mdFile.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
|
||||
// TODO: Don't just exit
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&mdFile);
|
||||
|
||||
//TestCreator title
|
||||
QString testName = testScriptLines.title;
|
||||
stream << "# " << testName << "\n";
|
||||
|
||||
stream << "## Run this script URL: [Manual](./test.js?raw=true) [Auto](./testAuto.js?raw=true)(from menu/Edit/Open and Run scripts from URL...)." << "\n\n";
|
||||
|
||||
stream << "## Preconditions" << "\n";
|
||||
stream << "- In an empty region of a domain with editing rights." << "\n\n";
|
||||
stream << "- In an empty region of a domain with editing rights." << "\n";
|
||||
stream << "\n";
|
||||
|
||||
// ExpectedImage_00000.png OR ExpectedImage_some_stu-ff_00000.png
|
||||
const QRegularExpression firstExpectedImage("^ExpectedImage(_[-_\\w]*)?_00000\\.png$");
|
||||
std::vector<QString> testDescriptors;
|
||||
std::vector<TestFilter> testFilters;
|
||||
|
||||
for (const auto& potentialImageFile : qDirectory.entryInfoList()) {
|
||||
if (potentialImageFile.isDir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto firstExpectedImageMatch = firstExpectedImage.match(potentialImageFile.fileName());
|
||||
if (!firstExpectedImageMatch.hasMatch()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString testDescriptor = firstExpectedImageMatch.captured(1);
|
||||
auto filterString = QString(testDescriptor).replace("_", ".").replace("-", ",");
|
||||
TestFilter descriptorAsFilter(filterString);
|
||||
|
||||
testDescriptors.push_back(testDescriptor);
|
||||
testFilters.push_back(descriptorAsFilter);
|
||||
}
|
||||
|
||||
stream << "## Steps\n";
|
||||
stream << "Press '" + ADVANCE_KEY + "' key to advance step by step\n\n"; // note apostrophes surrounding 'ADVANCE_KEY'
|
||||
|
||||
int snapShotIndex { 0 };
|
||||
for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
|
||||
stream << "### Step " << QString::number(i + 1) << "\n";
|
||||
stream << "- " << testScriptLines.stepList[i]->text << "\n";
|
||||
if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i]->takeSnapshot) {
|
||||
stream << "- .rightJustified(5, '0') << ".png)\n";
|
||||
for (int i = 0; i < (int)testDescriptors.size(); ++i) {
|
||||
const auto& testDescriptor = testDescriptors[i];
|
||||
const auto& descriptorAsFilter = testFilters[i];
|
||||
if (descriptorAsFilter.isValid()) {
|
||||
stream << "- Expected image on ";
|
||||
stream << (descriptorAsFilter.allowedTiers.empty() ? "any" : joinVector(descriptorAsFilter.allowedTiers, "/")) << " tier, ";
|
||||
stream << (descriptorAsFilter.allowedOperatingSystems.empty() ? "any" : joinVector(descriptorAsFilter.allowedOperatingSystems, "/")) << " OS, ";
|
||||
stream << (descriptorAsFilter.allowedGPUs.empty() ? "any" : joinVector(descriptorAsFilter.allowedGPUs, "/")) << " GPU";
|
||||
stream << ":";
|
||||
} else {
|
||||
// Fall back to displaying file name
|
||||
stream << "- ExpectedImage" << testDescriptor << "_" << QString::number(snapShotIndex).rightJustified(5, '0') << ".png";
|
||||
}
|
||||
stream << "\n";
|
||||
stream << "- .rightJustified(5, '0') << ".png)\n";
|
||||
}
|
||||
++snapShotIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <QtCore/QRegularExpression>
|
||||
#include <QProgressBar>
|
||||
|
||||
#include <platform/Profiler.h>
|
||||
|
||||
#include "AWSInterface.h"
|
||||
#include "ImageComparer.h"
|
||||
#include "Downloader.h"
|
||||
|
@ -30,6 +32,20 @@ public:
|
|||
|
||||
using StepList = std::vector<Step*>;
|
||||
|
||||
class TestFilter {
|
||||
public:
|
||||
TestFilter(const QString& filterString);
|
||||
bool isValid() const;
|
||||
QString getError() const;
|
||||
|
||||
std::vector<QString> allowedTiers;
|
||||
std::vector<QString> allowedOperatingSystems;
|
||||
std::vector<QString> allowedGPUs;
|
||||
|
||||
protected:
|
||||
QString error;
|
||||
};
|
||||
|
||||
class ExtractedText {
|
||||
public:
|
||||
QString title;
|
||||
|
|
Loading…
Reference in a new issue