diff --git a/launchers/qt/CMakeLists.txt b/launchers/qt/CMakeLists.txt index 9bd4436073..9896e3f5d9 100644 --- a/launchers/qt/CMakeLists.txt +++ b/launchers/qt/CMakeLists.txt @@ -130,10 +130,19 @@ set(src_files src/PathUtils.h src/Unzipper.h src/Unzipper.cpp + src/Helper.h deps/miniz/miniz.h deps/miniz/miniz.cpp ) + +if (APPLE) + set(src_files ${src_files} + src/darwin/Helper.mm + src/darwin/NSTask+NSTaskExecveAdditions.h + src/darwin/NSTask+NSTaskExecveAdditions.m + ) +endif() set(TARGET_NAME ${PROJECT_NAME}) diff --git a/launchers/qt/resources/images/hidePass.png b/launchers/qt/resources/images/hidePass.png new file mode 100644 index 0000000000..92500ae3f1 Binary files /dev/null and b/launchers/qt/resources/images/hidePass.png differ diff --git a/launchers/qt/resources/images/showPass.png b/launchers/qt/resources/images/showPass.png new file mode 100644 index 0000000000..8f1c0b14f4 Binary files /dev/null and b/launchers/qt/resources/images/showPass.png differ diff --git a/launchers/qt/resources/qml/Download.qml b/launchers/qt/resources/qml/Download.qml index 7393e0129e..a6548ac3b3 100644 --- a/launchers/qt/resources/qml/Download.qml +++ b/launchers/qt/resources/qml/Download.qml @@ -11,7 +11,7 @@ Item { width: parent.width height: parent.height mirror: true - source: "qrc:/images/hifi_window@2x.png" + source: PathUtils.resourcePath("images/hifi_window@2x.png"); transformOrigin: Item.Center rotation: 180 } @@ -20,7 +20,7 @@ Item { id: logo width: 150 height: 150 - source: "../images/HiFi_Voxel.png" + source: PathUtils.resourcePath("images/HiFi_Voxel.png"); anchors { top: root.top diff --git a/launchers/qt/resources/qml/HFBase/CreateAccountBase.qml b/launchers/qt/resources/qml/HFBase/CreateAccountBase.qml index d330a3d8e7..c18d14b314 100644 --- a/launchers/qt/resources/qml/HFBase/CreateAccountBase.qml +++ b/launchers/qt/resources/qml/HFBase/CreateAccountBase.qml @@ -6,7 +6,7 @@ import "../HFControls" Item { id: root anchors.centerIn: parent - property string titleText: "Create your account" + property string titleText: "Sign-in and pick a password" property string usernamePlaceholder: "User name" property string passwordPlaceholder: "Set a password" property int marginLeft: root.width * 0.15 @@ -16,18 +16,20 @@ Item { width: parent.width height: parent.height mirror: true - source: "qrc:/images/hifi_window@2x.png" + source: PathUtils.resourcePath("images/hifi_window@2x.png"); transformOrigin: Item.Center rotation: 180 } Text { id: title - width: 325 - height: 26 + width: 481 + height: 27 font.family: "Graphik" font.bold: true - font.pixelSize: 24 + font.pixelSize: 29 + lineHeight: 35 + lineHeightMode: Text.FixedHeight color: "#FFFFFF" text: root.titleText anchors { @@ -43,117 +45,165 @@ Item { width: 425 height: 22 font.family: "Graphik" - font.pixelSize: 10 + font.pixelSize: 14 color: "#C4C4C4" text: "Use the email address that you regisetered with." anchors { left: root.left leftMargin: root.marginLeft top: title.bottom - topMargin: 9 + topMargin: 18 } } HFTextField { id: organization - width: 353 - height: 40 + width: 430 + height: 50 font.family: "Graphik" font.pixelSize: 18 - placeholderText: "Organization name" - color: "#808080" + placeholderText: "Email Address" + color: "#7e8c81" seperatorColor: Qt.rgba(1, 1, 1, 0.3) anchors { top: instruction.bottom left: root.left leftMargin: root.marginLeft - topMargin: 4 + topMargin: 18 } } HFTextField { id: username - width: 353 - height: 40 + width: 430 + height: 50 font.family: "Graphik" font.pixelSize: 18 placeholderText: root.usernamePlaceholder - color: "#808080" + color: "#7e8c81" seperatorColor: Qt.rgba(1, 1, 1, 0.3) anchors { top: organization.bottom left: root.left leftMargin: root.marginLeft - topMargin: 10 + topMargin: 18 } } HFTextField { id: passwordField - width: 353 - height: 40 + width: 430 + height: 50 font.family: "Graphik" font.pixelSize: 18 placeholderText: root.passwordPlaceholder - color: "#808080" + color: "#7e8c81" seperatorColor: Qt.rgba(1, 1, 1, 0.3) + togglePasswordField: true echoMode: TextInput.Password anchors { top: username.bottom left: root.left leftMargin: root.marginLeft - topMargin: 10 + topMargin: 18 } } Text { id: displayNameText - text: "This is the display name other people see in world. It can be changed at \nanytime, from your profile" + text: "You can change this at anytime from you Profile" font.family: "Graphik" font.pixelSize: 10 - color: "#FFFFFF" + color: "#C4C4C4" anchors { top: passwordField.bottom left: root.left leftMargin: root.marginLeft - topMargin: 15 + topMargin: 22 } } HFTextField { id: displayName - width: 353 - height: 40 + width: 430 + height: 50 font.family: "Graphik" font.pixelSize: 18 - placeholderText: "Password" - color: "#C4C4C4" + placeholderText: "Display Name" + color: "#7e8c81" seperatorColor: Qt.rgba(1, 1, 1, 0.3) anchors { top: displayNameText.bottom left: root.left leftMargin: root.marginLeft - topMargin: 8 + topMargin: 4 } } HFButton { id: button - width: 122 - height: 36 + width: 134 + height: 50 font.family: "Graphik" - font.pixelSize: 18 + font.pixelSize: 14 text: "NEXT" anchors { top: displayName.bottom left: root.left leftMargin: root.marginLeft - topMargin: 15 + topMargin: 21 } onClicked: LauncherState.login(username.text, passwordField.text) } + + + Text { + width: 214 + height: 12 + + text: "Already have an account?" + font.family: "Graphik" + font.pixelSize: 14 + color: "#009EE0" + + anchors { + top: button.bottom + topMargin: 19 + left: button.left + } + + MouseArea { + anchors.fill: parent + + onClicked: { + console.log("clicked"); + root.parent.source = PathUtils.resourcePath("qml/Login.qml"); + } + } + } + + Text { + width: 100 + height: 17 + + text: "High Fidelity" + font.bold: true + font.family: "Graphik" + font.pixelSize: 24 + font.letterSpacing: -1 + color: "#FFFFFF" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + anchors { + bottom: root.bottom + bottomMargin: 46 + right: displayName.right + rightMargin: 30 + } + } } diff --git a/launchers/qt/resources/qml/HFBase/LoginBase.qml b/launchers/qt/resources/qml/HFBase/LoginBase.qml new file mode 100644 index 0000000000..9d468fc5c8 --- /dev/null +++ b/launchers/qt/resources/qml/HFBase/LoginBase.qml @@ -0,0 +1,183 @@ +import QtQuick 2.3 +import QtQuick 2.1 +import "../HFControls" + +Item { + id: root + anchors.fill: parent + + Image { + anchors.centerIn: parent + width: parent.width + height: parent.height + mirror: false + source: PathUtils.resourcePath("images/hifi_window@2x.png"); + transformOrigin: Item.Center + rotation: 0 + } + Text { + id: title + width: 325 + height: 26 + font.family: "Graphik" + font.pixelSize: 28 + font.bold: true + color: "#FFFFFF" + text: "Please Log in" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors { + top: root.top + topMargin: 40 + horizontalCenter: root.horizontalCenter + } + } + + Text { + id: instruction + width: 425 + height: 22 + font.family: "Graphik" + font.pixelSize: 14 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "#C4C4C4" + text: "Use the account credentials you created at sign-up" + anchors { + left: root.left + right: root.right + top: title.bottom + topMargin: 18 + } + } + + HFTextField { + id: username + width: 353 + height: 50 + font.family: "Graphik" + font.pixelSize: 18 + placeholderText: "Username" + color: "#7E8C81" + seperatorColor: Qt.rgba(1, 1, 1, 0.3) + anchors { + top: instruction.bottom + horizontalCenter: instruction.horizontalCenter + topMargin: 24 + } + } + + HFTextField { + id: password + width: 353 + height: 50 + font.family: "Graphik" + font.pixelSize: 18 + placeholderText: "Password" + color: "#7E8C81" + togglePasswordField: true + echoMode: TextInput.Password + seperatorColor: Qt.rgba(1, 1, 1, 0.3) + anchors { + top: username.bottom + horizontalCenter: instruction.horizontalCenter + topMargin: 25 + } + } + + + Text { + id: displayText + + text: "You can change this at anytime from your profile" + color: "#C4C4C4" + font.pixelSize: 14 + + anchors { + top: password.bottom + topMargin: 50 + left: password.left + } + } + + HFTextField { + id: displayName + width: 353 + height: 50 + font.family: "Graphik" + font.pixelSize: 18 + placeholderText: "Display name" + color: "#7E8C81" + seperatorColor: Qt.rgba(1, 1, 1, 0.3) + echoMode: TextInput.Password + anchors { + top: displayText.bottom + horizontalCenter: instruction.horizontalCenter + topMargin: 4 + } + } + + HFButton { + id: button + width: 110 + height: 50 + + font.family: "Graphik" + font.pixelSize: 18 + text: "NEXT" + + anchors { + top: displayName.bottom + left: displayName.left + topMargin: 25 + } + + onClicked: LauncherState.login(username.text, password.text) + } + + Text { + width: 214 + height: 12 + + text: "Create New Account" + font.family: "Graphik" + font.pixelSize: 14 + color: "#009EE0" + + anchors { + top: button.bottom + topMargin: 19 + left: button.left + } + + MouseArea { + anchors.fill: parent + + onClicked: { + console.log("clicked"); + root.parent.source = PathUtils.resourcePath("qml/Login.qml"); + } + } + } + + Text { + width: 100 + height: 17 + + text: "High Fidelity" + font.bold: true + font.family: "Graphik" + font.pixelSize: 18 + font.letterSpacing: -1 + color: "#FFFFFF" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + anchors { + bottom: root.bottom + bottomMargin: 58 + right: root.right + rightMargin: 136 + } + } +} diff --git a/launchers/qt/resources/qml/HFControls/HFTextField.qml b/launchers/qt/resources/qml/HFControls/HFTextField.qml index 41d7dbc968..77777a37d3 100644 --- a/launchers/qt/resources/qml/HFControls/HFTextField.qml +++ b/launchers/qt/resources/qml/HFControls/HFTextField.qml @@ -6,6 +6,7 @@ TextField { //color: "#000000" font.family: "Graphik Medium" font.pixelSize: 22 + property bool togglePasswordField: false verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignLeft placeholderText: "PlaceHolder" @@ -14,8 +15,37 @@ TextField { background: Item { anchors.fill: parent Rectangle { + id: background color: "#FFFFFF" anchors.fill: parent + + Image { + id: hide + visible: control.togglePasswordField + source: (control.echoMode == TextInput.Password) ? PathUtils.resourcePath("images/showPass.png") : + PathUtils.resourcePath("images/hidePass.png"); + width: 21 + height: 14 + anchors { + top: parent.top + topMargin: 18 + bottom: parent.bottom + bottomMargin: 18 + right: parent.right + rightMargin: 13 + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (control.echoMode === TextInput.Password) { + control.echoMode = TextInput.Normal; + } else { + control.echoMode = TextInput.Password; + } + } + } + } } } } diff --git a/launchers/qt/resources/qml/Login.qml b/launchers/qt/resources/qml/Login.qml index c6cb949cca..6ab50a8c33 100644 --- a/launchers/qt/resources/qml/Login.qml +++ b/launchers/qt/resources/qml/Login.qml @@ -1,111 +1,7 @@ // login -import QtQuick 2.3 -import QtQuick 2.1 -import "HFControls" +import "HFBase" -Item { - id: root +LoginBase { anchors.fill: parent - Text { - id: title - width: 325 - height: 26 - font.family: "Graphik" - font.pixelSize: 28 - color: "#FFFFFF" - text: "Please Log in" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors { - top: root.top - topMargin: 29 - horizontalCenter: root.horizontalCenter - } - } - - Text { - id: instruction - width: 425 - height: 22 - font.family: "Graphik" - font.pixelSize: 14 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: "#C4C4C4" - text: "Be sure you've uploaded your Avatar before signing in." - anchors { - left: root.left - right: root.right - top: title.bottom - topMargin: 18 - } - } - - HFTextField { - id: organization - width: 353 - height: 40 - font.family: "Graphik" - font.pixelSize: 18 - placeholderText: "Organization name" - color: "#808080" - seperatorColor: Qt.rgba(1, 1, 1, 0.3) - anchors { - top: instruction.bottom - horizontalCenter: instruction.horizontalCenter - topMargin: 24 - } - } - - HFTextField { - id: username - width: 353 - height: 40 - font.family: "Graphik" - font.pixelSize: 18 - placeholderText: "Username" - color: "#808080" - seperatorColor: Qt.rgba(1, 1, 1, 0.3) - anchors { - top: organization.bottom - horizontalCenter: instruction.horizontalCenter - topMargin: 28 - } - } - - HFTextField { - id: password - width: 353 - height: 40 - font.family: "Graphik" - font.pixelSize: 18 - placeholderText: "Password" - color: "#808080" - seperatorColor: Qt.rgba(1, 1, 1, 0.3) - echoMode: TextInput.Password - anchors { - top: username.bottom - horizontalCenter: instruction.horizontalCenter - topMargin: 28 - } - } - - HFButton { - id: button - width: 122 - height: 36 - - font.family: "Graphik" - font.pixelSize: 18 - text: "NEXT" - - anchors { - top: password.bottom - horizontalCenter: instruction.horizontalCenter - topMargin: 48 - } - - onClicked: LauncherState.login(username.text, password.text) - } } diff --git a/launchers/qt/resources/qml/root.qml b/launchers/qt/resources/qml/root.qml index bcba440afb..bfd704fa50 100644 --- a/launchers/qt/resources/qml/root.qml +++ b/launchers/qt/resources/qml/root.qml @@ -7,10 +7,8 @@ import "HFControls" Item { id: root - width: 515 - height: 390 - //source: "../images/hifi_window@2x.png" - + width: 627 + height: 540 Loader { anchors.fill: parent id: loader @@ -23,7 +21,13 @@ Item { }); } + + function loadPage(url) { + loader.source = url; + } + Text { + id: stateInfo font.pixelSize: 12 anchors.right: root.right diff --git a/launchers/qt/src/Helper.cpp b/launchers/qt/src/Helper.cpp new file mode 100644 index 0000000000..8bbab41698 --- /dev/null +++ b/launchers/qt/src/Helper.cpp @@ -0,0 +1,5 @@ +#include "Helper.h" + +void launchClient(const QString& homePath, const QString& defaultScriptOverride, const QString& displayName, + const QString& contentCachePath, const QString& loginResponseToken) { +} diff --git a/launchers/qt/src/Helper.h b/launchers/qt/src/Helper.h new file mode 100644 index 0000000000..ee145e56f3 --- /dev/null +++ b/launchers/qt/src/Helper.h @@ -0,0 +1,4 @@ +#include + +void launchClient(const QString& clientPath, const QString& homePath, const QString& defaultScriptOverride, + const QString& displayName, const QString& contentCachePath, const QString& loginResponseToken = QString()); diff --git a/launchers/qt/src/LauncherState.cpp b/launchers/qt/src/LauncherState.cpp index 9e1587e755..2111568780 100644 --- a/launchers/qt/src/LauncherState.cpp +++ b/launchers/qt/src/LauncherState.cpp @@ -2,6 +2,7 @@ #include "PathUtils.h" #include "Unzipper.h" +#include "Helper.h" #ifdef Q_OS_WIN #include @@ -415,14 +416,15 @@ void LauncherState::launchClient() { setApplicationState(ApplicationState::LaunchingHighFidelity); QDir installDirectory = _launcherDirectory.filePath("interface_install"); - auto clientPath = installDirectory.absoluteFilePath("interface.exe"); + auto clientPath = installDirectory.absoluteFilePath("interface.app/Contents/MacOS/interface"); QString homePath = "hifi://hq"; QString defaultScriptsPath = installDirectory.filePath("scripts/simplifiedUIBootstrapper"); QString displayName = "fixMe"; QString contentCachePath = _launcherDirectory.filePath("cache"); - // TODO Fix parameters + //::launchClient(clientPath, homePath, defaultScriptsPath, displayName, contentCachePath, _loginTokenResponse); + /* // TODO Fix parameters QString params = "--url " + homePath + " --setBookmark hqhome=\"" + homePath + "\"" + " --defaultScriptsOverride " + QDir::toNativeSeparators(defaultScriptsPath) @@ -463,7 +465,7 @@ void LauncherState::launchClient() { // TODO Implement launching of client #else #error UNSUPPORTED PLATFORM -#endif +#endif*/ } void LauncherState::setApplicationState(ApplicationState state) { diff --git a/launchers/qt/src/LauncherWindow.h b/launchers/qt/src/LauncherWindow.h index 376ab9ae7b..02012dc342 100644 --- a/launchers/qt/src/LauncherWindow.h +++ b/launchers/qt/src/LauncherWindow.h @@ -1,3 +1,4 @@ +#include "LauncherState.h" #include #include #include @@ -8,10 +9,10 @@ public: void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; - //void setLauncherState(std::shared_ptr launcherState) { _launcherState = launcherState; } + void setLauncherStatePtr(std::shared_ptr launcherState) { _launcherState = launcherState; } private: bool _drag { false }; QPoint _previousMousePos; - ///std::shared_ptr _launcherState { nullptr }; + std::shared_ptr _launcherState { nullptr }; }; diff --git a/launchers/qt/src/darwin/Helper.mm b/launchers/qt/src/darwin/Helper.mm new file mode 100644 index 0000000000..6bc0059fc8 --- /dev/null +++ b/launchers/qt/src/darwin/Helper.mm @@ -0,0 +1,42 @@ +#include "../Helper.h" + +#import "NSTask+NSTaskExecveAdditions.h" + +#import +#include +#include + +void launchClient(const QString& clientPath, const QString& homePath, const QString& defaultScriptOverride, + const QString& displayName, const QString& contentCachePath, const QString& loginTokenResponse) { + + NSString* homeBookmark = [[NSString stringWithFormat:@"hqhome="] stringByAppendingString:homePath.toNSString()]; + NSArray* arguments; + if (!loginTokenResponse.isEmpty()) { + arguments = [NSArray arrayWithObjects: + @"--url" , homePath.toNSString(), + @"--tokens", loginTokenResponse.toNSString(), + @"--cache", contentCachePath.toNSString(), + @"--displayName", displayName.toNSString(), + @"--defaultScriptsOverride", defaultScriptOverride.toNSString(), + @"--setBookmark", homeBookmark, + @"--no-updater", + @"--no-launcher", nil]; + } else { + arguments = [NSArray arrayWithObjects: + @"--url" , homePath.toNSString(), + @"--cache", contentCachePath.toNSString(), + @"--defaultScriptsOverride", defaultScriptOverride.toNSString(), + @"--setBookmark", homeBookmark, + @"--no-updater", + @"--no-launcher", nil]; + } + + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:clientPath.toNSString()]]; + NSLog(@"------> path %@: ", [url path]); + NSTask *task = [[NSTask alloc] init]; + task.launchPath = [url path]; + task.arguments = arguments; + [task replaceThisProcess]; + +} diff --git a/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.h b/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.h new file mode 100644 index 0000000000..f26a4021de --- /dev/null +++ b/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.h @@ -0,0 +1,9 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSTask (NSTaskExecveAdditions) +- (void) replaceThisProcess; +@end + +NS_ASSUME_NONNULL_END diff --git a/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.m b/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.m new file mode 100644 index 0000000000..6cdf669247 --- /dev/null +++ b/launchers/qt/src/darwin/NSTask+NSTaskExecveAdditions.m @@ -0,0 +1,75 @@ +#import "NSTask+NSTaskExecveAdditions.h" + +#import + +char ** +toCArray(NSArray *array) +{ + // Add one to count to accommodate the NULL that terminates the array. + char **cArray = (char **) calloc([array count] + 1, sizeof(char *)); + if (cArray == NULL) { + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + char *str; + for (int i = 0; i < [array count]; i++) { + str = (char *) [array[i] UTF8String]; + if (str == NULL) { + NSException *exception = [NSException + exceptionWithName:@"NULLStringException" + reason:@"UTF8String was NULL" + userInfo:nil]; + @throw exception; + } + if (asprintf(&cArray[i], "%s", str) == -1) { + for (int j = 0; j < i; j++) { + free(cArray[j]); + } + free(cArray); + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + } + return cArray; +} + +@implementation NSTask (NSTaskExecveAdditions) + +- (void) replaceThisProcess { + char **args = toCArray([@[[self launchPath]] arrayByAddingObjectsFromArray:[self arguments]]); + + NSMutableArray *env = [[NSMutableArray alloc] init]; + NSDictionary* environvment = [[NSProcessInfo processInfo] environment]; + for (NSString* key in environvment) { + NSString* environmentVariable = [[key stringByAppendingString:@"="] stringByAppendingString:environvment[key]]; + [env addObject:environmentVariable]; + } + + char** envp = toCArray(env); + // `execve` replaces the current process with `path`. + // It will only return if it fails to replace the current process. + NSLog(@"------------>"); + chdir(dirname(args[0])); + execve(args[0], (char * const *)args, envp); + + NSLog(@"----------- FAILED "); + // If we're here `execve` failed. :( + for (int i = 0; i < [[self arguments] count]; i++) { + free((void *) args[i]); + } + free((void *) args); + + NSException *exception = [NSException + exceptionWithName:@"ExecveException" + reason:[NSString stringWithFormat:@"couldn't execve: %s", strerror(errno)] + userInfo:nil]; + @throw exception; +} + +@end