File indexing completed on 2025-03-16 11:21:24
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2010 Rohan Prabhu <rohan@rohanprabhu.com> 0006 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org> 0007 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #pragma once 0013 0014 #include <kwinglobals.h> 0015 0016 #include <QHash> 0017 #include <QJSEngine> 0018 #include <QJSValue> 0019 #include <QStringList> 0020 #include <QTimer> 0021 0022 #include <QDBusContext> 0023 #include <QDBusMessage> 0024 0025 class QQmlComponent; 0026 class QQmlContext; 0027 class QQmlEngine; 0028 class QAction; 0029 class QMenu; 0030 class QRecursiveMutex; 0031 class QQuickWindow; 0032 class KConfigGroup; 0033 0034 /// @c true == javascript, @c false == qml 0035 typedef QList<QPair<bool, QPair<QString, QString>>> LoadScriptList; 0036 0037 namespace KWin 0038 { 0039 class Window; 0040 class QtScriptWorkspaceWrapper; 0041 0042 class KWIN_EXPORT AbstractScript : public QObject 0043 { 0044 Q_OBJECT 0045 public: 0046 AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent = nullptr); 0047 ~AbstractScript() override; 0048 int scriptId() const 0049 { 0050 return m_scriptId; 0051 } 0052 QString fileName() const 0053 { 0054 return m_fileName; 0055 } 0056 const QString &pluginName() 0057 { 0058 return m_pluginName; 0059 } 0060 bool running() const 0061 { 0062 return m_running; 0063 } 0064 0065 KConfigGroup config() const; 0066 0067 public Q_SLOTS: 0068 void stop(); 0069 virtual void run() = 0; 0070 0071 Q_SIGNALS: 0072 void runningChanged(bool); 0073 0074 protected: 0075 void setRunning(bool running) 0076 { 0077 if (m_running == running) { 0078 return; 0079 } 0080 m_running = running; 0081 Q_EMIT runningChanged(m_running); 0082 } 0083 0084 private: 0085 int m_scriptId; 0086 QString m_fileName; 0087 QString m_pluginName; 0088 bool m_running; 0089 }; 0090 0091 /** 0092 * In order to be able to construct QTimer objects in javascript, the constructor 0093 * must be declared with Q_INVOKABLE. 0094 */ 0095 class ScriptTimer : public QTimer 0096 { 0097 Q_OBJECT 0098 0099 public: 0100 Q_INVOKABLE ScriptTimer(QObject *parent = nullptr); 0101 }; 0102 0103 class Script : public AbstractScript, QDBusContext 0104 { 0105 Q_OBJECT 0106 public: 0107 Script(int id, QString scriptName, QString pluginName, QObject *parent = nullptr); 0108 virtual ~Script(); 0109 0110 Q_INVOKABLE QVariant readConfig(const QString &key, const QVariant &defaultValue = QVariant()); 0111 0112 Q_INVOKABLE void callDBus(const QString &service, const QString &path, 0113 const QString &interface, const QString &method, 0114 const QJSValue &arg1 = QJSValue(), 0115 const QJSValue &arg2 = QJSValue(), 0116 const QJSValue &arg3 = QJSValue(), 0117 const QJSValue &arg4 = QJSValue(), 0118 const QJSValue &arg5 = QJSValue(), 0119 const QJSValue &arg6 = QJSValue(), 0120 const QJSValue &arg7 = QJSValue(), 0121 const QJSValue &arg8 = QJSValue(), 0122 const QJSValue &arg9 = QJSValue()); 0123 0124 Q_INVOKABLE bool registerShortcut(const QString &objectName, const QString &text, 0125 const QString &keySequence, const QJSValue &callback); 0126 0127 Q_INVOKABLE bool registerScreenEdge(int edge, const QJSValue &callback); 0128 Q_INVOKABLE bool unregisterScreenEdge(int edge); 0129 0130 Q_INVOKABLE bool registerTouchScreenEdge(int edge, const QJSValue &callback); 0131 Q_INVOKABLE bool unregisterTouchScreenEdge(int edge); 0132 0133 /** 0134 * @brief Registers the given @p callback to be invoked whenever the UserActionsMenu is about 0135 * to be showed. In the callback the script can create a further sub menu or menu entry to be 0136 * added to the UserActionsMenu. 0137 * 0138 * @param callback Script method to execute when the UserActionsMenu is about to be shown. 0139 * @return void 0140 * @see actionsForUserActionMenu 0141 */ 0142 Q_INVOKABLE void registerUserActionsMenu(const QJSValue &callback); 0143 0144 /** 0145 * @brief Creates actions for the UserActionsMenu by invoking the registered callbacks. 0146 * 0147 * This method invokes all the callbacks previously registered with registerUseractionsMenuCallback. 0148 * The Client @p c is passed in as an argument to the invoked method. 0149 * 0150 * The invoked method is supposed to return a JavaScript object containing either the menu or 0151 * menu entry to be added. In case the callback returns a null or undefined or any other invalid 0152 * value, it is not considered for adding to the menu. 0153 * 0154 * The JavaScript object structure for a menu entry looks like the following: 0155 * @code 0156 * { 0157 * title: "My Menu Entry", 0158 * checkable: true, 0159 * checked: false, 0160 * triggered: function (action) { 0161 * // callback when the menu entry is triggered with the QAction as argument 0162 * } 0163 * } 0164 * @endcode 0165 * 0166 * To construct a complete Menu the JavaScript object looks like the following: 0167 * @code 0168 * { 0169 * title: "My Menu Title", 0170 * items: [{...}, {...}, ...] // list of menu entries as described above 0171 * } 0172 * @endcode 0173 * 0174 * The returned JavaScript object is introspected and for a menu entry a QAction is created, 0175 * while for a menu a QMenu is created and QActions for the individual entries. Of course it 0176 * is allowed to have nested structures. 0177 * 0178 * All created objects are (grand) children to the passed in @p parent menu, so that they get 0179 * deleted whenever the menu is destroyed. 0180 * 0181 * @param c The Client for which the menu is invoked, passed to the callback 0182 * @param parent The Parent for the created Menus or Actions 0183 * @return QList< QAction* > List of QActions obtained from asking the registered callbacks 0184 * @see registerUseractionsMenuCallback 0185 */ 0186 QList<QAction *> actionsForUserActionMenu(Window *client, QMenu *parent); 0187 0188 public Q_SLOTS: 0189 void run() override; 0190 0191 private Q_SLOTS: 0192 /** 0193 * Callback for when loadScriptFromFile has finished. 0194 */ 0195 void slotScriptLoadedFromFile(); 0196 0197 /** 0198 * Called when any reserve screen edge is triggered. 0199 */ 0200 bool slotBorderActivated(ElectricBorder border); 0201 0202 private: 0203 /** 0204 * Read the script from file into a byte array. 0205 * If file cannot be read an empty byte array is returned. 0206 */ 0207 QByteArray loadScriptFromFile(const QString &fileName); 0208 0209 /** 0210 * @brief Parses the @p value to either a QMenu or QAction. 0211 * 0212 * @param value The ScriptValue describing either a menu or action 0213 * @param parent The parent to use for the created menu or action 0214 * @return QAction* The parsed action or menu action, if parsing fails returns @c null. 0215 */ 0216 QAction *scriptValueToAction(const QJSValue &value, QMenu *parent); 0217 0218 /** 0219 * @brief Creates a new QAction from the provided data and registers it for invoking the 0220 * @p callback when the action is triggered. 0221 * 0222 * The created action is added to the map of actions and callbacks shared with the global 0223 * shortcuts. 0224 * 0225 * @param title The title of the action 0226 * @param checkable Whether the action is checkable 0227 * @param checked Whether the checkable action is checked 0228 * @param callback The callback to invoke when the action is triggered 0229 * @param parent The parent to be used for the new created action 0230 * @return QAction* The created action 0231 */ 0232 QAction *createAction(const QString &title, const QJSValue &item, QMenu *parent); 0233 0234 /** 0235 * @brief Parses the @p items and creates a QMenu from it. 0236 * 0237 * @param title The title of the Menu. 0238 * @param items JavaScript Array containing Menu items. 0239 * @param parent The parent to use for the new created menu 0240 * @return QAction* The menu action for the new Menu 0241 */ 0242 QAction *createMenu(const QString &title, const QJSValue &items, QMenu *parent); 0243 0244 QJSEngine *m_engine; 0245 QDBusMessage m_invocationContext; 0246 bool m_starting; 0247 QHash<int, QJSValueList> m_screenEdgeCallbacks; 0248 QHash<int, QAction *> m_touchScreenEdgeCallbacks; 0249 QJSValueList m_userActionsMenuCallbacks; 0250 }; 0251 0252 class DeclarativeScript : public AbstractScript 0253 { 0254 Q_OBJECT 0255 public: 0256 explicit DeclarativeScript(int id, QString scriptName, QString pluginName, QObject *parent = nullptr); 0257 ~DeclarativeScript() override; 0258 0259 public Q_SLOTS: 0260 Q_SCRIPTABLE void run() override; 0261 0262 private Q_SLOTS: 0263 void createComponent(); 0264 0265 private: 0266 QQmlContext *m_context; 0267 QQmlComponent *m_component; 0268 }; 0269 0270 class JSEngineGlobalMethodsWrapper : public QObject 0271 { 0272 Q_OBJECT 0273 public: 0274 //------------------------------------------------------------------ 0275 // enums copy&pasted from kwinglobals.h for exporting 0276 0277 enum ClientAreaOption { 0278 ///< geometry where a window will be initially placed after being mapped 0279 PlacementArea, 0280 ///< window movement snapping area? ignore struts 0281 MovementArea, 0282 ///< geometry to which a window will be maximized 0283 MaximizeArea, 0284 ///< like MaximizeArea, but ignore struts - used e.g. for topmenu 0285 MaximizeFullArea, 0286 ///< area for fullscreen windows 0287 FullScreenArea, 0288 ///< whole workarea (all screens together) 0289 WorkArea, 0290 ///< whole area (all screens together), ignore struts 0291 FullArea, 0292 ///< one whole screen, ignore struts 0293 ScreenArea 0294 }; 0295 Q_ENUM(ClientAreaOption) 0296 explicit JSEngineGlobalMethodsWrapper(DeclarativeScript *parent); 0297 ~JSEngineGlobalMethodsWrapper() override; 0298 0299 public Q_SLOTS: 0300 QVariant readConfig(const QString &key, QVariant defaultValue = QVariant()); 0301 void registerWindow(QQuickWindow *window); 0302 bool registerShortcut(const QString &name, const QString &text, const QKeySequence &keys, QJSValue function); 0303 0304 private: 0305 DeclarativeScript *m_script; 0306 }; 0307 0308 /** 0309 * The heart of KWin::Scripting. Infinite power lies beyond 0310 */ 0311 class KWIN_EXPORT Scripting : public QObject 0312 { 0313 Q_OBJECT 0314 Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") 0315 private: 0316 explicit Scripting(QObject *parent); 0317 QStringList scriptList; 0318 QList<KWin::AbstractScript *> scripts; 0319 /** 0320 * Lock to protect the scripts member variable. 0321 */ 0322 std::unique_ptr<QRecursiveMutex> m_scriptsLock; 0323 0324 // Preferably call ONLY at load time 0325 void runScripts(); 0326 0327 public: 0328 ~Scripting() override; 0329 Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath, const QString &pluginName = QString()); 0330 Q_SCRIPTABLE Q_INVOKABLE int loadDeclarativeScript(const QString &filePath, const QString &pluginName = QString()); 0331 Q_SCRIPTABLE Q_INVOKABLE bool isScriptLoaded(const QString &pluginName) const; 0332 Q_SCRIPTABLE Q_INVOKABLE bool unloadScript(const QString &pluginName); 0333 0334 /** 0335 * @brief Invokes all registered callbacks to add actions to the UserActionsMenu. 0336 * 0337 * @param c The Client for which the UserActionsMenu is about to be shown 0338 * @param parent The parent menu to which to add created child menus and items 0339 * @return QList< QAction* > List of all actions aggregated from all scripts. 0340 */ 0341 QList<QAction *> actionsForUserActionMenu(Window *c, QMenu *parent); 0342 0343 QQmlEngine *qmlEngine() const; 0344 QQmlEngine *qmlEngine(); 0345 QQmlContext *declarativeScriptSharedContext() const; 0346 QQmlContext *declarativeScriptSharedContext(); 0347 QtScriptWorkspaceWrapper *workspaceWrapper() const; 0348 0349 AbstractScript *findScript(const QString &pluginName) const; 0350 0351 static Scripting *self(); 0352 static Scripting *create(QObject *parent); 0353 0354 public Q_SLOTS: 0355 void scriptDestroyed(QObject *object); 0356 Q_SCRIPTABLE void start(); 0357 0358 private Q_SLOTS: 0359 void slotScriptsQueried(); 0360 0361 private: 0362 void init(); 0363 LoadScriptList queryScriptsToLoad(); 0364 static Scripting *s_self; 0365 QQmlEngine *m_qmlEngine; 0366 QQmlContext *m_declarativeScriptSharedContext; 0367 QtScriptWorkspaceWrapper *m_workspaceWrapper; 0368 }; 0369 0370 inline QQmlEngine *Scripting::qmlEngine() const 0371 { 0372 return m_qmlEngine; 0373 } 0374 0375 inline QQmlEngine *Scripting::qmlEngine() 0376 { 0377 return m_qmlEngine; 0378 } 0379 0380 inline QQmlContext *Scripting::declarativeScriptSharedContext() const 0381 { 0382 return m_declarativeScriptSharedContext; 0383 } 0384 0385 inline QQmlContext *Scripting::declarativeScriptSharedContext() 0386 { 0387 return m_declarativeScriptSharedContext; 0388 } 0389 0390 inline QtScriptWorkspaceWrapper *Scripting::workspaceWrapper() const 0391 { 0392 return m_workspaceWrapper; 0393 } 0394 0395 inline Scripting *Scripting::self() 0396 { 0397 return s_self; 0398 } 0399 0400 }