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:
parent
f7336a4ba3
commit
31757b4e31
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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}
|
||||
}
|
||||
##^##*/
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,5 @@ Theme=Light
|
||||
Accent=Orange
|
||||
Primary=White
|
||||
Foreground=Grey
|
||||
|
||||
|
||||
[Material\Font]
|
||||
Family=Roboto
|
||||
PixelSize=14
|
||||
Font\Family=Roboto
|
||||
Font\PixelSize=12
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
374
ScreenPlay/src/createimportvideo.cpp
Normal file
374
ScreenPlay/src/createimportvideo.cpp
Normal 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;
|
||||
}
|
77
ScreenPlay/src/createimportvideo.h
Normal file
77
ScreenPlay/src/createimportvideo.h
Normal 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;
|
||||
};
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
72
ScreenPlayWindow/src/basewindow.cpp
Normal file
72
ScreenPlayWindow/src/basewindow.cpp
Normal 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;
|
||||
}
|
||||
}
|
163
ScreenPlayWindow/src/basewindow.h
Normal file
163
ScreenPlayWindow/src/basewindow.h
Normal 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;
|
||||
};
|
30
ScreenPlayWindow/src/windowsdesktopproperties.cpp
Normal file
30
ScreenPlayWindow/src/windowsdesktopproperties.cpp
Normal 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));
|
||||
}
|
||||
}
|
120
ScreenPlayWindow/src/windowsdesktopproperties.h
Normal file
120
ScreenPlayWindow/src/windowsdesktopproperties.h
Normal 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;
|
||||
};
|
136
ScreenPlayWindow/src/winwindow.cpp
Normal file
136
ScreenPlayWindow/src/winwindow.cpp
Normal 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));
|
||||
}
|
45
ScreenPlayWindow/src/winwindow.h
Normal file
45
ScreenPlayWindow/src/winwindow.h
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user