Commit 72777c5d authored by Vincent Pinon's avatar Vincent Pinon
Browse files

Use localserver/localsocket rather than DBus (Mac, Windows)

parent 3a24da5d
......@@ -24,6 +24,7 @@ option(RELEASE_BUILD "Remove Git revision from program version" ON)
option(BUILD_TESTING "Build tests" ON)
option(CRASH_AUTO_TEST "Auto-generate testcases upon some crashes (uses RTTR library, needed for fuzzing)" OFF)
option(BUILD_FUZZING "Build fuzzing target" OFF)
option(USE_DBUS "Build with DBus IPC" ON)
# Minimum versions of main dependencies.
set(MLT_MIN_MAJOR_VERSION 7)
......@@ -48,12 +49,16 @@ include(ECMEnableSanitizers)
include(ECMAddQch)
add_definitions(-DTRANSLATION_DOMAIN=\"kdenlive\")
find_package(KF5 REQUIRED COMPONENTS Archive Bookmarks CoreAddons Config ConfigWidgets
DBusAddons KIO WidgetsAddons NotifyConfig NewStuff XmlGui Notifications GuiAddons TextWidgets IconThemes Declarative Solid
KIO WidgetsAddons NotifyConfig NewStuff XmlGui Notifications GuiAddons TextWidgets IconThemes Declarative Solid
OPTIONAL_COMPONENTS DocTools FileMetaData Crash Purpose)
# Qt
set(QT_MIN_VERSION 5.11.0)
find_package(Qt5 REQUIRED COMPONENTS Core DBus Widgets Svg Quick QuickControls2 Concurrent QuickWidgets Multimedia NetworkAuth)
if(USE_DBUS)
find_package(KF5 REQUIRED COMPONENTS DBusAddons)
find_package(Qt5 REQUIRED COMPONENTS DBus)
endif()
add_definitions(-DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
......
set(QT_DONT_USE_QTGUI 1)
if(USE_DBUS)
set(QT_USE_QTDBUS 1)
endif()
include_directories(
${MLT_INCLUDE_DIR}
${MLTPP_INCLUDE_DIR}
......@@ -18,6 +19,11 @@ ecm_mark_nongui_executable(kdenlive_render)
target_link_libraries(kdenlive_render Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Xml
${MLT_LIBRARIES}
${MLTPP_LIBRARIES})
if(USE_DBUS)
target_link_libraries(kdenlive_render Qt5::DBus)
target_compile_definitions(kdenlive_render PRIVATE USE_DBUS)
else()
target_link_libraries(kdenlive_render Qt5::Network)
endif()
install(TARGETS kdenlive_render DESTINATION ${BIN_INSTALL_DIR})
......@@ -22,8 +22,15 @@
#include <QFile>
#include <QStringList>
#include <QThread>
#ifdef USE_DBUS
#include <QtDBus>
#else
#include <QJsonObject>
#include <QJsonDocument>
#endif
#include <QDir>
#include <QElapsedTimer>
#include <QDebug>
#include <utility>
// Can't believe I need to do this to sleep.
class SleepThread : QThread
......@@ -40,8 +47,12 @@ RenderJob::RenderJob(const QString &render, const QString &scenelist, const QStr
, m_progress(0)
, m_prog(std::move(render))
, m_player()
#ifdef USE_DBUS
, m_jobUiserver(nullptr)
, m_kdenliveinterface(nullptr)
#else
, m_kdenlivesocket(new QLocalSocket(this))
#endif
, m_usekuiserver(true)
, m_logfile(target + QStringLiteral(".log"))
, m_erase(scenelist.startsWith(QDir::tempPath()) || scenelist.startsWith(QString("xml:%2").arg(QDir::tempPath())))
......@@ -76,8 +87,15 @@ RenderJob::RenderJob(const QString &render, const QString &scenelist, const QStr
RenderJob::~RenderJob()
{
#ifdef USE_DBUS
delete m_jobUiserver;
delete m_kdenliveinterface;
#else
if (m_kdenlivesocket->state() == QLocalSocket::ConnectedState) {
m_kdenlivesocket->disconnectFromServer();
}
delete m_kdenlivesocket;
#endif
delete m_renderProcess;
m_logfile.close();
}
......@@ -89,17 +107,36 @@ void RenderJob::slotAbort(const QString &url)
}
}
void RenderJob::slotAbort()
{
qWarning() << "Job aborted by user...";
m_renderProcess->kill();
void RenderJob::sendFinish(int status, QString error) {
#ifdef USE_DBUS
if (m_kdenliveinterface) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), {m_dest, -3, QString()});
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), {m_dest, status, error});
}
if (m_jobUiserver) {
if (status > -3) {
m_jobUiserver->call(QStringLiteral("setDescriptionField"), 1, tr("Rendered file"), m_dest);
}
m_jobUiserver->call(QStringLiteral("terminate"), QString());
}
#else
if (m_kdenlivesocket) {
QJsonObject method, args;
args["url"] = m_dest;
args["status"] = status;
args["error"] = error;
method["setRenderingFinished"] = args;
m_kdenlivesocket->write(QJsonDocument(method).toJson());
m_kdenlivesocket->flush();
}
#endif
}
void RenderJob::slotAbort()
{
qWarning() << "Job aborted by user...";
m_renderProcess->kill();
sendFinish(-3, QString());
if (m_erase) {
QFile(m_scenelist).remove();
}
......@@ -107,7 +144,7 @@ void RenderJob::slotAbort()
m_logstream << "Job aborted by user" << "\n";
m_logstream.flush();
m_logfile.close();
qApp->quit();
//qApp->quit();
}
void RenderJob::receivedStderr()
......@@ -128,14 +165,16 @@ void RenderJob::receivedStderr()
} else if (m_args.contains(QStringLiteral("pass=2"))) {
m_progress = 50 + m_progress / 2;
}
if ((m_kdenliveinterface != nullptr) && m_kdenliveinterface->isValid()) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingProgress"), {m_dest, m_progress, frame});
}
#ifdef USE_DBUS
qint64 elapsedTime = m_startTime.secsTo(QDateTime::currentDateTime());
if (elapsedTime == m_seconds) {
return;
}
int speed = (frame - m_frame) / (elapsedTime - m_seconds);
m_seconds = elapsedTime;
if ((m_kdenliveinterface != nullptr) && m_kdenliveinterface->isValid()) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingProgress"), {m_dest, m_progress, frame});
}
if (m_jobUiserver) {
qint64 remaining = elapsedTime * (100 - progress) / progress;
int days = int(remaining / 86400);
......@@ -152,14 +191,26 @@ void RenderJob::receivedStderr()
m_jobUiserver->call(QStringLiteral("setProcessedAmount"), qulonglong(frame - m_framein), tr("frames"));
m_jobUiserver->call(QStringLiteral("setSpeed"), qulonglong(speed));
}
m_seconds = int(elapsedTime);
m_logstream << QStringLiteral("%1\t%2\t%3\n").arg(m_seconds).arg(m_frame).arg(m_progress);
#else
if (m_kdenlivesocket) {
QJsonObject method, args;
args["url"] = m_dest;
args["progress"] = m_progress;
args["frame"] = frame;
method["setRenderingProgress"] = args;
m_kdenlivesocket->write(QJsonDocument(method).toJson());
m_kdenlivesocket->flush();
}
#endif
m_frame = frame;
m_logstream << QStringLiteral("%1\t%2\t%3\t%4\n").arg(m_seconds).arg(m_frame).arg(m_progress).arg(speed);
}
}
void RenderJob::start()
{
m_startTime = QDateTime::currentDateTime();
#ifdef USE_DBUS
QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
if ((interface != nullptr) && m_usekuiserver) {
if (!interface->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) {
......@@ -186,7 +237,6 @@ void RenderJob::start()
QString dbusView = QStringLiteral("org.kde.JobViewV2");
m_jobUiserver = new QDBusInterface(QStringLiteral("org.kde.JobViewServer"), reply, dbusView);
if ((m_jobUiserver != nullptr) && m_jobUiserver->isValid()) {
m_startTime = QDateTime::currentDateTime();
if (!m_args.contains(QStringLiteral("pass=2"))) {
m_jobUiserver->call(QStringLiteral("setPercent"), 0);
}
......@@ -201,6 +251,20 @@ void RenderJob::start()
if (m_pid > -1) {
initKdenliveDbusInterface();
}
#else
connect(m_kdenlivesocket, &QLocalSocket::connected, this, [this](){
m_kdenlivesocket->write(QJsonDocument({{"url", m_dest}}).toJson());
m_kdenlivesocket->flush();
});
connect(m_kdenlivesocket, &QLocalSocket::readyRead, this, [this](){
QByteArray msg = m_kdenlivesocket->readAll();
if (msg == "abort") {
slotAbort();
}
});
QString servername = QStringLiteral("org.kde.kdenlive-%1").arg(m_pid);
m_kdenlivesocket->connectToServer(servername);
#endif
// Make sure the destination directory is writable
/*QFileInfo checkDestination(QFileInfo(m_dest).absolutePath());
......@@ -215,6 +279,7 @@ void RenderJob::start()
m_logstream.flush();
}
#ifdef USE_DBUS
void RenderJob::initKdenliveDbusInterface()
{
QString kdenliveId;
......@@ -245,6 +310,7 @@ void RenderJob::initKdenliveDbusInterface()
connect(m_kdenliveinterface, SIGNAL(abortRenderJob(QString)), this, SLOT(slotAbort(QString)));
}
}
#endif
void RenderJob::slotCheckProcess(QProcess::ProcessState state)
{
......@@ -255,15 +321,10 @@ void RenderJob::slotCheckProcess(QProcess::ProcessState state)
void RenderJob::slotIsOver(QProcess::ExitStatus status, bool isWritable)
{
if (m_jobUiserver) {
m_jobUiserver->call(QStringLiteral("setDescriptionField"), 1, tr("Rendered file"), m_dest);
m_jobUiserver->call(QStringLiteral("terminate"), QString());
}
if (!isWritable) {
QString error = tr("Cannot write to %1, check permissions.").arg(m_dest);
if (m_kdenliveinterface) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), {m_dest, -2, error});
}
sendFinish(-2, error);
// assumes kdialog installed!!
QProcess::startDetached(QStringLiteral("kdialog"), {QStringLiteral("--error"), error});
m_logstream << error << "\n";
emit renderingFinished();
......@@ -274,9 +335,7 @@ void RenderJob::slotIsOver(QProcess::ExitStatus status, bool isWritable)
}
if (status == QProcess::CrashExit || m_renderProcess->error() != QProcess::UnknownError || m_renderProcess->exitCode() != 0) {
// rendering crashed
if (m_kdenliveinterface) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), {m_dest, -2, m_errorMessage});
}
sendFinish(-2, m_errorMessage);
QStringList args;
QString error = tr("Rendering of %1 aborted, resulting video will probably be corrupted.").arg(m_dest);
if (m_frame > 0) {
......@@ -287,8 +346,8 @@ void RenderJob::slotIsOver(QProcess::ExitStatus status, bool isWritable)
QProcess::startDetached(QStringLiteral("kdialog"), args);
emit renderingFinished();
} else {
if (!m_dualpass && (m_kdenliveinterface != nullptr)) {
m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, QStringLiteral("setRenderingFinished"), {m_dest, -1, QString()});
if (!m_dualpass) {
sendFinish(-1, QString());
}
m_logstream << "Rendering of " << m_dest << " finished" << "\n";
if (!m_dualpass && m_player.length() > 3 && m_player.contains(QLatin1Char(' '))) {
......
......@@ -20,7 +20,11 @@
#ifndef RENDERJOB_H
#define RENDERJOB_H
#ifdef USE_DBUS
#include <QDBusInterface>
#else
#include <QLocalSocket>
#endif
#include <QObject>
#include <QProcess>
#include <QDateTime>
......@@ -52,8 +56,12 @@ private:
int m_progress;
QString m_prog;
QString m_player;
#ifdef USE_DBUS
QDBusInterface *m_jobUiserver;
QDBusInterface *m_kdenliveinterface;
#else
QLocalSocket* m_kdenlivesocket;
#endif
bool m_usekuiserver;
/** @brief Used to create a temporary file for logging. */
QFile m_logfile;
......@@ -72,7 +80,13 @@ private:
QStringList m_args;
/** @brief Used to write to the log file. */
QTextStream m_logstream;
#ifdef USE_DBUS
void initKdenliveDbusInterface();
#else
void fromServer();
#endif
void sendFinish(int status, QString error);
void sendProgress();
signals:
void renderingFinished();
......
......@@ -77,7 +77,11 @@ list(APPEND kdenlive_SRCS ${top_SRCS})
kconfig_add_kcfg_files(kdenlive_SRCS kdenlivesettings.kcfgc)
install(FILES kdenlivesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR})
ecm_qt_declare_logging_category(kdenlive_SRCS HEADER kdenlive_debug.h IDENTIFIER KDENLIVE_LOG CATEGORY_NAME org.kde.multimedia.kdenlive)
qt5_add_dbus_adaptor(kdenlive_SRCS org.kdenlive.MainWindow.xml mainwindow.h MainWindow)
if(USE_DBUS)
qt5_add_dbus_adaptor(kdenlive_SRCS org.kdenlive.MainWindow.xml mainwindow.h MainWindow)
else()
add_subdirectory(render)
endif()
## UI's
file(GLOB kdenlive_UIS "ui/*.ui")
ki18n_wrap_ui(kdenlive_UIS ${kdenlive_UIS})
......@@ -125,7 +129,6 @@ target_link_libraries(kdenliveLib
KF5::KIOWidgets
KF5::NotifyConfig
KF5::NewStuff
KF5::DBusAddons
KF5::XmlGui
KF5::GuiAddons
KF5::Notifications
......@@ -147,6 +150,9 @@ target_link_libraries(kdenliveLib
${CMAKE_DL_LIBS}
${CMAKE_THREAD_LIBS_INIT}
kiss_fft)
if(USE_DBUS)
target_link_libraries(kdenliveLib KF5::DBusAddons)
endif()
if(BUILD_COVERAGE)
target_link_libraries(kdenliveLib gcov)
endif()
......@@ -204,6 +210,10 @@ if(HAVE_LINUX_INPUT_H)
target_link_libraries(kdenliveLib media_ctrl)
endif()
if (USE_DBUS)
target_compile_definitions(kdenliveLib PRIVATE -DUSE_DBUS)
endif()
if (BUILD_QCH)
ecm_add_qch(
Kdenlive_QCH
......
......@@ -43,7 +43,9 @@
#include "definitions.h"
#include "kdenlive_debug.h"
#ifdef USE_DBUS
#include <KDBusService>
#endif
#include <KIconTheme>
#include <kiconthemes_version.h>
#include <QResource>
......@@ -158,8 +160,10 @@ int main(int argc, char *argv[])
qputenv("XDG_CURRENT_DESKTOP","KDE");
#endif
#ifdef USE_DBUS
// Init DBus services
KDBusService programDBusService(KDBusService::NoExitOnFailure);
KDBusService programDBusService();
#endif
bool forceBreeze = grp.readEntry("force_breeze", QVariant(false)).toBool();
if (forceBreeze) {
bool darkBreeze = grp.readEntry("use_dark_breeze", QVariant(false)).toBool();
......
......@@ -46,7 +46,11 @@
#include "layoutmanagement.h"
#include "library/librarywidget.h"
#include "audiomixer/mixermanager.hpp"
#ifdef USE_DBUS
#include "mainwindowadaptor.h"
#else
#include "render/renderserver.h"
#endif
#include "mltconnection.h"
#include "mltcontroller/clipcontroller.h"
#include "monitor/monitor.h"
......@@ -211,7 +215,9 @@ void MainWindow::init(const QString &mltPath)
connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);
// QIcon::setThemeSearchPaths(QStringList() <<QStringLiteral(":/icons/"));
#ifdef USE_DBUS
new RenderingAdaptor(this);
#endif
QString defaultProfile = KdenliveSettings::default_profile();
// Initialise MLT connection
......@@ -835,6 +841,9 @@ void MainWindow::init(const QString &mltPath)
}
});
// m_messageLabel->setMessage(QStringLiteral("This is a beta version. Always backup your data"), MltError);
#ifndef USE_DBUS
new RenderServer(this);
#endif
}
void MainWindow::slotThemeChanged(const QString &name)
......@@ -2283,10 +2292,12 @@ void MainWindow::scriptRender(const QString &url)
m_renderWidget->slotPrepareExport(true, url);
}
#ifdef USE_DBUS
void MainWindow::exitApp()
{
QApplication::exit(0);
}
#endif
void MainWindow::slotCleanProject()
{
......@@ -3821,6 +3832,7 @@ void MainWindow::slotShutdown()
{
pCore->currentDoc()->setModified(false);
// Call shutdown
#ifdef USE_DBUS
QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
if ((interface != nullptr) && interface->isServiceRegistered(QStringLiteral("org.kde.ksmserver"))) {
QDBusInterface smserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QStringLiteral("org.kde.KSMServerInterface"));
......@@ -3830,6 +3842,7 @@ void MainWindow::slotShutdown()
QStringLiteral("org.gnome.SessionManager"));
smserver.call(QStringLiteral("Shutdown"));
}
#endif
}
void MainWindow::slotSwitchMonitors()
......
......@@ -21,7 +21,9 @@
#define MAINWINDOW_H
#include <QComboBox>
#ifdef USE_DBUS
#include <QDBusAbstractAdaptor>
#endif
#include <QDockWidget>
#include <QEvent>
#include <QImage>
......@@ -300,7 +302,9 @@ public slots:
Q_SCRIPTABLE void addTimelineClip(const QString &url);
Q_SCRIPTABLE void addEffect(const QString &effectId);
Q_SCRIPTABLE void scriptRender(const QString &url);
#ifdef USE_DBUS
Q_NOREPLY void exitApp();
#endif
void slotSwitchVideoThumbs();
void slotSwitchAudioThumbs();
......
set(kdenlive_SRCS
${kdenlive_SRCS}
render/renderserver.cpp
PARENT_SCOPE)
/*
SPDX-FileCopyrightText: 2021 Vincent Pinon <vpinon@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "renderserver.h"
#include "core.h"
#include "mainwindow.h"
#include <KI18n/KLocalizedString>
#include <QCoreApplication>
#include <QJsonDocument>
RenderServer::RenderServer(QObject *parent)
: QObject(parent)
{
qWarning() << "Starting render server";
m_server.setSocketOptions(QLocalServer::UserAccessOption);
QString servername = QStringLiteral("org.kde.kdenlive-%1").arg(QCoreApplication::applicationPid());
if (m_server.listen(servername)) {
connect(&m_server, &QLocalServer::newConnection, this, &RenderServer::jobConnected);
} else {
pCore->displayMessage(i18n("Can't open communication with render job %1").arg(servername), ErrorMessage);
}
}
RenderServer::~RenderServer() {}
void RenderServer::jobConnected()
{
QLocalSocket* socket = m_server.nextPendingConnection();
connect(socket, &QLocalSocket::readyRead, this, &RenderServer::jobSent);
}
void RenderServer::jobSent() {
QJsonParseError error;
QLocalSocket* socket = reinterpret_cast<QLocalSocket*>(sender());
const QByteArray bytes = socket->readAll();
const QJsonObject json = QJsonDocument::fromJson(bytes, &error).object();
if (error.error != QJsonParseError::NoError) {
pCore->displayMessage(i18n("Communication error with render job %1 %2")
.arg(error.errorString(), bytes), ErrorMessage);
}
if (json.contains("url")) {
m_jobSocket[json["url"].toString()] = socket;
}
if (json.contains("setRenderingProgress")) {
pCore->window()->setRenderingProgress(
json["setRenderingProgress"]["url"].toString(),
json["setRenderingProgress"]["progress"].toInt(),
json["setRenderingProgress"]["frame"].toInt());
}
if (json.contains("setRenderingFinished")) {
pCore->window()->setRenderingFinished(
json["setRenderingFinished"]["url"].toString(),
json["setRenderingFinished"]["status"].toInt(),
json["setRenderingFinished"]["error"].toString());
m_jobSocket.remove(json["setRenderingFinished"]["url"].toString());
}
}
void RenderServer::abortJob(QString job) {
if (m_jobSocket.contains(job)) {
m_jobSocket[job]->write("abort");
m_jobSocket[job]->flush();
} else {
pCore->displayMessage(i18n("Can't open communication with render job %1").arg(job), ErrorMessage);
}
}
/*
SPDX-FileCopyrightText: 2021 Vincent Pinon <vpinon@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef RENDERSERVER_H
#define RENDERSERVER_H
#include <QJsonObject>
#include <QLocalServer>
#include <QLocalSocket>
#include <QPointer>
#include <QProcess>
class RenderServer : public QObject
{
Q_OBJECT
public:
RenderServer(QObject *parent);
~RenderServer() override;
public slots:
void abortJob(QString job);
private slots:
void jobConnected();
void jobSent();
private:
QLocalServer m_server;
QHash<QString, QLocalSocket*> m_jobSocket;
};
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment