From 31757b4e31fcf24fb09825c7d29ba97dc6522833 Mon Sep 17 00:00:00 2001 From: Elias Date: Sun, 10 Mar 2019 21:20:55 +0100 Subject: [PATCH] Refactor window Refactor wallpaper convertion --- ScreenPlay/ScreenPlay.pro | 2 + ScreenPlay/main.cpp | 2 +- ScreenPlay/qml/Create/CreateUpload.qml | 2 +- .../CreateWallpaperVideoImportConvert.qml | 73 ++-- .../CreateWallpaper/CreateWallpaperWizard.qml | 13 +- ScreenPlay/qml/Installed/ScreenPlayItem.qml | 30 +- ScreenPlay/qtquickcontrols2.conf | 7 +- ScreenPlay/src/create.cpp | 376 ++---------------- ScreenPlay/src/create.h | 52 +-- ScreenPlay/src/createimportvideo.cpp | 374 +++++++++++++++++ ScreenPlay/src/createimportvideo.h | 77 ++++ ScreenPlay/src/installedlistmodel.cpp | 20 +- ScreenPlay/src/installedlistmodel.h | 4 + ScreenPlay/src/screenplay.cpp | 3 +- ScreenPlay/src/sdkconnector.cpp | 2 +- ScreenPlayWindow/SPWmain.cpp | 50 ++- ScreenPlayWindow/ScreenPlayWindow.pro | 12 +- ScreenPlayWindow/index.html | 9 +- ScreenPlayWindow/qml/main.qml | 215 +++++----- ScreenPlayWindow/src/SPWmainwindow.cpp | 77 ++-- ScreenPlayWindow/src/SPWmainwindow.h | 47 ++- ScreenPlayWindow/src/basewindow.cpp | 72 ++++ ScreenPlayWindow/src/basewindow.h | 163 ++++++++ .../src/windowsdesktopproperties.cpp | 30 ++ .../src/windowsdesktopproperties.h | 120 ++++++ ScreenPlayWindow/src/winwindow.cpp | 136 +++++++ ScreenPlayWindow/src/winwindow.h | 45 +++ 27 files changed, 1374 insertions(+), 639 deletions(-) create mode 100644 ScreenPlay/src/createimportvideo.cpp create mode 100644 ScreenPlay/src/createimportvideo.h create mode 100644 ScreenPlayWindow/src/basewindow.cpp create mode 100644 ScreenPlayWindow/src/basewindow.h create mode 100644 ScreenPlayWindow/src/windowsdesktopproperties.cpp create mode 100644 ScreenPlayWindow/src/windowsdesktopproperties.h create mode 100644 ScreenPlayWindow/src/winwindow.cpp create mode 100644 ScreenPlayWindow/src/winwindow.h diff --git a/ScreenPlay/ScreenPlay.pro b/ScreenPlay/ScreenPlay.pro index abf3ebd0..c62b7805 100644 --- a/ScreenPlay/ScreenPlay.pro +++ b/ScreenPlay/ScreenPlay.pro @@ -10,6 +10,7 @@ TARGETPATH = ScreenPlay ICON = favicon.ico SOURCES += main.cpp \ + src/createimportvideo.cpp \ src/installedlistmodel.cpp \ src/monitorlistmodel.cpp \ src/settings.cpp \ @@ -30,6 +31,7 @@ TRANSLATIONS = translations/ScreenPlay_en.ts \ translations/ScreenPlay_de.ts HEADERS += \ + src/createimportvideo.h \ src/installedlistmodel.h \ src/monitorlistmodel.h \ src/settings.h \ diff --git a/ScreenPlay/main.cpp b/ScreenPlay/main.cpp index 519ef94c..c33e71bd 100644 --- a/ScreenPlay/main.cpp +++ b/ScreenPlay/main.cpp @@ -30,7 +30,6 @@ int main(int argc, char* argv[]) QGuiApplication::setApplicationVersion("0.2.0"); QGuiApplication app(argc, argv); - app.setQuitOnLastWindowClosed(false); app.setWindowIcon(QIcon(":/assets/icons/favicon.ico")); @@ -54,6 +53,7 @@ int main(int argc, char* argv[]) ProfileListModel profileListModel; SDKConnector sdkConnector; + // Create settings in the end because for now it depends on // such things as the profile list model to complete // It will also set the m_absoluteStoragePath in profileListModel and installedListModel diff --git a/ScreenPlay/qml/Create/CreateUpload.qml b/ScreenPlay/qml/Create/CreateUpload.qml index e99c564c..da93a611 100644 --- a/ScreenPlay/qml/Create/CreateUpload.qml +++ b/ScreenPlay/qml/Create/CreateUpload.qml @@ -5,7 +5,7 @@ import Qt.labs.platform 1.0 import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 -import RemoteWorkshopCreationStatus 1.0 +//import RemoteWorkshopCreationStatus 1.0 Item { diff --git a/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml b/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml index 05e4899b..040d9b5a 100644 --- a/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml +++ b/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml @@ -8,6 +8,10 @@ import net.aimber.create 1.0 Item { id: wrapperContent + + + property string customVideoPreviewPath: "" + Text { id: txtHeadline text: qsTr("Convert a video to a wallpaper") @@ -24,7 +28,7 @@ Item { bottomMargin: 0 } } - + Item { id: wrapperLeft width: parent.width * .5 @@ -34,7 +38,7 @@ Item { margins: 30 bottom: parent.bottom } - + Rectangle { id: imgWrapper width: 425 @@ -43,9 +47,9 @@ Item { top: parent.top left: parent.left } - + color: Material.color(Material.Grey) - + AnimatedImage { id: imgPreview asynchronous: true @@ -53,13 +57,13 @@ Item { visible: false anchors.fill: parent } - + BusyIndicator { id: busyIndicator anchors.centerIn: parent running: true } - + Text { id: txtConvertNumber color: "white" @@ -71,7 +75,7 @@ Item { bottomMargin: 40 } } - + Text { id: txtConvert color: "white" @@ -83,10 +87,10 @@ Item { bottomMargin: 20 } } - + Connections { target: screenPlayCreate - + onCreateWallpaperStateChanged: { if (state === Create.State.ConvertingPreviewImageFinished) { imgPreview.source = "file:///" @@ -95,17 +99,17 @@ Item { txtConvert.text = qsTr( "Converting Video preview mp4") } - + if (state === Create.State.ConvertingPreviewVideo) { txtConvert.text = qsTr( "Generating preview video...") } - + if (state === Create.State.ConvertingPreviewGif) { txtConvert.text = qsTr( "Generating preview gif...") } - + if (state === Create.State.ConvertingPreviewGifFinished) { imgPreview.source = "file:///" + screenPlayCreate.workingDir + "/preview.gif" @@ -118,7 +122,7 @@ Item { if (state === Create.State.ConvertingVideo) { txtConvert.text = qsTr("Converting Video...") } - + if (state === Create.State.Finished) { imgSuccess.source = "file:///" + screenPlayCreate.workingDir + "/preview.gif" @@ -138,17 +142,17 @@ Item { anchors { top: imgWrapper.bottom topMargin: 20 - + right: parent.right rightMargin: 30 left: parent.left } - + Rectangle { height: 50 color: "#eeeeee" Layout.fillWidth: true - + Text { id: txtCustomPreviewPath color: "#333333" @@ -159,7 +163,7 @@ Item { leftMargin: 10 } } - + Button { id: button Material.background: Material.Orange @@ -171,14 +175,14 @@ Item { } onClicked: fileDialogOpenFile.open() } - - + + FileDialog { id: fileDialogOpenFile nameFilters: ["*.png *.jpg"] onAccepted: { var file = fileDialogOpenFile.file.toString() - + txtCustomPreviewPath.text = fileDialogOpenFile.file } } @@ -194,12 +198,12 @@ Item { bottom: parent.bottom right: parent.right } - + ColumnLayout { id: column spacing: 0 anchors { - + right: parent.right left: parent.left margins: 30 @@ -208,7 +212,7 @@ Item { bottom: column1.top bottomMargin: 50 } - + TextField { id: textField placeholderText: qsTr("Name") @@ -222,21 +226,21 @@ Item { } } } - + TextField { id: textField1 placeholderText: qsTr("Description") width:parent.width Layout.fillWidth: true } - + TextField { id: textField2 placeholderText: qsTr("Youtube URL") width:parent.width Layout.fillWidth: true } - + TextField { id: textField3 width:parent.width @@ -244,7 +248,7 @@ Item { Layout.fillWidth: true } } - + Row { id: column1 height: 80 @@ -254,19 +258,19 @@ Item { horizontalCenter: parent.horizontalCenter bottom: parent.bottom } - + Button { id: btnExit text: qsTr("Abort") Material.background: Material.Gray Material.foreground: "white" onClicked: { - screenPlayCreate.abort() + screenPlayCreate.abortAndCleanup() utility.setNavigationActive(true) utility.setNavigation("Create") } } - + NextButton { id: btnFinish onClicked: { @@ -279,7 +283,7 @@ Item { } } } - + Connections { target: screenPlayCreate onCreateWallpaperStateChanged: { @@ -290,3 +294,10 @@ Item { } } } + + + +/*##^## Designer { + D{i:0;autoSize:true;height:480;width:640} +} + ##^##*/ diff --git a/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperWizard.qml b/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperWizard.qml index 55581551..00e7d609 100644 --- a/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperWizard.qml +++ b/ScreenPlay/qml/Create/Wizards/CreateWallpaper/CreateWallpaperWizard.qml @@ -13,17 +13,16 @@ Item { property string filePath property bool canNext: false - property int importState: CreateWallpaperWizard.ImportState.Import Component.onCompleted: { state = "in" utility.setNavigationActive(false) - if (importState === CreateWallpaperWizard.ImportState.Import) { - loader_wrapperContent.source - = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImport.qml" - } else { - loader_wrapperContent.source = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml" - } + loader_wrapperContent.source = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml" +// if (importState === CreateWallpaperWizard.ImportState.Import) { +// loader_wrapperContent.source +// = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImport.qml" +// } else { +// } } //Blocks some MouseArea from create page diff --git a/ScreenPlay/qml/Installed/ScreenPlayItem.qml b/ScreenPlay/qml/Installed/ScreenPlayItem.qml index e3f3b377..dafa418d 100644 --- a/ScreenPlay/qml/Installed/ScreenPlayItem.qml +++ b/ScreenPlay/qml/Installed/ScreenPlayItem.qml @@ -177,23 +177,23 @@ Item { bottom: parent.bottom } - Text { - id: text1 - height: 29 - text: screenTitle - anchors { - right: parent.right - left: parent.left - top: parent.top - margins: 10 - } - wrapMode: Text.WordWrap +// Text { +// id: text1 +// height: 29 +// text: screenTitle +// anchors { +// right: parent.right +// left: parent.left +// top: parent.top +// margins: 10 +// } +// wrapMode: Text.WordWrap - color: "#2F2F2F" - font.pointSize: 9 +// color: "#2F2F2F" +// font.pointSize: 9 - font.family: "Segoe UI, Roboto" - } +// font.family: "Segoe UI, Roboto" +// } } } diff --git a/ScreenPlay/qtquickcontrols2.conf b/ScreenPlay/qtquickcontrols2.conf index f6d1ecac..aa446791 100644 --- a/ScreenPlay/qtquickcontrols2.conf +++ b/ScreenPlay/qtquickcontrols2.conf @@ -6,8 +6,5 @@ Theme=Light Accent=Orange Primary=White Foreground=Grey - - -[Material\Font] -Family=Roboto -PixelSize=14 +Font\Family=Roboto +Font\PixelSize=12 diff --git a/ScreenPlay/src/create.cpp b/ScreenPlay/src/create.cpp index d5908f00..6b26726b 100644 --- a/ScreenPlay/src/create.cpp +++ b/ScreenPlay/src/create.cpp @@ -6,9 +6,8 @@ Create::Create(Settings* st, QMLUtilities* util, QObject* parent) m_settings = st; m_utils = util; - m_futureWatcher.setFuture(m_future); - qRegisterMetaType(); + qRegisterMetaType(); qmlRegisterType("net.aimber.create", 1, 0, "Create"); } @@ -33,17 +32,19 @@ bool Create::copyRecursively(const QString& srcFilePath, const QString& tgtFileP targetDir.cdUp(); QDir sourceDir(srcFilePath); QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); - foreach (const QString& fileName, fileNames) { + Q_FOREACH (const QString& fileName, fileNames) { const QString newSrcFilePath = srcFilePath + QLatin1Char('/') + fileName; const QString newTgtFilePath = tgtFilePath + QLatin1Char('/') + fileName; - if (!copyRecursively(newSrcFilePath, newTgtFilePath)) + if (!copyRecursively(newSrcFilePath, newTgtFilePath)) { return false; + } } } else { - if (!QFile::copy(srcFilePath, tgtFilePath)) + if (!QFile::copy(srcFilePath, tgtFilePath)) { return false; + } } return true; } @@ -52,365 +53,44 @@ void Create::createWallpaperStart(QString videoPath) { videoPath.remove("file:///"); - m_future = QtConcurrent::run([=]() { - QDir dir; - dir.cd(this->m_settings->localStoragePath().toLocalFile()); + QDir dir; + dir.cd(this->m_settings->localStoragePath().toLocalFile()); - m_createWallpaperData.videoPath = videoPath; + m_createWallpaperData.videoPath = videoPath; - // Create a temp dir so we can later alter it to the workshop id - auto folderName = QString("_tmp_" + QTime::currentTime().toString()).replace(":", ""); + // Create a temp dir so we can later alter it to the workshop id + auto folderName = QString("_tmp_" + QTime::currentTime().toString()).replace(":", ""); - if (!dir.mkdir(folderName)) - return; - - m_createWallpaperData.exportPath = dir.path() + "/" + folderName; - this->setWorkingDir(m_createWallpaperData.exportPath); - - // If we return early/false this means the creation - // process did not work - bool ok = true; - - if (!this->createWallpaperInfo()) - ok = false; - - if (ok && !this->createWallpaperVideoPreview()) - ok = false; - - if (ok && !this->createWallpaperGifPreview()) - ok = false; - - if (ok && !this->extractWallpaperAudio()) - ok = false; - - if (!ok) - abortAndCleanup(); - }); -} - -bool Create::createWallpaperInfo() -{ - // Get video info - QStringList args; - args.append("-print_format"); - args.append("json"); - args.append("-show_format"); - args.append("-show_streams"); - args.append(m_createWallpaperData.videoPath); - QScopedPointer pro(new QProcess()); - pro.data()->setArguments(args); - -#ifdef Q_OS_WIN - pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe.exe"); -#endif -#ifdef Q_OS_MACOS - pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe"); -#endif - - pro.data()->start(); - emit createWallpaperStateChanged(Create::State::AnalyseVideo); - pro.data()->waitForFinished(-1); - emit createWallpaperStateChanged(Create::State::AnalyseVideoFinished); - QJsonObject obj; - QJsonParseError err; - QJsonDocument doc = QJsonDocument::fromJson(pro.data()->readAll(), &err); - if (err.error != QJsonParseError::NoError) { - emit processOutput("Error parsing ffmpeg json output"); - emit createWallpaperStateChanged(Create::State::AnalyseVideoError); - return false; + if (!dir.mkdir(folderName)) { + emit abortCreateWallpaper(); + return; } - obj = doc.object(); - pro.data()->close(); + m_createWallpaperData.exportPath = dir.path() + "/" + folderName; + setWorkingDir(m_createWallpaperData.exportPath); - // Get video length - QJsonObject objFormat = obj.value("format").toObject(); - bool okParseDuration = false; - auto tmpLength = objFormat.value("duration").toVariant().toFloat(&okParseDuration); - - if (!okParseDuration) { - qDebug() << "Error parsing video length"; - emit processOutput("Error parsing video length"); - emit createWallpaperStateChanged(Create::State::AnalyseVideoError); - return false; - } - - int length = 0; - length = static_cast(tmpLength); - m_createWallpaperData.length = length; - - // Get framerate - QJsonArray arrSteams = obj.value("streams").toArray(); - if (arrSteams.size() < 1) { - qDebug() << "Error container does not have any video streams"; - return false; - } - - QJsonObject tmpObjStreams = arrSteams.at(0).toObject(); - - // The paramter gets us the exact framerate - // "avg_frame_rate":"47850000/797509" - // so we need no calc the value by dividing the two numbers - QString avgFrameRate = tmpObjStreams.value("avg_frame_rate").toVariant().toString(); - - QStringList avgFrameRateList = avgFrameRate.split('/', QString::SkipEmptyParts); - if (avgFrameRateList.length() != 2) { - qDebug() << "Error could not parse streams with length: " << avgFrameRateList.length(); - return false; - } - - int framerate = 0; - float value1 = static_cast(avgFrameRateList.at(0).toInt()); - float value2 = static_cast(avgFrameRateList.at(1).toInt()); - - framerate = qCeil(value1 / value2); - m_createWallpaperData.framerate = framerate; - - return true; -} - -bool Create::createWallpaperVideoPreview() -{ - - QStringList args; - args.append("-loglevel"); - args.append("error"); - args.append("-y"); - args.append("-stats"); - args.append("-i"); - args.append(m_createWallpaperData.videoPath); - args.append("-speed"); - args.append("ultrafast"); - args.append("-vf"); - // We allways want to have a 5 second clip via 24fps -> 120 frames - // Divided by the number of frames we can skip (timeInSeconds * Framrate) - // scale & crop parameter: https://unix.stackexchange.com/a/284731 - args.append("select='not(mod(n," + QString::number((m_createWallpaperData.length / 5)) + "))',setpts=N/FRAME_RATE/TB,crop=in_h*16/9:in_h,scale=-2:400"); - // Disable audio - args.append("-an"); - args.append(m_createWallpaperData.exportPath + "/preview.webm"); - QScopedPointer proConvertPreviewWebM(new QProcess()); - - proConvertPreviewWebM.data()->setArguments(args); -#ifdef Q_OS_WIN - proConvertPreviewWebM.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); -#endif -#ifdef Q_OS_MACOS - proConvertPreviewMP4.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); -#endif - emit createWallpaperStateChanged(Create::State::ConvertingPreviewVideo); - - connect(this, &Create::abortCreateWallpaper, proConvertPreviewWebM.data(), &QProcess::kill); - proConvertPreviewWebM.data()->start(); - while (!proConvertPreviewWebM.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit - { - QCoreApplication::processEvents(); - } - disconnect(this, &Create::abortCreateWallpaper, proConvertPreviewWebM.data(), &QProcess::kill); - QString tmpErr = proConvertPreviewWebM.data()->readAllStandardError(); - if (!tmpErr.isEmpty()) { - QFile previewVideo(m_createWallpaperData.exportPath + "/preview.webm"); - if (!previewVideo.exists() && !(previewVideo.size() > 0)) { - emit processOutput(tmpErr); - emit createWallpaperStateChanged(Create::State::ConvertingPreviewVideoError); - return false; - } - } - - this->processOutput(proConvertPreviewWebM.data()->readAll()); - proConvertPreviewWebM.data()->close(); - - emit createWallpaperStateChanged(Create::State::ConvertingPreviewVideoFinished); - - return true; -} - -bool Create::createWallpaperGifPreview() -{ - - emit createWallpaperStateChanged(Create::State::ConvertingPreviewGif); - - QStringList args; - args.append("-y"); - args.append("-stats"); - args.append("-i"); - args.append(m_createWallpaperData.exportPath + "/preview.webm"); - args.append("-filter_complex"); - args.append("[0:v] fps=12,scale=w=480:h=-1,split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1"); - args.append(m_createWallpaperData.exportPath + "/preview.gif"); - - QScopedPointer proConvertGif(new QProcess()); - proConvertGif.data()->setArguments(args); -#ifdef Q_OS_WIN - proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); -#endif -#ifdef Q_OS_MACOS - proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); -#endif - connect(this, &Create::abortCreateWallpaper, proConvertGif.data(), &QProcess::kill); - proConvertGif.data()->start(); - while (!proConvertGif.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit - { - QCoreApplication::processEvents(); - } - disconnect(this, &Create::abortCreateWallpaper, proConvertGif.data(), &QProcess::kill); - QString tmpErrGif = proConvertGif.data()->readAllStandardError(); - if (!tmpErrGif.isEmpty()) { - QFile previewGif(m_createWallpaperData.exportPath + "/preview.gif"); - if (!previewGif.exists() && !(previewGif.size() > 0)) { - emit createWallpaperStateChanged(Create::State::ConvertingPreviewGifError); - return false; - } - } - - this->processOutput(proConvertGif.data()->readAll()); - proConvertGif.data()->close(); - emit createWallpaperStateChanged(Create::State::ConvertingPreviewGifFinished); - - return true; -} - -bool Create::createWallpaperImagePreview() -{ - - emit createWallpaperStateChanged(Create::State::ConvertingPreviewImage); - - QStringList args; - args.clear(); - args.append("-y"); - args.append("-stats"); - args.append("-ss"); - args.append("00:00:02"); - args.append("-i"); - args.append(m_createWallpaperData.videoPath); - args.append("-vframes"); - args.append("1"); - args.append("-q:v"); - args.append("2"); - args.append(m_createWallpaperData.exportPath + "/preview.png"); - - QScopedPointer proConvertImage(new QProcess()); - proConvertImage.data()->setArguments(args); -#ifdef Q_OS_WIN - proConvertImage.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); -#endif - -#ifdef Q_OS_MACOS - proConvertImage.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); -#endif - proConvertImage.data()->start(); - - while (!proConvertImage.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit - { - QCoreApplication::processEvents(); - } - QString tmpErrImg = proConvertImage.data()->readAllStandardError(); - if (!tmpErrImg.isEmpty()) { - QFile previewImg(m_createWallpaperData.exportPath + "/preview.png"); - if (!previewImg.exists() && !(previewImg.size() > 0)) { - emit createWallpaperStateChanged(Create::State::ConvertingPreviewImageError); - return false; - } - } - - this->processOutput(proConvertImage.data()->readAll()); - proConvertImage.data()->close(); - emit createWallpaperStateChanged(Create::State::ConvertingPreviewImageFinished); - - return true; -} - -bool Create::extractWallpaperAudio() -{ - - emit createWallpaperStateChanged(Create::State::ConvertingAudio); - - QStringList args; - args.clear(); - args.append("-y"); - args.append("-stats"); - args.append("-i"); - args.append(m_createWallpaperData.videoPath); - args.append("-f"); - args.append("mp3"); - args.append("-ab"); - args.append("192000"); - args.append("-vn"); - args.append(m_createWallpaperData.exportPath + "/audio.mp3"); - - QScopedPointer proConvertAudio(new QProcess()); - proConvertAudio.data()->setArguments(args); -#ifdef Q_OS_WIN - proConvertAudio.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); -#endif -#ifdef Q_OS_MACOS - pro.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); -#endif - - connect(this, &Create::abortCreateWallpaper, proConvertAudio.data(), &QProcess::kill); - proConvertAudio.data()->start(QIODevice::ReadOnly); - while (!proConvertAudio.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit - { - QCoreApplication::processEvents(); - } - disconnect(this, &Create::abortCreateWallpaper, proConvertAudio.data(), &QProcess::kill); - - QString tmpErrImg = proConvertAudio.data()->readAllStandardError(); - if (!tmpErrImg.isEmpty()) { - QFile previewImg(m_createWallpaperData.exportPath + "/audio.mp3"); - if (!previewImg.exists() && !(previewImg.size() > 0)) { - emit createWallpaperStateChanged(Create::State::ConvertingAudioError); - return false; - } - } - - this->processOutput(proConvertAudio.data()->readAll()); - proConvertAudio.data()->close(); - emit createWallpaperStateChanged(Create::State::ConvertingAudioFinished); - - return true; -} - -bool Create::createWallpaperProjectFile(const QString title, const QString description) -{ - //Copy Project File - QFile configFile(m_createWallpaperData.exportPath + "/project.json"); - - if (!configFile.open(QIODevice::ReadWrite | QIODevice::Text)) { - return false; - } - - QTextStream out(&configFile); - QJsonObject configObj; - - configObj.insert("description", description); - configObj.insert("title", title); - - configObj.insert("file", "video.webm"); - configObj.insert("preview", "preview.png"); - configObj.insert("previewGIF", "preview.gif"); - configObj.insert("previewWebM", "preview.webm"); - configObj.insert("type", "video"); - - QJsonDocument configJsonDocument(configObj); - out << configJsonDocument.toJson(); - configFile.close(); - - emit createWallpaperStateChanged(Create::State::Finished); - - return true; + auto* thread = new QThread; + m_createImportVideo = new CreateImportVideo(videoPath, m_createWallpaperData.exportPath); + connect(m_createImportVideo, SIGNAL(error(QString)), this, SLOT(errorString(QString))); + connect(thread, &QThread::started, m_createImportVideo, &CreateImportVideo::process); + connect(m_createImportVideo, &CreateImportVideo::finished, thread, &QThread::quit); + connect(m_createImportVideo, &CreateImportVideo::finished, m_createImportVideo, &QObject::deleteLater); + connect(thread, &QThread::finished, thread, &QObject::deleteLater); + m_createImportVideo->moveToThread(thread); + thread->start(); } void Create::abortAndCleanup() { emit abortCreateWallpaper(); - m_futureWatcher.cancel(); + + QDir exportPath(m_createWallpaperData.exportPath); if (exportPath.exists()) { if (!exportPath.removeRecursively()) { - emit createWallpaperStateChanged(Create::State::AbortCleanupError); + emit createWallpaperStateChanged(CreateImportVideo::State::AbortCleanupError); qWarning() << "Could not delete temp exportPath: " << exportPath; } else { qDebug() << "cleanup " << m_createWallpaperData.exportPath; diff --git a/ScreenPlay/src/create.h b/ScreenPlay/src/create.h index 5c784609..864ed6da 100644 --- a/ScreenPlay/src/create.h +++ b/ScreenPlay/src/create.h @@ -19,8 +19,10 @@ #include #include #include -#include +#include + +#include "createimportvideo.h" #include "qmlutilities.h" #include "settings.h" @@ -33,6 +35,8 @@ struct CreateWallpaperData { int framerate = 0; }; + + class Create : public QObject { Q_OBJECT public: @@ -44,35 +48,6 @@ public: Create() {} ~Create() {} - enum class State { - Idle, - Started, - AnalyseVideo, - AnalyseVideoFinished, - AnalyseVideoError, - ConvertingPreviewVideo, - ConvertingPreviewVideoFinished, - ConvertingPreviewVideoError, - ConvertingPreviewGif, - ConvertingPreviewGifFinished, - ConvertingPreviewGifError, - ConvertingPreviewImage, - ConvertingPreviewImageFinished, - ConvertingPreviewImageError, - ConvertingAudio, - ConvertingAudioFinished, - ConvertingAudioError, -// Oh well... Due to so many patents around video codecs -// the user has to convert the video on his own :( -// ConvertingVideo, -// ConvertingVideoFinished, -// ConvertingVideoError, - AbortCleanupError, - Finished, - ErrorUnknown, - }; - Q_ENUM(State) - QString workingDir() const { return m_workingDir; @@ -84,7 +59,7 @@ public: } signals: - void createWallpaperStateChanged(Create::State state); + void createWallpaperStateChanged(CreateImportVideo::State state); void processOutput(QString text); void workingDirChanged(QString workingDir); void progressChanged(float progress); @@ -93,17 +68,8 @@ signals: public slots: void copyProject(QString relativeProjectPath, QString toPath); bool copyRecursively(const QString& srcFilePath, const QString& tgtFilePath); - - // Steps to convert any video to a wallpaper broken down into - // several methods in this order: - void createWallpaperStart(QString videoPath); - bool createWallpaperInfo(); - bool createWallpaperVideoPreview(); - bool createWallpaperGifPreview(); - bool createWallpaperImagePreview(); - bool extractWallpaperAudio(); - bool createWallpaperProjectFile(const QString title, const QString description); void abortAndCleanup(); + void createWallpaperStart(QString videoPath); void setWorkingDir(QString workingDir) { @@ -124,11 +90,11 @@ public slots: } private: + + CreateImportVideo* m_createImportVideo; Settings* m_settings; QMLUtilities* m_utils; CreateWallpaperData m_createWallpaperData; QString m_workingDir; float m_progress = 0.0f; - QFuture m_future; - QFutureWatcher m_futureWatcher; }; diff --git a/ScreenPlay/src/createimportvideo.cpp b/ScreenPlay/src/createimportvideo.cpp new file mode 100644 index 00000000..30b10f26 --- /dev/null +++ b/ScreenPlay/src/createimportvideo.cpp @@ -0,0 +1,374 @@ +#include "createimportvideo.h" + +CreateImportVideo::CreateImportVideo(QString videoPath, QString exportPath, QObject* parent) + : QObject(parent) +{ + m_videoPath = videoPath; + m_exportPath = exportPath; +} + +void CreateImportVideo::process() +{ + qDebug() << "start converting video" << QThread::currentThreadId(); + createWallpaperInfo(); + createWallpaperVideoPreview(); + createWallpaperGifPreview(); + extractWallpaperAudio(); + emit finished(); +} + +bool CreateImportVideo::createWallpaperInfo() +{ + // Get video info + QStringList args; + args.append("-print_format"); + args.append("json"); + args.append("-show_format"); + args.append("-show_streams"); + args.append(m_videoPath); + QScopedPointer pro(new QProcess()); + pro.data()->setArguments(args); + +#ifdef Q_OS_WIN + pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe.exe"); +#endif +#ifdef Q_OS_MACOS + pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe"); +#endif + + pro.data()->start(); + emit createWallpaperStateChanged(CreateImportVideo::State::AnalyseVideo); + while (!pro.data()->waitForFinished(100)) { + if (QThread::currentThread()->isInterruptionRequested()) { + qDebug() << "Interrupt thread"; + pro.data()->terminate(); + if (!pro.data()->waitForFinished(1000)) { + pro.data()->kill(); + } + break; + } + QCoreApplication::processEvents(); + } + emit createWallpaperStateChanged(CreateImportVideo::State::AnalyseVideoFinished); + QJsonObject obj; + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(pro.data()->readAll(), &err); + if (err.error != QJsonParseError::NoError) { + emit processOutput("Error parsing ffmpeg json output"); + emit createWallpaperStateChanged(CreateImportVideo::State::AnalyseVideoError); + return false; + } + + obj = doc.object(); + pro.data()->close(); + + // Get video length + QJsonObject objFormat = obj.value("format").toObject(); + bool okParseDuration = false; + auto tmpLength = objFormat.value("duration").toVariant().toFloat(&okParseDuration); + + if (!okParseDuration) { + qDebug() << "Error parsing video length"; + emit processOutput("Error parsing video length"); + emit createWallpaperStateChanged(CreateImportVideo::State::AnalyseVideoError); + return false; + } + + int length = 0; + length = static_cast(tmpLength); + m_length = length; + + // Get framerate + QJsonArray arrSteams = obj.value("streams").toArray(); + if (arrSteams.size() < 1) { + qDebug() << "Error container does not have any video streams"; + return false; + } + + QJsonObject tmpObjStreams = arrSteams.at(0).toObject(); + + // The paramter gets us the exact framerate + // "avg_frame_rate":"47850000/797509" + // so we need no calc the value by dividing the two numbers + QString avgFrameRate = tmpObjStreams.value("avg_frame_rate").toVariant().toString(); + + QStringList avgFrameRateList = avgFrameRate.split('/', QString::SkipEmptyParts); + if (avgFrameRateList.length() != 2) { + qDebug() << "Error could not parse streams with length: " << avgFrameRateList.length(); + return false; + } + + int framerate = 0; + double value1 = static_cast(avgFrameRateList.at(0).toInt()); + double value2 = static_cast(avgFrameRateList.at(1).toInt()); + + framerate = qCeil(value1 / value2); + m_framerate = framerate; + + return true; +} + +bool CreateImportVideo::createWallpaperVideoPreview() +{ + + QStringList args; + // args.append("-loglevel"); + // args.append("error"); + // args.append("-y"); + // args.append("-stats"); + args.append("-i"); + args.append(m_videoPath); + + //args.append("-vf"); + // We allways want to have a 5 second clip via 24fps -> 120 frames + // Divided by the number of frames we can skip (timeInSeconds * Framrate) + // scale & crop parameter: https://unix.stackexchange.com/a/284731 + //args.append("select='not(mod(n," + QString::number((m_length / 5)) + "))',setpts=N/FRAME_RATE/TB,crop=in_h*16/9:in_h,scale=-2:400"); + // Disable audio + //args.append("-an"); + args.append(m_exportPath + "/preview.webm"); + QScopedPointer proConvertPreviewWebM(new QProcess()); + + qDebug() << args; + + proConvertPreviewWebM.data()->setArguments(args); +#ifdef Q_OS_WIN + proConvertPreviewWebM.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); +#endif +#ifdef Q_OS_MACOS + proConvertPreviewMP4.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); +#endif + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewVideo); + + //connect(this, &Create::abortCreateWallpaper, proConvertPreviewWebM.data(), &QProcess::kill); + proConvertPreviewWebM.data()->start(); + while (!proConvertPreviewWebM.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit + { + if (QThread::currentThread()->isInterruptionRequested()) { + qDebug() << "Interrupt thread"; + proConvertPreviewWebM.data()->terminate(); + if (!proConvertPreviewWebM.data()->waitForFinished(1000)) { + proConvertPreviewWebM.data()->kill(); + } + break; + } + QCoreApplication::processEvents(); + } + //disconnect(this, &Create::abortCreateWallpaper, proConvertPreviewWebM.data(), &QProcess::kill); + QString tmpErr = proConvertPreviewWebM.data()->readAllStandardError(); + if (!tmpErr.isEmpty()) { + QFile previewVideo(m_exportPath + "/preview.webm"); + if (!previewVideo.exists() && !(previewVideo.size() > 0)) { + emit processOutput(tmpErr); + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewVideoError); + return false; + } + } + + //this->processOutput(proConvertPreviewWebM.data()->readAll()); + proConvertPreviewWebM.data()->close(); + + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewVideoFinished); + + return true; +} + +bool CreateImportVideo::createWallpaperGifPreview() +{ + + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewGif); + + QStringList args; + args.append("-y"); + args.append("-stats"); + args.append("-i"); + args.append(m_exportPath + "/preview.webm"); + args.append("-filter_complex"); + args.append("[0:v] fps=12,scale=w=480:h=-1,split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1"); + args.append(m_exportPath + "/preview.gif"); + + QScopedPointer proConvertGif(new QProcess()); + proConvertGif.data()->setArguments(args); +#ifdef Q_OS_WIN + proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); +#endif +#ifdef Q_OS_MACOS + proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); +#endif + //connect(this, &Create::abortCreateWallpaper, proConvertGif.data(), &QProcess::kill); + proConvertGif.data()->start(); + while (!proConvertGif.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit + { + if (QThread::currentThread()->isInterruptionRequested()) { + qDebug() << "Interrupt thread"; + proConvertGif.data()->terminate(); + if (!proConvertGif.data()->waitForFinished(1000)) { + proConvertGif.data()->kill(); + } + break; + } + QCoreApplication::processEvents(); + } + //disconnect(this, &Create::abortCreateWallpaper, proConvertGif.data(), &QProcess::kill); + QString tmpErrGif = proConvertGif.data()->readAllStandardError(); + if (!tmpErrGif.isEmpty()) { + QFile previewGif(m_exportPath + "/preview.gif"); + if (!previewGif.exists() && !(previewGif.size() > 0)) { + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewGifError); + return false; + } + } + + //this->processOutput(proConvertGif.data()->readAll()); + proConvertGif.data()->close(); + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewGifFinished); + + return true; +} + +bool CreateImportVideo::createWallpaperImagePreview() +{ + + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewImage); + + QStringList args; + args.clear(); + args.append("-y"); + args.append("-stats"); + args.append("-ss"); + args.append("00:00:02"); + args.append("-i"); + args.append(m_videoPath); + args.append("-vframes"); + args.append("1"); + args.append("-q:v"); + args.append("2"); + args.append(m_exportPath + "/preview.png"); + + QScopedPointer proConvertImage(new QProcess()); + proConvertImage.data()->setArguments(args); +#ifdef Q_OS_WIN + proConvertImage.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); +#endif + +#ifdef Q_OS_MACOS + proConvertImage.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); +#endif + proConvertImage.data()->start(); + + while (!proConvertImage.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit + { + if (QThread::currentThread()->isInterruptionRequested()) { + qDebug() << "Interrupt thread"; + proConvertImage.data()->terminate(); + if (!proConvertImage.data()->waitForFinished(1000)) { + proConvertImage.data()->kill(); + } + break; + } + QCoreApplication::processEvents(); + } + QString tmpErrImg = proConvertImage.data()->readAllStandardError(); + if (!tmpErrImg.isEmpty()) { + QFile previewImg(m_exportPath + "/preview.png"); + if (!previewImg.exists() && !(previewImg.size() > 0)) { + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewImageError); + return false; + } + } + + //this->processOutput(proConvertImage.data()->readAll()); + proConvertImage.data()->close(); + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingPreviewImageFinished); + + return true; +} + +bool CreateImportVideo::extractWallpaperAudio() +{ + + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingAudio); + + QStringList args; + args.clear(); + args.append("-y"); + args.append("-stats"); + args.append("-i"); + args.append(m_videoPath); + args.append("-f"); + args.append("mp3"); + args.append("-ab"); + args.append("192000"); + args.append("-vn"); + args.append(m_exportPath + "/audio.mp3"); + + QScopedPointer proConvertAudio(new QProcess()); + proConvertAudio.data()->setArguments(args); +#ifdef Q_OS_WIN + proConvertAudio.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe"); +#endif +#ifdef Q_OS_MACOS + pro.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg"); +#endif + + //connect(this, &Create::abortCreateWallpaper, proConvertAudio.data(), &QProcess::kill); + proConvertAudio.data()->start(QIODevice::ReadOnly); + while (!proConvertAudio.data()->waitForFinished(100)) //Wake up every 100ms and check if we must exit + { + if (QThread::currentThread()->isInterruptionRequested()) { + qDebug() << "Interrupt thread"; + proConvertAudio.data()->terminate(); + if (!proConvertAudio.data()->waitForFinished(1000)) { + proConvertAudio.data()->kill(); + } + break; + } + QCoreApplication::processEvents(); + } + //disconnect(this, &Create::abortCreateWallpaper, proConvertAudio.data(), &QProcess::kill); + + QString tmpErrImg = proConvertAudio.data()->readAllStandardError(); + if (!tmpErrImg.isEmpty()) { + QFile previewImg(m_exportPath + "/audio.mp3"); + if (!previewImg.exists() && !(previewImg.size() > 0)) { + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingAudioError); + return false; + } + } + + //this->processOutput(proConvertAudio.data()->readAll()); + proConvertAudio.data()->close(); + emit createWallpaperStateChanged(CreateImportVideo::State::ConvertingAudioFinished); + + return true; +} + +bool CreateImportVideo::createWallpaperProjectFile(const QString title, const QString description) +{ + //Copy Project File + QFile configFile(m_exportPath + "/project.json"); + + if (!configFile.open(QIODevice::ReadWrite | QIODevice::Text)) { + return false; + } + + QTextStream out(&configFile); + QJsonObject configObj; + + configObj.insert("description", description); + configObj.insert("title", title); + + configObj.insert("file", "video.webm"); + configObj.insert("preview", "preview.png"); + configObj.insert("previewGIF", "preview.gif"); + configObj.insert("previewWebM", "preview.webm"); + configObj.insert("type", "video"); + + QJsonDocument configJsonDocument(configObj); + out << configJsonDocument.toJson(); + configFile.close(); + + emit createWallpaperStateChanged(CreateImportVideo::State::Finished); + + return true; +} diff --git a/ScreenPlay/src/createimportvideo.h b/ScreenPlay/src/createimportvideo.h new file mode 100644 index 00000000..41c5ade0 --- /dev/null +++ b/ScreenPlay/src/createimportvideo.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CreateImportVideo : public QObject { + Q_OBJECT +public: + explicit CreateImportVideo(QString videoPath, QString exportPath, QObject* parent = nullptr); + + enum class State { + Idle, + Started, + AnalyseVideo, + AnalyseVideoFinished, + AnalyseVideoError, + ConvertingPreviewVideo, + ConvertingPreviewVideoFinished, + ConvertingPreviewVideoError, + ConvertingPreviewGif, + ConvertingPreviewGifFinished, + ConvertingPreviewGifError, + ConvertingPreviewImage, + ConvertingPreviewImageFinished, + ConvertingPreviewImageError, + ConvertingAudio, + ConvertingAudioFinished, + ConvertingAudioError, + // Oh well... Due to so many patents around video codecs + // the user has to convert the video on his own :( + // ConvertingVideo, + // ConvertingVideoFinished, + // ConvertingVideoError, + AbortCleanupError, + Finished, + ErrorUnknown, + }; + Q_ENUM(State) + +signals: + void createWallpaperStateChanged(CreateImportVideo::State state); + void processOutput(QString text); + void finished(); + +public slots: + void process(); + void requestInterruption() + { + + } + + bool createWallpaperInfo(); + bool createWallpaperVideoPreview(); + bool createWallpaperGifPreview(); + bool createWallpaperImagePreview(); + bool extractWallpaperAudio(); + bool createWallpaperProjectFile(const QString title, const QString description); + +private: + QProcess m_process; + QString m_videoPath; + QString m_exportPath; + int m_length = 0; + int m_framerate = 0; +}; diff --git a/ScreenPlay/src/installedlistmodel.cpp b/ScreenPlay/src/installedlistmodel.cpp index 320c8b69..39886a6d 100644 --- a/ScreenPlay/src/installedlistmodel.cpp +++ b/ScreenPlay/src/installedlistmodel.cpp @@ -5,6 +5,12 @@ InstalledListModel::InstalledListModel(QObject* parent) { QObject::connect(this, &InstalledListModel::addInstalledItem, this, &InstalledListModel::append, Qt::QueuedConnection); + m_loadScreenWatcher.setFuture(m_loadScreenFuture); + + QObject::connect(&m_loadScreenWatcher, &QFutureWatcher::progressValueChanged, [](int progressValue) { + qDebug() << progressValue; + }); + } int InstalledListModel::rowCount(const QModelIndex& parent) const @@ -76,6 +82,7 @@ bool InstalledListModel::getProjectByAbsoluteStoragePath(QUrl* path, ProjectFile void InstalledListModel::append(const QJsonObject obj, const QString folderName) { + //qDebug() << QThread::currentThreadId(); beginInsertRows(QModelIndex(), m_screenPlayFiles.size(), m_screenPlayFiles.size()); ProjectFile tmpFile(obj, folderName, m_absoluteStoragePath); @@ -86,7 +93,12 @@ void InstalledListModel::append(const QJsonObject obj, const QString folderName) void InstalledListModel::loadInstalledContent() { - QtConcurrent::run([this]() { + if (m_loadScreenWatcher.isRunning()) + qDebug() << "allready running"; + qDebug() << QThread::currentThreadId(); + + m_loadScreenFuture = QtConcurrent::run([this]() { + qDebug() << QThread::currentThreadId(); QJsonDocument jsonProject; QJsonParseError parseError; @@ -126,14 +138,14 @@ void InstalledListModel::loadInstalledContent() obj.insert("type", "video"); } - if (fileEnding.endsWith(".webm") || (obj.value("type").toString() == "qmlScene" || fileEnding.endsWith(".mp4") ) || fileEnding.endsWith(".html")) + if (fileEnding.endsWith(".webm") || (obj.value("type").toString() == "qmlScene" || fileEnding.endsWith(".html"))) emit addInstalledItem(obj, item.baseName()); - if(obj.value("type") == "qmlWidget" || obj.value("type") == "standalonewidget") + if (obj.value("type") == "qmlWidget" || obj.value("type") == "standalonewidget") emit addInstalledItem(obj, item.baseName()); - } } + emit installedLoadingFinished(); }); } diff --git a/ScreenPlay/src/installedlistmodel.h b/ScreenPlay/src/installedlistmodel.h index 9db52001..9c7bf087 100644 --- a/ScreenPlay/src/installedlistmodel.h +++ b/ScreenPlay/src/installedlistmodel.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include /*! @@ -81,4 +83,6 @@ signals: private: QVector m_screenPlayFiles; QUrl m_absoluteStoragePath; + QFuture m_loadScreenFuture; + QFutureWatcher m_loadScreenWatcher; }; diff --git a/ScreenPlay/src/screenplay.cpp b/ScreenPlay/src/screenplay.cpp index 07359a90..0dd043ce 100644 --- a/ScreenPlay/src/screenplay.cpp +++ b/ScreenPlay/src/screenplay.cpp @@ -66,10 +66,11 @@ QString ScreenPlay::generateID() { const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); const int randomStringLength = 32; + auto* radomGen = QRandomGenerator::system(); QString randomString; for (int i = 0; i < randomStringLength; ++i) { - int index = qrand() % possibleCharacters.length(); + int index = radomGen->bounded(possibleCharacters.length()); QChar nextChar = possibleCharacters.at(index); randomString.append(nextChar); } diff --git a/ScreenPlay/src/sdkconnector.cpp b/ScreenPlay/src/sdkconnector.cpp index 3260b913..21b881e5 100644 --- a/ScreenPlay/src/sdkconnector.cpp +++ b/ScreenPlay/src/sdkconnector.cpp @@ -10,7 +10,7 @@ SDKConnector::SDKConnector(QObject* parent) m_server = QSharedPointer(new QLocalServer(this)); connect(m_server.data(), &QLocalServer::newConnection, this, &SDKConnector::newConnection); if (!m_server.data()->listen("ScreenPlay")) { - //TODO + //TODO what to do if no ScreenPlay local socket can be open } } diff --git a/ScreenPlayWindow/SPWmain.cpp b/ScreenPlayWindow/SPWmain.cpp index 753d5779..0fb2087c 100644 --- a/ScreenPlayWindow/SPWmain.cpp +++ b/ScreenPlayWindow/SPWmain.cpp @@ -1,5 +1,9 @@ #include "../ScreenPlaySDK/screenplaysdk.h" -#include "src/SPWmainwindow.h" + +#if defined(Q_OS_WIN) +#include "src/winwindow.h" +#endif + #include #include #include @@ -10,32 +14,46 @@ int main(int argc, char* argv[]) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication a(argc, argv); - ScreenPlaySDK sdk; + QApplication app(argc, argv); + QtWebEngine::initialize(); // 6 parameter + 1 OS working directory default paramter - QStringList argumentList = a.arguments(); + QStringList argumentList = app.arguments(); if (argumentList.length() != 7) { return -3; } - bool canParseMonitorNumber = false; - int monitor = argumentList.at(1).toInt(&canParseMonitorNumber); + ScreenPlaySDK sdk; + sdk.setAppID(argumentList.at(3)); - if (!canParseMonitorNumber) { - return -4; + QString monitorNumbers = argumentList.at(1); + QVector list; + + if (monitorNumbers.length() == 1) { + bool canParseMonitorNumber = false; + int monitor = monitorNumbers.toInt(&canParseMonitorNumber); + list.append(monitor); + } else { + QStringList activeScreensList = monitorNumbers.split(","); + for (QString s : activeScreensList) { + bool ok = false; + int val = s.toInt(&ok); + if (!ok) { + qFatal("Could not parse monitor id to diplay wallpaper"); + } + list.append(val); + } } - // Args: which monitor, (2) path to project, (3)wallpaper secret to identify the connected socket, (4) decoder, (5) volume, (6) fillmode + // Args: which monitor, (2) path to project, (3)wallpaper secret to identify the connected socket, (5) volume // See screenplay.h @ScreenPlayWallpaper constructor how the args get created - //MainWindow w(0,"D:/672870/_tmp_135011","","","",""); - sdk.setAppID(argumentList.at(3)); - qDebug() << "Starting MainWindow: " << argumentList.at(2) << argumentList.at(3) << argumentList.at(4) << argumentList.at(5) << argumentList.at(6); - MainWindow w(monitor, argumentList.at(2), argumentList.at(3), argumentList.at(4), argumentList.at(5), argumentList.at(6)); - QObject::connect(&sdk, &ScreenPlaySDK::sdkDisconnected, &w, &MainWindow::destroyThis); - QObject::connect(&sdk, &ScreenPlaySDK::incommingMessage, &w, &MainWindow::messageReceived); +#if defined(Q_OS_WIN) + WinWindow window = WinWindow(list, argumentList.at(2), argumentList.at(3), argumentList.at(5)); + QObject::connect(&sdk, &ScreenPlaySDK::sdkDisconnected, &window, &WinWindow::destroyThis); + QObject::connect(&sdk, &ScreenPlaySDK::incommingMessage, &window, &WinWindow::messageReceived); +#endif - return a.exec(); + return app.exec(); } diff --git a/ScreenPlayWindow/ScreenPlayWindow.pro b/ScreenPlayWindow/ScreenPlayWindow.pro index ad7f96a4..4c877a7f 100644 --- a/ScreenPlayWindow/ScreenPlayWindow.pro +++ b/ScreenPlayWindow/ScreenPlayWindow.pro @@ -1,16 +1,22 @@ TEMPLATE = app QT += qml quick quickcontrols2 widgets core webengine CONFIG += c++17 -#CONFIG += qtquickcompiler + msvc: LIBS += -luser32 TARGETPATH = ScreenPlayWindow SOURCES += \ SPWmain.cpp \ - src/SPWmainwindow.cpp + #src/SPWmainwindow.cpp \ + src/basewindow.cpp \ + src/windowsdesktopproperties.cpp \ + src/winwindow.cpp HEADERS += \ - src/SPWmainwindow.h + #src/SPWmainwindow.h \ + src/basewindow.h \ + src/windowsdesktopproperties.h \ + src/winwindow.h RESOURCES += \ SPWResources.qrc diff --git a/ScreenPlayWindow/index.html b/ScreenPlayWindow/index.html index 9cf754b2..078c58ff 100644 --- a/ScreenPlayWindow/index.html +++ b/ScreenPlayWindow/index.html @@ -8,14 +8,11 @@ height: 100%; object-fit: fill; overflow: hidden; - background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%); } body, html{ - margin: 0px; - padding: 0px; - overflow: hidden; - background:orange; - + margin: 0px; + padding: 0px; + overflow: hidden; } #errorMsg{ position: fixed; diff --git a/ScreenPlayWindow/qml/main.qml b/ScreenPlayWindow/qml/main.qml index c7c0fb22..d522f16b 100644 --- a/ScreenPlayWindow/qml/main.qml +++ b/ScreenPlayWindow/qml/main.qml @@ -1,23 +1,127 @@ -import QtQuick 2.12 +import QtQuick 2.13 import QtWebEngine 1.8 +import net.aimber.wallpaper 1.0 Rectangle { - id: root anchors.fill: parent + color: desktopProperties.color + + property bool canFadeIn: true + + Component.onCompleted: { + WebEngine.settings.allowRunningInsecureContent = true + WebEngine.settings.accelerated2dCanvasEnabled = true + WebEngine.settings.javascriptCanOpenWindows = false + WebEngine.settings.printElementBackgrounds = false + WebEngine.settings.showScrollBars = false + WebEngine.settings.playbackRequiresUserGesture = false + + switch (window.type) { + case Wallpaper.WallpaperType.Video: + webView.enabled = true + webView.url = Qt.resolvedUrl("file:///" + window.getApplicationPath( + ) + "/index.html") + + break + case Wallpaper.WallpaperType.Html: + webView.enabled = true + webView.url = Qt.resolvedUrl("file:///" + window.fullContentPath) + break + case Wallpaper.WallpaperType.ThreeJSScene: + webView.enabled = true + break + case Wallpaper.WallpaperType.Qml: + loader.source = Qt.resolvedUrl("file:///" + window.fullContentPath) + break + } + fadeIn() + } + + function fadeIn() { + window.setVisible(true) + if (canFadeIn) { + animFadeIn.start() + } else { + imgCover.opacity = 0 + } + } + + WebEngineView { + id: webView + enabled: false + anchors.fill: parent + onLoadProgressChanged: { + if (loadProgress === 100) { + + var src = "" + src += "var videoPlayer = document.getElementById('videoPlayer');" + src += "var videoSource = document.getElementById('videoSource');" + src += "videoSource.src = 'file:///" + window.fullContentPath + "';" + src += "videoPlayer.load();" + src += "videoPlayer.volume = " + window.volume + ";" + src += "videoPlayer.play();" + + webView.runJavaScript(src, function () { + fadeIn() + }) + } + } + } + Loader { + id: loader + anchors.fill: parent + } + + OpacityAnimator { + id: animFadeIn + target: imgCover + from: 1 + to: 0 + duration: 300 + easing.type: Easing.InCubic + running: true + } + + Image { + id: imgCover + anchors.fill: parent + source: Qt.resolvedUrl("file:///" + desktopProperties.wallpaperPath) + Component.onCompleted: { + switch (desktopProperties.wallpaperStyle) { + case 10: + imgCover.fillMode = Image.PreserveAspectCrop + break + case 6: + imgCover.fillMode = Image.PreserveAspectFit + break + case 2: + break + case 0: + if (desktopProperties.isTiled) { + // Tiled + imgCover.fillMode = Image.Tile + } else { + // Center + imgCover.fillMode = Image.PreserveAspectFit + imgCover.anchors.centerIn = parent + imgCover.width = sourceSize.width + imgCover.height = sourceSize.height + } + break + case 22: + canFadeIn = false + break + } + } + } Connections { - target: mainwindow + target: window onQmlExit: { - webView.runJavaScript( "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = 0;") - mainwindow.destroyThis() - } - - onFillModeChanged: { - - //TODO + window.destroyThis() } onQmlSceneValueReceived: { @@ -60,95 +164,4 @@ Rectangle { } } } - - Component.onCompleted: { - - if (mainwindow.type === "qmlScene") { - loader.setSource(Qt.resolvedUrl( - "file:///" + mainwindow.fullContentPath)) - mainwindow.init() - timer.start() - } else if (mainwindow.type === "video") { - webView.visible = true - webView.url = Qt.resolvedUrl( - "file:///" + mainwindow.getApplicationPath( - ) + "/index.html") - } else if (mainwindow.type === "html") { - webView.visible = true - webView.url = Qt.resolvedUrl( - "file:///" + mainwindow.fullContentPath) - mainwindow.init() - timer.start() - } - mainwindow.init() - timer.start() - } - - WebEngineView { - id: webView - anchors.fill: parent - onLoadProgressChanged: { - if (loadProgress === 100) { - runJavaScript((" -var videoPlayer = document.getElementById('videoPlayer'); -var videoSource = document.getElementById('videoSource'); -videoSource.src = \"file:///" + mainwindow.fullContentPath + "\"; -videoPlayer.load(); -videoPlayer.volume = " + mainwindow.volume + ";" + -" videoPlayer.play();"), function (result) { - -}) - } - } - onJavaScriptConsoleMessage: { - print(message) -// runJavaScript(" -// var videoPlayer = document.getElementById('errorMsg'); -// var text = document.createTextNode("+message+"); - -// videoPlayer.appendChild(text); - -//"); - - } - - settings.allowRunningInsecureContent: true - settings.accelerated2dCanvasEnabled: true - settings.javascriptCanOpenWindows: false - settings.printElementBackgrounds: false - settings.showScrollBars: false - settings.playbackRequiresUserGesture: false - } - - Loader { - id: loader - anchors.fill: parent - onStatusChanged: { - print(webViewWrapper.errorString()) - } - } - - Timer { - id: timer - interval: 200 - onTriggered: { - anim.start() - } - } - - Rectangle { - id: curtain - anchors.fill: parent - color: "black" - - PropertyAnimation { - id: anim - property: "opacity" - target: curtain - from: "1" - to: "0" - duration: 300 - } - } - } diff --git a/ScreenPlayWindow/src/SPWmainwindow.cpp b/ScreenPlayWindow/src/SPWmainwindow.cpp index 052eddca..b2287de5 100644 --- a/ScreenPlayWindow/src/SPWmainwindow.cpp +++ b/ScreenPlayWindow/src/SPWmainwindow.cpp @@ -18,14 +18,12 @@ BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam) //for mac https://github.com/silvansky/QtMacApp/search?q=myprivate&unscoped_q=myprivate -MainWindow::MainWindow(int i, QString projectPath, QString id, QString decoder, QString volume, QString fillmode, QScreen* parent) - : QWindow(parent) +MainWindow::MainWindow(int screenAt, QString projectPath, QString id, QString decoder, QString volume, QString fillmode, QWindow* parent) + : QQuickView(parent) { - - m_appID = id; - m_screenNumber.insert(0, i); + m_screenNumber.append(screenAt); setDecoder(decoder); setProjectPath(projectPath); @@ -62,23 +60,23 @@ MainWindow::MainWindow(int i, QString projectPath, QString id, QString decoder, if (m_project.value("type") == "video") { QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; setFullContentPath(tmpPath); - setType("video"); + setWallpaperType("video"); } else if (m_project.value("type") == "scene") { return; } else if (m_project.value("type") == "qmlScene") { QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; setFullContentPath(tmpPath); - setType("qmlScene"); + setWallpaperType("qmlScene"); } else if (m_project.value("type") == "html") { QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; setFullContentPath(tmpPath); - setType("html"); + setWallpaperType("html"); } } // Recalculate window coordiantes because of point (0,0) // Is at the origin monitor or the most left - QScreen* screen = QApplication::screens().at(i); + int offsetX = 0; int offsetY = 0; @@ -92,14 +90,13 @@ MainWindow::MainWindow(int i, QString projectPath, QString id, QString decoder, } } - #ifdef Q_OS_WIN +#ifdef Q_OS_WIN - m_hwnd = (HWND)this->winId(); + m_hwnd = reinterpret_cast(winId()); HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager"); // Spawn new worker window below desktop (using some undocumented Win32 magic) - // See - // https://www.codeproject.com/articles/856020/draw-behind-desktop-icons-in-windows + // See https://www.codeproject.com/articles/856020/draw-behind-desktop-icons-in-windows // for more details const DWORD WM_SPAWN_WORKER = 0x052C; SendMessageTimeoutW(progman_hwnd, WM_SPAWN_WORKER, 0xD, 0x1, SMTO_NORMAL, @@ -113,67 +110,79 @@ MainWindow::MainWindow(int i, QString projectPath, QString id, QString decoder, } //Hide first to avoid flickering - //ShowWindow(m_worker_hwnd, SW_HIDE); + QScreen* screen = QApplication::screens().at(screenAt); + if (screen == nullptr) { + destroyThis(); + } + ShowWindow(m_hwnd, SW_HIDE); - MoveWindow(m_hwnd, screen->geometry().x() + offsetX, screen->geometry().y() + offsetY, screen->size().width(), screen->size().height(), true); SetParent(m_hwnd, m_worker_hwnd); + UpdateWindow(m_hwnd); + UpdateWindow(m_worker_hwnd); + SetWindowLongPtr(m_hwnd, GWL_STYLE, WS_CHILDWINDOW); + SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE ); + // ShowWindow(m_hwnd, SW_SHOW); + //MoveWindow(m_hwnd, screen->geometry().x(), screen->geometry().y() , screen->size().width(), screen->size().height(), true); - SetWindowLongPtr(m_hwnd, GWL_STYLE, WS_CHILDWINDOW | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU | WS_POPUP); - SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE | WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW); + qDebug() << "Create window: " << screenAt << ", x: " << screen->geometry().x() << "y: " << screen->geometry().y() << screen->size().width() << screen->size().height(); - #endif +#endif #ifdef Q_OS_MACOS + //debug this->setWidth(screen->size().width()); this->setHeight(screen->size().height()); int x = screen->geometry().x(); int y = screen->geometry().y(); - this->setX(screen->geometry().x());// + offsetX); - this->setY(screen->geometry().y());// + offsetY);#endif + this->setX(screen->geometry().x()); // ); + this->setY(screen->geometry().y()); // ); #endif Qt::WindowFlags flags = this->flags(); - this->setFlags(flags | Qt::FramelessWindowHint | Qt::WindowStaysOnBottomHint); + setFlags(flags | Qt::FramelessWindowHint | Qt::WindowStaysOnBottomHint); + rootContext()->setContextProperty("mainwindow", this); + setSource(QUrl("qrc:/qml/main.qml")); + setResizeMode(QQuickView::ResizeMode::SizeRootObjectToView); + setGeometry(screen->geometry().x() + offsetX, screen->geometry().y() + offsetY, screen->size().width(), screen->size().height()); + //show(); - m_quickRenderer = QSharedPointer(new QQuickView(this)); - m_quickRenderer.data()->rootContext()->setContextProperty("mainwindow", this); - m_quickRenderer.data()->setSource(QUrl("qrc:/qml/main.qml")); - m_quickRenderer.data()->setGeometry(0, 0, width(), height()); - m_quickRenderer.data()->setResizeMode(QQuickView::ResizeMode::SizeRootObjectToView); - m_quickRenderer.data()->setFlags(flags | Qt::FramelessWindowHint | Qt::WindowStaysOnBottomHint); - m_quickRenderer.data()->show(); + UpdateWindow(m_hwnd); + UpdateWindow(m_worker_hwnd); setVolume(volume.toFloat()); setFillMode(fillmode); - connect(m_quickRenderer.data(), &QQuickView::statusChanged, [=](QQuickView::Status status) { + connect(this, &QQuickView::statusChanged, [=](QQuickView::Status status) { if (status == QQuickView::Error) { destroyThis(); } }); - setVisible(true); + QTimer::singleShot(3000,[=](){ + ShowWindow(m_hwnd, SW_SHOWNOACTIVATE); + }); + //setVisible(true); + //ShowWindow(m_hwnd, SW_SHOWDEFAULT); #ifdef Q_OS_MACOS MacIntegration* integration = new MacIntegration(this); integration->SetBackgroundLevel(this); #endif - } void MainWindow::init() { #ifdef Q_OS_WIN // This needs to be set in this order or // the window will be opened as fullscreen! - //ShowWindow(m_worker_hwnd, SW_SHOWDEFAULT); - ShowWindow(m_hwnd, SW_SHOWDEFAULT); + + //ShowWindow(m_hwnd, SW_SHOWDEFAULT); #endif } void MainWindow::destroyThis() { #ifdef Q_OS_WIN - ShowWindow(m_worker_hwnd, SW_HIDE); + ShowWindow(m_hwnd, SW_HIDE); #endif QCoreApplication::quit(); diff --git a/ScreenPlayWindow/src/SPWmainwindow.h b/ScreenPlayWindow/src/SPWmainwindow.h index d49692e4..c757a712 100644 --- a/ScreenPlayWindow/src/SPWmainwindow.h +++ b/ScreenPlayWindow/src/SPWmainwindow.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,18 +21,18 @@ #include #endif -class MainWindow : public QWindow { +class MainWindow : public QQuickView { Q_OBJECT public: - explicit MainWindow(int i, QString projectPath, QString id, QString decoder, QString volume, QString fillmode, QScreen* parent = nullptr); + explicit MainWindow(int screenAt, QString projectPath, QString id, QString decoder, QString volume, QString fillmode, QWindow* parent = nullptr); Q_PROPERTY(QVector screenNumber READ screenNumber WRITE setScreenNumber NOTIFY screenNumberChanged) Q_PROPERTY(QString projectConfig READ projectConfig WRITE setProjectConfig NOTIFY projectConfigChanged) Q_PROPERTY(QString appID READ name WRITE setname NOTIFY nameChanged) - Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged) - Q_PROPERTY(QString fullContentPath READ fullContentPath WRITE setFullContentPath NOTIFY fullContentPathChanged) + Q_PROPERTY(QString fullContentPath READ fullContentPath WRITE setFullContentPath NOTIFY fullContentPathChanged) + Q_PROPERTY(QString wallpaperType READ wallpaperType WRITE setWallpaperType NOTIFY wallpaperTypeChanged) Q_PROPERTY(QString fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(bool loops READ loops WRITE setLoops NOTIFY loopsChanged) Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged) @@ -59,11 +60,6 @@ public: return m_projectPath; } - QString type() const - { - return m_type; - } - QString fillMode() const { return m_fillMode; @@ -98,6 +94,11 @@ public: return m_decoder; } + QString wallpaperType() const + { + return m_wallpaperType; + } + public slots: void destroyThis(); void init(); @@ -126,15 +127,6 @@ public slots: emit nameChanged(m_appID); } - void setType(QString type) - { - if (m_type == type) - return; - - m_type = type; - emit typeChanged(m_type); - } - void setFillMode(QString fillMode) { if (m_fillMode == fillMode) @@ -155,7 +147,6 @@ public slots: void setVolume(float volume) { - qWarning("Floating point comparison needs context sanity check"); if (qFuzzyCompare(m_volume, volume)) return; @@ -210,13 +201,22 @@ public slots: emit decoderChanged(m_decoder); } + void setWallpaperType(QString wallpaperType) + { + if (m_wallpaperType == wallpaperType) + return; + + m_wallpaperType = wallpaperType; + emit wallpaperTypeChanged(m_wallpaperType); + } + signals: void playVideo(QString path); void playQmlScene(QString file); void projectConfigChanged(QString projectConfig); void nameChanged(QString appID); void screenNumberChanged(QVector screenNumber); - void typeChanged(QString type); + void fillModeChanged(QString fillMode); void loopsChanged(bool loops); void volumeChanged(float volume); @@ -227,12 +227,14 @@ signals: void decoderChanged(QString decoder); void qmlExit(); + void wallpaperTypeChanged(QString wallpaperType); + private: #ifdef Q_OS_WIN HWND m_hwnd; HWND m_worker_hwnd; #endif - QSharedPointer m_quickRenderer = nullptr; + QUrl m_projectPath; QString m_projectFile; QJsonObject m_project; @@ -240,7 +242,7 @@ private: QString m_appID; QVector m_screenNumber; - QString m_type; + QString m_fillMode; bool m_loops; float m_volume; @@ -248,4 +250,5 @@ private: bool m_isPlaying; float m_playbackRate; QString m_decoder; + QString m_wallpaperType; }; diff --git a/ScreenPlayWindow/src/basewindow.cpp b/ScreenPlayWindow/src/basewindow.cpp new file mode 100644 index 00000000..2aa83b4a --- /dev/null +++ b/ScreenPlayWindow/src/basewindow.cpp @@ -0,0 +1,72 @@ +#include "basewindow.h" + +BaseWindow::BaseWindow(QObject* parent) + : QObject(parent) +{ +} + +BaseWindow::BaseWindow(QString projectFilePath, QObject* parent) + : QObject(parent) +{ + qRegisterMetaType(); + qmlRegisterType("net.aimber.wallpaper", 1, 0, "Wallpaper"); + + QFile projectFile; + QJsonDocument configJsonDocument; + QJsonParseError parseError; + + projectFile.setFileName(projectFilePath + "/project.json"); + projectFile.open(QIODevice::ReadOnly | QIODevice::Text); + QString projectConfig = projectFile.readAll(); + configJsonDocument = QJsonDocument::fromJson(projectConfig.toUtf8(), &parseError); + + /* project.json example: + *{ + * "description": "", + * "file": "example.webm", + * "preview": "preview.png", + * "previewGIF": "preview.gif", + * "previewMP4": "preview.mp4", + * "title": "example title", + * "type": "video" + *} + */ + + if (!(parseError.error == QJsonParseError::NoError)) { + qDebug() << projectConfig << "\n" + << parseError.errorString(); + qFatal("Settings Json Parse Error. Exiting now!"); + } + + QJsonObject projectObject = configJsonDocument.object(); + + if (!projectObject.contains("file")) { + qFatal("No file was specified inside the json object!"); + } + + if (!projectObject.contains("type")) { + qFatal("No type was specified inside the json object!"); + } + + setFullContentPath(projectFilePath + "/" + projectObject.value("file").toString()); + + if (projectObject.value("type") == "video") { + setType(BaseWindow::WallpaperType::Video); + return; + } + + if (projectObject.value("type") == "scene") { + setType(BaseWindow::WallpaperType::ThreeJSScene); + return; + } + + if (projectObject.value("type") == "qml") { + setType(BaseWindow::WallpaperType::Qml); + return; + } + + if (projectObject.value("type") == "html") { + setType(BaseWindow::WallpaperType::Html); + return; + } +} diff --git a/ScreenPlayWindow/src/basewindow.h b/ScreenPlayWindow/src/basewindow.h new file mode 100644 index 00000000..f6b64a08 --- /dev/null +++ b/ScreenPlayWindow/src/basewindow.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class BaseWindow : public QObject { + Q_OBJECT + +public: + BaseWindow(QObject* parent = nullptr); + BaseWindow(QString projectFilePath, QObject* parent = nullptr); + + Q_PROPERTY(QString appID READ appID WRITE setAppID NOTIFY appIDChanged) + Q_PROPERTY(QString fullContentPath READ fullContentPath WRITE setFullContentPath NOTIFY fullContentPathChanged) + Q_PROPERTY(bool loops READ loops WRITE setLoops NOTIFY loopsChanged) + Q_PROPERTY(bool isPlaying READ isPlaying WRITE setIsPlaying NOTIFY isPlayingChanged) + Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged) + Q_PROPERTY(float playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged) + Q_PROPERTY(WallpaperType type READ type WRITE setType NOTIFY typeChanged) + + enum class WallpaperType { + Video, + Html, + ThreeJSScene, + Qml, + }; + Q_ENUM(WallpaperType) + + bool loops() const + { + return m_loops; + } + + float volume() const + { + return m_volume; + } + + bool isPlaying() const + { + return m_isPlaying; + } + + float playbackRate() const + { + return m_playbackRate; + } + + WallpaperType type() const + { + return m_type; + } + + QString fullContentPath() const + { + return m_fullContentPath; + } + + QString appID() const + { + return m_appID; + } + +signals: + void loopsChanged(bool loops); + void volumeChanged(float volume); + void isPlayingChanged(bool isPlaying); + void playbackRateChanged(float playbackRate); + void typeChanged(WallpaperType type); + void fullContentPathChanged(QString fullContentPath); + void appIDChanged(QString appID); + +public slots: + virtual void destroyThis() {} + virtual void setVisible(bool show) {Q_UNUSED(show)} + virtual void messageReceived(QString key, QString value){Q_UNUSED(key);Q_UNUSED(value)} + + QString getApplicationPath() + { + return QApplication::applicationDirPath(); + } + + void setLoops(bool loops) + { + if (m_loops == loops) + return; + + m_loops = loops; + emit loopsChanged(m_loops); + } + void setVolume(float volume) + { + qWarning("Floating point comparison needs context sanity check"); + if (qFuzzyCompare(m_volume, volume)) + return; + + m_volume = volume; + emit volumeChanged(m_volume); + } + void setIsPlaying(bool isPlaying) + { + if (m_isPlaying == isPlaying) + return; + + m_isPlaying = isPlaying; + emit isPlayingChanged(m_isPlaying); + } + void setPlaybackRate(float playbackRate) + { + qWarning("Floating point comparison needs context sanity check"); + if (qFuzzyCompare(m_playbackRate, playbackRate)) + return; + + m_playbackRate = playbackRate; + emit playbackRateChanged(m_playbackRate); + } + + void setType(WallpaperType type) + { + if (m_type == type) + return; + + m_type = type; + emit typeChanged(m_type); + } + + void setFullContentPath(QString fullContentPath) + { + if (m_fullContentPath == fullContentPath) + return; + + m_fullContentPath = fullContentPath; + emit fullContentPathChanged(m_fullContentPath); + } + + void setAppID(QString appID) + { + if (m_appID == appID) + return; + + m_appID = appID; + emit appIDChanged(m_appID); + } + +private: + bool m_loops = true; + bool m_isPlaying = true; + + float m_volume = 1.0f; + float m_playbackRate = 1.0f; + + QString m_fullContentPath; + QString m_appID; + + WallpaperType m_type = BaseWindow::WallpaperType::Qml; +}; diff --git a/ScreenPlayWindow/src/windowsdesktopproperties.cpp b/ScreenPlayWindow/src/windowsdesktopproperties.cpp new file mode 100644 index 00000000..3ea5096b --- /dev/null +++ b/ScreenPlayWindow/src/windowsdesktopproperties.cpp @@ -0,0 +1,30 @@ +#include "windowsdesktopproperties.h" + +WindowsDesktopProperties::WindowsDesktopProperties(QObject* parent) + : QObject(parent) +{ + qmlRegisterType(); + QSettings settings("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat); + + setWallpaperPath(settings.value("WallPaper").toString()); + setIsTiled(settings.value("TileWallpaper").toBool()); + + QPoint pos; + pos.setX(settings.value("WallpaperOriginX").toInt()); + pos.setY(settings.value("WallpaperOriginY").toInt()); + setPosition(pos); + + setWallpaperStyle(settings.value("WallpaperStyle").toInt()); + + QSettings settingscolor("HKEY_CURRENT_USER\\Control Panel\\Colors", QSettings::NativeFormat); + + QString colorStringRGB = settingscolor.value("Background").toString(); + QStringList colorStringRGBList = colorStringRGB.split(" "); + + if (colorStringRGBList.length() == 3) { + int colorR = colorStringRGBList.at(0).toInt(); + int colorG = colorStringRGBList.at(1).toInt(); + int colorB = colorStringRGBList.at(2).toInt(); + setColor(QColor::fromRgb(colorR, colorG, colorB)); + } +} diff --git a/ScreenPlayWindow/src/windowsdesktopproperties.h b/ScreenPlayWindow/src/windowsdesktopproperties.h new file mode 100644 index 00000000..a0cea8ba --- /dev/null +++ b/ScreenPlayWindow/src/windowsdesktopproperties.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class WindowsDesktopProperties : public QObject { + Q_OBJECT + +public: + explicit WindowsDesktopProperties(QObject* parent = nullptr); + + Q_PROPERTY(QString wallpaperPath READ wallpaperPath WRITE setWallpaperPath NOTIFY wallpaperPathChanged) + Q_PROPERTY(QPoint position READ position WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(bool isTiled READ isTiled WRITE setIsTiled NOTIFY isTiledChanged) + Q_PROPERTY(int wallpaperStyle READ wallpaperStyle WRITE setWallpaperStyle NOTIFY wallpaperStyleChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + + /* WallpaperStyle integer values in the windows registry + * 10 -> fill + * 6 -> fit + * 2 -> stretch + * 0 -> tile -> isTiled true + * 0 -> center -> isTiled false WTF Windows? + * 22 -> span + */ + + QString wallpaperPath() const + { + return m_wallpaperPath; + } + + QPoint position() const + { + return m_position; + } + + bool isTiled() const + { + return m_isTiled; + } + + int wallpaperStyle() const + { + return m_wallpaperStyle; + } + + QColor color() const + { + return m_color; + } + +signals: + + void wallpaperPathChanged(QString wallpaperPath); + + void positionChanged(QPoint position); + + void isTiledChanged(bool isTiled); + + void wallpaperStyleChanged(int wallpaperStyle); + + void colorChanged(QColor color); + +public slots: + void setWallpaperPath(QString wallpaperPath) + { + if (m_wallpaperPath == wallpaperPath) + return; + + m_wallpaperPath = wallpaperPath; + emit wallpaperPathChanged(m_wallpaperPath); + } + void setPosition(QPoint position) + { + if (m_position == position) + return; + + m_position = position; + emit positionChanged(m_position); + } + void setIsTiled(bool isTiled) + { + if (m_isTiled == isTiled) + return; + + m_isTiled = isTiled; + emit isTiledChanged(m_isTiled); + } + void setWallpaperStyle(int wallpaperStyle) + { + if (m_wallpaperStyle == wallpaperStyle) + return; + + m_wallpaperStyle = wallpaperStyle; + emit wallpaperStyleChanged(m_wallpaperStyle); + } + + void setColor(QColor color) + { + if (m_color == color) + return; + + m_color = color; + emit colorChanged(m_color); + } + +private: + QString m_wallpaperPath; + QPoint m_position; + bool m_isTiled; + int m_wallpaperStyle; + QColor m_color; +}; diff --git a/ScreenPlayWindow/src/winwindow.cpp b/ScreenPlayWindow/src/winwindow.cpp new file mode 100644 index 00000000..7a1a9271 --- /dev/null +++ b/ScreenPlayWindow/src/winwindow.cpp @@ -0,0 +1,136 @@ +#include "winwindow.h" + +BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam) +{ + // 0xXXXXXXX "" WorkerW + // ... + // 0xXXXXXXX "" SHELLDLL_DefView + // 0xXXXXXXXX "FolderView" SysListView32 + // 0xXXXXXXXX "" WorkerW <---- We want this one + // 0xXXXXXXXX "Program Manager" Progman + if (FindWindowExW(hwnd, nullptr, L"SHELLDLL_DefView", nullptr)) + *reinterpret_cast(lparam) = FindWindowExW(nullptr, hwnd, L"WorkerW", nullptr); + return TRUE; +} + +WinWindow::WinWindow(QVector& activeScreensList, QString projectPath, QString id, QString volume) + : BaseWindow(projectPath) +{ + m_windowHandle = reinterpret_cast(m_window.winId()); + setAppID(id); + + bool ok = false; + float volumeParsed = volume.toFloat(&ok); + if (!ok) { + qFatal("Could not parse volume"); + } + setVolume(volumeParsed); + + if (!searchWorkerWindowToParentTo()) { + qFatal("No worker window found"); + } + + // WARNING: Window flags must be called *here*! + Qt::WindowFlags flags = m_window.flags(); + m_window.setFlags(flags | Qt::FramelessWindowHint); + SetWindowLongPtr(m_windowHandle, GWL_STYLE, WS_CHILDWINDOW); + SetWindowLongPtr(m_windowHandle, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE); + + // Windows coordante system begins at 0x0 at the + // main monitor upper left and not at the most left top monitor + calcOffsets(); + + // Ether for one Screen or for all + if ((QApplication::screens().length() == activeScreensList.length()) && (activeScreensList.length() != 1)) { + setupWallpaperForAllScreens(); + } else if (activeScreensList.length() == 1) { + setupWallpaperForOneScreen(activeScreensList.at(0)); + } + + m_window.setResizeMode(QQuickView::ResizeMode::SizeRootObjectToView); + m_window.rootContext()->setContextProperty("window", this); + m_window.rootContext()->setContextProperty("desktopProperties", &m_windowsDesktopProperties); + m_window.setSource(QUrl("qrc:/main.qml")); + + // Let QML decide when were are read to show the window + ShowWindow(m_windowHandle, SW_HIDE); +} + +void WinWindow::setVisible(bool show) +{ + if (show) { + ShowWindow(m_windowHandle, SW_SHOW); + } else { + ShowWindow(m_windowHandle, SW_HIDE); + } +} + +void WinWindow::destroyThis() +{ + ShowWindow(m_windowHandle, SW_HIDE); + + QCoreApplication::quit(); +} + +void WinWindow::messageReceived(QString key, QString value) +{ + +} + +void WinWindow::calcOffsets() +{ + // Recalculate window coordiantes because of point (0,0) + // Is at the origin monitor or the most left + + for (int i = 0; i < QApplication::screens().count(); i++) { + QScreen* screen = QApplication::screens().at(i); + if (screen->availableGeometry().x() < 0) { + m_windowOffsetX += (screen->availableGeometry().x() * -1); + } + if (screen->availableGeometry().y() < 0) { + m_windowOffsetY += (screen->availableGeometry().y() * -1); + } + } +} + +void WinWindow::setupWallpaperForOneScreen(int activeScreen) +{ + QScreen* screen = QApplication::screens().at(activeScreen); + QRect screenRect = screen->geometry(); + m_window.setHeight(screenRect.height()); + m_window.setWidth(screenRect.width()); + if (!SetWindowPos(m_windowHandle, nullptr, screenRect.x() + m_windowOffsetX, screenRect.y() + m_windowOffsetY, screenRect.width(), screenRect.height(), SWP_SHOWWINDOW)) { + qFatal("Could not set window pos: "); + } + if (SetParent(m_windowHandle, m_windowHandleWorker) == nullptr) { + qFatal("Could not attach to parent window"); + } +} + +void WinWindow::setupWallpaperForAllScreens() +{ + QRect rect; + for (int i = 0; i < QApplication::screens().count(); i++) { + QScreen* screenTmp = QApplication::screens().at(i); + rect.setWidth(rect.width() + screenTmp->geometry().width()); + rect.setHeight(rect.height() + screenTmp->geometry().height()); + } + m_window.setHeight(rect.height()); + m_window.setWidth(rect.width()); + if (!SetWindowPos(m_windowHandle, nullptr, 0, 0, rect.width(), rect.height(), SWP_SHOWWINDOW)) { + qFatal("Could not set window pos: "); + } + if (SetParent(m_windowHandle, m_windowHandleWorker) == nullptr) { + qFatal("Could not attach to parent window"); + } +} + +bool WinWindow::searchWorkerWindowToParentTo() +{ + HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager"); + const DWORD WM_SPAWN_WORKER = 0x052C; + SendMessageTimeoutW(progman_hwnd, WM_SPAWN_WORKER, 0xD, 0x1, SMTO_NORMAL, + 10000, nullptr); + + return EnumWindows(SearchForWorkerWindow, reinterpret_cast(&m_windowHandleWorker)); +} diff --git a/ScreenPlayWindow/src/winwindow.h b/ScreenPlayWindow/src/winwindow.h new file mode 100644 index 00000000..86cf72ea --- /dev/null +++ b/ScreenPlayWindow/src/winwindow.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "windowsdesktopproperties.h" +#include +#include + +#include + +#include "basewindow.h" + +class WinWindow : public BaseWindow { + Q_OBJECT + +public: + explicit WinWindow(QVector& activeScreensList, QString projectPath, QString id, QString volume); + +public slots: + void setVisible(bool show) override; + void destroyThis() override; + void messageReceived(QString key, QString value) override; + +private: + void calcOffsets(); + void setupWallpaperForOneScreen(int activeScreen); + void setupWallpaperForAllScreens(); + bool searchWorkerWindowToParentTo(); + +private: + int m_windowOffsetX = 0; + int m_windowOffsetY = 0; + QQuickView m_window; + HWND m_windowHandle; + HWND m_windowHandleWorker; + WindowsDesktopProperties m_windowsDesktopProperties; +};