Hyprland-style named workspaces for KWin 6. Hit a hotkey and the comms
workspace appears on whichever monitor your mouse is on; hit it again and the
monitor goes back to what it was showing.
Pure KWin script — no patched Plasma, no C++.
A named workspace is a real virtual desktop, created lazily the first time you summon it. Summoning does two things, in this order:
- Points the output under the cursor at that desktop, via
workspace.setCurrentDesktopForScreen(desktop, output). - Migrates that desktop's windows onto that output with
workspace.sendClientToScreen().
Step 2 is what makes the workspace portable: in KWin a window's desktop and its output are independent properties, so pointing a monitor at a desktop shows only the windows that already happened to live on that monitor. Both halves are needed.
Order matters, in two places.
sendClientToScreen() re-homes a window onto the destination output's current
desktop, so moving a window before switching the output silently reassigns it to
the wrong desktop and it vanishes. The script switches first, moves second, and
re-asserts window.desktops afterwards.
Activating a window makes KWin display that window's desktop on whichever output holds the window. Activate the workspace's top window while KWin still considers it to be on the monitor you are summoning from, and that monitor is dragged straight back to the workspace — leaving the workspace apparently visible on two monitors, one of them empty once the windows finish moving. The script therefore only ever activates a window already on the summoning output, and releases the other outputs after focus has settled.
Because KWin can also pull an output back asynchronously, after the shortcut
handler has returned, the release is re-checked once via a QTimer 200ms later.
(KWin's JS engine has no setTimeout, but QTimer is constructible and its
callbacks fire normally.) The re-check is skipped if the workspace has since
moved elsewhere, so it cannot fight a newer summon.
A workspace lives on one monitor at a time. Summoning it somewhere else first returns its current monitor to the desktop it was showing.
KWin 6 with per-output virtual desktops enabled — this is the load-bearing feature and it has no GUI toggle:
kwriteconfig6 --file kwinrc --group Windows --key PerOutputVirtualDesktops true./install.shOr manually:
ln -sfn "$PWD" ~/.local/share/kwin/scripts/summonworkspaces
kwriteconfig6 --file kwinrc --group Plugins --key summonworkspacesEnabled true
busctl --user call org.kde.KWin /KWin org.kde.KWin reconfigureOn NixOS, ~/.local/share/kwin/scripts/ is outside the store and works fine as
a symlink; home-manager users can instead point
home.file.".local/share/kwin/scripts/summonworkspaces".source at this
directory.
The script registers shortcuts but leaves them unbound, so nothing collides with your existing setup. Bind them in System Settings → Keyboard → Shortcuts → KWin:
Summon Workspace: <name>— summon to the monitor under the cursor, or dismiss if it is already thereSend Window to Workspace: <name>— move the active window into a workspace (this is how you populate one); the window follows the workspace if it is currently on screenDismiss Summoned Workspace— return the monitor under the cursor to its previous desktop, whichever workspace is showingReload Summon Workspaces Config— re-read the workspace list; rarely needed, see below
Config lives in ~/.config/kwinrc under [Script-summonworkspaces], editable
from the Configure button on System Settings → Window Management → KWin Scripts.
Shipping contents/config/main.xml and contents/ui/config.ui is not enough on
its own to get that button — the package must also point at the generic config
module in metadata.json, which is what actually renders those two files:
"X-KDE-ConfigModule": "kwin/effects/configs/kcm_kwin4_genericscripted"The KCM caches package metadata, so System Settings has to be fully closed and reopened after adding it.
| Key | Default | Meaning |
|---|---|---|
workspaces |
comms,music,notes,scratch |
Comma-separated names. Each gets its own pair of shortcuts. |
moveWindows |
true |
Migrate a workspace's windows to the summoning monitor. Off = the monitor just switches desktops. |
activateOnSummon |
true |
Focus the top window after summoning. |
debug |
false |
Log to the journal (journalctl --user -f | grep summon). |
Add a name to workspaces and its two shortcuts appear in the Shortcuts KCM
immediately — the script watches options.configChanged, which KWin emits when
it reparses kwinrc. Saving in the settings dialog is enough. The virtual desktop
itself is created the first time you actually summon the new name.
From the command line the reparse has to be triggered by hand:
kwriteconfig6 --file kwinrc --group "Script-summonworkspaces" --key workspaces "comms,music,dev"
busctl --user call org.kde.KWin /KWin org.kde.KWin reconfigureReload Summon Workspaces Config covers the case where the config changed but
KWin has not reparsed it — bind it if you edit kwinrc by hand a lot. Note that
readConfig() serves a cached copy until KWin reparses, so the reload shortcut
alone will not see edits made since the last reconfigure.
Dropping a name from workspaces stops it working immediately, but its two
entries stay in the KWin shortcut list as unbound leftovers. KGlobalAccel has no
unregister API and a full script reload does not clear them (verified). They are
cosmetic; to actually remove them, delete the matching lines from the [kwin]
group of ~/.config/kglobalshortcutsrc while logged out — editing it in a live
session gets overwritten by the running daemon.
Delete the leftover virtual desktop in System Settings → Virtual Desktops.
A config name is matched to a virtual desktop by name, case-insensitively, so
Comms adopts an existing comms desktop rather than creating a second one
beside it. Exact matches always win when both exist.
This matters because a name that resolves to the wrong desktop breaks summoning in a confusing way: the summoned desktop is not the one already on screen, so the monitor displaying the old one is never dismissed and you get two same-named workspaces on two monitors. If you rename a desktop in System Settings → Virtual Desktops to something not in the list, it is orphaned in exactly this way — keep the two in sync.
workspace.sendClientToScreen() silently does nothing for some windows: no
error, no exception, the window simply stays on its old output. It has been seen
with a window bound to a custom tile (a 50/50 split), where the call was ignored
in one direction while working in the other on the same window. Clearing
window.tile does fix it, but not within the same synchronous block — an
immediate retry still fails, so the tile release evidently needs an event-loop
turn.
This is worth guarding against because of how it fails: the workspace gets
released from the old monitor as designed, the window never arrives at the new
one, and it ends up on a desktop no output is displaying — invisible everywhere,
with no error anywhere. moveToOutput() therefore verifies the window's output
after the call and escalates: place it by geometry, then drop the tile binding
and place it again, then log a loud failure. Note that windows carrying tiles do
not universally fail — several move correctly — so the trigger is narrower than
"is tiled".
- The desktops are real. Named workspaces show up in the pager, in the Overview, and in next/previous-desktop navigation. KWin has no concept of a hidden desktop, so this cannot be avoided from a script. Delete one in System Settings → Virtual Desktops; the script recreates it on next summon.
- Activating a window pulls its output to its desktop. Clicking a taskbar entry or alt-tabbing to a window on another desktop will switch that monitor and effectively dismiss a summoned workspace. That is KWin's normal activation behaviour, not something the script does.
- Windows are re-laid-out when they cross monitors, per KWin's usual geometry mapping. Harmless between identically sized outputs; expect repositioning between different resolutions or scales.
- Plasma's pager and Overview are written around a single global current desktop, so with per-output desktops they show a simplified view of reality.
kwriteconfig6 --file kwinrc --group Plugins --key summonworkspacesEnabled false
rm ~/.local/share/kwin/scripts/summonworkspaces
busctl --user call org.kde.KWin /KWin org.kde.KWin reconfigureDesktops the script created stay behind; remove them in System Settings → Virtual Desktops.