---
name: treeland-private-wayland-protocol
description: Use this skill for treeland private Wayland server-side protocol integration. Trigger whenever the task involves treeland-owned xml, `treeland_*` interfaces, `src/modules/*`, QtWayland scanner output, `QtWaylandServer::*`, `local_qtwayland_server_protocol_treeland(...)`, `globalRemove()`, `destroy_resource(...)`, or wiring a private compositor protocol into `Helper::init`. Do not use it for upstream wlroots/waylib protocol wrappers.
---

# Treeland Private Protocol Integration

## Scope
Use this skill only for treeland private protocols, not for upstream wlroots/waylib protocols.

Use this skill when any of these are true:

- the protocol xml is maintained by treeland
- the interface name is typically `treeland_*`
- the code lives under `src/modules/*`
- CMake uses `local_qtwayland_server_protocol_treeland(...)`
- the generated type is `QtWaylandServer::*`

If the protocol is a standard/ext/unstable/wlr upstream protocol, stop and use the upstream protocol skill instead.

## Goal
Complete these four things correctly:

- generate the server protocol code
- implement the manager and resource classes
- integrate it through `WServerInterface`
- wire it into the startup path from `Helper::init`

## Read First
1. The target module `CMakeLists.txt`
2. `src/modules/tools/CMakeLists.txt`
3. Generated files in the build directory
4. `src/seat/helper.cpp`

Look at generated files first:

- `qwayland-server-<basename>.h/.cpp`
- `wayland-<basename>-server-protocol.h/.c`

Do not guess generated class names, `Resource` callback signatures, `init(...)` overloads, or `interfaceName()`.

Note: the `treeland_*.xml` protocol files are provided by an external CMake package `TreelandProtocols` (via `find_package(TreelandProtocols REQUIRED)`), not from the treeland source tree. They are accessed through `${TREELAND_PROTOCOLS_DATA_DIR}/treeland-*.xml`. Do not search for XML files under `src/modules/`.

From generated `qwayland-server-*.h/.cpp`, confirm at least these facts directly:

- which helper APIs are generated, such as `resourceMap()`, `resource()`, `isGlobalRemoved()`, and `interfaceVersion()`
- what the default behavior of `destroy(...)`, `destroy_resource(...)`, and `bind_resource(...)` is
- that `handle_destroy(...)` only forwards the destroy request to your override of `destroy(Resource *resource)`; actual destruction still usually requires `wl_resource_destroy(resource->handle)`
- that `destroy_func` removes the resource from `m_resource_map` before calling your `destroy_resource(...)`
- that after `destroy_resource(...)` returns, generated code deletes the generated `Resource` wrapper object; this does not mean your own QObject/private object is deleted automatically
- that `globalRemove()` does not call `wl_global_destroy()` immediately; it first calls `wl_global_remove(...)` and then performs deferred destruction

These details affect implementation boundaries:

- `destroy_resource(...)` is responsible for cleaning up business objects and business state, not for deleting the generated `Resource` wrapper
- after `globalRemove()`, the more accurate meaning is "global removal scheduled", not "destroyed synchronously"
- if the manager interface in xml has no destructor request, generated code will not provide `handle_destroy(...)` or `virtual void destroy(Resource *resource)` for that manager; do not assume you can override manager destroy in that case

## Build Path
Prefer the existing helper:

`local_qtwayland_server_protocol_treeland(...)`

Typical outputs:

- `wayland-<basename>-server-protocol.c`
- `qwayland-server-<basename>.h/.cpp`

At minimum, verify:

1. the target source list includes `wayland-<basename>-server-protocol.c`
2. include paths contain `${CMAKE_CURRENT_BINARY_DIR}`
3. `BASENAME`, generated filenames, and C++ type names are consistent

## Recommended Code Structure
- public class: `QObject + WServerInterface`
- private manager: inherits `QtWaylandServer::treeland_xxx_manager_v1`
- private resource: inherits `QtWaylandServer::treeland_xxx_v1`

Keep `QtWaylandServer::*` inheritance in `.cpp` private implementation classes so those types do not leak into public headers.

### Layering Model
The dominant design in current treeland private protocols separates business API from protocol implementation details.

#### Public class
Public headers should expose business-facing APIs, not `QtWaylandServer::*` types.

The public class usually:

- inherits `QObject` and `WServerInterface`
- exposes methods, signals, and small query APIs to business code
- implements `create/destroy/global/interfaceName`
- owns the private manager via `std::unique_ptr`

This public class is what `Helper::init` and other treeland business code attach, connect, and call.

#### Private manager class
Defined in `.cpp`, directly inherits `QtWaylandServer::treeland_xxx_manager_v1`.

It usually owns:

- protocol global lifetime
- manager request dispatch
- initial state sync in `bind_resource(...)`
- creation of child resources or context objects

#### Private resource / context class
Defined in `.cpp`, inherits `QtWaylandServer::treeland_xxx_v1`.

It usually exists for one concrete protocol object and is responsible for:

- handling requests for that resource
- cleanup in `destroy_resource(...)`
- translating protocol events into public class signals, callbacks, or business actions

#### When a public child wrapper is needed
Not every child resource needs its own public header.

If the child object is only an internal detail of the manager with no business API needed by other treeland code, keep it as a private `.cpp` resource class.

If the child object must be referenced by treeland business code for a longer lifetime, needs a parent, emits signals, provides extra methods, or is owned by other classes, expose it as a public QObject wrapper (like the handle/context classes in `foreign-toplevel`), with a private protocol implementation class under it.

### Design Preference
Use this rule:

- create a public class only when business code actually needs a business-facing API
- keep pure protocol details inside `.cpp` private classes

Do not create a public header for every protocol child object just because the protocol contains one. Promote it to a public wrapper only when business code actually needs to know about it.

## Manager Interface Wiring
The public class usually implements:

- `create(WServer *server)`
- `destroy(WServer *server)`
- `global() const`
- `interfaceName() const`

Recommended pattern:

```cpp
// Required: #include <qwdisplay.h>
// operator* on QWDisplay returns the underlying wl_display&,
// which is equivalent to server->handle()->handle() but preferred.
void Manager::create(WServer *server)
{
    d->init(*server->handle(), InterfaceVersion);
}

void Manager::destroy(WServer *server)
{
    Q_UNUSED(server);
    d->globalRemove();
}

// In the private manager class (.cpp):
// m_global is protected in the generated base. The public class cannot access
// d->m_global directly (protected does not extend to external pointer access).
// Expose it through a small public accessor in the private subclass:
wl_global *globalHandle() const { return m_global; }

// In the public class:
wl_global *Manager::global() const
{
    return d->globalHandle();
}

QByteArrayView Manager::interfaceName() const
{
    return QtWaylandServer::treeland_foo_manager_v1::interfaceName();
}
```

Rules:

- define `static constexpr int InterfaceVersion = N;` in the public manager class and use it in `create()`
- call `globalRemove()` directly in `destroy()`
- do not wrap it with redundant `if (m_global)` checks
- use class-scoped constants for child interface/resource versions as well when they are not tied to `resource->version()`

### Common Manager Variants
Do not think of the manager as only "the thing that creates the global".

In current code, the manager also often:

- sends initial state in `bind_resource(Resource *resource)`
- manually creates child resources with `wl_resource_create(...)` inside request handlers
- uses `resource->version()` for child resource version instead of inventing a new hard-coded version

New private protocols should default to having a manager `destroy` request and implement:

```cpp
void destroy(Resource *resource) override
{
    wl_resource_destroy(resource->handle);
}
```

A few existing managers missing a `destroy` request are historical design issues. Treat them as compatibility exceptions, not as templates for new protocols.

## Resource Destruction
This is the most error-prone part.

### Explicit client destroy
In `destroy(Resource *resource)`, do only:

```cpp
wl_resource_destroy(resource->handle);
```

Do not put real business cleanup here.

### Final cleanup
Prefer `destroy_resource(Resource *resource)` for the actual cleanup path. This is where you:

- update liveness state
- emit disconnect/close-related signals
- clean up callbacks and connections
- `delete this` or `deleteLater()`

Typical pattern:

```cpp
void destroy_resource(Resource *) override
{
    m_alive = false;
    if (onDisconnected)
        onDisconnected();
    delete this;
}
```

If the callback can run inside Qt signal dispatch or another re-entrant path, prefer `deleteLater()`.

Do not assume `destroy_resource(...)` always deletes the object itself. In current code, these patterns also exist:

- a manager resource only clears maps, watch lists, or owner maps associated with `resource->handle`
- QObject lifetime is owned by a parent, an outer wrapper object, or an explicit container

In other words, the core responsibility of `destroy_resource(...)` is "finish this resource lifetime cleanly", not necessarily "delete every related object immediately".

### When to delete the object
If the QObject/private object was created exclusively for this single Wayland resource and has no other owner, `destroy_resource(...)` should end that object lifetime.

Typical signs:

- the constructor directly takes `wl_resource *` or `Resource *`
- one object corresponds to exactly one client resource
- the object's purpose is only to wrap this protocol object
- no parent, container, or manager continues to own it

Typical implementation:

```cpp
void destroy_resource(Resource *) override
{
    delete this;
}
```

If there is re-entrancy risk, use:

```cpp
void destroy_resource(Resource *) override
{
    deleteLater();
}
```

### When not to delete the object
If the QObject is not exclusively owned by one resource and only uses the resource to track client-specific state, `destroy_resource(...)` should only clean up that resource-specific state.

Typical signs:

- the object itself is the manager and follows global or outer interface lifetime
- the object has a clear QObject parent and that parent owns destruction
- the object is still held by static lists, management containers, or business code
- one object manages multiple resources rather than a strict one-to-one mapping

In that case, typically do only:

- remove `resource->handle` from maps/lists/sets
- clear owner, watch, or callback bookkeeping
- update connection state or availability flags

Do not delete a manager or shared object just because one client resource is destroyed if that object still serves other state.

### Decision rule
Ask one question first:

"Was this C++ object created only for this resource, and should it die with this resource?"

- if yes, `destroy_resource(...)` should delete the object
- if no, `destroy_resource(...)` should only clean up the state tied to that resource

### Manager per-client resources
If the manager only has a lightweight per-client proxy resource, `destroy(Resource*)` calling `wl_resource_destroy(...)` is usually enough. Do not pile business state cleanup into it.

### Child resource creation
Many current private protocols use the pattern "manager manually creates a child resource, then hands it to a private wrapper object", for example:

```cpp
auto *child = wl_resource_create(resource->client(),
                                 &treeland_foo_v1_interface,
                                 resource->version(),
                                 id);
if (!child) {
    wl_client_post_no_memory(resource->client());
    return;
}
new FooContext(child);
```

In this pattern, verify:

- child resource version follows `resource->version()`
- failures use `wl_client_post_no_memory(...)`
- child destruction removes the object from any static list, map, or owner structure when needed

## Wiring Through `Helper::init`
Eventually this must connect back into `src/seat/helper.cpp`.

Check:

1. whether it is initialized through `m_server->attach<...>()`
2. whether it is stored as a `Helper` member when needed
3. whether business `connect(...)` calls are made immediately after attach
4. whether it is wired into `ShellHandler`, `RootSurfaceContainer`, `QmlEngine`, or other existing objects

Do not interpret "the registration point is in `Helper::init`" too rigidly.

The repository also has cases where a higher-level object encapsulates protocol initialization, for example:

```cpp
m_shellHandler->init(m_server, m_seat);
```

That still counts as a protocol registration entry point. The real criteria are:

- whether startup still flows in from `Helper::init`
- whether a higher-level object cleanly encapsulates initialization for a protocol family
- whether the registration entry point and lifetime ownership are still easy to locate

If a protocol is neither registered directly from `Helper::init` nor initialized through a higher-level object such as `ShellHandler`, it is usually still not fully integrated.

## Samples To Avoid
`capture` uses `ws_generate_local` (raw wayland-scanner) instead of the preferred `local_qtwayland_server_protocol_treeland(...)` path, and does not use `QtWaylandServer::*` at all. Do not use it as a template.

`foreign-toplevel` has been fully migrated to the `QtWaylandServer::*` plus `local_qtwayland_server_protocol_treeland(...)` path. It is a valid reference for protocols that need public child wrappers (`ForeignToplevelHandleV1`, `DockPreviewContextV1`), but is too large and complex to be a starting point for simple new protocols.

For new private protocols, prefer `appidresolver`, `prelaunch-splash`, or `screensaver` as templates.

## Reference Starting Points
- `src/modules/app-id-resolver/appidresolver.cpp`
- `src/modules/prelaunch-splash/prelaunchsplash.cpp`
- `src/modules/screensaver/screensaverinterfacev1.cpp`
- `src/modules/tools/CMakeLists.txt`
- `src/seat/helper.cpp`

## Output Requirements
When using this skill for a real task, make these explicit first:

1. where the xml and generated files are
2. how manager `create/destroy/global/interfaceName` should be implemented
3. how `destroy(...)` and `destroy_resource(...)` split responsibilities
4. where the protocol enters the startup path from `Helper::init`
