You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2016/10/20 05:03:15 UTC

[1/4] incubator-guacamole-manual git commit: GUACAMOLE-88: Document size event.

Repository: incubator-guacamole-manual
Updated Branches:
  refs/heads/master 3310df421 -> d5f3ea90a


GUACAMOLE-88: Document size event.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/commit/7adfe325
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/tree/7adfe325
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/diff/7adfe325

Branch: refs/heads/master
Commit: 7adfe32532dc5cfd1ad8aea71db37e7de4746db6
Parents: 3310df4
Author: Michael Jumper <mj...@apache.org>
Authored: Wed Oct 19 12:25:27 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Wed Oct 19 12:43:51 2016 -0700

----------------------------------------------------------------------
 .../instructions/client/event/size.xml          | 25 ++++++++++++++++++++
 src/references/protocol.xml                     |  1 +
 2 files changed, 26 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/7adfe325/src/references/instructions/client/event/size.xml
----------------------------------------------------------------------
diff --git a/src/references/instructions/client/event/size.xml b/src/references/instructions/client/event/size.xml
new file mode 100644
index 0000000..871c578
--- /dev/null
+++ b/src/references/instructions/client/event/size.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<section xml:id="size-event-instruction" xmlns="http://docbook.org/ns/docbook" version="5.0"
+    xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>size</title>
+    <indexterm>
+        <primary>size</primary>
+    </indexterm>
+    <para>Specifies that the client's optimal screen size has changed from what was specified during
+        the handshake, or from previously-sent "size" instructions.</para>
+    <variablelist>
+        <varlistentry>
+            <term><parameter>width</parameter></term>
+            <listitem>
+                <para>The new, optimal screen width.</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><parameter>height</parameter></term>
+            <listitem>
+                <para>The new, optimal screen height.</para>
+            </listitem>
+        </varlistentry>
+    </variablelist>
+</section>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/7adfe325/src/references/protocol.xml
----------------------------------------------------------------------
diff --git a/src/references/protocol.xml b/src/references/protocol.xml
index 121df33..cb1116b 100644
--- a/src/references/protocol.xml
+++ b/src/references/protocol.xml
@@ -128,5 +128,6 @@
         </indexterm>
         <xi:include href="instructions/client/event/key.xml"/>
         <xi:include href="instructions/client/event/mouse.xml"/>
+        <xi:include href="instructions/client/event/size.xml"/>
     </section>
 </appendix>


[2/4] incubator-guacamole-manual git commit: GUACAMOLE-88: Update client plugin tutorial with respect to screen sharing changes.

Posted by jm...@apache.org.
GUACAMOLE-88: Update client plugin tutorial with respect to screen sharing changes.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/commit/dc368ba1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/tree/dc368ba1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/diff/dc368ba1

Branch: refs/heads/master
Commit: dc368ba1871043c13b5d57a2d037f4abfb7dcaf3
Parents: 7adfe32
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Oct 18 01:06:10 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Wed Oct 19 15:17:43 2016 -0700

----------------------------------------------------------------------
 src/chapters/adding-protocol.xml | 906 ++++++++++++++++++++++------------
 1 file changed, 594 insertions(+), 312 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/dc368ba1/src/chapters/adding-protocol.xml
----------------------------------------------------------------------
diff --git a/src/chapters/adding-protocol.xml b/src/chapters/adding-protocol.xml
index 7a4974b..cf19e90 100644
--- a/src/chapters/adding-protocol.xml
+++ b/src/chapters/adding-protocol.xml
@@ -6,39 +6,162 @@
         <primary>protocols</primary>
         <secondary>implementing</secondary>
     </indexterm>
-    <para>While Guacamole as a bundle ships with support for multiple remote
-        desktop protocols (VNC and RDP), this support is provided through
-        plugins which guacd loads dynamically. The Guacamole API has been
-        designed such that protocol support is easy to create, especially when a
-        C library exists providing a basic client implementation.</para>
-    <para>In this tutorial, we implement a simple "client" which renders a
-        bouncing ball using the Guacamole protocol. After completing the
-        tutorial and installing the result, you will be able to add a connection
-        to your Guacamole configuration using the "ball" protocol, and any users
-        using that connection will see a bouncing ball.</para>
-    <para>This example client plugin doesn't actually act as a client, but this
-        isn't important. The Guacamole client is really just a remote display,
-        and this client plugin functions as a simple example application which
-        renders to this display, just as the VNC or RDP support plugins function
-        as VNC or RDP clients which render to the remote display.</para>
+    <para>Guacamole's support for multiple remote desktop protocols is provided through plugins
+        which guacd loads dynamically. The Guacamole API has been designed such that protocol
+        support is easy to create, especially when a C library exists providing a basic client
+        implementation.</para>
+    <para>In this tutorial, we will implement a simple "client" which renders a bouncing ball using
+        the Guacamole protocol. After completing the tutorial and installing the result, you will be
+        able to add a connection to your Guacamole configuration using the "ball" protocol, and any
+        users using that connection will see a bouncing ball.</para>
+    <para>This example client plugin doesn't actually act as a client, but this isn't important. The
+        Guacamole client is really just a remote display, and this client plugin functions as a
+        simple example application which renders to this display, just as Guacamole's own VNC or RDP
+        plugins function as VNC or RDP clients which render to the remote display.</para>
     <para>Each step of this tutorial is intended to exercise a new concept,
         while also progressing towards the goal of a nifty bouncing ball. At the
         end of each step, you will have a buildable and working client
         plugin.</para>
+    <para>This tutorial will use the GNU Automake build system, which is the build system used by
+        Guacamole for libguac, guacd, etc. There will be four files involved:</para>
+    <variablelist>
+        <varlistentry>
+            <term><filename>configure.ac</filename></term>
+            <listitem>
+                <para>Used by GNU Automake to generate the <filename>configure</filename> script
+                    which ultimately serves to generate the <filename>Makefile</filename> which
+                        <command>make</command> will use when building.</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><filename>Makefile.am</filename></term>
+            <listitem>
+                <para>Used by GNU Automake and the <filename>configure</filename> script to generate
+                    the <filename>Makefile</filename> which <command>make</command> will use when
+                    building.</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><filename>src/ball.c</filename></term>
+            <listitem>
+                <para>The main body of code defining the bouncing ball "client".</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><filename>src/ball.h</filename></term>
+            <listitem>
+                <para>A header file defining the structure representing the state of the bouncing
+                    ball (once it becomes necessary to do so).</para>
+            </listitem>
+        </varlistentry>
+    </variablelist>
+    <para>All source files will be within the <filename>src</filename> subdirectory, as is common
+        with C projects, with build files being at the root level directory. The main
+            <filename>src/ball.c</filename> and the build-related <filename>configure.ac</filename>
+        and <filename>Makefile.am</filename> files will be created first, with each successive step
+        building upon those files iteratively, with <filename>src/ball.h</filename> being added when
+        it becomes necessary. After each step, you can build/rebuild the plugin by running
+            <command>make</command>, and then install it (such that guacd can find the plugin) by
+        running <command>make install</command> and <command>ldconfig</command> as root:</para>
+    <informalexample>
+        <screen><prompt>$</prompt> <userinput>make</userinput>
+<computeroutput>  CC       src/ball.lo
+  CCLD     libguac-client-ball.la</computeroutput>
+<prompt>#</prompt> <userinput>make install</userinput>
+<computeroutput>make[1]: Entering directory '/home/user/libguac-client-ball'
+ /usr/bin/mkdir -p '/usr/local/lib'
+ /bin/sh ./libtool   --mode=install /usr/bin/install -c   libguac-client-ball.la '/usr/local/lib'
+...
+----------------------------------------------------------------------
+Libraries have been installed in:
+   /usr/local/lib
+
+If you ever happen to want to link against installed libraries
+in a given directory, LIBDIR, you must either use libtool, and
+specify the full pathname of the library, or use the '-LLIBDIR'
+flag during linking and do at least one of the following:
+   - add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
+     during execution
+   - add LIBDIR to the 'LD_RUN_PATH' environment variable
+     during linking
+   - use the '-Wl,-rpath -Wl,LIBDIR' linker flag
+   - have your system administrator add LIBDIR to '/etc/ld.so.conf'
+
+See any operating system documentation about shared libraries for
+more information, such as the ld(1) and ld.so(8) manual pages.
+----------------------------------------------------------------------
+make[1]: Nothing to be done for 'install-data-am'.
+make[1]: Leaving directory '/home/user/libguac-client-ball'</computeroutput>
+<prompt>#</prompt> <userinput>ldconfig</userinput></screen>
+    </informalexample>
+    <para>Prior to the first time <command>make</command> is invoked, you will need to run the
+            <filename>configure</filename> script, which will first need to be generated using
+            <command>autoreconf</command>:</para>
+    <informalexample>
+        <screen><prompt>$</prompt> <userinput>autoreconf -fi</userinput>
+<computeroutput>libtoolize: putting auxiliary files in '.'.
+libtoolize: copying file './ltmain.sh'
+libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
+libtoolize: copying file 'm4/libtool.m4'
+libtoolize: copying file 'm4/ltoptions.m4'
+libtoolize: copying file 'm4/ltsugar.m4'
+libtoolize: copying file 'm4/ltversion.m4'
+libtoolize: copying file 'm4/lt~obsolete.m4'
+configure.ac:10: installing './compile'
+configure.ac:4: installing './missing'
+Makefile.am: installing './depcomp'</computeroutput>
+<prompt>$</prompt> <userinput>./configure</userinput>
+<computeroutput>checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+configure: creating ./config.status
+config.status: creating Makefile
+config.status: executing depfiles commands
+config.status: executing libtool commands</computeroutput>
+<prompt>$</prompt></screen>
+    </informalexample>
+    <para>This process is almost identical to that of building guacamole-server from git, as
+        documented in <xref xmlns:xlink="http://www.w3.org/1999/xlink"
+            linkend="building-guacamole-server"/>.</para>
+    <important>
+        <para>The libguac library which is part of guacamole-server is a required dependency of this
+            project. <emphasis>You must first install libguac, guacd, etc. by <link
+                    xmlns:xlink="http://www.w3.org/1999/xlink" linkend="building-guacamole-server"
+                    >building and installing guacamole-server</link>.</emphasis> If guacamole-server
+            has not been installed, and libguac is thus not present, the
+                <filename>configure</filename> script will fail with an error indicating that it
+            could not find libguac:</para>
+        <informalexample>
+            <screen><prompt>$</prompt> <userinput>./configure</userinput>
+<computeroutput>checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+checking for guac_client_stream_png in -lguac... no
+configure: error: "libguac is required for communication via "
+                   "the Guacamole protocol"</computeroutput>
+<prompt>$</prompt></screen>
+        </informalexample>
+        <para>You will need to install guacamole-server and then rerun
+                <filename>configure</filename>.</para>
+    </important>
     <section xml:id="libguac-client-ball-skeleton">
         <title>Minimal skeleton client</title>
-        <para> Very little needs too be done to implement the most basic client
-            plugin possible: </para>
+        <para>Very little needs too be done to implement the most basic client plugin possible. We
+            begin with <filename>src/ball.c</filename>, containing the absolute minimum required for
+            a client plugin:</para>
         <informalexample>
-            <programlisting xml:id="ball-01-ball_client.c" version="5.0" xml:lang="en">#include &lt;stdlib.h>
-#include &lt;guacamole/client.h>
+            <programlisting xml:id="ball-01-ball_client.c" version="5.0" xml:lang="en">#include &lt;guacamole/client.h>
+
+#include &lt;stdlib.h>
+
+/* Client plugin arguments (empty) */
+const char* TUTORIAL_ARGS[] = { NULL };
 
-/* Client plugin arguments */
-const char* GUAC_CLIENT_ARGS[] = { NULL };
+int guac_client_init(guac_client* client) {
 
-int guac_client_init(guac_client* client, int argc, char** argv) {
+    /* This example does not implement any arguments */
+    client->args = TUTORIAL_ARGS;
 
-    /* Do nothing ... for now */
     return 0;
 
 }</programlisting>
@@ -52,59 +175,39 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
             guacd loads the plugin when a new connection is made and your
             protocol is selected.</para>
         <para><methodname>guac_client_init</methodname> receives a single
-                <classname>guac_client</classname> and the same
-                <varname>argc</varname> and <varname>argv</varname> arguments
-            that are typical of a C entry point. While we won't be using
-            arguments in this tutorial, a typical client plugin implementation
-            would register its arguments by specifying them in the
-                <varname>GUAC_CLIENT_ARGS</varname> static variable, and would
-            receive their values as received from the remote client through
-                <varname>argv</varname>.</para>
-        <para>The <classname>guac_client</classname> given will live until the
-            connection with the remote display closes. Your
-                <methodname>guac_client_init</methodname> function is expected
-            to parse any arguments in <varname>argv</varname> and initialize the
-            given <classname>guac_client</classname>, returning a success code
-            (zero) if the client was initialized successfully.</para>
-        <para>Place this code in a file called
-                <filename>ball_client.c</filename> in a subdirectory called
-                <filename>src</filename>. The build files provided by this
-            tutorial assume this is the location of all source code.</para>
-        <para>This tutorial, as well as all other C-based Guacamole projects,
-            uses the GNU Automake build system due to its ubiquity and ease of
-            use. The minimal build files required for a libguac-based project
-            that uses GNU Automake are fairly simple. We need a file called
-                <filename>configure.in</filename> which describes the name of
-            the project and what it needs configuration-wise:</para>
+                <classname>guac_client</classname> which it must initialize. Part of this
+            initialization process involves declaring the list of arguments that joining users can
+            specify. While we won't be using arguments in this tutorial, and thus the arguments
+            assigned above are simply an empty list, a typical client plugin implementation would
+            register arguments which define the remote desktop connection and its behavior. Examples
+            of such parameters can be seen in the connection parameters for the protocols supported
+            by Guacamole out-of-the-box (see <xref xmlns:xlink="http://www.w3.org/1999/xlink"
+                linkend="connection-configuration"/>).</para>
+        <para>The <classname>guac_client</classname> instance given to
+                <methodname>guac_client_init</methodname> will be shared by the user that starts the
+            connection, and any users which join the connection via screen sharing. It lives until
+            the connection is explicitly closed, or until all users leave the connection.</para>
+        <para>For this project to build with GNU Automake, we a <filename>configure.ac</filename>
+            file which describes the name of the project and what it needs configuration-wise. In
+            this case, the project is "libguac-client-ball", and it depends on the "libguac" library
+            used by guacd and all client plugins:</para>
         <informalexample>
         <programlisting xml:id="ball-01-configure.in" version="5.0" xml:lang="en"># Project information
-AC_INIT(src/ball_client.c)
-AM_INIT_AUTOMAKE([libguac-client-ball], 0.1.0)
-AC_CONFIG_MACRO_DIR([m4])
+AC_PREREQ([2.61])
+AC_INIT([libguac-client-ball], [0.1.0])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+AM_SILENT_RULES([yes])
 
-# Checks for required build tools
+AC_CONFIG_MACRO_DIRS([m4])
+
+# Check for required build tools
 AC_PROG_CC
 AC_PROG_LIBTOOL
 
 # Check for libguac (http://guac-dev.org/)
-AC_CHECK_LIB([guac], [guac_client_plugin_open],,
+AC_CHECK_LIB([guac], [guac_client_stream_png],,
       AC_MSG_ERROR("libguac is required for communication via "
-                   "the guacamole protocol"))
-
-# Check for Cairo (http://www.cairo-graphics.org)
-AC_CHECK_LIB([cairo], [cairo_create],,
-      AC_MSG_ERROR("cairo is required for drawing"))
-
-# Checks for header files.
-AC_CHECK_HEADERS([stdlib.h \
-                  string.h \
-                  syslog.h \
-                  guacamole/client.h \
-                  guacamole/socket.h \
-                  guacamole/protocol.h])
-
-# Checks for library functions.
-AC_FUNC_MALLOC
+                   "the Guacamole protocol"))
 
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT</programlisting></informalexample>
@@ -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>
+        <para>We now need to update <methodname>guac_client_init</methodname> to actually create
+            this thread, initialize the ball state within the structure, and store the thread for
+            future cleanup when the client terminates:</para>
         <informalexample>
-        <programlisting xml:id="ball-04-ball_client.c" version="5.0" xml:lang="en"><emphasis>#include "ball_client.h"
-</emphasis>
-...
+        <programlisting xml:id="ball-04-ball_client.c" version="5.0" xml:lang="en">...
 
-int guac_client_init(guac_client* client, int argc, char** argv) {
+int ball_free_handler(guac_client* client) {
 
+    ball_client_data* data = (ball_client_data*) client->data;
 <emphasis>
-    ball_client_data* data = malloc(sizeof(ball_client_data));
+    /* Wait for render thread to terminate */
+    pthread_join(data->render_thread, NULL);
 </emphasis>
     ...
-<emphasis>
-    /* Set up client data and handlers */
-    client->data = data;
-    client->handle_messages = ball_client_handle_messages;
 
-    /* Set up our ball layer */
-    data->ball = guac_client_alloc_layer(client);
+}
+
+int guac_client_init(guac_client* client) {
 
+    ...
+<emphasis>
     /* Start ball at upper left */
     data->ball_x = 0;
     data->ball_y = 0;
 
     /* Move at a reasonable pace to the lower right */
     data->ball_velocity_x = 200; /* pixels per second */
-    data->ball_velocity_y = 200;   /* pixels per second */
+    data->ball_velocity_y = 200; /* pixels per second */
+
+    /* Start render thread */
+    pthread_create(&amp;data->render_thread, NULL, ball_render_thread, client);
 </emphasis>
     ...
 
 }</programlisting></informalexample>
-        <para>guacd will call the <methodname>handle_messages</methodname>
-            handler of the <classname>guac_client</classname> repeatedly, if
-            defined. It will stop calling
-                <methodname>handle_messages</methodname> temporarily if the
-            remote display appears to be lagging behind due to a slow network or
-            slow browser or computer, so there is no guarantee that
-                <methodname>handle_messages</methodname> will be called as
-            frequently as we would like, but for now, we assume there will be
-            essentially no delay between calls, and we include our own delay of
-            30ms between frames</para>
-        <para>Because we now have header files, we need to update
-                <filename>Makefile.am</filename> to include our header and the
-            directory it's in:</para>
-        <informalexample>
-        <programlisting xml:id="ball-04-Makefile.am" version="5.0" xml:lang="en">...
-
-<emphasis>AM_CFLAGS = -Werror -Wall -pedantic -Iinclude</emphasis>
-
-...
-
-<emphasis>noinst_HEADERS = include/ball_client.h</emphasis></programlisting></informalexample>
-        <para>Once built and installed, our ball client now has a bouncing ball,
-            albeit a very square and plain one.</para>
+        <para>The thread contains a render loop which continually checks the
+                <property>state</property> property of the <classname>guac_client</classname>. This
+            property is set to <constant>GUAC_CLIENT_RUNNING</constant> when the connection begins,
+            and remains that way for the duration of the connection. When guacd needs to terminate
+            the connection (such as when the last user leaves), the value will change to
+                <constant>GUAC_CLIENT_STOPPING</constant>. The free handler we've written can thus
+            rely on <function>pthread_join()</function> to block until the data previously used by
+            the plugin is no longer being used and can safely be freed.</para>
+        <para>Once built and installed, our ball client now has a bouncing ball, albeit a very
+            square and plain one. Now that the display is continually updating, and data is being
+            continually received from the server, connected clients will no longer automatically
+            disconnect.</para>
     </section>
     <section xml:id="libguac-client-ball-pretty">
         <title>A prettier ball</title>
-        <para>Now that we have our ball bouncing, we might as well try to make
-            it actually look like a ball, and try applying some of the fancier
-            graphics features that Guacamole offers.</para>
-        <para>Guacamole provides instructions common to most 2D drawing APIs,
-            including HTML5's canvas and Cairo. This means you can draw arcs,
-            curves, apply fill and stroke, and even use the contents of another
-            layer or buffer as the pattern for a fill or stroke.</para>
-        <para>We will try creating a simple gray checkerboard pattern in a
-            buffer and use that for the background instead of the previous gray
-            rectangle.</para>
-        <para>We will also modify the ball by removing the rectangle and
-            replacing it with an arc, in this case a circle, complete with
-            stroke (border) and translucent-blue fill.</para>
+        <para>Now that we have our ball bouncing, we might as well try to make it actually look like
+            a ball, and try applying some of the fancier graphics features that Guacamole offers.
+            Guacamole provides instructions common to most 2D drawing APIs, including HTML5's canvas
+            and Cairo. This means you can draw arcs, curves, apply fill and stroke, and even use the
+            contents of another layer or buffer as the pattern for a fill or stroke. In complex
+            cases involving many draw operations, it will actually be more efficient to render to a
+            server-side Cairo surface and send only image data to the client, but it's perfect for
+            relatively simple cases like our ball.</para>
+        <para>We will try creating a simple gray checkerboard pattern in a buffer, using that for
+            the background instead of the previous gray rectangle, and will modify the ball by
+            replacing the rectangle with an arc, in this case a full circle, complete with stroke
+            (border) and translucent-blue fill:</para>
         <informalexample>
-        <programlisting xml:id="ball-05-ball_client.c" version="5.0" xml:lang="en">int guac_client_init(guac_client* client, int argc, char** argv) {
-
-    ...
-
-    guac_layer* texture;
+        <programlisting xml:id="ball-05-ball_client.c" version="5.0" xml:lang="en">int ball_join_handler(guac_user* user, int argc, char** argv) {
 
     ...
 <emphasis>
     /* Create background tile */
-    texture = guac_client_alloc_buffer(client);
+    guac_layer* texture = guac_client_alloc_buffer(client);
 
-    guac_protocol_send_rect(client->socket, texture, 0, 0, 64, 64);
-    guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, texture,
+    guac_protocol_send_rect(socket, texture, 0, 0, 64, 64);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
             0x88, 0x88, 0x88, 0xFF);
 
-    guac_protocol_send_rect(client->socket, texture, 0, 0, 32, 32);
-    guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, texture,
+    guac_protocol_send_rect(socket, texture, 0, 0, 32, 32);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
             0xDD, 0xDD, 0xDD, 0xFF);
 
-    guac_protocol_send_rect(client->socket, texture, 32, 32, 32, 32);
-    guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, texture,
+    guac_protocol_send_rect(socket, texture, 32, 32, 32, 32);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
             0xDD, 0xDD, 0xDD, 0xFF);
 </emphasis>
 
-    /* Fill with solid color */
-    guac_protocol_send_rect(client->socket, GUAC_DEFAULT_LAYER,
+    /* Fill with <emphasis>texture</emphasis> */
+    guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER,
             0, 0, 1024, 768);
 
-<emphasis>
-    guac_protocol_send_lfill(client->socket,
+
+    <emphasis>guac_protocol_send_lfill</emphasis>(socket,
             GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
-            texture);
-</emphasis>
-    ...
+            <emphasis>texture</emphasis>);
+
+    /* Set up ball layer */
+    guac_protocol_send_size(socket, ball, 128, 128);
 <emphasis>
     /* Fill with solid color */
-    guac_protocol_send_arc(client->socket, data->ball,
+    guac_protocol_send_arc(socket, data->ball,
             64, 64, 62, 0, 6.28, 0);
 
-    guac_protocol_send_close(client->socket, data->ball);
+    guac_protocol_send_close(socket, data->ball);
 
-    guac_protocol_send_cstroke(client->socket,
+    guac_protocol_send_cstroke(socket,
             GUAC_COMP_OVER, data->ball,
             GUAC_LINE_CAP_ROUND, GUAC_LINE_JOIN_ROUND, 4,
             0x00, 0x00, 0x00, 0xFF);
 
-    guac_protocol_send_cfill(client->socket,
+    guac_protocol_send_cfill(socket,
             GUAC_COMP_OVER, data->ball,
             0x00, 0x80, 0x80, 0x80);
+
+    /* Free texture (no longer needed) */
+    guac_client_free_buffer(client, texture);
 </emphasis>
+    /* Mark end-of-frame */
+    guac_protocol_send_sync(socket, client->last_sent_timestamp);
+
     ...
 
 }</programlisting></informalexample>
-        <para>Again, because we put the ball in its own layer, we don't have to
-            worry about compositing it ourselves. The remote display will handle
-            this, and will likely do so with hardware acceleration.</para>
-        <para>Build and install the ball client after this step, and you will
-            have a rather nice-looking bouncing ball.</para>
+        <para>Again, because we put the ball in its own layer, we don't have to worry about
+            compositing it ourselves. The remote display will handle this, and will likely do so
+            with hardware acceleration, even though the ball is now translucent. Build and install
+            the ball client after this step, and you will have a rather nice-looking bouncing
+            ball.</para>
     </section>
     <section xml:id="libguac-client-ball-time">
         <title>Handling the passage of time</title>
-        <para>Because the <methodname>handle_messages</methodname> handler will
-            only be called as guacd deems appropriate, we cannot rely on
-            instantaneous return of control. The server may experience load,
-            causing guacd to lose priority and delay handling of messages, or
-            the remote display may lag due to network or software issues,
-            forcing guacd to temporarily pause updates.</para>
-        <para>We must modify our ball state to include the time the last update
-            took place:</para>
+        <para>There are never any guarantees when it comes to timing, threads, and network
+            performance. We cannot necessarily rely on the remote display to handle updates in a
+            timely manner (it may be slow), nor can we rely on the network or server to give
+            priority to communication from guacd. </para>
+        <para>The render thread needs to be modified to take this into account, by tracking the
+            actual time spent within each frame, and estimating the amount of time the client spends
+            rendering each frame:</para>
         <informalexample>
-        <programlisting xml:id="ball-06-ball_client.h" version="5.0" xml:lang="en">typedef struct ball_client_data {
+        <programlisting version="5.0" xml:lang="en">#include "ball.h"
 
-    ...
-
-    <emphasis>guac_timestamp last_update;</emphasis>
+#include &lt;guacamole/client.h>
+#include &lt;guacamole/layer.h>
+#include &lt;guacamole/protocol.h>
+#include &lt;guacamole/socket.h>
+<emphasis>#include &lt;guacamole/timestamp.h></emphasis>
+#include &lt;guacamole/user.h>
 
-} ball_client_data;</programlisting></informalexample>
-        <para>Naturally, this new structure member must be initialized within
-                <methodname>guac_client_init</methodname>:</para>
-        <informalexample>
-        <programlisting xml:id="ball-06-ball_client.c" version="5.0" xml:lang="en">int guac_client_init(guac_client* client, int argc, char** argv) {
+#include &lt;pthread.h>
+#include &lt;stdlib.h>
 
-    ball_client_data* data = malloc(sizeof(ball_client_data));
-
-    ...
+...
 
-<emphasis>    data->last_update = guac_protocol_get_timestamp();</emphasis>
+void* ball_render_thread(void* arg) {
 
     ...
-
-}</programlisting></informalexample>
-        <para>And we need to modify the message handler to check the last update
-            time, updating the ball's position based on its current velocity and
-            the elapsed time:</para>
-        <informalexample>
-        <programlisting version="5.0" xml:lang="en">int ball_client_handle_messages(guac_client* client) {
-
-    /* Get data */
-    ball_client_data* data = (ball_client_data*) client->data;
-
 <emphasis>
-    guac_timestamp current;
-    int delta_t;
+    /* Init time of last frame to current time */
+    guac_timestamp last_frame = guac_timestamp_current();
+</emphasis>
+    /* Update ball position as long as client is running */
+    while (client->state == CLIENT_RUNNING) {
+<emphasis>
+        /* Default to 30ms frames */
+        int frame_duration = 30;
 
-    /* Sleep for a bit, then get timestamp */
-    usleep(30000);
-    current = guac_protocol_get_timestamp();
+        /* Lengthen frame duration if client is lagging */
+        int processing_lag = guac_client_get_processing_lag(client);
+        if (processing_lag > frame_duration)
+            frame_duration = processing_lag;
 
-    /* Calculate change in time */
-    delta_t = current - data->last_update;
+        /* Sleep for duration of frame, then get timestamp */
+        usleep(frame_duration);
+        guac_timestamp current = guac_timestamp_current();
 
-    /* Update position */
-    data->ball_x += data->ball_velocity_x * delta_t / 1000;
-    data->ball_y += data->ball_velocity_y * delta_t / 1000;
+        /* Calculate change in time */
+        int delta_t = current - last_frame;
 </emphasis>
-    ...
+        /* Update position */
+        data->ball_x += data->ball_velocity_x * <emphasis>delta_t</emphasis> / 1000;
+        data->ball_y += data->ball_velocity_y * <emphasis>delta_t</emphasis> / 1000;
+
+        ...
 <emphasis>
-    /* Update timestamp */
-    data->last_update = current;
+        /* Update timestamp */
+        last_frame = current;
 </emphasis>
+    }
 
-    return 0;
+    ...
 
 }</programlisting></informalexample>
-        <para>At this point, we now have a robust Guacamole client plugin. It
-            properly handles the lack of time guarantees for message handler
-            calls, meanwhile providing the user with a seamlessly bouncing
-            ball.</para>
+        <para>The calculations are pretty simple. Rather than hard-code the duration of each frame,
+            we us a default of 30 milliseconds, lengthening the frame if Guacamole's built-in lag
+            estimation determines that the client is having trouble. The physics portion of the
+            update no longer assumes that the frame will be exactly 30 milliseconds, instead relying
+            on the actual time elapsed since the previous frame.</para>
+        <para>At this point, we now have a robust Guacamole client plugin. It handles
+            joining/leaving users correctly, continually updates the remote display state while
+            taking into account variable network/server/client conditions, and cleans up after
+            itself when the connection finally terminates.</para>
     </section>
 </chapter>


[4/4] incubator-guacamole-manual git commit: GUACAMOLE-88: Merge updated plugin creation tutorial.

Posted by jm...@apache.org.
GUACAMOLE-88: Merge updated plugin creation tutorial.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/commit/d5f3ea90
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/tree/d5f3ea90
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/diff/d5f3ea90

Branch: refs/heads/master
Commit: d5f3ea90a5f080db6ca73b3af241211980cb8d8a
Parents: 3310df4 6ea64c0
Author: James Muehlner <ja...@guac-dev.org>
Authored: Wed Oct 19 22:02:26 2016 -0700
Committer: James Muehlner <ja...@guac-dev.org>
Committed: Wed Oct 19 22:02:26 2016 -0700

----------------------------------------------------------------------
 src/chapters/adding-protocol.xml                | 906 ++++++++++++-------
 .../instructions/client/event/size.xml          |  25 +
 src/references/protocol.xml                     |   1 +
 tutorials/libguac-client-ball/.gitignore        |  47 +
 tutorials/libguac-client-ball/Makefile.am       |  13 +
 tutorials/libguac-client-ball/configure.ac      |  19 +
 tutorials/libguac-client-ball/src/ball.c        | 201 ++++
 tutorials/libguac-client-ball/src/ball.h        |  22 +
 8 files changed, 922 insertions(+), 312 deletions(-)
----------------------------------------------------------------------



[3/4] incubator-guacamole-manual git commit: GUACAMOLE-88: Add completed libguac-client-ball tutorial for reference.

Posted by jm...@apache.org.
GUACAMOLE-88: Add completed libguac-client-ball tutorial for reference.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/commit/6ea64c0a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/tree/6ea64c0a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/diff/6ea64c0a

Branch: refs/heads/master
Commit: 6ea64c0aec5f3e52726b68583e1933048930747e
Parents: dc368ba
Author: Michael Jumper <mj...@apache.org>
Authored: Wed Oct 19 15:12:33 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Wed Oct 19 15:17:43 2016 -0700

----------------------------------------------------------------------
 tutorials/libguac-client-ball/.gitignore   |  47 ++++++
 tutorials/libguac-client-ball/Makefile.am  |  13 ++
 tutorials/libguac-client-ball/configure.ac |  19 +++
 tutorials/libguac-client-ball/src/ball.c   | 201 ++++++++++++++++++++++++
 tutorials/libguac-client-ball/src/ball.h   |  22 +++
 5 files changed, 302 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/6ea64c0a/tutorials/libguac-client-ball/.gitignore
----------------------------------------------------------------------
diff --git a/tutorials/libguac-client-ball/.gitignore b/tutorials/libguac-client-ball/.gitignore
new file mode 100644
index 0000000..aeb5561
--- /dev/null
+++ b/tutorials/libguac-client-ball/.gitignore
@@ -0,0 +1,47 @@
+
+# Object code
+*.o
+*.so
+*.lo
+*.la
+
+# gcov files
+*.gcda
+*.gcov
+*.gcno
+
+# Backup files
+*~
+
+# Release files
+*.tar.gz
+
+# Files currently being edited by vim or vi
+*.swp
+
+# automake/autoconf
+.deps/
+.dirstamp
+.libs/
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+m4/*
+!README
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+test-driver
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/6ea64c0a/tutorials/libguac-client-ball/Makefile.am
----------------------------------------------------------------------
diff --git a/tutorials/libguac-client-ball/Makefile.am b/tutorials/libguac-client-ball/Makefile.am
new file mode 100644
index 0000000..5c5eea7
--- /dev/null
+++ b/tutorials/libguac-client-ball/Makefile.am
@@ -0,0 +1,13 @@
+AUTOMAKE_OPTIONS = foreign
+
+ACLOCAL_AMFLAGS = -I m4
+AM_CFLAGS = -Werror -Wall -pedantic
+
+lib_LTLIBRARIES = libguac-client-ball.la
+
+# All source files of libguac-client-ball
+noinst_HEADERS = src/ball.h
+libguac_client_ball_la_SOURCES = src/ball.c
+
+# libtool versioning information
+libguac_client_ball_la_LDFLAGS = -version-info 0:0:0

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/6ea64c0a/tutorials/libguac-client-ball/configure.ac
----------------------------------------------------------------------
diff --git a/tutorials/libguac-client-ball/configure.ac b/tutorials/libguac-client-ball/configure.ac
new file mode 100644
index 0000000..df49a1a
--- /dev/null
+++ b/tutorials/libguac-client-ball/configure.ac
@@ -0,0 +1,19 @@
+# Project information
+AC_PREREQ([2.61])
+AC_INIT([libguac-client-ball], [0.1.0])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+AM_SILENT_RULES([yes])
+
+AC_CONFIG_MACRO_DIRS([m4])
+
+# Check for required build tools
+AC_PROG_CC
+AC_PROG_LIBTOOL
+
+# Check for libguac (http://guac-dev.org/)
+AC_CHECK_LIB([guac], [guac_client_stream_png],,
+      AC_MSG_ERROR("libguac is required for communication via "
+                   "the Guacamole protocol"))
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/6ea64c0a/tutorials/libguac-client-ball/src/ball.c
----------------------------------------------------------------------
diff --git a/tutorials/libguac-client-ball/src/ball.c b/tutorials/libguac-client-ball/src/ball.c
new file mode 100644
index 0000000..2dcc966
--- /dev/null
+++ b/tutorials/libguac-client-ball/src/ball.c
@@ -0,0 +1,201 @@
+#include "ball.h"
+
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/timestamp.h>
+#include <guacamole/user.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+
+/* Client plugin arguments (empty) */
+const char* TUTORIAL_ARGS[] = { NULL };
+
+void* ball_render_thread(void* arg) {
+
+    /* Get data */
+    guac_client* client = (guac_client*) arg;
+    ball_client_data* data = (ball_client_data*) client->data;
+
+    /* Init time of last frame to current time */
+    guac_timestamp last_frame = guac_timestamp_current();
+
+    /* Update ball position as long as client is running */
+    while (client->state == GUAC_CLIENT_RUNNING) {
+
+        /* Default to 30ms frames */
+        int frame_duration = 30;
+
+        /* Lengthen frame duration if client is lagging */
+        int processing_lag = guac_client_get_processing_lag(client);
+        if (processing_lag > frame_duration)
+            frame_duration = processing_lag;
+
+        /* Sleep for duration of frame, then get timestamp */
+        usleep(frame_duration * 1000);
+        guac_timestamp current = guac_timestamp_current();
+
+        /* Calculate change in time */
+        int delta_t = current - last_frame;
+
+        /* Update position */
+        data->ball_x += data->ball_velocity_x * delta_t / 1000;
+        data->ball_y += data->ball_velocity_y * delta_t / 1000;
+
+        /* Bounce if necessary */
+        if (data->ball_x < 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 < 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);
+
+        /* Update timestamp */
+        last_frame = current;
+
+    }
+
+    return NULL;
+
+}
+
+int ball_join_handler(guac_user* user, int argc, char** argv) {
+
+    /* Get client associated with user */
+    guac_client* client = user->client;
+
+    /* Get ball layer from client data */
+    ball_client_data* data = (ball_client_data*) client->data;
+    guac_layer* ball = data->ball;
+
+    /* Get user-specific socket */
+    guac_socket* socket = user->socket;
+
+    /* Send the display size */
+    guac_protocol_send_size(socket, GUAC_DEFAULT_LAYER, 1024, 768);
+
+    /* Create background tile */
+    guac_layer* texture = guac_client_alloc_buffer(client);
+
+    guac_protocol_send_rect(socket, texture, 0, 0, 64, 64);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
+            0x88, 0x88, 0x88, 0xFF);
+
+    guac_protocol_send_rect(socket, texture, 0, 0, 32, 32);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
+            0xDD, 0xDD, 0xDD, 0xFF);
+
+    guac_protocol_send_rect(socket, texture, 32, 32, 32, 32);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
+            0xDD, 0xDD, 0xDD, 0xFF);
+
+    /* Fill with texture */
+    guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER,
+            0, 0, 1024, 768);
+
+    guac_protocol_send_lfill(socket,
+            GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
+            texture);
+
+    /* Set up ball layer */
+    guac_protocol_send_size(socket, ball, 128, 128);
+
+    /* Fill with solid color */
+    guac_protocol_send_arc(socket, data->ball,
+            64, 64, 62, 0, 6.28, 0);
+
+    guac_protocol_send_close(socket, data->ball);
+
+    guac_protocol_send_cstroke(socket,
+            GUAC_COMP_OVER, data->ball,
+            GUAC_LINE_CAP_ROUND, GUAC_LINE_JOIN_ROUND, 4,
+            0x00, 0x00, 0x00, 0xFF);
+
+    guac_protocol_send_cfill(socket,
+            GUAC_COMP_OVER, data->ball,
+            0x00, 0x80, 0x80, 0x80);
+
+    /* Free texture (no longer needed) */
+    guac_client_free_buffer(client, texture);
+
+    /* Mark end-of-frame */
+    guac_protocol_send_sync(socket, client->last_sent_timestamp);
+
+    /* Flush buffer */
+    guac_socket_flush(socket);
+
+    /* User successfully initialized */
+    return 0;
+
+}
+
+int ball_free_handler(guac_client* client) {
+
+    ball_client_data* data = (ball_client_data*) client->data;
+
+    /* Wait for render thread to terminate */
+    pthread_join(data->render_thread, NULL);
+
+    /* Free client-level ball layer */
+    guac_client_free_layer(client, data->ball);
+
+    /* Free client-specific data */
+    free(data);
+
+    /* Data successfully freed */
+    return 0;
+
+}
+
+int guac_client_init(guac_client* client) {
+
+    /* 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);
+
+    /* Start ball at upper left */
+    data->ball_x = 0;
+    data->ball_y = 0;
+
+    /* Move at a reasonable pace to the lower right */
+    data->ball_velocity_x = 200; /* pixels per second */
+    data->ball_velocity_y = 200; /* pixels per second */
+
+    /* Start render thread */
+    pthread_create(&data->render_thread, NULL, ball_render_thread, client);
+
+    /* This example does not implement any arguments */
+    client->args = TUTORIAL_ARGS;
+
+    /* Client-level handlers */
+    client->join_handler = ball_join_handler;
+    client->free_handler = ball_free_handler;
+
+    return 0;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-manual/blob/6ea64c0a/tutorials/libguac-client-ball/src/ball.h
----------------------------------------------------------------------
diff --git a/tutorials/libguac-client-ball/src/ball.h b/tutorials/libguac-client-ball/src/ball.h
new file mode 100644
index 0000000..7a7d5e4
--- /dev/null
+++ b/tutorials/libguac-client-ball/src/ball.h
@@ -0,0 +1,22 @@
+#ifndef BALL_CLIENT_H
+#define BALL_CLIENT_H
+
+#include <guacamole/layer.h>
+
+#include <pthread.h>
+
+typedef struct ball_client_data {
+
+    guac_layer* ball;
+
+    int ball_x;
+    int ball_y;
+
+    int ball_velocity_x;
+    int ball_velocity_y;
+
+    pthread_t render_thread;
+
+} ball_client_data;
+
+#endif