Github user jmuehlner commented on a diff in the pull request:

    
https://github.com/apache/incubator-guacamole-manual/pull/17#discussion_r84208938
  
    --- Diff: src/chapters/adding-protocol.xml ---
    @@ -118,385 +221,564 @@ AM_CFLAGS = -Werror -Wall -pedantic
     lib_LTLIBRARIES = libguac-client-ball.la
     
     # All source files of libguac-client-ball
    -libguac_client_ball_la_SOURCES = src/ball_client.c
    +libguac_client_ball_la_SOURCES = src/ball.c
     
     # libtool versioning information
     libguac_client_ball_la_LDFLAGS = -version-info 
0:0:0</programlisting></para>
             <para>The GNU Automake files will remain largely unchanged 
throughout
                 the rest of the tutorial. </para>
    -        <para>Once you have created all of the above files, you will have a
    -            functioning client plugin. It doesn't do anything yet, but it 
does
    -            work, and guacd will load it when requested, and unload it 
when the
    -            connection terminates.</para>
    +        <para>Once you have created all of the above files, you will have 
a functioning client
    +            plugin. It doesn't do anything yet, and any connection will be 
extremely short-lived
    +            (the lack of any data sent by the server will lead to the 
client disconnecting under the
    +            assumption that the connection has stopped responding), but it 
does technically
    +            work.</para>
         </section>
         <section xml:id="libguac-client-ball-display-init">
             <title>Initializing the remote display</title>
    -        <para>Now that we have a basic functioning skeleton, we need to 
actually
    -            do something with the remote display. A good first step would 
be
    -            initializing the display - giving the connection a name, 
setting the
    -            remote display size, and providing a basic background.</para>
    -        <para>In this case, we name our connection "Bouncing Ball", set the
    -            display to a nice default of 1024x768, and fill the background 
with
    -            a simple gray:</para>
    +        <para>Now that we have a basic functioning skeleton, we need to 
actually do something with
    +            the remote display. A good first step would be simply 
initializing the display - setting
    +            the remote display size and providing a basic 
background.</para>
    +        <para>In this case, we'll set the display to a nice default of 
1024x768, and fill the
    +            background with gray. Though the size of the display 
<emphasis>can</emphasis> be chosen
    +            based on the size of the user's browser window (which is 
provided by the user during the
    +                <link xmlns:xlink="http://www.w3.org/1999/xlink";
    +                linkend="guacamole-protocol-handshake">Guacamole protocol 
handshake</link>), or even
    +            updated when the window size changes (provided by the user via 
<link
    +                xmlns:xlink="http://www.w3.org/1999/xlink"; 
linkend="size-event-instruction">"size"
    +                instructions</link>), we won't be doing that here for the 
simplicity's sake:</para>
             <informalexample>
    -        <programlisting xml:id="ball-02-ball_client.c" version="5.0" 
xml:lang="en">int guac_client_init(guac_client* client, int argc, char** argv) {
    -<emphasis>
    -    /* Send the name of the connection */
    -    guac_protocol_send_name(client->socket, "Bouncing Ball");
    +        <programlisting xml:id="ball-02-ball_client.c" version="5.0" 
xml:lang="en">#include &lt;guacamole/client.h>
    +<emphasis>#include &lt;guacamole/protocol.h>
    +#include &lt;guacamole/socket.h>
    +#include &lt;guacamole/user.h></emphasis>
    +
    +#include &lt;stdlib.h>
    +
    +...
    +
    +<emphasis>int ball_join_handler(guac_user* user, int argc, char** argv) {
    +
    +    /* Get client associated with user */
    +    guac_client* client = user->client;
    +
    +    /* Get user-specific socket */
    +    guac_socket* socket = user->socket;
     
         /* Send the display size */
    -    guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, 1024, 768);
    +    guac_protocol_send_size(socket, GUAC_DEFAULT_LAYER, 1024, 768);
     
         /* Fill with solid color */
    -    guac_protocol_send_rect(client->socket, GUAC_DEFAULT_LAYER,
    +    guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER,
                 0, 0, 1024, 768);
     
    -    guac_protocol_send_cfill(client->socket,
    +    guac_protocol_send_cfill(socket,
                 GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
                 0x80, 0x80, 0x80, 0xFF);
     
    +    /* Mark end-of-frame */
    +    guac_protocol_send_sync(socket, client->last_sent_timestamp);
    +
         /* Flush buffer */
    -    guac_socket_flush(client->socket);
    +    guac_socket_flush(socket);
    +
    +    /* User successfully initialized */
    +    return 0;
    +
    +}</emphasis>
    +
    +int guac_client_init(guac_client* client) {
    +
    +    /* This example does not implement any arguments */
    +    client->args = TUTORIAL_ARGS;
    +<emphasis>
    +    /* Client-level handlers */
    +    client->join_handler = ball_join_handler;
     </emphasis>
    -    /* Done */
         return 0;
     
     }</programlisting></informalexample>
    -        <para>Note how communication is done with the remote display. The
    -                <classname>guac_client</classname> given to
    -                <methodname>guac_client_init</methodname> has a member,
    -                <property>socket</property>, which is used for 
bidirectional
    -            communication. Guacamole protocol functions, all starting with
    -                "<methodname>guac_protocol_send_</methodname>", provide a
    -            slightly high-level mechanism for sending specific Guacamole
    -            protocol instructions to the remote display over the client's
    -            socket.</para>
    -        <para>Here, we set the name of the connection using a "name" 
instruction
    -            (using <methodname>guac_protocol_send_name</methodname>), we 
resize
    -            the display using a "size" instruction (using
    -                <methodname>guac_protocol_send_size</methodname>), and we 
then
    -            draw to the display using drawing instructions (rect and
    -            cfill).</para>
    +        <para>The most important thing to notice here is the new
    +                <function>ball_join_handler()</function> function. As it 
is assigned to
    +                <property>join_handler</property> of the 
<classname>guac_client</classname> given to
    +                <function>guac_client_init</function>, users which join 
the connection (including
    +            the user that opened the connection in the first place) will 
be passed to this function.
    +            It is the duty of the join handler to initialize the provided
    +                <classname>guac_user</classname>, taking into account any 
arguments received from
    +            the user during the connection handshake (exposed through 
<varname>argc</varname> and
    +                <varname>argv</varname> to the join handler). We aren't 
implementing any arguments,
    +            so these values are simply ignored, but we do need to 
initialize the user with respect
    +            to display state. In this case, we:</para>
    +        <orderedlist>
    +            <listitem>
    +                <para>Send a <link 
xmlns:xlink="http://www.w3.org/1999/xlink";
    +                        linkend="size-instruction">"size" 
instruction</link>, initializing the
    +                    display size to 1024x768.</para>
    +            </listitem>
    +            <listitem>
    +                <para>Draw a 1024x768 gray rectangle over the display 
using the <link
    +                        xmlns:xlink="http://www.w3.org/1999/xlink"; 
linkend="rect-instruction"
    +                        >"rect"</link> and <link 
xmlns:xlink="http://www.w3.org/1999/xlink";
    +                        linkend="cfill-instruction">"cfill"</link> 
instructions.</para>
    +            </listitem>
    +            <listitem>
    +                <para>Send a <link 
xmlns:xlink="http://www.w3.org/1999/xlink";
    +                        linkend="server-sync-instruction">"sync" 
instruction</link>, informing the
    +                    remote display that a frame has been completed.</para>
    +            </listitem>
    +            <listitem>
    +                <para>Flush the socket, ensuring that all data written to 
the socket thus far is
    +                    immediately sent to the user.</para>
    +            </listitem>
    +        </orderedlist>
    +        <para>At this point, if you build, install, and connect using the 
plugin, you will see a
    +            gray screen. The connection will still be extremely 
short-lived, however, since the only
    +            data ever sent by the plugin is sent when the user first 
joins. The lack of any data
    +            sent by the server over the remaining life of the connection 
will lead to the client
    +            disconnecting under the assumption that the connection has 
stopped responding. This will
    +            be rectified shortly once we add the bouncing ball.</para>
         </section>
         <section xml:id="libguac-client-ball-layer">
             <title>Adding the ball</title>
    -        <para>This tutorial is about making a bouncing ball "client", so
    -            naturally we need a ball to bounce. </para>
    -        <para>While we could repeatedly draw and erase a ball on the remote
    -            display, a more efficient technique would be to leverage 
Guacamole's
    -            layers.</para>
    -        <para>The remote display has a single root layer,
    -                <varname>GUAC_DEFAULT_LAYER</varname>, but there can be
    -            infinitely many other child layers, which can have themselves 
have
    -            child layers, and so on. Each layer can be dynamically 
repositioned
    -            within and relative to another layer. Because the compositing 
of
    -            these layers is handled by the remote display, and is likely
    -            hardware-accelerated, this is a much better way to repeatedly
    -            reposition something we expect to move a lot:</para>
    +        <para>This tutorial is about making a bouncing ball "client", so 
naturally we need a ball to
    +            bounce. While we could repeatedly draw and erase a ball on the 
remote display, a more
    +            efficient technique would be to leverage Guacamole's 
layers.</para>
    +        <para>The remote display has a single root layer, 
<varname>GUAC_DEFAULT_LAYER</varname>, but
    +            there can be infinitely many other child layers, which can 
have themselves have child
    +            layers, and so on. Each layer can be dynamically repositioned 
within and relative to
    +            another layer. Because the compositing of these layers is 
handled by the remote display,
    +            and is likely hardware-accelerated, this is a much better way 
to repeatedly reposition
    +            something we expect to move a lot.</para>
    +        <para>Since we're finally adding the ball, and there needs to be 
some structure which
    +            maintains the state of the ball, we must create a header file,
    +                <filename>src/ball.h</filename>, to define this:</para>
             <informalexample>
    -        <programlisting xml:id="ball-03-ball_client.c" version="5.0" 
xml:lang="en">int guac_client_init(guac_client* client, int argc, char** argv) {
    -<emphasis>
    -    /* The layer which will contain our ball */
    +            <programlisting version="5.0" xml:lang="en">#ifndef BALL_H
    +#define BALL_H
    +
    +#include &lt;guacamole/layer.h>
    +
    +typedef struct ball_client_data {
    +
         guac_layer* ball;
    +
    +} ball_client_data;
    +
    +#endif</programlisting>
    +        </informalexample>
    +        <para>To make the build system aware of the existence of the new
    +                <filename>src/ball.h</filename> header file, 
<filename>Makefile.am</filename> must
    +            be updated as well:</para>
    +        <informalexample>
    +            <programlisting version="5.0" xml:lang="en">...
    +
    +# All source files of libguac-client-ball
    +<emphasis>noinst_HEADERS = src/ball.h</emphasis>
    +libguac_client_ball_la_SOURCES = src/ball.c
    +
    +...</programlisting>
    +        </informalexample>
    +        <para>This new structure is intended to house the client-level 
state of the ball,
    +            independent of any users which join or leave the connection. 
The structure must be
    +            allocated when the client begins (within 
<function>guac_client_init</function>), freed
    +            when the client terminates (via a new client free handler), 
and must contain the layer
    +            which represents the ball within the remote display. As this 
layer is part of the remote
    +            display state, it must additionally be initialized when a user 
joins, in the same way
    +            that the display overall was initialized in earlier 
steps:</para>
    +        <informalexample>
    +        <programlisting xml:id="ball-03-ball_client.c" version="5.0" 
xml:lang="en"><emphasis>#include "ball.h"</emphasis>
    +
    +#include &lt;guacamole/client.h>
    +<emphasis>#include &lt;guacamole/layer.h></emphasis>
    +#include &lt;guacamole/protocol.h>
    +#include &lt;guacamole/socket.h>
    +#include &lt;guacamole/user.h>
    +
    +#include &lt;stdlib.h>
    +
    +...
    +
    +int ball_join_handler(guac_user* user, int argc, char** argv) {
    +
    +    /* Get client associated with user */
    +    guac_client* client = user->client;
    +<emphasis>
    +    /* Get ball layer from client data */
    +    ball_client_data* data = (ball_client_data*) client->data;
    +    guac_layer* ball = data->ball;
     </emphasis>
         ...
     <emphasis>
    -    /* Set up our ball layer */
    -    ball = guac_client_alloc_layer(client);
    -    guac_protocol_send_size(client->socket, ball, 128, 128);
    +    /* Set up ball layer */
    +    guac_protocol_send_size(socket, ball, 128, 128);
     
         /* Fill with solid color */
    -    guac_protocol_send_rect(client->socket, ball,
    +    guac_protocol_send_rect(socket, ball,
                 0, 0, 128, 128);
     
    -    guac_protocol_send_cfill(client->socket,
    +    guac_protocol_send_cfill(socket,
                 GUAC_COMP_OVER, ball,
                 0x00, 0x80, 0x80, 0xFF);
     </emphasis>
    -    ...</programlisting></informalexample>
    -        <para>Beyond layers, Guacamole has the concept of buffers, which 
are
    -            identical in use to layers except they are invisible. Buffers 
are
    -            used to store image data for the sake of caching or drawing
    -            operations. We will use them later when we try to make this 
tutorial
    -            prettier.</para>
    -        <para>If you build and install the ball client as-is now, you will 
see a
    -            large gray rectangle (the root layer) with a small blue square 
in
    -            the upper left corner (the ball layer).</para>
    +    /* Mark end-of-frame */
    +    guac_protocol_send_sync(socket, client->last_sent_timestamp);
    +
    +    /* Flush buffer */
    +    guac_socket_flush(socket);
    +
    +    /* User successfully initialized */
    +    return 0;
    +
    +}
    +
    +<emphasis>int ball_free_handler(guac_client* client) {
    +
    +    ball_client_data* data = (ball_client_data*) client->data;
    +
    +    /* Free client-level ball layer */
    +    guac_client_free_layer(client, data->ball);
    +
    +    /* Free client-specific data */
    +    free(data);
    +
    +    /* Data successfully freed */
    +    return 0;
    +
    +}</emphasis>
    +
    +int guac_client_init(guac_client* client) {
    +<emphasis>
    +    /* Allocate storage for client-specific data */
    +    ball_client_data* data = malloc(sizeof(ball_client_data));
    +
    +    /* Set up client data and handlers */
    +    client->data = data;
    +
    +    /* Allocate layer at the client level */
    +    data->ball = guac_client_alloc_layer(client);
    +</emphasis>
    +    ...
    +
    +    /* Client-level handlers */
    +    client->join_handler = ball_join_handler;
    +    <emphasis>client->free_handler = ball_free_handler;</emphasis>
    +
    +    return 0;
    +
    +}</programlisting></informalexample>
    +        <para>The allocate/free pattern for the client-specific data and 
layers should be pretty
    +            straightforward - the allocation occurs when the objects (the 
layer and the structure
    +            housing it) are first needed, and the allocated objects are 
freed once they are no
    +            longer needed (when the client terminates) to avoid leaking 
memory. The initialization
    +            of the ball layer using the Guacamole protocol should be 
familiar as well - it's
    +            identical to the way the screen was initialized, and involves 
the same
    +            instructions.</para>
    +        <para>Beyond layers, Guacamole has the concept of buffers, which 
are identical in use to
    +            layers except they are invisible. Buffers are used to store 
image data for the sake of
    +            caching or drawing operations. We will use them later when we 
try to make this tutorial
    +            prettier. If you build and install the ball client as-is now, 
you will see a large gray
    +            rectangle (the root layer) with a small blue square in the 
upper left corner (the ball
    +            layer).</para>
         </section>
         <section xml:id="libguac-client-ball-bounce">
             <title>Making the ball bounce</title>
    -        <para>To make the ball bounce, we need to track the ball's state,
    -            including current position and velocity. This state information
    -            needs to be stored with the client such that it becomes 
available to
    -            all client handlers.</para>
    -        <para>The best way to do this is to create a data structure that
    -            contains all the information we need and store it in the
    -                <varname>data</varname> member of the
    -                <classname>guac_client</classname>. We create a header 
file to
    -            declare the structure:</para>
    +        <para>To make the ball bounce, we need to track the ball's state, 
including current position
    +            and velocity, as well as a thread which updates the ball's 
state (and the remote
    +            display) as time progresses. The ball state and thread can be 
stored alongside the ball
    +            layer in the existing client-level data structure:</para>
             <informalexample>
    -        <programlisting xml:id="ball-04-ball_client.h" version="5.0" 
xml:lang="en">#ifndef _BALL_CLIENT_H
    -#define _BALL_CLIENT_H
    +        <programlisting xml:id="ball-04-ball_client.h" version="5.0" 
xml:lang="en">...
     
    -#include &lt;guacamole/client.h>
    +#include &lt;guacamole/layer.h>
    +
    +<emphasis>#include &lt;pthread.h></emphasis>
     
     typedef struct ball_client_data {
     
         guac_layer* ball;
    -
    +<emphasis>
         int ball_x;
         int ball_y;
     
         int ball_velocity_x;
         int ball_velocity_y;
     
    +    pthread_t render_thread;
    +</emphasis>
     } ball_client_data;
     
    -int ball_client_handle_messages(guac_client* client);
    -
    -#endif</programlisting></informalexample>
    -        <para>We also need to implement an event handler for the 
handle_messages
    -            event triggered by guacd when the client plugin needs to 
handle any
    -            server messages received or, in this case, update the ball
    -            position:</para>
    +...</programlisting></informalexample>
    +        <para>The contents of the thread will update these values at a 
pre-defined rate, changing
    +            ball position with respect to velocity, and changing velocity 
with respect to collisions
    +            with the display boundaries:</para>
             <informalexample>
    -        <programlisting version="5.0" xml:lang="en">int 
ball_client_handle_messages(guac_client* client) {
    +        <programlisting version="5.0" xml:lang="en">#include "ball.h"
     
    -    /* Get data */
    -    ball_client_data* data = (ball_client_data*) client->data;
    +#include &lt;guacamole/client.h>
    +#include &lt;guacamole/layer.h>
    +#include &lt;guacamole/protocol.h>
    +#include &lt;guacamole/socket.h>
    +#include &lt;guacamole/user.h>
     
    -    /* Sleep a bit */
    -    usleep(30000);
    +<emphasis>#include &lt;pthread.h></emphasis>
    +#include &lt;stdlib.h>
     
    -    /* Update position */
    -    data->ball_x += data->ball_velocity_x * 30 / 1000;
    -    data->ball_y += data->ball_velocity_y * 30 / 1000;
    +...
     
    -    /* Bounce if necessary */
    -    if (data->ball_x &lt; 0) {
    -        data->ball_x = -data->ball_x;
    -        data->ball_velocity_x = -data->ball_velocity_x;
    -    }
    -    else if (data->ball_x >= 1024-128) {
    -        data->ball_x = (2*(1024-128)) - data->ball_x;
    -        data->ball_velocity_x = -data->ball_velocity_x;
    -    }
    +<emphasis>void* ball_render_thread(void* arg) {
     
    -    if (data->ball_y &lt; 0) {
    -        data->ball_y = -data->ball_y;
    -        data->ball_velocity_y = -data->ball_velocity_y;
    -    }
    -    else if (data->ball_y >= (768-128)) {
    -        data->ball_y = (2*(768-128)) - data->ball_y;
    -        data->ball_velocity_y = -data->ball_velocity_y;
    -    }
    +    /* Get data */
    +    guac_client* client = (guac_client*) arg;
    +    ball_client_data* data = (ball_client_data*) client->data;
     
    -    guac_protocol_send_move(client->socket, data->ball,
    -            GUAC_DEFAULT_LAYER, data->ball_x, data->ball_y, 0);
    +    /* Update ball position as long as client is running */
    +    while (client->state == GUAC_CLIENT_RUNNING) {
    +
    +        /* Sleep a bit */
    +        usleep(30000);
    +
    +        /* Update position */
    +        data->ball_x += data->ball_velocity_x * 30 / 1000;
    +        data->ball_y += data->ball_velocity_y * 30 / 1000;
    +
    +        /* Bounce if necessary */
    +        if (data->ball_x &lt; 0) {
    +            data->ball_x = -data->ball_x;
    +            data->ball_velocity_x = -data->ball_velocity_x;
    +        }
    +        else if (data->ball_x >= 1024-128) {
    +            data->ball_x = (2*(1024-128)) - data->ball_x;
    +            data->ball_velocity_x = -data->ball_velocity_x;
    +        }
    +
    +        if (data->ball_y &lt; 0) {
    +            data->ball_y = -data->ball_y;
    +            data->ball_velocity_y = -data->ball_velocity_y;
    +        }
    +        else if (data->ball_y >= (768-128)) {
    +            data->ball_y = (2*(768-128)) - data->ball_y;
    +            data->ball_velocity_y = -data->ball_velocity_y;
    +        }
    +
    +        guac_protocol_send_move(client->socket, data->ball,
    +                GUAC_DEFAULT_LAYER, data->ball_x, data->ball_y, 0);
    +
    +        /* End frame and flush socket */
    +        guac_client_end_frame(client);
    +        guac_socket_flush(client->socket);
     
    -    return 0;
    +    }
     
    -}</programlisting></informalexample>
    -        <para>We also must update 
<methodname>guac_client_init</methodname> to
    -            initialize the structure, store it in the client, and register 
our
    -            new event handler:</para>
    +    return NULL;
    +
    +}</emphasis>
    +
    +...</programlisting></informalexample>
    +        <para>Just as with the join handler, this thread sends a "sync" 
instruction to denote the end
    +            of each frame, though here this is accomplished with
    +                <function>guac_client_end_frame()</function>. This 
function sends a "sync"
    +            containing the current timestamp, and updates the properties 
of the
    +                <classname>guac_client</classname> with the last-sent 
timestamp (the value that our
    +            join handler uses to send <emphasis>its</emphasis> sync). Note 
that we don't redraw the
    +            whole display with each frame - we simply update the position 
of the ball layer using a
    +                <link xmlns:xlink="http://www.w3.org/1999/xlink"; 
linkend="move-instruction">"move"
    +                instruction</link>, and rely on the remote display to 
handle compositing on its
    +            own.Th</para>
    --- End diff --
    
    You probably don't mean to have "Th" here?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---

Reply via email to