Development/Tutorials/KWin/Scripting: Difference between revisions

From KDE TechBase
m (Moved)
Tag: Replaced
 
(14 intermediate revisions by 9 users not shown)
Line 1: Line 1:
= KWin Scripting Tutorial =
This page was moved to https://develop.kde.org/docs/plasma/kwin/
 
{{warning|KWin's Scripting functionality has been reworked for KDE Plasma Workspaces 4.9. This tutorial does not cover the previous API. In case you had an existing script, please consult [[Projects/KWin/Scripting_Update_Notes|Scripting Update Notes]] for migration note.}}
 
== Quick Start: Desktop Console ==
The easiest way to test KWin scripts is to use the Plasma Desktop Scripting Console which can be opened via the KRunner window (Alt+F2, by default, or via the "Run Command" entry in various desktop menus) by entering "wm console" as the search term It can be triggered directly via dbus with <syntaxhighlight lang="bash">qdbus org.kde.plasma-desktop /MainApplication showInteractiveKWinConsole</syntaxhighlight>
 
{{note|This method is not available for plasma-netbook.}}
 
The interactive console allows to send a script to the window manager which is directly loaded and executed. All debug output is displayed in the scripting console. This provides a very easy way to develop and test the script to be written. It is important to know that scripts executed from the scripting console are only used by the window manager as long as the window manager is running. In a new session the script has to be sent to the window manager again.
 
== Packaging KWin Scripts ==
In order to have KWin load a script on each session start the script has to be packaged. KWin Scripts use the [[Development/Tutorials/Plasma/PackageOverview|Plasma Package]] format. The recommended file ending for KWin Script packages is '''.kwinscript'''. In the metadata.desktop file of the package the value for "X-KDE-ServiceTypes" has to be '''KWin/Script''', as "X-Plasma-API" only '''javascript''' and '''declarativescript''' are supported.
 
A packaged KWin Script can either be installed via the KWin Script KCM (note: the list does not yet reload after installing a script) or with the plasmapkg tool: <syntaxhighlight lang="bash">plasmapkg --type kwinscript -i /path/to/myscript.kwinscript</syntaxhighlight>
 
After installation the script has to be enabled in the KWin Script KCM. The window manager will load the newly installed script directly at runtime as well as in new sessions.
 
== KWin scripting basics ==
 
To follow this tutorial, you must have some idea about [http://en.wikipedia.org/wiki/ECMAScript ECMAScript] (or [http://en.wikipedia.org/wiki/JavaScript JavaScript]. A quick introduction can be found in the [http://techbase.kde.org/Development/Tutorials/Plasma#JavaScript Plasma scripting tutorial].
 
KWin Scripts can either be written in [https://qt-project.org/doc/qt-4.8/scripting.html QtScript] (service type "javascript") or [https://qt-project.org/doc/qt-4.8/qdeclarativeintroduction.html QML] (service type "declarativescript"). In order to develop KWin Scripts you should know the basic concepts of signals and properties.
 
=== Global Objects and Functions ===
 
KWin Scripts can access two global properties '''workspace''' and '''options'''. The workspace object provides the interface to the core of the window manager, the options object provides read access to the current configuration options set on the window manager. To get an overview of what is available, please refer to the [[Development/Tutorials/KWin/Scripting/API_4.9|API documentation]].
 
The following global functions are available to both QML and QtScript:
* '''print(QVariant...)''': prints the provided arguments to stdout. Takes an arbitrary number of arguments. Comparable to console.log() which should be preferred in QML scripts.
* '''readConfig(QString key, QVariant defaultValue=QVariant())''': reads a config option of the KWin Script. First argument is the config key, second argument is an optional default value in case the config key does not exist in the config file.
 
=== Clients ===
The window manager calls a window it manages a "Client". Most methods of workspace operating on windows either return a Client or require a Client. Internally the window manager supports more types of windows, which are not clients. Those windows are not available for KWin Scripts, but for KWin Effects. To have a common set of properties some properties and signals are defined on the parent class of Client called '''Toplevel'''. Be sure to check the documentation of that class, too when looking for properties. Be aware that some properties are defined as read-only on Toplevel, but as read-write on Client.
 
The following examples illustrates how to get hold of all clients managed by the window manager and prints the clients' caption:
<syntaxhighlight lang="javascript">
var clients = workspace.clientList();
for (var i=0; i<clients.length; i++) {
  print(clients[i].caption);
}
</syntaxhighlight>
 
The following example illustrates how to get informed about newly managed clients and prints out the window id of the new client:
<syntaxhighlight lang="javascript">
workspace.clientAdded.connect(function(client) {
  print(client.windowId);
});
</syntaxhighlight>
To understand which parameters are passed to the event handlers (i.e. the functions we connect to), one can always refer the [[Development/Tutorials/KWin/Scripting/API]].
 
== Your first (useful) script ==
 
In this tutorial we will be creating a script based on a suggestion by Eike Hein. In Eike’s words: ''“A quick use case question: For many years I’ve desired the behavior of disabling keep-above on a window with keep-above enabled when it is maximized, and re-enabling keep-above when it is restored. Is that be possible with kwin scripting? It’ll need the ability to trigger methods on state changes and store information above a specific window across those state changes, I guess.”''
 
Other than the really function and useful script idea, what is really great about this is that it makes for a perfect tutorial example. I get to cover most of the important aspects of KWin scripting while at the same time creating something useful.
 
So let’s get on with it…
 
=== The basic outline ===
'''Design statement''': For every window that is set to '''‘Keep Above’''' others, the window should not be above all windows when it is maximized.
 
To do so, this is how we’ll proceed:
#Create an array of clients whose '''‘Keep Above’''' property has been removed for maximized windows
#Whenever a client is maximized, if it’s ''''‘Keep Above’''' property is set, remove the '''‘Keep Above’''' property.
#Whenever a client is restored, if it is in the ‘array’, set it’s '''‘Keep Above’''' property.
 
==== The basic framework ====
So, for first steps, let us just create an array:
<syntaxhighlight lang=javascript>
var keepAboveMaximized = new Array();
</syntaxhighlight>
 
Now we need to know whenever a window got maximized. There are two approaches to achieve that: either connect to a signal emitted on the workspace object or to a signal of the client. As we need to track all Clients it is easier to just use the signal '''clientMaximizeSet'' on the workspace. This signal is emitted whenever the maximization state of a Client changes and passes the client and two boolean flags to the callback. The flags indicate whether the Client is maximized horizontally and/or vertically. If a client is maximized both horizontally and vertically it is considered as fully maximized. Let's try it:
 
<syntaxhighlight lang=javascript>
workspace.clientMaximizeSet.connect(function(client, h, v) {
  if (h && v) {
    print(client.caption + " is fully maximized");
  } else {
    print(client.caption + " is not maximized");
  }
});
</syntaxhighlight>
 
Best give the script a try in the desktop scripting console and play with your windows. Remember right and middle clicking the maximize button changes the horizontal/vertical state of the window
 
==== Restoring it all ====
 
Now the last and most important part of it all. Whenver the client is restored, we must set it’s '''‘Keep Above’''' property if it was set earlier. To do this, we must simply extend our '''manageKeepAbove''' code to handle this scenario. In case the client is not maximized both vertically and horizontally, we check if the client is in our '''ka_clients''' arrray and if it is, we set its '''‘Keep Above’''' property, otherwise we don’t bother:
 
 
<syntaxhighlight lang=javascript>
function manageKeepAbove(client, h, v) {
  if (h && v) {
    // maximized
    if (client.keepAbove) {
      keepAboveMaximized[keepAboveMaximized.length] = client;
      client.keepAbove = false;
    }
  } else {
    // no longer maximized
    var found = keepAboveMaximized.indexOf(client);
    if (found != -1) {
      client.keepAbove = true;
      keepAboveMaximized.splice(found, 1);
    }
  }
}
</syntaxhighlight>
 
In the end, our entire script looks like:
 
<syntaxhighlight lang=javascript>
var keepAboveMaximized = new Array();
 
function manageKeepAbove(client, h, v) {
  if (h && v) {
    // maximized
    if (client.keepAbove) {
      keepAboveMaximized[keepAboveMaximized.length] = client;
      client.keepAbove = false;
    }
  } else {
    // no longer maximized
    var found = keepAboveMaximized.indexOf(client);
    if (found != -1) {
      client.keepAbove = true;
      keepAboveMaximized.splice(found, 1);
    }
  }
}
 
workspace.clientMaximizeSet.connect(manageKeepAbove);
</syntaxhighlight>
 
The script in Plasma Package structure can be found in the [https://projects.kde.org/projects/kde/kdeexamples/repository/revisions/master/show/kwin/scripts/keepaboverestored kde-examples repository].

Latest revision as of 20:31, 12 March 2021