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

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

@ -12,17 +12,10 @@
#include "widgetexplorer/widgetexplorer.h"
#include <Plasma/Containment>
#if KF5TextEditor_FOUND
#include "interactiveconsole/interactiveconsole.h"
#endif
void PlasmaShellPrivatePlugin::registerTypes(const char *uri)
{
Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.shell"));
qmlRegisterType<Plasma::Containment>();
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?
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<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) {
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<QVariant> arguments;
arguments << QVariant(path);

@ -106,67 +106,3 @@ private:
ConsoleMode m_mode;
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">
<arg name="show" type="b" direction="in"/>
</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">
<arg name="script" type="s" direction="in"/>
<arg name="output" type="s" direction="out"/>
</method>
<method name="dumpCurrentLayoutJS">
<arg name="script" type="ay" direction="out"/>

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

@ -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<QMenu> m_addPanelsMenu;
KPackage::Package m_lookAndFeelPackage;
QSet<QScreen *> m_redundantOutputs;
KDeclarative::QmlObjectSharedEngine *m_interactiveConsole;
QTimer m_waitingPanelsTimer;
QTimer m_appConfigSyncTimer;

Loading…
Cancel
Save