// // LauncherDlg.cpp // // Created by Luis Cuenca on 6/5/2019. // 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 "stdafx.h" #include #include "LauncherApp.h" #include "LauncherDlg.h" #include #include #include #pragma comment(lib, "d2d1") #ifdef _DEBUG #define new DEBUG_NEW #endif static int ACTION_FONT_SIZE = 40; static int MESSAGE_FONT_SIZE = 19; static int FIELDS_FONT_SIZE = 24; static int BUTTON_FONT_SIZE = 25; static int TERMS_FONT_SIZE = 17; static int TROUBLE_FONT_SIZE = 14; static COLORREF COLOR_GREY = RGB(120, 120, 120); static COLORREF COLOR_BLACK= RGB(0, 0, 0); static COLORREF COLOR_WHITE = RGB(255, 255, 255); static COLORREF COLOR_LIGHTER_GREY = RGB(230, 230, 230); static COLORREF COLOR_LIGHT_GREY = RGB(200, 200, 200); static COLORREF COLOR_BLUE = RGB(50, 160, 200); static CString FIRASANS_REGULAR = _T("FiraSans-Regular"); static CString FIRASANS_SEMIBOLD = _T("FiraSans-SemiBold"); static CString TROUBLE_URL = _T("https://www.highfidelity.com/hq-support"); static CString TERMS_URL = _T("https://www.highfidelity.com/termsofservice"); static int SPLASH_DURATION = 100; CLauncherDlg::CLauncherDlg(CWnd* pParent) : CDialog(IDD_LAUNCHER_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); EnableD2DSupport(); } CLauncherDlg::~CLauncherDlg() { theApp._manager.closeLog(); } void CLauncherDlg::DoDataExchange(CDataExchange* pDX) { DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext); DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link); DDX_Control(pDX, IDC_TERMS_LINK, m_terms_link); DDX_Control(pDX, IDC_ORGNAME, m_orgname); DDX_Control(pDX, IDC_USERNAME, m_username); DDX_Control(pDX, IDC_PASSWORD, m_password); CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CLauncherDlg, CDialog) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_TIMER() ON_EN_SETFOCUS(IDC_ORGNAME, &CLauncherDlg::OnOrgEditChangeFocus) ON_EN_SETFOCUS(IDC_USERNAME, &CLauncherDlg::OnUserEditChangeFocus) ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus) ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked) ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked) ON_BN_CLICKED(IDC_TERMS_LINK, &CLauncherDlg::OnTermsClicked) ON_WM_CTLCOLOR() ON_WM_DRAWITEM() ON_WM_SETCURSOR() END_MESSAGE_MAP() // CLauncherDlg message handlers BOOL CLauncherDlg::OnInitDialog() { CDialog::OnInitDialog(); MarkWindowAsUnpinnable(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CFont editFont; if (LauncherUtils::getFont(FIRASANS_REGULAR, FIELDS_FONT_SIZE, true, editFont)) { m_orgname.SetFont(&editFont); m_username.SetFont(&editFont); m_password.SetFont(&editFont); } CFont buttonFont; if (LauncherUtils::getFont(_T("FiraSans-Bold"), BUTTON_FONT_SIZE, true, buttonFont)) { m_btnNext.SetFont(&editFont); } m_message_label = (CStatic *)GetDlgItem(IDC_MESSAGE_LABEL); m_action_label = (CStatic *)GetDlgItem(IDC_ACTION_LABEL); m_message2_label = (CStatic *)GetDlgItem(IDC_MESSAGE2_LABEL); m_action2_label = (CStatic *)GetDlgItem(IDC_ACTION2_LABEL); m_orgname_banner = (CStatic *)GetDlgItem(IDC_ORGNAME_BANNER); m_username_banner = (CStatic *)GetDlgItem(IDC_USERNAME_BANNER); m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER); m_terms = (CStatic *)GetDlgItem(IDC_TERMS); m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE); m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL); m_progress = (CStatic *)GetDlgItem(IDC_PROGRESS); m_version = (CStatic *)GetDlgItem(IDC_VERSION); CString version; version.Format(_T("V.%s"), theApp._manager.getLauncherVersion()); m_version->SetWindowTextW(version); m_voxel->EnableD2DSupport(); m_progress->EnableD2DSupport(); m_pRenderTarget = GetRenderTarget(); SetTimer(1, 2, NULL); return TRUE; } void CLauncherDlg::MarkWindowAsUnpinnable() { HWND hwnd = AfxGetMainWnd()->m_hWnd; IPropertyStore* pps; HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps)); if (SUCCEEDED(hr)) { PROPVARIANT var; var.vt = VT_BOOL; var.boolVal = VARIANT_TRUE; hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var); pps->Release(); } } POINT CLauncherDlg::getMouseCoords(MSG* pMsg) { POINT pos; pos.x = (int)(short)LOWORD(pMsg->lParam); pos.y = (int)(short)HIWORD(pMsg->lParam); return pos; } BOOL CLauncherDlg::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYDOWN: if (pMsg->wParam == 'A' && GetKeyState(VK_CONTROL) < 0) { CWnd* wnd = GetFocus(); CWnd* myWnd = this->GetDlgItem(IDC_ORGNAME); if (wnd && (wnd == this->GetDlgItem(IDC_ORGNAME) || wnd == this->GetDlgItem(IDC_USERNAME) || wnd == this->GetDlgItem(IDC_PASSWORD))) { ((CEdit*)wnd)->SetSel(0, -1); } return TRUE; } else if (pMsg->wParam == VK_RETURN) { OnNextClicked(); return TRUE; } else if (pMsg->wParam == VK_ESCAPE) { theApp._manager.onCancel(); } break; case WM_LBUTTONDOWN: if (pMsg->hwnd == GetSafeHwnd()) { _draggingWindow = true; _dragOffset = getMouseCoords(pMsg); SetCapture(); } break; case WM_LBUTTONUP: if (_draggingWindow) { ReleaseCapture(); _draggingWindow = false; } break; case WM_MOUSEMOVE: if (_draggingWindow) { POINT pos = getMouseCoords(pMsg); RECT windowRect; GetWindowRect(&windowRect); int width = windowRect.right - windowRect.left; int height = windowRect.bottom - windowRect.top; ClientToScreen(&pos); MoveWindow(pos.x - _dragOffset.x, pos.y - _dragOffset.y, width, height, FALSE); } break; default: break; } return CDialog::PreTranslateMessage(pMsg); } void CLauncherDlg::setCustomDialog() { LONG lStyle = GetWindowLong(GetSafeHwnd(), GWL_STYLE); lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle); LONG lExStyle = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE); lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, lExStyle); SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); } void CLauncherDlg::OnPaint() { CPaintDC dc(this); setCustomDialog(); CDialog::OnPaint(); } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CLauncherDlg::OnQueryDragIcon() { return static_cast(m_hIcon); } void CLauncherDlg::startProcess() { if (theApp._manager.needsUpdate()) { theApp._manager.addToLog(_T("Starting Process Update")); setDrawDialog(DrawStep::DrawProcessUpdate); } else { theApp._manager.addToLog(_T("Starting Process Setup")); setDrawDialog(DrawStep::DrawProcessSetup); } theApp._manager.addToLog(_T("Deleting download directory")); CString downloadDir; theApp._manager.getAndCreatePaths(LauncherManager::PathType::Download_Directory, downloadDir); LauncherUtils::deleteDirectoryOnThread(downloadDir, [&](bool error) { if (!error) { if (!theApp._manager.isLoggedIn()) { theApp._manager.addToLog(_T("Downloading Content")); theApp._manager.downloadContent(); } else { theApp._manager.addToLog(_T("Downloading App")); theApp._manager.downloadApplication(); } } else { theApp._manager.addToLog(_T("Error deleting download directory.")); theApp._manager.setFailed(true); } }); } BOOL CLauncherDlg::getHQInfo(const CString& orgname) { CString hash; CString lowerOrgName = orgname; lowerOrgName.MakeLower(); LauncherUtils::hMac256(lowerOrgName, LAUNCHER_HMAC_SECRET, hash); CString msg; msg.Format(_T("Calculated hash: \"%s\" => \"%s\""), lowerOrgName, hash); theApp._manager.addToLog(msg); return theApp._manager.readOrganizationJSON(hash) == LauncherUtils::ResponseError::NoError; } afx_msg void CLauncherDlg::OnTroubleClicked() { LauncherUtils::executeOnForeground(TROUBLE_URL, _T("")); } afx_msg void CLauncherDlg::OnTermsClicked() { LauncherUtils::executeOnForeground(TERMS_URL, _T("")); } afx_msg void CLauncherDlg::OnNextClicked() { if (_drawStep == DrawStep::DrawChoose) { CString displayName; m_username.GetWindowTextW(displayName); theApp._manager.setDisplayName(displayName); theApp._manager.addToLog(_T("Setting display name: " + displayName)); startProcess(); } else if (_drawStep == DrawStep::DrawError) { theApp._manager.restartLauncher(); } else if (_drawStep == DrawStep::DrawLoginLogin || _drawStep == DrawStep::DrawLoginErrorCred || _drawStep == DrawStep::DrawLoginErrorOrg) { CString token; CString username, password, orgname; m_orgname.GetWindowTextW(orgname); m_username.GetWindowTextW(username); m_password.GetWindowTextW(password); // trim spaces orgname = CString(std::regex_replace(LauncherUtils::cStringToStd(orgname), std::regex("^ +| +$|( ) +"), "$1").c_str()); username = CString(std::regex_replace(LauncherUtils::cStringToStd(username), std::regex("^ +| +$|( ) +"), "$1").c_str()); // encode username = LauncherUtils::urlEncodeString(username); password = LauncherUtils::urlEncodeString(password); LauncherUtils::ResponseError error; if (orgname.GetLength() > 0 && username.GetLength() > 0 && password.GetLength() > 0) { theApp._manager.addToLog(_T("Trying to get organization data")); if (getHQInfo(orgname)) { theApp._manager.addToLog(_T("Organization data received.")); theApp._manager.addToLog(_T("Trying to log in with credentials")); error = theApp._manager.getAccessTokenForCredentials(username, password); if (error == LauncherUtils::ResponseError::NoError) { theApp._manager.addToLog(_T("Logged in correctly.")); setDrawDialog(DrawStep::DrawChoose); } else if (error == LauncherUtils::ResponseError::BadCredentials) { theApp._manager.addToLog(_T("Bad credentials. Try again")); setDrawDialog(DrawStep::DrawLoginErrorCred); } else { theApp._manager.addToLog(_T("Error Reading or retrieving response.")); MessageBox(L"Error Reading or retrieving response.", L"Network Error", MB_OK | MB_ICONERROR); } } else { theApp._manager.addToLog(_T("Organization name does not exist.")); setDrawDialog(DrawStep::DrawLoginErrorOrg); } } } } void CLauncherDlg::drawBackground(CHwndRenderTarget* pRenderTarget) { CD2DBitmap m_pBitmamBackground(pRenderTarget, IDB_PNG1, _T("PNG")); auto size = GetRenderTarget()->GetSize(); CD2DRectF backRec(0.0f, 0.0f, size.width, size.height); GetRenderTarget()->DrawBitmap(&m_pBitmamBackground, backRec); pRenderTarget->DrawBitmap(&m_pBitmamBackground, backRec); } void CLauncherDlg::drawLogo(CHwndRenderTarget* pRenderTarget) { CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG")); auto size = pRenderTarget->GetSize(); int logoWidth = 231; int logoHeight = 173; float logoPosX = 0.5f * (size.width - logoWidth); float logoPosY = 0.5f * (size.height - logoHeight); CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight); pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec); } void CLauncherDlg::drawSmallLogo(CHwndRenderTarget* pRenderTarget) { CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG5, _T("PNG")); auto size = pRenderTarget->GetSize(); int xPadding = 6; int yPadding = 22; int logoWidth = 100; int logoHeight = 18; float logoPosX = size.width - logoWidth - xPadding; float logoPosY = size.height - logoHeight - yPadding; CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight); pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec); } void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) { CD2DBitmap m_pBitmamVoxel(pRenderTarget, IDB_PNG4, _T("PNG")); auto size = pRenderTarget->GetSize(); int logoWidth = 132; int logoHeight = 134; float voxelPosX = 0.5f * (size.width - logoWidth); float voxelPosY = 0.5f * (size.height - logoHeight); CD2DRectF voxelRec(voxelPosX, voxelPosY, voxelPosX + logoWidth, voxelPosY + logoHeight); auto midPoint = D2D1::Point2F(0.5f * size.width, 0.5f * size.height); _logoRotation += 2.0f; CD2DSolidColorBrush brush(pRenderTarget, D2D1::ColorF(0.0f, 0.0f, 0.0f)); pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation - 2.0f, midPoint)); pRenderTarget->FillRectangle(voxelRec, &brush); pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation, midPoint)); pRenderTarget->DrawBitmap(&m_pBitmamVoxel, voxelRec); pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); } void CLauncherDlg::drawProgress(CHwndRenderTarget* pRenderTarget, float progress, const D2D1::ColorF& color) { auto size = pRenderTarget->GetSize(); if (progress == 0.0f) { return; } else { progress = min(1.0f, progress); } CRect winRec; float fullHeight = (float)size.height; float halfHeight = 0.5f * (float)size.height; CD2DRectF bkCircleRect1 = CD2DRectF(0.0f, 0.0f, fullHeight, fullHeight); float progPos = halfHeight + (float)(size.width - size.height) * progress; CD2DRectF bkCircleRect2 = CD2DRectF(progPos - halfHeight, 0.0f, progPos + halfHeight, fullHeight); CD2DRectF bkRect = CD2DRectF(halfHeight, 0.0f, progPos, fullHeight); CD2DEllipse bkCircle1 = CD2DEllipse(bkCircleRect1); CD2DEllipse bkCircle2 = CD2DEllipse(bkCircleRect2); CD2DSolidColorBrush brush(pRenderTarget, color); pRenderTarget->FillEllipse(bkCircle1, &brush); pRenderTarget->FillEllipse(bkCircle2, &brush); pRenderTarget->FillRectangle(bkRect, &brush); } void CLauncherDlg::showWindows(std::vector windows, bool show) { for (auto window : windows) { window->ShowWindow(show ? SW_SHOW : SW_HIDE); } } void CLauncherDlg::prepareLogin(DrawStep step) { m_voxel->ShowWindow(SW_HIDE); m_progress->ShowWindow(SW_HIDE); m_orgname_banner->SetWindowTextW(_T("Organization Name")); m_username_banner->SetWindowTextW(_T("Username")); m_password_banner->SetWindowTextW(_T("Password")); CString editText; m_orgname.GetWindowTextW(editText); m_orgname_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE); m_username.GetWindowTextW(editText); m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE); m_password.GetWindowTextW(editText); m_password_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE); m_orgname.ShowWindow(SW_SHOW); m_username.ShowWindow(SW_SHOW); m_password.ShowWindow(SW_SHOW); CString actionText = step == DrawStep::DrawLoginLogin ? _T("Please log in") : _T("Uh-oh, we have a problem"); CString messageText = step == DrawStep::DrawLoginLogin ? _T("Be sure you've uploaded your Avatar before signing in.") : step == DrawStep::DrawLoginErrorCred ? _T("There is a problem with your credentials.\n Please try again.") : _T("There is a problem with your Organization name.\n Please try again."); m_action_label->SetWindowTextW(actionText); m_message_label->SetWindowTextW(messageText); m_action_label->ShowWindow(SW_SHOW); m_message_label->ShowWindow(SW_SHOW); m_btnNext.ShowWindow(SW_SHOW); m_trouble->SetWindowTextW(_T("Having Trouble?")); m_trouble->ShowWindow(SW_SHOW); m_trouble_link.ShowWindow(SW_SHOW); } void CLauncherDlg::prepareChoose() { m_orgname.ShowWindow(SW_HIDE); m_username.SetWindowTextW(_T("")); m_username_banner->SetWindowTextW(_T("Display Name")); CString editText; m_username.GetWindowTextW(editText); m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE); m_password.ShowWindow(SW_HIDE); m_orgname_banner->ShowWindow(SW_HIDE); m_password_banner->ShowWindow(SW_HIDE); m_action_label->SetWindowTextW(_T("Choose a display name")); m_message_label->SetWindowTextW(_T("This is the name that your teammates will see.")); m_terms->ShowWindow(SW_SHOW); m_terms_link.ShowWindow(SW_SHOW); m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity")); m_terms_link.SetWindowTextW(_T("Terms of Service")); setVerticalElement(&m_btnNext, -35, 0, false); m_btnNext.ShowWindow(SW_SHOW); } void CLauncherDlg::prepareProcess(DrawStep step) { m_trouble->ShowWindow(SW_HIDE); m_trouble_link.ShowWindow(SW_HIDE); m_terms->ShowWindow(SW_HIDE); m_terms_link.ShowWindow(SW_HIDE); m_orgname_banner->ShowWindow(SW_HIDE); m_username_banner->ShowWindow(SW_HIDE); m_password_banner->ShowWindow(SW_HIDE); m_orgname.ShowWindow(SW_HIDE); m_username.ShowWindow(SW_HIDE); m_password.ShowWindow(SW_HIDE); m_action_label->SetWindowTextW(_T("")); m_message_label->SetWindowTextW(_T("")); m_btnNext.ShowWindow(SW_HIDE); m_action_label->ShowWindow(SW_HIDE); m_message_label->ShowWindow(SW_HIDE); m_voxel->ShowWindow(SW_SHOW); m_progress->ShowWindow(SW_SHOW); CString actionText = _T(""); CString messageText = _T(""); switch (step) { case DrawStep::DrawProcessSetup: actionText = _T("We're building your virtual HQ"); messageText = _T("Set up may take several minutes."); break; case DrawStep::DrawProcessUpdate: actionText = _T("Getting updates..."); messageText = _T("We're getting the latest and greatest for you, one sec."); break; case DrawStep::DrawProcessFinishHq: actionText = _T("Your new HQ is all setup"); messageText = _T("Thanks for being patient."); break; case DrawStep::DrawProcessFinishUpdate: actionText = _T("You're good to go!"); messageText = _T("Thanks for being patient."); break; case DrawStep::DrawProcessUninstall: actionText = _T("Uninstalling..."); messageText = _T("It'll take one sec."); break; case DrawStep::DrawError: actionText = _T("Uh oh."); messageText = _T("We seem to have a problem.\nPlease restart HQ."); setVerticalElement(m_message2_label, 0, 5, false); setVerticalElement(&m_btnNext, 10); m_btnNext.ShowWindow(SW_SHOW); m_progress->ShowWindow(SW_HIDE); break; default: break; } m_action2_label->SetWindowTextW(actionText); m_message2_label->SetWindowTextW(messageText); m_action2_label->ShowWindow(SW_SHOW); m_message2_label->ShowWindow(SW_SHOW); } BOOL CLauncherDlg::getTextFormat(int resID, TextFormat& formatOut) { // Set default values for message BOOL isText = TRUE; formatOut.color = COLOR_LIGHT_GREY; formatOut.isBold = false; formatOut.isButton = false; formatOut.size = MESSAGE_FONT_SIZE; formatOut.underlined = false; switch (resID) { case IDC_VOXEL: case IDD_LAUNCHER_DIALOG: isText = FALSE; case IDC_MESSAGE_LABEL: case IDC_MESSAGE2_LABEL: // Default values break; case IDC_ACTION_LABEL: case IDC_ACTION2_LABEL: formatOut.size = ACTION_FONT_SIZE; formatOut.isBold = true; formatOut.color = COLOR_LIGHTER_GREY; break; case IDC_USERNAME: case IDC_PASSWORD: case IDC_ORGNAME: formatOut.color = COLOR_WHITE; formatOut.size = FIELDS_FONT_SIZE; formatOut.underlined = true; break; case IDC_USERNAME_BANNER: case IDC_PASSWORD_BANNER: case IDC_ORGNAME_BANNER: formatOut.size = FIELDS_FONT_SIZE; formatOut.color = COLOR_GREY; break; case IDC_VERSION: case IDC_TERMS: formatOut.size = TERMS_FONT_SIZE; break; case IDC_TERMS_LINK: formatOut.size = TERMS_FONT_SIZE; formatOut.isBold = true; break; case IDC_TROUBLE: formatOut.size = TROUBLE_FONT_SIZE; formatOut.color = COLOR_BLUE; break; } return isText; } HBRUSH CLauncherDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); TextFormat textFormat; int resId = pWnd->GetDlgCtrlID(); if (getTextFormat(resId, textFormat)) { pDC->SetTextColor(textFormat.color); pDC->SetBkMode(TRANSPARENT); CFont textFont; CString fontFamily = textFormat.isBold ? FIRASANS_SEMIBOLD : FIRASANS_REGULAR; if (LauncherUtils::getFont(fontFamily, textFormat.size, textFormat.isBold, textFont)) { pDC->SelectObject(&textFont); } if (textFormat.underlined) { CRect rect; pWnd->GetClientRect(&rect); int borderThick = 1; int padding = 4; CRect lineRect = CRect(rect.left + padding, rect.bottom, rect.right - padding, rect.bottom + borderThick); lineRect.MoveToY(lineRect.bottom + 1); pDC->FillSolidRect(lineRect, COLOR_GREY); } } return (HBRUSH)GetStockObject(BLACK_BRUSH); } void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; dc.Attach(lpDrawItemStruct->hDC); CRect rect = lpDrawItemStruct->rcItem; CRect defrect = rect; CString btnName = _T(""); int xpan = 0; if (nIDCtl == IDC_BUTTON_NEXT) { if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) { 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) { btnName += _T("RESTART"); } else { btnName += _T("TRY AGAIN"); } int borderThick = 2; dc.FillSolidRect(rect, COLOR_BLACK); dc.FillSolidRect(defrect, COLOR_WHITE); defrect.DeflateRect(borderThick, borderThick, borderThick, borderThick); dc.FillSolidRect(defrect, COLOR_BLACK); UINT state = lpDrawItemStruct->itemState; dc.SetTextColor(COLOR_WHITE); CFont buttonFont; if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, BUTTON_FONT_SIZE, true, buttonFont)) { dc.SelectObject(buttonFont); } dc.DrawText(btnName, CRect(rect.left, rect.top + 4, rect.right, rect.bottom - 8), DT_CENTER | DT_VCENTER | DT_SINGLELINE); dc.Detach(); } else if (nIDCtl == IDC_TROUBLE_LINK) { dc.FillSolidRect(rect, COLOR_BLACK); dc.SetTextColor(COLOR_BLUE); CFont buttonFont; if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, TROUBLE_FONT_SIZE, true, buttonFont)) { dc.SelectObject(buttonFont); } dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE); } else if (nIDCtl == IDC_TERMS_LINK) { dc.FillSolidRect(rect, COLOR_BLACK); dc.SetTextColor(COLOR_LIGHT_GREY); CFont buttonFont; if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, TERMS_FONT_SIZE, true, buttonFont)) { dc.SelectObject(buttonFont); } dc.DrawText(_T("Terms of Service"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_LEFT | DT_TOP | DT_SINGLELINE); } } void CLauncherDlg::redrawBanner(const CEdit& edit, CStatic* banner) { CString editText; edit.GetWindowTextW(editText); if (editText.GetLength() == 0) { banner->Invalidate(); } } void CLauncherDlg::OnOrgEditChangeFocus() { redrawBanner(m_username, m_username_banner); redrawBanner(m_password, m_password_banner); } void CLauncherDlg::OnUserEditChangeFocus() { redrawBanner(m_orgname, m_orgname_banner); redrawBanner(m_password, m_password_banner); } void CLauncherDlg::OnPassEditChangeFocus() { redrawBanner(m_orgname, m_orgname_banner); redrawBanner(m_username, m_username_banner); } BOOL CLauncherDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (pWnd->IsKindOf(RUNTIME_CLASS(CButton))) { ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND)); return TRUE; } return CDialog::OnSetCursor(pWnd, nHitTest, message); } void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) { if (theApp._manager.hasFailed() && _drawStep != DrawStep::DrawError) { theApp._manager.saveErrorLog(); prepareProcess(DrawStep::DrawError); setDrawDialog(DrawStep::DrawError, false); } if (_drawStep != DrawStep::DrawError) { if (_drawStep == DrawStep::DrawProcessSetup || _drawStep == DrawStep::DrawProcessUpdate || _drawStep == DrawStep::DrawProcessUninstall || _drawStep == DrawStep::DrawProcessFinishHq || _drawStep == DrawStep::DrawProcessFinishUpdate) { // Refresh setDrawDialog(_drawStep, true); } if (theApp._manager.needsSelfUpdate()) { if (theApp._manager.needsSelfDownload()) { theApp._manager.downloadNewLauncher(); } else { if (_splashStep > SPLASH_DURATION && _splashStep < 2 * SPLASH_DURATION) { float progress = (float)(_splashStep - SPLASH_DURATION) / SPLASH_DURATION; if (theApp._manager.willContinueUpdating()) { progress = CONTINUE_UPDATING_GLOBAL_OFFSET * progress; progress = min(progress, CONTINUE_UPDATING_GLOBAL_OFFSET); } theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, progress); _splashStep++; } if (theApp._manager.needsRestartNewLauncher()) { if (_splashStep >= 2 * SPLASH_DURATION) { theApp._manager.restartNewLauncher(); exit(0); } } } } LauncherManager::ContinueActionOnStart continueAction = theApp._manager.getContinueAction(); if (_showSplash) { if (_splashStep == 0) { if (theApp._manager.needsUninstall()) { theApp._manager.addToLog(_T("Waiting to uninstall")); setDrawDialog(DrawStep::DrawProcessUninstall); } else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueUpdate) { setDrawDialog(DrawStep::DrawProcessUpdate); theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 0.0f); } else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueLogIn) { _splashStep = SPLASH_DURATION; } else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueFinish) { theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 1.0f); setDrawDialog(DrawStep::DrawProcessFinishUpdate); _splashStep = SPLASH_DURATION; _showSplash = false; } else { theApp._manager.addToLog(_T("Start splash screen")); setDrawDialog(DrawStep::DrawLogo); } } else if (_splashStep > SPLASH_DURATION && !theApp._manager.needsToWait()) { _showSplash = false; if (theApp._manager.shouldShutDown()) { if (_applicationWND != NULL) { ::SetForegroundWindow(_applicationWND); ::SetActiveWindow(_applicationWND); } if (LauncherUtils::isProcessWindowOpened(L"interface.exe")) { exit(0); } } else if (theApp._manager.needsUpdate()) { startProcess(); } else if (theApp._manager.needsUninstall()) { if (theApp._manager.uninstallApplication()) { theApp._manager.addToLog(_T("HQ uninstalled successfully.")); exit(0); } else { theApp._manager.addToLog(_T("HQ failed to uninstall.")); theApp._manager.setFailed(true); } } else if (theApp._manager.needsSelfUpdate()) { setDrawDialog(DrawStep::DrawProcessUpdate); } else { theApp._manager.addToLog(_T("Starting login")); setDrawDialog(DrawStep::DrawLoginLogin); } } else if (theApp._manager.needsUninstall()) { theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, (float)_splashStep / SPLASH_DURATION); } _splashStep++; } else if (theApp._manager.shouldShutDown()) { if (LauncherUtils::isProcessWindowOpened(L"interface.exe")) { exit(0); } } if (theApp._manager.shouldLaunch()) { if (theApp._manager.needsInstall() || theApp._manager.needsUpdate()) { auto finishProcess = theApp._manager.needsUpdate() ? DrawStep::DrawProcessFinishUpdate : DrawStep::DrawProcessFinishHq; setDrawDialog(finishProcess); } _applicationWND = theApp._manager.launchApplication(); } } if (theApp._manager.needsToSelfInstall()) { theApp._manager.tryToInstallLauncher(TRUE); } } void CLauncherDlg::setVerticalElement(CWnd* element, int verticalOffset, int heightOffset, bool fromMainWindowBottom) { CRect elementRec; CRect windowRec; if (element != NULL) { element->GetWindowRect(&elementRec); ScreenToClient(&elementRec); int offset = verticalOffset; if (fromMainWindowBottom) { GetWindowRect(&windowRec); ScreenToClient(&windowRec); int currentDistance = windowRec.bottom - elementRec.bottom; offset = currentDistance - verticalOffset; } elementRec.bottom = elementRec.bottom + offset + heightOffset; elementRec.top = elementRec.top + offset; element->MoveWindow(elementRec, FALSE); } } void CLauncherDlg::setDrawDialog(DrawStep step, BOOL isUpdate) { _drawStep = step; float progress = 0.0f; auto m_pRenderTarget = GetRenderTarget(); auto m_voxelRenderTarget = m_voxel->GetRenderTarget(); auto m_progressRenderTarget = m_progress->GetRenderTarget(); switch (_drawStep) { case DrawStep::DrawLogo: { m_pRenderTarget->BeginDraw(); drawBackground(m_pRenderTarget); drawLogo(m_pRenderTarget); m_pRenderTarget->EndDraw(); CRect redrawRec; GetClientRect(redrawRec); redrawRec.top = redrawRec.bottom - 30; RedrawWindow(redrawRec); break; } case DrawStep::DrawLoginLogin: case DrawStep::DrawLoginErrorOrg: case DrawStep::DrawLoginErrorCred: prepareLogin(_drawStep); m_pRenderTarget->BeginDraw(); drawBackground(m_pRenderTarget); drawSmallLogo(m_pRenderTarget); m_pRenderTarget->EndDraw(); RedrawWindow(); break; case DrawStep::DrawChoose: prepareChoose(); m_pRenderTarget->BeginDraw(); drawBackground(m_pRenderTarget); drawSmallLogo(m_pRenderTarget); m_pRenderTarget->EndDraw(); RedrawWindow(); break; case DrawStep::DrawError: case DrawStep::DrawProcessFinishHq: case DrawStep::DrawProcessFinishUpdate: case DrawStep::DrawProcessUpdate: case DrawStep::DrawProcessUninstall: case DrawStep::DrawProcessSetup: if (!isUpdate) { m_voxelRenderTarget->BeginDraw(); m_voxelRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f)); m_voxelRenderTarget->EndDraw(); m_pRenderTarget->BeginDraw(); prepareProcess(_drawStep); drawBackground(m_pRenderTarget); drawSmallLogo(m_pRenderTarget); m_pRenderTarget->EndDraw(); RedrawWindow(); } m_progressRenderTarget->BeginDraw(); m_progressRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f)); drawProgress(m_progressRenderTarget, 1.0f, D2D1::ColorF(0.2f, 0.2f, 0.2f)); drawProgress(m_progressRenderTarget, theApp._manager.getProgress(), D2D1::ColorF(0.0f, 0.62f, 0.9f)); m_progressRenderTarget->EndDraw(); m_voxelRenderTarget->BeginDraw(); drawVoxel(m_voxelRenderTarget); m_voxelRenderTarget->EndDraw(); break; default: break; } }