1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-09-02 08:39:49 +02: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
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 \

View File

@ -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

View File

@ -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 {

View File

@ -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}
}
##^##*/

View File

@ -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

View File

@ -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"
// }
}
}

View File

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

View File

@ -6,9 +6,8 @@ Create::Create(Settings* st, QMLUtilities* util, QObject* parent)
m_settings = st;
m_utils = util;
m_futureWatcher.setFuture(m_future);
qRegisterMetaType<Create::State>();
qRegisterMetaType<CreateImportVideo::State>();
qmlRegisterType<Create>("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<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;
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<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;
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;

View File

@ -19,8 +19,10 @@
#include <QTimer>
#include <QUrl>
#include <QtMath>
#include <QTimer>
#include <memory>
#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<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,
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
@ -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();
});
}

View File

@ -16,6 +16,8 @@
#include <QUrl>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
#include <QFuture>
/*!
@ -81,4 +83,6 @@ signals:
private:
QVector<ProjectFile> m_screenPlayFiles;
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 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);
}

View File

@ -10,7 +10,7 @@ SDKConnector::SDKConnector(QObject* parent)
m_server = QSharedPointer<QLocalServer>(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
}
}

View File

@ -1,5 +1,9 @@
#include "../ScreenPlaySDK/screenplaysdk.h"
#include "src/SPWmainwindow.h"
#if defined(Q_OS_WIN)
#include "src/winwindow.h"
#endif
#include <QApplication>
#include <QObject>
#include <QStringList>
@ -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<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
//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();
}

View File

@ -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

View File

@ -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;

View File

@ -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
}
}
}

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
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<HWND>(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<QQuickView>(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();

View File

@ -13,6 +13,7 @@
#include <QSharedPointer>
#include <QWindow>
#include <QtGlobal>
#include <QTimer>
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickWindow>
@ -20,18 +21,18 @@
#include <qt_windows.h>
#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<int> 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<int> 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<QQuickView> m_quickRenderer = nullptr;
QUrl m_projectPath;
QString m_projectFile;
QJsonObject m_project;
@ -240,7 +242,7 @@ private:
QString m_appID;
QVector<int> 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;
};

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;
};