This guide is written based on a set of language binding support patches that are not yet in libwayland. A patched version of libwayland is available on my libwayland fork on github.
I first got into the Wayland project because I had the crazy idea to make a Wayland compositor that ran as an Android app. As I started digging into this project I discovered that Android, in spit of having native code support, was not very friendly to C and that Wayland was not very friendly to Java. After much deliberation, I decided that the best place to tie the Java and C together was at the libwayland level and I set out to write Java bindings for Wayland. I am writing this guide in the hopes of helping other developers who wish to write language bindings for libwayland or who simply want to know why I submitted those patches to the project.
One of the questions that constantly comes up on IRC is, “Why bother binding to libwayland? Can’t you just implement the wire protocol yourself?” Yes, technically Wayland is a protocol and, as long as you implement the wire protocol correctly, you can implement it all in your language of choice. However, this is a bad idea for a number of reasons:
Many languages such as Java don’t have full support for UNIX domain sockets. Even if you use a library that provides support for UNIX domain sockets like junixsocket, it will likely not have support for sending and receiving file descriptors via the sendmsg system call.
As libraries get developed around Wayland, most of them will require users to pass in actual pointers to libwayland objects. You can’t do that if you’re not using libwayland
As a more specific version of the above point, EGL Wayland support requires libwayland on both the client and compositor sides.
So, yes, if you’re ok with only having SHM available for passing buffers and never using any libraries, you can bang the wire protocol yourself. However, if you want any of these nicer features, you have to bind to libwayland.
When you start looking at libwayland, binding it can look like a massive project. Fortunately, writing language bindings is not as difficult as it seems at first. For each side (compositor or client), there are less than half a dozen core objects that need to be wrapped. The rest is done by writing a program to parse a protocol XML file and dump out thin wrapper code for each of the protocol interfaces.
On the server side, you have to bind the following:
wl_display
wl_client
wl_event_loop
wl_resource
wl_global
wl_listener
While this looks like a lot, the last two are more-or-less opaque types that simply get passed into other things.
On the client side, you have to bind the following:
wl_display
wl_proxy
wl_event_queue
It is worth noting here that server-side wl_display
is
different from client-side wl_display
and they will need
separate bindings.
In the case of my Java bindings, I wrote an ant plugin that reads the XML file and produces a set of classes, one for each interface, that have nothing but static methods and a couple of static variables. Each class has (rather, will have when I’m finished with it) the following:
A public static member variable WAYLAND_INTERFACE
that contains the interface metadata to pass into
resource.newObject
Requests and Events interfaces for passing into
wl_proxy
or wl_resource
as the
implementation.
For each request or event a static postMyEvent
or
postMyClass
method that is used for sending events/requests
via a Resource or Proxy.
Exactly what form you make the auto-generated protocol code take is up to you. Within the context of Java, this seemed to make the most sense to me. If binding to a different language, I might do it differently. Regardless of the language, however, it will still need those 3 basic components.
Probably the hardest thing to get right when binding to libwayland is event and request dispatching.
First, it helps to know how it is done for C in libwayland. The core
of wayland’s event and resource dispatching system is the following
three structures defined in wayland-util.h
:
struct wl_message {
const char *name;
const char *signature;
const struct wl_interface **types;
};
struct wl_interface {
const char *name;
int version;
int method_count;
const struct wl_message *methods;
int event_count;
const struct wl_message *events;
};
struct wl_object {
const struct wl_interface *interface;
const void *implementation;
uint32_t id;
};
The wl_message
and wl_interface
structures
describe, in a binary computer-readable way, what is contained in the
protocol XML file. The wl_interface
structure tells
libwayland the name of the interface, the expected version, and what
requests (it calls them methods) or events it has. Each request or event
is encoded into a wl_message
structure which contains a
name, a signature (description of its arguments), and an array of types.
If one or more of the arguments described in the signature is of the
object type, the types array specifies the interface corresponding to
that argument.
The signature is simply a string with the event/request arguments encoded as follows:
So how does the whole event/request process work? As an example,
let’s say the client calls wl_surface.attach
. In XML, the
attach request is specified as follows:
<request name="attach">
<arg name="buffer" type="object" interface="wl_buffer" allow-null="true"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</request>
As you can see, the attach request has three arguments: one object
and two integers. From this XML, the scanner generates the following
wl_message
structure that looks something like this:
struct wl_message attach_request = {
"attach",
"?oii",
{&wl_surface_interface, NULL, NULL}
};
The scanner also generates the following inline function:
static inline void
wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer,
int32_t x, int32_t y)
{
wl_proxy_marshal((struct wl_proxy *) wl_surface,
WL_SURFACE_ATTACH, buffer, x, y);
}
These auto-generated inline functions provide an easy-to-read and
typesafe means of calling requests. When a client calls
wl_surface_attach
with a surface, the following things
happen:
The client calls wl_surface_attach
wl_surface_attach
calls the variadic function
wl_proxy_marshal
to make the actual request call.
wl_proxy_marshal
converts the request to the wayland
wire format and sends it to the server
The server recieves the requests and looks up the
wl_resource
that is supposed to recieve the
request.
The server uses libffi to dynamically call the function pointer provided by the compositor to handle the “attach” request.
While a bit simplistic, that is how requests are handled in C. For events, it is basically the same only with the roles of client and server reversed.
The biggest problem with interacting from libwayland in a language other than C is the large number of function pointers it requires. Fore every single request (on the compositor side) or event (on the client side) your program must provide a function pointer. For a language like C++ this isn’t a terrible requirement; you can simply write static wrapper functions for everything pass pointers to them into libwayland. However, even for C++ this makes for a lot of boilerplate code.
How about a dynamic language such as Java or Python? In that case, you have two options. One is that libffi does have the capability to dynamically generate function pointers. However this can be tricky and is not supported on all platforms. The other option is to auto-generate a wrapper function for each possible event/request. Then, not only do you have auto-generated code for the language you’re binding to, you also have auto-generated C code that has to be kept in sync with it.
This is where the concept of dispatchers comes into play. In libwayland, a dispatcher is a special function that takes the place of the call to libffi in the event/request sending and receiving process. A dispatcher is responsible for converting all of the C arguments into the corresponding language’s types and for calling the appropriate function. A dispatcher has the following prototype:
int
resource_dispatcher(const void * data, void *target, uint32_t opcode,
const struct wl_message *message,
union wl_argument *args);
The data
parameter is a piece of dispatcher-specific
data to help in calling the appropriate function or method. This piece
of data serves a similar function to the implementation
only more general. In the case of my Java wrappers, the dispatcher data
is an array of Java method id’s. The target
parameter
specifies is the object (either wl_proxy
or
wl_resource
). The opcode
parameter specifies
which event/request is being called. The events and requests are
zero-indexed and in the order in which they appear in the XML file. The
args
parameter is an array of arguments packed into a
union.
Dispatchers have an integer return type for error handling. On
success, dispatchers should return 0. On an error, they should set
errno
to something reasonable and return a non-zero value.
This will cause an immediate exit from the event loop or event queue. If
you ever return non-zero from a dispatcher, you must
check for errors and do something sensible when the event loop or queue
returns.
Along with the dispatching code, you will also have to write some of
your own marshalling code. On the calling side, the auto-generated C
stubs code calls a variadic function wl_proxy_marshal
or
wl_resource_post_event
. When writing language bindings, you
simply have to provide your language’s version of a variadic function
that performs this same task. In order to accommodate language bindings,
newer versions of libwayland provide versions of these functions that
take their arguments as an array of the union type
wl_argument
just like dispatchers receive them. This way
all of the argument conversion can be done dynamically and you don’t
have to worry about dynamically calling a variadic function.
I will not presume to write a complete guide to memory management for language bindings here. Instead, I will simply describe how I am handling it from the Java side and what facilities are built into libwayland to help with it.
One of the struggles with memory management is that most languages other than C and C++ are garbage collected. In order to make libwayland act the way the programmer expects, we need to have a good concept of ownership built into the way we write our bindings. There are a number of ownership relationships. On the server side, the display owns the clients, the resources, and its own event loop. Also, anything that has a signal associated with it owns the listeners to those signals.
The way I am handling memory management in Java is to store a global
reference to a Java object whenever it is “owned” by something inside
libwayland. Then, a listener is attached to the destroy signal of the
owning object that deletes the global reference when the owning object
is destroyed. This way, the garbage collector can clean up Java object.
As a quick example, I am using the following structure to correspond to
a wl_listener
:
struct wl_jni_listener {
struct wl_listener listener;
struct wl_listener destroy_listener;
jobject self_ref;
};
The first of these two listeners is the actual listener connected to
the signal. The second listener is connected to the destroy signal of
the object containing the signal to which the first is connected. The
self_ref
field contains a global reference to the Java
Listener
object associated with it. When the
destroy_listener
is notified, it removes this reference. A
little care must be taken wit this method when connecting to destroy
signals. In that case, you must connect listener
first and
then destroy_listener
. This ensures that the listener is
not destroyed before it is by the destroy signal.
On the client side, memory management is a lot little easier. This is because every client-side entity must be explicitly destroyed by the client. If the client code does not hang on to the wrapper object and it gets garbage collected, it won’t hurt anything because it never would have been able to call destroy anyway.