| <?xml version="1.0" encoding="UTF-8"?> |
| <chapter xml:id="guacamole-common-js" xmlns="http://docbook.org/ns/docbook" |
| version="5.0" xml:lang="en" xmlns:xl="http://www.w3.org/1999/xlink" |
| xmlns:xi="http://www.w3.org/2001/XInclude"> |
| <title>guacamole-common-js</title> |
| <indexterm> |
| <primary>API</primary> |
| <secondary>JavaScript</secondary> |
| </indexterm> |
| <indexterm> |
| <primary>guacamole-common-js</primary> |
| </indexterm> |
| <para>The Guacamole project provides a JavaScript API for interfacing with |
| other components that conform to the design of Guacamole, such as |
| projects using libguac or guacamole-common. This API is called |
| guacamole-common-js.</para> |
| <para>guacamole-common-js provides a JavaScript implementation of a |
| Guacamole client, as well as tunneling mechanisms for getting protocol |
| data out of JavaScript and into guacd or the server side of a web |
| application.</para> |
| <para>For convenience, it also provides mouse and keyboard abstraction objects that translate |
| JavaScript mouse, touch, and keyboard events into consistent data that Guacamole can more |
| easily digest. The extendable on-screen keyboard that was developed for the Guacamole web |
| application is also included.</para> |
| <section xml:id="guacamole-client"> |
| <title>Guacamole client</title> |
| <para>The main benefit to using the JavaScript API is the full Guacamole |
| client implementation, which implements all Guacamole instructions, |
| and makes use of the tunnel implementations provided by both the |
| JavaScript and Java APIs.</para> |
| <para>Using the Guacamole client is straightforward. The client, like |
| all other objects within the JavaScript API, is within the |
| <code>Guacamole</code> namespace. It is instantiated given an |
| existing, unconnected tunnel:</para> |
| <informalexample> |
| <programlisting>var client = new Guacamole.Client(tunnel);</programlisting> |
| </informalexample> |
| <para>Once you have the client, it won't immediately appear within the |
| DOM. You need to add its display element manually:</para> |
| <informalexample> |
| <programlisting>document.body.appendChild(client.getDisplay());</programlisting> |
| </informalexample> |
| <para>At this point, the client will be visible, rendering all updates |
| as soon as they are received through the tunnel.</para> |
| <informalexample> |
| <programlisting>client.connect();</programlisting> |
| </informalexample> |
| <para>It is possible to pass arbitrary data to the tunnel during |
| connection which can be used for authentication or for choosing a |
| particular connection. When the <methodname>connect()</methodname> |
| function of the Guacamole client is called, it in turn calls the |
| <methodname>connect()</methodname> function of the tunnel |
| originally given to the client, establishing a connection.</para> |
| <important> |
| <para>When creating the <classname>Guacamole.Client</classname>, the |
| tunnel used must not already be connected. The |
| <classname>Guacamole.Client</classname> will call the |
| <methodname>connect()</methodname> function for you when its |
| own <methodname>connect()</methodname> function is invoked. If |
| the tunnel is already connected when it is given to the |
| <classname>Guacamole.Client</classname>, connection may not |
| work at all.</para> |
| </important> |
| <para>In general, all instructions available within the Guacamole |
| protocol are automatically handled by the Guacamole client, |
| including instructions related to audio and video. The only |
| instructions which you must handle yourself are "name" (used to name |
| the connection), "clipboard" (used to update clipboard data on the |
| client side), and "error" (used when something goes wrong |
| server-side). Each of these instructions has a corresponding event |
| handler; you need only supply functions to handle these events. If |
| any of these event handlers are left unset, the corresponding |
| instructions are simply ignored.</para> |
| </section> |
| <section xml:id="http-tunnel"> |
| <title>HTTP tunnel</title> |
| <para>Both the Java and JavaScript API implement corresponding ends of |
| an HTTP tunnel, based on |
| <classname>XMLHttpRequest</classname>.</para> |
| <para>The tunnel is a true stream - there is no polling. An initial |
| request is made from the JavaScript side, and this request is |
| handled on the Java side. While this request is open, data is |
| streamed along the connection, and instructions within this stream |
| are handled as soon as they are received by the client.</para> |
| <para>While data is being streamed along this existing connection, a |
| second connection attempt is made. Data continues to be streamed |
| along the original connection until the server receives and handles |
| the second request, at which point the original connection closes |
| and the stream is transferred to the new connection.</para> |
| <para>This process repeats, alternating between active streams, thus |
| creating an unbroken sequence of instructions, while also allowing |
| JavaScript to free any memory used by the previously active |
| connection.</para> |
| <para>The tunnel is created by supplying the relative URL to the |
| server-side tunnel servlet:</para> |
| <informalexample> |
| <programlisting>var tunnel = new Guacamole.Tunnel("tunnel");</programlisting> |
| </informalexample> |
| <para>Once created, the tunnel can be passed to a |
| <classname>Guacamole.Client</classname> for use in a Guacamole |
| connection.</para> |
| <para>The tunnel actually takes care of the Guacamole protocol parsing |
| on behalf of the client, triggering "oninstruction" events for every |
| instruction received, splitting each element into elements of an |
| array so that the client doesn't have to.</para> |
| </section> |
| <section xml:id="input-abstraction"> |
| <title>Input abstraction</title> |
| <para>Browsers can be rather finicky when it comes to keyboard and mouse |
| input, not to mention touch events. There is little agreement on |
| which keyboard events get fired when, and what detail about the |
| event is made available to JavaScript. Touch and mouse events can |
| also cause confusion, as most browsers will generate |
| <emphasis>both</emphasis> events when the user touches the |
| screen (for compatibility with JavaScript code that only handles |
| mouse events), making it more difficult for applications to support |
| both mouse and touch independently.</para> |
| <para>The Guacamole JavaScript API abstracts mouse, keyboard, and touch |
| interaction, providing several helper objects which act as an |
| abstract interface between you and the browser events.</para> |
| <section xml:id="guacamole-mouse"> |
| <title>Mouse</title> |
| <para>Mouse event abstraction is provided by the |
| <classname>Guacamole.Mouse</classname> object. Given an |
| arbitrary DOM element, <classname>Guacamole.Mouse</classname> |
| triggers <property>onmousedown</property>, |
| <property>onmousemove</property>, and |
| <property>onmouseup</property> events which are consistent |
| across browsers. This object only response. to true mouse |
| events. Mouse events which are actually the result of touch |
| events are ignored.</para> |
| <informalexample> |
| <programlisting>var element = document.getElementById("some-arbitrary-id"); |
| var mouse = new Guacamole.Mouse(element); |
| |
| mouse.onmousedown = |
| mouse.onmousemove = |
| mouse.onmouseup = function(state) { |
| |
| // Do something with the mouse state received ... |
| |
| };</programlisting> |
| </informalexample> |
| <para>The handles of each event are given an instance of |
| <classname>Guacamole.Mouse.State</classname> which |
| represents the current state of the mouse, containing the state |
| of each button (including the scroll wheel) as well as the X and |
| Y coordinates of the pointer in pixels.</para> |
| </section> |
| <section xml:id="guacamole-touch"> |
| <title>Touch</title> |
| <para>Touch event abstraction is provided by either |
| <classname>Guacamole.Touchpad</classname> (emulates a |
| touchpad to generate artificial mouse events) or |
| <classname>Guacamole.Touchscreen</classname> (emulates a |
| touchscreen, again generating artificial mouse events). |
| Guacamole uses the touchpad emulation, as this provides the most |
| flexibility and mouse-like features, including scrollwheel and |
| clicking with different buttons, but your preferences may |
| differ.</para> |
| <informalexample> |
| <programlisting>var element = document.getElementById("some-arbitrary-id"); |
| var touch = new Guacamole.Touchpad(element); // or Guacamole.Touchscreen |
| |
| touch.onmousedown = |
| touch.onmousemove = |
| touch.onmouseup = function(state) { |
| |
| // Do something with the mouse state received ... |
| |
| };</programlisting> |
| </informalexample> |
| <para>Note that even though these objects are touch-specific, they |
| still provide mouse events. The state object given to the event |
| handlers of each event is still an instance of |
| <classname>Guacamole.Mouse.State</classname>.</para> |
| <para>Ultimately, you could assign the same event handler to all the |
| events of both an instance of |
| <classname>Guacamole.Mouse</classname> as well as |
| <classname>Guacamole.Touchscreen</classname> or |
| <classname>Guacamole.Touchpad</classname>, and you would |
| magically gain mouse and touch support. This support, being |
| driven by the needs of remote desktop, is naturally geared |
| around the mouse and providing a reasonable means of interacting |
| with it. For an actual mouse, events are translated simply and |
| literally, while touch events go through additional emulation |
| and heuristics. From the perspective of the user and the code, |
| this is all transparent.</para> |
| </section> |
| <section xml:id="guacamole-keyboard"> |
| <title>Keyboard</title> |
| <para>Keyboard events in Guacamole are abstracted with the |
| <classname>Guacamole.Keyboard</classname> object as only |
| keyup and keydown events; there is no keypress like there is in |
| JavaScript. Further, all the craziness of keycodes vs. scancodes |
| vs. key identifiers normally present across browsers is |
| abstracted away. All your event handlers will see is an X11 |
| keysym, which represent every key unambiguously. Conveniently, |
| X11 keysyms are also what the Guacamole protocol requires, so if |
| you want to use <classname>Guacamole.Keyboard</classname> to |
| drive key events sent over the Guacamole protocol, everything |
| can be connected directly.</para> |
| <para>Just like the other input abstraction objects, |
| <classname>Guacamole.Keyboard</classname> requires a DOM |
| element as an event target. Only key events directed at this |
| element will be handled.</para> |
| <informalexample> |
| <programlisting>var keyboard = new Guacamole.Keyboard(document); |
| |
| keyboard.onkeydown = function(keysym) { |
| // Do something ... |
| }; |
| |
| keyboard.onkeyup = function(keysym) { |
| // Do something ... |
| };</programlisting> |
| </informalexample> |
| <para>In this case, we are using <classname>document</classname> as |
| the event target, thus receiving all key events while the |
| browser window (or tab) has focus.</para> |
| </section> |
| </section> |
| <section xml:id="on-screen-keyboard"> |
| <title>On-screen keyboard</title> |
| <para>The Guacamole JavaScript API also provides an extendable on-screen |
| keyboard, <classname>Guacamole.OnScreenKeyboard</classname>, which |
| requires the URL of an XML file describing the keyboard layout. The |
| on-screen keyboard object provides no hard-coded layout information; |
| the keyboard layout is described entirely within the XML layout |
| file.</para> |
| <section xml:id="keyboard-layouts"> |
| <title>Keyboard layouts</title> |
| <para>The keyboard layout XML included in the Guacamole web |
| application would be a good place to start regarding how these |
| layout files are written, but in general, the keyboard is simply |
| a set of rows or columns, denoted with <code><row></code> and |
| <code><column></code> tags respectively, where each can |
| be nested within the other as desired.</para> |
| <para>Each key is represented with a <code><key></code> tag, but |
| this is not what the user sees, nor what generates the key |
| event. Each key contains any number of <code><cap></code> |
| tags, which represent the visible part of the key. The cap |
| describes which X11 keysym will be sent when the key is pressed. |
| Each cap can be associated with any combination of arbitrary |
| modifier flags which dictate when that cap is active.</para> |
| <para>For example:</para> |
| <informalexample> |
| <programlisting><keyboard lang="en_US" layout="example" size="5"> |
| <row> |
| <key size="4"> |
| <cap modifier="shift" keysym="0xFFE1">Shift</cap> |
| </key> |
| <key> |
| <cap>a</cap> |
| <cap if="shift">A</cap> |
| </key> |
| </row> |
| </keyboard></programlisting> |
| </informalexample> |
| <para>Here we have a very simple keyboard which defines only two |
| keys: "shift" (a modifier) and the letter "a". When "shift" is |
| pressed, it sets the "shift" modifier, affecting other keys in |
| the keyboard. The "a" key has two caps: one lowercase (the |
| default) and one uppercase (which requires the shift modifier to |
| be active).</para> |
| <para>Notice that the shift key needed the keysym explicitly |
| specified, while the "a" key did not. This is because the |
| on-screen keyboard will automatically derive the correct keysym |
| from the text of the key cap if the text contains only a single |
| character.</para> |
| </section> |
| <section xml:id="displaying-osk"> |
| <title>Displaying the keyboard</title> |
| <para>Once you have a keyboard layout available, adding an on-screen |
| keyboard to your application is simple:</para> |
| <informalexample> |
| <programlisting>// Add keyboard to body |
| var keyboard = new Guacamole.OnScreenKeyboard("path/to/layout.xml"); |
| document.body.appendChild(keyboard.getElement()); |
| |
| // Set size of keyboard to 100 pixels |
| keyboard.resize(100);</programlisting> |
| </informalexample> |
| <para>Here, we have explicitly specified the width of the keyboard |
| as 100 pixels. Normally, you would determine this by inspecting |
| the width of the containing component, or by deciding on a |
| reasonable width beforehand. Once the width is given, the height |
| of the keyboard is determined based on the arrangement of each |
| row.</para> |
| </section> |
| <section xml:id="styling-the-keyboard"> |
| <title>Styling the keyboard</title> |
| <para>While the <classname>Guacamole.OnScreenKeyboard</classname> |
| object will handle most of the layout, you will still need to |
| style everything yourself with CSS to get the elements to render |
| properly and the keys to change state when clicked or activated. |
| It defines several CSS classes, which you will need to manually |
| style to get things looking as desired:</para> |
| <variablelist> |
| <varlistentry> |
| <term><classname>guac-keyboard</classname></term> |
| <listitem> |
| <para>This class is assigned to the root element |
| containing the entire keyboard, returned by |
| <methodname>getElement()</methodname>,</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-row</classname></term> |
| <listitem> |
| <para>Assigned to the <code>div</code> elements which |
| contain each row.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-column</classname></term> |
| <listitem> |
| <para>Assigned to the <code>div</code> elements which |
| contain each column.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-gap</classname></term> |
| <listitem> |
| <para>Assigned to any <code>div</code> elements created |
| as a result of <code><gap></code> tags in the |
| keyboard layout. <code><gap></code> tags are |
| intended to behave as keys with no visible styling |
| or caps.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-key-container</classname></term> |
| <listitem> |
| <para>Assigned to the <code>div</code> element which |
| contains a key, and provides that key with its |
| required dimensions. It is this element that will be |
| scaled relative to the size specified in the layout |
| XML and the size given to the <code>resize()</code> |
| function.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-key</classname></term> |
| <listitem> |
| <para>Assigned to the <code>div</code> element which |
| represents the actual key, not the cap. This element |
| will not directly contain text, but it will contain |
| all caps that this key can have. With clever CSS |
| rules, you can take advantage of this and cause |
| inactive caps to appear on the key in a corner (for |
| example), or hide them entirely.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-cap</classname></term> |
| <listitem> |
| <para>Assigned to the <code>div</code> element |
| representing a key cap. Each cap is a child of its |
| corresponding key, and it is up to the author of the |
| CSS rules to hide or show or reposition each cap |
| appropriately. Each cap will contain the display |
| text defined within the <code><cap></code> |
| element in the layout XML.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-requires-<replaceable>MODIFIER</replaceable></classname></term> |
| <listitem> |
| <para>Added to the cap element when that cap requires a |
| specific modifier.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-uses-<replaceable>MODIFIER</replaceable></classname></term> |
| <listitem> |
| <para>Added to the key element when any cap contained |
| within it requires a specific modifier.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-modifier-<replaceable>MODIFIER</replaceable></classname></term> |
| <listitem> |
| <para>Added to and removed from the root keyboard |
| element when a modifier key is activated or |
| deactivated respectively.</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><classname>guac-keyboard-pressed</classname></term> |
| <listitem> |
| <para>Added to and removed from any key element as it is |
| pressed and released respectively.</para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| <important> |
| <para>The CSS rules required for the on-screen keyboard to work |
| as expected can be quite complex. Looking over the CSS rules |
| used by the on-screen keyboard in the Guacamole web |
| application would be a good place to start to see how the |
| appearance of each key can be driven through the simple |
| class changes described above.</para> |
| <para>Inspecting the elements of an active on-screen keyboard |
| within the Guacamole web application with the developer |
| tools of your favorite browser is also a good idea.</para> |
| </important> |
| </section> |
| <section xml:id="osk-event-handling"> |
| <title>Handling key events</title> |
| <para>Key events generated by the on-screen keyboard are identical |
| to those of <classname>Guacamole.Keyboard</classname> in that |
| they consist only of a single X11 keysym. Only keyup and keydown |
| events exist, as before; there is no keypress event.</para> |
| <informalexample> |
| <programlisting>// Assuming we have an instance of Guacamole.OnScreenKeyboard already |
| // called "keyboard" |
| |
| keyboard.onkeydown = function(keysym) { |
| // Do something ... |
| }; |
| |
| keyboard.onkeyup = function(keysym) { |
| // Do something ... |
| };</programlisting> |
| </informalexample> |
| </section> |
| </section> |
| </chapter> |