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.
wilder-5.24
David Edmundson 5 years ago
parent 7fb7e4bb42
commit afa8159f34
  1. 5
      CMakeLists.txt
  2. 12
      components/shellprivate/CMakeLists.txt
  3. 7
      components/shellprivate/shellprivateplugin.cpp
  4. 8
      interactiveconsole/CMakeLists.txt
  5. 22
      interactiveconsole/interactiveconsole.cpp
  6. 64
      interactiveconsole/interactiveconsole.h
  7. 16
      interactiveconsole/main.cpp
  8. 11
      shell/dbus/org.kde.PlasmaShell.xml
  9. 92
      shell/shellcorona.cpp
  10. 9
      shell/shellcorona.h

@ -41,7 +41,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
Plasma DocTools Runner Notifications NotifyConfig Su NewStuff Wallet IdleTime Plasma DocTools Runner Notifications NotifyConfig Su NewStuff Wallet IdleTime
Declarative I18n KCMUtils TextWidgets Crash GlobalAccel DBusAddons Wayland Declarative I18n KCMUtils TextWidgets Crash GlobalAccel DBusAddons Wayland
CoreAddons People ActivitiesStats Activities KIO Prison PlasmaQuick Package 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) find_package(KDED CONFIG REQUIRED)
@ -72,7 +72,6 @@ set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching"
PURPOSE "Needed for the File Search runner." PURPOSE "Needed for the File Search runner."
) )
find_package(KF5TextEditor)
find_package(KWinDBusInterface CONFIG REQUIRED) find_package(KWinDBusInterface CONFIG REQUIRED)
find_package(KF5Screen CONFIG REQUIRED) find_package(KF5Screen CONFIG REQUIRED)
@ -255,6 +254,8 @@ add_subdirectory(ktimezoned)
add_subdirectory(menu) add_subdirectory(menu)
add_subdirectory(phonon) add_subdirectory(phonon)
add_subdirectory(interactiveconsole)
# This ensures pressing the eject button on a CD drive ejects the disc # This ensures pressing the eject button on a CD drive ejects the disc
# It listens to the Solid::OpticalDrive::ejectPressed signal that is only # It listens to the Solid::OpticalDrive::ejectPressed signal that is only
# supported by Solid in the HAL backend and does nothing with UDev # supported by Solid in the HAL backend and does nothing with UDev

@ -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) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-shellprivate.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-shellprivate.h)
set(plasmashellprivateplugin_SRCS set(plasmashellprivateplugin_SRCS
@ -13,7 +7,6 @@ set(plasmashellprivateplugin_SRCS
widgetexplorer/openwidgetassistant.cpp widgetexplorer/openwidgetassistant.cpp
widgetexplorer/widgetexplorer.cpp widgetexplorer/widgetexplorer.cpp
shellprivateplugin.cpp shellprivateplugin.cpp
${interactiveconsole_SRCS}
) )
add_library(plasmashellprivateplugin SHARED ${plasmashellprivateplugin_SRCS}) add_library(plasmashellprivateplugin SHARED ${plasmashellprivateplugin_SRCS})
@ -37,11 +30,6 @@ target_link_libraries(plasmashellprivateplugin
KF5::TextWidgets 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(TARGETS plasmashellprivateplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/shell)
install(FILES install(FILES
wallpaperplugin.knsrc wallpaperplugin.knsrc

@ -12,17 +12,10 @@
#include "widgetexplorer/widgetexplorer.h" #include "widgetexplorer/widgetexplorer.h"
#include <Plasma/Containment> #include <Plasma/Containment>
#if KF5TextEditor_FOUND
#include "interactiveconsole/interactiveconsole.h"
#endif
void PlasmaShellPrivatePlugin::registerTypes(const char *uri) void PlasmaShellPrivatePlugin::registerTypes(const char *uri)
{ {
Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.shell")); Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.shell"));
qmlRegisterType<Plasma::Containment>(); qmlRegisterType<Plasma::Containment>();
qmlRegisterType<WidgetExplorer>(uri, 2, 0, "WidgetExplorer"); qmlRegisterType<WidgetExplorer>(uri, 2, 0, "WidgetExplorer");
#if KF5TextEditor_FOUND
qmlRegisterType<InteractiveConsoleItem>(uri, 2, 0, "InteractiveConsoleWindow");
#endif
} }

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

@ -46,6 +46,7 @@
// interactive help? // interactive help?
static const QString s_autosaveFileName(QStringLiteral("interactiveconsoleautosave.js")); static const QString s_autosaveFileName(QStringLiteral("interactiveconsoleautosave.js"));
static const QString s_kwinService = QStringLiteral("org.kde.KWin"); static const QString s_kwinService = QStringLiteral("org.kde.KWin");
static const QString s_plasmaShellService = QStringLiteral("org.kde.plasmashell");
InteractiveConsole::InteractiveConsole(QWidget *parent) InteractiveConsole::InteractiveConsole(QWidget *parent)
: QDialog(parent) : QDialog(parent)
@ -504,9 +505,6 @@ void InteractiveConsole::reenableEditor(KJob *job)
void InteractiveConsole::evaluateScript() void InteractiveConsole::evaluateScript()
{ {
// qDebug() << "evaluating" << m_editor->toPlainText(); // qDebug() << "evaluating" << m_editor->toPlainText();
const QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + s_autosaveFileName;
saveScript(QUrl::fromLocalFile(path));
m_output->moveCursor(QTextCursor::End); m_output->moveCursor(QTextCursor::End);
QTextCursor cursor = m_output->textCursor(); QTextCursor cursor = m_output->textCursor();
m_output->setTextCursor(cursor); m_output->setTextCursor(cursor);
@ -531,11 +529,23 @@ void InteractiveConsole::evaluateScript()
t.start(); t.start();
if (m_mode == PlasmaConsole) { if (m_mode == PlasmaConsole) {
if (m_scriptEngine) { QDBusMessage message = QDBusMessage::createMethodCall(s_plasmaShellService,
const QString script = m_editorPart ? m_editorPart->text() : m_editor->toPlainText(); QStringLiteral("/PlasmaShell"),
QMetaObject::invokeMethod(m_scriptEngine, "evaluateScript", Q_ARG(QString, script)); QStringLiteral("org.kde.PlasmaShell"),
QStringLiteral("evaluateScript"));
QList<QVariant> 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) { } 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")); QDBusMessage message = QDBusMessage::createMethodCall(s_kwinService, QStringLiteral("/Scripting"), QString(), QStringLiteral("loadScript"));
QList<QVariant> arguments; QList<QVariant> arguments;
arguments << QVariant(path); arguments << QVariant(path);

@ -106,67 +106,3 @@ private:
ConsoleMode m_mode; ConsoleMode m_mode;
QPointer<QObject> m_scriptEngine; QPointer<QObject> 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;
};

@ -0,0 +1,16 @@
/*
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "interactiveconsole.h"
#include <QApplication>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// set to delete on close
auto console = new InteractiveConsole;
console->show();
app.exec();
}

@ -11,18 +11,9 @@
<method name="setDashboardShown"> <method name="setDashboardShown">
<arg name="show" type="b" direction="in"/> <arg name="show" type="b" direction="in"/>
</method> </method>
<method name="showInteractiveConsole">
</method>
<method name="loadScriptInInteractiveConsole">
<arg name="script" type="s" direction="in"/>
</method>
<method name="showInteractiveKWinConsole">
</method>
<method name="loadKWinScriptInInteractiveConsole">
<arg name="script" type="s" direction="in"/>
</method>
<method name="evaluateScript"> <method name="evaluateScript">
<arg name="script" type="s" direction="in"/> <arg name="script" type="s" direction="in"/>
<arg name="output" type="s" direction="out"/>
</method> </method>
<method name="dumpCurrentLayoutJS"> <method name="dumpCurrentLayoutJS">
<arg name="script" type="ay" direction="out"/> <arg name="script" type="ay" direction="out"/>

@ -80,7 +80,6 @@ ShellCorona::ShellCorona(QObject *parent)
, m_activityController(new KActivities::Controller(this)) , m_activityController(new KActivities::Controller(this))
, m_addPanelAction(nullptr) , m_addPanelAction(nullptr)
, m_addPanelsMenu(nullptr) , m_addPanelsMenu(nullptr)
, m_interactiveConsole(nullptr)
, m_waylandPlasmaShell(nullptr) , m_waylandPlasmaShell(nullptr)
, m_closingDown(false) , m_closingDown(false)
, m_strutManager(new StrutManager(this)) , m_strutManager(new StrutManager(this))
@ -1425,102 +1424,41 @@ void ShellCorona::toggleDashboard()
setDashboardShown(!KWindowSystem::showingDesktop()); setDashboardShown(!KWindowSystem::showingDesktop());
} }
void ShellCorona::loadInteractiveConsole() QString ShellCorona::evaluateScript(const QString &script)
{
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)
{ {
if (calledFromDBus()) { if (calledFromDBus()) {
if (immutability() == Plasma::Types::SystemImmutable) { if (immutability() == Plasma::Types::SystemImmutable) {
sendErrorReply(QDBusError::Failed, QStringLiteral("Widgets are locked")); sendErrorReply(QDBusError::Failed, QStringLiteral("Widgets are locked"));
return; return QString();
} else if (!KAuthorized::authorize(QStringLiteral("plasma-desktop/scripting_console"))) { } else if (!KAuthorized::authorize(QStringLiteral("plasma-desktop/scripting_console"))) {
sendErrorReply(QDBusError::Failed, QStringLiteral("Administrative policies prevent script execution")); sendErrorReply(QDBusError::Failed, QStringLiteral("Administrative policies prevent script execution"));
return; return QString();
} }
} }
WorkspaceScripting::ScriptEngine scriptEngine(this); 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; 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; qDebug() << msg;
bufferStream << msg;
}); });
scriptEngine.evaluateScript(script); scriptEngine.evaluateScript(script);
if (!scriptEngine.errorString().isEmpty() && calledFromDBus()) {
bufferStream.flush();
if (calledFromDBus() && !scriptEngine.errorString().isEmpty()) {
sendErrorReply(QDBusError::Failed, scriptEngine.errorString()); sendErrorReply(QDBusError::Failed, scriptEngine.errorString());
return QString();
} }
}
void ShellCorona::interactiveConsoleVisibilityChanged(bool visible) return buffer;
{
if (!visible) {
m_interactiveConsole->deleteLater();
m_interactiveConsole = nullptr;
}
} }
void ShellCorona::checkActivities() void ShellCorona::checkActivities()

@ -128,14 +128,9 @@ public Q_SLOTS:
/// DBUS methods /// DBUS methods
void toggleDashboard(); void toggleDashboard();
void setDashboardShown(bool show); void setDashboardShown(bool show);
void loadInteractiveConsole();
void showInteractiveConsole();
void loadScriptInInteractiveConsole(const QString &script);
void showInteractiveKWinConsole();
void loadKWinScriptInInteractiveConsole(const QString &script);
void toggleActivityManager(); void toggleActivityManager();
void toggleWidgetExplorer(); void toggleWidgetExplorer();
void evaluateScript(const QString &string); QString evaluateScript(const QString &string);
void activateLauncherMenu(); void activateLauncherMenu();
QByteArray dumpCurrentLayoutJS() const; QByteArray dumpCurrentLayoutJS() const;
@ -205,7 +200,6 @@ private Q_SLOTS:
void primaryOutputChanged(); void primaryOutputChanged();
void panelContainmentDestroyed(QObject *cont); void panelContainmentDestroyed(QObject *cont);
void interactiveConsoleVisibilityChanged(bool visible);
void handleScreenRemoved(QScreen *screen); void handleScreenRemoved(QScreen *screen);
void activateTaskManagerEntry(int index); void activateTaskManagerEntry(int index);
@ -244,7 +238,6 @@ private:
QScopedPointer<QMenu> m_addPanelsMenu; QScopedPointer<QMenu> m_addPanelsMenu;
KPackage::Package m_lookAndFeelPackage; KPackage::Package m_lookAndFeelPackage;
QSet<QScreen *> m_redundantOutputs; QSet<QScreen *> m_redundantOutputs;
KDeclarative::QmlObjectSharedEngine *m_interactiveConsole;
QTimer m_waitingPanelsTimer; QTimer m_waitingPanelsTimer;
QTimer m_appConfigSyncTimer; QTimer m_appConfigSyncTimer;

Loading…
Cancel
Save