diff --git a/hecl-gui/.gitmodules b/hecl-gui/.gitmodules new file mode 100644 index 000000000..23a6d2689 --- /dev/null +++ b/hecl-gui/.gitmodules @@ -0,0 +1,3 @@ +[submodule "quazip"] + path = quazip + url = https://github.com/mnafees/quazip.git diff --git a/hecl-gui/CMakeLists.txt b/hecl-gui/CMakeLists.txt index 8e01db940..f40964b7f 100644 --- a/hecl-gui/CMakeLists.txt +++ b/hecl-gui/CMakeLists.txt @@ -26,18 +26,31 @@ add_subdirectory(platforms/freedesktop) declare_qticon_target() list(APPEND PLAT_SRCS mainicon_qt.cpp) +file(GLOB QUAZIP_SRCS quazip/quazip/*.c quazip/quazip/*.cpp quazip/quazip/*.h) +list(REMOVE_ITEM QUAZIP_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/quazip/quazip/quagzipfile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/quazip/quazip/quagzipfile.h) +include_directories(quazip/quazip) +add_definitions(-DQUAZIP_STATIC=1) + add_executable(hecl-gui WIN32 MACOSX_BUNDLE MainWindow.ui MainWindow.hpp MainWindow.cpp + LaunchMenu.hpp LaunchMenu.cpp EscapeSequenceParser.hpp EscapeSequenceParser.cpp FileDirDialog.hpp ErrorLabel.hpp SysReqTableView.hpp SysReqTableView.cpp VectorISATableView.hpp VectorISATableView.cpp VectorISATableModel.hpp VectorISATableModelIntel.hpp + FindBlender.hpp FindBlender.cpp DownloadManager.hpp DownloadManager.cpp + ExtractZip.hpp ExtractZip.cpp ${QUAZIP_SRCS} Common.hpp Common.cpp ${PLAT_SRCS} main.cpp) set_target_properties(hecl-gui PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist") target_link_libraries(hecl-gui ${PLAT_LIBS} - ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Xml_LIBRARIES} zeus) + ${Qt5Widgets_LIBRARIES} + ${Qt5Network_LIBRARIES} + ${Qt5Xml_LIBRARIES} + hecl-light logvisor zeus athena-core athena-libyaml xxhash z) diff --git a/hecl-gui/Common.cpp b/hecl-gui/Common.cpp index c6ba5c709..4213b0da4 100644 --- a/hecl-gui/Common.cpp +++ b/hecl-gui/Common.cpp @@ -135,12 +135,16 @@ QString URDEVersion::fileString(bool withExtension) const VectorISAToString(m_vectorISA)); } +#ifndef _WIN32 static void HUPHandler(int) {} +#endif void InitializePlatform() { +#ifndef _WIN32 /* This can happen when terminating hecl - do nothing */ signal(SIGHUP, HUPHandler); +#endif #if ZEUS_ARCH_X86_64 const_cast(CurArchitecture) = Architecture::X86_64; diff --git a/hecl-gui/DownloadManager.cpp b/hecl-gui/DownloadManager.cpp index 8cebbabb4..1f0e433c5 100644 --- a/hecl-gui/DownloadManager.cpp +++ b/hecl-gui/DownloadManager.cpp @@ -1,5 +1,7 @@ #include "DownloadManager.hpp" #include "Common.hpp" +#include +#include static const char AxioDLPublicKeyPEM[] = "-----BEGIN PUBLIC KEY-----\n" @@ -119,19 +121,19 @@ void DownloadManager::binaryFinished() if (m_progBar) m_progBar->setValue(100); - QFile fp(m_outPath); - if (!fp.open(QIODevice::WriteOnly)) + QByteArray all = m_binaryInProgress->readAll(); + QBuffer buff(&all); + QuaZip zip(&buff); + if (!zip.open(QuaZip::mdUnzip)) { - setError(QNetworkReply::ContentAccessDenied, fp.errorString()); + setError(QNetworkReply::UnknownContentError, "Unable to open zip archive."); m_binaryInProgress->deleteLater(); m_binaryInProgress = nullptr; return; } - fp.write(m_binaryInProgress->readAll()); - fp.close(); if (m_completionHandler) - m_completionHandler(m_outPath); + m_completionHandler(zip); m_binaryInProgress->deleteLater(); m_binaryInProgress = nullptr; @@ -147,7 +149,7 @@ void DownloadManager::binaryError(QNetworkReply::NetworkError error) m_progBar->setEnabled(false); if (m_failedHandler) - m_failedHandler(m_outPath); + m_failedHandler(); } void DownloadManager::binaryValidateCert() diff --git a/hecl-gui/DownloadManager.hpp b/hecl-gui/DownloadManager.hpp index e7a9ce150..44f504a04 100644 --- a/hecl-gui/DownloadManager.hpp +++ b/hecl-gui/DownloadManager.hpp @@ -7,6 +7,8 @@ #include #include +class QuaZip; + class DownloadManager : public QObject { Q_OBJECT @@ -18,8 +20,8 @@ class DownloadManager : public QObject QProgressBar* m_progBar = nullptr; QLabel* m_errorLabel = nullptr; std::function m_indexCompletionHandler; - std::function m_completionHandler; - std::function m_failedHandler; + std::function m_completionHandler; + std::function m_failedHandler; void resetError() { @@ -44,8 +46,8 @@ public: : QObject(parent), m_netManager(this) {} void connectWidgets(QProgressBar* progBar, QLabel* errorLabel, std::function&& indexCompletionHandler, - std::function&& completionHandler, - std::function&& failedHandler) + std::function&& completionHandler, + std::function&& failedHandler) { m_progBar = progBar; m_errorLabel = errorLabel; diff --git a/hecl-gui/EscapeSequenceParser.cpp b/hecl-gui/EscapeSequenceParser.cpp index 0d80a2739..e6b4577f1 100644 --- a/hecl-gui/EscapeSequenceParser.cpp +++ b/hecl-gui/EscapeSequenceParser.cpp @@ -514,3 +514,85 @@ void ParseEscapeSequence(int attribute, QListIterator& i, QTextCharForm } } } + +void ReturnInsert(QTextCursor& cur, const QString& text) +{ + auto DoLine = [&](const QString& line) + { + auto DoReturn = [&](const QString& ret) + { + if (!ret.isEmpty()) + { + cur.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ret.size()); + cur.insertText(ret); + } + }; + QStringList list = line.split('\r'); + DoReturn(list.front()); + if (list.size() > 1) + { + for (auto it = list.begin() + 1; it != list.end(); ++it) + { + cur.movePosition(QTextCursor::StartOfBlock); + DoReturn(*it); + } + } + }; + +#if _WIN32 + QStringList lineSplit = text.split("\r\n"); +#else + QStringList lineSplit = text.split('\n'); +#endif + DoLine(lineSplit.front()); + if (lineSplit.size() > 1) + { + for (auto it = lineSplit.begin() + 1; it != lineSplit.end(); ++it) + { + cur.movePosition(QTextCursor::EndOfLine); + cur.insertBlock(); + DoLine(*it); + } + } +} + +void ReturnInsert(QTextCursor& cur, const QString& text, const QTextCharFormat& format) +{ + auto DoLine = [&](const QString& line) + { + auto DoReturn = [&](const QString& ret) + { + if (!ret.isEmpty()) + { + cur.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ret.size()); + cur.insertText(ret, format); + } + }; + QStringList list = line.split('\r'); + DoReturn(list.front()); + if (list.size() > 1) + { + for (auto it = list.begin() + 1; it != list.end(); ++it) + { + cur.movePosition(QTextCursor::StartOfBlock); + DoReturn(*it); + } + } + }; + +#if _WIN32 + QStringList lineSplit = text.split("\r\n"); +#else + QStringList lineSplit = text.split('\n'); +#endif + DoLine(lineSplit.front()); + if (lineSplit.size() > 1) + { + for (auto it = lineSplit.begin() + 1; it != lineSplit.end(); ++it) + { + cur.movePosition(QTextCursor::EndOfLine); + cur.insertBlock(); + DoLine(*it); + } + } +} diff --git a/hecl-gui/EscapeSequenceParser.hpp b/hecl-gui/EscapeSequenceParser.hpp index 25384e033..af915612c 100644 --- a/hecl-gui/EscapeSequenceParser.hpp +++ b/hecl-gui/EscapeSequenceParser.hpp @@ -3,8 +3,13 @@ #include #include +#include void ParseEscapeSequence(int attribute, QListIterator& i, QTextCharFormat& textCharFormat, const QTextCharFormat& defaultTextCharFormat); +void ReturnInsert(QTextCursor& cur, const QString& text); + +void ReturnInsert(QTextCursor& cur, const QString& text, const QTextCharFormat& format); + #endif // GUI_ESCAPESEQUENCEPARSER_HPP diff --git a/hecl-gui/ExtractZip.cpp b/hecl-gui/ExtractZip.cpp new file mode 100644 index 000000000..0c03c6c31 --- /dev/null +++ b/hecl-gui/ExtractZip.cpp @@ -0,0 +1,141 @@ +#include "ExtractZip.hpp" +#include +#include +#include + +/** + * Modified JICompress utilities to operate on in-memory zip. + * Only contains directory extraction functionality. + */ + +static bool copyData(QIODevice &inFile, QIODevice &outFile) +{ + while (!inFile.atEnd()) { + char buf[4096]; + qint64 readLen = inFile.read(buf, 4096); + if (readLen <= 0) + return false; + if (outFile.write(buf, readLen) != readLen) + return false; + } + return true; +} + +QStringList ExtractZip::getFileList(QuaZip& zip) +{ + // Estraggo i nomi dei file + QStringList lst; + QuaZipFileInfo64 info; + for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { + if(!zip.getCurrentFileInfo(&info)) + return {}; + lst << info.name; + //info.name.toLocal8Bit().constData() + } + + return lst; +} + +/**OK + * Estrae il file fileName, contenuto nell'oggetto zip, con il nome fileDest. + * Se la funzione fallisce restituisce false e cancella il file che si e tentato di estrarre. + * + * La funzione fallisce se: + * * zip==NULL; + * * l'oggetto zip e stato aperto in una modalita non compatibile con l'estrazione di file; + * * non e possibile aprire il file all'interno dell'oggetto zip; + * * non e possibile creare il file estratto; + * * si e rilevato un errore nella copia dei dati (1); + * * non e stato possibile chiudere il file all'interno dell'oggetto zip (1); + * + * (1): prima di uscire dalla funzione cancella il file estratto. + */ +bool ExtractZip::extractFile(QuaZip& zip, QString fileName, QString fileDest) +{ + // zip: oggetto dove aggiungere il file + // filename: nome del file reale + // fileincompress: nome del file all'interno del file compresso + + // Controllo l'apertura dello zip + if (zip.getMode()!=QuaZip::mdUnzip) return false; + + // Apro il file compresso + if (!fileName.isEmpty()) + zip.setCurrentFile(fileName); + QuaZipFile inFile(&zip); + if(!inFile.open(QIODevice::ReadOnly) || inFile.getZipError()!=UNZ_OK) return false; + + // Controllo esistenza cartella file risultato + QDir curDir; + if (fileDest.endsWith('/')) { + if (!curDir.mkpath(fileDest)) { + return false; + } + } else { + if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) { + return false; + } + } + + QuaZipFileInfo64 info; + if (!zip.getCurrentFileInfo(&info)) + return false; + + QFile::Permissions srcPerm = info.getPermissions(); + if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir()) { + if (srcPerm != 0) { + QFile(fileDest).setPermissions(srcPerm); + } + return true; + } + + // Apro il file risultato + QFile outFile; + outFile.setFileName(fileDest); + if(!outFile.open(QIODevice::WriteOnly)) return false; + + // Copio i dati + if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK) { + outFile.close(); + return false; + } + outFile.close(); + + // Chiudo i file + inFile.close(); + if (inFile.getZipError()!=UNZ_OK) { + return false; + } + + if (srcPerm != 0) { + outFile.setPermissions(srcPerm); + } + return true; +} + +/**OK + * Estrae il file fileCompressed nella cartella dir. + * Se dir = "" allora il file viene estratto nella cartella corrente. + * Se la funzione fallisce cancella i file che si e tentato di estrarre. + * Restituisce i nomi assoluti dei file estratti. + * + * La funzione fallisce se: + * * non si riesce ad aprire l'oggetto zip; + * * la compressione di un file fallisce; + * * non si riesce a chiudere l'oggetto zip; + */ +bool ExtractZip::extractDir(QuaZip& zip, QString dir) +{ + QDir directory(dir); + if (!zip.goToFirstFile()) { + return false; + } + do { + QString name = zip.getCurrentFileName(); + QString absFilePath = directory.absoluteFilePath(name); + if (!extractFile(zip, "", absFilePath)) + return false; + } while (zip.goToNextFile()); + + return true; +} diff --git a/hecl-gui/ExtractZip.hpp b/hecl-gui/ExtractZip.hpp new file mode 100644 index 000000000..f09dde846 --- /dev/null +++ b/hecl-gui/ExtractZip.hpp @@ -0,0 +1,16 @@ +#ifndef GUI_EXTRACTZIP_HPP +#define GUI_EXTRACTZIP_HPP + +class QStringList; +class QuaZip; +class QString; + +class ExtractZip +{ +public: + static QStringList getFileList(QuaZip& zip); + static bool extractFile(QuaZip& zip, QString fileName, QString fileDest); + static bool extractDir(QuaZip& zip, QString dir); +}; + +#endif GUI_EXTRACTZIP_HPP diff --git a/hecl-gui/FindBlender.cpp b/hecl-gui/FindBlender.cpp new file mode 100644 index 000000000..393c53003 --- /dev/null +++ b/hecl-gui/FindBlender.cpp @@ -0,0 +1,123 @@ +#include "FindBlender.hpp" +#include "hecl/SteamFinder.hpp" +#include "hecl/hecl.hpp" + +namespace hecl::blender +{ + +#ifdef __APPLE__ +#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender" +#else +#define DEFAULT_BLENDER_BIN "blender" +#endif + +static const std::regex regBlenderVersion(R"(Blender ([0-9]+).([0-9]+) )", + std::regex::ECMAScript|std::regex::optimize); + +static bool RegFileExists(const hecl::SystemChar* path) +{ + if (!path) + return false; + hecl::Sstat theStat; + return !hecl::Stat(path, &theStat) && S_ISREG(theStat.st_mode); +} + +hecl::SystemString FindBlender(int& major, int& minor) +{ + major = 0; + minor = 0; + + /* User-specified blender path */ +#if _WIN32 + wchar_t BLENDER_BIN_BUF[2048]; + const wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN"); +#else + const char* blenderBin = getenv("BLENDER_BIN"); +#endif + + /* Steam blender */ + hecl::SystemString steamBlender; + + /* Child process of blender */ +#if _WIN32 + if (!blenderBin || !RegFileExists(blenderBin)) + { + /* Environment not set; try steam */ + steamBlender = hecl::FindCommonSteamApp(_S("Blender")); + if (steamBlender.size()) + { + steamBlender += _S("\\blender.exe"); + blenderBin = steamBlender.c_str(); + } + + if (!RegFileExists(blenderBin)) + { + /* No steam; try default */ + wchar_t progFiles[256]; + if (GetEnvironmentVariableW(L"ProgramFiles", progFiles, 256)) + { + _snwprintf(BLENDER_BIN_BUF, 2048, L"%s\\Blender Foundation\\Blender\\blender.exe", progFiles); + blenderBin = BLENDER_BIN_BUF; + if (!RegFileExists(blenderBin)) + blenderBin = nullptr; + } + else + blenderBin = nullptr; + } + } +#else + if (!RegFileExists(blenderBin)) + { + /* Try steam */ + steamBlender = hecl::FindCommonSteamApp(_S("Blender")); + if (steamBlender.size()) + { +#ifdef __APPLE__ + steamBlender += "/blender.app/Contents/MacOS/blender"; +#else + steamBlender += "/blender"; +#endif + blenderBin = steamBlender.c_str(); + if (!RegFileExists(blenderBin)) + { + blenderBin = DEFAULT_BLENDER_BIN; + if (!RegFileExists(blenderBin)) + { + blenderBin = nullptr; + } + } + } + } +#endif + + if (!blenderBin) + return {}; + + hecl::SystemString command = hecl::SystemString(_S("\"")) + blenderBin + _S("\" --version"); +#if _WIN32 + FILE* fp = _wpopen(command.c_str(), _S("r")); +#else + FILE* fp = popen(command.c_str(), "r"); +#endif + char versionBuf[256]; + size_t rdSize = fread(versionBuf, 1, 255, fp); + versionBuf[rdSize] = '\0'; +#if _WIN32 + _pclose(fp); +#else + pclose(fp); +#endif + + std::cmatch match; + if (std::regex_search(versionBuf, match, regBlenderVersion)) + { + major = atoi(match[1].str().c_str()); + minor = atoi(match[2].str().c_str()); + return blenderBin; + } + + return blenderBin; +} + +} + diff --git a/hecl-gui/FindBlender.hpp b/hecl-gui/FindBlender.hpp new file mode 100644 index 000000000..186fe9253 --- /dev/null +++ b/hecl-gui/FindBlender.hpp @@ -0,0 +1,13 @@ +#ifndef FINDBLENDER_HPP +#define FINDBLENDER_HPP + +#include "hecl/hecl.hpp" + +namespace hecl::blender +{ + +hecl::SystemString FindBlender(int& major, int& minor); + +} + +#endif // FINDBLENDER_HPP diff --git a/hecl-gui/LaunchMenu.cpp b/hecl-gui/LaunchMenu.cpp new file mode 100644 index 000000000..8dfbc7910 --- /dev/null +++ b/hecl-gui/LaunchMenu.cpp @@ -0,0 +1,89 @@ +#include "LaunchMenu.hpp" +#include "hecl/CVarCommons.hpp" + +LaunchMenu::LaunchMenu(hecl::CVarCommons& commons, QWidget* parent) +: QMenu("Launch Menu", parent), + m_commons(commons), + m_apiMenu("Graphics API", this), + m_msaaMenu("Anti-Aliasing", this), + m_anisoMenu("Anisotropic Filtering", this), + m_apiGroup(this), + m_msaaGroup(this), + m_anisoGroup(this) +{ +#ifdef _WIN32 + initApiAction(QStringLiteral("D3D11")); + initApiAction(QStringLiteral("Vulkan")); + initApiAction(QStringLiteral("OpenGL")); +#elif defined(__APPLE__) + initApiAction(QStringLiteral("Metal")); + initApiAction(QStringLiteral("OpenGL")); +#else + initApiAction(QStringLiteral("OpenGL")); + initApiAction(QStringLiteral("Vulkan")); +#endif + + initMsaaAction(QStringLiteral("1")); + initMsaaAction(QStringLiteral("2")); + initMsaaAction(QStringLiteral("4")); + initMsaaAction(QStringLiteral("8")); + initMsaaAction(QStringLiteral("16")); + + initAnisoAction(QStringLiteral("1")); + initAnisoAction(QStringLiteral("2")); + initAnisoAction(QStringLiteral("4")); + initAnisoAction(QStringLiteral("8")); + initAnisoAction(QStringLiteral("16")); + + m_apiMenu.addActions(m_apiGroup.actions()); + m_msaaMenu.addActions(m_msaaGroup.actions()); + m_anisoMenu.addActions(m_anisoGroup.actions()); + addMenu(&m_apiMenu); + addMenu(&m_msaaMenu); + addMenu(&m_anisoMenu); +} + +void LaunchMenu::initApiAction(const QString& action) +{ + QAction* act = m_apiGroup.addAction(action); + connect(act, SIGNAL(triggered()), this, SLOT(apiTriggered())); + act->setCheckable(true); + if (!action.compare(QString::fromStdString(m_commons.getGraphicsApi()), Qt::CaseInsensitive)) + act->setChecked(true); +} + +void LaunchMenu::initMsaaAction(const QString& action) +{ + QAction* act = m_msaaGroup.addAction(action); + connect(act, SIGNAL(triggered()), this, SLOT(msaaTriggered())); + act->setCheckable(true); + if (!action.compare(QString::number(m_commons.getSamples()), Qt::CaseInsensitive)) + act->setChecked(true); +} + +void LaunchMenu::initAnisoAction(const QString& action) +{ + QAction* act = m_anisoGroup.addAction(action); + connect(act, SIGNAL(triggered()), this, SLOT(anisoTriggered())); + act->setCheckable(true); + if (!action.compare(QString::number(m_commons.getAnisotropy()), Qt::CaseInsensitive)) + act->setChecked(true); +} + +void LaunchMenu::apiTriggered() +{ + m_commons.setGraphicsApi(qobject_cast(sender())->text().toStdString()); + m_commons.serialize(); +} + +void LaunchMenu::msaaTriggered() +{ + m_commons.setSamples(qobject_cast(sender())->text().toInt()); + m_commons.serialize(); +} + +void LaunchMenu::anisoTriggered() +{ + m_commons.setAnisotropy(qobject_cast(sender())->text().toInt()); + m_commons.serialize(); +} diff --git a/hecl-gui/LaunchMenu.hpp b/hecl-gui/LaunchMenu.hpp new file mode 100644 index 000000000..25a916420 --- /dev/null +++ b/hecl-gui/LaunchMenu.hpp @@ -0,0 +1,33 @@ +#ifndef GUI_LAUNCHMENU_HPP +#define GUI_LAUNCHMENU_HPP + +#include +namespace hecl { class CVarCommons; } + +class LaunchMenu : public QMenu +{ + Q_OBJECT + hecl::CVarCommons& m_commons; + + QMenu m_apiMenu; + QMenu m_msaaMenu; + QMenu m_anisoMenu; + + QActionGroup m_apiGroup; + QActionGroup m_msaaGroup; + QActionGroup m_anisoGroup; + + void initApiAction(const QString& action); + void initMsaaAction(const QString& action); + void initAnisoAction(const QString& action); + +public: + LaunchMenu(hecl::CVarCommons& commons, QWidget* parent = Q_NULLPTR); + +public slots: + void apiTriggered(); + void msaaTriggered(); + void anisoTriggered(); +}; + +#endif // GUI_LAUNCHMENU_HPP diff --git a/hecl-gui/MainWindow.cpp b/hecl-gui/MainWindow.cpp index 231956f03..1517a0d8b 100644 --- a/hecl-gui/MainWindow.cpp +++ b/hecl-gui/MainWindow.cpp @@ -4,17 +4,68 @@ #include #include "EscapeSequenceParser.hpp" #include "FileDirDialog.hpp" +#include "ExtractZip.hpp" #if _WIN32 #include #include +#include + +static void KillProcessTree(QProcess& proc) +{ + Q_PID pid = proc.pid(); + if (!pid) + return; + + DWORD myprocID = pid->dwProcessId; + PROCESSENTRY32 pe = {}; + pe.dwSize = sizeof(PROCESSENTRY32); + + HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (::Process32First(hSnap, &pe)) + { + BOOL bContinue = TRUE; + + // kill child processes + while (bContinue) + { + // only kill child processes + if (pe.th32ParentProcessID == myprocID) + { + HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); + + if (hChildProc) + { + ::TerminateProcess(hChildProc, 1); + ::CloseHandle(hChildProc); + } + } + + bContinue = ::Process32Next(hSnap, &pe); + } + } + + proc.close(); + proc.terminate(); +} +#else +static void KillProcessTree(QProcess& proc) +{ + proc.close(); + proc.terminate(); +} #endif MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - m_ui(new Ui::MainWindow) + QMainWindow(parent) + , m_fileMgr(_S("urde")) + , m_cvarManager(m_fileMgr) + , m_cvarCommons(m_cvarManager) + , m_ui(new Ui::MainWindow) , m_heclProc(this) , m_dlManager(this) + , m_launchMenu(m_cvarCommons, this) , m_settings("AxioDL", "HECL", this) { m_ui->setupUi(this); @@ -41,7 +92,7 @@ MainWindow::MainWindow(QWidget *parent) : m_dlManager.connectWidgets(m_ui->downloadProgressBar, m_ui->downloadErrorLabel, std::bind(&MainWindow::onIndexDownloaded, this, std::placeholders::_1), std::bind(&MainWindow::onBinaryDownloaded, this, std::placeholders::_1), - std::bind(&MainWindow::onBinaryFailed, this, std::placeholders::_1)); + std::bind(&MainWindow::onBinaryFailed, this)); initSlots(); @@ -52,8 +103,7 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { - m_heclProc.close(); - m_heclProc.terminate(); + KillProcessTree(m_heclProc); delete m_ui; } @@ -67,14 +117,17 @@ void MainWindow::onExtract() return; m_ui->processOutput->clear(); - m_heclProc.close(); - m_heclProc.terminate(); + KillProcessTree(m_heclProc); m_heclProc.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); m_heclProc.setWorkingDirectory(m_path); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("TERM", "xterm-color"); + env.insert("ConEmuANSI", "ON"); m_heclProc.setProcessEnvironment(env); - m_heclProc.start(m_heclPath, {"extract", "-y", "-o", m_path, imgPath}); + disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); + connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onExtractFinished(int))); + m_heclProc.start(m_heclPath, {"extract", "-y", "-g", "-o", m_path, imgPath}, + QIODevice::ReadOnly | QIODevice::Unbuffered); m_ui->heclTabs->setCurrentIndex(0); @@ -83,13 +136,12 @@ void MainWindow::onExtract() m_ui->extractBtn->setEnabled(true); disconnect(m_ui->extractBtn, SIGNAL(clicked()), nullptr, nullptr); connect(m_ui->extractBtn, SIGNAL(clicked()), this, SLOT(doHECLTerminate())); - - disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); - connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onExtractFinished(int))); } void MainWindow::onExtractFinished(int returnCode) { + m_cursor.movePosition(QTextCursor::End); + m_cursor.insertBlock(); disconnect(m_ui->extractBtn, SIGNAL(clicked()), nullptr, nullptr); connect(m_ui->extractBtn, SIGNAL(clicked()), this, SLOT(onExtract())); checkDownloadedBinary(); @@ -100,14 +152,17 @@ void MainWindow::onPackage() if (m_path.isEmpty()) return; m_ui->processOutput->clear(); - m_heclProc.close(); - m_heclProc.terminate(); + KillProcessTree(m_heclProc); m_heclProc.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); m_heclProc.setWorkingDirectory(m_path); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("TERM", "xterm-color"); + env.insert("ConEmuANSI", "ON"); m_heclProc.setProcessEnvironment(env); - m_heclProc.start(m_heclPath, {"package", "-y"}); + disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); + connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onPackageFinished(int))); + m_heclProc.start(m_heclPath, {"package", "-y", "-g"}, + QIODevice::ReadOnly | QIODevice::Unbuffered); m_ui->heclTabs->setCurrentIndex(0); @@ -116,13 +171,12 @@ void MainWindow::onPackage() m_ui->packageBtn->setEnabled(true); disconnect(m_ui->packageBtn, SIGNAL(clicked()), nullptr, nullptr); connect(m_ui->packageBtn, SIGNAL(clicked()), this, SLOT(doHECLTerminate())); - - disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); - connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onPackageFinished(int))); } void MainWindow::onPackageFinished(int returnCode) { + m_cursor.movePosition(QTextCursor::End); + m_cursor.insertBlock(); disconnect(m_ui->packageBtn, SIGNAL(clicked()), nullptr, nullptr); connect(m_ui->packageBtn, SIGNAL(clicked()), this, SLOT(onPackage())); checkDownloadedBinary(); @@ -133,33 +187,33 @@ void MainWindow::onLaunch() if (m_path.isEmpty()) return; m_ui->processOutput->clear(); - m_heclProc.close(); - m_heclProc.terminate(); + KillProcessTree(m_heclProc); m_heclProc.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); m_heclProc.setWorkingDirectory(m_path); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("TERM", "xterm-color"); + env.insert("ConEmuANSI", "ON"); m_heclProc.setProcessEnvironment(env); - m_heclProc.start(m_urdePath, {"--no-shader-warmup", m_path + "/out"}); + disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); + connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onLaunchFinished(int))); + m_heclProc.start(m_urdePath, {"--no-shader-warmup", m_path + "/out"}, + QIODevice::ReadOnly | QIODevice::Unbuffered); m_ui->heclTabs->setCurrentIndex(0); disableOperations(); - - disconnect(&m_heclProc, SIGNAL(finished(int)), nullptr, nullptr); - connect(&m_heclProc, SIGNAL(finished(int)), this, SLOT(onLaunchFinished(int))); } void MainWindow::onLaunchFinished(int returnCode) { + m_cursor.movePosition(QTextCursor::End); + m_cursor.insertBlock(); checkDownloadedBinary(); } void MainWindow::doHECLTerminate() { - m_heclProc.terminate(); - m_cursor.movePosition(QTextCursor::End); - m_cursor.insertText("\n"); + KillProcessTree(m_heclProc); } void MainWindow::onReturnPressed() @@ -206,37 +260,26 @@ void MainWindow::onUpdateURDEPressed() onDownloadPressed(); } -void MainWindow::onBinaryDownloaded(const QString& file) +void MainWindow::onBinaryDownloaded(QuaZip& file) { - QFileInfo path(file); -#ifndef _WIN32 - QProcess unzip; - unzip.setWorkingDirectory(path.dir().absolutePath()); - unzip.start("unzip", {"-o", path.fileName()}); - unzip.waitForFinished(); - int err = unzip.exitCode(); -#else - SHFILEOPSTRUCT fileOp = {}; - fileOp.wFunc = FO_COPY; - fileOp.pFrom = (path.absoluteFilePath().toStdWString() + L"\\*.*\0\0").c_str(); - fileOp.pTo = (path.absolutePath().toStdWString() + L"\0\0").c_str(); - fileOp.fFlags |= FOF_NOCONFIRMATION; - int err = SHFileOperationW(&fileOp); - if (fileOp.fAnyOperationsAborted) - err = 1; -#endif - QFile::remove(file); + bool err = !ExtractZip::extractDir(file, m_path); + if (err) - m_ui->downloadErrorLabel->setText(QStringLiteral("Error extracting ") + path.fileName()); + m_ui->downloadErrorLabel->setText(QStringLiteral("Error extracting zip")); else m_ui->downloadErrorLabel->setText(QStringLiteral("Download successful"), true); + m_ui->downloadButton->setEnabled(true); checkDownloadedBinary(); + if (!err && m_ui->extractBtn->isEnabled()) m_ui->downloadErrorLabel->setText(QStringLiteral("Download successful - Press 'Extract' to continue."), true); + if (!err && !m_ui->sysReqTable->isBlenderVersionOk()) + m_ui->downloadErrorLabel->setText( + QStringLiteral("Blender 2.78+ must be installed. Please download via Steam or blender.org.")); } -void MainWindow::onBinaryFailed(const QString& file) +void MainWindow::onBinaryFailed() { m_ui->downloadButton->setEnabled(true); checkDownloadedBinary(); @@ -263,6 +306,9 @@ void MainWindow::enableOperations() m_ui->downloadButton->setEnabled(true); + if (m_heclPath.isEmpty()) + return; + m_ui->extractBtn->setText(QStringLiteral("Extract")); m_ui->packageBtn->setText(QStringLiteral("Package")); m_ui->launchBtn->setText(QStringLiteral("Launch")); @@ -275,7 +321,9 @@ void MainWindow::enableOperations() m_ui->launchBtn->setEnabled(true); } - if (m_ui->launchBtn->isEnabled()) + if (!m_ui->sysReqTable->isBlenderVersionOk()) + insertContinueNote("Blender 2.78+ must be installed. Please download via Steam or blender.org."); + else if (m_ui->launchBtn->isEnabled()) insertContinueNote("Package complete - Press 'Launch' to start URDE."); else if (m_ui->packageBtn->isEnabled()) insertContinueNote("Extract complete - Press 'Package' to continue."); @@ -299,7 +347,6 @@ static bool GetDLPackage(const QString& path, QString& dlPackage) bool MainWindow::checkDownloadedBinary() { - disableOperations(); m_updateURDEButton->hide(); m_urdePath = QString(); @@ -309,6 +356,7 @@ bool MainWindow::checkDownloadedBinary() { m_ui->heclTabs->setCurrentIndex(1); m_ui->downloadErrorLabel->setText(QStringLiteral("Set working directory to continue."), true); + enableOperations(); return false; } @@ -351,9 +399,9 @@ bool MainWindow::checkDownloadedBinary() m_ui->currentBinaryLabel->setText(QStringLiteral("unknown -- re-download recommended")); } - enableOperations(); m_urdePath = urdePath; m_heclPath = heclPath; + enableOperations(); return true; } else @@ -361,6 +409,7 @@ bool MainWindow::checkDownloadedBinary() m_ui->currentBinaryLabel->setText(QStringLiteral("none")); m_ui->heclTabs->setCurrentIndex(1); m_ui->downloadErrorLabel->setText(QStringLiteral("Press 'Download' to fetch latest URDE binary."), true); + enableOperations(); } return false; @@ -370,7 +419,7 @@ void MainWindow::setPath(const QString& path) { QFileInfo finfo(path); QString usePath = finfo.absoluteFilePath(); - if (!finfo.exists()) + if (!usePath.isEmpty() && !finfo.exists()) { if (QMessageBox::question(this, QStringLiteral("Make Directory"), QStringLiteral("%1 does not exist. Create it now?").arg(usePath)) == QMessageBox::Yes) @@ -403,22 +452,22 @@ void MainWindow::setPath(const QString& path) m_ui->currentBinaryLabel->setText(QStringLiteral("none")); } + m_ui->sysReqTable->updateFreeDiskSpace(m_path); checkDownloadedBinary(); } void MainWindow::initSlots() { -#ifdef Q_OS_WIN - m_heclProc.setEnvironment(QProcessEnvironment::systemEnvironment().toStringList() + QStringList("ConEmuANSI=ON")); -#endif connect(&m_heclProc, &QProcess::readyRead, [=](){ - setTextTermFormatting(m_heclProc.readAll()); + QByteArray bytes = m_heclProc.readAll(); + setTextTermFormatting(bytes); m_ui->processOutput->ensureCursorVisible(); }); connect(m_ui->extractBtn, SIGNAL(clicked()), this, SLOT(onExtract())); connect(m_ui->packageBtn, SIGNAL(clicked()), this, SLOT(onPackage())); connect(m_ui->launchBtn, SIGNAL(clicked()), this, SLOT(onLaunch())); + m_ui->launchMenuBtn->setMenu(&m_launchMenu); connect(m_ui->browseBtn, &QPushButton::clicked, [=]() { FileDirDialog dialog(this); @@ -437,80 +486,6 @@ void MainWindow::initSlots() connect(m_ui->downloadButton, SIGNAL(clicked()), this, SLOT(onDownloadPressed())); } -static void ReturnInsert(QTextCursor& cur, const QString& text) -{ - auto DoLine = [&](const QString& line) - { - auto DoReturn = [&](const QString& ret) - { - if (!ret.isEmpty()) - { - cur.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ret.size()); - cur.insertText(ret); - } - }; - QStringList list = line.split('\r'); - DoReturn(list.front()); - if (list.size() > 1) - { - for (auto it = list.begin() + 1; it != list.end(); ++it) - { - cur.movePosition(QTextCursor::StartOfLine); - DoReturn(*it); - } - } - }; - - QStringList lineSplit = text.split('\n'); - DoLine(lineSplit.front()); - if (lineSplit.size() > 1) - { - for (auto it = lineSplit.begin() + 1; it != lineSplit.end(); ++it) - { - cur.movePosition(QTextCursor::EndOfLine); - cur.insertText("\n"); - DoLine(*it); - } - } -} - -static void ReturnInsert(QTextCursor& cur, const QString& text, const QTextCharFormat& format) -{ - auto DoLine = [&](const QString& line) - { - auto DoReturn = [&](const QString& ret) - { - if (!ret.isEmpty()) - { - cur.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ret.size()); - cur.insertText(ret, format); - } - }; - QStringList list = line.split('\r'); - DoReturn(list.front()); - if (list.size() > 1) - { - for (auto it = list.begin() + 1; it != list.end(); ++it) - { - cur.movePosition(QTextCursor::StartOfLine); - DoReturn(*it); - } - } - }; - - QStringList lineSplit = text.split('\n'); - DoLine(lineSplit.front()); - if (lineSplit.size() > 1) - { - for (auto it = lineSplit.begin() + 1; it != lineSplit.end(); ++it) - { - cur.movePosition(QTextCursor::EndOfLine); - cur.insertText("\n", format); - DoLine(*it); - } - } -} - void MainWindow::setTextTermFormatting(const QString& text) { m_inContinueNote = false; @@ -552,5 +527,6 @@ void MainWindow::insertContinueNote(const QString& text) m_cursor.movePosition(QTextCursor::End); QTextCharFormat textCharFormat = m_cursor.charFormat(); textCharFormat.setForeground(QColor(0,255,0)); - m_cursor.insertText(text + '\n', textCharFormat); + m_cursor.insertText(text, textCharFormat); + m_cursor.insertBlock(); } diff --git a/hecl-gui/MainWindow.hpp b/hecl-gui/MainWindow.hpp index 90b8e3d7a..985652a6c 100644 --- a/hecl-gui/MainWindow.hpp +++ b/hecl-gui/MainWindow.hpp @@ -6,10 +6,14 @@ #include #include #include "DownloadManager.hpp" +#include "LaunchMenu.hpp" #include "Common.hpp" +#include "hecl/Runtime.hpp" +#include "hecl/CVarCommons.hpp" class QTextEdit; class QTextCharFormat; class QPushButton; +class QuaZip; namespace Ui { class MainWindow; @@ -18,6 +22,9 @@ class MainWindow; class MainWindow : public QMainWindow { Q_OBJECT + hecl::Runtime::FileStoreManager m_fileMgr; + hecl::CVarManager m_cvarManager; + hecl::CVarCommons m_cvarCommons; Ui::MainWindow* m_ui; QTextCursor m_cursor; QString m_path; @@ -25,6 +32,7 @@ class MainWindow : public QMainWindow QString m_heclPath; QProcess m_heclProc; DownloadManager m_dlManager; + LaunchMenu m_launchMenu; QSettings m_settings; URDEVersion m_recommendedVersion; QPushButton* m_updateURDEButton; @@ -50,8 +58,8 @@ private: void setPath(const QString& path); void initSlots(); void onIndexDownloaded(const QStringList& index); - void onBinaryDownloaded(const QString& file); - void onBinaryFailed(const QString& file); + void onBinaryDownloaded(QuaZip& file); + void onBinaryFailed(); void disableOperations(); void enableOperations(); }; diff --git a/hecl-gui/MainWindow.ui b/hecl-gui/MainWindow.ui index a38220a84..9328e7760 100644 --- a/hecl-gui/MainWindow.ui +++ b/hecl-gui/MainWindow.ui @@ -21,6 +21,74 @@ + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + 16777215 + 16777215 + + + + &Launch + + + + + + + + 0 + 0 + + + + + 15 + 16777215 + + + + + + + + + + @@ -34,10 +102,10 @@ - + - 0 + 1 @@ -216,7 +284,7 @@ false - + 0 0 @@ -637,7 +705,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.14286pt; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Noto Sans'; font-size:10pt; font-weight:600;">About HECL Frontend</span></p> <p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Noto Sans'; font-size:10pt;"><br />The hecl frontend UI is designed and built by </span><a href="https://axiodl.com"><span style=" font-family:'Noto Sans'; font-size:10pt; text-decoration: underline; color:#007af4;">Axiomatic Data Laboratories</span></a><span style=" font-family:'Noto Sans'; font-size:10pt;"> Copyright 2018<br /><br /></span><span style=" font-family:'Noto Sans'; font-size:10pt; font-weight:600;">Authors:</span><span style=" font-family:'Noto Sans'; font-size:10pt;"><br />Phillip &quot;Antidote&quot; Stephens<br />Jack &quot;jackoalan&quot; Andersen</span></p> <p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New'; font-size:10pt;">The MIT License</span></p> @@ -665,6 +733,12 @@ p, li { white-space: pre-wrap; } false + + + 16777215 + 16777215 + + &Extract @@ -675,22 +749,18 @@ p, li { white-space: pre-wrap; } false + + + 16777215 + 16777215 + + &Package - - - - false - - - &Launch - - - - + Qt::Horizontal @@ -703,7 +773,7 @@ p, li { white-space: pre-wrap; } - + @@ -760,7 +830,6 @@ p, li { white-space: pre-wrap; } - diff --git a/hecl-gui/SysReqTableView.cpp b/hecl-gui/SysReqTableView.cpp index 461fc6c63..9c7ebd550 100644 --- a/hecl-gui/SysReqTableView.cpp +++ b/hecl-gui/SysReqTableView.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include "FindBlender.hpp" #if _WIN32 #include @@ -84,6 +86,22 @@ SysReqTableModel::SysReqTableModel(QObject* parent) m_cpuSpeedStr.sprintf("%g GHz", speed); } } +#elif _WIN32 + HKEY hkey; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + _S("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), + 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) + { + DWORD MHz; + DWORD size = sizeof(MHz); + if (RegQueryValueEx(hkey, _S("~MHz"), nullptr, nullptr, + (LPBYTE)&MHz, &size) == ERROR_SUCCESS) + { + m_cpuSpeed = uint64_t(MHz); + m_cpuSpeedStr.sprintf("%1.1f GHz", MHz / 1000.f); + } + } + RegCloseKey(hkey); #else /* This only works for Skylake+ */ int regs[4] = {}; @@ -115,11 +133,32 @@ SysReqTableModel::SysReqTableModel(QObject* parent) #elif __linux__ m_osVersion = QStringLiteral("Linux"); #endif + hecl::blender::FindBlender(m_blendMajor, m_blendMinor); + if (m_blendMajor) + m_blendVersionStr = QStringLiteral("Blender ") + QString::number(m_blendMajor) + + '.' + QString::number(m_blendMinor); + else + m_blendVersionStr = QStringLiteral("Not Found"); +} + +void SysReqTableModel::updateFreeDiskSpace(const QString& path) +{ + if (path.isEmpty()) + { + m_freeDiskSpace = 0; + m_freeDiskSpaceStr = QStringLiteral(""); + } + else + { + m_freeDiskSpace = QStorageInfo(path).bytesFree(); + m_freeDiskSpaceStr.sprintf("%1.1f GB", m_freeDiskSpace / 1000.f / 1000.f / 1000.f); + } + emit dataChanged(index(3, 0), index(3, 0)); } int SysReqTableModel::rowCount(const QModelIndex& parent) const { - return 5; + return 7; } int SysReqTableModel::columnCount(const QModelIndex& parent) const @@ -145,6 +184,8 @@ QVariant SysReqTableModel::data(const QModelIndex& index, int role) const case 2: return m_memorySize >= 0xC0000000; case 3: + return m_freeDiskSpace >= qint64(16) * 1000 * 1000 * 1000; + case 4: #ifdef __APPLE__ return m_macosMajor > 10 || m_macosMinor >= 9; #elif defined(_WIN32) @@ -152,6 +193,8 @@ QVariant SysReqTableModel::data(const QModelIndex& index, int role) const #else return true; #endif + case 5: + return isBlenderVersionOk(); } } else @@ -172,6 +215,8 @@ QVariant SysReqTableModel::data(const QModelIndex& index, int role) const case 2: return QStringLiteral("3 GB"); case 3: + return QStringLiteral("16 GB (MP1)"); + case 4: #ifdef __APPLE__ return QStringLiteral("macOS 10.9"); #elif defined(_WIN32) @@ -181,6 +226,8 @@ QVariant SysReqTableModel::data(const QModelIndex& index, int role) const #else return {}; #endif + case 5: + return QStringLiteral("Blender 2.78"); } } else if (index.column() == 1) @@ -199,7 +246,11 @@ QVariant SysReqTableModel::data(const QModelIndex& index, int role) const case 2: return m_memorySizeStr; case 3: + return m_freeDiskSpaceStr; + case 4: return m_osVersion; + case 5: + return m_blendVersionStr; } } } @@ -236,8 +287,12 @@ QVariant SysReqTableModel::headerData(int section, Qt::Orientation orientation, case 2: return QStringLiteral("Memory"); case 3: - return QStringLiteral("OS"); + return QStringLiteral("Disk Space"); case 4: + return QStringLiteral("OS"); + case 5: + return QStringLiteral("Blender"); + case 6: return QStringLiteral("Vector ISA"); } } @@ -248,9 +303,17 @@ void SysReqTableView::paintEvent(QPaintEvent* e) int tableWidth = columnWidth(0) + columnWidth(1); int tableX = verticalHeader()->width() + columnViewportPosition(0); int tableY = horizontalHeader()->height(); - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 6; ++i) { QWidget* w = std::get<0>(m_backgroundWidgets[i]); + + QPalette pal = palette(); + if (m_model.data(m_model.index(i, 0), Qt::UserRole).toBool()) + pal.setColor(QPalette::Background, QColor::fromRgbF(0.f, 1.f, 0.f, 0.2f)); + else + pal.setColor(QPalette::Background, QColor::fromRgbF(1.f, 0.f, 0.f, 0.2f)); + w->setPalette(pal); + QSequentialAnimationGroup* animation = std::get<1>(m_backgroundWidgets[i]); QPropertyAnimation* pAnimation = static_cast(animation->animationAt(1)); bool& running = std::get<2>(m_backgroundWidgets[i]); @@ -274,15 +337,15 @@ SysReqTableView::SysReqTableView(QWidget* parent) : QTableView(parent), m_vectorISATable(this) { setModel(&m_model); - setIndexWidget(m_model.index(4, 0), &m_vectorISATable); - setSpan(4, 0, 1, 2); + setIndexWidget(m_model.index(6, 0), &m_vectorISATable); + setSpan(6, 0, 1, 2); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); setSelectionMode(QAbstractItemView::SelectionMode::NoSelection); setFocusPolicy(Qt::NoFocus); - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 6; ++i) { QWidget* w = new QWidget(this); std::get<0>(m_backgroundWidgets[i]) = w; diff --git a/hecl-gui/SysReqTableView.hpp b/hecl-gui/SysReqTableView.hpp index 637f21bbb..778689ab2 100644 --- a/hecl-gui/SysReqTableView.hpp +++ b/hecl-gui/SysReqTableView.hpp @@ -13,6 +13,8 @@ class SysReqTableModel : public QAbstractTableModel QString m_cpuSpeedStr; uint64_t m_memorySize = 0; QString m_memorySizeStr; + qint64 m_freeDiskSpace = 0; + QString m_freeDiskSpaceStr = QStringLiteral(""); #if __APPLE__ int m_macosMajor = 0; int m_macosMinor = 0; @@ -21,12 +23,18 @@ class SysReqTableModel : public QAbstractTableModel bool m_win7SP1OrGreater = false; #endif QString m_osVersion; + int m_blendMajor = 0; + int m_blendMinor = 0; + QString m_blendVersionStr; public: SysReqTableModel(QObject* parent = Q_NULLPTR); int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + bool isBlenderVersionOk() const + { return m_blendMajor > 2 || (m_blendMajor == 2 && m_blendMinor >= 78); } + void updateFreeDiskSpace(const QString& path); }; class SysReqTableView : public QTableView @@ -34,7 +42,7 @@ class SysReqTableView : public QTableView Q_OBJECT SysReqTableModel m_model; VectorISATableView m_vectorISATable; - std::tuple m_backgroundWidgets[4] = {}; + std::tuple m_backgroundWidgets[6] = {}; public: SysReqTableView(QWidget* parent = Q_NULLPTR); void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; @@ -45,6 +53,8 @@ public: return v.getArchitecture() == CurArchitecture && v.getPlatform() == CurPlatform && m_vectorISATable.willRun(v.getVectorISA()); } + bool isBlenderVersionOk() const { return m_model.isBlenderVersionOk(); } + void updateFreeDiskSpace(const QString& path) { m_model.updateFreeDiskSpace(path); } }; #endif // GUI_SYSREQTABLEVIEW_HPP diff --git a/hecl-gui/VectorISATableView.cpp b/hecl-gui/VectorISATableView.cpp index 82fdde5a1..d1104c0d8 100644 --- a/hecl-gui/VectorISATableView.cpp +++ b/hecl-gui/VectorISATableView.cpp @@ -6,7 +6,7 @@ void VectorISATableView::paintEvent(QPaintEvent* e) { QTableView* p = static_cast(parent()->parent()); - int tableY = p->horizontalHeader()->height() + p->rowViewportPosition(4); + int tableY = p->horizontalHeader()->height() + p->rowViewportPosition(6); int rHeight = rowHeight(0); for (int i = 0; i < 2; ++i) { @@ -89,7 +89,7 @@ VectorISATableView::VectorISATableView(QWidget* parent) QSequentialAnimationGroup* seq = new QSequentialAnimationGroup(this); std::get<1>(m_backgroundWidgets[i]) = seq; - seq->addPause(4 * 100); + seq->addPause(6 * 100); seq->addAnimation(animation); } } diff --git a/hecl-gui/main.cpp b/hecl-gui/main.cpp index 588c5d109..aab64c300 100644 --- a/hecl-gui/main.cpp +++ b/hecl-gui/main.cpp @@ -28,13 +28,13 @@ int main(int argc, char* argv[]) { InitializePlatform(); - QApplication a(argc, argv); - a.setStyle(QStyleFactory::create("Fusion")); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - a.setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif - a.setAttribute(Qt::AA_UseHighDpiPixmaps); - a.setWindowIcon(MakeAppIcon()); + QApplication::setStyle(QStyleFactory::create("Fusion")); + QApplication a(argc, argv); + QApplication::setWindowIcon(MakeAppIcon()); QPalette darkPalette; darkPalette.setColor(QPalette::Window, QColor(53,53,53)); diff --git a/hecl-gui/platforms/win/hecl-gui.manifest b/hecl-gui/platforms/win/hecl-gui.manifest index 201220079..0baf9bb8e 100644 --- a/hecl-gui/platforms/win/hecl-gui.manifest +++ b/hecl-gui/platforms/win/hecl-gui.manifest @@ -1,6 +1,6 @@ - my exe + HECL-GUI @@ -15,4 +15,9 @@ + + + true/PM + + diff --git a/hecl-gui/quazip b/hecl-gui/quazip new file mode 160000 index 000000000..cf36e59d2 --- /dev/null +++ b/hecl-gui/quazip @@ -0,0 +1 @@ +Subproject commit cf36e59d2ad38bac63447d02f1b1833276099ec0