LUDASH / API

Talk to your desktop.

Use the local control socket, write a Quickshell component, or build an opt-in native effect. Each interface has a different scope and trust boundary.

This is a local API, not an HTTP service. The website documents and previews configuration; it does not control your desktop. The compositor's Unix socket is restricted to the session user.

Start with ludashctl

Applications launched inside LunaDash inherit LUDASH_CONTROL. For an external terminal, point it at your running session's socket. The default session name is ludash-0; substitute the name passed to --socket.

TERMINAL
export LUDASH_CONTROL="$XDG_RUNTIME_DIR/ludash-test-control"
./build/ludashctl status
./build/ludashctl appearance '{"gap":16,"animations":true}'
./build/ludashctl open-settings modules

Send one UTF-8 JSON object followed by a newline. method and value are strings; JSON-valued settings are encoded inside value. Numeric IDs are also sent as strings.

WIRE REQUEST
{"method":"appearance","value":"{\"gap\":16}"}

A successful call generally returns an updated state object. Failure returns {"error":"message"}; the CLI exits nonzero. Audio and power helpers run asynchronously, so acceptance is not completion—poll status for busy/error fields. Requests are limited to 64 KiB with a three-second server deadline; a client should also bound response size and time.

PYTHON / DIRECT SOCKET CLIENT
import json, os, socket

request = {
    "method": "appearance",
    "value": json.dumps({"accent": "#c4b5fd", "gap": 16})
}
with socket.socket(socket.AF_UNIX) as client:
    client.settimeout(3)
    client.connect(os.environ["LUDASH_CONTROL"])
    client.sendall(json.dumps(request).encode() + b"\n")
    data = b""
    while b"\n" not in data:
        chunk = client.recv(65536)
        if not chunk or len(data) + len(chunk) > 1024 * 1024:
            raise RuntimeError("Invalid LunaDash response")
        data += chunk
    response = json.loads(data)
    if "error" in response:
        raise RuntimeError(response["error"])
    print(response["appearance"])

Control methods

MethodValueResult
statusEmptyRead the session, clients, graphics health, preferences, module state and available services.
appearanceJSON object encoded as a stringValidate and save a partial desktop-preference update. Invalid fields reject the entire update.
open-settingsCategory ID or emptyOpen the settings center. IDs include general, appearance, windows, modules, display, input, sound, network, bluetooth, power, applications, privacy, system, devices and about.
workspaceZero-based workspace indexSwitch to an existing workspace (0 to workspaceCount−1).
focus / minimize / closeClient ID from statusRestore and focus, minimize, or request normal close of a window. Close can trigger an application's save dialog.
languageen_US or zh_TWSave the UI language. The shell refreshes its translation dictionary; reopen native applications.
wallpaper-imageLocal absolute path or file URLValidate and select an image wallpaper.
wallpaper / wallpaper-default0 or 1 / emptySelect a shader palette or restore the bundled image.
default-appsJSON object: {"terminal":[],"files":[]}Save literal executable/argument arrays. Empty arrays select LunaDash defaults; executables must be available.
launch-defaultterminal or filesLaunch the selected default application in this Wayland session.
module-validate / module-saveVersioned module JSON stringValidate without writing, or atomically save and apply a module document (maximum 16 KiB).
module-templatepanel or overviewCreate a starter QML file without overwriting existing code.
module-code-trustLiteral string "true" or "false"Allow or disable custom QML execution for this user. Code is not sandboxed.
module-resetEmptyRestore built-in module settings and disable custom code; keep user QML files.
audioJSON string: {"device":"output","volume":50}Request default input/output volume 0–100, or use a boolean mute field. Requires WirePlumber.
power-profileAdvertised profile ID from statusRequest a supported power-profiles-daemon profile; check status for completion or errors.
system-toolAvailable tool ID from status.settingsToolsOpen a fixed, validated settings helper. Host tools are explicitly marked in the snapshot.
desktop-size1280x720, 1440x900 or 1920x1080Resize a nested window. Fullscreen and standalone EGLFS are rejected.
configure-networkEmptyOpen the available system network editor. Credentials remain in that tool.
setup / finish-setupEmptyReopen the first-run guide or mark it complete.
reset-preferencesEmptyReset LunaDash desktop preferences; module JSON and default-app commands have separate reset controls.
launch-x11Program and quoted argumentsLaunch an X11 program in the authenticated rootful compatibility container. Shell operators are not evaluated.
quitEmptyRequest session shutdown and normal client close.

A small QML interface

Each custom component receives shell, resolved style, and moduleId. Read shell.state as a snapshot and use commands for changes. The host manages the window, dimensions, animation and fallback content.

QML / COMMON CALLS
shell.launch("terminal")
shell.launch("files")
shell.command("workspace", 1)
shell.setAppearance({ blur: true, blurRadius: 18 })
shell.settingsOpen = true
const title = shell.focusedTitle
const label = shell.tr("Settings")

The component guide includes a complete Item template, JSON styles and recovery steps. Custom QML can execute processes and access files; metadata does not isolate it.

Native effect plugins · API 1

Native plugins use the LuDash::CompositorPlugin interface. Implement windowOpened(QQuickItem*) and windowFocused(QQuickItem*) in a QObject subclass, declare Q_INTERFACES, and embed metadata with Q_PLUGIN_METADATA.

METADATA.JSON
{
  "KPlugin": {
    "Id": "org.example.effect",
    "Name": "Example effect",
    "Version": "1.0.0",
    "License": "GPL-3.0-only",
    "EnabledByDefault": false
  },
  "LuDash": {
    "ApiVersion": 1,
    "Type": "WindowEffect",
    "Library": "libexample-effect.so"
  }
}
C++ / HEADER INTERFACE
#include <QObject>
#include <LuDash/plugins/CompositorPlugin.h>

namespace LuDash {
class ExampleEffect final : public QObject, public CompositorPlugin {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID LUDASH_COMPOSITOR_PLUGIN_IID FILE "metadata.json")
    Q_INTERFACES(LuDash::CompositorPlugin)
public:
    void windowOpened(QQuickItem* frame) override;
    void windowFocused(QQuickItem* frame) override;
};
}

Put implementations in a matching .cpp file and build a CMake MODULE library linked to Qt Quick. Store the library and metadata together in ~/.local/share/ludash/plugins/<id>/, enable it in Plugins, then restart the compositor. Keep retained window references in QPointer and never access a destroyed frame.

Native code runs inside the compositor. Plugins are disabled by default. There is no sandbox or signing system, and a faulty plugin can crash the session. The metadata resembles KDE's structure; LunaDash does not implement KWin's ABI or load KWin plugins.

The repository contains a complete fade effect implementation, its header, and packaging instructions.