Nova Flow OS
KDE Developer Platform
KDE Developer Platform
  • KDE Developer Platform
    • Getting started
      • Building KDE software
        • KDE software
        • Where to find the development team
        • Learning more
        • Choose what to work on
        • Source code cross-referencing
        • Installing build dependencies
        • Set up a development environment
        • Building KDE software with kdesrc-build
        • Basic troubleshooting
        • Tips and tricks
        • IDE Configuration
          • Setting up an IDE for KDE development
          • Visual Studio Code
          • Qt Creator
          • Kate
          • KDevelop
          • CLion
          • Sublime Text
        • Building KDE software manually
        • Building KDE software with distrobox and podman
      • Kirigami
        • KDE is ours
        • Setting up and getting started
        • Explaining pages
        • Layouts, ListViews, and Cards
        • Adding actions
        • Adding a dialog
        • Using separate files
        • Next steps
        • Colors and themes in Kirigami
        • Typography
        • Actions based components
        • Page rows and page stacks
        • Scrollable pages and list views
        • Cards
        • Drawers
        • Chips
        • Dialog types
        • Controls and interactive elements
        • Form layouts
        • Inline messages
        • Action toolbars
        • Progress bars and indicators
        • List views
        • Understanding CMakeLists
        • Figuring out main.cpp
        • Connect logic to your QML user interface
        • Connect models to your QML user interface
        • About page
        • Introduction to Kirigami Addons
        • FormCard About pages
        • Form delegates in your settings pages
      • KXmlGui
        • Getting started with KXmlGui
        • Hello World!
        • Creating the main window
        • Using actions
        • Saving and loading
        • Command line interface
      • Python with Kirigami
        • Apps with QML and Python
        • Your first Python + Kirigami application
        • Creating a Python package
        • Creating a Flatpak
      • Common programming mistakes
      • Adding a new KDE project
    • Features
      • Icons
      • Configuration
        • The KConfig Framework
        • Introduction to KConfig
        • Using KConfig XT
        • KDE Frameworks 6 porting guide
        • Settings module (KCM) development
        • KConfigDialog
      • D-Bus
        • What is D-Bus practically useful for?
        • Introduction to D-Bus
        • Accessing D-Bus interfaces
        • Intermediate D-Bus
        • Creating D-Bus interfaces
        • Using custom types with D-Bus
        • D-Bus autostart services
      • Create your own mouse cursor theme
      • Session management
      • Archives
      • Desktop file
      • KAuth
        • Privilege Escalation
        • Using actions in your applications
      • KIdleTime
      • Akonadi: personal information management
        • Debugging Akonadi Resources
        • Using Akonadi in applications
      • Concurrent programming
      • Solid
      • Sonnet
    • Plasma themes and plugins
      • Getting started
      • Plasma Widget tutorial
        • How to create a plasmoid
        • Setup
        • Porting Plasmoids to KF6
        • Testing
        • QML
        • Plasma's QML API
        • Widget Properties
        • Configuration
        • Translations / i18n
        • Examples
        • C++ API
      • KWin Effects
      • Plasma Desktop scripting
        • Javascript Interaction With Plasma Shells
        • Templates
        • Examples
        • API documentation
        • Configuration keys
      • Plasma Style tutorial
        • Creating a Plasma Style quickstart
        • Understanding Plasma Styles
        • SVG elements and Inkscape
        • Background SVG format
        • System and accent colors
        • Theme elements reference
        • Porting themes to Plasma 5
        • Porting themes to Plasma 6
      • Aurorae window decorations
      • KWin scripting tutorial
        • Quick start
        • KWin scripting API
      • Wallpapers
      • Plasma comic
        • Tutorial
        • Testing and debugging
        • Examples
      • Create a custom Window Switcher
      • KRunner C++ Plugin
        • Basic Anatomy of a Runner
        • KRunner metadata format
    • Applications
      • Creating sensor faces
      • Dolphin
        • Creating Dolphin service menus
      • Kate
        • Kate plugin tutorial
      • KMines
        • Making a KMines theme
      • Writing tests
        • Appium automation testing
    • Packaging
      • Android
        • KDE on Android
        • Building applications for Android
        • Packaging and publishing applications for Android
        • Publishing on Google Play
          • Introduction
          • Packaging your app
          • Adding your app to Google Play
          • Publishing your app
          • Releasing new versions of old apps
        • Porting applications to Android
          • Basic porting
          • Making applications run well on Android
          • Metadata
      • Windows
        • Packaging and publishing applications for Windows
        • Publish your app in the Microsoft Store
          • Packaging your app for the Microsoft Store
          • Submitting your app to the Microsoft Store
      • Plasma Mobile
        • KDE on mobile devices
        • Porting a new device to Plasma Mobile
        • KDE Telephony stack
          • General Overview
          • Kernel layer
          • System daemons
            • General overview
            • Developing Telephony functionality
            • ModemManager Telephony functions
          • Session daemons
          • QML declarative plugin layer
          • KDE application layer
        • Execute applications
      • Distributing KDE software as Flatpak
        • Your first Flatpak
        • Extending your package
        • Nightly Flatpaks and Flathub
        • Testing your Flatpak
    • System administration
      • Shell scripting with KDE dialogs
      • Kiosk: Simple configuration management for large deployment
        • Abstract
        • Introduction to Kiosk
        • Kiosk keys
    • Contribute to the documentation
    • About
      • Readme
      • License
        • Creative Commons Attribution-ShareAlike 4.0 International
        • GNU General Public License 3.0 or later
Powered by GitBook
On this page
  • Quick Start: Desktop Console
  • Packaging KWin scripts
  • Where can I find example scripts?
  • KWin scripting basics
  • Configuration
  • Your first (useful) script
  • Publishing
  1. KDE Developer Platform
  2. Plasma themes and plugins
  3. KWin scripting tutorial

Quick start

Learn how to programmatically manipulate windows with KWin scripts.

PreviousKWin scripting tutorialNextKWin scripting API

Last updated 8 months ago

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.

The console can also be opened from the terminal using the following command:

plasma-interactiveconsole --kwin

In Plasma versions earlier than 5.23 the command is:

qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.showInteractiveKWinConsole

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.

Since Plasma version 5.23, . You can retrieve all KWin scripting output from the journal instead, as described in the section on output below.

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

Package structure

A KWin script package has the following directory structure:

myscript
├── contents
│   ├── code
│   │   └── main.js
├── metadata.json

contents/code/main.js, or contents/code/main.qml if you write your script in QML, is where the main code of the script resides.

Additionally you need a metadata.json file which provides some general information about the script.

The toplevel folder myscript may contain any number of additional files, such as a readme.

Metadata

Open up metadata.json in your text editor then paste the following. Keep in mind that Id is the folder name the script is installed to. Eg: .../share/kwin/scripts/myscript/.

X-Plasma-API can be either javascript or declarativescript if you want to generate QML windows.

{
    "KPlugin": {
        "Name": "My Script",
        "Description": "Description of the script",
        "Icon": "preferences-system-windows",
        
        "Authors": [
            {
                "Email": "username@gmail.com",
                "Name": "Firstname Lastname"
            }
        ],
        "Id": "myscript",
        "Version": "1.0",
        "License": "GPLv3",
        "Website": "https://github.com/username/myscript"
    },
    "X-Plasma-API": "javascript",
    "X-Plasma-MainScript": "code/main.js",
    "KPackageStructure": "KWin/Script"
}

Installation

Installed KWin scripts can be found in System Settings > Window Management > KWin scripts.

A packaged KWin script can either be installed from there (note: the list does not yet reload after installing a script) or with the kpackagetool6 tool:

kpackagetool6 --type=KWin/Script -i ~/Code/myscript/

After installing the script, enable it, either from the system settings page or with the kwriteconfig6 tool:

kwriteconfig6 --file kwinrc --group Plugins --key myscriptEnabled true
qdbus org.kde.KWin /KWin reconfigure

Providing a custom installation shell script that would automatically perform additional steps upon installation is not possible for KWin scripts.

Where can I find example scripts?

A few KWin scripts are shipped directly with the window manager. You can find those in your system installation. Just use kpackagetool6 to get a list of the available scripts:

kpackagetool6 --type=KWin/Script --list --global # /usr/share/kwin/scripts/
kpackagetool6 --type=KWin/Script --list # ~/.local/share/kwin/scripts/

KWin scripting basics

Output

The following global function is available to both QML and JavaScript:

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

The print output of Plasma and KWin scripts can be read from the journal:

journalctl -f QT_CATEGORY=js QT_CATEGORY=kwin_scripting

In order to obtain log information for KWin scripts, QT_LOGGING_RULES="kwin_*.debug=true" must be set in your environment. Open kdebugsettings and ensure KWin Scripting is set to Full Debug. If you are still not getting any output, try adding

export QT_LOGGING_RULES="kwin_*.debug=true"

to your ~/.bash_profile or /etc/environent and relogin.

Workspace and Options

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.

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:

const clients = workspace.clientList();
for (var i = 0; i < clients.length; i++) {
  print(clients[i].caption);
}

The following example illustrates how to get informed about newly managed clients and prints out the window id of the new client:

workspace.clientAdded.connect(function(client) {
  print(client.windowId);
});

Configuration

KWin scripts can have a user configuration. To enable this, add the following line to your metadata.json:

"X-KDE-ConfigModule": "kwin/effects/configs/kcm_kwin4_genericscripted"

Declaration

myscript
├── contents
│   ├── code
│   │   └── main.js
│   ├── config
│   │   └── main.xml
├── metadata.json

Graphical interface

A graphical configuration menu for the script can be provided in the form of a file myscript/contents/ui/config.ui.

A KWin script package with a configuration UI will thus have the following sctructure:

myscript
├── contents
│   ├── code
│   │   └── main.js
│   ├── config
│   │   └── main.xml
│   └── ui
│       └── config.ui
├── metadata.json

.ui widgets are most easily edited with the Qt Designer application.

The object name of each UI element which provides the setting for a configuration value must be kcfg_keyName, where keyName is the value of the name attribute for the corresponding entry in the xml flie.

Reading configuration values

Configuration values for KWin scripts will be stored in the file ~/.config/kwinrc, and can be read with the following global function (available in both Javascript and 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.

The key to be used is the value of the name attribute for the corresponding entry in the xml flie.

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 functional and useful script idea, what is really great about this is that it makes for a perfect tutorial example. We 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:

  1. Create an array of clients whose Keep Above property has been removed for maximized windows

  2. Whenever a client is maximized, if it’s Keep Above property is set, remove the Keep Above property.

  3. 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:

var keepAboveMaximized = [];

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:

workspace.clientMaximizeSet.connect(function(client, h, v) {
  if (h && v) {
    print(client.caption + " is fully maximized");
  } else {
    print(client.caption + " is not maximized");
  }
});

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.

Checking keep above

Now we actually want to do something with the maximized Client. We need to check whether the window is set as keep above. If it is so, we need to remove the keep above state and remember that we modified the Client. For better readability the callback is moved into an own method:

function manageKeepAbove(client, h, v) {
  if (h && v) {
    // maximized
    if (client.keepAbove) {
      keepAboveMaximized.push(client);
      client.keepAbove = false;
    }
  }
}

This code checks whether the window is maximized, if that is the case we access the Client's keepAbove property which is a boolean. If the Client is keep above we append the Client to our global array keepAboveMaximized of Clients we modified. This is important to be able to reset the keep above state when the window gets restored again.

Last but not least we have to remove keep above which is a simple assignment to the Client's property. If you want to test it in the desktop scripting console remember to adjust the signal connection:

workspace.clientMaximizeSet.connect(manageKeepAbove);

Restoring it all

Now the last and most important part of it all. Whenever 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 keepAboveMaximized array and if it is, we set its ‘Keep Above’ property, otherwise we don’t bother:

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

In the end, our entire script looks like:

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

What next?

The script is of course very simple. It does not take care of windows which are already present when the window manager starts. It might be an idea to restrict the script to some window classes (e.g. video players). It's up to you.

Publishing

The default scripts bundled with the window manager can also be .

can be found in your user's data install path under kwin/scripts/. This is where your new script will be installed to.

To follow this tutorial, you must have some idea about (or ). A quick introduction can be found in the .

KWin scripts can either be written in (service type javascript) or (service type declarativescript). In order to develop KWin scripts you should know the basic concepts of .

To get a full overview of the available objects and functions, please refer to the .

Depending on your distribution and graphics platform, it may also be necessary to have enabled (this is the default since Plasma 5.25).

To understand which parameters are passed to the event handlers (i.e. the functions we connect to), one can always refer the .

User configurable settings are defined in a file myscript/contents/config/main.xml. An example can be found in the . This results in the following package structure:

The configuration menu is then accessible through the configuration button in the KWin script KCM. Note that users on Plasma versions <5.23 will have to take to be able to access the configuration menu.

Once you have created something nice, consider sharing it with other Plasma users! Create a zip file of the package folder myscript, and upload it to the under the category Linux/Unix Desktops > Window Managers > KWin > KWin scripts. Users will then be able to find and install your script with Discover or via “Get New Scripts…” in System Settings.

the output is not visible in the console window
KPackage
found in the KWin repository
Downloaded KWin scripts
ECMAScript
JavaScript
Plasma scripting tutorial
JavaScript
QML
signals and properties
API documentation
Plasma systemd boot
API documentation
Plasma widget tutorial
additional steps after installation
KDE Store