D-Bus scripting guide
Script layouts, snap windows, and watch PlasmaZones state from the shell.
PlasmaZones exposes 13 D-Bus interfaces on org.plasmazones, object path /PlasmaZones. Nine are meant for scripting; four are internal IPC between the daemon, compositor, editor, and settings app. The per-interface XML files under dbus/ in the source tree are the source of truth for method and signal definitions.
Basics
Everything is on the session bus. Two command-line clients handle most work: qdbus6 for one-shot calls, and gdbus for signal watching. Both ship with standard Qt and GNOME installs.
# Call a method: service object-path interface.methodqdbus6 org.plasmazones /PlasmaZones org.plasmazones.Control.getFullState
# List every interface and method exposed by the daemon at runtimeqdbus6 org.plasmazones /PlasmaZonesqdbus6 org.plasmazones /PlasmaZones with no method prints the full introspection tree. Useful when the XML in the repo is ahead of the version you have installed, or vice versa.
Interfaces
| Interface | Purpose | Audience |
|---|---|---|
Control | High-level convenience: snap a window, toggle autotile, full-state snapshot, support report. | Scripting |
LayoutRegistry | Layout CRUD, active-layout selection, quick-layout slots, per-screen assignment, editor launch. | Scripting |
Autotile | Per-screen autotile enable/disable, algorithm selection, master ratio/count, focus + swap ops. | Scripting |
ZoneDetection | Zone lookup by position, by number, or by direction from another zone. Drag modifier state. | Scripting |
WindowTracking | Window-to-zone queries, floating state, pre-tile geometry cache. | Scripting |
Overlay | Show / hide the zone overlay, highlight specific zones, snap-assist thumbnails, shader preview. | Scripting |
Screen | Physical and virtual screen topology, primary-screen selection, virtual-screen config. | Scripting |
Settings | Generic get / set for every configurable setting, schemas, zone-selector controls. | Scripting |
Shader | Shader enumeration, parameter defaults, user-shader directory, compilation signals. | Scripting |
CompositorBridge | Compositor-facing IPC: applyWindowGeometry, activateWindow signals. | Internal |
WindowDrag | Drag lifecycle (beginDrag, updateDragCursor, endDrag) driven by the compositor. | Internal |
EditorApp | Editor launch handling from the daemon. | Internal |
SettingsApp | Settings-app page switching for single-instance -p / --page flags. | Internal |
Internal interfaces still respond to calls, but they assume the compositor, editor, or settings-app lifecycle is driving them. Invoking WindowDrag.beginDrag from a shell script will compile and dispatch, but the daemon won't be in the right state to handle the drag.
Scripting recipes
List and switch layouts
# Every known layout (JSON with id, name, zones, ...)qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.LayoutRegistry.getLayoutList
# Active layoutqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.LayoutRegistry.getActiveLayout
# Switch to a layout by UUIDqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.LayoutRegistry.setActiveLayout \ '{d2a6c1e3-4b88-4f3f-9b2f-7c0a1e5f2b11}'
# Apply a quick-layout slot (1-9) to a specific screenqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.LayoutRegistry.applyQuickLayout \ 3 "$(qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Screen.getPrimaryScreen)"Layout IDs are QUuid strings; use them verbatim from getLayoutList output including the braces. Screen IDs come from Screen.getScreens or Screen.getPrimaryScreen.
Snap a window programmatically
# Snap window "$WID" to zone 3 on the primary screenqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Control.snapWindowToZone "$WID" 3 ""windowId is the compositor's stable window handle (on KDE: the KWin window UUID). An empty screenId resolves to the primary screen. The zone number is 1-indexed and matches what shows in the overlay.
Toggle autotile per screen
SCREEN=$(qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Screen.getPrimaryScreen)
# Flip between snapping and autotile on this screenqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Control.toggleAutotileForScreen "$SCREEN"
# Or force an explicit retile of one screen or all screensqdbus6 org.plasmazones /PlasmaZones org.plasmazones.Autotile.retile "$SCREEN"qdbus6 org.plasmazones /PlasmaZones org.plasmazones.Autotile.retileAllScreensShow the overlay without dragging
Handy for demos, screenshots, and pairing with zone lookups from ZoneDetection.
qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Overlay.showOverlay
# Highlight a specific zone by UUID (see ZoneDetection for how to get one)qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Overlay.highlightZone "{...uuid...}"
qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Overlay.hideOverlayRead or change a setting
# Dump every setting as JSONqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Settings.getAllSettings
# Read one setting by key. Keys mirror the UI hierarchy, e.g.# "Snapping.Behavior.ZoneSpan.enabled".qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Settings.getSetting "Snapping.Behavior.ZoneSpan.enabled"
# Write one. The value is a QVariant; pass it as a literal string.qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Settings.setSetting \ "Snapping.Behavior.ZoneSpan.enabled" trueFor the full schema, call Settings.getAllSettingSchemas. It returns per-key type, default, and range metadata. The settings app uses the same call to build its UI.
Generate a support report
# 30 minutes of logs (the 0 means "use default")qdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Control.generateSupportReport 0
# Same thing with a 90-minute log windowqdbus6 org.plasmazones /PlasmaZones \ org.plasmazones.Control.generateSupportReport 90The return value is a Markdown string with home paths redacted, safe to paste into a GitHub issue. The plasmazones-report CLI calls this method and wraps it with archive and output options; see the Troubleshooting page for the CLI flags.
Watching signals
Most interfaces emit change signals. To watch them from the shell without writing a client, use gdbus monitor:
# Everything under the PlasmaZones objectgdbus monitor --session \ --dest org.plasmazones --object-path /PlasmaZones
# Or filter to one interfacegdbus monitor --session \ --dest org.plasmazones --object-path /PlasmaZones \ | grep org.plasmazones.LayoutRegistryUseful signals include LayoutRegistry.activeLayoutChanged, Screen.virtualScreensChanged, Overlay.overlayVisibilityChanged, Shader.shadersChanged, and Settings.settingsChanged. Per-interface signal lists are in the XML files linked at the top of this page.
Python, Rust, and other clients
Anything that speaks session D-Bus can talk to the daemon. A few starting points:
- Python:
dbus-pythonfor low-level access, ordasbusfor a higher-level asyncio-friendly API. Proxyorg.plasmazonesat/PlasmaZonesand call methods directly. - Rust:
zbusgenerates typed proxies from#[proxy(...)]macros driven by the interface XML. - Shell:
busctl --user callreplacesqdbus6when systemd is already in the picture. Same arguments in a slightly different order.
Where the source of truth lives
The per-interface XML files under dbus/ carry org.gtk.GDBus.DocString annotations on every method, signal, and argument. Those annotations are the reference for what each call does. The adaptor implementations live next to them under src/dbus/. When scripting against a specific PlasmaZones version, check out the matching tag; method signatures change across releases.
See also
- Keyboard shortcuts → Every default binding. Most of them dispatch to these D-Bus methods internally.
- Troubleshooting → Support-report collection uses Control.generateSupportReport.
- Interface XML on GitHub → Source of truth for method signatures, signals, and DocString annotations.