1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2010 Rohan Prabhu <rohan@rohanprabhu.com> |
6 | Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org> |
7 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | *********************************************************************/ |
21 | |
22 | #ifndef KWIN_SCRIPTING_H |
23 | #define KWIN_SCRIPTING_H |
24 | |
25 | #include <kwinglobals.h> |
26 | #include <kservice.h> |
27 | |
28 | #include <QFile> |
29 | #include <QHash> |
30 | #include <QStringList> |
31 | #include <QtScript/QScriptEngineAgent> |
32 | |
33 | class QDeclarativeComponent; |
34 | class QDeclarativeEngine; |
35 | class QAction; |
36 | class QDBusPendingCallWatcher; |
37 | class QGraphicsScene; |
38 | class ; |
39 | class QMutex; |
40 | class QScriptEngine; |
41 | class QScriptValue; |
42 | class KConfigGroup; |
43 | |
44 | /// @c true == javascript, @c false == qml |
45 | typedef QList< QPair<bool, QPair<QString, QString > > > LoadScriptList; |
46 | |
47 | namespace KWin |
48 | { |
49 | class Client; |
50 | class ScriptUnloaderAgent; |
51 | class WorkspaceWrapper; |
52 | |
53 | class AbstractScript : public QObject |
54 | { |
55 | Q_OBJECT |
56 | public: |
57 | AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent = NULL); |
58 | ~AbstractScript(); |
59 | QString fileName() const { |
60 | return m_scriptFile.fileName(); |
61 | } |
62 | const QString &pluginName() { |
63 | return m_pluginName; |
64 | } |
65 | |
66 | void printMessage(const QString &message); |
67 | void registerShortcut(QAction *a, QScriptValue callback); |
68 | /** |
69 | * @brief Registers the given @p callback to be invoked whenever the UserActionsMenu is about |
70 | * to be showed. In the callback the script can create a further sub menu or menu entry to be |
71 | * added to the UserActionsMenu. |
72 | * |
73 | * @param callback Script method to execute when the UserActionsMenu is about to be shown. |
74 | * @return void |
75 | * @see actionsForUserActionMenu |
76 | **/ |
77 | void (QScriptValue callback); |
78 | /** |
79 | * @brief Creates actions for the UserActionsMenu by invoking the registered callbacks. |
80 | * |
81 | * This method invokes all the callbacks previously registered with registerUseractionsMenuCallback. |
82 | * The Client @p c is passed in as an argument to the invoked method. |
83 | * |
84 | * The invoked method is supposed to return a JavaScript object containing either the menu or |
85 | * menu entry to be added. In case the callback returns a null or undefined or any other invalid |
86 | * value, it is not considered for adding to the menu. |
87 | * |
88 | * The JavaScript object structure for a menu entry looks like the following: |
89 | * @code |
90 | * { |
91 | * title: "My Menu Entry", |
92 | * checkable: true, |
93 | * checked: false, |
94 | * triggered: function (action) { |
95 | * // callback when the menu entry is triggered with the QAction as argument |
96 | * } |
97 | * } |
98 | * @endcode |
99 | * |
100 | * To construct a complete Menu the JavaScript object looks like the following: |
101 | * @code |
102 | * { |
103 | * title: "My Menu Title", |
104 | * items: [{...}, {...}, ...] // list of menu entries as described above |
105 | * } |
106 | * @endcode |
107 | * |
108 | * The returned JavaScript object is introspected and for a menu entry a QAction is created, |
109 | * while for a menu a QMenu is created and QActions for the individual entries. Of course it |
110 | * is allowed to have nested structures. |
111 | * |
112 | * All created objects are (grand) children to the passed in @p parent menu, so that they get |
113 | * deleted whenever the menu is destroyed. |
114 | * |
115 | * @param c The Client for which the menu is invoked, passed to the callback |
116 | * @param parent The Parent for the created Menus or Actions |
117 | * @return QList< QAction* > List of QActions obtained from asking the registered callbacks |
118 | * @see registerUseractionsMenuCallback |
119 | **/ |
120 | QList<QAction*> (Client *c, QMenu *parent); |
121 | |
122 | KConfigGroup config() const; |
123 | const QHash<QAction*, QScriptValue> &shortcutCallbacks() const { |
124 | return m_shortcutCallbacks; |
125 | } |
126 | QHash<int, QList<QScriptValue > > &screenEdgeCallbacks() { |
127 | return m_screenEdgeCallbacks; |
128 | } |
129 | |
130 | int registerCallback(QScriptValue value); |
131 | |
132 | public Q_SLOTS: |
133 | Q_SCRIPTABLE void stop(); |
134 | Q_SCRIPTABLE virtual void run() = 0; |
135 | void slotPendingDBusCall(QDBusPendingCallWatcher *watcher); |
136 | |
137 | private Q_SLOTS: |
138 | void globalShortcutTriggered(); |
139 | bool borderActivated(ElectricBorder edge); |
140 | /** |
141 | * @brief Slot invoked when a menu action is destroyed. Used to remove the action and callback |
142 | * from the map of actions. |
143 | * |
144 | * @param object The destroyed action |
145 | **/ |
146 | void actionDestroyed(QObject *object); |
147 | |
148 | Q_SIGNALS: |
149 | Q_SCRIPTABLE void print(const QString &text); |
150 | |
151 | protected: |
152 | QFile &scriptFile() { |
153 | return m_scriptFile; |
154 | } |
155 | bool running() const { |
156 | return m_running; |
157 | } |
158 | void setRunning(bool running) { |
159 | m_running = running; |
160 | } |
161 | int scriptId() const { |
162 | return m_scriptId; |
163 | } |
164 | |
165 | WorkspaceWrapper *workspace() { |
166 | return m_workspace; |
167 | } |
168 | |
169 | void installScriptFunctions(QScriptEngine *engine); |
170 | |
171 | private: |
172 | /** |
173 | * @brief Parses the @p value to either a QMenu or QAction. |
174 | * |
175 | * @param value The ScriptValue describing either a menu or action |
176 | * @param parent The parent to use for the created menu or action |
177 | * @return QAction* The parsed action or menu action, if parsing fails returns @c null. |
178 | **/ |
179 | QAction *(QScriptValue &value, QMenu *parent); |
180 | /** |
181 | * @brief Creates a new QAction from the provided data and registers it for invoking the |
182 | * @p callback when the action is triggered. |
183 | * |
184 | * The created action is added to the map of actions and callbacks shared with the global |
185 | * shortcuts. |
186 | * |
187 | * @param title The title of the action |
188 | * @param checkable Whether the action is checkable |
189 | * @param checked Whether the checkable action is checked |
190 | * @param callback The callback to invoke when the action is triggered |
191 | * @param parent The parent to be used for the new created action |
192 | * @return QAction* The created action |
193 | **/ |
194 | QAction *(const QString &title, bool checkable, bool checked, QScriptValue &callback, QMenu *parent); |
195 | /** |
196 | * @brief Parses the @p items and creates a QMenu from it. |
197 | * |
198 | * @param title The title of the Menu. |
199 | * @param items JavaScript Array containing Menu items. |
200 | * @param parent The parent to use for the new created menu |
201 | * @return QAction* The menu action for the new Menu |
202 | **/ |
203 | QAction *(const QString &title, QScriptValue &items, QMenu *parent); |
204 | int m_scriptId; |
205 | QFile m_scriptFile; |
206 | QString m_pluginName; |
207 | bool m_running; |
208 | WorkspaceWrapper *m_workspace; |
209 | QHash<QAction*, QScriptValue> m_shortcutCallbacks; |
210 | QHash<int, QList<QScriptValue> > m_screenEdgeCallbacks; |
211 | QHash<int, QScriptValue> m_callbacks; |
212 | /** |
213 | * @brief List of registered functions to call when the UserActionsMenu is about to show |
214 | * to add further entries. |
215 | **/ |
216 | QList<QScriptValue> ; |
217 | }; |
218 | |
219 | class Script : public AbstractScript |
220 | { |
221 | Q_OBJECT |
222 | Q_CLASSINFO("D-Bus Interface" , "org.kde.kwin.Scripting" ) |
223 | public: |
224 | |
225 | Script(int id, QString scriptName, QString pluginName, QObject *parent = NULL); |
226 | virtual ~Script(); |
227 | QScriptEngine *engine() { |
228 | return m_engine; |
229 | } |
230 | |
231 | public Q_SLOTS: |
232 | Q_SCRIPTABLE void run(); |
233 | |
234 | Q_SIGNALS: |
235 | Q_SCRIPTABLE void printError(const QString &text); |
236 | |
237 | private slots: |
238 | /** |
239 | * A nice clean way to handle exceptions in scripting. |
240 | * TODO: Log to file, show from notifier.. |
241 | */ |
242 | void sigException(const QScriptValue &exception); |
243 | /** |
244 | * Callback for when loadScriptFromFile has finished. |
245 | **/ |
246 | void slotScriptLoadedFromFile(); |
247 | |
248 | private: |
249 | /** |
250 | * Read the script from file into a byte array. |
251 | * If file cannot be read an empty byte array is returned. |
252 | **/ |
253 | QByteArray loadScriptFromFile(); |
254 | QScriptEngine *m_engine; |
255 | bool m_starting; |
256 | QScopedPointer<ScriptUnloaderAgent> m_agent; |
257 | }; |
258 | |
259 | class ScriptUnloaderAgent : public QScriptEngineAgent |
260 | { |
261 | public: |
262 | explicit ScriptUnloaderAgent(Script *script); |
263 | virtual void scriptUnload(qint64 id); |
264 | |
265 | private: |
266 | Script *m_script; |
267 | }; |
268 | |
269 | class DeclarativeScript : public AbstractScript |
270 | { |
271 | Q_OBJECT |
272 | Q_CLASSINFO("D-Bus Interface" , "org.kde.kwin.Scripting" ) |
273 | public: |
274 | explicit DeclarativeScript(int id, QString scriptName, QString pluginName, QObject *parent = 0); |
275 | virtual ~DeclarativeScript(); |
276 | |
277 | public Q_SLOTS: |
278 | Q_SCRIPTABLE void run(); |
279 | |
280 | private Q_SLOTS: |
281 | void createComponent(); |
282 | |
283 | private: |
284 | QDeclarativeEngine *m_engine; |
285 | QDeclarativeComponent *m_component; |
286 | QGraphicsScene *m_scene; |
287 | }; |
288 | |
289 | /** |
290 | * The heart of KWin::Scripting. Infinite power lies beyond |
291 | */ |
292 | class Scripting : public QObject |
293 | { |
294 | Q_OBJECT |
295 | Q_CLASSINFO("D-Bus Interface" , "org.kde.kwin.Scripting" ) |
296 | private: |
297 | explicit Scripting(QObject *parent); |
298 | QStringList scriptList; |
299 | QList<KWin::AbstractScript*> scripts; |
300 | /** |
301 | * Lock to protect the scripts member variable. |
302 | **/ |
303 | QScopedPointer<QMutex> m_scriptsLock; |
304 | |
305 | // Preferably call ONLY at load time |
306 | void runScripts(); |
307 | |
308 | public: |
309 | ~Scripting(); |
310 | Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath, const QString &pluginName = QString()); |
311 | Q_SCRIPTABLE Q_INVOKABLE int loadDeclarativeScript(const QString &filePath, const QString &pluginName = QString()); |
312 | Q_SCRIPTABLE Q_INVOKABLE bool isScriptLoaded(const QString &pluginName) const; |
313 | Q_SCRIPTABLE Q_INVOKABLE bool unloadScript(const QString &pluginName); |
314 | |
315 | /** |
316 | * @brief Invokes all registered callbacks to add actions to the UserActionsMenu. |
317 | * |
318 | * @param c The Client for which the UserActionsMenu is about to be shown |
319 | * @param parent The parent menu to which to add created child menus and items |
320 | * @return QList< QAction* > List of all actions aggregated from all scripts. |
321 | **/ |
322 | QList<QAction*> (Client *c, QMenu *parent); |
323 | |
324 | static Scripting *self(); |
325 | static Scripting *create(QObject *parent); |
326 | |
327 | public Q_SLOTS: |
328 | void scriptDestroyed(QObject *object); |
329 | Q_SCRIPTABLE void start(); |
330 | |
331 | private Q_SLOTS: |
332 | void slotScriptsQueried(); |
333 | |
334 | private: |
335 | LoadScriptList queryScriptsToLoad(); |
336 | static Scripting *s_self; |
337 | }; |
338 | |
339 | inline |
340 | Scripting *Scripting::self() |
341 | { |
342 | return s_self; |
343 | } |
344 | |
345 | } |
346 | #endif |
347 | |