1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-11-07 03:22:33 +01:00

Refactor window

Refactor wallpaper convertion
This commit is contained in:
Elias 2019-03-10 21:20:55 +01:00
parent f7336a4ba3
commit 31757b4e31
27 changed files with 1374 additions and 639 deletions

View File

@ -10,6 +10,7 @@ TARGETPATH = ScreenPlay
ICON = favicon.ico ICON = favicon.ico
SOURCES += main.cpp \ SOURCES += main.cpp \
src/createimportvideo.cpp \
src/installedlistmodel.cpp \ src/installedlistmodel.cpp \
src/monitorlistmodel.cpp \ src/monitorlistmodel.cpp \
src/settings.cpp \ src/settings.cpp \
@ -30,6 +31,7 @@ TRANSLATIONS = translations/ScreenPlay_en.ts \
translations/ScreenPlay_de.ts translations/ScreenPlay_de.ts
HEADERS += \ HEADERS += \
src/createimportvideo.h \
src/installedlistmodel.h \ src/installedlistmodel.h \
src/monitorlistmodel.h \ src/monitorlistmodel.h \
src/settings.h \ src/settings.h \

View File

@ -30,7 +30,6 @@ int main(int argc, char* argv[])
QGuiApplication::setApplicationVersion("0.2.0"); QGuiApplication::setApplicationVersion("0.2.0");
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
app.setQuitOnLastWindowClosed(false); app.setQuitOnLastWindowClosed(false);
app.setWindowIcon(QIcon(":/assets/icons/favicon.ico")); app.setWindowIcon(QIcon(":/assets/icons/favicon.ico"));
@ -54,6 +53,7 @@ int main(int argc, char* argv[])
ProfileListModel profileListModel; ProfileListModel profileListModel;
SDKConnector sdkConnector; SDKConnector sdkConnector;
// Create settings in the end because for now it depends on // Create settings in the end because for now it depends on
// such things as the profile list model to complete // such things as the profile list model to complete
// It will also set the m_absoluteStoragePath in profileListModel and installedListModel // It will also set the m_absoluteStoragePath in profileListModel and installedListModel

View File

@ -5,7 +5,7 @@ import Qt.labs.platform 1.0
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.2
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import RemoteWorkshopCreationStatus 1.0 //import RemoteWorkshopCreationStatus 1.0
Item { Item {

View File

@ -8,6 +8,10 @@ import net.aimber.create 1.0
Item { Item {
id: wrapperContent id: wrapperContent
property string customVideoPreviewPath: ""
Text { Text {
id: txtHeadline id: txtHeadline
text: qsTr("Convert a video to a wallpaper") text: qsTr("Convert a video to a wallpaper")
@ -261,7 +265,7 @@ Item {
Material.background: Material.Gray Material.background: Material.Gray
Material.foreground: "white" Material.foreground: "white"
onClicked: { onClicked: {
screenPlayCreate.abort() screenPlayCreate.abortAndCleanup()
utility.setNavigationActive(true) utility.setNavigationActive(true)
utility.setNavigation("Create") utility.setNavigation("Create")
} }
@ -290,3 +294,10 @@ Item {
} }
} }
} }
/*##^## Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -13,17 +13,16 @@ Item {
property string filePath property string filePath
property bool canNext: false property bool canNext: false
property int importState: CreateWallpaperWizard.ImportState.Import
Component.onCompleted: { Component.onCompleted: {
state = "in" state = "in"
utility.setNavigationActive(false) utility.setNavigationActive(false)
if (importState === CreateWallpaperWizard.ImportState.Import) { loader_wrapperContent.source = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml"
loader_wrapperContent.source // if (importState === CreateWallpaperWizard.ImportState.Import) {
= "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImport.qml" // loader_wrapperContent.source
} else { // = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImport.qml"
loader_wrapperContent.source = "qrc:/qml/Create/Wizards/CreateWallpaper/CreateWallpaperVideoImportConvert.qml" // } else {
} // }
} }
//Blocks some MouseArea from create page //Blocks some MouseArea from create page

View File

@ -177,23 +177,23 @@ Item {
bottom: parent.bottom bottom: parent.bottom
} }
Text { // Text {
id: text1 // id: text1
height: 29 // height: 29
text: screenTitle // text: screenTitle
anchors { // anchors {
right: parent.right // right: parent.right
left: parent.left // left: parent.left
top: parent.top // top: parent.top
margins: 10 // margins: 10
} // }
wrapMode: Text.WordWrap // wrapMode: Text.WordWrap
color: "#2F2F2F" // color: "#2F2F2F"
font.pointSize: 9 // font.pointSize: 9
font.family: "Segoe UI, Roboto" // font.family: "Segoe UI, Roboto"
} // }
} }
} }

View File

@ -6,8 +6,5 @@ Theme=Light
Accent=Orange Accent=Orange
Primary=White Primary=White
Foreground=Grey Foreground=Grey
Font\Family=Roboto
Font\PixelSize=12
[Material\Font]
Family=Roboto
PixelSize=14

View File

@ -6,9 +6,8 @@ Create::Create(Settings* st, QMLUtilities* util, QObject* parent)
m_settings = st; m_settings = st;
m_utils = util; m_utils = util;
m_futureWatcher.setFuture(m_future);
qRegisterMetaType<Create::State>(); qRegisterMetaType<CreateImportVideo::State>();
qmlRegisterType<Create>("net.aimber.create", 1, 0, "Create"); qmlRegisterType<Create>("net.aimber.create", 1, 0, "Create");
} }
@ -33,17 +32,19 @@ bool Create::copyRecursively(const QString& srcFilePath, const QString& tgtFileP
targetDir.cdUp(); targetDir.cdUp();
QDir sourceDir(srcFilePath); QDir sourceDir(srcFilePath);
QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); 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 const QString newSrcFilePath
= srcFilePath + QLatin1Char('/') + fileName; = srcFilePath + QLatin1Char('/') + fileName;
const QString newTgtFilePath const QString newTgtFilePath
= tgtFilePath + QLatin1Char('/') + fileName; = tgtFilePath + QLatin1Char('/') + fileName;
if (!copyRecursively(newSrcFilePath, newTgtFilePath)) if (!copyRecursively(newSrcFilePath, newTgtFilePath)) {
return false; return false;
}
} }
} else { } else {
if (!QFile::copy(srcFilePath, tgtFilePath)) if (!QFile::copy(srcFilePath, tgtFilePath)) {
return false; return false;
}
} }
return true; return true;
} }
@ -52,365 +53,44 @@ void Create::createWallpaperStart(QString videoPath)
{ {
videoPath.remove("file:///"); videoPath.remove("file:///");
m_future = QtConcurrent::run([=]() { QDir dir;
QDir dir; dir.cd(this->m_settings->localStoragePath().toLocalFile());
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 // Create a temp dir so we can later alter it to the workshop id
auto folderName = QString("_tmp_" + QTime::currentTime().toString()).replace(":", ""); auto folderName = QString("_tmp_" + QTime::currentTime().toString()).replace(":", "");
if (!dir.mkdir(folderName)) if (!dir.mkdir(folderName)) {
return; emit abortCreateWallpaper();
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<QProcess> 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;
} }
obj = doc.object(); m_createWallpaperData.exportPath = dir.path() + "/" + folderName;
pro.data()->close(); setWorkingDir(m_createWallpaperData.exportPath);
// Get video length auto* thread = new QThread;
QJsonObject objFormat = obj.value("format").toObject(); m_createImportVideo = new CreateImportVideo(videoPath, m_createWallpaperData.exportPath);
bool okParseDuration = false; connect(m_createImportVideo, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
auto tmpLength = objFormat.value("duration").toVariant().toFloat(&okParseDuration); connect(thread, &QThread::started, m_createImportVideo, &CreateImportVideo::process);
connect(m_createImportVideo, &CreateImportVideo::finished, thread, &QThread::quit);
if (!okParseDuration) { connect(m_createImportVideo, &CreateImportVideo::finished, m_createImportVideo, &QObject::deleteLater);
qDebug() << "Error parsing video length"; connect(thread, &QThread::finished, thread, &QObject::deleteLater);
emit processOutput("Error parsing video length"); m_createImportVideo->moveToThread(thread);
emit createWallpaperStateChanged(Create::State::AnalyseVideoError); thread->start();
return false;
}
int length = 0;
length = static_cast<int>(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<float>(avgFrameRateList.at(0).toInt());
float value2 = static_cast<float>(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<QProcess> 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<QProcess> 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<QProcess> 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<QProcess> 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;
} }
void Create::abortAndCleanup() void Create::abortAndCleanup()
{ {
emit abortCreateWallpaper(); emit abortCreateWallpaper();
m_futureWatcher.cancel();
QDir exportPath(m_createWallpaperData.exportPath); QDir exportPath(m_createWallpaperData.exportPath);
if (exportPath.exists()) { if (exportPath.exists()) {
if (!exportPath.removeRecursively()) { if (!exportPath.removeRecursively()) {
emit createWallpaperStateChanged(Create::State::AbortCleanupError); emit createWallpaperStateChanged(CreateImportVideo::State::AbortCleanupError);
qWarning() << "Could not delete temp exportPath: " << exportPath; qWarning() << "Could not delete temp exportPath: " << exportPath;
} else { } else {
qDebug() << "cleanup " << m_createWallpaperData.exportPath; qDebug() << "cleanup " << m_createWallpaperData.exportPath;

View File

@ -19,8 +19,10 @@
#include <QTimer> #include <QTimer>
#include <QUrl> #include <QUrl>
#include <QtMath> #include <QtMath>
#include <QTimer>
#include <memory>
#include "createimportvideo.h"
#include "qmlutilities.h" #include "qmlutilities.h"
#include "settings.h" #include "settings.h"
@ -33,6 +35,8 @@ struct CreateWallpaperData {
int framerate = 0; int framerate = 0;
}; };
class Create : public QObject { class Create : public QObject {
Q_OBJECT Q_OBJECT
public: public:
@ -44,35 +48,6 @@ public:
Create() {} Create() {}
~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 QString workingDir() const
{ {
return m_workingDir; return m_workingDir;
@ -84,7 +59,7 @@ public:
} }
signals: signals:
void createWallpaperStateChanged(Create::State state); void createWallpaperStateChanged(CreateImportVideo::State state);
void processOutput(QString text); void processOutput(QString text);
void workingDirChanged(QString workingDir); void workingDirChanged(QString workingDir);
void progressChanged(float progress); void progressChanged(float progress);
@ -93,17 +68,8 @@ signals:
public slots: public slots:
void copyProject(QString relativeProjectPath, QString toPath); void copyProject(QString relativeProjectPath, QString toPath);
bool copyRecursively(const QString& srcFilePath, const QString& tgtFilePath); 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 abortAndCleanup();
void createWallpaperStart(QString videoPath);
void setWorkingDir(QString workingDir) void setWorkingDir(QString workingDir)
{ {
@ -124,11 +90,11 @@ public slots:
} }
private: private:
CreateImportVideo* m_createImportVideo;
Settings* m_settings; Settings* m_settings;
QMLUtilities* m_utils; QMLUtilities* m_utils;
CreateWallpaperData m_createWallpaperData; CreateWallpaperData m_createWallpaperData;
QString m_workingDir; QString m_workingDir;
float m_progress = 0.0f; float m_progress = 0.0f;
QFuture<void> m_future;
QFutureWatcher<void> m_futureWatcher;
}; };

View File

@ -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<QProcess> 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<int>(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<double>(avgFrameRateList.at(0).toInt());
double value2 = static_cast<double>(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<QProcess> 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<QProcess> 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<QProcess> 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<QProcess> 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;
}

View File

@ -0,0 +1,77 @@
#pragma once
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QObject>
#include <QString>
#include <QDebug>
#include <QThread>
#include <QProcess>
#include <QApplication>
#include <QtMath>
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;
};

View File

@ -5,6 +5,12 @@ InstalledListModel::InstalledListModel(QObject* parent)
{ {
QObject::connect(this, &InstalledListModel::addInstalledItem, QObject::connect(this, &InstalledListModel::addInstalledItem,
this, &InstalledListModel::append, Qt::QueuedConnection); this, &InstalledListModel::append, Qt::QueuedConnection);
m_loadScreenWatcher.setFuture(m_loadScreenFuture);
QObject::connect(&m_loadScreenWatcher, &QFutureWatcher<void>::progressValueChanged, [](int progressValue) {
qDebug() << progressValue;
});
} }
int InstalledListModel::rowCount(const QModelIndex& parent) const 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) void InstalledListModel::append(const QJsonObject obj, const QString folderName)
{ {
//qDebug() << QThread::currentThreadId();
beginInsertRows(QModelIndex(), m_screenPlayFiles.size(), m_screenPlayFiles.size()); beginInsertRows(QModelIndex(), m_screenPlayFiles.size(), m_screenPlayFiles.size());
ProjectFile tmpFile(obj, folderName, m_absoluteStoragePath); ProjectFile tmpFile(obj, folderName, m_absoluteStoragePath);
@ -86,7 +93,12 @@ void InstalledListModel::append(const QJsonObject obj, const QString folderName)
void InstalledListModel::loadInstalledContent() 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; QJsonDocument jsonProject;
QJsonParseError parseError; QJsonParseError parseError;
@ -126,14 +138,14 @@ void InstalledListModel::loadInstalledContent()
obj.insert("type", "video"); 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()); 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 addInstalledItem(obj, item.baseName());
} }
} }
emit installedLoadingFinished(); emit installedLoadingFinished();
}); });
} }

View File

@ -16,6 +16,8 @@
#include <QUrl> #include <QUrl>
#include <QVector> #include <QVector>
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
#include <QFuture>
/*! /*!
@ -81,4 +83,6 @@ signals:
private: private:
QVector<ProjectFile> m_screenPlayFiles; QVector<ProjectFile> m_screenPlayFiles;
QUrl m_absoluteStoragePath; QUrl m_absoluteStoragePath;
QFuture<void> m_loadScreenFuture;
QFutureWatcher<void> m_loadScreenWatcher;
}; };

View File

@ -66,10 +66,11 @@ QString ScreenPlay::generateID()
{ {
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
const int randomStringLength = 32; const int randomStringLength = 32;
auto* radomGen = QRandomGenerator::system();
QString randomString; QString randomString;
for (int i = 0; i < randomStringLength; ++i) { for (int i = 0; i < randomStringLength; ++i) {
int index = qrand() % possibleCharacters.length(); int index = radomGen->bounded(possibleCharacters.length());
QChar nextChar = possibleCharacters.at(index); QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar); randomString.append(nextChar);
} }

View File

@ -10,7 +10,7 @@ SDKConnector::SDKConnector(QObject* parent)
m_server = QSharedPointer<QLocalServer>(new QLocalServer(this)); m_server = QSharedPointer<QLocalServer>(new QLocalServer(this));
connect(m_server.data(), &QLocalServer::newConnection, this, &SDKConnector::newConnection); connect(m_server.data(), &QLocalServer::newConnection, this, &SDKConnector::newConnection);
if (!m_server.data()->listen("ScreenPlay")) { if (!m_server.data()->listen("ScreenPlay")) {
//TODO //TODO what to do if no ScreenPlay local socket can be open
} }
} }

View File

@ -1,5 +1,9 @@
#include "../ScreenPlaySDK/screenplaysdk.h" #include "../ScreenPlaySDK/screenplaysdk.h"
#include "src/SPWmainwindow.h"
#if defined(Q_OS_WIN)
#include "src/winwindow.h"
#endif
#include <QApplication> #include <QApplication>
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
@ -10,32 +14,46 @@ int main(int argc, char* argv[])
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv); QApplication app(argc, argv);
ScreenPlaySDK sdk;
QtWebEngine::initialize(); QtWebEngine::initialize();
// 6 parameter + 1 OS working directory default paramter // 6 parameter + 1 OS working directory default paramter
QStringList argumentList = a.arguments(); QStringList argumentList = app.arguments();
if (argumentList.length() != 7) { if (argumentList.length() != 7) {
return -3; return -3;
} }
bool canParseMonitorNumber = false; ScreenPlaySDK sdk;
int monitor = argumentList.at(1).toInt(&canParseMonitorNumber); sdk.setAppID(argumentList.at(3));
if (!canParseMonitorNumber) { QString monitorNumbers = argumentList.at(1);
return -4; QVector<int> 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 // 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); #if defined(Q_OS_WIN)
QObject::connect(&sdk, &ScreenPlaySDK::incommingMessage, &w, &MainWindow::messageReceived); 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();
} }

View File

@ -1,16 +1,22 @@
TEMPLATE = app TEMPLATE = app
QT += qml quick quickcontrols2 widgets core webengine QT += qml quick quickcontrols2 widgets core webengine
CONFIG += c++17 CONFIG += c++17
#CONFIG += qtquickcompiler
msvc: LIBS += -luser32 msvc: LIBS += -luser32
TARGETPATH = ScreenPlayWindow TARGETPATH = ScreenPlayWindow
SOURCES += \ SOURCES += \
SPWmain.cpp \ SPWmain.cpp \
src/SPWmainwindow.cpp #src/SPWmainwindow.cpp \
src/basewindow.cpp \
src/windowsdesktopproperties.cpp \
src/winwindow.cpp
HEADERS += \ HEADERS += \
src/SPWmainwindow.h #src/SPWmainwindow.h \
src/basewindow.h \
src/windowsdesktopproperties.h \
src/winwindow.h
RESOURCES += \ RESOURCES += \
SPWResources.qrc SPWResources.qrc

View File

@ -8,14 +8,11 @@
height: 100%; height: 100%;
object-fit: fill; object-fit: fill;
overflow: hidden; overflow: hidden;
background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%);
} }
body, html{ body, html{
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
overflow: hidden; overflow: hidden;
background:orange;
} }
#errorMsg{ #errorMsg{
position: fixed; position: fixed;

View File

@ -1,23 +1,127 @@
import QtQuick 2.12 import QtQuick 2.13
import QtWebEngine 1.8 import QtWebEngine 1.8
import net.aimber.wallpaper 1.0
Rectangle { Rectangle {
id: root
anchors.fill: parent 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 { Connections {
target: mainwindow target: window
onQmlExit: { onQmlExit: {
webView.runJavaScript( webView.runJavaScript(
"var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = 0;") "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = 0;")
mainwindow.destroyThis() window.destroyThis()
}
onFillModeChanged: {
//TODO
} }
onQmlSceneValueReceived: { 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
}
}
} }

View File

@ -18,14 +18,12 @@ BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam)
//for mac https://github.com/silvansky/QtMacApp/search?q=myprivate&unscoped_q=myprivate //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) MainWindow::MainWindow(int screenAt, QString projectPath, QString id, QString decoder, QString volume, QString fillmode, QWindow* parent)
: QWindow(parent) : QQuickView(parent)
{ {
m_appID = id; m_appID = id;
m_screenNumber.insert(0, i); m_screenNumber.append(screenAt);
setDecoder(decoder); setDecoder(decoder);
setProjectPath(projectPath); setProjectPath(projectPath);
@ -62,23 +60,23 @@ MainWindow::MainWindow(int i, QString projectPath, QString id, QString decoder,
if (m_project.value("type") == "video") { if (m_project.value("type") == "video") {
QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; QString tmpPath = m_projectPath.toString() + "/" + m_projectFile;
setFullContentPath(tmpPath); setFullContentPath(tmpPath);
setType("video"); setWallpaperType("video");
} else if (m_project.value("type") == "scene") { } else if (m_project.value("type") == "scene") {
return; return;
} else if (m_project.value("type") == "qmlScene") { } else if (m_project.value("type") == "qmlScene") {
QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; QString tmpPath = m_projectPath.toString() + "/" + m_projectFile;
setFullContentPath(tmpPath); setFullContentPath(tmpPath);
setType("qmlScene"); setWallpaperType("qmlScene");
} else if (m_project.value("type") == "html") { } else if (m_project.value("type") == "html") {
QString tmpPath = m_projectPath.toString() + "/" + m_projectFile; QString tmpPath = m_projectPath.toString() + "/" + m_projectFile;
setFullContentPath(tmpPath); setFullContentPath(tmpPath);
setType("html"); setWallpaperType("html");
} }
} }
// Recalculate window coordiantes because of point (0,0) // Recalculate window coordiantes because of point (0,0)
// Is at the origin monitor or the most left // Is at the origin monitor or the most left
QScreen* screen = QApplication::screens().at(i);
int offsetX = 0; int offsetX = 0;
int offsetY = 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<HWND>(winId());
HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager"); HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager");
// Spawn new worker window below desktop (using some undocumented Win32 magic) // Spawn new worker window below desktop (using some undocumented Win32 magic)
// See // See https://www.codeproject.com/articles/856020/draw-behind-desktop-icons-in-windows
// https://www.codeproject.com/articles/856020/draw-behind-desktop-icons-in-windows
// for more details // for more details
const DWORD WM_SPAWN_WORKER = 0x052C; const DWORD WM_SPAWN_WORKER = 0x052C;
SendMessageTimeoutW(progman_hwnd, WM_SPAWN_WORKER, 0xD, 0x1, SMTO_NORMAL, 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 //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); 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); 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); qDebug() << "Create window: " << screenAt << ", x: " << screen->geometry().x() << "y: " << screen->geometry().y() << screen->size().width() << screen->size().height();
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);
#endif #endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
//debug //debug
this->setWidth(screen->size().width()); this->setWidth(screen->size().width());
this->setHeight(screen->size().height()); this->setHeight(screen->size().height());
int x = screen->geometry().x(); int x = screen->geometry().x();
int y = screen->geometry().y(); int y = screen->geometry().y();
this->setX(screen->geometry().x());// + offsetX); this->setX(screen->geometry().x()); // );
this->setY(screen->geometry().y());// + offsetY);#endif this->setY(screen->geometry().y()); // );
#endif #endif
Qt::WindowFlags flags = this->flags(); 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<QQuickView>(new QQuickView(this)); UpdateWindow(m_hwnd);
m_quickRenderer.data()->rootContext()->setContextProperty("mainwindow", this); UpdateWindow(m_worker_hwnd);
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();
setVolume(volume.toFloat()); setVolume(volume.toFloat());
setFillMode(fillmode); setFillMode(fillmode);
connect(m_quickRenderer.data(), &QQuickView::statusChanged, [=](QQuickView::Status status) { connect(this, &QQuickView::statusChanged, [=](QQuickView::Status status) {
if (status == QQuickView::Error) { if (status == QQuickView::Error) {
destroyThis(); destroyThis();
} }
}); });
setVisible(true); QTimer::singleShot(3000,[=](){
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
});
//setVisible(true);
//ShowWindow(m_hwnd, SW_SHOWDEFAULT);
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
MacIntegration* integration = new MacIntegration(this); MacIntegration* integration = new MacIntegration(this);
integration->SetBackgroundLevel(this); integration->SetBackgroundLevel(this);
#endif #endif
} }
void MainWindow::init() void MainWindow::init()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// This needs to be set in this order or // This needs to be set in this order or
// the window will be opened as fullscreen! // the window will be opened as fullscreen!
//ShowWindow(m_worker_hwnd, SW_SHOWDEFAULT);
ShowWindow(m_hwnd, SW_SHOWDEFAULT); //ShowWindow(m_hwnd, SW_SHOWDEFAULT);
#endif #endif
} }
void MainWindow::destroyThis() void MainWindow::destroyThis()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
ShowWindow(m_worker_hwnd, SW_HIDE);
ShowWindow(m_hwnd, SW_HIDE); ShowWindow(m_hwnd, SW_HIDE);
#endif #endif
QCoreApplication::quit(); QCoreApplication::quit();

View File

@ -13,6 +13,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QWindow> #include <QWindow>
#include <QtGlobal> #include <QtGlobal>
#include <QTimer>
#include <QtQuick/QQuickView> #include <QtQuick/QQuickView>
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
@ -20,18 +21,18 @@
#include <qt_windows.h> #include <qt_windows.h>
#endif #endif
class MainWindow : public QWindow { class MainWindow : public QQuickView {
Q_OBJECT Q_OBJECT
public: 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<int> screenNumber READ screenNumber WRITE setScreenNumber NOTIFY screenNumberChanged) Q_PROPERTY(QVector<int> screenNumber READ screenNumber WRITE setScreenNumber NOTIFY screenNumberChanged)
Q_PROPERTY(QString projectConfig READ projectConfig WRITE setProjectConfig NOTIFY projectConfigChanged) Q_PROPERTY(QString projectConfig READ projectConfig WRITE setProjectConfig NOTIFY projectConfigChanged)
Q_PROPERTY(QString appID READ name WRITE setname NOTIFY nameChanged) 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(QString fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
Q_PROPERTY(bool loops READ loops WRITE setLoops NOTIFY loopsChanged) Q_PROPERTY(bool loops READ loops WRITE setLoops NOTIFY loopsChanged)
Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
@ -59,11 +60,6 @@ public:
return m_projectPath; return m_projectPath;
} }
QString type() const
{
return m_type;
}
QString fillMode() const QString fillMode() const
{ {
return m_fillMode; return m_fillMode;
@ -98,6 +94,11 @@ public:
return m_decoder; return m_decoder;
} }
QString wallpaperType() const
{
return m_wallpaperType;
}
public slots: public slots:
void destroyThis(); void destroyThis();
void init(); void init();
@ -126,15 +127,6 @@ public slots:
emit nameChanged(m_appID); emit nameChanged(m_appID);
} }
void setType(QString type)
{
if (m_type == type)
return;
m_type = type;
emit typeChanged(m_type);
}
void setFillMode(QString fillMode) void setFillMode(QString fillMode)
{ {
if (m_fillMode == fillMode) if (m_fillMode == fillMode)
@ -155,7 +147,6 @@ public slots:
void setVolume(float volume) void setVolume(float volume)
{ {
qWarning("Floating point comparison needs context sanity check");
if (qFuzzyCompare(m_volume, volume)) if (qFuzzyCompare(m_volume, volume))
return; return;
@ -210,13 +201,22 @@ public slots:
emit decoderChanged(m_decoder); emit decoderChanged(m_decoder);
} }
void setWallpaperType(QString wallpaperType)
{
if (m_wallpaperType == wallpaperType)
return;
m_wallpaperType = wallpaperType;
emit wallpaperTypeChanged(m_wallpaperType);
}
signals: signals:
void playVideo(QString path); void playVideo(QString path);
void playQmlScene(QString file); void playQmlScene(QString file);
void projectConfigChanged(QString projectConfig); void projectConfigChanged(QString projectConfig);
void nameChanged(QString appID); void nameChanged(QString appID);
void screenNumberChanged(QVector<int> screenNumber); void screenNumberChanged(QVector<int> screenNumber);
void typeChanged(QString type);
void fillModeChanged(QString fillMode); void fillModeChanged(QString fillMode);
void loopsChanged(bool loops); void loopsChanged(bool loops);
void volumeChanged(float volume); void volumeChanged(float volume);
@ -227,12 +227,14 @@ signals:
void decoderChanged(QString decoder); void decoderChanged(QString decoder);
void qmlExit(); void qmlExit();
void wallpaperTypeChanged(QString wallpaperType);
private: private:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HWND m_hwnd; HWND m_hwnd;
HWND m_worker_hwnd; HWND m_worker_hwnd;
#endif #endif
QSharedPointer<QQuickView> m_quickRenderer = nullptr;
QUrl m_projectPath; QUrl m_projectPath;
QString m_projectFile; QString m_projectFile;
QJsonObject m_project; QJsonObject m_project;
@ -240,7 +242,7 @@ private:
QString m_appID; QString m_appID;
QVector<int> m_screenNumber; QVector<int> m_screenNumber;
QString m_type;
QString m_fillMode; QString m_fillMode;
bool m_loops; bool m_loops;
float m_volume; float m_volume;
@ -248,4 +250,5 @@ private:
bool m_isPlaying; bool m_isPlaying;
float m_playbackRate; float m_playbackRate;
QString m_decoder; QString m_decoder;
QString m_wallpaperType;
}; };

View File

@ -0,0 +1,72 @@
#include "basewindow.h"
BaseWindow::BaseWindow(QObject* parent)
: QObject(parent)
{
}
BaseWindow::BaseWindow(QString projectFilePath, QObject* parent)
: QObject(parent)
{
qRegisterMetaType<BaseWindow::WallpaperType>();
qmlRegisterType<BaseWindow>("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;
}
}

View File

@ -0,0 +1,163 @@
#pragma once
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QObject>
#include <QDebug>
#include <QString>
#include <QApplication>
#include <qqml.h>
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;
};

View File

@ -0,0 +1,30 @@
#include "windowsdesktopproperties.h"
WindowsDesktopProperties::WindowsDesktopProperties(QObject* parent)
: QObject(parent)
{
qmlRegisterType<WindowsDesktopProperties>();
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));
}
}

View File

@ -0,0 +1,120 @@
#pragma once
#include <QColor>
#include <QDebug>
#include <QObject>
#include <QPoint>
#include <QSettings>
#include <QString>
#include <QList>
#include <qqml.h>
#include <qt_windows.h>
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;
};

View File

@ -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<HWND*>(lparam) = FindWindowExW(nullptr, hwnd, L"WorkerW", nullptr);
return TRUE;
}
WinWindow::WinWindow(QVector<int>& activeScreensList, QString projectPath, QString id, QString volume)
: BaseWindow(projectPath)
{
m_windowHandle = reinterpret_cast<HWND>(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<LPARAM>(&m_windowHandleWorker));
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <QApplication>
#include <QDebug>
#include <QObject>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickView>
#include <QScreen>
#include <QVector>
#include <memory>
#include "windowsdesktopproperties.h"
#include <QSettings>
#include <QString>
#include <qt_windows.h>
#include "basewindow.h"
class WinWindow : public BaseWindow {
Q_OBJECT
public:
explicit WinWindow(QVector<int>& 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;
};