Table of Contents
The C API for extending and developing with Guacamole is libguac. All native components produced by the Guacamole project link with this library, and this library provides the common basis for extending the native functionality of those native components (by implementing client plugins).
libguac is used mainly for developing client plugins like libguac-client-vnc or libguac-client-rdp, or for developing a proxy supporting the Guacamole protocol like guacd. This chapter is intended to give an overview of how libguac is used, and how to use it for general communication with the Guacamole protocol.
Most functions within libguac handle errors by returning a zero or
non-zero value, whichever is appropriate for the function at hand.
If an error is encountered, the guac_error
variable is set appropriately, and
guac_error_message
contains a
statically-allocated human-readable string describing the context of
the error. These variables intentionally mimic the functionality
provided by errno
and
errno.h
.
Both guac_error
and
guac_error_message
are defined within
error.h
. A human-readable string describing
the error indicated by guac_error
can be
retrieved using guac_status_string()
, which
is also statically allocated.
If functions defined within client plugins set
guac_error
and
guac_error_message
appropriately when errors
are encountered, the messages logged to syslog by guacd will be more
meaningful for both users and developers.
Client plugins are libraries which follow specific conventions such that they can be loaded dynamically by guacd. All client plugins must:
Follow a naming convention, where the name of the library is libguac-client-
PROTOCOL
. This is necessary for guacd to locate the library for a requested protocol.Be linked against libguac, the library used by guacd to handle the Guacamole protocol. The structures which are given to functions invoked by guacd are defined by libguac, and are expected to be manipulated via the functions provided by libguac or as otherwise documented within the structure itself. Communication between guacd and client plugins is only possible if they share the same core structural and functional definitions provided by libguac.
Implement the standard entry point for client plugins,
guac_client_init()
, described in more detail below. It is this function which initializes the structures provided by guacd such that users can join and interact with the connection.
All client plugins must provide a function named
guac_client_init
which accepts, as its sole argument, a
pointer to a guac_client
structure. This function is similar
in principle to the main()
function of a C program, and it
is the responsibility of this function to initialize the provided structure as
necessary to begin the actual remote desktop connection, allow users to join/leave,
etc.
The provided guac_client
will already have been initialized
with handlers for logging, the broadcast socket, etc. The absolutely critical pieces
which must be provided by guac_client_init
are:
A handler for users which join the connection (join_handler). The join handler is also usually the most appropriate place for the actual remote desktop connection to be established.
A
NULL
-terminated set of argument names which the client plugin accepts, assigned to the args property of the givenguac_client
. As the handshake procedure is completed for each connecting user, these argument names will be presented as part of the handshake, and the values for those arguments will be passed to the join handler once the handshake completes.A handler for users leaving the connection (leave_handler), if any cleanup, updates, etc. are required.
A handler for freeing the data associated with the
guac_client
after the connection has terminated (free_handler). If your plugin will allocate any data at all, implementing the free handler is necessary to avoid memory leaks.
If guac_client_init
returns successfully, guacd will
proceed with allowing the first use to join the connection, and the rest of the
plugin lifecycle commences.
Whenever a user joins a connection, including the very first user of a connection
(the user which is establishing the remote desktop connection in the first place),
the join handler of the guac_client will be invoked. This
handler is provided with the guac_user
structure representing
the user that just joined, along with the arguments provided during the handshake
procedure:
int join_handler(guac_user* user, int argc, char** argv) { /* Synchronize display state, init the user, etc. */ } ... /* Within guac_client_init */ client->join_handler = join_handler;
As the parameters and user information provided during the Guacamole protocol handshake are often required to be known before the remote desktop connection can be established, the join handler is usually the best place to create a thread which establishes the remote desktop connection and updates the display accordingly.
If necessary, the user which first established the connection can be distinguished
from all other users by the owner flag of
guac_user
, which will be set to a non-zero value.
Once a user has disconnected, the leave handler of
guac_client
will be invoked. Just as with the join
handler, this handler is provided the guac_user
structure of
the user that disconnected. The guac_user
structure will be
freed immediately after the handler completes:
int leave_handler(guac_user* user) { /* Free user-specific data and clean up */ } ... /* Within guac_client_init */ client->leave_handler = leave_handler;
Once the last user of a connection has left, guacd will begin freeing resources
allocated to that connection, invoking the free handler of the
guac_client
. At this point, the "leave" handler has been
invoked for all previous users. All that remains is for the client plugin to free
any remaining data that it allocated, such that guacd can clean up the rest:
int free_handler(guac_client* client) { /* Disconnect, free client-specific data, etc. */ } ... /* Within guac_client_init */ client->free_handler = free_handler;
The main operand of all drawing instructions is the layer,
represented within libguac by the guac_layer
structure. Each guac_layer
is normally
allocated using guac_client_alloc_layer()
or guac_client_alloc_buffer()
, depending on
whether a layer or buffer is desired, and freed with
guac_client_free_layer()
or
guac_client_free_buffer()
.
Important
Care must be taken to invoke the allocate and free pairs of
each type of layer correctly.
guac_client_free_layer()
should
only be used to free layers allocated with
guac_client_alloc_layer()
, and
guac_client_free_buffer()
should
only be used to free layers allocated with
guac_client_alloc_buffer()
, all
called using the same instance of
guac_client
.
If these restrictions are not observed, the effect of invoking these functions is undefined.
Using these layer management functions allows you to reuse existing layers or buffers after their original purpose has expired, thus conserving resources on the client side, as allocation of new layers within the remote client is a relatively expensive operation.
It is through layers and buffers that Guacamole provides support for hardware-accelerated compositing and cached updates. Creative use of layers and buffers leads to efficient updates on the client side, which usually translates into speed and responsiveness.
Regardless of whether you allocate new layers or buffers, there is
always one layer guaranteed to be present: the default layer,
represented by libguac as GUAC_DEFAULT_LAYER
. If
you only wish to affect to the main display of the connected client
somehow, this is the layer you want to use as the operand of your
drawing instruction.
In addition to drawing, the Guacamole protocol supports streaming of arbitrary data.
The main operand of all streaming instructions is the stream, represented within libguac
by the guac_stream
structure. Each
guac_stream
exists either at the user or client levels,
depending on whether the stream is intended to be broadcast to all users or just one,
and is thus allocated using either guac_client_alloc_stream()
or guac_user_alloc_stream()
. Explicitly-allocated streams must
eventually be freed with guac_client_free_stream()
or
guac_user_free_stream()
.
Important
Just as with layers, care must be taken to invoke the allocate and free pairs
correctly for each explicitly-allocated stream.
guac_client_free_stream()
should only be used to free
streams allocated with guac_client_alloc_stream()
, and
guac_user_free_stream()
should only be used to free
streams allocated with guac_user_alloc_stream()
.
If these restrictions are not observed, the effect of invoking these functions is undefined.
Streams are the means by which data is transmitted for clipboard (via the "clipboard" instruction), audio (via the "audio" instruction), and even the images which make up typical drawing
operations (via the "img" instruction). They will either be allocated
for you, when an inbound stream is received from a user, or allocated manually, when an
outbound stream needs to be sent to a user. As with guac_client
and guac_user
, each guac_stream
has a set
of handlers which correspond to instructions received related to streams. These
instructions are documented in more detail in the section called “Streams and objects”
and Appendix B, Guacamole protocol reference.
All drawing in Guacamole is accomplished through the sending of instructions to the connected client using the Guacamole protocol. The same goes for streaming audio, video, or file content. All features and content supported by Guacamole ultimately reduce to one or more instructions which are part of the documented protocol.
Most drawing using libguac is done using Cairo functions on a
cairo_surface_t
(see the Cairo API documentation) which is
later streamed to the client using an img
instruction and
subsequent blob
instructions, sent via
guac_client_stream_png()
. Cairo was chosen as a dependency
of libguac to provide developers an existing and stable means of drawing to image
buffers which will ultimately be sent as easy-to-digest PNG images.
The Guacamole protocol also supports drawing primitives similar to
those present in the Cairo API and HTML5's canvas tag. These
instructions are documented individually in the Guacamole Protocol
Reference in a section dedicated to drawing instructions, and like
all Guacamole protocol instructions, each instruction has a
corresponding function in libguac following the naming convention
guac_protocol_send_
.OPCODE
()
Each protocol function takes a guac_socket
as an argument,
which is the buffered I/O object used by libguac. For each active connection, there are
two important types of guac_socket
instance: the broadcast
socket, which exists at the client level within the guac_client
,
and the per-user socket, which is accessible within each
guac_user
. Data sent along the client-level broadcast socket
will be sent to all connected users beneath that guac_client
,
while data sent along a user-level socket will be sent only to that user.
For example, to send a "size" instruction to all connected users via the client-level broadcast socket, you could invoke:
guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, 1024, 768);
Or, if the instruction is only relevant to a particular user, the socket associated with that user can be used instead:
guac_protocol_send_size(user->socket, GUAC_DEFAULT_LAYER, 1024, 768);
The sockets provided by libguac are threadsafe at the protocol level. Instructions written to a socket by multiple threads are guaranteed to be written atomically with respect to that socket.
Generally, as guacd receives instructions from the connected client, it invokes event
handlers if set within the associated guac_user
or
guac_client
, depending on the nature of the event. Most
events are user-specific, and thus the event handlers reside within the
guac_user
structure, but there are client-specific events as
well, such as a user joining or leaving the current connection. Event handlers typically
correspond to Guacamole protocol instructions received over the socket by a connected
user, which in turn correspond to events which occur on the client side.
When keys are pressed or released on the client side, the client sends key
instructions to the server. These instructions are parsed and handled by calling the
key event handler installed in the key_handler member of the
guac_user
. This key handler is given the keysym of the
key that was changed, and a boolean value indicating whether the key was pressed or
released.
int key_handler(guac_user* user, int keysym, int pressed) { /* Do something */ } ... /* Within the "join" handler of guac_client */ user->key_handler = key_handler;
When the mouse is moved, and buttons are pressed or released, the client sends
mouse instructions to the server. These instructions are parsed and handled by
calling the mouse event handler installed in the mouse_handler
member of the guac_user
. This mouse handler is given the
current X and Y coordinates of the mouse pointer, as well as a mask indicating which
buttons are pressed and which are released.
int mouse_handler(guac_user* user, int x, int y, int button_mask) { /* Do something */ } ... /* Within the "join" handler of guac_client */ user->mouse_handler = mouse_handler;
The file client.h
also defines the mask
of each button for convenience:
GUAC_CLIENT_MOUSE_LEFT
The left mouse button, set when pressed.
GUAC_CLIENT_MOUSE_MIDDLE
The middle mouse button, set when pressed.
GUAC_CLIENT_MOUSE_RIGHT
The right mouse button, set when pressed.
GUAC_CLIENT_MOUSE_UP
The button corresponding to one scroll in the upwards direction of the mouse scroll wheel, set when scrolled.
GUAC_CLIENT_MOUSE_DOWN
The button corresponding to one scroll in the downwards direction of the mouse scroll wheel, set when scrolled.
If a connected user sends data which should be sent to the clipboard of the remote
desktop, guacd will trigger the clipboard handler installed in the
clipboard_handler member of the
guac_user
associated with that user.
int clipboard_handler(guac_user* user, guac_stream* stream, char* mimetype) { /* Do something */ } ... /* Within the "join" handler of guac_client */ user->clipboard_handler = clipboard_handler;
The handler is expected to assign further handlers to the provided
guac_stream
as necessary, such that the "blob" and "end" instructions received along the stream
can be handled. A similar handler is provided for received files:
int file_handler(guac_user* user, guac_stream* stream, char* mimetype, char* filename) { /* Do something */ } ... /* Within the "join" handler of guac_client */ user->file_handler = file_handler;
This pattern continues for all other types of streams which can be received from a
user. The instruction which begins the stream has a corresponding handler within
guac_user
, and the metadata describing that stream and
provided with the instruction is included within the parameters passed to that
handler.
These handlers are, of course, optional. If any type of stream lacks a corresponding handler, guacd will automatically close the stream and respond with an "ack" instruction and appropriate error code, informing the user's Guacamole client that the stream is unsupported.