aboutsummaryrefslogtreecommitdiff
path: root/CONTRIBUTING.md
diff options
context:
space:
mode:
authoremersion <contact@emersion.fr>2018-04-29 22:42:35 +0100
committeremersion <contact@emersion.fr>2018-05-03 18:45:27 +0100
commit8676155ae1fa8730560f04ddc703aef0c29b4a3c (patch)
treecf3a005ed1fe87168f2b1e8cbae7e254adf07120 /CONTRIBUTING.md
parent2964248f42568d7fae410072be30cee1981d6f96 (diff)
Update CONTRIBUTING.md with protocol implementation guidelines
Diffstat (limited to 'CONTRIBUTING.md')
-rw-r--r--CONTRIBUTING.md124
1 files changed, 123 insertions, 1 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fc5ccd9b..41f504bc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -171,7 +171,7 @@ Try to keep the use of macros to a minimum, especially if a function can do the
job. If you do need to use them, try to keep them close to where they're being
used and `#undef` them after.
-## Example
+### Example
```c
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
@@ -232,3 +232,125 @@ error_session:
return NULL;
}
```
+
+## Wayland protocol implementation
+
+Each protocol generally lives in a file with the same name, usually containing
+at leats one struct for each interface in the protocol. For instance,
+`xdg_shell` lives in `types/wlr_xdg_shell.h` and has a `wlr_xdg_surface` struct.
+
+### Globals
+
+Global interfaces generally have public constructors and destructors. Their
+struct has a field holding the `wl_global` itself, a list of resources clients
+created by binding to the global, a destroy signal and a `wl_display` destroy
+listener. Example:
+
+```c
+struct wlr_compositor {
+ struct wl_global *wl_global;
+ struct wl_list wl_resources;
+ …
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal new_surface;
+ struct wl_signal destroy;
+ } events;
+};
+```
+
+When the destructor is called, it should emit the destroy signal, remove the
+display destroy listener, destroy the `wl_global`, destroy all bound resources
+and then destroy the struct.
+
+### Resources
+
+Resources are the representation of Wayland objects on the compositor side. They
+generally have an associated struct, called the _object struct_, stored in their
+`user_data` field.
+
+Object structs can be retrieved from resources via `wl_resource_get_data`. To
+prevent bad casts, a safe helper function checking the type of the resource is
+used:
+
+```c
+static const struct wl_surface_interface surface_impl;
+
+struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &wl_surface_interface,
+ &surface_impl));
+ return wl_resource_get_user_data(resource);
+}
+```
+
+### Destroying resources
+
+Object structs should only be destroyed when their resource is destroyed, ie.
+in the resource destroy handler (set with `wl_resource_set_implementation`).
+Destructor requests should only call `wl_resource_destroy`.
+
+The compositor should not destroy resources on its own.
+
+### Inert resources
+
+Some resources can become inert in situations described in the protocol or when
+the compositor decides to get rid of them. All requests made to inert resources
+should be ignored, except the destructor. This is achieved by:
+
+1. When the resource becomes inert: destroy the object struct and call
+ `wl_resource_set_user_data(resource, NULL)`. Do not destroy the resource.
+2. For each request made to a resource that can be inert: add a NULL check to
+ ignore the request if the resource is inert.
+3. When the client calls the destructor request on the resource: call
+ `wl_resource_destroy(resource)` as usual.
+4. When the resource is destroyed, if the resource isn't inert, destroy the
+ object struct.
+
+Example:
+
+```c
+// Handles the destroy request
+static void subsurface_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+// Handles a regular request
+static void subsurface_set_position(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y) {
+ struct wlr_subsurface *subsurface = subsurface_from_resource(resource);
+ if (subsurface == NULL) {
+ return;
+ }
+
+ …
+}
+
+// Destroys the wlr_subsurface struct
+static void subsurface_destroy(struct wlr_subsurface *subsurface) {
+ if (subsurface == NULL) {
+ return;
+ }
+
+ wl_resource_set_user_data(subsurface->resource, NULL);
+
+ …
+ free(subsurface);
+}
+
+// Resource destroy listener
+static void subsurface_handle_resource_destroy(struct wl_resource *resource) {
+ struct wlr_subsurface *subsurface = subsurface_from_resource(resource);
+ subsurface_destroy(subsurface);
+}
+
+// Makes the resource inert
+static void subsurface_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_subsurface *subsurface =
+ wl_container_of(listener, subsurface, surface_destroy);
+ subsurface_destroy(subsurface);
+}
+```