mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' into tweak_sit_pose
This commit is contained in:
commit
e8492beb4a
24 changed files with 468 additions and 268 deletions
|
@ -186,15 +186,6 @@ Rectangle {
|
|||
backgroundOnColor: "#E3E3E3";
|
||||
checked: muted;
|
||||
onClicked: {
|
||||
if (pushToTalk && !checked) {
|
||||
// disable push to talk if unmuting
|
||||
if (bar.currentIndex === 0) {
|
||||
AudioScriptingInterface.pushToTalkDesktop = false;
|
||||
}
|
||||
else {
|
||||
AudioScriptingInterface.pushToTalkHMD = false;
|
||||
}
|
||||
}
|
||||
if (bar.currentIndex === 0) {
|
||||
AudioScriptingInterface.mutedDesktop = checked;
|
||||
}
|
||||
|
|
|
@ -5863,8 +5863,13 @@ bool MyAvatar::endReaction(QString reactionName) {
|
|||
int reactionIndex = beginEndReactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
|
||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||
_reactionEnabledRefCounts[reactionIndex]--;
|
||||
return true;
|
||||
if (_reactionEnabledRefCounts[reactionIndex] > 0) {
|
||||
_reactionEnabledRefCounts[reactionIndex]--;
|
||||
return true;
|
||||
} else {
|
||||
_reactionEnabledRefCounts[reactionIndex] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -101,6 +101,10 @@ void Audio::setMutedDesktop(bool isMuted) {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (!isMuted && _settingsLoaded) {
|
||||
// Disable Push-To-Talk if muted is changed to false. Settings also need to be loaded.
|
||||
setPTTDesktop(isMuted);
|
||||
}
|
||||
if (changed) {
|
||||
if (!isHMD) {
|
||||
emit mutedChanged(isMuted);
|
||||
|
@ -128,6 +132,10 @@ void Audio::setMutedHMD(bool isMuted) {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (!isMuted && _settingsLoaded) {
|
||||
// Disable Push-To-Talk if muted is changed to false. Settings also need to be loaded.
|
||||
setPTTHMD(isMuted);
|
||||
}
|
||||
if (changed) {
|
||||
if (isHMD) {
|
||||
emit mutedChanged(isMuted);
|
||||
|
@ -187,9 +195,9 @@ void Audio::setPTTDesktop(bool enabled) {
|
|||
_pttDesktop = enabled;
|
||||
}
|
||||
});
|
||||
if (enabled || _settingsLoaded) {
|
||||
if (enabled && _settingsLoaded) {
|
||||
// Set to default behavior (muted for Desktop) on Push-To-Talk disable or when enabled. Settings also need to be loaded.
|
||||
setMutedDesktop(true);
|
||||
setMutedDesktop(enabled);
|
||||
}
|
||||
if (changed) {
|
||||
emit pushToTalkChanged(enabled);
|
||||
|
@ -211,7 +219,7 @@ void Audio::setPTTHMD(bool enabled) {
|
|||
_pttHMD = enabled;
|
||||
}
|
||||
});
|
||||
if (enabled || _settingsLoaded) {
|
||||
if (enabled && _settingsLoaded) {
|
||||
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable or muted for when PTT is enabled.
|
||||
setMutedHMD(enabled);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
|
||||
NSLog(@"interface downloaded %d%%", (int)(100.0*prog));
|
||||
|
||||
if ((int)(100.0 * prog) != (int)self.progressPercentage) {
|
||||
NSLog(@"interface downloaded %d%%", (int)(100.0*prog));
|
||||
}
|
||||
|
||||
self.progressPercentage = (100.0 * prog);
|
||||
[[Launcher sharedLauncher] updateProgressIndicator];
|
||||
|
|
|
@ -1,31 +1,20 @@
|
|||
#import "LatestBuildRequest.h"
|
||||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
#import "Interface.h"
|
||||
|
||||
@implementation LatestBuildRequest
|
||||
|
||||
- (NSInteger) getCurrentVersion {
|
||||
NSInteger currentVersion;
|
||||
@try {
|
||||
NSString* interfaceAppPath = [[Launcher.sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
|
||||
NSError* error = nil;
|
||||
Interface* interface = [[Interface alloc] initWith:interfaceAppPath];
|
||||
currentVersion = [interface getVersion:&error];
|
||||
if (currentVersion == 0 && error != nil) {
|
||||
NSLog(@"can't get version from interface, falling back to settings: %@", error);
|
||||
currentVersion = [Settings.sharedSettings latestBuildVersion];
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"an exception was thrown: %@", exception);
|
||||
currentVersion = [Settings.sharedSettings latestBuildVersion];
|
||||
}
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
- (void) requestLatestBuildInfo {
|
||||
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";
|
||||
}
|
||||
|
||||
NSLog(@"Making request for builds to: %@", buildsURL);
|
||||
|
||||
NSMutableURLRequest* request = [NSMutableURLRequest new];
|
||||
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
||||
[request setURL:[NSURL URLWithString:buildsURL]];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
@ -39,7 +28,7 @@
|
|||
NSLog(@"Latest Build Request Response: %ld", [ne statusCode]);
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
|
||||
if ([ne statusCode] == 500) {
|
||||
if (error || [ne statusCode] == 500) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[sharedLauncher displayErrorPage];
|
||||
});
|
||||
|
@ -60,36 +49,26 @@
|
|||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSArray* values = [json valueForKey:@"results"];
|
||||
NSDictionary* launcherValues = [json valueForKey:@"launcher"];
|
||||
NSDictionary* value = [values objectAtIndex:0];
|
||||
|
||||
NSString* defaultBuildTag = [json valueForKey:@"default_tag"];
|
||||
|
||||
NSString* launcherVersion = [launcherValues valueForKey:@"version"];
|
||||
NSString* launcherUrl = [[launcherValues valueForKey:@"mac"] valueForKey:@"url"];
|
||||
NSString* buildNumber = [value valueForKey:@"latest_version"];
|
||||
NSDictionary* installers = [value objectForKey:@"installers"];
|
||||
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
|
||||
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
|
||||
|
||||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
NSInteger currentVersion = [self getCurrentVersion];
|
||||
NSInteger currentLauncherVersion = atoi(LAUNCHER_BUILD_VERSION);
|
||||
NSLog(@"Latest Build Request -> current launcher version %ld", currentLauncherVersion);
|
||||
NSLog(@"Latest Build Request -> latest launcher version %ld", launcherVersion.integerValue);
|
||||
NSLog(@"Latest Build Request -> launcher url %@", launcherUrl);
|
||||
NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE");
|
||||
NSLog(@"Latest Build Request -> current version: %ld", currentVersion);
|
||||
NSLog(@"Latest Build Request -> latest version: %ld", buildNumber.integerValue);
|
||||
NSLog(@"Latest Build Request -> mac url: %@", macInstallerUrl);
|
||||
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
|
||||
BOOL latestLauncherVersionAvailable = (currentLauncherVersion != launcherVersion.integerValue);
|
||||
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
|
||||
|
||||
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
|
||||
NSLog(@"Latest Build Request -> SHOULD DOWNLOAD: %@", shouldDownloadInterface ? @"TRUE" : @"FALSE");
|
||||
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl
|
||||
:latestLauncherVersionAvailable :launcherUrl];
|
||||
[sharedLauncher shouldDownloadLatestBuild:values
|
||||
:defaultBuildTag
|
||||
:latestLauncherVersionAvailable
|
||||
:launcherUrl];
|
||||
});
|
||||
}];
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ struct LatestBuildInfo {
|
|||
@property (nonatomic) BOOL waitingForInterfaceToTerminate;
|
||||
@property (nonatomic) BOOL shouldDownloadInterface;
|
||||
@property (nonatomic) BOOL latestBuildRequestFinished;
|
||||
@property (nonatomic, assign) NSArray* latestBuilds;
|
||||
@property (nonatomic, assign) NSString* defaultBuildTag;
|
||||
@property (nonatomic, assign) NSTimer* updateProgressIndicatorTimer;
|
||||
@property (nonatomic, assign, readwrite) ProcessState processState;
|
||||
@property (nonatomic, assign, readwrite) LoginError loginError;
|
||||
|
@ -70,6 +72,7 @@ struct LatestBuildInfo {
|
|||
- (void) domainContentDownloadFinished;
|
||||
- (void) domainScriptsDownloadFinished;
|
||||
- (void) setDomainURLInfo:(NSString*) aDomainURL :(NSString*) aDomainContentUrl :(NSString*) aDomainScriptsUrl;
|
||||
- (void) setOrganizationBuildTag:(NSString*) organizationBuildTag;
|
||||
- (void) organizationRequestFinished:(BOOL) aOriginzationAccepted;
|
||||
- (BOOL) loginShouldSetErrorState;
|
||||
- (void) displayErrorPage;
|
||||
|
@ -81,7 +84,9 @@ struct LatestBuildInfo {
|
|||
- (void) setCurrentProcessState:(ProcessState) aProcessState;
|
||||
- (void) setLoginErrorState:(LoginError) aLoginError;
|
||||
- (LoginError) getLoginErrorState;
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl;
|
||||
- (void) updateLatestBuildInfo;
|
||||
- (void) shouldDownloadLatestBuild:(NSArray*) latestBuilds :(NSString*) defaultBuildTag :(BOOL) newLauncherAvailable :(NSString*) launcherUrl;
|
||||
- (void) tryDownloadLatestBuild:(BOOL)progressScreenAlreadyDisplayed;
|
||||
- (void) interfaceFinishedDownloading;
|
||||
- (NSString*) getDownloadPathForContentAndScripts;
|
||||
- (void) launchInterface;
|
||||
|
@ -97,11 +102,9 @@ struct LatestBuildInfo {
|
|||
- (NSString*) getDownloadFilename;
|
||||
- (void) startUpdateProgressIndicatorTimer;
|
||||
- (void) endUpdateProgressIndicatorTimer;
|
||||
- (BOOL) isLoadedIn;
|
||||
- (BOOL) isLoggedIn;
|
||||
- (NSString*) getAppPath;
|
||||
- (void) updateProgressIndicator;
|
||||
- (void) setLatestBuildInfo:(struct LatestBuildInfo) latestBuildInfo;
|
||||
- (struct LatestBuildInfo) getLatestBuildInfo;
|
||||
|
||||
+ (id) sharedLauncher;
|
||||
@end
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#import "ErrorViewController.h"
|
||||
#import "Settings.h"
|
||||
#import "NSTask+NSTaskExecveAdditions.h"
|
||||
#import "Interface.h"
|
||||
|
||||
@interface Launcher ()
|
||||
|
||||
|
@ -241,7 +242,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
return self.waitingForInterfaceToTerminate;
|
||||
}
|
||||
|
||||
- (BOOL) isLoadedIn
|
||||
- (BOOL) isLoggedIn
|
||||
{
|
||||
return [[Settings sharedSettings] isLoggedIn];
|
||||
}
|
||||
|
@ -255,6 +256,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
[[Settings sharedSettings] setDomainUrl:aDomainURL];
|
||||
}
|
||||
|
||||
- (void) setOrganizationBuildTag:(NSString*) organizationBuildTag;
|
||||
{
|
||||
[[Settings sharedSettings] setOrganizationBuildTag:organizationBuildTag];
|
||||
}
|
||||
|
||||
- (NSString*) getAppPath
|
||||
{
|
||||
return [self getDownloadPathForContentAndScripts];
|
||||
|
@ -275,13 +281,26 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
self.displayName = aDiplayName;
|
||||
}
|
||||
|
||||
- (NSInteger) getCurrentVersion {
|
||||
NSInteger currentVersion;
|
||||
@try {
|
||||
NSString* interfaceAppPath = [[self getAppPath] stringByAppendingString:@"interface.app"];
|
||||
NSError* error = nil;
|
||||
Interface* interface = [[Interface alloc] initWith:interfaceAppPath];
|
||||
currentVersion = [interface getVersion:&error];
|
||||
if (currentVersion == 0 && error != nil) {
|
||||
NSLog(@"can't get version from interface: %@", error);
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"an exception was thrown while getting current interface version: %@", exception);
|
||||
currentVersion = 0;
|
||||
}
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
- (void) domainContentDownloadFinished
|
||||
{
|
||||
if (self.shouldDownloadInterface) {
|
||||
[self.downloadInterface downloadInterface: self.interfaceDownloadUrl];
|
||||
return;
|
||||
}
|
||||
[self interfaceFinishedDownloading];
|
||||
[self tryDownloadLatestBuild:TRUE];
|
||||
}
|
||||
|
||||
- (void) domainScriptsDownloadFinished
|
||||
|
@ -337,6 +356,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
{
|
||||
self.credentialsAccepted = aOriginzationAccepted;
|
||||
if (aOriginzationAccepted) {
|
||||
[self updateLatestBuildInfo];
|
||||
[self.credentialsRequest confirmCredentials:self.username : self.password];
|
||||
} else {
|
||||
LoginScreen* loginScreen = [[LoginScreen alloc] initWithNibName:@"LoginScreen" bundle:nil];
|
||||
|
@ -349,42 +369,26 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (struct LatestBuildInfo) getLatestBuildInfo
|
||||
{
|
||||
return self.buildInfo;
|
||||
}
|
||||
|
||||
- (void) setLatestBuildInfo:(struct LatestBuildInfo) latestBuildInfo
|
||||
{
|
||||
self.buildInfo = latestBuildInfo;
|
||||
}
|
||||
|
||||
-(void) showLoginScreen
|
||||
{
|
||||
LoginScreen* loginScreen = [[LoginScreen alloc] initWithNibName:@"LoginScreen" bundle:nil];
|
||||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
|
||||
}
|
||||
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl
|
||||
- (void) shouldDownloadLatestBuild:(NSArray*) latestBuilds :(NSString*) defaultBuildTag :(BOOL) newLauncherAvailable :(NSString*) launcherUrl
|
||||
{
|
||||
self.latestBuilds = [[NSArray alloc] initWithArray:latestBuilds copyItems:true];
|
||||
self.defaultBuildTag = defaultBuildTag;
|
||||
|
||||
[self updateLatestBuildInfo];
|
||||
|
||||
NSDictionary* launcherArguments = [LauncherCommandlineArgs arguments];
|
||||
if (newLauncherAvailable && ![launcherArguments valueForKey: @"--noUpdate"]) {
|
||||
[self.downloadLauncher downloadLauncher: launcherUrl];
|
||||
} else {
|
||||
self.shouldDownloadInterface = shouldDownload;
|
||||
self.interfaceDownloadUrl = downloadUrl;
|
||||
self.latestBuildRequestFinished = TRUE;
|
||||
if ([self isLoadedIn]) {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
|
||||
if (shouldDownload) {
|
||||
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
|
||||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
|
||||
[self startUpdateProgressIndicatorTimer];
|
||||
[self.downloadInterface downloadInterface: downloadUrl];
|
||||
return;
|
||||
}
|
||||
[self interfaceFinishedDownloading];
|
||||
if ([self isLoggedIn]) {
|
||||
[self tryDownloadLatestBuild:FALSE];
|
||||
} else {
|
||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
|
||||
[self showLoginScreen];
|
||||
|
@ -392,6 +396,67 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
}
|
||||
}
|
||||
|
||||
// The latest builds are always retrieved on application start because they contain not only
|
||||
// the latest interface builds, but also the latest launcher builds, which are required to know if
|
||||
// we need to self-update first. The interface builds are categorized by build tag, and we may
|
||||
// not know at application start which build tag we should be using. There are 2 scenarios where
|
||||
// we call this function to determine our build tag and the correct build:
|
||||
//
|
||||
// 1. If we are logged in, we will have our build tag and can immediately get the correct build
|
||||
// after receiving the builds.
|
||||
// 2. If we are not logged in, we need to wait until we have logged in and received the org
|
||||
// metadata for the user. The latest build info also needs to be updated _before_ downloading
|
||||
// the content set cache because the progress bar value depends on it.
|
||||
//
|
||||
- (void) updateLatestBuildInfo {
|
||||
NSLog(@"Updating latest build info");
|
||||
|
||||
NSInteger currentVersion = [self getCurrentVersion];
|
||||
NSInteger latestVersion = 0;
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
|
||||
BOOL newVersionAvailable = false;
|
||||
NSString* url = @"";
|
||||
NSString* buildTag = [[Settings sharedSettings] organizationBuildTag];
|
||||
if ([buildTag length] == 0) {
|
||||
buildTag = self.defaultBuildTag;
|
||||
}
|
||||
|
||||
for (NSDictionary* build in self.latestBuilds) {
|
||||
NSString* name = [build valueForKey:@"name"];
|
||||
NSLog(@"Checking %@", name);
|
||||
if ([name isEqual:buildTag]) {
|
||||
url = [[[build objectForKey:@"installers"] objectForKey:@"mac"] valueForKey:@"zip_url"];
|
||||
NSString* thisLatestVersion = [build valueForKey:@"latest_version"];
|
||||
latestVersion = thisLatestVersion.integerValue;
|
||||
newVersionAvailable = currentVersion != latestVersion;
|
||||
NSLog(@"Using %@, %ld", name, latestVersion);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.shouldDownloadInterface = newVersionAvailable;
|
||||
self.interfaceDownloadUrl = url;
|
||||
|
||||
NSLog(@"Updating latest build info, currentVersion=%ld, latestVersion=%ld, %@ %@",
|
||||
currentVersion, latestVersion, (self.shouldDownloadInterface ? @"Yes" : @"No"), self.interfaceDownloadUrl);
|
||||
}
|
||||
|
||||
- (void) tryDownloadLatestBuild:(BOOL)progressScreenAlreadyDisplayed
|
||||
{
|
||||
if (self.shouldDownloadInterface) {
|
||||
if (!progressScreenAlreadyDisplayed) {
|
||||
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
|
||||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
|
||||
[self startUpdateProgressIndicatorTimer];
|
||||
}
|
||||
[self.downloadInterface downloadInterface: self.interfaceDownloadUrl];
|
||||
return;
|
||||
}
|
||||
|
||||
[self interfaceFinishedDownloading];
|
||||
}
|
||||
|
||||
-(void)runAutoupdater
|
||||
{
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <CommonCrypto/CommonDigest.h>
|
||||
#include <CommonCrypto/CommonHMAC.h>
|
||||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
|
||||
|
||||
static NSString* const organizationURL = @"https://orgs.highfidelity.com/organizations/";
|
||||
|
@ -76,11 +77,16 @@ static NSString* const organizationURL = @"https://orgs.highfidelity.com/organiz
|
|||
}
|
||||
NSString* domainURL = [json valueForKey:@"domain"];
|
||||
NSString* contentSetURL = [json valueForKey:@"content_set_url"];
|
||||
NSString* buildTag = [json valueForKey:@"build_tag"];
|
||||
if (buildTag == nil) {
|
||||
buildTag = @"";
|
||||
}
|
||||
|
||||
if (domainURL != nil && contentSetURL != nil) {
|
||||
NSLog(@"Organization: getting org file successful");
|
||||
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :nil];
|
||||
[sharedLauncher setLoginErrorState: NONE];
|
||||
[[Settings sharedSettings] setOrganizationBuildTag:buildTag];
|
||||
[sharedLauncher organizationRequestFinished:TRUE];
|
||||
} else {
|
||||
NSLog(@"Organization: Either domainURL: %@ or contentSetURL: %@ json entries are invalid", domainURL, contentSetURL);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
@property (nonatomic, assign) BOOL loggedIn;
|
||||
@property (nonatomic, assign) NSString* domain;
|
||||
@property (nonatomic, assign) NSString* launcher;
|
||||
@property (nonatomic, assign) NSString* _organizationBuildTag;
|
||||
- (NSInteger) latestBuildVersion;
|
||||
- (BOOL) isLoggedIn;
|
||||
- (void) login:(BOOL)aLoggedIn;
|
||||
|
@ -15,6 +16,8 @@
|
|||
- (NSString*) getLaucnherPath;
|
||||
- (void) setDomainUrl:(NSString*) aDomainUrl;
|
||||
- (NSString*) getDomainUrl;
|
||||
- (void) setOrganizationBuildTag:(NSString*) aOrganizationBuildTag;
|
||||
- (NSString*) organizationBuildTag;
|
||||
- (void) save;
|
||||
+ (id) sharedSettings;
|
||||
@end
|
||||
|
|
|
@ -32,13 +32,18 @@
|
|||
NSError * err;
|
||||
NSData *data =[jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary * json;
|
||||
if(data!=nil){
|
||||
if (data != nil) {
|
||||
json = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
|
||||
|
||||
self.loggedIn = [[json valueForKey:@"loggedIn"] boolValue];
|
||||
self.build = [[json valueForKey:@"build_version"] integerValue];
|
||||
self.launcher = [json valueForKey:@"luancherPath"];
|
||||
self.domain = [json valueForKey:@"domain"];
|
||||
self.organizationBuildTag = [json valueForKey:@"organizationBuildTag"];
|
||||
if ([self.organizationBuildTag length] == 0) {
|
||||
self.organizationBuildTag = @"";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +58,9 @@
|
|||
[NSString stringWithFormat:@"%ld", self.build], @"build_version",
|
||||
self.loggedIn ? @"TRUE" : @"FALSE", @"loggedIn",
|
||||
self.domain, @"domain",
|
||||
self.launcher, @"launcherPath", nil];
|
||||
self.launcher, @"launcherPath",
|
||||
self.organizationBuildTag, @"organizationBuildTag",
|
||||
nil];
|
||||
NSError * err;
|
||||
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&err];
|
||||
NSString * jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
@ -114,6 +121,16 @@
|
|||
return self.domain;
|
||||
}
|
||||
|
||||
- (void) setOrganizationBuildTag:(NSString*) aOrganizationBuildTag
|
||||
{
|
||||
self._organizationBuildTag = aOrganizationBuildTag;
|
||||
}
|
||||
|
||||
- (NSString*) organizationBuildTag
|
||||
{
|
||||
return self._organizationBuildTag;
|
||||
}
|
||||
|
||||
- (NSString*) getLaucnherPath
|
||||
{
|
||||
return self.launcher;
|
||||
|
|
|
@ -36,6 +36,7 @@ void LauncherManager::init(BOOL allowUpdate, ContinueActionOnStart continueActio
|
|||
}
|
||||
addToLog(_T("Launcher is running version: " + _launcherVersion));
|
||||
addToLog(_T("Getting most recent builds"));
|
||||
_isInstalled = isApplicationInstalled(_currentVersion, _domainURL, _contentURL, _loggedIn, _organizationBuildTag);
|
||||
getMostRecentBuilds(_latestLauncherURL, _latestLauncherVersion, _latestApplicationURL, _latestVersion);
|
||||
}
|
||||
|
||||
|
@ -260,14 +261,14 @@ BOOL LauncherManager::deleteShortcuts() {
|
|||
}
|
||||
|
||||
BOOL LauncherManager::isApplicationInstalled(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn) {
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag) {
|
||||
CString applicationDir;
|
||||
getAndCreatePaths(PathType::Launcher_Directory, applicationDir);
|
||||
CString applicationPath = applicationDir + "interface\\interface.exe";
|
||||
BOOL isInstalled = PathFileExistsW(applicationPath);
|
||||
BOOL configFileExist = PathFileExistsW(applicationDir + _T("interface\\config.json"));
|
||||
if (configFileExist) {
|
||||
LauncherUtils::ResponseError status = readConfigJSON(version, domain, content, loggedIn);
|
||||
LauncherUtils::ResponseError status = readConfigJSON(version, domain, content, loggedIn, organizationBuildTag);
|
||||
return isInstalled && status == LauncherUtils::ResponseError::NoError;
|
||||
}
|
||||
return FALSE;
|
||||
|
@ -326,7 +327,6 @@ BOOL LauncherManager::getInstalledVersion(const CString& path, CString& version)
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
HWND LauncherManager::launchApplication() {
|
||||
CString installDir;
|
||||
LauncherManager::getAndCreatePaths(PathType::Interface_Directory, installDir);
|
||||
|
@ -368,6 +368,7 @@ BOOL LauncherManager::createConfigJSON() {
|
|||
config["launcherPath"] = LauncherUtils::cStringToStd(applicationPath);
|
||||
config["version"] = LauncherUtils::cStringToStd(_latestVersion);
|
||||
config["domain"] = LauncherUtils::cStringToStd(_domainURL);
|
||||
config["organizationBuildTag"] = LauncherUtils::cStringToStd(_organizationBuildTag);
|
||||
CString content;
|
||||
getAndCreatePaths(PathType::Content_Directory, content);
|
||||
config["content"] = LauncherUtils::cStringToStd(content);
|
||||
|
@ -377,7 +378,7 @@ BOOL LauncherManager::createConfigJSON() {
|
|||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn) {
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag) {
|
||||
CString configPath;
|
||||
getAndCreatePaths(PathType::Interface_Directory, configPath);
|
||||
configPath += "\\config.json";
|
||||
|
@ -394,6 +395,13 @@ LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, C
|
|||
version = config["version"].asCString();
|
||||
domain = config["domain"].asCString();
|
||||
content = config["content"].asCString();
|
||||
|
||||
if (config["organizationBuildTag"].isString()) {
|
||||
organizationBuildTag = config["organizationBuildTag"].asCString();
|
||||
} else {
|
||||
organizationBuildTag = "";
|
||||
}
|
||||
|
||||
configFile.close();
|
||||
return LauncherUtils::ResponseError::NoError;
|
||||
}
|
||||
|
@ -401,12 +409,43 @@ LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, C
|
|||
return LauncherUtils::ResponseError::ParsingJSON;
|
||||
}
|
||||
|
||||
bool findBuildInResponse(const Json::Value& json, const CString& tag, CString& interfaceUrlOut, CString& interfaceVersionOut) {
|
||||
if (json["results"].isArray()) {
|
||||
auto& results = json["results"];
|
||||
int count = results.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (results[i].isObject()) {
|
||||
Json::Value result = results[i];
|
||||
if (result["name"].asCString() == tag) {
|
||||
if (result["latest_version"].isInt()) {
|
||||
std::string version = std::to_string(result["latest_version"].asInt());
|
||||
interfaceVersionOut = CString(version.c_str());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (result["installers"].isObject() &&
|
||||
result["installers"]["windows"].isObject() &&
|
||||
result["installers"]["windows"]["zip_url"].isString()) {
|
||||
interfaceUrlOut = result["installers"]["windows"]["zip_url"].asCString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString& hash) {
|
||||
CString contentTypeJson = L"content-type:application/json";
|
||||
CString response;
|
||||
CString url = _T("/organizations/") + hash + _T(".json");
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
L"orgs.highfidelity.com", url,
|
||||
true, L"orgs.highfidelity.com", url,
|
||||
contentTypeJson, CStringA(),
|
||||
response, false);
|
||||
if (error != LauncherUtils::ResponseError::NoError) {
|
||||
|
@ -417,6 +456,13 @@ LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString
|
|||
if (json["content_set_url"].isString() && json["domain"].isString()) {
|
||||
_contentURL = json["content_set_url"].asCString();
|
||||
_domainURL = json["domain"].asCString();
|
||||
_organizationBuildTag = json.get("build_tag", "").asCString();
|
||||
auto buildTag = _organizationBuildTag.IsEmpty() ? _defaultBuildTag : _organizationBuildTag;
|
||||
|
||||
if (!findBuildInResponse(_latestBuilds, buildTag, _latestApplicationURL, _latestVersion)) {
|
||||
return LauncherUtils::ResponseError::ParsingJSON;
|
||||
}
|
||||
|
||||
return LauncherUtils::ResponseError::NoError;
|
||||
}
|
||||
}
|
||||
|
@ -429,8 +475,12 @@ void LauncherManager::getMostRecentBuilds(CString& launcherUrlOut, CString& laun
|
|||
std::function<void(CString, int)> httpCallback = [&](CString response, int err) {
|
||||
LauncherUtils::ResponseError error = LauncherUtils::ResponseError(err);
|
||||
if (error == LauncherUtils::ResponseError::NoError) {
|
||||
Json::Value json;
|
||||
Json::Value& json = _latestBuilds;
|
||||
if (LauncherUtils::parseJSON(response, json)) {
|
||||
_defaultBuildTag = json.get("default_tag", "").asCString();
|
||||
auto buildTag = _organizationBuildTag.IsEmpty() ? _defaultBuildTag : _organizationBuildTag;
|
||||
addToLog(_T("Build tag is: ") + buildTag);
|
||||
|
||||
if (json["launcher"].isObject()) {
|
||||
if (json["launcher"]["windows"].isObject() && json["launcher"]["windows"]["url"].isString()) {
|
||||
launcherUrlOut = json["launcher"]["windows"]["url"].asCString();
|
||||
|
@ -440,41 +490,50 @@ void LauncherManager::getMostRecentBuilds(CString& launcherUrlOut, CString& laun
|
|||
launcherVersionOut = CString(version.c_str());
|
||||
}
|
||||
}
|
||||
int count = json["count"].isInt() ? json["count"].asInt() : 0;
|
||||
if (count > 0 && json["results"].isArray()) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (json["results"][i].isObject()) {
|
||||
Json::Value result = json["results"][i];
|
||||
if (result["latest_version"].isInt()) {
|
||||
std::string version = std::to_string(result["latest_version"].asInt());
|
||||
interfaceVersionOut = CString(version.c_str());
|
||||
}
|
||||
if (result["installers"].isObject() &&
|
||||
result["installers"]["windows"].isObject() &&
|
||||
result["installers"]["windows"]["zip_url"].isString()) {
|
||||
interfaceUrlOut = result["installers"]["windows"]["zip_url"].asCString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (launcherUrlOut.IsEmpty() || launcherVersionOut.IsEmpty()) {
|
||||
error = LauncherUtils::ResponseError::ParsingJSON;
|
||||
}
|
||||
if (launcherUrlOut.IsEmpty() || launcherVersionOut.IsEmpty() || interfaceUrlOut.IsEmpty() || interfaceVersionOut.IsEmpty()) {
|
||||
|
||||
if (!findBuildInResponse(json, buildTag, _latestApplicationURL, _latestVersion)) {
|
||||
addToLog(_T("Failed to find build"));
|
||||
error = LauncherUtils::ResponseError::ParsingJSON;
|
||||
}
|
||||
}
|
||||
}
|
||||
onMostRecentBuildsReceived(response, error);
|
||||
};
|
||||
|
||||
bool useHTTPS{ true };
|
||||
|
||||
CString domainName;
|
||||
if (domainName.GetEnvironmentVariable(L"HQ_LAUNCHER_BUILDS_DOMAIN")) {
|
||||
addToLog(_T("Using overridden builds domain: ") + domainName);
|
||||
useHTTPS = false;
|
||||
} else {
|
||||
domainName = L"thunder.highfidelity.com";
|
||||
}
|
||||
|
||||
CString pathName;
|
||||
if (pathName.GetEnvironmentVariable(L"HQ_LAUNCHER_BUILDS_PATH")) {
|
||||
addToLog(_T("Using overridden builds path: ") + pathName);
|
||||
useHTTPS = false;
|
||||
} else {
|
||||
pathName = L"/builds/api/tags/latest?format=json";
|
||||
}
|
||||
|
||||
LauncherUtils::httpCallOnThread(getHttpUserAgent(),
|
||||
L"thunder.highfidelity.com",
|
||||
L"/builds/api/tags/latest?format=json",
|
||||
useHTTPS,
|
||||
domainName,
|
||||
pathName,
|
||||
contentTypeJson, CStringA(), false, httpCallback);
|
||||
}
|
||||
|
||||
void LauncherManager::onMostRecentBuildsReceived(const CString& response, LauncherUtils::ResponseError error) {
|
||||
if (error == LauncherUtils::ResponseError::NoError) {
|
||||
addToLog(_T("Latest launcher version: ") + _latestLauncherVersion);
|
||||
CString currentVersion;
|
||||
BOOL isInstalled = (isApplicationInstalled(currentVersion, _domainURL, _contentURL, _loggedIn) && _loggedIn);
|
||||
CString currentVersion = _currentVersion;
|
||||
BOOL isInstalled = _isInstalled && _loggedIn;
|
||||
bool newInterfaceVersion = _latestVersion.Compare(currentVersion) != 0;
|
||||
bool newLauncherVersion = _latestLauncherVersion.Compare(_launcherVersion) != 0 && _updateLauncherAllowed;
|
||||
if (newLauncherVersion) {
|
||||
|
@ -511,7 +570,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
_shouldWait = FALSE;
|
||||
|
||||
} else {
|
||||
_hasFailed = true;
|
||||
setFailed(true);
|
||||
CString msg;
|
||||
msg.Format(_T("Getting most recent builds has failed with error: %d"), error);
|
||||
addToLog(msg);
|
||||
|
@ -531,6 +590,7 @@ LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const
|
|||
CString contentTypeText = L"content-type:application/x-www-form-urlencoded";
|
||||
CString response;
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
true,
|
||||
L"metaverse.highfidelity.com",
|
||||
L"/oauth/token",
|
||||
contentTypeText, post,
|
||||
|
@ -611,7 +671,7 @@ BOOL LauncherManager::extractApplication() {
|
|||
onZipExtracted((ProcessType)type, size);
|
||||
} else {
|
||||
addToLog(_T("Error decompressing application zip file."));
|
||||
_hasFailed = true;
|
||||
setFailed(true);
|
||||
}
|
||||
};
|
||||
std::function<void(float)> onProgress = [&](float progress) {
|
||||
|
@ -688,7 +748,7 @@ BOOL LauncherManager::installContent() {
|
|||
}
|
||||
else {
|
||||
addToLog(_T("Error decompressing content zip file."));
|
||||
_hasFailed = true;
|
||||
setFailed(true);
|
||||
}
|
||||
};
|
||||
std::function<void(float)> onProgress = [&](float progress) {
|
||||
|
@ -728,7 +788,7 @@ BOOL LauncherManager::downloadFile(ProcessType type, const CString& url, CString
|
|||
} else {
|
||||
addToLog(_T("Error downloading application."));
|
||||
}
|
||||
_hasFailed = true;
|
||||
setFailed(true);
|
||||
}
|
||||
};
|
||||
std::function<void(float)> onProgress = [&, type](float progress) {
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
BOOL getAndCreatePaths(PathType type, CString& outPath);
|
||||
BOOL getInstalledVersion(const CString& path, CString& version);
|
||||
BOOL isApplicationInstalled(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn);
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag);
|
||||
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
|
||||
void getMostRecentBuilds(CString& launcherUrlOut,
|
||||
CString& launcherVersionOut,
|
||||
|
@ -84,7 +84,7 @@ public:
|
|||
CString& interfaceVersionOut);
|
||||
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
|
||||
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn);
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag);
|
||||
BOOL createConfigJSON();
|
||||
BOOL createApplicationRegistryKeys(int size);
|
||||
BOOL deleteApplicationRegistryKeys();
|
||||
|
@ -139,10 +139,12 @@ public:
|
|||
private:
|
||||
ProcessType _currentProcess { ProcessType::DownloadApplication };
|
||||
void onMostRecentBuildsReceived(const CString& response, LauncherUtils::ResponseError error);
|
||||
|
||||
CString _latestApplicationURL;
|
||||
CString _latestVersion;
|
||||
CString _latestLauncherURL;
|
||||
CString _latestLauncherVersion;
|
||||
|
||||
CString _contentURL;
|
||||
CString _domainURL;
|
||||
CString _version;
|
||||
|
@ -152,6 +154,8 @@ private:
|
|||
CString _contentZipPath;
|
||||
CString _launcherVersion;
|
||||
CString _tempLauncherPath;
|
||||
CString _currentVersion;
|
||||
bool _isInstalled{ false };
|
||||
bool _loggedIn { false };
|
||||
bool _hasFailed { false };
|
||||
BOOL _shouldUpdate { FALSE };
|
||||
|
@ -171,4 +175,7 @@ private:
|
|||
float _progressOffset { 0.0f };
|
||||
float _progress { 0.0f };
|
||||
CStdioFile _logFile;
|
||||
Json::Value _latestBuilds;
|
||||
CString _defaultBuildTag;
|
||||
CString _organizationBuildTag;
|
||||
};
|
||||
|
|
|
@ -181,7 +181,7 @@ BOOL LauncherUtils::deleteRegistryKey(const CString& registryPath) {
|
|||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherUtils::makeHTTPCall(const CString& callerName,
|
||||
const CString& mainUrl, const CString& dirUrl,
|
||||
bool useHTTPS, const CString& mainUrl, const CString& dirUrl,
|
||||
const CString& contentType, CStringA& postData,
|
||||
CString& response, bool isPost = false) {
|
||||
|
||||
|
@ -190,12 +190,12 @@ LauncherUtils::ResponseError LauncherUtils::makeHTTPCall(const CString& callerNa
|
|||
if (!hopen) {
|
||||
return ResponseError::Open;
|
||||
}
|
||||
HINTERNET hconnect = WinHttpConnect(hopen, mainUrl, INTERNET_DEFAULT_HTTPS_PORT, 0);
|
||||
HINTERNET hconnect = WinHttpConnect(hopen, mainUrl, useHTTPS ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0);
|
||||
if (!hconnect) {
|
||||
return ResponseError::Connect;
|
||||
}
|
||||
HINTERNET hrequest = WinHttpOpenRequest(hconnect, isPost ? L"POST" : L"GET", dirUrl,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, useHTTPS ? WINHTTP_FLAG_SECURE : 0);
|
||||
if (!hrequest) {
|
||||
return ResponseError::OpenRequest;
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ DWORD WINAPI LauncherUtils::deleteDirectoryThread(LPVOID lpParameter) {
|
|||
DWORD WINAPI LauncherUtils::httpThread(LPVOID lpParameter) {
|
||||
HttpThreadData& data = *((HttpThreadData*)lpParameter);
|
||||
CString response;
|
||||
auto error = LauncherUtils::makeHTTPCall(data._callerName, data._mainUrl, data._dirUrl,
|
||||
auto error = LauncherUtils::makeHTTPCall(data._callerName, data._useHTTPS, data._mainUrl, data._dirUrl,
|
||||
data._contentType, data._postData, response, data._isPost);
|
||||
data._callback(response, error);
|
||||
return 0;
|
||||
|
@ -552,12 +552,13 @@ BOOL LauncherUtils::deleteDirectoryOnThread(const CString& dirPath, std::functio
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LauncherUtils::httpCallOnThread(const CString& callerName, const CString& mainUrl, const CString& dirUrl,
|
||||
BOOL LauncherUtils::httpCallOnThread(const CString& callerName, bool useHTTPS, const CString& mainUrl, const CString& dirUrl,
|
||||
const CString& contentType, CStringA& postData, bool isPost,
|
||||
std::function<void(CString, int)> callback) {
|
||||
DWORD myThreadID;
|
||||
HttpThreadData* httpThreadData = new HttpThreadData();
|
||||
httpThreadData->_callerName = callerName;
|
||||
httpThreadData->_useHTTPS = useHTTPS;
|
||||
httpThreadData->_mainUrl = mainUrl;
|
||||
httpThreadData->_dirUrl = dirUrl;
|
||||
httpThreadData->_contentType = contentType;
|
||||
|
|
|
@ -120,6 +120,7 @@ public:
|
|||
|
||||
struct HttpThreadData {
|
||||
CString _callerName;
|
||||
bool _useHTTPS;
|
||||
CString _mainUrl;
|
||||
CString _dirUrl;
|
||||
CString _contentType;
|
||||
|
@ -137,7 +138,7 @@ public:
|
|||
};
|
||||
|
||||
static BOOL parseJSON(const CString& jsonTxt, Json::Value& jsonObject);
|
||||
static ResponseError makeHTTPCall(const CString& callerName, const CString& mainUrl,
|
||||
static ResponseError makeHTTPCall(const CString& callerName, bool useHTTPS, const CString& mainUrl,
|
||||
const CString& dirUrl, const CString& contentType,
|
||||
CStringA& postData, CString& response, bool isPost);
|
||||
static std::string cStringToStd(CString cstring);
|
||||
|
@ -163,7 +164,7 @@ public:
|
|||
std::function<void(int, bool)> callback,
|
||||
std::function<void(float)> progressCallback);
|
||||
static BOOL deleteDirectoryOnThread(const CString& dirPath, std::function<void(bool)> callback);
|
||||
static BOOL httpCallOnThread(const CString& callerName, const CString& mainUrl, const CString& dirUrl,
|
||||
static BOOL httpCallOnThread(const CString& callerName, bool useHTTPS, const CString& mainUrl, const CString& dirUrl,
|
||||
const CString& contentType, CStringA& postData, bool isPost,
|
||||
std::function<void(CString, int)> callback);
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ protected:
|
|||
virtual void doRender(RenderArgs* args) = 0;
|
||||
|
||||
virtual bool isFading() const { return _isFading; }
|
||||
void updateModelTransformAndBound();
|
||||
virtual void updateModelTransformAndBound();
|
||||
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
|
||||
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
|
||||
|
||||
|
@ -124,7 +124,6 @@ signals:
|
|||
protected:
|
||||
template<typename T>
|
||||
std::shared_ptr<T> asTypedEntity() { return std::static_pointer_cast<T>(_entity); }
|
||||
|
||||
|
||||
static void makeStatusGetters(const EntityItemPointer& entity, Item::Status::Getters& statusGetters);
|
||||
const Transform& getModelTransform() const;
|
||||
|
@ -153,7 +152,6 @@ protected:
|
|||
|
||||
quint64 _created;
|
||||
|
||||
private:
|
||||
// The base class relies on comparing the model transform to the entity transform in order
|
||||
// to trigger an update, so the member must not be visible to derived classes as a modifiable
|
||||
// transform
|
||||
|
|
|
@ -41,23 +41,55 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
|
|||
}
|
||||
}
|
||||
|
||||
void PolyLineEntityRenderer::buildPipelines() {
|
||||
// FIXME: opaque pipelines
|
||||
void PolyLineEntityRenderer::updateModelTransformAndBound() {
|
||||
bool success = false;
|
||||
auto newModelTransform = _entity->getTransformToCenter(success);
|
||||
if (success) {
|
||||
_modelTransform = newModelTransform;
|
||||
|
||||
auto lineEntity = std::static_pointer_cast<PolyLineEntityItem>(_entity);
|
||||
AABox bound;
|
||||
lineEntity->computeTightLocalBoundingBox(bound);
|
||||
bound.transform(newModelTransform);
|
||||
_bound = bound;
|
||||
}
|
||||
}
|
||||
|
||||
bool PolyLineEntityRenderer::isTransparent() const {
|
||||
return _glow || (_textureLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha());
|
||||
}
|
||||
|
||||
void PolyLineEntityRenderer::buildPipelines() {
|
||||
static const std::vector<std::pair<render::Args::RenderMethod, bool>> keys = {
|
||||
{ render::Args::DEFERRED, false }, { render::Args::DEFERRED, true }, { render::Args::FORWARD, false }, { render::Args::FORWARD, true },
|
||||
};
|
||||
|
||||
for (auto& key : keys) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(key.first == render::Args::DEFERRED ? shader::entities_renderer::program::paintStroke : shader::entities_renderer::program::paintStroke_forward);
|
||||
gpu::ShaderPointer program;
|
||||
render::Args::RenderMethod renderMethod = key.first;
|
||||
bool transparent = key.second;
|
||||
|
||||
if (renderMethod == render::Args::DEFERRED) {
|
||||
if (transparent) {
|
||||
program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke_translucent);
|
||||
} else {
|
||||
program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke);
|
||||
}
|
||||
} else { // render::Args::FORWARD
|
||||
program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke_forward);
|
||||
}
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
state->setCullMode(gpu::State::CullMode::CULL_NONE);
|
||||
state->setDepthTest(true, !key.second, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setDepthTest(true, !transparent, gpu::LESS_EQUAL);
|
||||
if (transparent) {
|
||||
PrepareStencil::testMask(*state);
|
||||
} else {
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
}
|
||||
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
state->setBlendFunction(transparent, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
_pipelines[key] = gpu::Pipeline::create(program, state);
|
||||
|
@ -65,11 +97,16 @@ void PolyLineEntityRenderer::buildPipelines() {
|
|||
}
|
||||
|
||||
ItemKey PolyLineEntityRenderer::getKey() {
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
return isTransparent() ?
|
||||
ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()) :
|
||||
ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
}
|
||||
|
||||
ShapeKey PolyLineEntityRenderer::getShapeKey() {
|
||||
auto builder = ShapeKey::Builder().withOwnPipeline().withTranslucent().withoutCullFace();
|
||||
auto builder = ShapeKey::Builder().withOwnPipeline().withoutCullFace();
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
|
@ -294,7 +331,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
|
|||
buildPipelines();
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipelines[{args->_renderMethod, _glow}]);
|
||||
batch.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]);
|
||||
batch.setModelTransform(transform);
|
||||
batch.setResourceTexture(0, texture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0);
|
||||
|
|
|
@ -25,8 +25,9 @@ class PolyLineEntityRenderer : public TypedEntityRenderer<PolyLineEntityItem> {
|
|||
public:
|
||||
PolyLineEntityRenderer(const EntityItemPointer& entity);
|
||||
|
||||
// FIXME: shouldn't always be transparent: take into account texture and glow
|
||||
virtual bool isTransparent() const override { return true; }
|
||||
void updateModelTransformAndBound() override;
|
||||
|
||||
virtual bool isTransparent() const override;
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
|
|
|
@ -1 +1 @@
|
|||
DEFINES forward
|
||||
DEFINES translucent:f forward
|
||||
|
|
|
@ -34,7 +34,11 @@ void main(void) {
|
|||
texel.a *= mix(1.0, pow(1.0 - min(1.0, abs(_distanceFromCenter)), 10.0), _polylineData.faceCameraGlow.y);
|
||||
|
||||
<@if not HIFI_USE_FORWARD@>
|
||||
packDeferredFragmentTranslucent((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb, DEFAULT_ROUGHNESS);
|
||||
<@if HIFI_USE_TRANSLUCENT@>
|
||||
packDeferredFragmentTranslucent((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb, DEFAULT_ROUGHNESS);
|
||||
<@else@>
|
||||
packDeferredFragmentUnlit((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb);
|
||||
<@endif@>
|
||||
<@else@>
|
||||
_fragColor0 = texel;
|
||||
<@endif@>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityItemProperties.h"
|
||||
|
@ -85,7 +86,7 @@ void PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
|
|||
_points = points;
|
||||
_pointsChanged = true;
|
||||
});
|
||||
computeAndUpdateDimensionsAndPosition();
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
|
||||
|
@ -93,7 +94,7 @@ void PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
|
|||
_widths = strokeWidths;
|
||||
_widthsChanged = true;
|
||||
});
|
||||
computeAndUpdateDimensionsAndPosition();
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
|
||||
|
@ -110,7 +111,7 @@ void PolyLineEntityItem::setStrokeColors(const QVector<glm::vec3>& strokeColors)
|
|||
});
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::computeAndUpdateDimensionsAndPosition() {
|
||||
void PolyLineEntityItem::computeAndUpdateDimensions() {
|
||||
QVector<glm::vec3> points;
|
||||
QVector<float> widths;
|
||||
|
||||
|
@ -129,6 +130,32 @@ void PolyLineEntityItem::computeAndUpdateDimensionsAndPosition() {
|
|||
setScaledDimensions(2.0f * (maxHalfDim + maxWidth));
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::computeTightLocalBoundingBox(AABox& localBox) const {
|
||||
QVector<glm::vec3> points;
|
||||
QVector<float> widths;
|
||||
withReadLock([&] {
|
||||
points = _points;
|
||||
widths = _widths;
|
||||
});
|
||||
|
||||
if (points.size() > 0) {
|
||||
Extents extents;
|
||||
float maxWidth = DEFAULT_LINE_WIDTH;
|
||||
for (int i = 0; i < points.length(); i++) {
|
||||
extents.addPoint(points[i]);
|
||||
if (i < widths.size()) {
|
||||
maxWidth = glm::max(maxWidth, widths[i]);
|
||||
}
|
||||
}
|
||||
extents.addPoint(extents.minimum - maxWidth * Vectors::ONE);
|
||||
extents.addPoint(extents.maximum + maxWidth * Vectors::ONE);
|
||||
|
||||
localBox.setBox(extents.minimum, extents.maximum - extents.minimum);
|
||||
} else {
|
||||
localBox.setBox(glm::vec3(-0.5f * DEFAULT_LINE_WIDTH), glm::vec3(DEFAULT_LINE_WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
|
@ -267,4 +294,4 @@ void PolyLineEntityItem::setFaceCamera(bool faceCamera) {
|
|||
_needsRenderUpdate = _faceCamera != faceCamera;
|
||||
_faceCamera = faceCamera;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "EntityItem.h"
|
||||
|
||||
class PolyLineEntityItem : public EntityItem {
|
||||
public:
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
PolyLineEntityItem(const EntityItemID& entityItemID);
|
||||
|
@ -90,10 +90,12 @@ class PolyLineEntityItem : public EntityItem {
|
|||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
|
||||
|
||||
void computeTightLocalBoundingBox(AABox& box) const;
|
||||
|
||||
virtual void debugDump() const override;
|
||||
private:
|
||||
void computeAndUpdateDimensionsAndPosition();
|
||||
|
||||
void computeAndUpdateDimensions();
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
QVector<glm::vec3> _points;
|
||||
|
|
|
@ -60,74 +60,108 @@ void PrepareJointsTask::configure(const Config& config) {
|
|||
_passthrough = config.passthrough;
|
||||
}
|
||||
|
||||
|
||||
void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& jointsIn = input.get0();
|
||||
auto& jointsOut = output.edit0();
|
||||
|
||||
std::vector<QString> jointNames;
|
||||
if (_passthrough) {
|
||||
jointsOut = jointsIn;
|
||||
} else {
|
||||
const auto& mapping = input.get1();
|
||||
auto& jointRotationOffsets = output.edit1();
|
||||
auto& jointIndices = output.edit2();
|
||||
|
||||
bool newJointRot = false;
|
||||
static const QString JOINT_ROTATION_OFFSET2_FIELD = "jointRotationOffset2";
|
||||
QVariantHash fstHashMap = mapping;
|
||||
if (fstHashMap.contains(JOINT_ROTATION_OFFSET2_FIELD)) {
|
||||
newJointRot = true;
|
||||
} else {
|
||||
newJointRot = false;
|
||||
}
|
||||
bool newJointRot = fstHashMap.contains(JOINT_ROTATION_OFFSET2_FIELD);
|
||||
|
||||
// Get joint renames
|
||||
auto jointNameMapping = getJointNameMapping(mapping);
|
||||
QMap<QString, QString> jointNameMapping = getJointNameMapping(mapping);
|
||||
// Apply joint metadata from FST file mappings
|
||||
for (const auto& jointIn : jointsIn) {
|
||||
for (const hfm::Joint& jointIn : jointsIn) {
|
||||
jointsOut.push_back(jointIn);
|
||||
auto& jointOut = jointsOut.back();
|
||||
hfm::Joint& jointOut = jointsOut.back();
|
||||
|
||||
if (!newJointRot) {
|
||||
auto jointNameMapKey = jointNameMapping.key(jointIn.name);
|
||||
// Offset rotations are referenced by the new joint's names
|
||||
// We need to assign new joint's names now, before the offsets are read.
|
||||
QString jointNameMapKey = jointNameMapping.key(jointIn.name);
|
||||
if (jointNameMapping.contains(jointNameMapKey)) {
|
||||
jointOut.name = jointNameMapKey;
|
||||
}
|
||||
}
|
||||
jointIndices.insert(jointOut.name, (int)jointsOut.size());
|
||||
jointNames.push_back(jointOut.name);
|
||||
}
|
||||
|
||||
// Get joint rotation offsets from FST file mappings
|
||||
auto offsets = getJointRotationOffsets(mapping);
|
||||
QMap<QString, glm::quat> offsets = getJointRotationOffsets(mapping);
|
||||
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
|
||||
QString jointName = itr.key();
|
||||
int jointIndex = jointIndices.value(jointName) - 1;
|
||||
if (jointIndex >= 0) {
|
||||
glm::quat rotationOffset = itr.value();
|
||||
jointRotationOffsets.insert(jointIndex, rotationOffset);
|
||||
qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
|
||||
auto jointToOffset = std::find_if(jointsOut.begin(), jointsOut.end(), [jointName](const hfm::Joint& joint) {
|
||||
return (joint.name == jointName && joint.isSkeletonJoint);
|
||||
});
|
||||
if (jointToOffset != jointsOut.end()) {
|
||||
// In case there are named duplicates we'll assign the offset rotation to the first skeleton joint that matches the name.
|
||||
int jointIndex = (int)distance(jointsOut.begin(), jointToOffset);
|
||||
if (jointIndex >= 0) {
|
||||
glm::quat rotationOffset = itr.value();
|
||||
jointRotationOffsets.insert(jointIndex, rotationOffset);
|
||||
qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newJointRot) {
|
||||
for (auto& jointOut : jointsOut) {
|
||||
auto jointNameMapKey = jointNameMapping.key(jointOut.name);
|
||||
int mappedIndex = jointIndices.value(jointOut.name);
|
||||
// Offset rotations are referenced using the original joint's names
|
||||
// We need to apply new names now, once we have read the offsets
|
||||
for (size_t i = 0; i < jointsOut.size(); i++) {
|
||||
hfm::Joint& jointOut = jointsOut[i];
|
||||
QString jointNameMapKey = jointNameMapping.key(jointOut.name);
|
||||
if (jointNameMapping.contains(jointNameMapKey)) {
|
||||
// delete and replace with hifi name
|
||||
jointIndices.remove(jointOut.name);
|
||||
jointOut.name = jointNameMapKey;
|
||||
jointIndices.insert(jointOut.name, mappedIndex);
|
||||
} else {
|
||||
// nothing mapped to this fbx joint name
|
||||
if (jointNameMapping.contains(jointOut.name)) {
|
||||
// but the name is in the list of hifi names is mapped to a different joint
|
||||
int extraIndex = jointIndices.value(jointOut.name);
|
||||
jointIndices.remove(jointOut.name);
|
||||
jointOut.name = "";
|
||||
jointIndices.insert(jointOut.name, extraIndex);
|
||||
}
|
||||
jointNames[i] = jointNameMapKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QString APPEND_DUPLICATE_JOINT = "_joint";
|
||||
const QString APPEND_DUPLICATE_MESH = "_mesh";
|
||||
|
||||
// resolve duplicates and set jointIndices
|
||||
auto& jointIndices = output.edit2();
|
||||
for (size_t i = 0; i < jointsOut.size(); i++) {
|
||||
hfm::Joint& jointOut = jointsOut[i];
|
||||
if (jointIndices.contains(jointOut.name)) {
|
||||
int duplicatedJointIndex = jointIndices[jointOut.name] - 1;
|
||||
if (duplicatedJointIndex >= 0) {
|
||||
auto& duplicatedJoint = jointsOut[duplicatedJointIndex];
|
||||
bool areBothJoints = jointOut.isSkeletonJoint && duplicatedJoint.isSkeletonJoint;
|
||||
QString existJointName = jointOut.name;
|
||||
QString appendName;
|
||||
if (areBothJoints) {
|
||||
appendName = APPEND_DUPLICATE_JOINT;
|
||||
qCWarning(model_baker) << "Duplicated skeleton joints found: " << existJointName;
|
||||
} else {
|
||||
appendName = APPEND_DUPLICATE_MESH;
|
||||
qCDebug(model_baker) << "Duplicated joints found. Renaming the mesh joint: " << existJointName;
|
||||
}
|
||||
QString newName = existJointName + appendName;
|
||||
// Make sure the new name is unique
|
||||
int duplicateIndex = 0;
|
||||
while (jointIndices.contains(newName)) {
|
||||
newName = existJointName + appendName + QString::number(++duplicateIndex);
|
||||
}
|
||||
// Find and rename the mesh joint
|
||||
if (!jointOut.isSkeletonJoint) {
|
||||
jointOut.name = newName;
|
||||
} else {
|
||||
jointIndices.remove(jointOut.name);
|
||||
jointIndices.insert(newName, duplicatedJointIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
jointIndices.insert(jointOut.name, (int)(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,8 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float r
|
|||
}
|
||||
|
||||
void packDeferredFragmentUnlit(vec3 normal, float alpha, vec3 color) {
|
||||
if (alpha < 1.0) {
|
||||
// to reduce texel flickering for floating point error we discard when alpha is "almost one"
|
||||
if (alpha < 0.999999) {
|
||||
discard;
|
||||
}
|
||||
_fragColor0 = vec4(color, packUnlit());
|
||||
|
|
|
@ -40,9 +40,9 @@ body
|
|||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
#main p {
|
||||
line-height: 24px;
|
||||
margin-bottom: 24px;
|
||||
p {
|
||||
font-size: 0.95rem;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
section
|
||||
|
@ -118,30 +118,17 @@ h5, .container-overview .subsection-title
|
|||
**************************** Table styles **************************
|
||||
********************************************************************/
|
||||
|
||||
table
|
||||
{
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: solid #d8e1d9 1px;
|
||||
text-align: left;
|
||||
overflow: auto;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
border: solid #d8e1d9 1px;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
table > thead {
|
||||
thead {
|
||||
border-color: #d8e1d9;
|
||||
background: #d8e1d9;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 0.5rem;
|
||||
border-left: 1px solid #d8e1d9;
|
||||
font-size: .95em;
|
||||
background:#d8e1d9;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table tr {
|
||||
|
@ -152,6 +139,23 @@ table tr:nth-child(even) {
|
|||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
td {
|
||||
border: solid #c7cccb 1px;
|
||||
}
|
||||
|
||||
article table thead tr th, article table tbody tr td, article table tbody tr td p {
|
||||
font-size: .89rem;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
article table thead tr th, article table tbody tr td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
article table tbody tr td ul li {
|
||||
font-size: .89rem;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
****************************** Link styles *************************
|
||||
********************************************************************/
|
||||
|
@ -169,21 +173,15 @@ a, a:hover, a:active, a:visited {
|
|||
********************************************************************/
|
||||
|
||||
article ul {
|
||||
margin-bottom: 1.7em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
article li {
|
||||
font-size: .95rem;
|
||||
line-height: 20px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.readme ul {
|
||||
font-size: 0.95rem;
|
||||
line-height: 24px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
********************** Navigation sidebar styles *******************
|
||||
********************************************************************/
|
||||
|
@ -388,20 +386,16 @@ tt, code, kbd, samp {
|
|||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
p, ul, ol, blockquote {
|
||||
ul, ol, blockquote {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.class-description {
|
||||
font-size: 130%;
|
||||
line-height: 140%;
|
||||
|
@ -423,7 +417,6 @@ header {
|
|||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
|
||||
.apiLinks
|
||||
{
|
||||
display: block;
|
||||
|
@ -529,7 +522,6 @@ header {
|
|||
|
||||
.code-caption {
|
||||
font-style: italic;
|
||||
font-size: 107%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -696,51 +688,6 @@ span.param-type, .params td .param-type, .param-type dd {
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
**************************** Mobile styles **************************
|
||||
*********************************************************************/
|
||||
|
||||
@media only screen and (min-width: 320px) and (max-width: 680px) {
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
nav {
|
||||
background: #FFF;
|
||||
width: 250px;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -250px;
|
||||
z-index: 3;
|
||||
padding: 0 10px;
|
||||
transition: left 0.2s;
|
||||
}
|
||||
|
||||
.navicon-button {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
top: 1.5em;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#main {
|
||||
width: 100%;
|
||||
min-width: 360px;
|
||||
}
|
||||
|
||||
#main section {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a '#' to static members */
|
||||
[data-type="member"] a::before {
|
||||
content: '#';
|
||||
|
|
Loading…
Reference in a new issue