From afa8159f3496e4fcd49e35ee4d4ac38429df4329 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 25 Aug 2021 18:55:26 +0200 Subject: [PATCH] Split Interactive console into it's own binary Plasma had an entire text editor for the script engine in process. The method for loading this was very convoluted, it loaded a QML file (in plasma-desktop!) which contained an Item(!) which then loaded a QML component that showed a widget dialog. It also means loading a very heavy lib KTextEditor in ShellComponents which is very heavy. This will get loaded in things like the logout greeter, which is super slow on the pinephone. We already had the concept of evalating a script remotely, we can just hook up the print statement and move the whole executable out. Technically this is an API break on the plasmashell DBus API, but pragmatically going from void -> something on the return type isn't going to break anything. --- CMakeLists.txt | 5 +- components/shellprivate/CMakeLists.txt | 12 --- .../shellprivate/shellprivateplugin.cpp | 7 -- interactiveconsole/CMakeLists.txt | 8 ++ .../interactiveconsole.cpp | 22 +++-- .../interactiveconsole.h | 64 ------------- interactiveconsole/main.cpp | 16 ++++ shell/dbus/org.kde.PlasmaShell.xml | 11 +-- shell/shellcorona.cpp | 92 +++---------------- shell/shellcorona.h | 9 +- 10 files changed, 60 insertions(+), 186 deletions(-) create mode 100644 interactiveconsole/CMakeLists.txt rename {components/shellprivate/interactiveconsole => interactiveconsole}/interactiveconsole.cpp (94%) rename {components/shellprivate/interactiveconsole => interactiveconsole}/interactiveconsole.h (58%) create mode 100644 interactiveconsole/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ffdc50348..98c7282ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma DocTools Runner Notifications NotifyConfig Su NewStuff Wallet IdleTime Declarative I18n KCMUtils TextWidgets Crash GlobalAccel DBusAddons Wayland CoreAddons People ActivitiesStats Activities KIO Prison PlasmaQuick Package - GuiAddons Archive ItemModels IconThemes UnitConversion ItemModels Init) + GuiAddons Archive ItemModels IconThemes UnitConversion ItemModels Init TextEditor) find_package(KDED CONFIG REQUIRED) @@ -72,7 +72,6 @@ set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching" PURPOSE "Needed for the File Search runner." ) -find_package(KF5TextEditor) find_package(KWinDBusInterface CONFIG REQUIRED) find_package(KF5Screen CONFIG REQUIRED) @@ -255,6 +254,8 @@ add_subdirectory(ktimezoned) add_subdirectory(menu) add_subdirectory(phonon) +add_subdirectory(interactiveconsole) + # This ensures pressing the eject button on a CD drive ejects the disc # It listens to the Solid::OpticalDrive::ejectPressed signal that is only # supported by Solid in the HAL backend and does nothing with UDev diff --git a/components/shellprivate/CMakeLists.txt b/components/shellprivate/CMakeLists.txt index 5d2a7899d..64ecf659f 100644 --- a/components/shellprivate/CMakeLists.txt +++ b/components/shellprivate/CMakeLists.txt @@ -1,10 +1,4 @@ -if (KF5TextEditor_FOUND) - set(interactiveconsole_SRCS - interactiveconsole/interactiveconsole.cpp - ) -endif (KF5TextEditor_FOUND) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-shellprivate.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-shellprivate.h) set(plasmashellprivateplugin_SRCS @@ -13,7 +7,6 @@ set(plasmashellprivateplugin_SRCS widgetexplorer/openwidgetassistant.cpp widgetexplorer/widgetexplorer.cpp shellprivateplugin.cpp - ${interactiveconsole_SRCS} ) add_library(plasmashellprivateplugin SHARED ${plasmashellprivateplugin_SRCS}) @@ -37,11 +30,6 @@ target_link_libraries(plasmashellprivateplugin KF5::TextWidgets ) -if (KF5TextEditor_FOUND) - target_link_libraries(plasmashellprivateplugin - KF5::TextEditor) -endif (KF5TextEditor_FOUND) - install(TARGETS plasmashellprivateplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/shell) install(FILES wallpaperplugin.knsrc diff --git a/components/shellprivate/shellprivateplugin.cpp b/components/shellprivate/shellprivateplugin.cpp index 4792610c4..a3a1da5d5 100644 --- a/components/shellprivate/shellprivateplugin.cpp +++ b/components/shellprivate/shellprivateplugin.cpp @@ -12,17 +12,10 @@ #include "widgetexplorer/widgetexplorer.h" #include -#if KF5TextEditor_FOUND -#include "interactiveconsole/interactiveconsole.h" -#endif - void PlasmaShellPrivatePlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.shell")); qmlRegisterType(); qmlRegisterType(uri, 2, 0, "WidgetExplorer"); -#if KF5TextEditor_FOUND - qmlRegisterType(uri, 2, 0, "InteractiveConsoleWindow"); -#endif } diff --git a/interactiveconsole/CMakeLists.txt b/interactiveconsole/CMakeLists.txt new file mode 100644 index 000000000..5999fad81 --- /dev/null +++ b/interactiveconsole/CMakeLists.txt @@ -0,0 +1,8 @@ +set(interactiveconsole_SRCS + interactiveconsole.cpp + main.cpp +) + +add_executable(plasma-interactiveconsole ${interactiveconsole_SRCS}) +target_link_libraries(plasma-interactiveconsole Qt::Widgets Qt::DBus KF5::KIOCore KF5::WidgetsAddons KF5::ConfigWidgets KF5::TextEditor KF5::Package) +install(TARGETS plasma-interactiveconsole ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/components/shellprivate/interactiveconsole/interactiveconsole.cpp b/interactiveconsole/interactiveconsole.cpp similarity index 94% rename from components/shellprivate/interactiveconsole/interactiveconsole.cpp rename to interactiveconsole/interactiveconsole.cpp index 724e5012e..dc42970cd 100644 --- a/components/shellprivate/interactiveconsole/interactiveconsole.cpp +++ b/interactiveconsole/interactiveconsole.cpp @@ -46,6 +46,7 @@ // interactive help? static const QString s_autosaveFileName(QStringLiteral("interactiveconsoleautosave.js")); static const QString s_kwinService = QStringLiteral("org.kde.KWin"); +static const QString s_plasmaShellService = QStringLiteral("org.kde.plasmashell"); InteractiveConsole::InteractiveConsole(QWidget *parent) : QDialog(parent) @@ -504,9 +505,6 @@ void InteractiveConsole::reenableEditor(KJob *job) void InteractiveConsole::evaluateScript() { // qDebug() << "evaluating" << m_editor->toPlainText(); - const QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + s_autosaveFileName; - saveScript(QUrl::fromLocalFile(path)); - m_output->moveCursor(QTextCursor::End); QTextCursor cursor = m_output->textCursor(); m_output->setTextCursor(cursor); @@ -531,11 +529,23 @@ void InteractiveConsole::evaluateScript() t.start(); if (m_mode == PlasmaConsole) { - if (m_scriptEngine) { - const QString script = m_editorPart ? m_editorPart->text() : m_editor->toPlainText(); - QMetaObject::invokeMethod(m_scriptEngine, "evaluateScript", Q_ARG(QString, script)); + QDBusMessage message = QDBusMessage::createMethodCall(s_plasmaShellService, + QStringLiteral("/PlasmaShell"), + QStringLiteral("org.kde.PlasmaShell"), + QStringLiteral("evaluateScript")); + QList arguments; + arguments << QVariant(m_editorPart->text()); + message.setArguments(arguments); + QDBusMessage reply = QDBusConnection::sessionBus().call(message); + if (reply.type() == QDBusMessage::ErrorMessage) { + print(reply.errorMessage()); + } else { + print(reply.arguments().first().toString()); } } else if (m_mode == KWinConsole) { + const QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + s_autosaveFileName; + saveScript(QUrl::fromLocalFile(path)); + QDBusMessage message = QDBusMessage::createMethodCall(s_kwinService, QStringLiteral("/Scripting"), QString(), QStringLiteral("loadScript")); QList arguments; arguments << QVariant(path); diff --git a/components/shellprivate/interactiveconsole/interactiveconsole.h b/interactiveconsole/interactiveconsole.h similarity index 58% rename from components/shellprivate/interactiveconsole/interactiveconsole.h rename to interactiveconsole/interactiveconsole.h index cc552f1a5..69432e000 100644 --- a/components/shellprivate/interactiveconsole/interactiveconsole.h +++ b/interactiveconsole/interactiveconsole.h @@ -106,67 +106,3 @@ private: ConsoleMode m_mode; QPointer m_scriptEngine; }; - -class InteractiveConsoleItem : public QObject -{ - Q_OBJECT - Q_PROPERTY(QObject *scriptEngine READ scriptEngine WRITE setScriptInterface NOTIFY scriptEngineChanged) - Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged) - Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) - -public: - InteractiveConsoleItem() - : QObject(nullptr) - , m_dialog(new InteractiveConsole(nullptr)) - { - connect(m_dialog, &InteractiveConsole::scriptEngineChanged, this, &InteractiveConsoleItem::scriptEngineChanged); - connect(m_dialog, &InteractiveConsole::modeChanged, this, &InteractiveConsoleItem::modeChanged); - connect(m_dialog, &InteractiveConsole::visibleChanged, this, &InteractiveConsoleItem::visibleChanged); - } - - ~InteractiveConsoleItem() override - { - m_dialog->deleteLater(); - } - - void setMode(const QString &mode) - { - m_dialog->setMode(mode); - } - QString mode() const - { - return m_dialog->mode(); - } - - void setScriptInterface(QObject *obj) - { - m_dialog->setScriptInterface(obj); - } - QObject *scriptEngine() const - { - return m_dialog->scriptEngine(); - } - - void setVisible(bool visible) const - { - visible ? m_dialog->show() : m_dialog->hide(); - } - bool isVisible() const - { - return m_dialog->isVisible(); - } - -public Q_SLOTS: - void loadScript(const QString &path) - { - m_dialog->loadScript(path); - } - -Q_SIGNALS: - void scriptEngineChanged(); - void modeChanged(); - void visibleChanged(bool); - -private: - InteractiveConsole *m_dialog; -}; diff --git a/interactiveconsole/main.cpp b/interactiveconsole/main.cpp new file mode 100644 index 000000000..b036d7908 --- /dev/null +++ b/interactiveconsole/main.cpp @@ -0,0 +1,16 @@ +/* + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "interactiveconsole.h" +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + // set to delete on close + auto console = new InteractiveConsole; + console->show(); + app.exec(); +} diff --git a/shell/dbus/org.kde.PlasmaShell.xml b/shell/dbus/org.kde.PlasmaShell.xml index 72e8ca5ef..8583e0732 100644 --- a/shell/dbus/org.kde.PlasmaShell.xml +++ b/shell/dbus/org.kde.PlasmaShell.xml @@ -11,18 +11,9 @@ - - - - - - - - - - + diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp index 926de29ec..d453b51d2 100644 --- a/shell/shellcorona.cpp +++ b/shell/shellcorona.cpp @@ -80,7 +80,6 @@ ShellCorona::ShellCorona(QObject *parent) , m_activityController(new KActivities::Controller(this)) , m_addPanelAction(nullptr) , m_addPanelsMenu(nullptr) - , m_interactiveConsole(nullptr) , m_waylandPlasmaShell(nullptr) , m_closingDown(false) , m_strutManager(new StrutManager(this)) @@ -1425,102 +1424,41 @@ void ShellCorona::toggleDashboard() setDashboardShown(!KWindowSystem::showingDesktop()); } -void ShellCorona::loadInteractiveConsole() -{ - if (KSharedConfig::openConfig()->isImmutable() || !KAuthorized::authorize(QStringLiteral("plasma-desktop/scripting_console"))) { - delete m_interactiveConsole; - m_interactiveConsole = nullptr; - return; - } - - if (!m_interactiveConsole) { - const QUrl consoleQML = kPackage().fileUrl("interactiveconsole"); - if (consoleQML.isEmpty()) { - return; - } - - m_interactiveConsole = new KDeclarative::QmlObjectSharedEngine(this); - m_interactiveConsole->setInitializationDelayed(true); - m_interactiveConsole->setSource(consoleQML); - - QObject *engine = new WorkspaceScripting::ScriptEngine(this, m_interactiveConsole); - m_interactiveConsole->rootContext()->setContextProperty(QStringLiteral("scriptEngine"), engine); - - m_interactiveConsole->completeInitialization(); - if (m_interactiveConsole->rootObject()) { - connect(m_interactiveConsole->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(interactiveConsoleVisibilityChanged(bool))); - } - } -} - -void ShellCorona::showInteractiveConsole() -{ - loadInteractiveConsole(); - if (m_interactiveConsole && m_interactiveConsole->rootObject()) { - m_interactiveConsole->rootObject()->setProperty("mode", "desktop"); - m_interactiveConsole->rootObject()->setProperty("visible", true); - } -} - -void ShellCorona::loadScriptInInteractiveConsole(const QString &script) -{ - showInteractiveConsole(); - if (m_interactiveConsole) { - m_interactiveConsole->rootObject()->setProperty("script", script); - } -} - -void ShellCorona::showInteractiveKWinConsole() -{ - loadInteractiveConsole(); - - if (m_interactiveConsole && m_interactiveConsole->rootObject()) { - m_interactiveConsole->rootObject()->setProperty("mode", "windowmanager"); - m_interactiveConsole->rootObject()->setProperty("visible", true); - } -} - -void ShellCorona::loadKWinScriptInInteractiveConsole(const QString &script) -{ - showInteractiveKWinConsole(); - if (m_interactiveConsole) { - m_interactiveConsole->rootObject()->setProperty("script", script); - } -} - -void ShellCorona::evaluateScript(const QString &script) +QString ShellCorona::evaluateScript(const QString &script) { if (calledFromDBus()) { if (immutability() == Plasma::Types::SystemImmutable) { sendErrorReply(QDBusError::Failed, QStringLiteral("Widgets are locked")); - return; + return QString(); } else if (!KAuthorized::authorize(QStringLiteral("plasma-desktop/scripting_console"))) { sendErrorReply(QDBusError::Failed, QStringLiteral("Administrative policies prevent script execution")); - return; + return QString(); } } WorkspaceScripting::ScriptEngine scriptEngine(this); + QString buffer; + QTextStream bufferStream(&buffer, QIODevice::WriteOnly | QIODevice::Text); - connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { + connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [&bufferStream](const QString &msg) { qWarning() << msg; + bufferStream << msg; }); - connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { + connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [&bufferStream](const QString &msg) { qDebug() << msg; + bufferStream << msg; }); scriptEngine.evaluateScript(script); - if (!scriptEngine.errorString().isEmpty() && calledFromDBus()) { + + bufferStream.flush(); + + if (calledFromDBus() && !scriptEngine.errorString().isEmpty()) { sendErrorReply(QDBusError::Failed, scriptEngine.errorString()); + return QString(); } -} -void ShellCorona::interactiveConsoleVisibilityChanged(bool visible) -{ - if (!visible) { - m_interactiveConsole->deleteLater(); - m_interactiveConsole = nullptr; - } + return buffer; } void ShellCorona::checkActivities() diff --git a/shell/shellcorona.h b/shell/shellcorona.h index 9987793f7..8d5008768 100644 --- a/shell/shellcorona.h +++ b/shell/shellcorona.h @@ -128,14 +128,9 @@ public Q_SLOTS: /// DBUS methods void toggleDashboard(); void setDashboardShown(bool show); - void loadInteractiveConsole(); - void showInteractiveConsole(); - void loadScriptInInteractiveConsole(const QString &script); - void showInteractiveKWinConsole(); - void loadKWinScriptInInteractiveConsole(const QString &script); void toggleActivityManager(); void toggleWidgetExplorer(); - void evaluateScript(const QString &string); + QString evaluateScript(const QString &string); void activateLauncherMenu(); QByteArray dumpCurrentLayoutJS() const; @@ -205,7 +200,6 @@ private Q_SLOTS: void primaryOutputChanged(); void panelContainmentDestroyed(QObject *cont); - void interactiveConsoleVisibilityChanged(bool visible); void handleScreenRemoved(QScreen *screen); void activateTaskManagerEntry(int index); @@ -244,7 +238,6 @@ private: QScopedPointer m_addPanelsMenu; KPackage::Package m_lookAndFeelPackage; QSet m_redundantOutputs; - KDeclarative::QmlObjectSharedEngine *m_interactiveConsole; QTimer m_waitingPanelsTimer; QTimer m_appConfigSyncTimer;