[Kimchi-devel] [PATCH V3] Add the front end support for SPICE

zhoumeina zhoumein at linux.vnet.ibm.com
Tue Jan 7 03:33:48 UTC 2014


On 01/07/2014 03:34 AM, Aline Manera wrote:
> On 01/02/2014 07:23 AM, zhoumeina wrote:
>> v2-v3 Add related make files
>> v1-v2 add spice.html.tmpl
>> This patch add the front end support of spice.
>>
>> 1 If there were a spice vm in host, show a "spice" button
>> but not "vnc" in guest page.
>>
>> 2 click "spice" we can show a screen just like the demo
>> http://www.spice-space.org/spice-html5/spice.html
>>
>> Signed-off-by: zhoumeina <zhoumein at linux.vnet.ibm.com>
>> ---
>>   configure.ac                   |    1 +
>>   contrib/kimchi.spec.fedora.in  |    1 +
>>   contrib/kimchi.spec.suse.in    |    1 +
>>   ui/css/spice/spice.css         |  115 ++++
>>   ui/js/Makefile.am              |    2 +-
>>   ui/js/spice/Makefile.am        |   25 +
>>   ui/js/spice/atKeynames.js      |  183 ++++++
>>   ui/js/spice/bitmap.js          |   51 ++
>>   ui/js/spice/cursor.js          |   92 +++
>>   ui/js/spice/display.js         |  806 ++++++++++++++++++++++++
>>   ui/js/spice/enums.js           |  282 +++++++++
>>   ui/js/spice/inputs.js          |  251 ++++++++
>>   ui/js/spice/jsbn.js            |  589 ++++++++++++++++++
>>   ui/js/spice/lz.js              |  166 +++++
>>   ui/js/spice/main.js            |  176 ++++++
>>   ui/js/spice/png.js             |  256 ++++++++
>>   ui/js/spice/prng4.js           |   79 +++
>>   ui/js/spice/quic.js            | 1335 
>> ++++++++++++++++++++++++++++++++++++++++
>>   ui/js/spice/rng.js             |  102 +++
>>   ui/js/spice/rsa.js             |  146 +++++
>>   ui/js/spice/sha1.js            |  346 +++++++++++
>>   ui/js/spice/spiceconn.js       |  447 ++++++++++++++
>>   ui/js/spice/spicedataview.js   |   96 +++
>>   ui/js/spice/spicemsg.js        |  883 ++++++++++++++++++++++++++
>>   ui/js/spice/spicetype.js       |  480 +++++++++++++++
>>   ui/js/spice/ticket.js          |  250 ++++++++
>>   ui/js/spice/utils.js           |  261 ++++++++
>>   ui/js/spice/wire.js            |  123 ++++
>>   ui/js/src/kimchi.api.js        |   21 +
>>   ui/js/src/kimchi.guest_main.js |   26 +-
>>   ui/pages/guest.html.tmpl       |    1 +
>>   ui/pages/spice.html.tmpl       |  138 +++++
>>   32 files changed, 7725 insertions(+), 6 deletions(-)
>>   create mode 100644 ui/css/spice/spice.css
>>   create mode 100644 ui/js/spice/Makefile.am
>>   create mode 100644 ui/js/spice/atKeynames.js
>>   create mode 100644 ui/js/spice/bitmap.js
>>   create mode 100644 ui/js/spice/cursor.js
>>   create mode 100644 ui/js/spice/display.js
>>   create mode 100644 ui/js/spice/enums.js
>>   create mode 100644 ui/js/spice/inputs.js
>>   create mode 100644 ui/js/spice/jsbn.js
>>   create mode 100644 ui/js/spice/lz.js
>>   create mode 100644 ui/js/spice/main.js
>>   create mode 100644 ui/js/spice/png.js
>>   create mode 100644 ui/js/spice/prng4.js
>>   create mode 100644 ui/js/spice/quic.js
>>   create mode 100644 ui/js/spice/rng.js
>>   create mode 100644 ui/js/spice/rsa.js
>>   create mode 100644 ui/js/spice/sha1.js
>>   create mode 100644 ui/js/spice/spiceconn.js
>>   create mode 100644 ui/js/spice/spicedataview.js
>>   create mode 100644 ui/js/spice/spicemsg.js
>>   create mode 100644 ui/js/spice/spicetype.js
>>   create mode 100644 ui/js/spice/ticket.js
>>   create mode 100644 ui/js/spice/utils.js
>>   create mode 100644 ui/js/spice/wire.js
>>   create mode 100644 ui/pages/spice.html.tmpl
>>
>> diff --git a/configure.ac b/configure.ac
>> index 0d04d51..68855f8 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -66,6 +66,7 @@ AC_CONFIG_FILES([
>>       ui/images/theme-default/Makefile
>>       ui/js/Makefile
>>       ui/js/novnc/Makefile
>> +    ui/js/spice/Makefile
>>       ui/js/novnc/web-socket-js/Makefile
>>       ui/libs/Makefile
>>       ui/libs/themes/Makefile
>> diff --git a/contrib/kimchi.spec.fedora.in 
>> b/contrib/kimchi.spec.fedora.in
>> index 577516c..9f92044 100644
>> --- a/contrib/kimchi.spec.fedora.in
>> +++ b/contrib/kimchi.spec.fedora.in
>> @@ -133,6 +133,7 @@ rm -rf $RPM_BUILD_ROOT
>>   %{_datadir}/kimchi/ui/js/jquery.min.js
>>   %{_datadir}/kimchi/ui/js/modernizr.custom.2.6.2.min.js
>>   %{_datadir}/kimchi/ui/js/novnc/*.js
>> +%{_datadir}/kimchi/ui/js/spice/*.js
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/WebSocketMain.swf
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/swfobject.js
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/web_socket.js
>> diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in
>> index 12d02ec..369fac2 100644
>> --- a/contrib/kimchi.spec.suse.in
>> +++ b/contrib/kimchi.spec.suse.in
>> @@ -80,6 +80,7 @@ rm -rf $RPM_BUILD_ROOT
>>   %{_datadir}/kimchi/ui/js/jquery.min.js
>>   %{_datadir}/kimchi/ui/js/modernizr.custom.2.6.2.min.js
>>   %{_datadir}/kimchi/ui/js/novnc/*.js
>> +%{_datadir}/kimchi/ui/js/spice/*.js
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/WebSocketMain.swf
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/swfobject.js
>>   %{_datadir}/kimchi/ui/js/novnc/web-socket-js/web_socket.js
>> diff --git a/ui/css/spice/spice.css b/ui/css/spice/spice.css
>> new file mode 100644
>> index 0000000..16b19b6
>> --- /dev/null
>> +++ b/ui/css/spice/spice.css
>> @@ -0,0 +1,115 @@
>> +body
>> +{
>> +  background-color: #999999;
>> +  color: #000000; margin: 0; padding: 0;
>> +  font-family: "Lucida Grande", "Lucida Sans Unicode", "Helvetica 
>> Neue", Helvetica, Arial, Verdana, sans-serif;
>> +  font-size: 12pt;
>> +  line-height: 1.5em;
>> +}
>> +
>> +* { margin: 0; }
>> +
>> +#login
>> +{
>> +    width: 790px;
>> +    margin-top: 20px;
>> +    margin-left: auto;
>> +    margin-right: auto;
>> +    padding: 2px 10px 2px 20px;
>> +    border: 1px solid #999999;
>> +    background: -webkit-gradient(linear, left top, left bottom, 
>> from(#fff), to(#24414e));
>> +    background: -moz-linear-gradient(top,  #fff,  #24414e);
>> +    background-color: #24414e;
>> +    -moz-border-radius: 10px;
>> +    -webkit-border-radius: 10px;
>> +    border-radius: 10px;
>> +}
>> +#login span.logo
>> +{
>> +    display: inline-block;
>> +    margin-right: 5px;
>> +    padding-right: 10px;
>> +    border-right: 1px solid #999999;
>> +    font-size: 20px;
>> +    font-weight: bolder;
>> +    text-shadow: #efefef 1px 1px 0px;
>> +}
>> +#login label { color: #ffffff; text-shadow: 1px 1px 0px rgba(175, 
>> 210, 220, 0.8); }
>> +#login input
>> +{
>> +    padding: 5px;
>> +    background-color: #fAfAfA;
>> +    border: 1px inset #999999;
>> +    outline: none;
>> +    -moz-border-radius: 3px; -webkit-border-radius: 3px; 
>> border-radius: 3px;
>> +    box-sizing: border-box;
>> +    -moz-box-sizing: border-box;
>> +    -webkit-box-sizing: border-box;
>> +}
>> +#login input#host { width: 200px; }
>> +#login input#port { width: 75px; }
>> +#login input#password { width: 100px; }
>> +#login button
>> +{
>> +    padding: 5px 10px 5px 10px;
>> +    margin-left: 5px;
>> +    text-shadow: #efefef 1px 1px 0px;
>> +    border: 1px outset #999999;
>> +    cursor: pointer;
>> +    -moz-border-radius: 5px; -webkit-border-radius: 5px; 
>> border-radius: 5px;
>> +}
>> +#login button:hover
>> +{
>> +    background-color: #666666;
>> +    color: #ffffff;
>> +}
>> +
>> +#spice-area
>> +{
>> +    text-align: center;
>> +}
>> +.spice-screen
>> +{
>> +    display: inline-block;
>> +    padding: 10px;
>> +    min-width: 800px;
>> +    min-height: 600px;
>> +    border: solid #222222 1px;
>> +    background-color: #333333;
>> +    -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +    -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +    box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +    -moz-border-radius: 10px;
>> +    -webkit-border-radius: 10px;
>> +    border-radius: 10px;
>> +}
>> +.spice-message {
>> +    width: 700px;
>> +    height: 50px;
>> +    overflow: auto;
>> +    margin-top: 5px;
>> +    margin-left: auto;
>> +    margin-right: auto;
>> +    padding: 10px;
>> +    background-color: #efefef;
>> +    border: solid #c3c3c3 2px;
>> +    font-size: 8pt;
>> +    line-height: 1.1em;
>> +    font-family: 'Andale Mono', monospace;
>> +    -moz-border-radius: 10px;
>> +    -webkit-border-radius: 10px;
>> +    border-radius: 10px;
>> +    -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +    -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +    box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
>> +}
>> +.spice-message p {
>> +    margin-bottom: 0em;
>> +    margin-top: 0em;
>> +}
>> +.spice-message-warning {
>> +    color: orange;
>> +}
>> +.spice-message-error {
>> +    color: red;
>> +}
>> diff --git a/ui/js/Makefile.am b/ui/js/Makefile.am
>> index 337e369..15a4a5a 100644
>> --- a/ui/js/Makefile.am
>> +++ b/ui/js/Makefile.am
>> @@ -18,7 +18,7 @@
>>   # See the License for the specific language governing permissions and
>>   # limitations under the License.
>>
>> -SUBDIRS = novnc
>> +SUBDIRS = novnc spice
>>
>>   EXTRA_DIST = src
>>
>> diff --git a/ui/js/spice/Makefile.am b/ui/js/spice/Makefile.am
>> new file mode 100644
>> index 0000000..d326e54
>> --- /dev/null
>> +++ b/ui/js/spice/Makefile.am
>> @@ -0,0 +1,25 @@
>> +#
>> +# Kimchi
>> +#
>> +# Copyright IBM, Corp. 2013
>> +#
>> +# Authors:
>> +# zhou meina <zhoumein at linux.vnet.ibm.com>
>> +#
>> +# Licensed under the Apache License, Version 2.0 (the "License");
>> +# you may not use this file except in compliance with the License.
>> +# You may obtain a copy of the License at
>> +#
>> +#     http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
>> implied.
>> +# See the License for the specific language governing permissions and
>> +# limitations under the License.
>> +
>> +jsdir = $(datadir)/kimchi/ui/js/spice
>> +
>> +dist_js_DATA = \
>> +    *.js
>> +    $(NULL)
>> diff --git a/ui/js/spice/atKeynames.js b/ui/js/spice/atKeynames.js
>> new file mode 100644
>> index 0000000..e1e27fd
>> --- /dev/null
>> +++ b/ui/js/spice/atKeynames.js
>> @@ -0,0 +1,183 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Aric Stewart <aric at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +/*
>> + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
>> + *
>> + * Permission to use, copy, modify, distribute, and sell this 
>> software and its
>> + * documentation for any purpose is hereby granted without fee, 
>> provided that
>> + * the above copyright notice appear in all copies and that both that
>> + * copyright notice and this permission notice appear in supporting
>> + * documentation, and that the name of Thomas Roell not be used in
>> + * advertising or publicity pertaining to distribution of the 
>> software without
>> + * specific, written prior permission.  Thomas Roell makes no 
>> representations
>> + * about the suitability of this software for any purpose.  It is 
>> provided
>> + * "as is" without express or implied warranty.
>> + *
>> + * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
>> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 
>> IN NO
>> + * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
>> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
>> LOSS OF USE,
>> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 
>> OTHER
>> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
>> + * PERFORMANCE OF THIS SOFTWARE.
>> + *
>> + */
>> +/*
>> + * Copyright (c) 1994-2003 by The XFree86 Project, Inc.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining a
>> + * copy of this software and associated documentation files (the 
>> "Software"),
>> + * to deal in the Software without restriction, including without 
>> limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, 
>> sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom 
>> the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be 
>> included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
>> EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
>> MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO 
>> EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, 
>> DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
>> OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
>> USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + *
>> + * Except as contained in this notice, the name of the copyright 
>> holder(s)
>> + * and author(s) shall not be used in advertising or otherwise to 
>> promote
>> + * the sale, use or other dealings in this Software without prior 
>> written
>> + * authorization from the copyright holder(s) and author(s).
>> + */
>> +
>> +/*
>> + * NOTE: The AT/MF keyboards can generate (via the 8042) two (MF: 
>> three)
>> + *       sets of scancodes. Set3 can only be generated by a MF 
>> keyboard.
>> + *       Set2 sends a makecode for keypress, and the same code 
>> prefixed by a
>> + *       F0 for keyrelease. This is a little bit ugly to handle. 
>> Thus we use
>> + *       here for X386 the PC/XT compatible Set1. This set uses 8bit 
>> scancodes.
>> + *       Bit 7 ist set if the key is released. The code E0 switches 
>> to a
>> + *       different meaning to add the new MF cursorkeys, while not 
>> breaking old
>> + *       applications. E1 is another special prefix. Since I assume 
>> that there
>> + *       will be further versions of PC/XT scancode compatible 
>> keyboards, we
>> + *       may be in trouble one day.
>> + *
>> + * IDEA: 1) Use Set2 on AT84 keyboards and translate it to MF Set3.
>> + *       2) Use the keyboards native set and translate it to common 
>> keysyms.
>> + */
>> +
>> +/*
>> + * definition of the AT84/MF101/MF102 Keyboard:
>> + * ============================================================
>> + *       Defined             Key Cap Glyphs       Pressed value
>> + *      Key Name            Main       Also       (hex) (dec)
>> + *      ----------------   ---------- -------    ------ ------
>> + */
>> +
>> +var KEY_Escape      =/* Escape                0x01  */    1
>> +var KEY_1           =/* 1           !         0x02  */    2
>> +var KEY_2           =/* 2           @         0x03  */    3
>> +var KEY_3           =/* 3           #         0x04  */    4
>> +var KEY_4           =/* 4           $         0x05  */    5
>> +var KEY_5           =/* 5           %         0x06  */    6
>> +var KEY_6           =/* 6           ^         0x07  */    7
>> +var KEY_7           =/* 7           &         0x08  */    8
>> +var KEY_8           =/* 8           *         0x09  */    9
>> +var KEY_9           =/* 9           (         0x0a  */   10
>> +var KEY_0           =/* 0           )         0x0b  */   11
>> +var KEY_Minus       =/* - (Minus)   _ (Under) 0x0c  */   12
>> +var KEY_Equal       =/* = (Equal)   +         0x0d  */   13
>> +var KEY_BackSpace   =/* Back Space            0x0e  */   14
>> +var KEY_Tab         =/* Tab                   0x0f  */   15
>> +var KEY_Q           =/* Q                     0x10  */   16
>> +var KEY_W           =/* W                     0x11  */   17
>> +var KEY_E           =/* E                     0x12  */   18
>> +var KEY_R           =/* R                     0x13  */   19
>> +var KEY_T           =/* T                     0x14  */   20
>> +var KEY_Y           =/* Y                     0x15  */   21
>> +var KEY_U           =/* U                     0x16  */   22
>> +var KEY_I           =/* I                     0x17  */   23
>> +var KEY_O           =/* O                     0x18  */   24
>> +var KEY_P           =/* P                     0x19  */   25
>> +var KEY_LBrace      =/* [           {         0x1a  */   26
>> +var KEY_RBrace      =/* ]           }         0x1b  */   27
>> +var KEY_Enter       =/* Enter                 0x1c  */   28
>> +var KEY_LCtrl       =/* Ctrl(left)            0x1d  */   29
>> +var KEY_A           =/* A                     0x1e  */   30
>> +var KEY_S           =/* S                     0x1f  */   31
>> +var KEY_D           =/* D                     0x20  */   32
>> +var KEY_F           =/* F                     0x21  */   33
>> +var KEY_G           =/* G                     0x22  */   34
>> +var KEY_H           =/* H                     0x23  */   35
>> +var KEY_J           =/* J                     0x24  */   36
>> +var KEY_K           =/* K                     0x25  */   37
>> +var KEY_L           =/* L                     0x26  */   38
>> +var KEY_SemiColon   =/* ;(SemiColon) :(Colon) 0x27  */   39
>> +var KEY_Quote       =/* ' (Apostr)  " (Quote) 0x28  */   40
>> +var KEY_Tilde       =/* ` (Accent)  ~ (Tilde) 0x29  */   41
>> +var KEY_ShiftL      =/* Shift(left)           0x2a  */   42
>> +var KEY_BSlash      =/* \(BckSlash) |(VertBar)0x2b  */   43
>> +var KEY_Z           =/* Z                     0x2c  */   44
>> +var KEY_X           =/* X                     0x2d  */   45
>> +var KEY_C           =/* C                     0x2e  */   46
>> +var KEY_V           =/* V                     0x2f  */   47
>> +var KEY_B           =/* B                     0x30  */   48
>> +var KEY_N           =/* N                     0x31  */   49
>> +var KEY_M           =/* M                     0x32  */   50
>> +var KEY_Comma       =/* , (Comma)   < (Less)  0x33  */   51
>> +var KEY_Period      =/* . (Period)  >(Greater)0x34  */   52
>> +var KEY_Slash       =/* / (Slash)   ?         0x35  */   53
>> +var KEY_ShiftR      =/* Shift(right)          0x36  */   54
>> +var KEY_KP_Multiply =/* *                     0x37  */   55
>> +var KEY_Alt         =/* Alt(left)             0x38  */   56
>> +var KEY_Space       =/*   (SpaceBar)          0x39  */   57
>> +var KEY_CapsLock    =/* CapsLock              0x3a  */   58
>> +var KEY_F1          =/* F1                    0x3b  */   59
>> +var KEY_F2          =/* F2                    0x3c  */   60
>> +var KEY_F3          =/* F3                    0x3d  */   61
>> +var KEY_F4          =/* F4                    0x3e  */   62
>> +var KEY_F5          =/* F5                    0x3f  */   63
>> +var KEY_F6          =/* F6                    0x40  */   64
>> +var KEY_F7          =/* F7                    0x41  */   65
>> +var KEY_F8          =/* F8                    0x42  */   66
>> +var KEY_F9          =/* F9                    0x43  */   67
>> +var KEY_F10         =/* F10                   0x44  */   68
>> +var KEY_NumLock     =/* NumLock               0x45  */   69
>> +var KEY_ScrollLock  =/* ScrollLock            0x46  */   70
>> +var KEY_KP_7        =/* 7           Home      0x47  */   71
>> +var KEY_KP_8        =/* 8           Up        0x48  */   72
>> +var KEY_KP_9        =/* 9           PgUp      0x49  */   73
>> +var KEY_KP_Minus    =/* - (Minus)             0x4a  */   74
>> +var KEY_KP_4        =/* 4           Left      0x4b  */   75
>> +var KEY_KP_5        =/* 5                     0x4c  */   76
>> +var KEY_KP_6        =/* 6           Right     0x4d  */   77
>> +var KEY_KP_Plus     =/* + (Plus)              0x4e  */   78
>> +var KEY_KP_1        =/* 1           End       0x4f  */   79
>> +var KEY_KP_2        =/* 2           Down      0x50  */   80
>> +var KEY_KP_3        =/* 3           PgDown    0x51  */   81
>> +var KEY_KP_0        =/* 0           Insert    0x52  */   82
>> +var KEY_KP_Decimal  =/* . (Decimal) Delete    0x53  */   83
>> +var KEY_SysReqest   =/* SysReqest             0x54  */   84
>> +                         /* NOTUSED               0x55  */
>> +var KEY_Less        =/* < (Less)   >(Greater) 0x56  */ 86
>> +var KEY_F11         =/* F11                   0x57  */   87
>> +var KEY_F12         =/* F12                   0x58  */   88
>> +
>> +var KEY_Prefix0     =/* special               0x60  */   96
>> +var KEY_Prefix1     =/* specail               0x61  */   97
>> diff --git a/ui/js/spice/bitmap.js b/ui/js/spice/bitmap.js
>> new file mode 100644
>> index 0000000..03f5127
>> --- /dev/null
>> +++ b/ui/js/spice/bitmap.js
>> @@ -0,0 +1,51 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  bitmap.js
>> +**      Handle SPICE_IMAGE_TYPE_BITMAP
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function convert_spice_bitmap_to_web(context, spice_bitmap)
>> +{
>> +    var ret;
>> +    var offset, x;
>> +    var u8 = new Uint8Array(spice_bitmap.data);
>> +    if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT &&
>> +        spice_bitmap.format != SPICE_BITMAP_FMT_RGBA)
>> +        return undefined;
>> +
>> +    ret = context.createImageData(spice_bitmap.x, spice_bitmap.y);
>> +    for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); )
>> +        for (x = 0; x < spice_bitmap.x; x++, offset += 4)
>> +        {
>> +            ret.data[offset + 0 ] = u8[offset + 2];
>> +            ret.data[offset + 1 ] = u8[offset + 1];
>> +            ret.data[offset + 2 ] = u8[offset + 0];
>> +
>> +            // FIXME - We effectively treat all images as having 
>> SPICE_IMAGE_FLAGS_HIGH_BITS_SET
>> +            if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT)
>> +                ret.data[offset + 3] = 255;
>> +            else
>> +                ret.data[offset + 3] = u8[offset];
>> +        }
>> +
>> +    return ret;
>> +}
>> diff --git a/ui/js/spice/cursor.js b/ui/js/spice/cursor.js
>> new file mode 100644
>> index 0000000..b3184c3
>> --- /dev/null
>> +++ b/ui/js/spice/cursor.js
>> @@ -0,0 +1,92 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceCursorConn
>> +**      Drive the Spice Cursor Channel
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceCursorConn()
>> +{
>> +    SpiceConn.apply(this, arguments);
>> +}
>> +
>> +SpiceCursorConn.prototype = Object.create(SpiceConn.prototype);
>> +SpiceCursorConn.prototype.process_channel_message = function(msg)
>> +{
>> +    if (msg.type == SPICE_MSG_CURSOR_INIT)
>> +    {
>> +        var cursor_init = new SpiceMsgCursorInit(msg.data);
>> +        DEBUG > 1 && console.log("SpiceMsgCursorInit");
>> +        if (this.parent && this.parent.inputs &&
>> +            this.parent.inputs.mouse_mode == SPICE_MOUSE_MODE_SERVER)
>> +        {
>> +            // FIXME - this imagines that the server actually
>> +            //          provides the current cursor position,
>> +            //          instead of 0,0.  As of May 11, 2012,
>> +            //          that assumption was false :-(.
>> +            this.parent.inputs.mousex = cursor_init.position.x;
>> +            this.parent.inputs.mousey = cursor_init.position.y;
>> +        }
>> +        // FIXME - We don't handle most of the parameters here...
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_CURSOR_SET)
>> +    {
>> +        var cursor_set = new SpiceMsgCursorSet(msg.data);
>> +        DEBUG > 1 && console.log("SpiceMsgCursorSet");
>> +        if (cursor_set.flags & SPICE_CURSOR_FLAGS_NONE)
>> +        {
>> + document.getElementById(this.parent.screen_id).style.cursor = "none";
>> +            return true;
>> +        }
>> +
>> +        if (cursor_set.flags > 0)
>> +            this.log_warn("FIXME: No support for cursor flags " + 
>> cursor_set.flags);
>> +
>> +        if (cursor_set.cursor.header.type != SPICE_CURSOR_TYPE_ALPHA)
>> +        {
>> +            this.log_warn("FIXME: No support for cursor type " + 
>> cursor_set.cursor.header.type);
>> +            return false;
>> +        }
>> +
>> +        this.set_cursor(cursor_set.cursor);
>> +
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_CURSOR_HIDE)
>> +    {
>> +        DEBUG > 1 && console.log("SpiceMsgCursorHide");
>> + document.getElementById(this.parent.screen_id).style.cursor = "none";
>> +        return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +SpiceCursorConn.prototype.set_cursor = function(cursor)
>> +{
>> +    var pngstr = create_rgba_png(cursor.header.height, 
>> cursor.header.width, cursor.data);
>> +    var curstr = 'url(data:image/png,' + pngstr + ') ' +
>> +        cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y + 
>> ", default";
>> +    document.getElementById(this.parent.screen_id).style.cursor = 
>> curstr;
>> +}
>> diff --git a/ui/js/spice/display.js b/ui/js/spice/display.js
>> new file mode 100644
>> index 0000000..fdf9dc5
>> --- /dev/null
>> +++ b/ui/js/spice/display.js
>> @@ -0,0 +1,806 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  FIXME: putImageData  does not support Alpha blending
>> +**           or compositing.  So if we have data in an ImageData
>> +**           format, we have to draw it onto a context,
>> +**           and then use drawImage to put it onto the target,
>> +**           as drawImage does alpha.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function putImageDataWithAlpha(context, d, x, y)
>> +{
>> +    var c = document.createElement("canvas");
>> +    var t = c.getContext("2d");
>> +    c.setAttribute('width', d.width);
>> +    c.setAttribute('height', d.height);
>> +    t.putImageData(d, 0, 0);
>> +    context.drawImage(c, x, y, d.width, d.height);
>> +}
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  FIXME: Spice will send an image with '0' alpha when it is 
>> intended to
>> +**           go on a surface w/no alpha.  So in that case, we have 
>> to strip
>> +**           out the alpha.  The test case for this was flux box; in 
>> a Xspice
>> +**           server, right click on the desktop to get the menu; the 
>> top bar
>> +**           doesn't paint/highlight correctly w/out this change.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function stripAlpha(d)
>> +{
>> +    var i;
>> +    for (i = 0; i < (d.width * d.height * 4); i += 4)
>> +        d.data[i + 3] = 255;
>> +}
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceDisplayConn
>> +**      Drive the Spice Display Channel
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceDisplayConn()
>> +{
>> +    SpiceConn.apply(this, arguments);
>> +}
>> +
>> +SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype);
>> +SpiceDisplayConn.prototype.process_channel_message = function(msg)
>> +{
>> +    if (msg.type == SPICE_MSG_DISPLAY_MARK)
>> +    {
>> +        // FIXME - DISPLAY_MARK not implemented (may be hard or 
>> impossible)
>> +        this.known_unimplemented(msg.type, "Display Mark");
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_RESET)
>> +    {
>> +        DEBUG > 2 && console.log("Display reset");
>> + this.surfaces[this.primary_surface].canvas.context.restore();
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_DRAW_COPY)
>> +    {
>> +        var draw_copy = new SpiceMsgDisplayDrawCopy(msg.data);
>> +
>> +        DEBUG > 1 && this.log_draw("DrawCopy", draw_copy);
>> +
>> +        if (! draw_copy.base.box.is_same_size(draw_copy.data.src_area))
>> +            this.log_warn("FIXME: DrawCopy src_area is a different 
>> size than base.box; we do not handle that yet.");
>> +        if (draw_copy.base.clip.type != SPICE_CLIP_TYPE_NONE)
>> +            this.log_warn("FIXME: DrawCopy we don't handle clipping 
>> yet");
>> +        if (draw_copy.data.rop_descriptor != SPICE_ROPD_OP_PUT)
>> +            this.log_warn("FIXME: DrawCopy we don't handle ropd 
>> type: " + draw_copy.data.rop_descriptor);
>> +        if (draw_copy.data.mask.flags)
>> +            this.log_warn("FIXME: DrawCopy we don't handle mask 
>> flag: " + draw_copy.data.mask.flags);
>> +        if (draw_copy.data.mask.bitmap)
>> +            this.log_warn("FIXME: DrawCopy we don't handle mask");
>> +
>> +        if (draw_copy.data && draw_copy.data.src_bitmap)
>> +        {
>> +            if (draw_copy.data.src_bitmap.descriptor.flags &&
>> +                draw_copy.data.src_bitmap.descriptor.flags != 
>> SPICE_IMAGE_FLAGS_CACHE_ME &&
>> +                draw_copy.data.src_bitmap.descriptor.flags != 
>> SPICE_IMAGE_FLAGS_HIGH_BITS_SET)
>> +            {
>> +                this.log_warn("FIXME: DrawCopy unhandled image 
>> flags: " + draw_copy.data.src_bitmap.descriptor.flags);
>> +                DEBUG <= 1 && this.log_draw("DrawCopy", draw_copy);
>> +            }
>> +
>> +            if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_QUIC)
>> +            {
>> +                var canvas = 
>> this.surfaces[draw_copy.base.surface_id].canvas;
>> +                if (! draw_copy.data.src_bitmap.quic)
>> +                {
>> +                    this.log_warn("FIXME: DrawCopy could not handle 
>> this QUIC file.");
>> +                    return false;
>> +                }
>> +                var source_img = 
>> convert_spice_quic_to_web(canvas.context,
>> + draw_copy.data.src_bitmap.quic);
>> +
>> +                return this.draw_copy_helper(
>> +                    { base: draw_copy.base,
>> +                      src_area: draw_copy.data.src_area,
>> +                      image_data: source_img,
>> +                      tag: "copyquic." + 
>> draw_copy.data.src_bitmap.quic.type,
>> +                      has_alpha: 
>> (draw_copy.data.src_bitmap.quic.type == QUIC_IMAGE_TYPE_RGBA ? true : 
>> false) ,
>> +                      descriptor : draw_copy.data.src_bitmap.descriptor
>> +                    });
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_FROM_CACHE ||
>> +                    draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS)
>> +            {
>> +                if (! this.cache || ! 
>> this.cache[draw_copy.data.src_bitmap.descriptor.id])
>> +                {
>> +                    this.log_warn("FIXME: DrawCopy did not find 
>> image id " + draw_copy.data.src_bitmap.descriptor.id + " in cache.");
>> +                    return false;
>> +                }
>> +
>> +                return this.draw_copy_helper(
>> +                    { base: draw_copy.base,
>> +                      src_area: draw_copy.data.src_area,
>> +                      image_data: 
>> this.cache[draw_copy.data.src_bitmap.descriptor.id],
>> +                      tag: "copycache." + 
>> draw_copy.data.src_bitmap.descriptor.id,
>> +                      has_alpha: true, /* FIXME - may want this to 
>> be false... */
>> +                      descriptor : draw_copy.data.src_bitmap.descriptor
>> +                    });
>> +
>> +                /* FIXME - LOSSLESS CACHE ramifications not 
>> understood or handled */
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_SURFACE)
>> +            {
>> +                var source_context = 
>> this.surfaces[draw_copy.data.src_bitmap.surface_id].canvas.context;
>> +                var target_context = 
>> this.surfaces[draw_copy.base.surface_id].canvas.context;
>> +
>> +                var source_img = source_context.getImageData(
>> +                        draw_copy.data.src_area.left, 
>> draw_copy.data.src_area.top,
>> +                        draw_copy.data.src_area.right - 
>> draw_copy.data.src_area.left,
>> +                        draw_copy.data.src_area.bottom - 
>> draw_copy.data.src_area.top);
>> +                var computed_src_area = new SpiceRect;
>> +                computed_src_area.top = computed_src_area.left = 0;
>> +                computed_src_area.right = source_img.width;
>> +                computed_src_area.bottom = source_img.height;
>> +
>> +                /* FIXME - there is a potential optimization here.
>> +                           That is, if the surface is from 0,0, and
>> +                           both surfaces are alpha surfaces, you should
>> +                           be able to just do a drawImage, which should
>> +                           save time.  */
>> +
>> +                return this.draw_copy_helper(
>> +                    { base: draw_copy.base,
>> +                      src_area: computed_src_area,
>> +                      image_data: source_img,
>> +                      tag: "copysurf." + 
>> draw_copy.data.src_bitmap.surface_id,
>> +                      has_alpha: 
>> this.surfaces[draw_copy.data.src_bitmap.surface_id].format == 
>> SPICE_SURFACE_FMT_32_xRGB ? false : true,
>> +                      descriptor : draw_copy.data.src_bitmap.descriptor
>> +                    });
>> +
>> +                return true;
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_JPEG)
>> +            {
>> +                if (! draw_copy.data.src_bitmap.jpeg)
>> +                {
>> +                    this.log_warn("FIXME: DrawCopy could not handle 
>> this JPEG file.");
>> +                    return false;
>> +                }
>> +
>> +                // FIXME - how lame is this.  Be have it in binary 
>> format, and we have
>> +                //         to put it into string to get it back into 
>> jpeg.  Blech.
>> +                var tmpstr = "data:image/jpeg,";
>> +                var img = new Image;
>> +                var i;
>> +                var qdv = new 
>> Uint8Array(draw_copy.data.src_bitmap.jpeg.data);
>> +                for (i = 0; i < qdv.length; i++)
>> +                {
>> +                    tmpstr +=  '%';
>> +                    if (qdv[i] < 16)
>> +                        tmpstr += '0';
>> +                    tmpstr += qdv[i].toString(16);
>> +                }
>> +
>> +                img.o =
>> +                    { base: draw_copy.base,
>> +                      tag: "jpeg." + 
>> draw_copy.data.src_bitmap.surface_id,
>> +                      descriptor : 
>> draw_copy.data.src_bitmap.descriptor,
>> +                      sc : this,
>> +                    };
>> +                img.onload = handle_draw_jpeg_onload;
>> +                img.src = tmpstr;
>> +
>> +                return true;
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_JPEG_ALPHA)
>> +            {
>> +                if (! draw_copy.data.src_bitmap.jpeg_alpha)
>> +                {
>> +                    this.log_warn("FIXME: DrawCopy could not handle 
>> this JPEG ALPHA file.");
>> +                    return false;
>> +                }
>> +
>> +                // FIXME - how lame is this.  Be have it in binary 
>> format, and we have
>> +                //         to put it into string to get it back into 
>> jpeg.  Blech.
>> +                var tmpstr = "data:image/jpeg,";
>> +                var img = new Image;
>> +                var i;
>> +                var qdv = new 
>> Uint8Array(draw_copy.data.src_bitmap.jpeg_alpha.data);
>> +                for (i = 0; i < qdv.length; i++)
>> +                {
>> +                    tmpstr +=  '%';
>> +                    if (qdv[i] < 16)
>> +                        tmpstr += '0';
>> +                    tmpstr += qdv[i].toString(16);
>> +                }
>> +
>> +                img.o =
>> +                    { base: draw_copy.base,
>> +                      tag: "jpeg." + 
>> draw_copy.data.src_bitmap.surface_id,
>> +                      descriptor : 
>> draw_copy.data.src_bitmap.descriptor,
>> +                      sc : this,
>> +                    };
>> +
>> +                if (this.surfaces[draw_copy.base.surface_id].format 
>> == SPICE_SURFACE_FMT_32_ARGB)
>> +                {
>> +
>> +                    var canvas = 
>> this.surfaces[draw_copy.base.surface_id].canvas;
>> +                    img.alpha_img = 
>> convert_spice_lz_to_web(canvas.context,
>> + draw_copy.data.src_bitmap.jpeg_alpha.alpha);
>> +                }
>> +                img.onload = handle_draw_jpeg_onload;
>> +                img.src = tmpstr;
>> +
>> +                return true;
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_BITMAP)
>> +            {
>> +                var canvas = 
>> this.surfaces[draw_copy.base.surface_id].canvas;
>> +                if (! draw_copy.data.src_bitmap.bitmap)
>> +                {
>> +                    this.log_err("null bitmap");
>> +                    return false;
>> +                }
>> +
>> +                var source_img = 
>> convert_spice_bitmap_to_web(canvas.context,
>> + draw_copy.data.src_bitmap.bitmap);
>> +                if (! source_img)
>> +                {
>> +                    this.log_warn("FIXME: Unable to interpret bitmap 
>> of format: " +
>> + draw_copy.data.src_bitmap.bitmap.format);
>> +                    return false;
>> +                }
>> +
>> +                return this.draw_copy_helper(
>> +                    { base: draw_copy.base,
>> +                      src_area: draw_copy.data.src_area,
>> +                      image_data: source_img,
>> +                      tag: "bitmap." + 
>> draw_copy.data.src_bitmap.bitmap.format,
>> +                      has_alpha: draw_copy.data.src_bitmap.bitmap == 
>> SPICE_BITMAP_FMT_32BIT ? false : true,
>> +                      descriptor : draw_copy.data.src_bitmap.descriptor
>> +                    });
>> +            }
>> +            else if (draw_copy.data.src_bitmap.descriptor.type == 
>> SPICE_IMAGE_TYPE_LZ_RGB)
>> +            {
>> +                var canvas = 
>> this.surfaces[draw_copy.base.surface_id].canvas;
>> +                if (! draw_copy.data.src_bitmap.lz_rgb)
>> +                {
>> +                    this.log_err("null lz_rgb ");
>> +                    return false;
>> +                }
>> +
>> +                if (draw_copy.data.src_bitmap.lz_rgb.top_down != 1)
>> +                    this.log_warn("FIXME: Implement non top down 
>> support for lz_rgb");
>> +
>> +                var source_img = 
>> convert_spice_lz_to_web(canvas.context,
>> + draw_copy.data.src_bitmap.lz_rgb);
>> +                if (! source_img)
>> +                {
>> +                    this.log_warn("FIXME: Unable to interpret bitmap 
>> of type: " +
>> +                        draw_copy.data.src_bitmap.lz_rgb.type);
>> +                    return false;
>> +                }
>> +
>> +                return this.draw_copy_helper(
>> +                    { base: draw_copy.base,
>> +                      src_area: draw_copy.data.src_area,
>> +                      image_data: source_img,
>> +                      tag: "lz_rgb." + 
>> draw_copy.data.src_bitmap.lz_rgb.type,
>> +                      has_alpha: 
>> draw_copy.data.src_bitmap.lz_rgb.type == LZ_IMAGE_TYPE_RGBA ? true : 
>> false ,
>> +                      descriptor : draw_copy.data.src_bitmap.descriptor
>> +                    });
>> +            }
>> +            else
>> +            {
>> +                this.log_warn("FIXME: DrawCopy unhandled image type: 
>> " + draw_copy.data.src_bitmap.descriptor.type);
>> +                this.log_draw("DrawCopy", draw_copy);
>> +                return false;
>> +            }
>> +        }
>> +
>> +        this.log_warn("FIXME: DrawCopy no src_bitmap.");
>> +        return false;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_DRAW_FILL)
>> +    {
>> +        var draw_fill = new SpiceMsgDisplayDrawFill(msg.data);
>> +
>> +        DEBUG > 1 && this.log_draw("DrawFill", draw_fill);
>> +
>> +        if (draw_fill.data.rop_descriptor != SPICE_ROPD_OP_PUT)
>> +            this.log_warn("FIXME: DrawFill we don't handle ropd 
>> type: " + draw_fill.data.rop_descriptor);
>> +        if (draw_fill.data.mask.flags)
>> +            this.log_warn("FIXME: DrawFill we don't handle mask 
>> flag: " + draw_fill.data.mask.flags);
>> +        if (draw_fill.data.mask.bitmap)
>> +            this.log_warn("FIXME: DrawFill we don't handle mask");
>> +
>> +        if (draw_fill.data.brush.type == SPICE_BRUSH_TYPE_SOLID)
>> +        {
>> +            // FIXME - do brushes ever have alpha?
>> +            var color = draw_fill.data.brush.color & 0xffffff;
>> +            var color_str = "rgb(" + (color >> 16) + ", " + ((color 
>> >> 8) & 0xff) + ", " + (color & 0xff) + ")";
>> + this.surfaces[draw_fill.base.surface_id].canvas.context.fillStyle = 
>> color_str;
>> +
>> + this.surfaces[draw_fill.base.surface_id].canvas.context.fillRect(
>> +                draw_fill.base.box.left, draw_fill.base.box.top,
>> +                draw_fill.base.box.right - draw_fill.base.box.left,
>> +                draw_fill.base.box.bottom - draw_fill.base.box.top);
>> +
>> +            if (DUMP_DRAWS && this.parent.dump_id)
>> +            {
>> +                var debug_canvas = document.createElement("canvas");
>> +                debug_canvas.setAttribute('width', 
>> this.surfaces[draw_fill.base.surface_id].canvas.width);
>> +                debug_canvas.setAttribute('height', 
>> this.surfaces[draw_fill.base.surface_id].canvas.height);
>> +                debug_canvas.setAttribute('id', "fillbrush." + 
>> draw_fill.base.surface_id + "." + 
>> this.surfaces[draw_fill.base.surface_id].draw_count);
>> +                debug_canvas.getContext("2d").fillStyle = color_str;
>> +                debug_canvas.getContext("2d").fillRect(
>> +                    draw_fill.base.box.left, draw_fill.base.box.top,
>> +                    draw_fill.base.box.right - draw_fill.base.box.left,
>> +                    draw_fill.base.box.bottom - 
>> draw_fill.base.box.top);
>> + 
>> document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
>> +            }
>> +
>> + this.surfaces[draw_fill.base.surface_id].draw_count++;
>> +
>> +        }
>> +        else
>> +        {
>> +            this.log_warn("FIXME: DrawFill can't handle brush type: 
>> " + draw_fill.data.brush.type);
>> +        }
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS)
>> +    {
>> +        var copy_bits = new SpiceMsgDisplayCopyBits(msg.data);
>> +
>> +        DEBUG > 1 && this.log_draw("CopyBits", copy_bits);
>> +
>> +        var source_canvas = 
>> this.surfaces[copy_bits.base.surface_id].canvas;
>> +        var source_context = source_canvas.context;
>> +
>> +        var width = source_canvas.width - copy_bits.src_pos.x;
>> +        var height = source_canvas.height - copy_bits.src_pos.y;
>> +        if (width > (copy_bits.base.box.right - 
>> copy_bits.base.box.left))
>> +            width = copy_bits.base.box.right - copy_bits.base.box.left;
>> +        if (height > (copy_bits.base.box.bottom - 
>> copy_bits.base.box.top))
>> +            height = copy_bits.base.box.bottom - 
>> copy_bits.base.box.top;
>> +
>> +        var source_img = source_context.getImageData(
>> +                copy_bits.src_pos.x, copy_bits.src_pos.y, width, 
>> height);
>> +        //source_context.putImageData(source_img, 
>> copy_bits.base.box.left, copy_bits.base.box.top);
>> +        putImageDataWithAlpha(source_context, source_img, 
>> copy_bits.base.box.left, copy_bits.base.box.top);
>> +
>> +        if (DUMP_DRAWS && this.parent.dump_id)
>> +        {
>> +            var debug_canvas = document.createElement("canvas");
>> +            debug_canvas.setAttribute('width', width);
>> +            debug_canvas.setAttribute('height', height);
>> +            debug_canvas.setAttribute('id', "copybits" + 
>> copy_bits.base.surface_id + "." + 
>> this.surfaces[copy_bits.base.surface_id].draw_count);
>> + debug_canvas.getContext("2d").putImageData(source_img, 0, 0);
>> + 
>> document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
>> +        }
>> +
>> +
>> +        this.surfaces[copy_bits.base.surface_id].draw_count++;
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES)
>> +    {
>> +        this.known_unimplemented(msg.type, "Inval All Palettes");
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_SURFACE_CREATE)
>> +    {
>> +        if (! ("surfaces" in this))
>> +            this.surfaces = [];
>> +
>> +        var m = new SpiceMsgSurfaceCreate(msg.data);
>> +        DEBUG > 1 && console.log(this.type + ": MsgSurfaceCreate id 
>> " + m.surface.surface_id
>> +                                    + "; " + m.surface.width + "x" + 
>> m.surface.height
>> +                                    + "; format " + m.surface.format
>> +                                    + "; flags " + m.surface.flags);
>> +        if (m.surface.format != SPICE_SURFACE_FMT_32_xRGB &&
>> +            m.surface.format != SPICE_SURFACE_FMT_32_ARGB)
>> +        {
>> +            this.log_warn("FIXME: cannot handle surface format " + 
>> m.surface.format + " yet.");
>> +            return false;
>> +        }
>> +
>> +        var canvas = document.createElement("canvas");
>> +        canvas.setAttribute('width', m.surface.width);
>> +        canvas.setAttribute('height', m.surface.height);
>> +        canvas.setAttribute('id', "spice_surface_" + 
>> m.surface.surface_id);
>> +        canvas.setAttribute('tabindex', m.surface.surface_id);
>> +        canvas.context = canvas.getContext("2d");
>> +
>> +        if (DUMP_CANVASES && this.parent.dump_id)
>> + document.getElementById(this.parent.dump_id).appendChild(canvas);
>> +
>> +        m.surface.canvas = canvas;
>> +        m.surface.draw_count = 0;
>> +        this.surfaces[m.surface.surface_id] = m.surface;
>> +
>> +        if (m.surface.flags & SPICE_SURFACE_FLAGS_PRIMARY)
>> +        {
>> +            this.primary_surface = m.surface.surface_id;
>> +
>> +            /* This .save() is done entirely to enable 
>> SPICE_MSG_DISPLAY_RESET */
>> +            canvas.context.save();
>> + document.getElementById(this.parent.screen_id).appendChild(canvas);
>> + 
>> document.getElementById(this.parent.screen_id).setAttribute('width', 
>> m.surface.width);
>> + 
>> document.getElementById(this.parent.screen_id).setAttribute('height', 
>> m.surface.height);
>> +            this.hook_events();
>> +        }
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_SURFACE_DESTROY)
>> +    {
>> +        var m = new SpiceMsgSurfaceDestroy(msg.data);
>> +        DEBUG > 1 && console.log(this.type + ": MsgSurfaceDestroy id 
>> " + m.surface_id);
>> +        this.delete_surface(m.surface_id);
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE)
>> +    {
>> +        var m = new SpiceMsgDisplayStreamCreate(msg.data);
>> +        DEBUG > 1 && console.log(this.type + ": MsgStreamCreate id" 
>> + m.id);
>> +        if (!this.streams)
>> +            this.streams = new Array();
>> +        if (this.streams[m.id])
>> +            console.log("Stream already exists");
>> +        else
>> +            this.streams[m.id] = m;
>> +        if (m.codec_type != SPICE_VIDEO_CODEC_TYPE_MJPEG)
>> +            console.log("Unhandled stream codec: "+m.codec_type);
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA)
>> +    {
>> +        var m = new SpiceMsgDisplayStreamData(msg.data);
>> +        if (!this.streams[m.base.id])
>> +        {
>> +            console.log("no stream for data");
>> +            return false;
>> +        }
>> +        if (this.streams[m.base.id].codec_type === 
>> SPICE_VIDEO_CODEC_TYPE_MJPEG)
>> +        {
>> +            var tmpstr = "data:image/jpeg,";
>> +            var img = new Image;
>> +            var i;
>> +            for (i = 0; i < m.data.length; i++)
>> +            {
>> +                tmpstr +=  '%';
>> +                if (m.data[i] < 16)
>> +                tmpstr += '0';
>> +                tmpstr += m.data[i].toString(16);
>> +            }
>> +            var strm_base = new SpiceMsgDisplayBase();
>> +            strm_base.surface_id = this.streams[m.base.id].surface_id;
>> +            strm_base.box = this.streams[m.base.id].dest;
>> +            strm_base.clip = this.streams[m.base.id].clip;
>> +            img.o =
>> +                { base: strm_base,
>> +                  tag: "mjpeg." + m.base.id,
>> +                  descriptor: null,
>> +                  sc : this,
>> +                };
>> +            img.onload = handle_draw_jpeg_onload;
>> +            img.src = tmpstr;
>> +        }
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
>> +    {
>> +        var m = new SpiceMsgDisplayStreamClip(msg.data);
>> +        DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + 
>> m.id);
>> +        this.streams[m.id].clip = m.clip;
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY)
>> +    {
>> +        var m = new SpiceMsgDisplayStreamDestroy(msg.data);
>> +        DEBUG > 1 && console.log(this.type + ": MsgStreamDestroy id" 
>> + m.id);
>> +        this.streams[m.id] = undefined;
>> +        return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +SpiceDisplayConn.prototype.delete_surface = function(surface_id)
>> +{
>> +    var canvas = document.getElementById("spice_surface_" + 
>> surface_id);
>> +    if (DUMP_CANVASES && this.parent.dump_id)
>> + document.getElementById(this.parent.dump_id).removeChild(canvas);
>> +    if (this.primary_surface == surface_id)
>> +    {
>> +        this.unhook_events();
>> +        this.primary_surface = undefined;
>> + document.getElementById(this.parent.screen_id).removeChild(canvas);
>> +    }
>> +
>> +    delete this.surfaces[surface_id];
>> +}
>> +
>> +
>> +SpiceDisplayConn.prototype.draw_copy_helper = function(o)
>> +{
>> +
>> +    var canvas = this.surfaces[o.base.surface_id].canvas;
>> +    if (o.has_alpha)
>> +    {
>> +        /* FIXME - This is based on trial + error, not a serious 
>> thoughtful
>> +                   analysis of what Spice requires.  See display.js 
>> for more. */
>> +        if (this.surfaces[o.base.surface_id].format == 
>> SPICE_SURFACE_FMT_32_xRGB)
>> +        {
>> +            stripAlpha(o.image_data);
>> +            canvas.context.putImageData(o.image_data, 
>> o.base.box.left, o.base.box.top);
>> +        }
>> +        else
>> +            putImageDataWithAlpha(canvas.context, o.image_data,
>> +                    o.base.box.left, o.base.box.top);
>> +    }
>> +    else
>> +        canvas.context.putImageData(o.image_data, o.base.box.left, 
>> o.base.box.top);
>> +
>> +    if (o.src_area.left > 0 || o.src_area.top > 0)
>> +    {
>> +        this.log_warn("FIXME: DrawCopy not shifting draw copies just 
>> yet...");
>> +    }
>> +
>> +    if (o.descriptor && (o.descriptor.flags & 
>> SPICE_IMAGE_FLAGS_CACHE_ME))
>> +    {
>> +        if (! ("cache" in this))
>> +            this.cache = [];
>> +        this.cache[o.descriptor.id] = o.image_data;
>> +    }
>> +
>> +    if (DUMP_DRAWS && this.parent.dump_id)
>> +    {
>> +        var debug_canvas = document.createElement("canvas");
>> +        debug_canvas.setAttribute('width', o.image_data.width);
>> +        debug_canvas.setAttribute('height', o.image_data.height);
>> +        debug_canvas.setAttribute('id', o.tag + "." +
>> +            this.surfaces[o.base.surface_id].draw_count + "." +
>> +            o.base.surface_id + "@" + o.base.box.left + "x" + 
>> o.base.box.top);
>> + debug_canvas.getContext("2d").putImageData(o.image_data, 0, 0);
>> + 
>> document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
>> +    }
>> +
>> +    this.surfaces[o.base.surface_id].draw_count++;
>> +
>> +    return true;
>> +}
>> +
>> +
>> +SpiceDisplayConn.prototype.log_draw = function(prefix, draw)
>> +{
>> +    var str = prefix + "." + draw.base.surface_id + "." + 
>> this.surfaces[draw.base.surface_id].draw_count + ": ";
>> +    str += "base.box " + draw.base.box.left + ", " + 
>> draw.base.box.top + " to " +
>> +                           draw.base.box.right + ", " + 
>> draw.base.box.bottom;
>> +    str += "; clip.type " + draw.base.clip.type;
>> +
>> +    if (draw.data)
>> +    {
>> +        if (draw.data.src_area)
>> +            str += "; src_area " + draw.data.src_area.left + ", " + 
>> draw.data.src_area.top + " to "
>> +                                 + draw.data.src_area.right + ", " + 
>> draw.data.src_area.bottom;
>> +
>> +        if (draw.data.src_bitmap && draw.data.src_bitmap != null)
>> +        {
>> +            str += "; src_bitmap id: " + 
>> draw.data.src_bitmap.descriptor.id;
>> +            str += "; src_bitmap width " + 
>> draw.data.src_bitmap.descriptor.width + ", height " + 
>> draw.data.src_bitmap.descriptor.height;
>> +            str += "; src_bitmap type " + 
>> draw.data.src_bitmap.descriptor.type + ", flags " + 
>> draw.data.src_bitmap.descriptor.flags;
>> +            if (draw.data.src_bitmap.surface_id !== undefined)
>> +                str += "; src_bitmap surface_id " + 
>> draw.data.src_bitmap.surface_id;
>> +            if (draw.data.src_bitmap.quic)
>> +                str += "; QUIC type " + 
>> draw.data.src_bitmap.quic.type +
>> +                        "; width " + draw.data.src_bitmap.quic.width +
>> +                        "; height " + 
>> draw.data.src_bitmap.quic.height ;
>> +            if (draw.data.src_bitmap.lz_rgb)
>> +                str += "; LZ_RGB length " + 
>> draw.data.src_bitmap.lz_rgb.length +
>> +                       "; magic " + draw.data.src_bitmap.lz_rgb.magic +
>> +                       "; version 0x" + 
>> draw.data.src_bitmap.lz_rgb.version.toString(16) +
>> +                       "; type " + draw.data.src_bitmap.lz_rgb.type +
>> +                       "; width " + draw.data.src_bitmap.lz_rgb.width +
>> +                       "; height " + 
>> draw.data.src_bitmap.lz_rgb.height +
>> +                       "; stride " + 
>> draw.data.src_bitmap.lz_rgb.stride +
>> +                       "; top down " + 
>> draw.data.src_bitmap.lz_rgb.top_down;
>> +        }
>> +        else
>> +            str += "; src_bitmap is null";
>> +
>> +        if (draw.data.brush)
>> +        {
>> +            if (draw.data.brush.type == SPICE_BRUSH_TYPE_SOLID)
>> +                str += "; brush.color 0x" + 
>> draw.data.brush.color.toString(16);
>> +            if (draw.data.brush.type == SPICE_BRUSH_TYPE_PATTERN)
>> +            {
>> +                str += "; brush.pat ";
>> +                if (draw.data.brush.pattern.pat != null)
>> +                    str += "[SpiceImage]";
>> +                else
>> +                    str += "[null]";
>> +                str += " at " + draw.data.brush.pattern.pos.x + ", " 
>> + draw.data.brush.pattern.pos.y;
>> +            }
>> +        }
>> +
>> +        str += "; rop_descriptor " + draw.data.rop_descriptor;
>> +        if (draw.data.scale_mode !== undefined)
>> +            str += "; scale_mode " + draw.data.scale_mode;
>> +        str += "; mask.flags " + draw.data.mask.flags;
>> +        str += "; mask.pos " + draw.data.mask.pos.x + ", " + 
>> draw.data.mask.pos.y;
>> +        if (draw.data.mask.bitmap != null)
>> +        {
>> +            str += "; mask.bitmap width " + 
>> draw.data.mask.bitmap.descriptor.width + ", height " + 
>> draw.data.mask.bitmap.descriptor.height;
>> +            str += "; mask.bitmap type " + 
>> draw.data.mask.bitmap.descriptor.type + ", flags " + 
>> draw.data.mask.bitmap.descriptor.flags;
>> +        }
>> +        else
>> +            str += "; mask.bitmap is null";
>> +    }
>> +
>> +    console.log(str);
>> +}
>> +
>> +SpiceDisplayConn.prototype.hook_events = function()
>> +{
>> +    if (this.primary_surface !== undefined)
>> +    {
>> +        var canvas = this.surfaces[this.primary_surface].canvas;
>> +        canvas.sc = this.parent;
>> +        canvas.addEventListener('mousemove', handle_mousemove);
>> +        canvas.addEventListener('mousedown', handle_mousedown);
>> +        canvas.addEventListener('contextmenu', handle_contextmenu);
>> +        canvas.addEventListener('mouseup', handle_mouseup);
>> +        canvas.addEventListener('keydown', handle_keydown);
>> +        canvas.addEventListener('keyup', handle_keyup);
>> +        canvas.addEventListener('mouseout', handle_mouseout);
>> +        canvas.addEventListener('mouseover', handle_mouseover);
>> +        canvas.addEventListener('mousewheel', handle_mousewheel);
>> +        canvas.focus();
>> +    }
>> +}
>> +
>> +SpiceDisplayConn.prototype.unhook_events = function()
>> +{
>> +    if (this.primary_surface !== undefined)
>> +    {
>> +        var canvas = this.surfaces[this.primary_surface].canvas;
>> +        canvas.removeEventListener('mousemove', handle_mousemove);
>> +        canvas.removeEventListener('mousedown', handle_mousedown);
>> +        canvas.removeEventListener('contextmenu', handle_contextmenu);
>> +        canvas.removeEventListener('mouseup', handle_mouseup);
>> +        canvas.removeEventListener('keydown', handle_keydown);
>> +        canvas.removeEventListener('keyup', handle_keyup);
>> +        canvas.removeEventListener('mouseout', handle_mouseout);
>> +        canvas.removeEventListener('mouseover', handle_mouseover);
>> +        canvas.removeEventListener('mousewheel', handle_mousewheel);
>> +    }
>> +}
>> +
>> +
>> +SpiceDisplayConn.prototype.destroy_surfaces = function()
>> +{
>> +    for (var s in this.surfaces)
>> +    {
>> +        this.delete_surface(this.surfaces[s].surface_id);
>> +    }
>> +
>> +    this.surfaces = undefined;
>> +}
>> +
>> +
>> +function handle_mouseover(e)
>> +{
>> +    this.focus();
>> +}
>> +
>> +function handle_mouseout(e)
>> +{
>> +    this.blur();
>> +}
>> +
>> +function handle_draw_jpeg_onload()
>> +{
>> +    var temp_canvas = null;
>> +    var context;
>> +
>> + /*------------------------------------------------------------
>> +    ** FIXME:
>> +    **  The helper should be extended to be able to handle actual 
>> HtmlImageElements
>> +    **  ...and the cache should be modified to do so as well
>> + **----------------------------------------------------------*/
>> +    if (this.o.sc.surfaces[this.o.base.surface_id] === undefined)
>> +    {
>> +        // This can happen; if the jpeg image loads after our surface
>> +        //  has been destroyed (e.g. open a menu, close it quickly),
>> +        //  we'll find we have no surface.
>> +        DEBUG > 2 && this.o.sc.log_info("Discarding jpeg; presumed 
>> lost surface " + this.o.base.surface_id);
>> +        temp_canvas = document.createElement("canvas");
>> +        temp_canvas.setAttribute('width', this.o.base.box.right);
>> +        temp_canvas.setAttribute('height', this.o.base.box.bottom);
>> +        context = temp_canvas.getContext("2d");
>> +    }
>> +    else
>> +        context = 
>> this.o.sc.surfaces[this.o.base.surface_id].canvas.context;
>> +
>> +    if (this.alpha_img)
>> +    {
>> +        var c = document.createElement("canvas");
>> +        var t = c.getContext("2d");
>> +        c.setAttribute('width', this.alpha_img.width);
>> +        c.setAttribute('height', this.alpha_img.height);
>> +        t.putImageData(this.alpha_img, 0, 0);
>> +        t.globalCompositeOperation = 'source-in';
>> +        t.drawImage(this, 0, 0);
>> +
>> +        context.drawImage(c, this.o.base.box.left, 
>> this.o.base.box.top);
>> +
>> +        if (this.o.descriptor &&
>> +            (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
>> +        {
>> +            if (! ("cache" in this.o.sc))
>> +                this.o.sc.cache = [];
>> +
>> +            this.o.sc.cache[this.o.descriptor.id] =
>> +                t.getImageData(0, 0,
>> +                    this.alpha_img.width,
>> +                    this.alpha_img.height);
>> +        }
>> +    }
>> +    else
>> +    {
>> +        context.drawImage(this, this.o.base.box.left, 
>> this.o.base.box.top);
>> +
>> +        if (this.o.descriptor &&
>> +            (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
>> +        {
>> +            if (! ("cache" in this.o.sc))
>> +                this.o.sc.cache = [];
>> +
>> +            this.o.sc.cache[this.o.descriptor.id] =
>> +                context.getImageData(this.o.base.box.left, 
>> this.o.base.box.top,
>> +                    this.o.base.box.right - this.o.base.box.left,
>> +                    this.o.base.box.bottom - this.o.base.box.top);
>> +        }
>> +    }
>> +
>> +    if (temp_canvas == null)
>> +    {
>> +        if (DUMP_DRAWS && this.o.sc.parent.dump_id)
>> +        {
>> +            var debug_canvas = document.createElement("canvas");
>> +            debug_canvas.setAttribute('id', this.o.tag + "." +
>> + this.o.sc.surfaces[this.o.base.surface_id].draw_count + "." +
>> +                this.o.base.surface_id + "@" + this.o.base.box.left 
>> + "x" +  this.o.base.box.top);
>> +            debug_canvas.getContext("2d").drawImage(this, 0, 0);
>> + 
>> document.getElementById(this.o.sc.parent.dump_id).appendChild(debug_canvas);
>> +        }
>> +
>> + this.o.sc.surfaces[this.o.base.surface_id].draw_count++;
>> +    }
>> +}
>> diff --git a/ui/js/spice/enums.js b/ui/js/spice/enums.js
>> new file mode 100644
>> index 0000000..fd42a14
>> --- /dev/null
>> +++ b/ui/js/spice/enums.js
>> @@ -0,0 +1,282 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  enums.js
>> +**      'constants' for Spice
>> +**--------------------------------------------------------------------------*/ 
>>
>> +var SPICE_MAGIC         = "REDQ";
>> +var SPICE_VERSION_MAJOR = 2;
>> +var SPICE_VERSION_MINOR = 2;
>> +
>> +var SPICE_CONNECT_TIMEOUT = (30 * 1000);
>> +
>> +var SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION = 0;
>> +var SPICE_COMMON_CAP_AUTH_SPICE              = 1;
>> +var SPICE_COMMON_CAP_AUTH_SASL               = 2;
>> +var SPICE_COMMON_CAP_MINI_HEADER             = 3;
>> +
>> +var SPICE_TICKET_KEY_PAIR_LENGTH             = 1024;
>> +var SPICE_TICKET_PUBKEY_BYTES                = 
>> (SPICE_TICKET_KEY_PAIR_LENGTH / 8 + 34);
>> +
>> +var SPICE_LINK_ERR_OK                        = 0,
>> +    SPICE_LINK_ERR_ERROR                     = 1,
>> +    SPICE_LINK_ERR_INVALID_MAGIC             = 2,
>> +    SPICE_LINK_ERR_INVALID_DATA              = 3,
>> +    SPICE_LINK_ERR_VERSION_MISMATCH          = 4,
>> +    SPICE_LINK_ERR_NEED_SECURED              = 5,
>> +    SPICE_LINK_ERR_NEED_UNSECURED            = 6,
>> +    SPICE_LINK_ERR_PERMISSION_DENIED         = 7,
>> +    SPICE_LINK_ERR_BAD_CONNECTION_ID         = 8,
>> +    SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE     = 9;
>> +
>> +var SPICE_MSG_MIGRATE                   = 1;
>> +var SPICE_MSG_MIGRATE_DATA              = 2;
>> +var SPICE_MSG_SET_ACK                   = 3;
>> +var SPICE_MSG_PING                      = 4;
>> +var SPICE_MSG_WAIT_FOR_CHANNELS         = 5;
>> +var SPICE_MSG_DISCONNECTING             = 6;
>> +var SPICE_MSG_NOTIFY                    = 7;
>> +var SPICE_MSG_LIST                      = 8;
>> +
>> +var SPICE_MSG_MAIN_MIGRATE_BEGIN        = 101;
>> +var SPICE_MSG_MAIN_MIGRATE_CANCEL       = 102;
>> +var SPICE_MSG_MAIN_INIT                 = 103;
>> +var SPICE_MSG_MAIN_CHANNELS_LIST        = 104;
>> +var SPICE_MSG_MAIN_MOUSE_MODE           = 105;
>> +var SPICE_MSG_MAIN_MULTI_MEDIA_TIME     = 106;
>> +var SPICE_MSG_MAIN_AGENT_CONNECTED      = 107;
>> +var SPICE_MSG_MAIN_AGENT_DISCONNECTED   = 108;
>> +var SPICE_MSG_MAIN_AGENT_DATA           = 109;
>> +var SPICE_MSG_MAIN_AGENT_TOKEN          = 110;
>> +var SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST  = 111;
>> +var SPICE_MSG_MAIN_MIGRATE_END          = 112;
>> +var SPICE_MSG_MAIN_NAME                 = 113;
>> +var SPICE_MSG_MAIN_UUID                 = 114;
>> +var SPICE_MSG_END_MAIN                  = 115;
>> +
>> +
>> +var SPICE_MSGC_ACK_SYNC                 = 1;
>> +var SPICE_MSGC_ACK                      = 2;
>> +var SPICE_MSGC_PONG                     = 3;
>> +var SPICE_MSGC_MIGRATE_FLUSH_MARK       = 4;
>> +var SPICE_MSGC_MIGRATE_DATA             = 5;
>> +var SPICE_MSGC_DISCONNECTING            = 6;
>> +
>> +
>> +var SPICE_MSGC_MAIN_CLIENT_INFO         = 101;
>> +var SPICE_MSGC_MAIN_MIGRATE_CONNECTED   = 102;
>> +var SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR = 103;
>> +var SPICE_MSGC_MAIN_ATTACH_CHANNELS     = 104;
>> +var SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST  = 105;
>> +var SPICE_MSGC_MAIN_AGENT_START         = 106;
>> +var SPICE_MSGC_MAIN_AGENT_DATA          = 107;
>> +var SPICE_MSGC_MAIN_AGENT_TOKEN         = 108;
>> +var SPICE_MSGC_MAIN_MIGRATE_END         = 109;
>> +var SPICE_MSGC_END_MAIN                 = 110;
>> +
>> +var SPICE_MSG_DISPLAY_MODE              = 101;
>> +var SPICE_MSG_DISPLAY_MARK              = 102;
>> +var SPICE_MSG_DISPLAY_RESET             = 103;
>> +var SPICE_MSG_DISPLAY_COPY_BITS         = 104;
>> +var SPICE_MSG_DISPLAY_INVAL_LIST        = 105;
>> +var SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS = 106;
>> +var SPICE_MSG_DISPLAY_INVAL_PALETTE     = 107;
>> +var SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES= 108;
>> +
>> +var SPICE_MSG_DISPLAY_STREAM_CREATE     = 122;
>> +var SPICE_MSG_DISPLAY_STREAM_DATA       = 123;
>> +var SPICE_MSG_DISPLAY_STREAM_CLIP       = 124;
>> +var SPICE_MSG_DISPLAY_STREAM_DESTROY    = 125;
>> +var SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL= 126;
>> +
>> +var SPICE_MSG_DISPLAY_DRAW_FILL         = 302;
>> +var SPICE_MSG_DISPLAY_DRAW_OPAQUE       = 303;
>> +var SPICE_MSG_DISPLAY_DRAW_COPY         = 304;
>> +var SPICE_MSG_DISPLAY_DRAW_BLEND        = 305;
>> +var SPICE_MSG_DISPLAY_DRAW_BLACKNESS    = 306;
>> +var SPICE_MSG_DISPLAY_DRAW_WHITENESS    = 307;
>> +var SPICE_MSG_DISPLAY_DRAW_INVERS       = 308;
>> +var SPICE_MSG_DISPLAY_DRAW_ROP3         = 309;
>> +var SPICE_MSG_DISPLAY_DRAW_STROKE       = 310;
>> +var SPICE_MSG_DISPLAY_DRAW_TEXT         = 311;
>> +var SPICE_MSG_DISPLAY_DRAW_TRANSPARENT  = 312;
>> +var SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND  = 313;
>> +var SPICE_MSG_DISPLAY_SURFACE_CREATE    = 314;
>> +var SPICE_MSG_DISPLAY_SURFACE_DESTROY   = 315;
>> +
>> +var SPICE_MSGC_DISPLAY_INIT             = 101;
>> +
>> +var SPICE_MSG_INPUTS_INIT               = 101;
>> +var SPICE_MSG_INPUTS_KEY_MODIFIERS      = 102;
>> +
>> +var SPICE_MSG_INPUTS_MOUSE_MOTION_ACK   = 111;
>> +
>> +var SPICE_MSGC_INPUTS_KEY_DOWN          = 101;
>> +var SPICE_MSGC_INPUTS_KEY_UP            = 102;
>> +var SPICE_MSGC_INPUTS_KEY_MODIFIERS     = 103;
>> +
>> +var SPICE_MSGC_INPUTS_MOUSE_MOTION      = 111;
>> +var SPICE_MSGC_INPUTS_MOUSE_POSITION    = 112;
>> +var SPICE_MSGC_INPUTS_MOUSE_PRESS       = 113;
>> +var SPICE_MSGC_INPUTS_MOUSE_RELEASE     = 114;
>> +
>> +var SPICE_MSG_CURSOR_INIT               = 101;
>> +var SPICE_MSG_CURSOR_RESET              = 102;
>> +var SPICE_MSG_CURSOR_SET                = 103;
>> +var SPICE_MSG_CURSOR_MOVE               = 104;
>> +var SPICE_MSG_CURSOR_HIDE               = 105;
>> +var SPICE_MSG_CURSOR_TRAIL              = 106;
>> +var SPICE_MSG_CURSOR_INVAL_ONE          = 107;
>> +var SPICE_MSG_CURSOR_INVAL_ALL          = 108;
>> +
>> +
>> +var SPICE_CHANNEL_MAIN                  = 1;
>> +var SPICE_CHANNEL_DISPLAY               = 2;
>> +var SPICE_CHANNEL_INPUTS                = 3;
>> +var SPICE_CHANNEL_CURSOR                = 4;
>> +var SPICE_CHANNEL_PLAYBACK              = 5;
>> +var SPICE_CHANNEL_RECORD                = 6;
>> +var SPICE_CHANNEL_TUNNEL                = 7;
>> +var SPICE_CHANNEL_SMARTCARD             = 8;
>> +var SPICE_CHANNEL_USBREDIR              = 9;
>> +
>> +var SPICE_SURFACE_FLAGS_PRIMARY = (1 << 0);
>> +
>> +var SPICE_NOTIFY_SEVERITY_INFO  = 0;
>> +var SPICE_NOTIFY_SEVERITY_WARN  = 1;
>> +var SPICE_NOTIFY_SEVERITY_ERROR = 2;
>> +
>> +var SPICE_MOUSE_MODE_SERVER = (1 << 0),
>> +    SPICE_MOUSE_MODE_CLIENT = (1 << 1),
>> +    SPICE_MOUSE_MODE_MASK = 0x3;
>> +
>> +var SPICE_CLIP_TYPE_NONE            = 0;
>> +var SPICE_CLIP_TYPE_RECTS           = 1;
>> +
>> +var SPICE_IMAGE_TYPE_BITMAP         = 0;
>> +var SPICE_IMAGE_TYPE_QUIC           = 1;
>> +var SPICE_IMAGE_TYPE_RESERVED       = 2;
>> +var SPICE_IMAGE_TYPE_LZ_PLT         = 100;
>> +var SPICE_IMAGE_TYPE_LZ_RGB         = 101;
>> +var SPICE_IMAGE_TYPE_GLZ_RGB        = 102;
>> +var SPICE_IMAGE_TYPE_FROM_CACHE     = 103;
>> +var SPICE_IMAGE_TYPE_SURFACE        = 104;
>> +var SPICE_IMAGE_TYPE_JPEG           = 105;
>> +var SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS = 106;
>> +var SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB   = 107;
>> +var SPICE_IMAGE_TYPE_JPEG_ALPHA     = 108;
>> +
>> +var SPICE_IMAGE_FLAGS_CACHE_ME = (1 << 0),
>> +    SPICE_IMAGE_FLAGS_HIGH_BITS_SET = (1 << 1),
>> +    SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME = (1 << 2);
>> +
>> +var SPICE_BITMAP_FLAGS_PAL_CACHE_ME = (1 << 0),
>> +    SPICE_BITMAP_FLAGS_PAL_FROM_CACHE = (1 << 1),
>> +    SPICE_BITMAP_FLAGS_TOP_DOWN = (1 << 2),
>> +    SPICE_BITMAP_FLAGS_MASK = 0x7;
>> +
>> +var SPICE_BITMAP_FMT_INVALID        = 0,
>> +    SPICE_BITMAP_FMT_1BIT_LE        = 1,
>> +    SPICE_BITMAP_FMT_1BIT_BE        = 2,
>> +    SPICE_BITMAP_FMT_4BIT_LE        = 3,
>> +    SPICE_BITMAP_FMT_4BIT_BE        = 4,
>> +    SPICE_BITMAP_FMT_8BIT           = 5,
>> +    SPICE_BITMAP_FMT_16BIT          = 6,
>> +    SPICE_BITMAP_FMT_24BIT          = 7,
>> +    SPICE_BITMAP_FMT_32BIT          = 8,
>> +    SPICE_BITMAP_FMT_RGBA           = 9;
>> +
>> +
>> +var SPICE_CURSOR_FLAGS_NONE = (1 << 0),
>> +    SPICE_CURSOR_FLAGS_CACHE_ME = (1 << 1),
>> +    SPICE_CURSOR_FLAGS_FROM_CACHE = (1 << 2),
>> +    SPICE_CURSOR_FLAGS_MASK = 0x7;
>> +
>> +var SPICE_MOUSE_BUTTON_MASK_LEFT = (1 << 0),
>> +    SPICE_MOUSE_BUTTON_MASK_MIDDLE = (1 << 1),
>> +    SPICE_MOUSE_BUTTON_MASK_RIGHT = (1 << 2),
>> +    SPICE_MOUSE_BUTTON_MASK_MASK = 0x7;
>> +
>> +var SPICE_MOUSE_BUTTON_INVALID  = 0;
>> +var SPICE_MOUSE_BUTTON_LEFT     = 1;
>> +var SPICE_MOUSE_BUTTON_MIDDLE   = 2;
>> +var SPICE_MOUSE_BUTTON_RIGHT    = 3;
>> +var SPICE_MOUSE_BUTTON_UP       = 4;
>> +var SPICE_MOUSE_BUTTON_DOWN     = 5;
>> +
>> +var SPICE_BRUSH_TYPE_NONE = 0,
>> +    SPICE_BRUSH_TYPE_SOLID = 1,
>> +    SPICE_BRUSH_TYPE_PATTERN = 2;
>> +
>> +var SPICE_SURFACE_FMT_INVALID = 0,
>> +    SPICE_SURFACE_FMT_1_A = 1,
>> +    SPICE_SURFACE_FMT_8_A = 8,
>> +    SPICE_SURFACE_FMT_16_555 = 16,
>> +    SPICE_SURFACE_FMT_32_xRGB = 32,
>> +    SPICE_SURFACE_FMT_16_565 = 80,
>> +    SPICE_SURFACE_FMT_32_ARGB = 96;
>> +
>> +var SPICE_ROPD_INVERS_SRC = (1 << 0),
>> +    SPICE_ROPD_INVERS_BRUSH = (1 << 1),
>> +    SPICE_ROPD_INVERS_DEST = (1 << 2),
>> +    SPICE_ROPD_OP_PUT = (1 << 3),
>> +    SPICE_ROPD_OP_OR = (1 << 4),
>> +    SPICE_ROPD_OP_AND = (1 << 5),
>> +    SPICE_ROPD_OP_XOR = (1 << 6),
>> +    SPICE_ROPD_OP_BLACKNESS = (1 << 7),
>> +    SPICE_ROPD_OP_WHITENESS = (1 << 8),
>> +    SPICE_ROPD_OP_INVERS = (1 << 9),
>> +    SPICE_ROPD_INVERS_RES = (1 << 10),
>> +    SPICE_ROPD_MASK = 0x7ff;
>> +
>> +var LZ_IMAGE_TYPE_INVALID = 0,
>> +    LZ_IMAGE_TYPE_PLT1_LE = 1,
>> +    LZ_IMAGE_TYPE_PLT1_BE = 2,      // PLT stands for palette
>> +    LZ_IMAGE_TYPE_PLT4_LE = 3,
>> +    LZ_IMAGE_TYPE_PLT4_BE = 4,
>> +    LZ_IMAGE_TYPE_PLT8    = 5,
>> +    LZ_IMAGE_TYPE_RGB16   = 6,
>> +    LZ_IMAGE_TYPE_RGB24   = 7,
>> +    LZ_IMAGE_TYPE_RGB32   = 8,
>> +    LZ_IMAGE_TYPE_RGBA    = 9,
>> +    LZ_IMAGE_TYPE_XXXA    = 10;
>> +
>> +
>> +var QUIC_IMAGE_TYPE_INVALID = 0,
>> +    QUIC_IMAGE_TYPE_GRAY    = 1,
>> +    QUIC_IMAGE_TYPE_RGB16   = 2,
>> +    QUIC_IMAGE_TYPE_RGB24   = 3,
>> +    QUIC_IMAGE_TYPE_RGB32   = 4,
>> +    QUIC_IMAGE_TYPE_RGBA    = 5;
>> +
>> +var SPICE_INPUT_MOTION_ACK_BUNCH = 4;
>> +
>> +
>> +var SPICE_CURSOR_TYPE_ALPHA     = 0,
>> +    SPICE_CURSOR_TYPE_MONO      = 1,
>> +    SPICE_CURSOR_TYPE_COLOR4    = 2,
>> +    SPICE_CURSOR_TYPE_COLOR8    = 3,
>> +    SPICE_CURSOR_TYPE_COLOR16   = 4,
>> +    SPICE_CURSOR_TYPE_COLOR24   = 5,
>> +    SPICE_CURSOR_TYPE_COLOR32   = 6;
>> +
>> +var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1;
>> diff --git a/ui/js/spice/inputs.js b/ui/js/spice/inputs.js
>> new file mode 100644
>> index 0000000..4d3b28f
>> --- /dev/null
>> +++ b/ui/js/spice/inputs.js
>> @@ -0,0 +1,251 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> + ** Modifier Keystates
>> + **     These need to be tracked because focus in and out can get 
>> the keyboard
>> + **     out of sync.
>> + 
>> **------------------------------------------------------------------------*/
>> +var Shift_state = -1;
>> +var Ctrl_state = -1;
>> +var Alt_state = -1;
>> +var Meta_state = -1;
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceInputsConn
>> +**      Drive the Spice Inputs channel (e.g. mouse + keyboard)
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceInputsConn()
>> +{
>> +    SpiceConn.apply(this, arguments);
>> +
>> +    this.mousex = undefined;
>> +    this.mousey = undefined;
>> +    this.button_state = 0;
>> +    this.waiting_for_ack = 0;
>> +}
>> +
>> +SpiceInputsConn.prototype = Object.create(SpiceConn.prototype);
>> +SpiceInputsConn.prototype.process_channel_message = function(msg)
>> +{
>> +    if (msg.type == SPICE_MSG_INPUTS_INIT)
>> +    {
>> +        var inputs_init = new SpiceMsgInputsInit(msg.data);
>> +        this.keyboard_modifiers = inputs_init.keyboard_modifiers;
>> +        DEBUG > 1 && console.log("MsgInputsInit - modifier " + 
>> this.keyboard_modifiers);
>> +        // FIXME - We don't do anything with the keyboard modifiers...
>> +        return true;
>> +    }
>> +    if (msg.type == SPICE_MSG_INPUTS_KEY_MODIFIERS)
>> +    {
>> +        var key = new SpiceMsgInputsKeyModifiers(msg.data);
>> +        this.keyboard_modifiers = key.keyboard_modifiers;
>> +        DEBUG > 1 && console.log("MsgInputsKeyModifiers - modifier " 
>> + this.keyboard_modifiers);
>> +        // FIXME - We don't do anything with the keyboard modifiers...
>> +        return true;
>> +    }
>> +    if (msg.type == SPICE_MSG_INPUTS_MOUSE_MOTION_ACK)
>> +    {
>> +        DEBUG > 1 && console.log("mouse motion ack");
>> +        this.waiting_for_ack -= SPICE_INPUT_MOTION_ACK_BUNCH;
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>> +
>> +
>> +function handle_mousemove(e)
>> +{
>> +    var msg = new SpiceMiniData();
>> +    var move;
>> +    if (this.sc.mouse_mode == SPICE_MOUSE_MODE_CLIENT)
>> +    {
>> +        move = new SpiceMsgcMousePosition(this.sc, e)
>> +        msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_POSITION, move);
>> +    }
>> +    else
>> +    {
>> +        move = new SpiceMsgcMouseMotion(this.sc, e)
>> +        msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_MOTION, move);
>> +    }
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +    {
>> +        if (this.sc.inputs.waiting_for_ack < (2 * 
>> SPICE_INPUT_MOTION_ACK_BUNCH))
>> +        {
>> +            this.sc.inputs.send_msg(msg);
>> +            this.sc.inputs.waiting_for_ack++;
>> +        }
>> +        else
>> +        {
>> +            DEBUG > 0 && this.sc.log_info("Discarding mouse motion");
>> +        }
>> +    }
>> +}
>> +
>> +function handle_mousedown(e)
>> +{
>> +    var press = new SpiceMsgcMousePress(this.sc, e)
>> +    var msg = new SpiceMiniData();
>> +    msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_PRESS, press);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    e.preventDefault();
>> +}
>> +
>> +function handle_contextmenu(e)
>> +{
>> +    e.preventDefault();
>> +    return false;
>> +}
>> +
>> +function handle_mouseup(e)
>> +{
>> +    var release = new SpiceMsgcMouseRelease(this.sc, e)
>> +    var msg = new SpiceMiniData();
>> +    msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_RELEASE, release);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    e.preventDefault();
>> +}
>> +
>> +function handle_mousewheel(e)
>> +{
>> +    var press = new SpiceMsgcMousePress;
>> +    var release = new SpiceMsgcMouseRelease;
>> +    if (e.wheelDelta > 0)
>> +        press.button = release.button = SPICE_MOUSE_BUTTON_UP;
>> +    else
>> +        press.button = release.button = SPICE_MOUSE_BUTTON_DOWN;
>> +    press.buttons_state = 0;
>> +    release.buttons_state = 0;
>> +
>> +    var msg = new SpiceMiniData();
>> +    msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_PRESS, press);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    msg.build_msg(SPICE_MSGC_INPUTS_MOUSE_RELEASE, release);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    e.preventDefault();
>> +}
>> +
>> +function handle_keydown(e)
>> +{
>> +    var key = new SpiceMsgcKeyDown(e)
>> +    var msg = new SpiceMiniData();
>> +    check_and_update_modifiers(e, key.code, this.sc);
>> +    msg.build_msg(SPICE_MSGC_INPUTS_KEY_DOWN, key);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    e.preventDefault();
>> +}
>> +
>> +function handle_keyup(e)
>> +{
>> +    var key = new SpiceMsgcKeyUp(e)
>> +    var msg = new SpiceMiniData();
>> +    check_and_update_modifiers(e, key.code, this.sc);
>> +    msg.build_msg(SPICE_MSGC_INPUTS_KEY_UP, key);
>> +    if (this.sc && this.sc.inputs && this.sc.inputs.state === "ready")
>> +        this.sc.inputs.send_msg(msg);
>> +
>> +    e.preventDefault();
>> +}
>> +
>> +function update_modifier(state, code, sc)
>> +{
>> +    var msg = new SpiceMiniData();
>> +    if (!state)
>> +    {
>> +        var key = new SpiceMsgcKeyUp()
>> +        key.code =(0x80|code);
>> +        msg.build_msg(SPICE_MSGC_INPUTS_KEY_UP, key);
>> +    }
>> +    else
>> +    {
>> +        var key = new SpiceMsgcKeyDown()
>> +        key.code = code;
>> +        msg.build_msg(SPICE_MSGC_INPUTS_KEY_DOWN, key);
>> +    }
>> +
>> +    sc.inputs.send_msg(msg);
>> +}
>> +
>> +function check_and_update_modifiers(e, code, sc)
>> +{
>> +    if (Shift_state === -1)
>> +    {
>> +        Shift_state = e.shiftKey;
>> +        Ctrl_state = e.ctrlKey;
>> +        Alt_state = e.altKey;
>> +        Meta_state = e.metaKey;
>> +    }
>> +
>> +    if (code === KEY_ShiftL)
>> +        Shift_state = true;
>> +    else if (code === KEY_Alt)
>> +        Alt_state = true;
>> +    else if (code === KEY_LCtrl)
>> +        Ctrl_state = true;
>> +    else if (code === 0xE0B5)
>> +        Meta_state = true;
>> +    else if (code === (0x80|KEY_ShiftL))
>> +        Shift_state = false;
>> +    else if (code === (0x80|KEY_Alt))
>> +        Alt_state = false;
>> +    else if (code === (0x80|KEY_LCtrl))
>> +        Ctrl_state = false;
>> +    else if (code === (0x80|0xE0B5))
>> +        Meta_state = false;
>> +
>> +    if (sc && sc.inputs && sc.inputs.state === "ready")
>> +    {
>> +        if (Shift_state != e.shiftKey)
>> +        {
>> +            console.log("Shift state out of sync");
>> +            update_modifier(e.shiftKey, KEY_ShiftL, sc);
>> +            Shift_state = e.shiftKey;
>> +        }
>> +        if (Alt_state != e.altKey)
>> +        {
>> +            console.log("Alt state out of sync");
>> +            update_modifier(e.altKey, KEY_Alt, sc);
>> +            Alt_state = e.altKey;
>> +        }
>> +        if (Ctrl_state != e.ctrlKey)
>> +        {
>> +            console.log("Ctrl state out of sync");
>> +            update_modifier(e.ctrlKey, KEY_LCtrl, sc);
>> +            Ctrl_state = e.ctrlKey;
>> +        }
>> +        if (Meta_state != e.metaKey)
>> +        {
>> +            console.log("Meta state out of sync");
>> +            update_modifier(e.metaKey, 0xE0B5, sc);
>> +            Meta_state = e.metaKey;
>> +        }
>> +    }
>> +}
>> diff --git a/ui/js/spice/jsbn.js b/ui/js/spice/jsbn.js
>> new file mode 100644
>> index 0000000..d88ec54
>> --- /dev/null
>> +++ b/ui/js/spice/jsbn.js
>> @@ -0,0 +1,589 @@
>> +// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by 
>> Jeremy White on 6/1/2012
>> +
>> +/*
>> + * Copyright (c) 2003-2005  Tom Wu
>> + * All Rights Reserved.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining
>> + * a copy of this software and associated documentation files (the
>> + * "Software"), to deal in the Software without restriction, including
>> + * without limitation the rights to use, copy, modify, merge, publish,
>> + * distribute, sublicense, and/or sell copies of the Software, and to
>> + * permit persons to whom the Software is furnished to do so, 
>> subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be
>> + * included in all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
>> + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
>> + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
>> + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES 
>> WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT 
>> ADVISED OF
>> + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, 
>> ARISING OUT
>> + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + *
>> + * In addition, the following condition applies:
>> + *
>> + * All redistributions must retain an intact copy of this copyright 
>> notice
>> + * and disclaimer.
>> + */
>> +
>> +
>> +// Basic JavaScript BN library - subset useful for RSA encryption.
>> +
>> +// Bits per digit
>> +var dbits;
>> +
>> +// JavaScript engine analysis
>> +var canary = 0xdeadbeefcafe;
>> +var j_lm = ((canary&0xffffff)==0xefcafe);
>> +
>> +// (public) Constructor
>> +function BigInteger(a,b,c) {
>> +  if(a != null)
>> +    if("number" == typeof a) this.fromNumber(a,b,c);
>> +    else if(b == null && "string" != typeof a) this.fromString(a,256);
>> +    else this.fromString(a,b);
>> +}
>> +
>> +// return new, unset BigInteger
>> +function nbi() { return new BigInteger(null); }
>> +
>> +// am: Compute w_j += (x*this_i), propagate carries,
>> +// c is initial carry, returns final carry.
>> +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
>> +// We need to select the fastest one that works in this environment.
>> +
>> +// am1: use a single mult and divide to get the high bits,
>> +// max digit bits should be 26 because
>> +// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
>> +function am1(i,x,w,j,c,n) {
>> +  while(--n >= 0) {
>> +    var v = x*this[i++]+w[j]+c;
>> +    c = Math.floor(v/0x4000000);
>> +    w[j++] = v&0x3ffffff;
>> +  }
>> +  return c;
>> +}
>> +// am2 avoids a big mult-and-extract completely.
>> +// Max digit bits should be <= 30 because we do bitwise ops
>> +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
>> +function am2(i,x,w,j,c,n) {
>> +  var xl = x&0x7fff, xh = x>>15;
>> +  while(--n >= 0) {
>> +    var l = this[i]&0x7fff;
>> +    var h = this[i++]>>15;
>> +    var m = xh*l+h*xl;
>> +    l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
>> +    c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
>> +    w[j++] = l&0x3fffffff;
>> +  }
>> +  return c;
>> +}
>> +// Alternately, set max digit bits to 28 since some
>> +// browsers slow down when dealing with 32-bit numbers.
>> +function am3(i,x,w,j,c,n) {
>> +  var xl = x&0x3fff, xh = x>>14;
>> +  while(--n >= 0) {
>> +    var l = this[i]&0x3fff;
>> +    var h = this[i++]>>14;
>> +    var m = xh*l+h*xl;
>> +    l = xl*l+((m&0x3fff)<<14)+w[j]+c;
>> +    c = (l>>28)+(m>>14)+xh*h;
>> +    w[j++] = l&0xfffffff;
>> +  }
>> +  return c;
>> +}
>> +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
>> +  BigInteger.prototype.am = am2;
>> +  dbits = 30;
>> +}
>> +else if(j_lm && (navigator.appName != "Netscape")) {
>> +  BigInteger.prototype.am = am1;
>> +  dbits = 26;
>> +}
>> +else { // Mozilla/Netscape seems to prefer am3
>> +  BigInteger.prototype.am = am3;
>> +  dbits = 28;
>> +}
>> +
>> +BigInteger.prototype.DB = dbits;
>> +BigInteger.prototype.DM = ((1<<dbits)-1);
>> +BigInteger.prototype.DV = (1<<dbits);
>> +
>> +var BI_FP = 52;
>> +BigInteger.prototype.FV = Math.pow(2,BI_FP);
>> +BigInteger.prototype.F1 = BI_FP-dbits;
>> +BigInteger.prototype.F2 = 2*dbits-BI_FP;
>> +
>> +// Digit conversions
>> +var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
>> +var BI_RC = new Array();
>> +var rr,vv;
>> +rr = "0".charCodeAt(0);
>> +for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
>> +rr = "a".charCodeAt(0);
>> +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
>> +rr = "A".charCodeAt(0);
>> +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
>> +
>> +function int2char(n) { return BI_RM.charAt(n); }
>> +function intAt(s,i) {
>> +  var c = BI_RC[s.charCodeAt(i)];
>> +  return (c==null)?-1:c;
>> +}
>> +
>> +// (protected) copy this to r
>> +function bnpCopyTo(r) {
>> +  for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
>> +  r.t = this.t;
>> +  r.s = this.s;
>> +}
>> +
>> +// (protected) set from integer value x, -DV <= x < DV
>> +function bnpFromInt(x) {
>> +  this.t = 1;
>> +  this.s = (x<0)?-1:0;
>> +  if(x > 0) this[0] = x;
>> +  else if(x < -1) this[0] = x+DV;
>> +  else this.t = 0;
>> +}
>> +
>> +// return bigint initialized to value
>> +function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
>> +
>> +// (protected) set from string and radix
>> +function bnpFromString(s,b) {
>> +  var k;
>> +  if(b == 16) k = 4;
>> +  else if(b == 8) k = 3;
>> +  else if(b == 256) k = 8; // byte array
>> +  else if(b == 2) k = 1;
>> +  else if(b == 32) k = 5;
>> +  else if(b == 4) k = 2;
>> +  else { this.fromRadix(s,b); return; }
>> +  this.t = 0;
>> +  this.s = 0;
>> +  var i = s.length, mi = false, sh = 0;
>> +  while(--i >= 0) {
>> +    var x = (k==8)?s[i]&0xff:intAt(s,i);
>> +    if(x < 0) {
>> +      if(s.charAt(i) == "-") mi = true;
>> +      continue;
>> +    }
>> +    mi = false;
>> +    if(sh == 0)
>> +      this[this.t++] = x;
>> +    else if(sh+k > this.DB) {
>> +      this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
>> +      this[this.t++] = (x>>(this.DB-sh));
>> +    }
>> +    else
>> +      this[this.t-1] |= x<<sh;
>> +    sh += k;
>> +    if(sh >= this.DB) sh -= this.DB;
>> +  }
>> +  if(k == 8 && (s[0]&0x80) != 0) {
>> +    this.s = -1;
>> +    if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
>> +  }
>> +  this.clamp();
>> +  if(mi) BigInteger.ZERO.subTo(this,this);
>> +}
>> +
>> +// (protected) clamp off excess high words
>> +function bnpClamp() {
>> +  var c = this.s&this.DM;
>> +  while(this.t > 0 && this[this.t-1] == c) --this.t;
>> +}
>> +
>> +// (public) return string representation in given radix
>> +function bnToString(b) {
>> +  if(this.s < 0) return "-"+this.negate().toString(b);
>> +  var k;
>> +  if(b == 16) k = 4;
>> +  else if(b == 8) k = 3;
>> +  else if(b == 2) k = 1;
>> +  else if(b == 32) k = 5;
>> +  else if(b == 4) k = 2;
>> +  else return this.toRadix(b);
>> +  var km = (1<<k)-1, d, m = false, r = "", i = this.t;
>> +  var p = this.DB-(i*this.DB)%k;
>> +  if(i-- > 0) {
>> +    if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = 
>> int2char(d); }
>> +    while(i >= 0) {
>> +      if(p < k) {
>> +        d = (this[i]&((1<<p)-1))<<(k-p);
>> +        d |= this[--i]>>(p+=this.DB-k);
>> +      }
>> +      else {
>> +        d = (this[i]>>(p-=k))&km;
>> +        if(p <= 0) { p += this.DB; --i; }
>> +      }
>> +      if(d > 0) m = true;
>> +      if(m) r += int2char(d);
>> +    }
>> +  }
>> +  return m?r:"0";
>> +}
>> +
>> +// (public) -this
>> +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); 
>> return r; }
>> +
>> +// (public) |this|
>> +function bnAbs() { return (this.s<0)?this.negate():this; }
>> +
>> +// (public) return + if this > a, - if this < a, 0 if equal
>> +function bnCompareTo(a) {
>> +  var r = this.s-a.s;
>> +  if(r != 0) return r;
>> +  var i = this.t;
>> +  r = i-a.t;
>> +  if(r != 0) return r;
>> +  while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
>> +  return 0;
>> +}
>> +
>> +// returns bit length of the integer x
>> +function nbits(x) {
>> +  var r = 1, t;
>> +  if((t=x>>>16) != 0) { x = t; r += 16; }
>> +  if((t=x>>8) != 0) { x = t; r += 8; }
>> +  if((t=x>>4) != 0) { x = t; r += 4; }
>> +  if((t=x>>2) != 0) { x = t; r += 2; }
>> +  if((t=x>>1) != 0) { x = t; r += 1; }
>> +  return r;
>> +}
>> +
>> +// (public) return the number of bits in "this"
>> +function bnBitLength() {
>> +  if(this.t <= 0) return 0;
>> +  return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
>> +}
>> +
>> +// (protected) r = this << n*DB
>> +function bnpDLShiftTo(n,r) {
>> +  var i;
>> +  for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
>> +  for(i = n-1; i >= 0; --i) r[i] = 0;
>> +  r.t = this.t+n;
>> +  r.s = this.s;
>> +}
>> +
>> +// (protected) r = this >> n*DB
>> +function bnpDRShiftTo(n,r) {
>> +  for(var i = n; i < this.t; ++i) r[i-n] = this[i];
>> +  r.t = Math.max(this.t-n,0);
>> +  r.s = this.s;
>> +}
>> +
>> +// (protected) r = this << n
>> +function bnpLShiftTo(n,r) {
>> +  var bs = n%this.DB;
>> +  var cbs = this.DB-bs;
>> +  var bm = (1<<cbs)-1;
>> +  var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
>> +  for(i = this.t-1; i >= 0; --i) {
>> +    r[i+ds+1] = (this[i]>>cbs)|c;
>> +    c = (this[i]&bm)<<bs;
>> +  }
>> +  for(i = ds-1; i >= 0; --i) r[i] = 0;
>> +  r[ds] = c;
>> +  r.t = this.t+ds+1;
>> +  r.s = this.s;
>> +  r.clamp();
>> +}
>> +
>> +// (protected) r = this >> n
>> +function bnpRShiftTo(n,r) {
>> +  r.s = this.s;
>> +  var ds = Math.floor(n/this.DB);
>> +  if(ds >= this.t) { r.t = 0; return; }
>> +  var bs = n%this.DB;
>> +  var cbs = this.DB-bs;
>> +  var bm = (1<<bs)-1;
>> +  r[0] = this[ds]>>bs;
>> +  for(var i = ds+1; i < this.t; ++i) {
>> +    r[i-ds-1] |= (this[i]&bm)<<cbs;
>> +    r[i-ds] = this[i]>>bs;
>> +  }
>> +  if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
>> +  r.t = this.t-ds;
>> +  r.clamp();
>> +}
>> +
>> +// (protected) r = this - a
>> +function bnpSubTo(a,r) {
>> +  var i = 0, c = 0, m = Math.min(a.t,this.t);
>> +  while(i < m) {
>> +    c += this[i]-a[i];
>> +    r[i++] = c&this.DM;
>> +    c >>= this.DB;
>> +  }
>> +  if(a.t < this.t) {
>> +    c -= a.s;
>> +    while(i < this.t) {
>> +      c += this[i];
>> +      r[i++] = c&this.DM;
>> +      c >>= this.DB;
>> +    }
>> +    c += this.s;
>> +  }
>> +  else {
>> +    c += this.s;
>> +    while(i < a.t) {
>> +      c -= a[i];
>> +      r[i++] = c&this.DM;
>> +      c >>= this.DB;
>> +    }
>> +    c -= a.s;
>> +  }
>> +  r.s = (c<0)?-1:0;
>> +  if(c < -1) r[i++] = this.DV+c;
>> +  else if(c > 0) r[i++] = c;
>> +  r.t = i;
>> +  r.clamp();
>> +}
>> +
>> +// (protected) r = this * a, r != this,a (HAC 14.12)
>> +// "this" should be the larger one if appropriate.
>> +function bnpMultiplyTo(a,r) {
>> +  var x = this.abs(), y = a.abs();
>> +  var i = x.t;
>> +  r.t = i+y.t;
>> +  while(--i >= 0) r[i] = 0;
>> +  for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
>> +  r.s = 0;
>> +  r.clamp();
>> +  if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
>> +}
>> +
>> +// (protected) r = this^2, r != this (HAC 14.16)
>> +function bnpSquareTo(r) {
>> +  var x = this.abs();
>> +  var i = r.t = 2*x.t;
>> +  while(--i >= 0) r[i] = 0;
>> +  for(i = 0; i < x.t-1; ++i) {
>> +    var c = x.am(i,x[i],r,2*i,0,1);
>> +    if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
>> +      r[i+x.t] -= x.DV;
>> +      r[i+x.t+1] = 1;
>> +    }
>> +  }
>> +  if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
>> +  r.s = 0;
>> +  r.clamp();
>> +}
>> +
>> +// (protected) divide this by m, quotient and remainder to q, r (HAC 
>> 14.20)
>> +// r != q, this != m.  q or r may be null.
>> +function bnpDivRemTo(m,q,r) {
>> +  var pm = m.abs();
>> +  if(pm.t <= 0) return;
>> +  var pt = this.abs();
>> +  if(pt.t < pm.t) {
>> +    if(q != null) q.fromInt(0);
>> +    if(r != null) this.copyTo(r);
>> +    return;
>> +  }
>> +  if(r == null) r = nbi();
>> +  var y = nbi(), ts = this.s, ms = m.s;
>> +  var nsh = this.DB-nbits(pm[pm.t-1]);    // normalize modulus
>> +  if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
>> +  else { pm.copyTo(y); pt.copyTo(r); }
>> +  var ys = y.t;
>> +  var y0 = y[ys-1];
>> +  if(y0 == 0) return;
>> +  var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
>> +  var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
>> +  var i = r.t, j = i-ys, t = (q==null)?nbi():q;
>> +  y.dlShiftTo(j,t);
>> +  if(r.compareTo(t) >= 0) {
>> +    r[r.t++] = 1;
>> +    r.subTo(t,r);
>> +  }
>> +  BigInteger.ONE.dlShiftTo(ys,t);
>> +  t.subTo(y,y);    // "negative" y so we can replace sub with am later
>> +  while(y.t < ys) y[y.t++] = 0;
>> +  while(--j >= 0) {
>> +    // Estimate quotient digit
>> +    var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
>> +    if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) {    // Try it out
>> +      y.dlShiftTo(j,t);
>> +      r.subTo(t,r);
>> +      while(r[i] < --qd) r.subTo(t,r);
>> +    }
>> +  }
>> +  if(q != null) {
>> +    r.drShiftTo(ys,q);
>> +    if(ts != ms) BigInteger.ZERO.subTo(q,q);
>> +  }
>> +  r.t = ys;
>> +  r.clamp();
>> +  if(nsh > 0) r.rShiftTo(nsh,r);    // Denormalize remainder
>> +  if(ts < 0) BigInteger.ZERO.subTo(r,r);
>> +}
>> +
>> +// (public) this mod a
>> +function bnMod(a) {
>> +  var r = nbi();
>> +  this.abs().divRemTo(a,null,r);
>> +  if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
>> +  return r;
>> +}
>> +
>> +// Modular reduction using "classic" algorithm
>> +function Classic(m) { this.m = m; }
>> +function cConvert(x) {
>> +  if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
>> +  else return x;
>> +}
>> +function cRevert(x) { return x; }
>> +function cReduce(x) { x.divRemTo(this.m,null,x); }
>> +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
>> +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
>> +
>> +Classic.prototype.convert = cConvert;
>> +Classic.prototype.revert = cRevert;
>> +Classic.prototype.reduce = cReduce;
>> +Classic.prototype.mulTo = cMulTo;
>> +Classic.prototype.sqrTo = cSqrTo;
>> +
>> +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
>> +// justification:
>> +//         xy == 1 (mod m)
>> +//         xy =  1+km
>> +//   xy(2-xy) = (1+km)(1-km)
>> +// x[y(2-xy)] = 1-k^2m^2
>> +// x[y(2-xy)] == 1 (mod m^2)
>> +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
>> +// should reduce x and y(2-xy) by m^2 at each step to keep size 
>> bounded.
>> +// JS multiply "overflows" differently from C/C++, so care is needed 
>> here.
>> +function bnpInvDigit() {
>> +  if(this.t < 1) return 0;
>> +  var x = this[0];
>> +  if((x&1) == 0) return 0;
>> +  var y = x&3;        // y == 1/x mod 2^2
>> +  y = (y*(2-(x&0xf)*y))&0xf;    // y == 1/x mod 2^4
>> +  y = (y*(2-(x&0xff)*y))&0xff;    // y == 1/x mod 2^8
>> +  y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff;    // y == 1/x mod 2^16
>> +  // last step - calculate inverse mod DV directly;
>> +  // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
>> +  y = (y*(2-x*y%this.DV))%this.DV;        // y == 1/x mod 2^dbits
>> +  // we really want the negative inverse, and -DV < y < DV
>> +  return (y>0)?this.DV-y:-y;
>> +}
>> +
>> +// Montgomery reduction
>> +function Montgomery(m) {
>> +  this.m = m;
>> +  this.mp = m.invDigit();
>> +  this.mpl = this.mp&0x7fff;
>> +  this.mph = this.mp>>15;
>> +  this.um = (1<<(m.DB-15))-1;
>> +  this.mt2 = 2*m.t;
>> +}
>> +
>> +// xR mod m
>> +function montConvert(x) {
>> +  var r = nbi();
>> +  x.abs().dlShiftTo(this.m.t,r);
>> +  r.divRemTo(this.m,null,r);
>> +  if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
>> +  return r;
>> +}
>> +
>> +// x/R mod m
>> +function montRevert(x) {
>> +  var r = nbi();
>> +  x.copyTo(r);
>> +  this.reduce(r);
>> +  return r;
>> +}
>> +
>> +// x = x/R mod m (HAC 14.32)
>> +function montReduce(x) {
>> +  while(x.t <= this.mt2)    // pad x so am has enough room later
>> +    x[x.t++] = 0;
>> +  for(var i = 0; i < this.m.t; ++i) {
>> +    // faster way of calculating u0 = x[i]*mp mod DV
>> +    var j = x[i]&0x7fff;
>> +    var u0 = 
>> (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
>> +    // use am to combine the multiply-shift-add into one call
>> +    j = i+this.m.t;
>> +    x[j] += this.m.am(0,u0,x,i,0,this.m.t);
>> +    // propagate carry
>> +    while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
>> +  }
>> +  x.clamp();
>> +  x.drShiftTo(this.m.t,x);
>> +  if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
>> +}
>> +
>> +// r = "x^2/R mod m"; x != r
>> +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
>> +
>> +// r = "xy/R mod m"; x,y != r
>> +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
>> +
>> +Montgomery.prototype.convert = montConvert;
>> +Montgomery.prototype.revert = montRevert;
>> +Montgomery.prototype.reduce = montReduce;
>> +Montgomery.prototype.mulTo = montMulTo;
>> +Montgomery.prototype.sqrTo = montSqrTo;
>> +
>> +// (protected) true iff this is even
>> +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
>> +
>> +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
>> +function bnpExp(e,z) {
>> +  if(e > 0xffffffff || e < 1) return BigInteger.ONE;
>> +  var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
>> +  g.copyTo(r);
>> +  while(--i >= 0) {
>> +    z.sqrTo(r,r2);
>> +    if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
>> +    else { var t = r; r = r2; r2 = t; }
>> +  }
>> +  return z.revert(r);
>> +}
>> +
>> +// (public) this^e % m, 0 <= e < 2^32
>> +function bnModPowInt(e,m) {
>> +  var z;
>> +  if(e < 256 || m.isEven()) z = new Classic(m); else z = new 
>> Montgomery(m);
>> +  return this.exp(e,z);
>> +}
>> +
>> +// protected
>> +BigInteger.prototype.copyTo = bnpCopyTo;
>> +BigInteger.prototype.fromInt = bnpFromInt;
>> +BigInteger.prototype.fromString = bnpFromString;
>> +BigInteger.prototype.clamp = bnpClamp;
>> +BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
>> +BigInteger.prototype.drShiftTo = bnpDRShiftTo;
>> +BigInteger.prototype.lShiftTo = bnpLShiftTo;
>> +BigInteger.prototype.rShiftTo = bnpRShiftTo;
>> +BigInteger.prototype.subTo = bnpSubTo;
>> +BigInteger.prototype.multiplyTo = bnpMultiplyTo;
>> +BigInteger.prototype.squareTo = bnpSquareTo;
>> +BigInteger.prototype.divRemTo = bnpDivRemTo;
>> +BigInteger.prototype.invDigit = bnpInvDigit;
>> +BigInteger.prototype.isEven = bnpIsEven;
>> +BigInteger.prototype.exp = bnpExp;
>> +
>> +// public
>> +BigInteger.prototype.toString = bnToString;
>> +BigInteger.prototype.negate = bnNegate;
>> +BigInteger.prototype.abs = bnAbs;
>> +BigInteger.prototype.compareTo = bnCompareTo;
>> +BigInteger.prototype.bitLength = bnBitLength;
>> +BigInteger.prototype.mod = bnMod;
>> +BigInteger.prototype.modPowInt = bnModPowInt;
>> +
>> +// "constants"
>> +BigInteger.ZERO = nbv(0);
>> +BigInteger.ONE = nbv(1);
>> diff --git a/ui/js/spice/lz.js b/ui/js/spice/lz.js
>> new file mode 100644
>> index 0000000..4292eac
>> --- /dev/null
>> +++ b/ui/js/spice/lz.js
>> @@ -0,0 +1,166 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  lz.js
>> +**      Functions for handling SPICE_IMAGE_TYPE_LZ_RGB
>> +**  Adapted from lz.c .
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function lz_rgb32_decompress(in_buf, at, out_buf, type, default_alpha)
>> +{
>> +    var encoder = at;
>> +    var op = 0;
>> +    var ctrl;
>> +    var ctr = 0;
>> +
>> +    for (ctrl = in_buf[encoder++]; (op * 4) < out_buf.length; ctrl = 
>> in_buf[encoder++])
>> +    {
>> +        var ref = op;
>> +        var len = ctrl >> 5;
>> +        var ofs = (ctrl & 31) << 8;
>> +
>> +//if (type == LZ_IMAGE_TYPE_RGBA)
>> +//console.log(ctr++ + ": from " + (encoder + 28) + ", ctrl " + ctrl 
>> + ", len " + len + ", ofs " + ofs + ", op " + op);
>> +        if (ctrl >= 32) {
>> +
>> +            var code;
>> +            len--;
>> +
>> +            if (len == 7 - 1) {
>> +                do {
>> +                    code = in_buf[encoder++];
>> +                    len += code;
>> +                } while (code == 255);
>> +            }
>> +            code = in_buf[encoder++];
>> +            ofs += code;
>> +
>> +
>> +            if (code == 255) {
>> +                if ((ofs - code) == (31 << 8)) {
>> +                    ofs = in_buf[encoder++] << 8;
>> +                    ofs += in_buf[encoder++];
>> +                    ofs += 8191;
>> +                }
>> +            }
>> +            len += 1;
>> +            if (type == LZ_IMAGE_TYPE_RGBA)
>> +                len += 2;
>> +
>> +            ofs += 1;
>> +
>> +            ref -= ofs;
>> +            if (ref == (op - 1)) {
>> +                var b = ref;
>> +//if (type == LZ_IMAGE_TYPE_RGBA) console.log("alpha " + 
>> out_buf[(b*4)+3] + " dupped into pixel " + op + " through pixel " + 
>> (op + len));
>> +                for (; len; --len) {
>> +                    if (type == LZ_IMAGE_TYPE_RGBA)
>> +                    {
>> +                        out_buf[(op*4) + 3] = out_buf[(b*4)+3];
>> +                    }
>> +                    else
>> +                    {
>> +                        for (i = 0; i < 4; i++)
>> +                            out_buf[(op*4) + i] = out_buf[(b*4)+i];
>> +                    }
>> +                    op++;
>> +                }
>> +            } else {
>> +//if (type == LZ_IMAGE_TYPE_RGBA) console.log("alpha copied to pixel 
>> " + op + " through " + (op + len) + " from " + ref);
>> +                for (; len; --len) {
>> +                    if (type == LZ_IMAGE_TYPE_RGBA)
>> +                    {
>> +                        out_buf[(op*4) + 3] = out_buf[(ref*4)+3];
>> +                    }
>> +                    else
>> +                    {
>> +                        for (i = 0; i < 4; i++)
>> +                            out_buf[(op*4) + i] = out_buf[(ref*4)+i];
>> +                    }
>> +                    op++; ref++;
>> +                }
>> +            }
>> +        } else {
>> +            ctrl++;
>> +
>> +            if (type == LZ_IMAGE_TYPE_RGBA)
>> +            {
>> +//console.log("alpha " + in_buf[encoder] + " set into pixel " + op);
>> +                out_buf[(op*4) + 3] = in_buf[encoder++];
>> +            }
>> +            else
>> +            {
>> +                out_buf[(op*4) + 0] = in_buf[encoder + 2];
>> +                out_buf[(op*4) + 1] = in_buf[encoder + 1];
>> +                out_buf[(op*4) + 2] = in_buf[encoder + 0];
>> +                if (default_alpha)
>> +                    out_buf[(op*4) + 3] = 255;
>> +                encoder += 3;
>> +            }
>> +            op++;
>> +
>> +
>> +            for (--ctrl; ctrl; ctrl--) {
>> +                if (type == LZ_IMAGE_TYPE_RGBA)
>> +                {
>> +//console.log("alpha " + in_buf[encoder] + " set into pixel " + op);
>> +                    out_buf[(op*4) + 3] = in_buf[encoder++];
>> +                }
>> +                else
>> +                {
>> +                    out_buf[(op*4) + 0] = in_buf[encoder + 2];
>> +                    out_buf[(op*4) + 1] = in_buf[encoder + 1];
>> +                    out_buf[(op*4) + 2] = in_buf[encoder + 0];
>> +                    if (default_alpha)
>> +                        out_buf[(op*4) + 3] = 255;
>> +                    encoder += 3;
>> +                }
>> +                op++;
>> +            }
>> +        }
>> +
>> +    }
>> +    return encoder - 1;
>> +}
>> +
>> +function convert_spice_lz_to_web(context, lz_image)
>> +{
>> +    var at;
>> +    if (lz_image.type === LZ_IMAGE_TYPE_RGB32 || lz_image.type === 
>> LZ_IMAGE_TYPE_RGBA)
>> +    {
>> +        var u8 = new Uint8Array(lz_image.data);
>> +        var ret = context.createImageData(lz_image.width, 
>> lz_image.height);
>> +
>> +        at = lz_rgb32_decompress(u8, 0, ret.data, 
>> LZ_IMAGE_TYPE_RGB32, lz_image.type != LZ_IMAGE_TYPE_RGBA);
>> +        if (lz_image.type == LZ_IMAGE_TYPE_RGBA)
>> +            lz_rgb32_decompress(u8, at, ret.data, 
>> LZ_IMAGE_TYPE_RGBA, false);
>> +    }
>> +    else if (lz_image.type === LZ_IMAGE_TYPE_XXXA)
>> +    {
>> +        var u8 = new Uint8Array(lz_image.data);
>> +        var ret = context.createImageData(lz_image.width, 
>> lz_image.height);
>> +        lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGBA, 
>> false);
>> +    }
>> +    else
>> +        return undefined;
>> +
>> +    return ret;
>> +}
>> diff --git a/ui/js/spice/main.js b/ui/js/spice/main.js
>> new file mode 100644
>> index 0000000..e7eb1ed
>> --- /dev/null
>> +++ b/ui/js/spice/main.js
>> @@ -0,0 +1,176 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceMainConn
>> +**      This is the master Javascript class for establishing and
>> +**  managing a connection to a Spice Server.
>> +**
>> +**      Invocation:  You must pass an object with properties as 
>> follows:
>> +**          uri         (required)  Uri of a WebSocket listener that is
>> +**                                  connected to a spice server.
>> +**          password    (required)  Password to send to the spice 
>> server
>> +**          message_id  (optional)  Identifier of an element in the DOM
>> +**                                  where SpiceConn will write 
>> messages.
>> +**                                  It will use classes 
>> spice-messages-x,
>> +**                                  where x is one of info, warning, 
>> or error.
>> +**          screen_id   (optional)  Identifier of an element in the DOM
>> +**                                  where SpiceConn will create any new
>> +**                                  client screens.  This is the 
>> main UI.
>> +**          dump_id     (optional)  If given, an element to use for
>> +**                                  dumping every single image + 
>> canvas drawn.
>> +**                                  Sometimes useful for debugging.
>> +**          onerror     (optional)  If given, a function to receive 
>> async
>> +**                                  errors.  Note that you should 
>> also catch
>> +**                                  errors for ones that occur inline
>> +**
>> +**  Throws error if there are troubles.  Requires a modern (by 2012 
>> standards)
>> +**      browser, including WebSocket and WebSocket.binaryType == 
>> arraybuffer
>> +**
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceMainConn()
>> +{
>> +    if (typeof WebSocket === "undefined")
>> +        throw new Error("WebSocket unavailable.  You need to use a 
>> different browser.");
>> +
>> +    SpiceConn.apply(this, arguments);
>> +
>> +}
>> +
>> +SpiceMainConn.prototype = Object.create(SpiceConn.prototype);
>> +SpiceMainConn.prototype.process_channel_message = function(msg)
>> +{
>> +    if (msg.type == SPICE_MSG_MAIN_INIT)
>> +    {
>> +        this.log_info("Connected to " + this.ws.url);
>> +        this.main_init = new SpiceMsgMainInit(msg.data);
>> +        this.connection_id = this.main_init.session_id;
>> +
>> +        if (DEBUG > 0)
>> +        {
>> +            // FIXME - there is a lot here we don't handle; mouse 
>> modes, agent,
>> +            //          ram_hint, multi_media_time
>> +            this.log_info("session id "                 + 
>> this.main_init.session_id +
>> +                          " ; display_channels_hint "   + 
>> this.main_init.display_channels_hint +
>> +                          " ; supported_mouse_modes "   + 
>> this.main_init.supported_mouse_modes +
>> +                          " ; current_mouse_mode "      + 
>> this.main_init.current_mouse_mode +
>> +                          " ; agent_connected "         + 
>> this.main_init.agent_connected +
>> +                          " ; agent_tokens "            + 
>> this.main_init.agent_tokens +
>> +                          " ; multi_media_time "        + 
>> this.main_init.multi_media_time +
>> +                          " ; ram_hint "                + 
>> this.main_init.ram_hint);
>> +        }
>> +
>> +        this.mouse_mode = this.main_init.current_mouse_mode;
>> +        if (this.main_init.current_mouse_mode != 
>> SPICE_MOUSE_MODE_CLIENT &&
>> +            (this.main_init.supported_mouse_modes & 
>> SPICE_MOUSE_MODE_CLIENT))
>> +        {
>> +            var mode_request = new 
>> SpiceMsgcMainMouseModeRequest(SPICE_MOUSE_MODE_CLIENT);
>> +            var mr = new SpiceMiniData();
>> +            mr.build_msg(SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, 
>> mode_request);
>> +            this.send_msg(mr);
>> +        }
>> +
>> +        var attach = new SpiceMiniData;
>> +        attach.type = SPICE_MSGC_MAIN_ATTACH_CHANNELS;
>> +        attach.size = attach.buffer_size();
>> +        this.send_msg(attach);
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_MAIN_MOUSE_MODE)
>> +    {
>> +        var mode = new SpiceMsgMainMouseMode(msg.data);
>> +        DEBUG > 0 && this.log_info("Mouse supported modes " + 
>> mode.supported_modes + "; current " + mode.current_mode);
>> +        this.mouse_mode = mode.current_mode;
>> +        if (this.inputs)
>> +            this.inputs.mouse_mode = mode.current_mode;
>> +        return true;
>> +    }
>> +
>> +    if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST)
>> +    {
>> +        var i;
>> +        var chans;
>> +        DEBUG > 0 && console.log("channels");
>> +        chans = new SpiceMsgChannels(msg.data);
>> +        for (i = 0; i < chans.channels.length; i++)
>> +        {
>> +            var conn = {
>> +                        uri: this.ws.url,
>> +                        parent: this,
>> +                        connection_id : this.connection_id,
>> +                        type : chans.channels[i].type,
>> +                        chan_id : chans.channels[i].id
>> +                    };
>> +            if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY)
>> +                this.display = new SpiceDisplayConn(conn);
>> +            else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS)
>> +            {
>> +                this.inputs = new SpiceInputsConn(conn);
>> +                this.inputs.mouse_mode = this.mouse_mode;
>> +            }
>> +            else if (chans.channels[i].type == SPICE_CHANNEL_CURSOR)
>> +                this.cursor = new SpiceCursorConn(conn);
>> +            else
>> +            {
>> +                this.log_err("Channel type " + 
>> chans.channels[i].type + " unknown.");
>> +                if (! ("extra_channels" in this))
>> +                    this.extra_channels = [];
>> +                this.extra_channels[i] = new SpiceConn(conn);
>> +            }
>> +
>> +        }
>> +
>> +        return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +SpiceMainConn.prototype.stop = function(msg)
>> +{
>> +    this.state = "closing";
>> +
>> +    if (this.inputs)
>> +    {
>> +        this.inputs.cleanup();
>> +        this.inputs = undefined;
>> +    }
>> +
>> +    if (this.cursor)
>> +    {
>> +        this.cursor.cleanup();
>> +        this.cursor = undefined;
>> +    }
>> +
>> +    if (this.display)
>> +    {
>> +        this.display.cleanup();
>> +        this.display.destroy_surfaces();
>> +        this.display = undefined;
>> +    }
>> +
>> +    this.cleanup();
>> +
>> +    if ("extra_channels" in this)
>> +        for (var e in this.extra_channels)
>> +            this.extra_channels[e].cleanup();
>> +    this.extra_channels = undefined;
>> +}
>> diff --git a/ui/js/spice/png.js b/ui/js/spice/png.js
>> new file mode 100644
>> index 0000000..6a26151
>> --- /dev/null
>> +++ b/ui/js/spice/png.js
>> @@ -0,0 +1,256 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  crc logic from rfc2083 ported to Javascript
>> +**--------------------------------------------------------------------------*/ 
>>
>> +
>> +var rfc2083_crc_table = Array(256);
>> +var rfc2083_crc_table_computed = 0;
>> +/* Make the table for a fast CRC. */
>> +function rfc2083_make_crc_table()
>> +{
>> +    var c;
>> +    var n, k;
>> +    for (n = 0; n < 256; n++)
>> +    {
>> +        c = n;
>> +        for (k = 0; k < 8; k++)
>> +        {
>> +            if (c & 1)
>> +                c = ((0xedb88320 ^ (c >>> 1)) >>> 0) & 0xffffffff;
>> +            else
>> +                c = c >>> 1;
>> +        }
>> +        rfc2083_crc_table[n] = c;
>> +    }
>> +
>> +    rfc2083_crc_table_computed = 1;
>> +}
>> +
>> +/* Update a running CRC with the bytes buf[0..len-1]--the CRC
>> +     should be initialized to all 1's, and the transmitted value
>> +     is the 1's complement of the final running CRC (see the
>> +     crc() routine below)). */
>> +
>> +function rfc2083_update_crc(crc, u8buf, at, len)
>> +{
>> +    var c = crc;
>> +    var n;
>> +
>> +    if (!rfc2083_crc_table_computed)
>> +        rfc2083_make_crc_table();
>> +
>> +    for (n = 0; n < len; n++)
>> +    {
>> +        c = rfc2083_crc_table[(c ^ u8buf[at + n]) & 0xff] ^ (c >>> 8);
>> +    }
>> +
>> +    return c;
>> +}
>> +
>> +function rfc2083_crc(u8buf, at, len)
>> +{
>> +    return rfc2083_update_crc(0xffffffff, u8buf, at, len) ^ 0xffffffff;
>> +}
>> +
>> +function crc32(mb, at, len)
>> +{
>> +    var u8 = new Uint8Array(mb);
>> +    return rfc2083_crc(u8, at, len);
>> +}
>> +
>> +function PngIHDR(width, height)
>> +{
>> +    this.width = width;
>> +    this.height = height;
>> +    this.depth = 8;
>> +    this.type = 6;
>> +    this.compression = 0;
>> +    this.filter = 0;
>> +    this.interlace = 0;
>> +}
>> +
>> +PngIHDR.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var orig = at;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.buffer_size() - 12); at += 4;
>> +        dv.setUint8(at, 'I'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'H'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'D'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'R'.charCodeAt(0)); at++;
>> +        dv.setUint32(at, this.width); at += 4;
>> +        dv.setUint32(at, this.height); at += 4;
>> +        dv.setUint8(at, this.depth); at++;
>> +        dv.setUint8(at, this.type); at++;
>> +        dv.setUint8(at, this.compression); at++;
>> +        dv.setUint8(at, this.filter); at++;
>> +        dv.setUint8(at, this.interlace); at++;
>> +        dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 
>> 8)); at += 4;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 12 + 13;
>> +    }
>> +}
>> +
>> +
>> +function adler()
>> +{
>> +    this.s1 = 1;
>> +    this.s2 = 0;
>> +}
>> +
>> +adler.prototype.update = function(b)
>> +{
>> +    this.s1 += b;
>> +    this.s1 %= 65521;
>> +    this.s2 += this.s1;
>> +    this.s2 %= 65521;
>> +}
>> +
>> +function PngIDAT(width, height, bytes)
>> +{
>> +    if (bytes.byteLength > 65535)
>> +    {
>> +        throw new Error("Cannot handle more than 64K");
>> +    }
>> +    this.data = bytes;
>> +    this.width = width;
>> +    this.height = height;
>> +}
>> +
>> +PngIDAT.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var orig = at;
>> +        var x, y, i, j;
>> +        var dv = new SpiceDataView(a);
>> +        var zsum = new adler();
>> +        dv.setUint32(at, this.buffer_size() - 12); at += 4;
>> +        dv.setUint8(at, 'I'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'D'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'A'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'T'.charCodeAt(0)); at++;
>> +
>> +        /* zlib header.  */
>> +        dv.setUint8(at, 0x78); at++;
>> +        dv.setUint8(at, 0x01); at++;
>> +
>> +        /* Deflate header.  Specifies uncompressed, final bit */
>> +        dv.setUint8(at, 0x80); at++;
>> +        dv.setUint16(at, this.data.byteLength + this.height); at += 2;
>> +        dv.setUint16(at, ~(this.data.byteLength + this.height)); at 
>> += 2;
>> +        var u8 = new Uint8Array(this.data);
>> +        for (i = 0, y = 0; y < this.height; y++)
>> +        {
>> +            /* Filter type 0 - uncompressed */
>> +            dv.setUint8(at, 0); at++;
>> +            zsum.update(0);
>> +            for (x = 0; x < this.width && i < this.data.byteLength; 
>> x++)
>> +            {
>> +                zsum.update(u8[i]);
>> +                dv.setUint8(at, u8[i++]); at++;
>> +                zsum.update(u8[i]);
>> +                dv.setUint8(at, u8[i++]); at++;
>> +                zsum.update(u8[i]);
>> +                dv.setUint8(at, u8[i++]); at++;
>> +                zsum.update(u8[i]);
>> +                dv.setUint8(at, u8[i++]); at++;
>> +            }
>> +        }
>> +
>> +        /* zlib checksum.   */
>> +        dv.setUint16(at, zsum.s2); at+=2;
>> +        dv.setUint16(at, zsum.s1); at+=2;
>> +
>> +        /* FIXME - something is not quite right with the zlib code;
>> +                   you get an error from libpng if you open the 
>> image in
>> +                   gimp.  But it works, so it's good enough for 
>> now... */
>> +
>> +        dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 
>> 8)); at += 4;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 12 + this.data.byteLength + this.height + 4 + 2 + 1 + 
>> 2 + 2;
>> +    }
>> +}
>> +
>> +
>> +function PngIEND()
>> +{
>> +}
>> +
>> +PngIEND.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var orig = at;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.buffer_size() - 12); at += 4;
>> +        dv.setUint8(at, 'I'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'E'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'N'.charCodeAt(0)); at++;
>> +        dv.setUint8(at, 'D'.charCodeAt(0)); at++;
>> +        dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 
>> 8)); at += 4;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 12;
>> +    }
>> +}
>> +
>> +
>> +function create_rgba_png(width, height, bytes)
>> +{
>> +    var i;
>> +    var ihdr = new PngIHDR(width, height);
>> +    var idat = new PngIDAT(width, height, bytes);
>> +    var iend = new PngIEND;
>> +
>> +    var mb = new ArrayBuffer(ihdr.buffer_size() + idat.buffer_size() 
>> + iend.buffer_size());
>> +    var at = ihdr.to_buffer(mb);
>> +    at = idat.to_buffer(mb, at);
>> +    at = iend.to_buffer(mb, at);
>> +
>> +    var u8 = new Uint8Array(mb);
>> +    var str = "";
>> +    for (i = 0; i < at; i++)
>> +    {
>> +        str += "%";
>> +        if (u8[i] < 16)
>> +            str += "0";
>> +        str += u8[i].toString(16);
>> +    }
>> +
>> +
>> +    return "%89PNG%0D%0A%1A%0A" + str;
>> +}
>> diff --git a/ui/js/spice/prng4.js b/ui/js/spice/prng4.js
>> new file mode 100644
>> index 0000000..ef3efd6
>> --- /dev/null
>> +++ b/ui/js/spice/prng4.js
>> @@ -0,0 +1,79 @@
>> +// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by 
>> Jeremy White on 6/1/2012
>> +
>> +/*
>> + * Copyright (c) 2003-2005  Tom Wu
>> + * All Rights Reserved.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining
>> + * a copy of this software and associated documentation files (the
>> + * "Software"), to deal in the Software without restriction, including
>> + * without limitation the rights to use, copy, modify, merge, publish,
>> + * distribute, sublicense, and/or sell copies of the Software, and to
>> + * permit persons to whom the Software is furnished to do so, 
>> subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be
>> + * included in all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
>> + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
>> + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
>> + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES 
>> WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT 
>> ADVISED OF
>> + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, 
>> ARISING OUT
>> + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + *
>> + * In addition, the following condition applies:
>> + *
>> + * All redistributions must retain an intact copy of this copyright 
>> notice
>> + * and disclaimer.
>> + */
>> +
>> +
>> +// prng4.js - uses Arcfour as a PRNG
>> +
>> +function Arcfour() {
>> +  this.i = 0;
>> +  this.j = 0;
>> +  this.S = new Array();
>> +}
>> +
>> +// Initialize arcfour context from key, an array of ints, each from 
>> [0..255]
>> +function ARC4init(key) {
>> +  var i, j, t;
>> +  for(i = 0; i < 256; ++i)
>> +    this.S[i] = i;
>> +  j = 0;
>> +  for(i = 0; i < 256; ++i) {
>> +    j = (j + this.S[i] + key[i % key.length]) & 255;
>> +    t = this.S[i];
>> +    this.S[i] = this.S[j];
>> +    this.S[j] = t;
>> +  }
>> +  this.i = 0;
>> +  this.j = 0;
>> +}
>> +
>> +function ARC4next() {
>> +  var t;
>> +  this.i = (this.i + 1) & 255;
>> +  this.j = (this.j + this.S[this.i]) & 255;
>> +  t = this.S[this.i];
>> +  this.S[this.i] = this.S[this.j];
>> +  this.S[this.j] = t;
>> +  return this.S[(t + this.S[this.i]) & 255];
>> +}
>> +
>> +Arcfour.prototype.init = ARC4init;
>> +Arcfour.prototype.next = ARC4next;
>> +
>> +// Plug in your RNG constructor here
>> +function prng_newstate() {
>> +  return new Arcfour();
>> +}
>> +
>> +// Pool size must be a multiple of 4 and greater than 32.
>> +// An array of bytes the size of the pool will be passed to init()
>> +var rng_psize = 256;
>> diff --git a/ui/js/spice/quic.js b/ui/js/spice/quic.js
>> new file mode 100644
>> index 0000000..9bb9f47
>> --- /dev/null
>> +++ b/ui/js/spice/quic.js
>> @@ -0,0 +1,1335 @@
>> +/*"use strict";*/
>> +/* use strict is commented out because it results in a 5x slowdone 
>> in chrome */
>> +/*
>> + *    Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> + *    Copyright (C) 2012 by Aric Stewart <aric at codeweavers.com>
>> + *
>> + *    This file is part of spice-html5.
>> + *
>> + *    spice-html5 is free software: you can redistribute it and/or 
>> modify
>> + *    it under the terms of the GNU Lesser General Public License as 
>> published by
>> + *    the Free Software Foundation, either version 3 of the License, or
>> + *    (at your option) any later version.
>> + *
>> + *    spice-html5 is distributed in the hope that it will be useful,
>> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + *    GNU Lesser General Public License for more details.
>> + *
>> + *    You should have received a copy of the GNU Lesser General 
>> Public License
>> + *    along with spice-html5.  If not, see 
>> <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +var encoder;
>> +
>> +var QUIC_IMAGE_TYPE_INVALID = 0;
>> +var QUIC_IMAGE_TYPE_GRAY = 1;
>> +var QUIC_IMAGE_TYPE_RGB16 = 2;
>> +var QUIC_IMAGE_TYPE_RGB24 = 3;
>> +var QUIC_IMAGE_TYPE_RGB32 = 4;
>> +var QUIC_IMAGE_TYPE_RGBA = 5;
>> +var DEFevol = 3;
>> +var DEFwmimax = 6;
>> +var DEFwminext = 2048;
>> +var need_init = true;
>> +var DEFmaxclen = 26;
>> +var evol = DEFevol;
>> +var wmimax = DEFwmimax;
>> +var wminext = DEFwminext;
>> +var family_5bpc = { nGRcodewords:[0,0,0,0,0,0,0,0],
>> +                    notGRcwlen:[0,0,0,0,0,0,0,0],
>> +                    notGRprefixmask:[0,0,0,0,0,0,0,0],
>> +                    notGRsuffixlen:[0,0,0,0,0,0,0,0],
>> +                    xlatU2L:[0,0,0,0,0,0,0,0],
>> +                    xlatL2U:[0,0,0,0,0,0,0,0]
>> +                  };
>> +var family_8bpc = { nGRcodewords:[0,0,0,0,0,0,0,0],
>> +                    notGRcwlen:[0,0,0,0,0,0,0,0],
>> +                    notGRprefixmask:[0,0,0,0,0,0,0,0],
>> +                    notGRsuffixlen:[0,0,0,0,0,0,0,0],
>> +                    xlatU2L:[0,0,0,0,0,0,0,0],
>> +                    xlatL2U:[0,0,0,0,0,0,0,0]
>> +                  };
>> +var bppmask = [ 0x00000000,
>> +    0x00000001, 0x00000003, 0x00000007, 0x0000000f,
>> +    0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
>> +    0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
>> +    0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
>> +    0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
>> +    0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
>> +    0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
>> +    0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff];
>> +
>> +var zeroLUT = [];
>> +
>> +var besttrigtab = [
>> +            [ 550, 900, 800, 700, 500, 350, 300, 200, 180, 180, 160],
>> +            [ 110, 550, 900, 800, 550, 400, 350, 250, 140, 160, 140],
>> +            [ 100, 120, 550, 900, 700, 500, 400, 300, 220, 250, 160]];
>> +
>> +var J = [ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 
>> 5, 6, 6,
>> +          7, 7, 8, 9, 10, 11, 12, 13, 14, 15];
>> +
>> +var lzeroes = [
>> +    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 
>> 3, 3, 3, 3,
>> +    3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
>> 2, 2, 2, 2,
>> +    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 
>> 1, 1, 1, 1,
>> +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
>> 1, 1, 1, 1,
>> +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
>> 1, 1, 1, 1,
>> +    1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
>> 0, 0, 0, 0,
>> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
>> 0, 0, 0, 0,
>> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
>> 0, 0, 0, 0,
>> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
>> 0, 0, 0, 0,
>> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
>> 0, 0, 0, 0,
>> +    0, 0, 0, 0, 0, 0];
>> +
>> +var tabrand_chaos = [
>> +    0x02c57542, 0x35427717, 0x2f5a2153, 0x9244f155, 0x7bd26d07, 
>> 0x354c6052,
>> +    0x57329b28, 0x2993868e, 0x6cd8808c, 0x147b46e0, 0x99db66af, 
>> 0xe32b4cac,
>> +    0x1b671264, 0x9d433486, 0x62a4c192, 0x06089a4b, 0x9e3dce44, 
>> 0xdaabee13,
>> +    0x222425ea, 0xa46f331d, 0xcd589250, 0x8bb81d7f, 0xc8b736b9, 
>> 0x35948d33,
>> +    0xd7ac7fd0, 0x5fbe2803, 0x2cfbc105, 0x013dbc4e, 0x7a37820f, 
>> 0x39f88e9e,
>> +    0xedd58794, 0xc5076689, 0xfcada5a4, 0x64c2f46d, 0xb3ba3243, 
>> 0x8974b4f9,
>> +    0x5a05aebd, 0x20afcd00, 0x39e2b008, 0x88a18a45, 0x600bde29, 
>> 0xf3971ace,
>> +    0xf37b0a6b, 0x7041495b, 0x70b707ab, 0x06beffbb, 0x4206051f, 
>> 0xe13c4ee3,
>> +    0xc1a78327, 0x91aa067c, 0x8295f72a, 0x732917a6, 0x1d871b4d, 
>> 0x4048f136,
>> +    0xf1840e7e, 0x6a6048c1, 0x696cb71a, 0x7ff501c3, 0x0fc6310b, 
>> 0x57e0f83d,
>> +    0x8cc26e74, 0x11a525a2, 0x946934c7, 0x7cd888f0, 0x8f9d8604, 
>> 0x4f86e73b,
>> +    0x04520316, 0xdeeea20c, 0xf1def496, 0x67687288, 0xf540c5b2, 
>> 0x22401484,
>> +    0x3478658a, 0xc2385746, 0x01979c2c, 0x5dad73c8, 0x0321f58b, 
>> 0xf0fedbee,
>> +    0x92826ddf, 0x284bec73, 0x5b1a1975, 0x03df1e11, 0x20963e01, 
>> 0xa17cf12b,
>> +    0x740d776e, 0xa7a6bf3c, 0x01b5cce4, 0x1118aa76, 0xfc6fac0a, 
>> 0xce927e9b,
>> +    0x00bf2567, 0x806f216c, 0xbca69056, 0x795bd3e9, 0xc9dc4557, 
>> 0x8929b6c2,
>> +    0x789d52ec, 0x3f3fbf40, 0xb9197368, 0xa38c15b5, 0xc3b44fa8, 
>> 0xca8333b0,
>> +    0xb7e8d590, 0xbe807feb, 0xbf5f8360, 0xd99e2f5c, 0x372928e1, 
>> 0x7c757c4c,
>> +    0x0db5b154, 0xc01ede02, 0x1fc86e78, 0x1f3985be, 0xb4805c77, 
>> 0x00c880fa,
>> +    0x974c1b12, 0x35ab0214, 0xb2dc840d, 0x5b00ae37, 0xd313b026, 
>> 0xb260969d,
>> +    0x7f4c8879, 0x1734c4d3, 0x49068631, 0xb9f6a021, 0x6b863e6f, 
>> 0xcee5debf,
>> +    0x29f8c9fb, 0x53dd6880, 0x72b61223, 0x1f67a9fd, 0x0a0f6993, 
>> 0x13e59119,
>> +    0x11cca12e, 0xfe6b6766, 0x16b6effc, 0x97918fc4, 0xc2b8a563, 
>> 0x94f2f741,
>> +    0x0bfa8c9a, 0xd1537ae8, 0xc1da349c, 0x873c60ca, 0x95005b85, 
>> 0x9b5c080e,
>> +    0xbc8abbd9, 0xe1eab1d2, 0x6dac9070, 0x4ea9ebf1, 0xe0cf30d4, 
>> 0x1ef5bd7b,
>> +    0xd161043e, 0x5d2fa2e2, 0xff5d3cae, 0x86ed9f87, 0x2aa1daa1, 
>> 0xbd731a34,
>> +    0x9e8f4b22, 0xb1c2c67a, 0xc21758c9, 0xa182215d, 0xccb01948, 
>> 0x8d168df7,
>> +    0x04238cfe, 0x368c3dbc, 0x0aeadca5, 0xbad21c24, 0x0a71fee5, 
>> 0x9fc5d872,
>> +    0x54c152c6, 0xfc329483, 0x6783384a, 0xeddb3e1c, 0x65f90e30, 
>> 0x884ad098,
>> +    0xce81675a, 0x4b372f7d, 0x68bf9a39, 0x43445f1e, 0x40f8d8cb, 
>> 0x90d5acb6,
>> +    0x4cd07282, 0x349eeb06, 0x0c9d5332, 0x520b24ef, 0x80020447, 
>> 0x67976491,
>> +    0x2f931ca3, 0xfe9b0535, 0xfcd30220, 0x61a9e6cc, 0xa487d8d7, 
>> 0x3f7c5dd1,
>> +    0x7d0127c5, 0x48f51d15, 0x60dea871, 0xc9a91cb7, 0x58b53bb3, 
>> 0x9d5e0b2d,
>> +    0x624a78b4, 0x30dbee1b, 0x9bdf22e7, 0x1df5c299, 0x2d5643a7, 
>> 0xf4dd35ff,
>> +    0x03ca8fd6, 0x53b47ed8, 0x6f2c19aa, 0xfeb0c1f4, 0x49e54438, 
>> 0x2f2577e6,
>> +    0xbf876969, 0x72440ea9, 0xfa0bafb8, 0x74f5b3a0, 0x7dd357cd, 
>> 0x89ce1358,
>> +    0x6ef2cdda, 0x1e7767f3, 0xa6be9fdb, 0x4f5f88f8, 0xba994a3a, 
>> 0x08ca6b65,
>> +    0xe0893818, 0x9e00a16a, 0xf42bfc8f, 0x9972eedc, 0x749c8b51, 
>> 0x32c05f5e,
>> +    0xd706805f, 0x6bfbb7cf, 0xd9210a10, 0x31a1db97, 0x923a9559, 
>> 0x37a7a1f6,
>> +    0x059f8861, 0xca493e62, 0x65157e81, 0x8f6467dd, 0xab85ff9f, 
>> 0x9331aff2,
>> +    0x8616b9f5, 0xedbd5695, 0xee7e29b1, 0x313ac44f, 0xb903112f, 
>> 0x432ef649,
>> +    0xdc0a36c0, 0x61cf2bba, 0x81474925, 0xa8b6c7ad, 0xee5931de, 
>> 0xb2f8158d,
>> +    0x59fb7409, 0x2e3dfaed, 0x9af25a3f, 0xe1fed4d5 ];
>> +
>> +var rgb32_pixel_pad = 3;
>> +var rgb32_pixel_r = 2;
>> +var rgb32_pixel_g = 1;
>> +var rgb32_pixel_b = 0;
>> +var rgb32_pixel_size = 4;
>> +
>> +/* Helper Functions */
>> +
>> +function ceil_log_2(val)
>> +{
>> +    if (val === 1)
>> +        return 0;
>> +
>> +    var result = 1;
>> +    val -= 1;
>> +    while (val = val >>> 1)
>> +        result++;
>> +
>> +    return result;
>> +}
>> +
>> +function family_init(family, bpc, limit)
>> +{
>> +    var l;
>> +    for (l = 0; l < bpc; l++)
>> +    {
>> +        var altprefixlen, altcodewords;
>> +        altprefixlen = limit - bpc;
>> +        if (altprefixlen > bppmask[bpc - l])
>> +            altprefixlen = bppmask[bpc - l];
>> +
>> +        altcodewords = bppmask[bpc] + 1 - (altprefixlen << l);
>> +        family.nGRcodewords[l] = (altprefixlen << l);
>> +        family.notGRcwlen[l] = altprefixlen + ceil_log_2(altcodewords);
>> +        family.notGRprefixmask[l] = bppmask[32 - altprefixlen]>>>0;
>> +        family.notGRsuffixlen[l] = ceil_log_2(altcodewords);
>> +    }
>> +
>> +    /* decorelate_init */
>> +    var pixelbitmask = bppmask[bpc];
>> +    var pixelbitmaskshr = pixelbitmask >>> 1;
>> +    var s;
>> +    for (s = 0; s <= pixelbitmask; s++) {
>> +        if (s <= pixelbitmaskshr) {
>> +            family.xlatU2L[s] = s << 1;
>> +        } else {
>> +            family.xlatU2L[s] = ((pixelbitmask - s) << 1) + 1;
>> +        }
>> +    }
>> +
>> +    /* corelate_init */
>> +    for (s = 0; s <= pixelbitmask; s++) {
>> +        if (s & 0x01) {
>> +            family.xlatL2U[s] = pixelbitmask - (s >>> 1);
>> +        } else {
>> +            family.xlatL2U[s] = (s >>> 1);
>> +        }
>> +    }
>> +}
>> +
>> +function quic_image_bpc(type)
>> +{
>> +    switch (type) {
>> +    case QUIC_IMAGE_TYPE_GRAY:
>> +        return 8;
>> +    case QUIC_IMAGE_TYPE_RGB16:
>> +        return 5;
>> +    case QUIC_IMAGE_TYPE_RGB24:
>> +        return 8;
>> +    case QUIC_IMAGE_TYPE_RGB32:
>> +        return 8;
>> +    case QUIC_IMAGE_TYPE_RGBA:
>> +        return 8;
>> +    case QUIC_IMAGE_TYPE_INVALID:
>> +    default:
>> +        console.log("quic: bad image type\n");
>> +        return 0;
>> +    }
>> +}
>> +
>> +function cnt_l_zeroes(bits)
>> +{
>> +    if (bits & 0xff800000) {
>> +        return lzeroes[bits >>> 24];
>> +    } else if (bits & 0xffff8000) {
>> +        return 8 + lzeroes[(bits >>> 16) & 0x000000ff];
>> +    } else if (bits & 0xffffff80) {
>> +        return 16 + lzeroes[(bits >>> 8) & 0x000000ff];
>> +    } else {
>> +        return 24 + lzeroes[bits & 0x000000ff];
>> +    }
>> +}
>> +
>> +function golomb_decoding_8bpc(l, bits)
>> +{
>> +    var rc;
>> +    var cwlen;
>> +
>> +    if (bits < 0 || bits > family_8bpc.notGRprefixmask[l])
>> +    {
>> +        var zeroprefix = cnt_l_zeroes(bits);
>> +        cwlen = zeroprefix + 1 + l;
>> +        rc = (zeroprefix << l) | (bits >> (32-cwlen)) & bppmask[l];
>> +    }
>> +    else
>> +    {
>> +        cwlen = family_8bpc.notGRcwlen[l];
>> +        rc = family_8bpc.nGRcodewords[l] + ((bits >> (32-cwlen)) & 
>> bppmask[family_8bpc.notGRsuffixlen[l]]);
>> +    }
>> +    return {'codewordlen':cwlen, 'rc':rc};
>> +}
>> +
>> +function golomb_code_len_8bpc(n, l)
>> +{
>> +    if (n < family_8bpc.nGRcodewords[l]) {
>> +        return (n >>> l) + 1 + l;
>> +    } else {
>> +        return family_8bpc.notGRcwlen[l];
>> +    }
>> +}
>> +
>> +function QuicModel(bpc)
>> +{
>> +    var bstart;
>> +    var bend = 0;
>> +
>> +    this.levels = 0x1 << bpc;
>> +    this.n_buckets_ptrs = 0;
>> +
>> +    switch (evol) {
>> +        case 1:
>> +            this.repfirst = 3;
>> +            this.firstsize = 1;
>> +            this.repnext = 2;
>> +            this.mulsize = 2;
>> +            break;
>> +        case 3:
>> +            this.repfirst = 1;
>> +            this.firstsize = 1;
>> +            this.repnext = 1;
>> +            this.mulsize = 2;
>> +            break;
>> +        case 5:
>> +            this.repfirst = 1;
>> +            this.firstsize = 1;
>> +            this.repnext = 1;
>> +            this.mulsize = 4;
>> +            break;
>> +        case 0:
>> +        case 2:
>> +        case 4:
>> +            console.log("quic: findmodelparams(): evol value 
>> obsolete!!!\n");
>> +        default:
>> +            console.log("quic: findmodelparams(): evol out of 
>> range!!!\n");
>> +    }
>> +
>> +    this.n_buckets = 0;
>> +    var repcntr = this.repfirst + 1;
>> +    var bsize = this.firstsize;
>> +
>> +    do {
>> +        if (this.n_buckets) {
>> +            bstart = bend + 1;
>> +        } else {
>> +            bstart = 0;
>> +        }
>> +
>> +        if (!--repcntr) {
>> +            repcntr = this.repnext;
>> +            bsize *= this.mulsize;
>> +        }
>> +
>> +        bend = bstart + bsize - 1;
>> +        if (bend + bsize >= this.levels) {
>> +            bend = this.levels - 1;
>> +        }
>> +
>> +        if (!this.n_buckets_ptrs) {
>> +            this.n_buckets_ptrs = this.levels;
>> +        }
>> +
>> +        (this.n_buckets)++;
>> +    } while (bend < this.levels - 1);
>> +}
>> +
>> +QuicModel.prototype = {
>> +    n_buckets : 0,
>> +    n_buckets_ptrs : 0,
>> +    repfirst : 0,
>> +    firstsize : 0,
>> +    repnext : 0,
>> +    mulsize : 0,
>> +    levels :0
>> +}
>> +
>> +function QuicBucket()
>> +{
>> +    this.counters = [0,0,0,0,0,0,0,0];
>> +}
>> +
>> +QuicBucket.prototype = {
>> +    bestcode: 0,
>> +
>> +    reste : function (bpp)
>> +    {
>> +        this.bestcode = bpp;
>> +        this.counters = [0,0,0,0,0,0,0,0];
>> +    },
>> +
>> +    update_model_8bpc : function (state, curval, bpp)
>> +    {
>> +        var i;
>> +
>> +        var bestcode = bpp - 1;
>> +        var bestcodelen = (this.counters[bestcode] += 
>> golomb_code_len_8bpc(curval, bestcode));
>> +
>> +        for (i = bpp - 2; i >= 0; i--) {
>> +            var ithcodelen = (this.counters[i] += 
>> golomb_code_len_8bpc(curval, i));
>> +
>> +            if (ithcodelen < bestcodelen) {
>> +                bestcode = i;
>> +                bestcodelen = ithcodelen;
>> +            }
>> +        }
>> +
>> +        this.bestcode = bestcode;
>> +
>> +        if (bestcodelen > state.wm_trigger) {
>> +            for (i = 0; i < bpp; i++) {
>> +                this.counters[i] = this.counters[i] >>> 1;
>> +            }
>> +        }
>> +    }
>> +}
>> +
>> +function QuicFamilyStat()
>> +{
>> +    this.buckets_ptrs = [];
>> +    this.buckets_buf = [];
>> +}
>> +
>> +QuicFamilyStat.prototype = {
>> +
>> +    fill_model_structures : function(model)
>> +    {
>> +        var bstart;
>> +        var bend = 0;
>> +        var bnumber = 0;
>> +
>> +        var repcntr = model.repfirst + 1;
>> +        var bsize = model.firstsize;
>> +
>> +        do {
>> +            if (bnumber) {
>> +                bstart = bend + 1;
>> +            } else {
>> +                bstart = 0;
>> +            }
>> +
>> +            if (!--repcntr) {
>> +                repcntr = model.repnext;
>> +                bsize *= model.mulsize;
>> +            }
>> +
>> +            bend = bstart + bsize - 1;
>> +            if (bend + bsize >= model.levels) {
>> +                bend = model.levels - 1;
>> +            }
>> +
>> +            this.buckets_buf[bnumber] = new QuicBucket;
>> +
>> +            var i;
>> +            for (i = bstart; i <= bend; i++) {
>> +                this.buckets_ptrs[i] = this.buckets_buf[bnumber];
>> +            }
>> +
>> +            bnumber++;
>> +        } while (bend < model.levels - 1);
>> +        return true;
>> +    }
>> +}
>> +
>> +function QuicChannel(model_8bpc, model_5bpc)
>> +{
>> +    this.state = new CommonState;
>> +    this.family_stat_8bpc = new QuicFamilyStat;
>> +    this.family_stat_5bpc = new QuicFamilyStat;
>> +    this.correlate_row = { zero: 0 , row:[] };
>> +    this.model_8bpc = model_8bpc;
>> +    this.model_5bpc = model_5bpc;
>> +    this.buckets_ptrs = [];
>> +
>> +    if (!this.family_stat_8bpc.fill_model_structures(this.model_8bpc))
>> +        return undefined;
>> +
>> +    if (!this.family_stat_5bpc.fill_model_structures(this.model_5bpc))
>> +        return undefined;
>> +}
>> +
>> +QuicChannel.prototype = {
>> +
>> +    reste : function (bpc)
>> +    {
>> +        var j;
>> +        this.correlate_row = { zero: 0 , row: []};
>> +
>> +        if (bpc == 8) {
>> +            for (j = 0; j < this.model_8bpc.n_buckets; j++)
>> +                this.family_stat_8bpc.buckets_buf[j].reste(7);
>> +            this.buckets_ptrs = this.family_stat_8bpc.buckets_ptrs;
>> +        } else if (bpc == 5) {
>> +            for (j = 0; j < this.model_5bpc.n_buckets; j++)
>> +                this.family_stat_8bpc.buckets_buf[j].reste(4);
>> +            this.buckets_ptrs = this.family_stat_5bpc.buckets_ptrs;
>> +        } else {
>> +            console.log("quic: %s: bad bpc %d\n", __FUNCTION__, bpc);
>> +            return false;
>> +        }
>> +
>> +        this.state.reste();
>> +        return true;
>> +    }
>> +}
>> +
>> +function CommonState()
>> +{
>> +}
>> +
>> +CommonState.prototype = {
>> +    waitcnt: 0,
>> +    tabrand_seed: 0xff,
>> +    wm_trigger: 0,
>> +    wmidx: 0,
>> +    wmileft: wminext,
>> +    melcstate: 0,
>> +    melclen: 0,
>> +    melcorder: 0,
>> +
>> +    set_wm_trigger : function()
>> +    {
>> +        var wm = this.wmidx;
>> +        if (wm > 10) {
>> +            wm = 10;
>> +        }
>> +
>> +        this.wm_trigger = besttrigtab[Math.floor(evol / 2)][wm];
>> +    },
>> +
>> +    reste : function()
>> +    {
>> +        this.waitcnt = 0;
>> +        this.tabrand_seed = 0x0ff;
>> +        this.wmidx = 0;
>> +        this.wmileft = wminext;
>> +
>> +        this.set_wm_trigger();
>> +
>> +        this.melcstate = 0;
>> +        this.melclen = J[0];
>> +        this.melcorder = 1 << this.melclen;
>> +    },
>> +
>> +    tabrand : function()
>> +    {
>> +        this.tabrand_seed++;
>> +        return tabrand_chaos[this.tabrand_seed & 0x0ff];
>> +    }
>> +}
>> +
>> +
>> +function QuicEncoder()
>> +{
>> +    this.rgb_state = new CommonState;
>> +    this.model_8bpc = new QuicModel(8);
>> +    this.model_5bpc = new QuicModel(5);
>> +    this.channels = [];
>> +
>> +    var i;
>> +    for (i = 0; i < 4; i++) {
>> +        this.channels[i] = new QuicChannel(this.model_8bpc, 
>> this.model_5bpc);
>> +        if (!this.channels[i])
>> +        {
>> +            console.log("quic: failed to create channel");
>> +            return undefined;
>> +        }
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype = {
>> +                    type: 0,
>> +                    width: 0,
>> +                    height: 0,
>> +                    io_idx: 0,
>> +                    io_available_bits: 0,
>> +                    io_word: 0,
>> +                    io_next_word: 0,
>> +                    io_now: 0,
>> +                    io_end: 0,
>> +                    rows_completed: 0,
>> +              };
>> +
>> +QuicEncoder.prototype.reste = function(io_ptr)
>> +{
>> +    this.rgb_state.reste();
>> +
>> +    this.io_now = io_ptr;
>> +    this.io_end = this.io_now.length;
>> +    this.io_idx = 0;
>> +    this.rows_completed = 0;
>> +    return true;
>> +}
>> +
>> +QuicEncoder.prototype.read_io_word = function()
>> +{
>> +    if (this.io_idx >= this.io_end)
>> +        throw("quic: out of data");
>> +    this.io_next_word = this.io_now[this.io_idx++] | 
>> this.io_now[this.io_idx++]<<8 | this.io_now[this.io_idx++]<<16 | 
>> this.io_now[this.io_idx++]<<24;
>> +}
>> +
>> +QuicEncoder.prototype.decode_eatbits = function (len)
>> +{
>> +    this.io_word = this.io_word << len;
>> +
>> +    var delta = (this.io_available_bits - len);
>> +    if (delta >= 0)
>> +    {
>> +        this.io_available_bits = delta;
>> +        this.io_word |= this.io_next_word >>> this.io_available_bits;
>> +    }
>> +    else
>> +    {
>> +        delta = -1 * delta;
>> +        this.io_word |= this.io_next_word << delta;
>> +        this.read_io_word();
>> +        this.io_available_bits = 32 - delta;
>> +        this.io_word |= this.io_next_word >>> this.io_available_bits;
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype.decode_eat32bits = function()
>> +{
>> +    this.decode_eatbits(16);
>> +    this.decode_eatbits(16);
>> +}
>> +
>> +QuicEncoder.prototype.reste_channels = function(bpc)
>> +{
>> +    var i;
>> +
>> +    for (i = 0; i < 4; i++)
>> +        if (!this.channels[i].reste(bpc))
>> +            return false;
>> +    return true;
>> +}
>> +
>> +QuicEncoder.prototype.quic_decode_begin = function(io_ptr)
>> +{
>> +    if (!this.reste(io_ptr)) {
>> +        return false;
>> +    }
>> +
>> +    this.io_idx = 0;
>> +    this.io_next_word = this.io_now[this.io_idx++] | 
>> this.io_now[this.io_idx++]<<8 | this.io_now[this.io_idx++]<<16 | 
>> this.io_now[this.io_idx++]<<24;
>> +    this.io_word = this.io_next_word;
>> +    this.io_available_bits = 0;
>> +
>> +    var magic = this.io_word;
>> +    this.decode_eat32bits();
>> +    if (magic != 0x43495551) /*QUIC*/ {
>> +        console.log("quic: bad magic "+magic.toString(16));
>> +        return false;
>> +    }
>> +
>> +    var version = this.io_word;
>> +    this.decode_eat32bits();
>> +    if (version != ((0 << 16) | (0 & 0xffff))) {
>> +        console.log("quic: bad version "+version.toString(16));
>> +        return false;
>> +    }
>> +
>> +    this.type = this.io_word;
>> +    this.decode_eat32bits();
>> +
>> +    this.width = this.io_word;
>> +    this.decode_eat32bits();
>> +
>> +    this.height = this.io_word;
>> +    this.decode_eat32bits();
>> +
>> +    var bpc = quic_image_bpc(this.type);
>> +
>> +    if (!this.reste_channels(bpc))
>> +        return false;
>> +
>> +    return true;
>> +}
>> +
>> +QuicEncoder.prototype.quic_rgb32_uncompress_row0_seg = function (i, 
>> cur_row, end,
>> +                                       waitmask, bpc, bpc_mask)
>> +{
>> +    var stopidx;
>> +    var n_channels = 3;
>> +    var c;
>> +    var a;
>> +
>> +    if (!i) {
>> +        cur_row[rgb32_pixel_pad] = 0;
>> +        c = 0;
>> +        do
>> +        {
>> +            a = 
>> golomb_decoding_8bpc(this.channels[c].buckets_ptrs[this.channels[c].correlate_row.zero].bestcode, 
>> this.io_word);
>> +            this.channels[c].correlate_row.row[0] = a.rc;
>> +            cur_row[2-c] = (family_8bpc.xlatL2U[a.rc]&0xFF);
>> +            this.decode_eatbits(a.codewordlen);
>> +        } while (++c < n_channels);
>> +
>> +        if (this.rgb_state.waitcnt) {
>> +            --this.rgb_state.waitcnt;
>> +        } else {
>> +            this.rgb_state.waitcnt = (this.rgb_state.tabrand() & 
>> waitmask);
>> +            c = 0;
>> +            do
>> +            {
>> + 
>> this.channels[c].buckets_ptrs[this.channels[c].correlate_row.zero].update_model_8bpc(this.rgb_state, 
>> this.channels[c].correlate_row.row[0], bpc);
>> +            } while (++c < n_channels);
>> +        }
>> +        stopidx = ++i + this.rgb_state.waitcnt;
>> +    } else {
>> +        stopidx = i + this.rgb_state.waitcnt;
>> +    }
>> +
>> +    while (stopidx < end) {
>> +        for (; i <= stopidx; i++) {
>> +            cur_row[(i* rgb32_pixel_size)+rgb32_pixel_pad] = 0;
>> +            c = 0;
>> +            do
>> +            {
>> +                a = 
>> golomb_decoding_8bpc(this.channels[c].buckets_ptrs[this.channels[c].correlate_row.row[i 
>> - 1]].bestcode, this.io_word);
>> +                this.channels[c].correlate_row.row[i] = a.rc;
>> +                cur_row[(i* rgb32_pixel_size)+(2-c)] = 
>> (family_8bpc.xlatL2U[a.rc] + cur_row[((i-1) * rgb32_pixel_size) + 
>> (2-c)]) & bpc_mask;
>> +                this.decode_eatbits(a.codewordlen);
>> +            } while (++c < n_channels);
>> +        }
>> +        c = 0;
>> +        do
>> +        {
>> + 
>> this.channels[c].buckets_ptrs[this.channels[c].correlate_row.row[stopidx 
>> - 1]].update_model_8bpc(this.rgb_state, 
>> this.channels[c].correlate_row.row[stopidx], bpc);
>> +        } while (++c < n_channels);
>> +        stopidx = i + (this.rgb_state.tabrand() & waitmask);
>> +    }
>> +
>> +    for (; i < end; i++) {
>> +        cur_row[(i* rgb32_pixel_size)+rgb32_pixel_pad] = 0;
>> +        c = 0;
>> +        do
>> +        {
>> +            a = 
>> golomb_decoding_8bpc(this.channels[c].buckets_ptrs[this.channels[c].correlate_row.row[i 
>> - 1]].bestcode, this.io_word);
>> +            this.channels[c].correlate_row.row[i] = a.rc;
>> +            cur_row[(i* rgb32_pixel_size)+(2-c)] = 
>> (family_8bpc.xlatL2U[a.rc] + cur_row[((i-1) * rgb32_pixel_size) + 
>> (2-c)]) & bpc_mask;
>> +            this.decode_eatbits(a.codewordlen);
>> +        } while (++c < n_channels);
>> +    }
>> +    this.rgb_state.waitcnt = stopidx - end;
>> +}
>> +
>> +QuicEncoder.prototype.quic_rgb32_uncompress_row0 = function (cur_row)
>> +{
>> +    var bpc = 8;
>> +    var bpc_mask = 0xff;
>> +    var pos = 0;
>> +    var width = this.width;
>> +
>> +    while ((wmimax > this.rgb_state.wmidx) && 
>> (this.rgb_state.wmileft <= width)) {
>> +        if (this.rgb_state.wmileft) {
>> +            this.quic_rgb32_uncompress_row0_seg(pos, cur_row,
>> +                                       pos + this.rgb_state.wmileft,
>> + bppmask[this.rgb_state.wmidx],
>> +                                       bpc, bpc_mask);
>> +            pos += this.rgb_state.wmileft;
>> +            width -= this.rgb_state.wmileft;
>> +        }
>> +
>> +        this.rgb_state.wmidx++;
>> +        this.rgb_state.set_wm_trigger();
>> +        this.rgb_state.wmileft = wminext;
>> +    }
>> +
>> +    if (width) {
>> +        this.quic_rgb32_uncompress_row0_seg(pos, cur_row, pos + width,
>> + bppmask[this.rgb_state.wmidx], bpc, bpc_mask);
>> +        if (wmimax > this.rgb_state.wmidx) {
>> +            this.rgb_state.wmileft -= width;
>> +        }
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype.quic_rgb32_uncompress_row_seg = function( 
>> prev_row, cur_row, i, end, bpc, bpc_mask)
>> +{
>> +    var n_channels = 3;
>> +    var waitmask = bppmask[this.rgb_state.wmidx];
>> +
>> +    var a;
>> +    var run_index = 0;
>> +    var stopidx = 0;
>> +    var run_end = 0;
>> +    var c;
>> +
>> +    if (!i)
>> +    {
>> +        cur_row[rgb32_pixel_pad] = 0;
>> +
>> +        c = 0;
>> +        do {
>> +            a = 
>> golomb_decoding_8bpc(this.channels[c].buckets_ptrs[this.channels[c].correlate_row.zero].bestcode, 
>> this.io_word);
>> +            this.channels[c].correlate_row.row[0] = a.rc;
>> +            cur_row[2-c] = 
>> (family_8bpc.xlatL2U[this.channels[c].correlate_row.row[0]] + 
>> prev_row[2-c]) & bpc_mask;
>> +            this.decode_eatbits(a.codewordlen);
>> +        } while (++c < n_channels);
>> +
>> +        if (this.rgb_state.waitcnt) {
>> +            --this.rgb_state.waitcnt;
>> +        } else {
>> +            this.rgb_state.waitcnt = (this.rgb_state.tabrand() & 
>> waitmask);
>> +            c = 0;
>> +            do {
>> + 
>> this.channels[c].buckets_ptrs[this.channels[c].correlate_row.zero].update_model_8bpc(this.rgb_state, 
>> this.channels[c].correlate_row.row[0], bpc);
>> +            } while (++c < n_channels);
>> +        }
>> +        stopidx = ++i + this.rgb_state.waitcnt;
>> +    } else {
>> +        stopidx = i + this.rgb_state.waitcnt;
>> +    }
>> +    for (;;) {
>> +        var rc = 0;
>> +        while (stopidx < end && !rc) {
>> +            for (; i <= stopidx && !rc; i++) {
>> +                var pixel = i * rgb32_pixel_size;
>> +                var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                var pixelm2 = (i-2) * rgb32_pixel_size;
>> +
>> +                if ( prev_row[pixelm1+rgb32_pixel_r] == 
>> prev_row[pixel+rgb32_pixel_r] && prev_row[pixelm1+rgb32_pixel_g] == 
>> prev_row[pixel+rgb32_pixel_g] && prev_row[pixelm1 + rgb32_pixel_b] == 
>> prev_row[pixel+rgb32_pixel_b])
>> +                {
>> +                    if (run_index != i && i > 2 && 
>> (cur_row[pixelm1+rgb32_pixel_r] == cur_row[pixelm2+rgb32_pixel_r] && 
>> cur_row[pixelm1+rgb32_pixel_g] == cur_row[pixelm2+rgb32_pixel_g] && 
>> cur_row[pixelm1+rgb32_pixel_b] == cur_row[pixelm2+rgb32_pixel_b]))
>> +                    {
>> +                        /* do run */
>> +                        this.rgb_state.waitcnt = stopidx - i;
>> +                        run_index = i;
>> +                        run_end = i + this.decode_run(this.rgb_state);
>> +
>> +                        for (; i < run_end; i++) {
>> +                            var pixel = i * rgb32_pixel_size;
>> +                            var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                            cur_row[pixel+rgb32_pixel_pad] = 0;
>> +                            cur_row[pixel+rgb32_pixel_r] = 
>> cur_row[pixelm1+rgb32_pixel_r];
>> +                            cur_row[pixel+rgb32_pixel_g] = 
>> cur_row[pixelm1+rgb32_pixel_g];
>> +                            cur_row[pixel+rgb32_pixel_b] = 
>> cur_row[pixelm1+rgb32_pixel_b];
>> +                        }
>> +
>> +                        if (i == end) {
>> +                            return;
>> +                        }
>> +                        else
>> +                        {
>> +                            stopidx = i + this.rgb_state.waitcnt;
>> +                            rc = 1;
>> +                            break;
>> +                        }
>> +                    }
>> +                }
>> +
>> +                c = 0;
>> +                cur_row[pixel+rgb32_pixel_pad] = 0;
>> +                do {
>> +                    var cc = this.channels[c];
>> +                    var cr = cc.correlate_row;
>> +
>> +                    a = 
>> golomb_decoding_8bpc(cc.buckets_ptrs[cr.row[i-1]].bestcode, 
>> this.io_word);
>> +                    cr.row[i] = a.rc;
>> +                cur_row[pixel+(2-c)] = (family_8bpc.xlatL2U[a.rc] + 
>> ((cur_row[pixelm1+(2-c)] + prev_row[pixel+(2-c)]) >> 1)) & bpc_mask;
>> +                    this.decode_eatbits(a.codewordlen);
>> +                } while (++c < n_channels);
>> +            }
>> +            if (rc)
>> +                break;
>> +
>> +            c = 0;
>> +            do {
>> + 
>> this.channels[c].buckets_ptrs[this.channels[c].correlate_row.row[stopidx 
>> - 1]].update_model_8bpc(this.rgb_state, 
>> this.channels[c].correlate_row.row[stopidx], bpc);
>> +            } while (++c < n_channels);
>> +
>> +            stopidx = i + (this.rgb_state.tabrand() & waitmask);
>> +        }
>> +
>> +        for (; i < end && !rc; i++) {
>> +            var pixel = i * rgb32_pixel_size;
>> +            var pixelm1 = (i-1) * rgb32_pixel_size;
>> +            var pixelm2 = (i-2) * rgb32_pixel_size;
>> +
>> +            if (prev_row[pixelm1+rgb32_pixel_r] == 
>> prev_row[pixel+rgb32_pixel_r] && prev_row[pixelm1+rgb32_pixel_g] == 
>> prev_row[pixel+rgb32_pixel_g] && prev_row[pixelm1+rgb32_pixel_b] == 
>> prev_row[pixel+rgb32_pixel_b])
>> +            {
>> +                if (run_index != i && i > 2 && 
>> (cur_row[pixelm1+rgb32_pixel_r] == cur_row[pixelm2+rgb32_pixel_r] && 
>> cur_row[pixelm1+rgb32_pixel_g] == cur_row[pixelm2+rgb32_pixel_g] && 
>> cur_row[pixelm1+rgb32_pixel_b] == cur_row[pixelm2+rgb32_pixel_b]))
>> +                {
>> +                    /* do run */
>> +                    this.rgb_state.waitcnt = stopidx - i;
>> +                    run_index = i;
>> +                    run_end = i + this.decode_run(this.rgb_state);
>> +
>> +                    for (; i < run_end; i++) {
>> +                        var pixel = i * rgb32_pixel_size;
>> +                        var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                        cur_row[pixel+rgb32_pixel_pad] = 0;
>> +                        cur_row[pixel+rgb32_pixel_r] = 
>> cur_row[pixelm1+rgb32_pixel_r];
>> +                        cur_row[pixel+rgb32_pixel_g] = 
>> cur_row[pixelm1+rgb32_pixel_g];
>> +                        cur_row[pixel+rgb32_pixel_b] = 
>> cur_row[pixelm1+rgb32_pixel_b];
>> +                    }
>> +
>> +                    if (i == end) {
>> +                        return;
>> +                    }
>> +                    else
>> +                    {
>> +                        stopidx = i + this.rgb_state.waitcnt;
>> +                        rc = 1;
>> +                        break;
>> +                    }
>> +                }
>> +            }
>> +
>> +            cur_row[pixel+rgb32_pixel_pad] = 0;
>> +            c = 0;
>> +            do
>> +            {
>> +                a = 
>> golomb_decoding_8bpc(this.channels[c].buckets_ptrs[this.channels[c].correlate_row.row[i-1]].bestcode, 
>> this.io_word);
>> +                this.channels[c].correlate_row.row[i] = a.rc;
>> +                cur_row[pixel+(2-c)] = (family_8bpc.xlatL2U[a.rc] + 
>> ((cur_row[pixelm1+(2-c)] + prev_row[pixel+(2-c)]) >> 1)) & bpc_mask;
>> +                this.decode_eatbits(a.codewordlen);
>> +            } while (++c < n_channels);
>> +        }
>> +
>> +          if (!rc)
>> +          {
>> +            this.rgb_state.waitcnt = stopidx - end;
>> +            return;
>> +          }
>> +        }
>> +}
>> +
>> +QuicEncoder.prototype.decode_run = function(state)
>> +{
>> +    var runlen = 0;
>> +
>> +    do {
>> +        var hits;
>> +        var x = (~(this.io_word >>> 24)>>>0)&0xff;
>> +        var temp = zeroLUT[x];
>> +
>> +        for (hits = 1; hits <= temp; hits++) {
>> +            runlen += state.melcorder;
>> +
>> +            if (state.melcstate < 32) {
>> +                state.melclen = J[++state.melcstate];
>> +                state.melcorder = (1 << state.melclen);
>> +            }
>> +        }
>> +        if (temp != 8) {
>> +            this.decode_eatbits(temp + 1);
>> +
>> +            break;
>> +        }
>> +        this.decode_eatbits(8);
>> +    } while (true);
>> +
>> +    if (state.melclen) {
>> +        runlen += this.io_word >>> (32 - state.melclen);
>> +        this.decode_eatbits(state.melclen);
>> +    }
>> +
>> +    if (state.melcstate) {
>> +        state.melclen = J[--state.melcstate];
>> +        state.melcorder = (1 << state.melclen);
>> +    }
>> +
>> +    return runlen;
>> +}
>> +
>> +QuicEncoder.prototype.quic_rgb32_uncompress_row = function 
>> (prev_row, cur_row)
>> +{
>> +    var bpc = 8;
>> +    var bpc_mask = 0xff;
>> +    var pos = 0;
>> +    var width = this.width;
>> +
>> +    while ((wmimax > this.rgb_state.wmidx) && 
>> (this.rgb_state.wmileft <= width)) {
>> +        if (this.rgb_state.wmileft) {
>> +            this.quic_rgb32_uncompress_row_seg(prev_row, cur_row, pos,
>> +                                      pos + this.rgb_state.wmileft, 
>> bpc, bpc_mask);
>> +            pos += this.rgb_state.wmileft;
>> +            width -= this.rgb_state.wmileft;
>> +        }
>> +
>> +        this.rgb_state.wmidx++;
>> +        this.rgb_state.set_wm_trigger();
>> +        this.rgb_state.wmileft = wminext;
>> +    }
>> +
>> +    if (width) {
>> +        this.quic_rgb32_uncompress_row_seg(prev_row, cur_row, pos,
>> +                                  pos + width, bpc, bpc_mask);
>> +        if (wmimax > this.rgb_state.wmidx) {
>> +            this.rgb_state.wmileft -= width;
>> +        }
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype.quic_four_uncompress_row0_seg = function 
>> (channel, i,
>> +                                       correlate_row, cur_row, end, 
>> waitmask,
>> +                                       bpc, bpc_mask)
>> +{
>> +    var stopidx;
>> +    var a;
>> +
>> +    if (i == 0) {
>> +        a = 
>> golomb_decoding_8bpc(channel.buckets_ptrs[correlate_row.zero].bestcode, 
>> this.io_word);
>> +        correlate_row.row[0] = a.rc;
>> +        cur_row[rgb32_pixel_pad] = family_8bpc.xlatL2U[a.rc];
>> +        this.decode_eatbits(a.codewordlen);
>> +
>> +        if (channel.state.waitcnt) {
>> +            --channel.state.waitcnt;
>> +        } else {
>> +            channel.state.waitcnt = (channel.state.tabrand() & 
>> waitmask);
>> + 
>> channel.buckets_ptrs[correlate_row.zero].update_model_8bpc(channel.state, 
>> correlate_row.row[0], bpc);
>> +        }
>> +        stopidx = ++i + channel.state.waitcnt;
>> +    } else {
>> +        stopidx = i + channel.state.waitcnt;
>> +    }
>> +
>> +    while (stopidx < end) {
>> +        var pbucket;
>> +
>> +        for (; i <= stopidx; i++) {
>> +            pbucket = channel.buckets_ptrs[correlate_row.row[i - 1]];
>> +
>> +            a = golomb_decoding_8bpc(pbucket.bestcode, this.io_word);
>> +            correlate_row.row[i] = a.rc;
>> +            cur_row[(i*rgb32_pixel_size)+rgb32_pixel_pad] = 
>> (family_8bpc.xlatL2U[a.rc] + 
>> cur_row[((i-1)*rgb32_pixel_size)+rgb32_pixel_pad]) & bpc_mask;
>> +            this.decode_eatbits(a.codewordlen);
>> +        }
>> +
>> +        pbucket.update_model_8bpc(channel.state, 
>> correlate_row.row[stopidx], bpc);
>> +
>> +        stopidx = i + (channel.state.tabrand() & waitmask);
>> +    }
>> +
>> +    for (; i < end; i++) {
>> +        a = 
>> golomb_decoding_8bpc(channel.buckets_ptrs[correlate_row.row[i-1]].bestcode, 
>> this.io_word);
>> +
>> +        correlate_row.row[i] = a.rc;
>> +        cur_row[(i*rgb32_pixel_size)+rgb32_pixel_pad] = 
>> (family_8bpc.xlatL2U[a.rc] + 
>> cur_row[((i-1)*rgb32_pixel_size)+rgb32_pixel_pad]) & bpc_mask;
>> +        this.decode_eatbits(a.codewordlen);
>> +    }
>> +    channel.state.waitcnt = stopidx - end;
>> +}
>> +
>> +QuicEncoder.prototype.quic_four_uncompress_row0 = function(channel, 
>> cur_row)
>> +{
>> +    var bpc = 8;
>> +    var bpc_mask = 0xff;
>> +    var correlate_row = channel.correlate_row;
>> +    var pos = 0;
>> +    var width = this.width;
>> +
>> +    while ((wmimax > channel.state.wmidx) && (channel.state.wmileft 
>> <= width)) {
>> +        if (channel.state.wmileft) {
>> +            this.quic_four_uncompress_row0_seg(channel, pos, 
>> correlate_row, cur_row,
>> +                                       pos + channel.state.wmileft, 
>> bppmask[channel.state.wmidx],
>> +                                       bpc, bpc_mask);
>> +            pos += channel.state.wmileft;
>> +            width -= channel.state.wmileft;
>> +        }
>> +
>> +        channel.state.wmidx++;
>> +        channel.state.set_wm_trigger();
>> +        channel.state.wmileft = wminext;
>> +    }
>> +
>> +    if (width) {
>> +        this.quic_four_uncompress_row0_seg(channel, pos, 
>> correlate_row, cur_row, pos + width,
>> + bppmask[channel.state.wmidx], bpc, bpc_mask);
>> +        if (wmimax > channel.state.wmidx) {
>> +            channel.state.wmileft -= width;
>> +        }
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype.quic_four_uncompress_row_seg = function (channel,
>> +                                      correlate_row, prev_row, 
>> cur_row, i,
>> +                                      end, bpc, bpc_mask)
>> +{
>> +    var waitmask = bppmask[channel.state.wmidx];
>> +    var stopidx;
>> +
>> +    var run_index = 0;
>> +    var run_end;
>> +
>> +    var a;
>> +
>> +    if (i == 0) {
>> +        a = 
>> golomb_decoding_8bpc(channel.buckets_ptrs[correlate_row.zero].bestcode, 
>> this.io_word);
>> +
>> +        correlate_row.row[0] = a.rc
>> +        cur_row[rgb32_pixel_pad] = (family_8bpc.xlatL2U[a.rc] + 
>> prev_row[rgb32_pixel_pad]) & bpc_mask;
>> +        this.decode_eatbits(a.codewordlen);
>> +
>> +        if (channel.state.waitcnt) {
>> +            --channel.state.waitcnt;
>> +        } else {
>> +            channel.state.waitcnt = (channel.state.tabrand() & 
>> waitmask);
>> + 
>> channel.buckets_ptrs[correlate_row.zero].update_model_8bpc(channel.state, 
>> correlate_row.row[0], bpc);
>> +        }
>> +        stopidx = ++i + channel.state.waitcnt;
>> +    } else {
>> +        stopidx = i + channel.state.waitcnt;
>> +    }
>> +    for (;;) {
>> +        var rc = 0;
>> +        while (stopidx < end && !rc) {
>> +            var pbucket;
>> +            for (; i <= stopidx && !rc; i++) {
>> +                var pixel = i * rgb32_pixel_size;
>> +                var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                var pixelm2 = (i-2) * rgb32_pixel_size;
>> +
>> +                if (prev_row[pixelm1+rgb32_pixel_pad] == 
>> prev_row[pixel+rgb32_pixel_pad])
>> +                {
>> +                    if (run_index != i && i > 2 && 
>> cur_row[pixelm1+rgb32_pixel_pad] == cur_row[pixelm2+rgb32_pixel_pad])
>> +                    {
>> +                        /* do run */
>> +                        channel.state.waitcnt = stopidx - i;
>> +                        run_index = i;
>> +
>> +                        run_end = i + this.decode_run(channel.state);
>> +
>> +                        for (; i < run_end; i++) {
>> +                            var pixel = i * rgb32_pixel_size;
>> +                            var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                            cur_row[pixel+rgb32_pixel_pad] = 
>> cur_row[pixelm1+rgb32_pixel_pad];
>> +                        }
>> +
>> +                        if (i == end) {
>> +                            return;
>> +                        }
>> +                        else
>> +                        {
>> +                            stopidx = i + channel.state.waitcnt;
>> +                            rc = 1;
>> +                            break;
>> +                        }
>> +                    }
>> +                }
>> +
>> +                pbucket = channel.buckets_ptrs[correlate_row.row[i - 
>> 1]];
>> +                a = golomb_decoding_8bpc(pbucket.bestcode, 
>> this.io_word);
>> +                correlate_row.row[i] = a.rc
>> +                cur_row[pixel+rgb32_pixel_pad] = 
>> (family_8bpc.xlatL2U[a.rc] + ((cur_row[pixelm1+rgb32_pixel_pad] + 
>> prev_row[pixel+rgb32_pixel_pad]) >> 1)) & bpc_mask;
>> +                this.decode_eatbits(a.codewordlen);
>> +            }
>> +            if (rc)
>> +                break;
>> +
>> +            pbucket.update_model_8bpc(channel.state, 
>> correlate_row.row[stopidx], bpc);
>> +
>> +            stopidx = i + (channel.state.tabrand() & waitmask);
>> +        }
>> +
>> +        for (; i < end && !rc; i++) {
>> +            var pixel = i * rgb32_pixel_size;
>> +            var pixelm1 = (i-1) * rgb32_pixel_size;
>> +            var pixelm2 = (i-2) * rgb32_pixel_size;
>> +            if (prev_row[pixelm1+rgb32_pixel_pad] == 
>> prev_row[pixel+rgb32_pixel_pad])
>> +            {
>> +                if (run_index != i && i > 2 && 
>> cur_row[pixelm1+rgb32_pixel_pad] == cur_row[pixelm2+rgb32_pixel_pad])
>> +                {
>> +                    /* do run */
>> +                    channel.state.waitcnt = stopidx - i;
>> +                    run_index = i;
>> +
>> +                    run_end = i + this.decode_run(channel.state);
>> +
>> +                    for (; i < run_end; i++) {
>> +                        var pixel = i * rgb32_pixel_size;
>> +                        var pixelm1 = (i-1) * rgb32_pixel_size;
>> +                        cur_row[pixel+rgb32_pixel_pad] = 
>> cur_row[pixelm1+rgb32_pixel_pad];
>> +                    }
>> +
>> +                    if (i == end) {
>> +                        return;
>> +                    }
>> +                    else
>> +                    {
>> +                        stopidx = i + channel.state.waitcnt;
>> +                        rc = 1;
>> +                        break;
>> +                    }
>> +                }
>> +            }
>> +
>> +            a = 
>> golomb_decoding_8bpc(channel.buckets_ptrs[correlate_row.row[i-1]].bestcode, 
>> this.io_word);
>> +            correlate_row.row[i] = a.rc;
>> +            cur_row[pixel+rgb32_pixel_pad] = 
>> (family_8bpc.xlatL2U[a.rc] + ((cur_row[pixelm1+rgb32_pixel_pad] + 
>> prev_row[pixel+rgb32_pixel_pad]) >> 1)) & bpc_mask;
>> +            this.decode_eatbits(a.codewordlen);
>> +        }
>> +
>> +        if (!rc)
>> +        {
>> +            channel.state.waitcnt = stopidx - end;
>> +            return;
>> +        }
>> +    }
>> +}
>> +
>> +QuicEncoder.prototype.quic_four_uncompress_row = function(channel, 
>> prev_row,
>> + cur_row)
>> +{
>> +    var bpc = 8;
>> +    var bpc_mask = 0xff;
>> +    var correlate_row = channel.correlate_row;
>> +    var pos = 0;
>> +    var width = this.width;
>> +
>> +    while ((wmimax > channel.state.wmidx) && (channel.state.wmileft 
>> <= width)) {
>> +        if (channel.state.wmileft) {
>> +            this.quic_four_uncompress_row_seg(channel, 
>> correlate_row, prev_row, cur_row, pos,
>> +                                      pos + channel.state.wmileft, 
>> bpc, bpc_mask);
>> +            pos += channel.state.wmileft;
>> +            width -= channel.state.wmileft;
>> +        }
>> +
>> +        channel.state.wmidx++;
>> +        channel.state.set_wm_trigger();
>> +        channel.state.wmileft = wminext;
>> +    }
>> +
>> +    if (width) {
>> +        this.quic_four_uncompress_row_seg(channel, correlate_row, 
>> prev_row, cur_row, pos,
>> +                                  pos + width, bpc, bpc_mask);
>> +        if (wmimax > channel.state.wmidx) {
>> +            channel.state.wmileft -= width;
>> +        }
>> +    }
>> +}
>> +
>> +/* We need to be generating rgb32 or rgba */
>> +QuicEncoder.prototype.quic_decode = function(buf, stride)
>> +{
>> +    var row;
>> +
>> +    switch (this.type)
>> +    {
>> +        case QUIC_IMAGE_TYPE_RGB32:
>> +        case QUIC_IMAGE_TYPE_RGB24:
>> +            this.channels[0].correlate_row.zero = 0;
>> +            this.channels[1].correlate_row.zero = 0;
>> +            this.channels[2].correlate_row.zero = 0;
>> +            this.quic_rgb32_uncompress_row0(buf);
>> +
>> +            this.rows_completed++;
>> +            for (row = 1; row < this.height; row++)
>> +            {
>> +                var prev = buf;
>> +                buf = prev.subarray(stride);
>> +                this.channels[0].correlate_row.zero = 
>> this.channels[0].correlate_row.row[0];
>> +                this.channels[1].correlate_row.zero = 
>> this.channels[1].correlate_row.row[0];
>> +                this.channels[2].correlate_row.zero = 
>> this.channels[2].correlate_row.row[0];
>> +                this.quic_rgb32_uncompress_row(prev, buf);
>> +                this.rows_completed++;
>> +            };
>> +            break;
>> +        case QUIC_IMAGE_TYPE_RGB16:
>> +            console.log("quic: unsupported output format\n");
>> +            return false;
>> +            break;
>> +        case QUIC_IMAGE_TYPE_RGBA:
>> +            this.channels[0].correlate_row.zero = 0;
>> +            this.channels[1].correlate_row.zero = 0;
>> +            this.channels[2].correlate_row.zero = 0;
>> +            this.quic_rgb32_uncompress_row0(buf);
>> +
>> +            this.channels[3].correlate_row.zero = 0;
>> +            this.quic_four_uncompress_row0(this.channels[3], buf);
>> +
>> +            this.rows_completed++;
>> +            for (row = 1; row < this.height; row++) {
>> +                var prev = buf;
>> +                buf = prev.subarray(stride);
>> +
>> +                this.channels[0].correlate_row.zero = 
>> this.channels[0].correlate_row.row[0];
>> +                this.channels[1].correlate_row.zero = 
>> this.channels[1].correlate_row.row[0];
>> +                this.channels[2].correlate_row.zero = 
>> this.channels[2].correlate_row.row[0];
>> +                this.quic_rgb32_uncompress_row(prev, buf);
>> +
>> +                this.channels[3].correlate_row.zero = 
>> this.channels[3].correlate_row.row[0];
>> + this.quic_four_uncompress_row(encoder.channels[3], prev, buf);
>> +                this.rows_completed++;
>> +            }
>> +            break;
>> +
>> +        case QUIC_IMAGE_TYPE_GRAY:
>> +            console.log("quic: unsupported output format\n");
>> +            return false;
>> +            break;
>> +
>> +        case QUIC_IMAGE_TYPE_INVALID:
>> +        default:
>> +            console.log("quic: bad image type\n");
>> +            return false;
>> +    }
>> +    return true;
>> +}
>> +
>> +QuicEncoder.prototype.simple_quic_decode = function(buf)
>> +{
>> +    var stride = 4; /* FIXME - proper stride calc please */
>> +    if (!this.quic_decode_begin(buf))
>> +        return undefined;
>> +    if (this.type != QUIC_IMAGE_TYPE_RGB32 && this.type != 
>> QUIC_IMAGE_TYPE_RGB24
>> +        && this.type != QUIC_IMAGE_TYPE_RGBA)
>> +        return undefined;
>> +    var out = new Uint8Array(this.width*this.height*4);
>> +    out[0] = 69;
>> +    if (this.quic_decode( out, (this.width * stride)))
>> +        return out;
>> +    return undefined;
>> +}
>> +
>> +function SpiceQuic()
>> +{
>> +}
>> +
>> +SpiceQuic.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        if (!encoder)
>> +            throw("quic: no quic encoder");
>> +        this.data_size = dv.getUint32(at, true);
>> +        at += 4;
>> +        var buf = new Uint8Array(mb.slice(at));
>> +        this.outptr = encoder.simple_quic_decode(buf);
>> +        if (this.outptr)
>> +        {
>> +            this.type = encoder.type;
>> +            this.width = encoder.width;
>> +            this.height = encoder.height;
>> +        }
>> +        at += buf.length;
>> +        return at;
>> +    },
>> +}
>> +
>> +function convert_spice_quic_to_web(context, spice_quic)
>> +{
>> +    var ret = context.createImageData(spice_quic.width, 
>> spice_quic.height);
>> +    var i;
>> +    for (i = 0; i < (ret.width * ret.height * 4); i+=4)
>> +    {
>> +        ret.data[i + 0] = spice_quic.outptr[i + 2];
>> +        ret.data[i + 1] = spice_quic.outptr[i + 1];
>> +        ret.data[i + 2] = spice_quic.outptr[i + 0];
>> +        if (spice_quic.type !== QUIC_IMAGE_TYPE_RGBA)
>> +            ret.data[i + 3] = 255;
>> +        else
>> +            ret.data[i + 3] = 255 - spice_quic.outptr[i + 3];
>> +    }
>> +   return ret;
>> +}
>> +
>> +/* Module initialization */
>> +if (need_init)
>> +{
>> +    need_init = false;
>> +
>> +    family_init(family_8bpc, 8, DEFmaxclen);
>> +    family_init(family_5bpc, 5, DEFmaxclen);
>> +    /* init_zeroLUT */
>> +    var i, j, k, l;
>> +
>> +    j = k = 1;
>> +    l = 8;
>> +    for (i = 0; i < 256; ++i) {
>> +        zeroLUT[i] = l;
>> +        --k;
>> +        if (k == 0) {
>> +            k = j;
>> +            --l;
>> +            j *= 2;
>> +        }
>> +    }
>> +
>> +    encoder = new QuicEncoder;
>> +
>> +    if (!encoder)
>> +        throw("quic: failed to create encoder");
>> +}
>> diff --git a/ui/js/spice/rng.js b/ui/js/spice/rng.js
>> new file mode 100644
>> index 0000000..efbf382
>> --- /dev/null
>> +++ b/ui/js/spice/rng.js
>> @@ -0,0 +1,102 @@
>> +// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by 
>> Jeremy White on 6/1/2012
>> +
>> +/*
>> + * Copyright (c) 2003-2005  Tom Wu
>> + * All Rights Reserved.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining
>> + * a copy of this software and associated documentation files (the
>> + * "Software"), to deal in the Software without restriction, including
>> + * without limitation the rights to use, copy, modify, merge, publish,
>> + * distribute, sublicense, and/or sell copies of the Software, and to
>> + * permit persons to whom the Software is furnished to do so, 
>> subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be
>> + * included in all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
>> + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
>> + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
>> + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES 
>> WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT 
>> ADVISED OF
>> + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, 
>> ARISING OUT
>> + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + *
>> + * In addition, the following condition applies:
>> + *
>> + * All redistributions must retain an intact copy of this copyright 
>> notice
>> + * and disclaimer.
>> + */
>> +
>> +
>> +// Random number generator - requires a PRNG backend, e.g. prng4.js
>> +
>> +// For best results, put code like
>> +// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
>> +// in your main HTML document.
>> +
>> +var rng_state;
>> +var rng_pool;
>> +var rng_pptr;
>> +
>> +// Mix in a 32-bit integer into the pool
>> +function rng_seed_int(x) {
>> +  rng_pool[rng_pptr++] ^= x & 255;
>> +  rng_pool[rng_pptr++] ^= (x >> 8) & 255;
>> +  rng_pool[rng_pptr++] ^= (x >> 16) & 255;
>> +  rng_pool[rng_pptr++] ^= (x >> 24) & 255;
>> +  if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
>> +}
>> +
>> +// Mix in the current time (w/milliseconds) into the pool
>> +function rng_seed_time() {
>> +  rng_seed_int(new Date().getTime());
>> +}
>> +
>> +// Initialize the pool with junk if needed.
>> +if(rng_pool == null) {
>> +  rng_pool = new Array();
>> +  rng_pptr = 0;
>> +  var t;
>> +  if(navigator.appName == "Netscape" && navigator.appVersion < "5" 
>> && window.crypto) {
>> +    // Extract entropy (256 bits) from NS4 RNG if available
>> +    var z = window.crypto.random(32);
>> +    for(t = 0; t < z.length; ++t)
>> +      rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
>> +  }
>> +  while(rng_pptr < rng_psize) {  // extract some randomness from 
>> Math.random()
>> +    t = Math.floor(65536 * Math.random());
>> +    rng_pool[rng_pptr++] = t >>> 8;
>> +    rng_pool[rng_pptr++] = t & 255;
>> +  }
>> +  rng_pptr = 0;
>> +  rng_seed_time();
>> +  //rng_seed_int(window.screenX);
>> +  //rng_seed_int(window.screenY);
>> +}
>> +
>> +function rng_get_byte() {
>> +  if(rng_state == null) {
>> +    rng_seed_time();
>> +    rng_state = prng_newstate();
>> +    rng_state.init(rng_pool);
>> +    for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
>> +      rng_pool[rng_pptr] = 0;
>> +    rng_pptr = 0;
>> +    //rng_pool = null;
>> +  }
>> +  // TODO: allow reseeding after first request
>> +  return rng_state.next();
>> +}
>> +
>> +function rng_get_bytes(ba) {
>> +  var i;
>> +  for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
>> +}
>> +
>> +function SecureRandom() {}
>> +
>> +SecureRandom.prototype.nextBytes = rng_get_bytes;
>> diff --git a/ui/js/spice/rsa.js b/ui/js/spice/rsa.js
>> new file mode 100644
>> index 0000000..ea0e45b
>> --- /dev/null
>> +++ b/ui/js/spice/rsa.js
>> @@ -0,0 +1,146 @@
>> +// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by 
>> Jeremy White on 6/1/2012
>> +
>> +/*
>> + * Copyright (c) 2003-2005  Tom Wu
>> + * All Rights Reserved.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining
>> + * a copy of this software and associated documentation files (the
>> + * "Software"), to deal in the Software without restriction, including
>> + * without limitation the rights to use, copy, modify, merge, publish,
>> + * distribute, sublicense, and/or sell copies of the Software, and to
>> + * permit persons to whom the Software is furnished to do so, 
>> subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be
>> + * included in all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
>> + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
>> + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
>> + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES 
>> WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT 
>> ADVISED OF
>> + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, 
>> ARISING OUT
>> + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + *
>> + * In addition, the following condition applies:
>> + *
>> + * All redistributions must retain an intact copy of this copyright 
>> notice
>> + * and disclaimer.
>> + */
>> +
>> +
>> +// Depends on jsbn.js and rng.js
>> +
>> +// Version 1.1: support utf-8 encoding in pkcs1pad2
>> +
>> +// convert a (hex) string to a bignum object
>> +function parseBigInt(str,r) {
>> +  return new BigInteger(str,r);
>> +}
>> +
>> +function linebrk(s,n) {
>> +  var ret = "";
>> +  var i = 0;
>> +  while(i + n < s.length) {
>> +    ret += s.substring(i,i+n) + "\n";
>> +    i += n;
>> +  }
>> +  return ret + s.substring(i,s.length);
>> +}
>> +
>> +function byte2Hex(b) {
>> +  if(b < 0x10)
>> +    return "0" + b.toString(16);
>> +  else
>> +    return b.toString(16);
>> +}
>> +
>> +// PKCS#1 (type 2, random) pad input string s to n bytes, and return 
>> a bigint
>> +function pkcs1pad2(s,n) {
>> +  if(n < s.length + 11) { // TODO: fix for utf-8
>> +    alert("Message too long for RSA");
>> +    return null;
>> +  }
>> +  var ba = new Array();
>> +  var i = s.length - 1;
>> +  while(i >= 0 && n > 0) {
>> +    var c = s.charCodeAt(i--);
>> +    if(c < 128) { // encode using utf-8
>> +      ba[--n] = c;
>> +    }
>> +    else if((c > 127) && (c < 2048)) {
>> +      ba[--n] = (c & 63) | 128;
>> +      ba[--n] = (c >> 6) | 192;
>> +    }
>> +    else {
>> +      ba[--n] = (c & 63) | 128;
>> +      ba[--n] = ((c >> 6) & 63) | 128;
>> +      ba[--n] = (c >> 12) | 224;
>> +    }
>> +  }
>> +  ba[--n] = 0;
>> +  var rng = new SecureRandom();
>> +  var x = new Array();
>> +  while(n > 2) { // random non-zero pad
>> +    x[0] = 0;
>> +    while(x[0] == 0) rng.nextBytes(x);
>> +    ba[--n] = x[0];
>> +  }
>> +  ba[--n] = 2;
>> +  ba[--n] = 0;
>> +  return new BigInteger(ba);
>> +}
>> +
>> +// "empty" RSA key constructor
>> +function RSAKey() {
>> +  this.n = null;
>> +  this.e = 0;
>> +  this.d = null;
>> +  this.p = null;
>> +  this.q = null;
>> +  this.dmp1 = null;
>> +  this.dmq1 = null;
>> +  this.coeff = null;
>> +}
>> +
>> +// Set the public key fields N and e from hex strings
>> +function RSASetPublic(N,E) {
>> +  if(N != null && E != null && N.length > 0 && E.length > 0) {
>> +    this.n = parseBigInt(N,16);
>> +    this.e = parseInt(E,16);
>> +  }
>> +  else
>> +    alert("Invalid RSA public key");
>> +}
>> +
>> +// Perform raw public operation on "x": return x^e (mod n)
>> +function RSADoPublic(x) {
>> +  return x.modPowInt(this.e, this.n);
>> +}
>> +
>> +// Return the PKCS#1 RSA encryption of "text" as an even-length hex 
>> string
>> +function RSAEncrypt(text) {
>> +  var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
>> +  if(m == null) return null;
>> +  var c = this.doPublic(m);
>> +  if(c == null) return null;
>> +  var h = c.toString(16);
>> +  if((h.length & 1) == 0) return h; else return "0" + h;
>> +}
>> +
>> +// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded 
>> string
>> +//function RSAEncryptB64(text) {
>> +//  var h = this.encrypt(text);
>> +//  if(h) return hex2b64(h); else return null;
>> +//}
>> +
>> +// protected
>> +RSAKey.prototype.doPublic = RSADoPublic;
>> +
>> +// public
>> +RSAKey.prototype.setPublic = RSASetPublic;
>> +RSAKey.prototype.encrypt = RSAEncrypt;
>> +//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
>> diff --git a/ui/js/spice/sha1.js b/ui/js/spice/sha1.js
>> new file mode 100644
>> index 0000000..363c83d
>> --- /dev/null
>> +++ b/ui/js/spice/sha1.js
>> @@ -0,0 +1,346 @@
>> +/*
>> + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, 
>> as defined
>> + * in FIPS 180-1
>> + * Version 2.2 Copyright Paul Johnston 2000 - 2009.
>> + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
>> + * Distributed under the BSD License
>> + * See http://pajhome.org.uk/crypt/md5 for details.
>> + */
>> +
>> + /* Downloaded 6/1/2012 from the above address by Jeremy White.
>> +    License reproduce here for completeness:
>> +
>> +Copyright (c) 1998 - 2009, Paul Johnston & Contributors
>> +All rights reserved.
>> +
>> +Redistribution and use in source and binary forms, with or without 
>> modification, are permitted provided that the following conditions 
>> are met:
>> +
>> +Redistributions of source code must retain the above copyright 
>> notice, this list of conditions and the following disclaimer. 
>> Redistributions in binary form must reproduce the above copyright 
>> notice, this list of conditions and the following disclaimer in the 
>> documentation and/or other materials provided with the distribution.
>> +
>> +Neither the name of the author nor the names of its contributors may 
>> be used to endorse or promote products derived from this software 
>> without specific prior written permission.
>> +
>> +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
>> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
>> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
>> A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
>> OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
>> SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
>> LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
>> DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
>> THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
>> (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
>> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>> +
>> + */
>> +
>> +/*
>> + * Configurable variables. You may need to tweak these to be 
>> compatible with
>> + * the server-side, but the defaults work in most cases.
>> + */
>> +var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - 
>> uppercase        */
>> +var b64pad  = ""; /* base-64 pad character. "=" for strict RFC 
>> compliance   */
>> +
>> +/*
>> + * These are the functions you'll usually want to call
>> + * They take string arguments and return either hex or base-64 
>> encoded strings
>> + */
>> +function hex_sha1(s)    { return 
>> rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
>> +function b64_sha1(s)    { return 
>> rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
>> +function any_sha1(s, e) { return 
>> rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
>> +function hex_hmac_sha1(k, d)
>> +  { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), 
>> str2rstr_utf8(d))); }
>> +function b64_hmac_sha1(k, d)
>> +  { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), 
>> str2rstr_utf8(d))); }
>> +function any_hmac_sha1(k, d, e)
>> +  { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), 
>> str2rstr_utf8(d)), e); }
>> +
>> +/*
>> + * Perform a simple self-test to see if the VM is working
>> + */
>> +function sha1_vm_test()
>> +{
>> +  return hex_sha1("abc").toLowerCase() == 
>> "a9993e364706816aba3e25717850c26c9cd0d89d";
>> +}
>> +
>> +/*
>> + * Calculate the SHA1 of a raw string
>> + */
>> +function rstr_sha1(s)
>> +{
>> +  return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
>> +}
>> +
>> +/*
>> + * Calculate the HMAC-SHA1 of a key and some data (raw strings)
>> + */
>> +function rstr_hmac_sha1(key, data)
>> +{
>> +  var bkey = rstr2binb(key);
>> +  if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
>> +
>> +  var ipad = Array(16), opad = Array(16);
>> +  for(var i = 0; i < 16; i++)
>> +  {
>> +    ipad[i] = bkey[i] ^ 0x36363636;
>> +    opad[i] = bkey[i] ^ 0x5C5C5C5C;
>> +  }
>> +
>> +  var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + 
>> data.length * 8);
>> +  return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
>> +}
>> +
>> +/*
>> + * Convert a raw string to a hex string
>> + */
>> +function rstr2hex(input)
>> +{
>> +  try { hexcase } catch(e) { hexcase=0; }
>> +  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
>> +  var output = "";
>> +  var x;
>> +  for(var i = 0; i < input.length; i++)
>> +  {
>> +    x = input.charCodeAt(i);
>> +    output += hex_tab.charAt((x >>> 4) & 0x0F)
>> +           +  hex_tab.charAt( x        & 0x0F);
>> +  }
>> +  return output;
>> +}
>> +
>> +/*
>> + * Convert a raw string to a base-64 string
>> + */
>> +function rstr2b64(input)
>> +{
>> +  try { b64pad } catch(e) { b64pad=''; }
>> +  var tab = 
>> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
>> +  var output = "";
>> +  var len = input.length;
>> +  for(var i = 0; i < len; i += 3)
>> +  {
>> +    var triplet = (input.charCodeAt(i) << 16)
>> +                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
>> +                | (i + 2 < len ? input.charCodeAt(i+2) : 0);
>> +    for(var j = 0; j < 4; j++)
>> +    {
>> +      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
>> +      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
>> +    }
>> +  }
>> +  return output;
>> +}
>> +
>> +/*
>> + * Convert a raw string to an arbitrary string encoding
>> + */
>> +function rstr2any(input, encoding)
>> +{
>> +  var divisor = encoding.length;
>> +  var remainders = Array();
>> +  var i, q, x, quotient;
>> +
>> +  /* Convert to an array of 16-bit big-endian values, forming the 
>> dividend */
>> +  var dividend = Array(Math.ceil(input.length / 2));
>> +  for(i = 0; i < dividend.length; i++)
>> +  {
>> +    dividend[i] = (input.charCodeAt(i * 2) << 8) | 
>> input.charCodeAt(i * 2 + 1);
>> +  }
>> +
>> +  /*
>> +   * Repeatedly perform a long division. The binary array forms the 
>> dividend,
>> +   * the length of the encoding is the divisor. Once computed, the 
>> quotient
>> +   * forms the dividend for the next step. We stop when the dividend 
>> is zero.
>> +   * All remainders are stored for later use.
>> +   */
>> +  while(dividend.length > 0)
>> +  {
>> +    quotient = Array();
>> +    x = 0;
>> +    for(i = 0; i < dividend.length; i++)
>> +    {
>> +      x = (x << 16) + dividend[i];
>> +      q = Math.floor(x / divisor);
>> +      x -= q * divisor;
>> +      if(quotient.length > 0 || q > 0)
>> +        quotient[quotient.length] = q;
>> +    }
>> +    remainders[remainders.length] = x;
>> +    dividend = quotient;
>> +  }
>> +
>> +  /* Convert the remainders to the output string */
>> +  var output = "";
>> +  for(i = remainders.length - 1; i >= 0; i--)
>> +    output += encoding.charAt(remainders[i]);
>> +
>> +  /* Append leading zero equivalents */
>> +  var full_length = Math.ceil(input.length * 8 /
>> +                                    (Math.log(encoding.length) / 
>> Math.log(2)))
>> +  for(i = output.length; i < full_length; i++)
>> +    output = encoding[0] + output;
>> +
>> +  return output;
>> +}
>> +
>> +/*
>> + * Encode a string as utf-8.
>> + * For efficiency, this assumes the input is valid utf-16.
>> + */
>> +function str2rstr_utf8(input)
>> +{
>> +  var output = "";
>> +  var i = -1;
>> +  var x, y;
>> +
>> +  while(++i < input.length)
>> +  {
>> +    /* Decode utf-16 surrogate pairs */
>> +    x = input.charCodeAt(i);
>> +    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
>> +    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
>> +    {
>> +      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
>> +      i++;
>> +    }
>> +
>> +    /* Encode output as utf-8 */
>> +    if(x <= 0x7F)
>> +      output += String.fromCharCode(x);
>> +    else if(x <= 0x7FF)
>> +      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
>> +                                    0x80 | ( x         & 0x3F));
>> +    else if(x <= 0xFFFF)
>> +      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
>> +                                    0x80 | ((x >>> 6 ) & 0x3F),
>> +                                    0x80 | ( x         & 0x3F));
>> +    else if(x <= 0x1FFFFF)
>> +      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
>> +                                    0x80 | ((x >>> 12) & 0x3F),
>> +                                    0x80 | ((x >>> 6 ) & 0x3F),
>> +                                    0x80 | ( x         & 0x3F));
>> +  }
>> +  return output;
>> +}
>> +
>> +/*
>> + * Encode a string as utf-16
>> + */
>> +function str2rstr_utf16le(input)
>> +{
>> +  var output = "";
>> +  for(var i = 0; i < input.length; i++)
>> +    output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
>> +                                  (input.charCodeAt(i) >>> 8) & 0xFF);
>> +  return output;
>> +}
>> +
>> +function str2rstr_utf16be(input)
>> +{
>> +  var output = "";
>> +  for(var i = 0; i < input.length; i++)
>> +    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
>> +                                   input.charCodeAt(i) & 0xFF);
>> +  return output;
>> +}
>> +
>> +/*
>> + * Convert a raw string to an array of big-endian words
>> + * Characters >255 have their high-byte silently ignored.
>> + */
>> +function rstr2binb(input)
>> +{
>> +  var output = Array(input.length >> 2);
>> +  for(var i = 0; i < output.length; i++)
>> +    output[i] = 0;
>> +  for(var i = 0; i < input.length * 8; i += 8)
>> +    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
>> +  return output;
>> +}
>> +
>> +/*
>> + * Convert an array of big-endian words to a string
>> + */
>> +function binb2rstr(input)
>> +{
>> +  var output = "";
>> +  for(var i = 0; i < input.length * 32; i += 8)
>> +    output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 
>> 0xFF);
>> +  return output;
>> +}
>> +
>> +/*
>> + * Calculate the SHA-1 of an array of big-endian words, and a bit 
>> length
>> + */
>> +function binb_sha1(x, len)
>> +{
>> +  /* append padding */
>> +  x[len >> 5] |= 0x80 << (24 - len % 32);
>> +  x[((len + 64 >> 9) << 4) + 15] = len;
>> +
>> +  var w = Array(80);
>> +  var a =  1732584193;
>> +  var b = -271733879;
>> +  var c = -1732584194;
>> +  var d =  271733878;
>> +  var e = -1009589776;
>> +
>> +  for(var i = 0; i < x.length; i += 16)
>> +  {
>> +    var olda = a;
>> +    var oldb = b;
>> +    var oldc = c;
>> +    var oldd = d;
>> +    var olde = e;
>> +
>> +    for(var j = 0; j < 80; j++)
>> +    {
>> +      if(j < 16) w[j] = x[i + j];
>> +      else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
>> +      var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
>> +                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
>> +      e = d;
>> +      d = c;
>> +      c = bit_rol(b, 30);
>> +      b = a;
>> +      a = t;
>> +    }
>> +
>> +    a = safe_add(a, olda);
>> +    b = safe_add(b, oldb);
>> +    c = safe_add(c, oldc);
>> +    d = safe_add(d, oldd);
>> +    e = safe_add(e, olde);
>> +  }
>> +  return Array(a, b, c, d, e);
>> +
>> +}
>> +
>> +/*
>> + * Perform the appropriate triplet combination function for the current
>> + * iteration
>> + */
>> +function sha1_ft(t, b, c, d)
>> +{
>> +  if(t < 20) return (b & c) | ((~b) & d);
>> +  if(t < 40) return b ^ c ^ d;
>> +  if(t < 60) return (b & c) | (b & d) | (c & d);
>> +  return b ^ c ^ d;
>> +}
>> +
>> +/*
>> + * Determine the appropriate additive constant for the current 
>> iteration
>> + */
>> +function sha1_kt(t)
>> +{
>> +  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
>> +         (t < 60) ? -1894007588 : -899497514;
>> +}
>> +
>> +/*
>> + * Add integers, wrapping at 2^32. This uses 16-bit operations 
>> internally
>> + * to work around bugs in some JS interpreters.
>> + */
>> +function safe_add(x, y)
>> +{
>> +  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
>> +  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
>> +  return (msw << 16) | (lsw & 0xFFFF);
>> +}
>> +
>> +/*
>> + * Bitwise rotate a 32-bit number to the left.
>> + */
>> +function bit_rol(num, cnt)
>> +{
>> +  return (num << cnt) | (num >>> (32 - cnt));
>> +}
>> diff --git a/ui/js/spice/spiceconn.js b/ui/js/spice/spiceconn.js
>> new file mode 100644
>> index 0000000..7d26913
>> --- /dev/null
>> +++ b/ui/js/spice/spiceconn.js
>> @@ -0,0 +1,447 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceConn
>> +**      This is the base Javascript class for establishing and
>> +**  managing a connection to a Spice Server.
>> +**  It is used to provide core functionality to the Spice main,
>> +**  display, inputs, and cursor channels.  See main.js for
>> +**  usage.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceConn(o)
>> +{
>> +    if (o === undefined || o.uri === undefined || ! o.uri)
>> +        throw new Error("You must specify a uri");
>> +
>> +    this.ws = new WebSocket(o.uri, 'binary');
>> +
>> +    if (! this.ws.binaryType)
>> +        throw new Error("WebSocket doesn't support binaryType. Try a 
>> different browser.");
>> +
>> +    this.connection_id = o.connection_id !== undefined ? 
>> o.connection_id : 0;
>> +    this.type = o.type !== undefined ? o.type : SPICE_CHANNEL_MAIN;
>> +    this.chan_id = o.chan_id !== undefined ? o.chan_id : 0;
>> +    if (o.parent !== undefined)
>> +    {
>> +        this.parent = o.parent;
>> +        this.message_id = o.parent.message_id;
>> +        this.password = o.parent.password;
>> +    }
>> +    if (o.screen_id !== undefined)
>> +        this.screen_id = o.screen_id;
>> +    if (o.dump_id !== undefined)
>> +        this.dump_id = o.dump_id;
>> +    if (o.message_id !== undefined)
>> +        this.message_id = o.message_id;
>> +    if (o.password !== undefined)
>> +        this.password = o.password;
>> +    if (o.onerror !== undefined)
>> +        this.onerror = o.onerror;
>> +
>> +    this.state = "connecting";
>> +    this.ws.parent = this;
>> +    this.wire_reader = new SpiceWireReader(this, this.process_inbound);
>> +    this.messages_sent = 0;
>> +    this.warnings = [];
>> +
>> +    this.ws.addEventListener('open', function(e) {
>> +        DEBUG > 0 && console.log(">> WebSockets.onopen");
>> +        DEBUG > 0 && console.log("id " + this.parent.connection_id 
>> +"; type " + this.parent.type);
>> +
>> + 
>> /***********************************************************************
>> +        **          WHERE IT ALL REALLY BEGINS
>> + 
>> ***********************************************************************/
>> +        this.parent.send_hdr();
>> + 
>> this.parent.wire_reader.request(SpiceLinkHeader.prototype.buffer_size());
>> +        this.parent.state = "start";
>> +    });
>> +    this.ws.addEventListener('error', function(e) {
>> +        this.parent.log_err(">> WebSockets.onerror" + e.toString());
>> +        this.parent.report_error(e);
>> +    });
>> +    this.ws.addEventListener('close', function(e) {
>> +        DEBUG > 0 && console.log(">> WebSockets.onclose");
>> +        DEBUG > 0 && console.log("id " + this.parent.connection_id 
>> +"; type " + this.parent.type);
>> +        DEBUG > 0 && console.log(e);
>> +        if (this.parent.state != "closing" && this.parent.state != 
>> "error" && this.parent.onerror !== undefined)
>> +        {
>> +            var e;
>> +            if (this.parent.state == "connecting")
>> +                e = new Error("Connection refused.");
>> +            else if (this.parent.state == "start" || 
>> this.parent.state == "link")
>> +                e = new Error("Unexpected protocol mismatch.");
>> +            else if (this.parent.state == "ticket")
>> +                e = new Error("Bad password.");
>> +            else
>> +                e = new Error("Unexpected close while " + 
>> this.parent.state);
>> +
>> +            this.parent.onerror(e);
>> +            this.parent.log_err(e.toString());
>> +        }
>> +    });
>> +
>> +    if (this.ws.readyState == 2 || this.ws.readyState == 3)
>> +        throw new Error("Unable to connect to " + o.uri);
>> +
>> +    this.timeout = window.setTimeout(spiceconn_timeout, 
>> SPICE_CONNECT_TIMEOUT, this);
>> +}
>> +
>> +SpiceConn.prototype =
>> +{
>> +    send_hdr : function ()
>> +    {
>> +        var hdr = new SpiceLinkHeader;
>> +        var msg = new SpiceLinkMess;
>> +
>> +        msg.connection_id = this.connection_id;
>> +        msg.channel_type = this.type;
>> +        // FIXME - we're not setting a channel_id...
>> +        msg.common_caps.push(
>> +            (1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION) |
>> +            (1 << SPICE_COMMON_CAP_MINI_HEADER)
>> +            );
>> +
>> +        hdr.size = msg.buffer_size();
>> +
>> +        var mb = new ArrayBuffer(hdr.buffer_size() + 
>> msg.buffer_size());
>> +        hdr.to_buffer(mb);
>> +        msg.to_buffer(mb, hdr.buffer_size());
>> +
>> +        DEBUG > 1 && console.log("Sending header:");
>> +        DEBUG > 2 && hexdump_buffer(mb);
>> +        this.ws.send(mb);
>> +    },
>> +
>> +    send_ticket: function(ticket)
>> +    {
>> +        var hdr = new SpiceLinkAuthTicket();
>> +        hdr.auth_mechanism = SPICE_COMMON_CAP_AUTH_SPICE;
>> +        // FIXME - we need to implement RSA to make this work right
>> +        hdr.encrypted_data = ticket;
>> +        var mb = new ArrayBuffer(hdr.buffer_size());
>> +
>> +        hdr.to_buffer(mb);
>> +        DEBUG > 1 && console.log("Sending ticket:");
>> +        DEBUG > 2 && hexdump_buffer(mb);
>> +        this.ws.send(mb);
>> +    },
>> +
>> +    send_msg: function(msg)
>> +    {
>> +        var mb = new ArrayBuffer(msg.buffer_size());
>> +        msg.to_buffer(mb);
>> +        this.messages_sent++;
>> +        DEBUG > 0 && console.log(">> hdr " + this.channel_type() + " 
>> type " + msg.type + " size " + mb.byteLength);
>> +        DEBUG > 2 && hexdump_buffer(mb);
>> +        this.ws.send(mb);
>> +    },
>> +
>> +    process_inbound: function(mb, saved_header)
>> +    {
>> +        DEBUG > 2 && console.log(this.type + ": processing message 
>> of size " + mb.byteLength + "; state is " + this.state);
>> +        if (this.state == "ready")
>> +        {
>> +            if (saved_header == undefined)
>> +            {
>> +                var msg = new SpiceMiniData(mb);
>> +
>> +                if (msg.type > 500)
>> +                {
>> +                    alert("Something has gone very wrong; we think 
>> we have message of type " + msg.type);
>> +                    debugger;
>> +                }
>> +
>> +                if (msg.size == 0)
>> +                {
>> +                    this.process_message(msg);
>> + this.wire_reader.request(SpiceMiniData.prototype.buffer_size());
>> +                }
>> +                else
>> +                {
>> +                    this.wire_reader.request(msg.size);
>> +                    this.wire_reader.save_header(msg);
>> +                }
>> +            }
>> +            else
>> +            {
>> +                saved_header.data = mb;
>> +                this.process_message(saved_header);
>> + this.wire_reader.request(SpiceMiniData.prototype.buffer_size());
>> +                this.wire_reader.save_header(undefined);
>> +            }
>> +        }
>> +
>> +        else if (this.state == "start")
>> +        {
>> +            this.reply_hdr = new SpiceLinkHeader(mb);
>> +            if (this.reply_hdr.magic != SPICE_MAGIC)
>> +            {
>> +                this.state = "error";
>> +                var e = new Error('Error: magic mismatch: ' + 
>> this.reply_hdr.magic);
>> +                this.report_error(e);
>> +            }
>> +            else
>> +            {
>> +                // FIXME - Determine major/minor version requirements
>> +                this.wire_reader.request(this.reply_hdr.size);
>> +                this.state = "link";
>> +            }
>> +        }
>> +
>> +        else if (this.state == "link")
>> +        {
>> +            this.reply_link = new SpiceLinkReply(mb);
>> +             // FIXME - Screen the caps - require minihdr at least, 
>> right?
>> +            if (this.reply_link.error)
>> +            {
>> +                this.state = "error";
>> +                var e = new Error('Error: reply link error ' + 
>> this.reply_link.error);
>> +                this.report_error(e);
>> +            }
>> +            else
>> +            {
>> + this.send_ticket(rsa_encrypt(this.reply_link.pub_key, this.password 
>> + String.fromCharCode(0)));
>> +                this.state = "ticket";
>> + this.wire_reader.request(SpiceLinkAuthReply.prototype.buffer_size());
>> +            }
>> +        }
>> +
>> +        else if (this.state == "ticket")
>> +        {
>> +            this.auth_reply = new SpiceLinkAuthReply(mb);
>> +            if (this.auth_reply.auth_code == SPICE_LINK_ERR_OK)
>> +            {
>> +                DEBUG > 0 && console.log(this.type + ': Connected');
>> +
>> +                if (this.type == SPICE_CHANNEL_DISPLAY)
>> +                {
>> +                    // FIXME - pixmap and glz dictionary config info?
>> +                    var dinit = new SpiceMsgcDisplayInit();
>> +                    var reply = new SpiceMiniData();
>> +                    reply.build_msg(SPICE_MSGC_DISPLAY_INIT, dinit);
>> +                    DEBUG > 0 && console.log("Request display init");
>> +                    this.send_msg(reply);
>> +                }
>> +                this.state = "ready";
>> + this.wire_reader.request(SpiceMiniData.prototype.buffer_size());
>> +                if (this.timeout)
>> +                {
>> +                    window.clearTimeout(this.timeout);
>> +                    delete this.timeout;
>> +                }
>> +            }
>> +            else
>> +            {
>> +                this.state = "error";
>> +                if (this.auth_reply.auth_code == 
>> SPICE_LINK_ERR_PERMISSION_DENIED)
>> +                {
>> +                    var e = new Error("Permission denied.");
>> +                }
>> +                else
>> +                {
>> +                    var e = new Error("Unexpected link error " + 
>> this.auth_reply.auth_code);
>> +                }
>> +                this.report_error(e);
>> +            }
>> +        }
>> +    },
>> +
>> +    process_common_messages : function(msg)
>> +    {
>> +        if (msg.type == SPICE_MSG_SET_ACK)
>> +        {
>> +            var ack = new SpiceMsgSetAck(msg.data);
>> +            // FIXME - what to do with generation?
>> +            this.ack_window = ack.window;
>> +            DEBUG > 1 && console.log(this.type + ": set ack to " + 
>> ack.window);
>> +            this.msgs_until_ack = this.ack_window;
>> +            var ackack = new SpiceMsgcAckSync(ack);
>> +            var reply = new SpiceMiniData();
>> +            reply.build_msg(SPICE_MSGC_ACK_SYNC, ackack);
>> +            this.send_msg(reply);
>> +            return true;
>> +        }
>> +
>> +        if (msg.type == SPICE_MSG_PING)
>> +        {
>> +            DEBUG > 1 && console.log("ping!");
>> +            var pong = new SpiceMiniData;
>> +            pong.type = SPICE_MSGC_PONG;
>> +            if (msg.data)
>> +            {
>> +                pong.data = msg.data.slice(0, 12);
>> +            }
>> +            pong.size = pong.buffer_size();
>> +            this.send_msg(pong);
>> +            return true;
>> +        }
>> +
>> +        if (msg.type == SPICE_MSG_NOTIFY)
>> +        {
>> +            // FIXME - Visibility + what
>> +            var notify = new SpiceMsgNotify(msg.data);
>> +            if (notify.severity == SPICE_NOTIFY_SEVERITY_ERROR)
>> +                this.log_err(notify.message);
>> +            else if (notify.severity == SPICE_NOTIFY_SEVERITY_WARN )
>> +                this.log_warn(notify.message);
>> +            else
>> +                this.log_info(notify.message);
>> +            return true;
>> +        }
>> +
>> +        return false;
>> +
>> +    },
>> +
>> +    process_message: function(msg)
>> +    {
>> +        var rc;
>> +        DEBUG > 0 && console.log("<< hdr " + this.channel_type() + " 
>> type " + msg.type + " size " + (msg.data && msg.data.byteLength));
>> +        rc = this.process_common_messages(msg);
>> +        if (rc)
>> +            return rc;
>> +
>> +        if (this.process_channel_message)
>> +            rc = this.process_channel_message(msg);
>> +        else
>> +        {
>> +            this.log_err(this.type + ": No message handlers for this 
>> channel; message " + msg.type);
>> +            return false;
>> +        }
>> +
>> +        if (! rc)
>> +            this.log_warn(this.type + ": Unknown message type " + 
>> msg.type + "!");
>> +
>> +        if (this.msgs_until_ack !== undefined && this.ack_window)
>> +        {
>> +            this.msgs_until_ack--;
>> +            if (this.msgs_until_ack <= 0)
>> +            {
>> +                this.msgs_until_ack = this.ack_window;
>> +                var ack = new SpiceMiniData();
>> +                ack.type = SPICE_MSGC_ACK;
>> +                this.send_msg(ack);
>> +                DEBUG > 1 && console.log(this.type + ": sent ack");
>> +            }
>> +        }
>> +
>> +        return rc;
>> +    },
>> +
>> +    channel_type: function()
>> +    {
>> +        if (this.type == SPICE_CHANNEL_MAIN)
>> +            return "main";
>> +        else if (this.type == SPICE_CHANNEL_DISPLAY)
>> +            return "display";
>> +        else if (this.type == SPICE_CHANNEL_INPUTS)
>> +            return "inputs";
>> +        else if (this.type == SPICE_CHANNEL_CURSOR)
>> +            return "cursor";
>> +        return "unknown-" + this.type;
>> +
>> +    },
>> +
>> +    log_info: function()
>> +    {
>> +        var msg = Array.prototype.join.call(arguments, " ");
>> +        console.log(msg);
>> +        if (this.message_id)
>> +        {
>> +            var p = document.createElement("p");
>> +            p.appendChild(document.createTextNode(msg));
>> +            p.className += "spice-message-info";
>> + document.getElementById(this.message_id).appendChild(p);
>> +        }
>> +    },
>> +
>> +    log_warn: function()
>> +    {
>> +        var msg = Array.prototype.join.call(arguments, " ");
>> +        console.log("WARNING: " + msg);
>> +        if (this.message_id)
>> +        {
>> +            var p = document.createElement("p");
>> +            p.appendChild(document.createTextNode(msg));
>> +            p.className += "spice-message-warning";
>> + document.getElementById(this.message_id).appendChild(p);
>> +        }
>> +    },
>> +
>> +    log_err: function()
>> +    {
>> +        var msg = Array.prototype.join.call(arguments, " ");
>> +        console.log("ERROR: " + msg);
>> +        if (this.message_id)
>> +        {
>> +            var p = document.createElement("p");
>> +            p.appendChild(document.createTextNode(msg));
>> +            p.className += "spice-message-error";
>> + document.getElementById(this.message_id).appendChild(p);
>> +        }
>> +    },
>> +
>> +    known_unimplemented: function(type, msg)
>> +    {
>> +        if ( (!this.warnings[type]) || DEBUG > 1)
>> +        {
>> +            var str = "";
>> +            if (DEBUG <= 1)
>> +                str = " [ further notices suppressed ]";
>> +            this.log_warn("Unimplemented function " + type + "(" + 
>> msg + ")" + str);
>> +            this.warnings[type] = true;
>> +        }
>> +    },
>> +
>> +    report_error: function(e)
>> +    {
>> +        this.log_err(e.toString());
>> +        if (this.onerror != undefined)
>> +            this.onerror(e);
>> +        else
>> +            throw(e);
>> +    },
>> +
>> +    cleanup: function()
>> +    {
>> +        if (this.timeout)
>> +        {
>> +            window.clearTimeout(this.timeout);
>> +            delete this.timeout;
>> +        }
>> +        if (this.ws)
>> +        {
>> +            this.ws.close();
>> +            this.ws = undefined;
>> +        }
>> +    },
>> +
>> +    handle_timeout: function()
>> +    {
>> +        var e = new Error("Connection timed out.");
>> +        this.report_error(e);
>> +    },
>> +}
>> +
>> +function spiceconn_timeout(sc)
>> +{
>> +    SpiceConn.prototype.handle_timeout.call(sc);
>> +}
>> diff --git a/ui/js/spice/spicedataview.js b/ui/js/spice/spicedataview.js
>> new file mode 100644
>> index 0000000..0699ed5
>> --- /dev/null
>> +++ b/ui/js/spice/spicedataview.js
>> @@ -0,0 +1,96 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  SpiceDataView
>> +** FIXME FIXME
>> +**    This is used because Firefox does not have DataView yet.
>> +**    We should use DataView if we have it, because it *has* to
>> +**    be faster than this code
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceDataView(buffer, byteOffset, byteLength)
>> +{
>> +    if (byteOffset !== undefined)
>> +    {
>> +        if (byteLength !== undefined)
>> +            this.u8 = new Uint8Array(buffer, byteOffset, byteLength);
>> +        else
>> +            this.u8 = new Uint8Array(buffer, byteOffset);
>> +    }
>> +    else
>> +        this.u8 = new Uint8Array(buffer);
>> +};
>> +
>> +SpiceDataView.prototype = {
>> +    getUint8:  function(byteOffset)
>> +    {
>> +        return this.u8[byteOffset];
>> +    },
>> +    getUint16:  function(byteOffset, littleEndian)
>> +    {
>> +        var low = 1, high = 0;
>> +        if (littleEndian)
>> +        {
>> +            low = 0;
>> +            high = 1;
>> +        }
>> +
>> +        return (this.u8[byteOffset + high] << 8) | 
>> this.u8[byteOffset + low];
>> +    },
>> +    getUint32:  function(byteOffset, littleEndian)
>> +    {
>> +        var low = 2, high = 0;
>> +        if (littleEndian)
>> +        {
>> +            low = 0;
>> +            high = 2;
>> +        }
>> +
>> +        return (this.getUint16(byteOffset + high, littleEndian) << 
>> 16) |
>> +                this.getUint16(byteOffset + low, littleEndian);
>> +    },
>> +    setUint8:  function(byteOffset, b)
>> +    {
>> +        this.u8[byteOffset] = (b & 0xff);
>> +    },
>> +    setUint16:  function(byteOffset, i, littleEndian)
>> +    {
>> +        var low = 1, high = 0;
>> +        if (littleEndian)
>> +        {
>> +            low = 0;
>> +            high = 1;
>> +        }
>> +        this.u8[byteOffset + high] = (i & 0xffff) >> 8;
>> +        this.u8[byteOffset + low]  = (i & 0x00ff);
>> +    },
>> +    setUint32:  function(byteOffset, w, littleEndian)
>> +    {
>> +        var low = 2, high = 0;
>> +        if (littleEndian)
>> +        {
>> +            low = 0;
>> +            high = 2;
>> +        }
>> +
>> +        this.setUint16(byteOffset + high, (w & 0xffffffff) >> 16, 
>> littleEndian);
>> +        this.setUint16(byteOffset + low,  (w & 0x0000ffff), 
>> littleEndian);
>> +    },
>> +}
>> diff --git a/ui/js/spice/spicemsg.js b/ui/js/spice/spicemsg.js
>> new file mode 100644
>> index 0000000..dac4b30
>> --- /dev/null
>> +++ b/ui/js/spice/spicemsg.js
>> @@ -0,0 +1,883 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  Spice messages
>> +**      This file contains classes for passing messages to and from
>> +**  a spice server.  This file should arguably be generated from
>> +**  spice.proto, but it was instead put together by hand.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function SpiceLinkHeader(a, at)
>> +{
>> +    this.magic = SPICE_MAGIC;
>> +    this.major_version = SPICE_VERSION_MAJOR;
>> +    this.minor_version = SPICE_VERSION_MINOR;
>> +    this.size = 0;
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceLinkHeader.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.magic = "";
>> +        for (var i = 0; i < 4; i++)
>> +            this.magic += String.fromCharCode(dv.getUint8(at + i));
>> +        at += 4;
>> +
>> +        this.major_version = dv.getUint32(at, true); at += 4;
>> +        this.minor_version = dv.getUint32(at, true); at += 4;
>> +        this.size = dv.getUint32(at, true); at += 4;
>> +    },
>> +
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        for (var i = 0; i < 4; i++)
>> +            dv.setUint8(at + i, this.magic.charCodeAt(i));
>> +        at += 4;
>> +
>> +        dv.setUint32(at, this.major_version, true); at += 4;
>> +        dv.setUint32(at, this.minor_version, true); at += 4;
>> +        dv.setUint32(at, this.size, true); at += 4;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 16;
>> +    },
>> +}
>> +
>> +function SpiceLinkMess(a, at)
>> +{
>> +    this.connection_id = 0;
>> +    this.channel_type = 0;
>> +    this.channel_id = 0;
>> +    this.common_caps = [];
>> +    this.channel_caps = [];
>> +
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceLinkMess.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var orig_at = at;
>> +        var dv = new SpiceDataView(a);
>> +        this.connection_id = dv.getUint32(at, true); at += 4;
>> +        this.channel_type = dv.getUint8(at, true); at++;
>> +        this.channel_id = dv.getUint8(at, true); at++;
>> +        var num_common_caps = dv.getUint32(at, true); at += 4;
>> +        var num_channel_caps  = dv.getUint32(at, true); at += 4;
>> +        var caps_offset = dv.getUint32(at, true); at += 4;
>> +
>> +        at = orig_at + caps_offset;
>> +        this.common_caps = [];
>> +        for (i = 0; i < num_common_caps; i++)
>> +        {
>> +            this.common_caps.unshift(dv.getUint32(at, true)); at += 4;
>> +        }
>> +
>> +        this.channel_caps = [];
>> +        for (i = 0; i < num_channel_caps; i++)
>> +        {
>> +            this.channel_caps.unshift(dv.getUint32(at, true)); at += 4;
>> +        }
>> +    },
>> +
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var orig_at = at;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.connection_id, true); at += 4;
>> +        dv.setUint8(at, this.channel_type, true); at++;
>> +        dv.setUint8(at, this.channel_id, true); at++;
>> +        dv.setUint32(at, this.common_caps.length, true); at += 4;
>> +        dv.setUint32(at, this.channel_caps.length, true); at += 4;
>> +        dv.setUint32(at, (at - orig_at) + 4, true); at += 4;
>> +
>> +        for (i = 0; i < this.common_caps.length; i++)
>> +        {
>> +            dv.setUint32(at, this.common_caps[i], true); at += 4;
>> +        }
>> +
>> +        for (i = 0; i < this.channel_caps.length; i++)
>> +        {
>> +            dv.setUint32(at, this.channel_caps[i], true); at += 4;
>> +        }
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 18 + (4 * this.common_caps.length) + (4 * 
>> this.channel_caps.length);
>> +    }
>> +}
>> +
>> +function SpiceLinkReply(a, at)
>> +{
>> +    this.error = 0;
>> +    this.pub_key = undefined;
>> +    this.common_caps = [];
>> +    this.channel_caps = [];
>> +
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceLinkReply.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var orig_at = at;
>> +        var dv = new SpiceDataView(a);
>> +        this.error = dv.getUint32(at, true); at += 4;
>> +
>> +        this.pub_key = create_rsa_from_mb(a, at);
>> +        at += SPICE_TICKET_PUBKEY_BYTES;
>> +
>> +        var num_common_caps = dv.getUint32(at, true); at += 4;
>> +        var num_channel_caps  = dv.getUint32(at, true); at += 4;
>> +        var caps_offset = dv.getUint32(at, true); at += 4;
>> +
>> +        at = orig_at + caps_offset;
>> +        this.common_caps = [];
>> +        for (i = 0; i < num_common_caps; i++)
>> +        {
>> +            this.common_caps.unshift(dv.getUint32(at, true)); at += 4;
>> +        }
>> +
>> +        this.channel_caps = [];
>> +        for (i = 0; i < num_channel_caps; i++)
>> +        {
>> +            this.channel_caps.unshift(dv.getUint32(at, true)); at += 4;
>> +        }
>> +    },
>> +}
>> +
>> +function SpiceLinkAuthTicket(a, at)
>> +{
>> +    this.auth_mechanism = 0;
>> +    this.encrypted_data = undefined;
>> +}
>> +
>> +SpiceLinkAuthTicket.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.auth_mechanism, true); at += 4;
>> +        for (i = 0; i < SPICE_TICKET_KEY_PAIR_LENGTH / 8; i++)
>> +        {
>> +            if (this.encrypted_data && i < this.encrypted_data.length)
>> +                dv.setUint8(at, this.encrypted_data[i], true);
>> +            else
>> +                dv.setUint8(at, 0, true);
>> +            at++;
>> +        }
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 4 + (SPICE_TICKET_KEY_PAIR_LENGTH / 8);
>> +    }
>> +}
>> +
>> +function SpiceLinkAuthReply(a, at)
>> +{
>> +    this.auth_code = 0;
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceLinkAuthReply.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.auth_code = dv.getUint32(at, true); at += 4;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 4;
>> +    }
>> +}
>> +
>> +function SpiceMiniData(a, at)
>> +{
>> +    this.type = 0;
>> +    this.size = 0;
>> +    this.data = undefined;
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMiniData.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        this.type = dv.getUint16(at, true); at += 2;
>> +        this.size = dv.getUint32(at, true); at += 4;
>> +        if (a.byteLength > at)
>> +        {
>> +            this.data = a.slice(at);
>> +            at += this.data.byteLength;
>> +        }
>> +    },
>> +    to_buffer : function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint16(at, this.type, true); at += 2;
>> +        dv.setUint32(at, this.data ? this.data.byteLength : 0, 
>> true); at += 4;
>> +        if (this.data && this.data.byteLength > 0)
>> +        {
>> +            var u8arr = new Uint8Array(this.data);
>> +            for (i = 0; i < u8arr.length; i++, at++)
>> +                dv.setUint8(at, u8arr[i], true);
>> +        }
>> +    },
>> +    build_msg : function(in_type,  extra)
>> +    {
>> +        this.type = in_type;
>> +        this.size = extra.buffer_size();
>> +        this.data = new ArrayBuffer(this.size);
>> +        extra.to_buffer(this.data);
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        if (this.data)
>> +            return 6 + this.data.byteLength;
>> +        else
>> +            return 6;
>> +    },
>> +}
>> +
>> +function SpiceMsgChannels(a, at)
>> +{
>> +    this.num_of_channels = 0;
>> +    this.channels = [];
>> +    if (a !== undefined)
>> +        this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgChannels.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        this.num_of_channels = dv.getUint32(at, true); at += 4;
>> +        for (i = 0; i < this.num_of_channels; i++)
>> +        {
>> +            var chan = new SpiceChannelId();
>> +            at = chan.from_dv(dv, at, a);
>> +            this.channels.push(chan);
>> +        }
>> +    },
>> +}
>> +
>> +function SpiceMsgMainInit(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgMainInit.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.session_id = dv.getUint32(at, true); at += 4;
>> +        this.display_channels_hint = dv.getUint32(at, true); at += 4;
>> +        this.supported_mouse_modes = dv.getUint32(at, true); at += 4;
>> +        this.current_mouse_mode = dv.getUint32(at, true); at += 4;
>> +        this.agent_connected = dv.getUint32(at, true); at += 4;
>> +        this.agent_tokens = dv.getUint32(at, true); at += 4;
>> +        this.multi_media_time = dv.getUint32(at, true); at += 4;
>> +        this.ram_hint = dv.getUint32(at, true); at += 4;
>> +    },
>> +}
>> +
>> +function SpiceMsgMainMouseMode(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgMainMouseMode.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.supported_modes = dv.getUint16(at, true); at += 2;
>> +        this.current_mode = dv.getUint16(at, true); at += 2;
>> +    },
>> +}
>> +
>> +function SpiceMsgSetAck(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgSetAck.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.generation = dv.getUint32(at, true); at += 4;
>> +        this.window = dv.getUint32(at, true); at += 4;
>> +    },
>> +}
>> +
>> +function SpiceMsgcAckSync(ack)
>> +{
>> +    this.generation = ack.generation;
>> +}
>> +
>> +SpiceMsgcAckSync.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.generation, true); at += 4;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 4;
>> +    }
>> +}
>> +
>> +function SpiceMsgcMainMouseModeRequest(mode)
>> +{
>> +    this.mode = mode;
>> +}
>> +
>> +SpiceMsgcMainMouseModeRequest.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint16(at, this.mode, true); at += 2;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 2;
>> +    }
>> +}
>> +
>> +function SpiceMsgNotify(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgNotify.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var i;
>> +        var dv = new SpiceDataView(a);
>> +        this.time_stamp = [];
>> +        this.time_stamp[0] = dv.getUint32(at, true); at += 4;
>> +        this.time_stamp[1] = dv.getUint32(at, true); at += 4;
>> +        this.severity = dv.getUint32(at, true); at += 4;
>> +        this.visibility = dv.getUint32(at, true); at += 4;
>> +        this.what = dv.getUint32(at, true); at += 4;
>> +        this.message_len = dv.getUint32(at, true); at += 4;
>> +        this.message = "";
>> +        for (i = 0; i < this.message_len; i++)
>> +        {
>> +            var c = dv.getUint8(at, true); at++;
>> +            this.message += String.fromCharCode(c);
>> +        }
>> +    },
>> +}
>> +
>> +function SpiceMsgcDisplayInit()
>> +{
>> +    this.pixmap_cache_id = 1;
>> +    this.glz_dictionary_id = 0;
>> +    this.pixmap_cache_size = 10 * 1024 * 1024;
>> +    this.glz_dictionary_window_size = 0;
>> +}
>> +
>> +SpiceMsgcDisplayInit.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint8(at, this.pixmap_cache_id, true); at++;
>> +        dv.setUint32(at, 0, true); at += 4;
>> +        dv.setUint32(at, this.pixmap_cache_size, true); at += 4;
>> +        dv.setUint8(at, this.glz_dictionary_id, true); at++;
>> +        dv.setUint32(at, this.glz_dictionary_window_size, true); at 
>> += 4;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 14;
>> +    }
>> +}
>> +
>> +function SpiceMsgDisplayBase()
>> +{
>> +}
>> +
>> +SpiceMsgDisplayBase.prototype =
>> +{
>> +    from_dv : function(dv, at, mb)
>> +    {
>> +        this.surface_id = dv.getUint32(at, true); at += 4;
>> +        this.box = new SpiceRect;
>> +        at = this.box.from_dv(dv, at, mb);
>> +        this.clip = new SpiceClip;
>> +        return this.clip.from_dv(dv, at, mb);
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayDrawCopy(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayDrawCopy.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.base = new SpiceMsgDisplayBase;
>> +        at = this.base.from_dv(dv, at, a);
>> +        this.data = new SpiceCopy;
>> +        return this.data.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayDrawFill(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayDrawFill.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.base = new SpiceMsgDisplayBase;
>> +        at = this.base.from_dv(dv, at, a);
>> +        this.data = new SpiceFill;
>> +        return this.data.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayCopyBits(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayCopyBits.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.base = new SpiceMsgDisplayBase;
>> +        at = this.base.from_dv(dv, at, a);
>> +        this.src_pos = new SpicePoint;
>> +        return this.src_pos.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +
>> +function SpiceMsgSurfaceCreate(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgSurfaceCreate.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.surface = new SpiceSurface;
>> +        return this.surface.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +function SpiceMsgSurfaceDestroy(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgSurfaceDestroy.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.surface_id = dv.getUint32(at, true); at += 4;
>> +    },
>> +}
>> +
>> +function SpiceMsgInputsInit(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgInputsInit.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.keyboard_modifiers = dv.getUint16(at, true); at += 2;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceMsgInputsKeyModifiers(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgInputsKeyModifiers.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.keyboard_modifiers = dv.getUint16(at, true); at += 2;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceMsgCursorInit(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgCursorInit.prototype =
>> +{
>> +    from_buffer: function(a, at, mb)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.position = new SpicePoint16;
>> +        at = this.position.from_dv(dv, at, mb);
>> +        this.trail_length = dv.getUint16(at, true); at += 2;
>> +        this.trail_frequency = dv.getUint16(at, true); at += 2;
>> +        this.visible = dv.getUint8(at, true); at ++;
>> +        this.cursor = new SpiceCursor;
>> +        return this.cursor.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +
>> +function SpiceMsgCursorSet(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgCursorSet.prototype =
>> +{
>> +    from_buffer: function(a, at, mb)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.position = new SpicePoint16;
>> +        at = this.position.from_dv(dv, at, mb);
>> +        this.visible = dv.getUint8(at, true); at ++;
>> +        this.cursor = new SpiceCursor;
>> +        return this.cursor.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +
>> +function SpiceMsgcMousePosition(sc, e)
>> +{
>> +    // FIXME - figure out how to correctly compute display_id
>> +    this.display_id = 0;
>> +    this.buttons_state = sc.buttons_state;
>> +    if (e)
>> +    {
>> +        this.x = e.clientX - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetLeft + 
>> document.body.scrollLeft;
>> +        this.y = e.clientY - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetTop + 
>> document.body.scrollTop;
>> +        sc.mousex = this.x;
>> +        sc.mousey = this.y;
>> +    }
>> +    else
>> +    {
>> +        this.x = this.y = this.buttons_state = 0;
>> +    }
>> +}
>> +
>> +SpiceMsgcMousePosition.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.x, true); at += 4;
>> +        dv.setUint32(at, this.y, true); at += 4;
>> +        dv.setUint16(at, this.buttons_state, true); at += 2;
>> +        dv.setUint8(at, this.display_id, true); at += 1;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 11;
>> +    }
>> +}
>> +
>> +function SpiceMsgcMouseMotion(sc, e)
>> +{
>> +    // FIXME - figure out how to correctly compute display_id
>> +    this.display_id = 0;
>> +    this.buttons_state = sc.buttons_state;
>> +    if (e)
>> +    {
>> +        this.x = e.clientX - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetLeft;
>> +        this.y = e.clientY - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetTop;
>> +
>> +        if (sc.mousex !== undefined)
>> +        {
>> +            this.x -= sc.mousex;
>> +            this.y -= sc.mousey;
>> +        }
>> +        sc.mousex = e.clientX - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetLeft;
>> +        sc.mousey = e.clientY - 
>> sc.display.surfaces[sc.display.primary_surface].canvas.offsetTop;
>> +    }
>> +    else
>> +    {
>> +        this.x = this.y = this.buttons_state = 0;
>> +    }
>> +}
>> +
>> +/* Use the same functions as for MousePosition */
>> +SpiceMsgcMouseMotion.prototype.to_buffer = 
>> SpiceMsgcMousePosition.prototype.to_buffer;
>> +SpiceMsgcMouseMotion.prototype.buffer_size = 
>> SpiceMsgcMousePosition.prototype.buffer_size;
>> +
>> +function SpiceMsgcMousePress(sc, e)
>> +{
>> +    if (e)
>> +    {
>> +        this.button = e.button + 1;
>> +        this.buttons_state = 1 << e.button;
>> +        sc.buttons_state = this.buttons_state;
>> +    }
>> +    else
>> +    {
>> +        this.button = SPICE_MOUSE_BUTTON_LEFT;
>> +        this.buttons_state = SPICE_MOUSE_BUTTON_MASK_LEFT;
>> +    }
>> +}
>> +
>> +SpiceMsgcMousePress.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint8(at, this.button, true); at ++;
>> +        dv.setUint16(at, this.buttons_state, true); at += 2;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 3;
>> +    }
>> +}
>> +
>> +function SpiceMsgcMouseRelease(sc, e)
>> +{
>> +    if (e)
>> +    {
>> +        this.button = e.button + 1;
>> +        this.buttons_state = 0;
>> +        sc.buttons_state = this.buttons_state;
>> +    }
>> +    else
>> +    {
>> +        this.button = SPICE_MOUSE_BUTTON_LEFT;
>> +        this.buttons_state = 0;
>> +    }
>> +}
>> +
>> +/* Use the same functions as for MousePress */
>> +SpiceMsgcMouseRelease.prototype.to_buffer = 
>> SpiceMsgcMousePress.prototype.to_buffer;
>> +SpiceMsgcMouseRelease.prototype.buffer_size = 
>> SpiceMsgcMousePress.prototype.buffer_size;
>> +
>> +
>> +function SpiceMsgcKeyDown(e)
>> +{
>> +    if (e)
>> +    {
>> +        this.code = keycode_to_start_scan(e.keyCode);
>> +    }
>> +    else
>> +    {
>> +        this.code = 0;
>> +    }
>> +}
>> +
>> +SpiceMsgcKeyDown.prototype =
>> +{
>> +    to_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        dv.setUint32(at, this.code, true); at += 4;
>> +        return at;
>> +    },
>> +    buffer_size: function()
>> +    {
>> +        return 4;
>> +    }
>> +}
>> +
>> +function SpiceMsgcKeyUp(e)
>> +{
>> +    if (e)
>> +    {
>> +        this.code = keycode_to_end_scan(e.keyCode);
>> +    }
>> +    else
>> +    {
>> +        this.code = 0;
>> +    }
>> +}
>> +
>> +/* Use the same functions as for KeyDown */
>> +SpiceMsgcKeyUp.prototype.to_buffer = 
>> SpiceMsgcKeyDown.prototype.to_buffer;
>> +SpiceMsgcKeyUp.prototype.buffer_size = 
>> SpiceMsgcKeyDown.prototype.buffer_size;
>> +
>> +function SpiceMsgDisplayStreamCreate(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayStreamCreate.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.surface_id = dv.getUint32(at, true); at += 4;
>> +        this.id = dv.getUint32(at, true); at += 4;
>> +        this.flags = dv.getUint8(at, true); at += 1;
>> +        this.codec_type = dv.getUint8(at, true); at += 1;
>> +        /*stamp */ at += 8;
>> +        this.stream_width = dv.getUint32(at, true); at += 4;
>> +        this.stream_height = dv.getUint32(at, true); at += 4;
>> +        this.src_width = dv.getUint32(at, true); at += 4;
>> +        this.src_height = dv.getUint32(at, true); at += 4;
>> +
>> +        this.dest = new SpiceRect;
>> +        at = this.dest.from_dv(dv, at, a);
>> +        this.clip = new SpiceClip;
>> +        this.clip.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +function SpiceStreamDataHeader(a, at)
>> +{
>> +}
>> +
>> +SpiceStreamDataHeader.prototype =
>> +{
>> +    from_dv : function(dv, at, mb)
>> +    {
>> +        this.id = dv.getUint32(at, true); at += 4;
>> +        this.multi_media_time = dv.getUint32(at, true); at += 4;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayStreamData(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayStreamData.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.base = new SpiceStreamDataHeader;
>> +        at = this.base.from_dv(dv, at, a);
>> +        this.data_size = dv.getUint32(at, true); at += 4;
>> +        this.data = dv.u8.subarray(at, at + this.data_size);
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayStreamClip(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayStreamClip.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.id = dv.getUint32(at, true); at += 4;
>> +        this.clip = new SpiceClip;
>> +        this.clip.from_dv(dv, at, a);
>> +    },
>> +}
>> +
>> +function SpiceMsgDisplayStreamDestroy(a, at)
>> +{
>> +    this.from_buffer(a, at);
>> +}
>> +
>> +SpiceMsgDisplayStreamDestroy.prototype =
>> +{
>> +    from_buffer: function(a, at)
>> +    {
>> +        at = at || 0;
>> +        var dv = new SpiceDataView(a);
>> +        this.id = dv.getUint32(at, true); at += 4;
>> +    },
>> +}
>> diff --git a/ui/js/spice/spicetype.js b/ui/js/spice/spicetype.js
>> new file mode 100644
>> index 0000000..b88ba28
>> --- /dev/null
>> +++ b/ui/js/spice/spicetype.js
>> @@ -0,0 +1,480 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  Spice types
>> +**      This file contains classes for common spice types.
>> +**  Generally, they are used as helpers in reading and writing messages
>> +**  to and from the server.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +
>> +function SpiceChannelId()
>> +{
>> +}
>> +SpiceChannelId.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.type = dv.getUint8(at, true); at ++;
>> +        this.id = dv.getUint8(at, true); at ++;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceRect()
>> +{
>> +}
>> +
>> +SpiceRect.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.top = dv.getUint32(at, true); at += 4;
>> +        this.left = dv.getUint32(at, true); at += 4;
>> +        this.bottom = dv.getUint32(at, true); at += 4;
>> +        this.right = dv.getUint32(at, true); at += 4;
>> +        return at;
>> +    },
>> +    is_same_size : function(r)
>> +    {
>> +        if ((this.bottom - this.top) == (r.bottom - r.top) &&
>> +            (this.right - this.left) == (r.right - r.left) )
>> +            return true;
>> +
>> +        return false;
>> +    },
>> +}
>> +
>> +function SpiceClipRects()
>> +{
>> +}
>> +
>> +SpiceClipRects.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        var i;
>> +        this.num_rects = dv.getUint32(at, true); at += 4;
>> +        if (this.num_rects > 0)
>> +            this.rects = [];
>> +        for (i = 0; i < this.num_rects; i++)
>> +        {
>> +            this.rects[i] = new SpiceRect();
>> +            at = this.rects[i].from_dv(dv, at, mb);
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceClip()
>> +{
>> +}
>> +
>> +SpiceClip.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.type = dv.getUint8(at, true); at ++;
>> +        if (this.type == SPICE_CLIP_TYPE_RECTS)
>> +        {
>> +            this.rects = new SpiceClipRects();
>> +            at = this.rects.from_dv(dv, at, mb);
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceImageDescriptor()
>> +{
>> +}
>> +
>> +SpiceImageDescriptor.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.id = dv.getUint32(at, true); at += 4;
>> +        this.id += (dv.getUint32(at, true) << 32); at += 4;
>> +        this.type  = dv.getUint8(at, true); at ++;
>> +        this.flags = dv.getUint8(at, true); at ++;
>> +        this.width = dv.getUint32(at, true); at += 4;
>> +        this.height= dv.getUint32(at, true); at += 4;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpicePalette()
>> +{
>> +}
>> +
>> +SpicePalette.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        var i;
>> +        this.unique = [];
>> +        this.unique[0] = dv.getUint32(at, true); at += 4;
>> +        this.unique[1] = dv.getUint32(at, true); at += 4;
>> +        this.num_ents = dv.getUint16(at, true); at += 2;
>> +        this.ents = [];
>> +        for (i = 0; i < this.num_ents; i++)
>> +        {
>> +            this.ents[i] = dv.getUint32(at, true); at += 4;
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceBitmap()
>> +{
>> +}
>> +
>> +SpiceBitmap.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.format = dv.getUint8(at, true); at++;
>> +        this.flags  = dv.getUint8(at, true); at++;
>> +        this.x = dv.getUint32(at, true); at += 4;
>> +        this.y = dv.getUint32(at, true); at += 4;
>> +        this.stride = dv.getUint32(at, true); at += 4;
>> +        if (this.flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE)
>> +        {
>> +            this.palette_id = [];
>> +            this.palette_id[0] = dv.getUint32(at, true); at += 4;
>> +            this.palette_id[1] = dv.getUint32(at, true); at += 4;
>> +        }
>> +        else
>> +        {
>> +            var offset = dv.getUint32(at, true); at += 4;
>> +            if (offset == 0)
>> +                this.palette = null;
>> +            else
>> +            {
>> +                this.palette = new SpicePalette;
>> +                this.palette.from_dv(dv, offset, mb);
>> +            }
>> +        }
>> +        // FIXME - should probably constrain this to the offset
>> +        //          of palette, if non zero
>> +        this.data   = mb.slice(at);
>> +        at += this.data.byteLength;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceImage()
>> +{
>> +}
>> +
>> +SpiceImage.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.descriptor = new SpiceImageDescriptor;
>> +        at = this.descriptor.from_dv(dv, at, mb);
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_LZ_RGB)
>> +        {
>> +            this.lz_rgb = new Object();
>> +            this.lz_rgb.length = dv.getUint32(at, true); at += 4;
>> +            var initial_at = at;
>> +            this.lz_rgb.magic = "";
>> +            for (var i = 3; i >= 0; i--)
>> +                this.lz_rgb.magic += 
>> String.fromCharCode(dv.getUint8(at + i));
>> +            at += 4;
>> +
>> +            // NOTE:  The endian change is *correct*
>> +            this.lz_rgb.version = dv.getUint32(at); at += 4;
>> +            this.lz_rgb.type = dv.getUint32(at); at += 4;
>> +            this.lz_rgb.width = dv.getUint32(at); at += 4;
>> +            this.lz_rgb.height = dv.getUint32(at); at += 4;
>> +            this.lz_rgb.stride = dv.getUint32(at); at += 4;
>> +            this.lz_rgb.top_down = dv.getUint32(at); at += 4;
>> +
>> +            var header_size = at - initial_at;
>> +
>> +            this.lz_rgb.data   = mb.slice(at, this.lz_rgb.length + 
>> at - header_size);
>> +            at += this.lz_rgb.data.byteLength;
>> +
>> +        }
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_BITMAP)
>> +        {
>> +            this.bitmap = new SpiceBitmap;
>> +            at = this.bitmap.from_dv(dv, at, mb);
>> +        }
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_SURFACE)
>> +        {
>> +            this.surface_id = dv.getUint32(at, true); at += 4;
>> +        }
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_JPEG)
>> +        {
>> +            this.jpeg = new Object;
>> +            this.jpeg.data_size = dv.getUint32(at, true); at += 4;
>> +            this.jpeg.data = mb.slice(at);
>> +            at += this.jpeg.data.byteLength;
>> +        }
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_JPEG_ALPHA)
>> +        {
>> +            this.jpeg_alpha = new Object;
>> +            this.jpeg_alpha.flags = dv.getUint8(at, true); at += 1;
>> +            this.jpeg_alpha.jpeg_size = dv.getUint32(at, true); at 
>> += 4;
>> +            this.jpeg_alpha.data_size = dv.getUint32(at, true); at 
>> += 4;
>> +            this.jpeg_alpha.data = mb.slice(at, 
>> this.jpeg_alpha.jpeg_size + at);
>> +            at += this.jpeg_alpha.data.byteLength;
>> +            // Alpha channel is an LZ image
>> +            this.jpeg_alpha.alpha = new Object();
>> +            this.jpeg_alpha.alpha.length = this.jpeg_alpha.data_size 
>> - this.jpeg_alpha.jpeg_size;
>> +            var initial_at = at;
>> +            this.jpeg_alpha.alpha.magic = "";
>> +            for (var i = 3; i >= 0; i--)
>> +                this.jpeg_alpha.alpha.magic += 
>> String.fromCharCode(dv.getUint8(at + i));
>> +            at += 4;
>> +
>> +            // NOTE:  The endian change is *correct*
>> +            this.jpeg_alpha.alpha.version = dv.getUint32(at); at += 4;
>> +            this.jpeg_alpha.alpha.type = dv.getUint32(at); at += 4;
>> +            this.jpeg_alpha.alpha.width = dv.getUint32(at); at += 4;
>> +            this.jpeg_alpha.alpha.height = dv.getUint32(at); at += 4;
>> +            this.jpeg_alpha.alpha.stride = dv.getUint32(at); at += 4;
>> +            this.jpeg_alpha.alpha.top_down = dv.getUint32(at); at += 4;
>> +
>> +            var header_size = at - initial_at;
>> +
>> +            this.jpeg_alpha.alpha.data   = mb.slice(at, 
>> this.jpeg_alpha.alpha.length + at - header_size);
>> +            at += this.jpeg_alpha.alpha.data.byteLength;
>> +        }
>> +
>> +        if (this.descriptor.type == SPICE_IMAGE_TYPE_QUIC)
>> +        {
>> +            this.quic = new SpiceQuic;
>> +            at = this.quic.from_dv(dv, at, mb);
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +
>> +function SpiceQMask()
>> +{
>> +}
>> +
>> +SpiceQMask.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.flags  = dv.getUint8(at, true); at++;
>> +        this.pos = new SpicePoint;
>> +        at = this.pos.from_dv(dv, at, mb);
>> +        var offset = dv.getUint32(at, true); at += 4;
>> +        if (offset == 0)
>> +        {
>> +            this.bitmap = null;
>> +            return at;
>> +        }
>> +
>> +        this.bitmap = new SpiceImage;
>> +        return this.bitmap.from_dv(dv, offset, mb);
>> +    },
>> +}
>> +
>> +
>> +function SpicePattern()
>> +{
>> +}
>> +
>> +SpicePattern.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        var offset = dv.getUint32(at, true); at += 4;
>> +        if (offset == 0)
>> +        {
>> +            this.pat = null;
>> +        }
>> +        else
>> +        {
>> +            this.pat = new SpiceImage;
>> +            this.pat.from_dv(dv, offset, mb);
>> +        }
>> +
>> +        this.pos = new SpicePoint;
>> +        return this.pos.from_dv(dv, at, mb);
>> +    }
>> +}
>> +
>> +function SpiceBrush()
>> +{
>> +}
>> +
>> +SpiceBrush.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.type = dv.getUint8(at, true); at ++;
>> +        if (this.type == SPICE_BRUSH_TYPE_SOLID)
>> +        {
>> +            this.color = dv.getUint32(at, true); at += 4;
>> +        }
>> +        else if (this.type == SPICE_BRUSH_TYPE_PATTERN)
>> +        {
>> +            this.pattern = new SpicePattern;
>> +            at = this.pattern.from_dv(dv, at, mb);
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceFill()
>> +{
>> +}
>> +
>> +SpiceFill.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.brush = new SpiceBrush;
>> +        at = this.brush.from_dv(dv, at, mb);
>> +        this.rop_descriptor = dv.getUint16(at, true); at += 2;
>> +        this.mask = new SpiceQMask;
>> +        return this.mask.from_dv(dv, at, mb);
>> +    },
>> +}
>> +
>> +
>> +function SpiceCopy()
>> +{
>> +}
>> +
>> +SpiceCopy.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        var offset = dv.getUint32(at, true); at += 4;
>> +        if (offset == 0)
>> +        {
>> +            this.src_bitmap = null;
>> +        }
>> +        else
>> +        {
>> +            this.src_bitmap = new SpiceImage;
>> +            this.src_bitmap.from_dv(dv, offset, mb);
>> +        }
>> +        this.src_area = new SpiceRect;
>> +        at = this.src_area.from_dv(dv, at, mb);
>> +        this.rop_descriptor = dv.getUint16(at, true); at += 2;
>> +        this.scale_mode = dv.getUint8(at, true); at ++;
>> +        this.mask = new SpiceQMask;
>> +        return this.mask.from_dv(dv, at, mb);
>> +    },
>> +}
>> +
>> +function SpicePoint16()
>> +{
>> +}
>> +
>> +SpicePoint16.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.x = dv.getUint16(at, true); at += 2;
>> +        this.y = dv.getUint16(at, true); at += 2;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpicePoint()
>> +{
>> +}
>> +
>> +SpicePoint.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.x = dv.getUint32(at, true); at += 4;
>> +        this.y = dv.getUint32(at, true); at += 4;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceCursorHeader()
>> +{
>> +}
>> +
>> +SpiceCursorHeader.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.unique = [];
>> +        this.unique[0] = dv.getUint32(at, true); at += 4;
>> +        this.unique[1] = dv.getUint32(at, true); at += 4;
>> +        this.type = dv.getUint8(at, true); at ++;
>> +        this.width = dv.getUint16(at, true); at += 2;
>> +        this.height = dv.getUint16(at, true); at += 2;
>> +        this.hot_spot_x = dv.getUint16(at, true); at += 2;
>> +        this.hot_spot_y = dv.getUint16(at, true); at += 2;
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceCursor()
>> +{
>> +}
>> +
>> +SpiceCursor.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.flags = dv.getUint16(at, true); at += 2;
>> +        if (this.flags & SPICE_CURSOR_FLAGS_NONE)
>> +            this.header = null;
>> +        else
>> +        {
>> +            this.header = new SpiceCursorHeader;
>> +            at = this.header.from_dv(dv, at, mb);
>> +            this.data   = mb.slice(at);
>> +            at += this.data.byteLength;
>> +        }
>> +        return at;
>> +    },
>> +}
>> +
>> +function SpiceSurface()
>> +{
>> +}
>> +
>> +SpiceSurface.prototype =
>> +{
>> +    from_dv: function(dv, at, mb)
>> +    {
>> +        this.surface_id = dv.getUint32(at, true); at += 4;
>> +        this.width = dv.getUint32(at, true); at += 4;
>> +        this.height = dv.getUint32(at, true); at += 4;
>> +        this.format = dv.getUint32(at, true); at += 4;
>> +        this.flags = dv.getUint32(at, true); at += 4;
>> +        return at;
>> +    },
>> +}
>> +
>> +/* FIXME - SpiceImage  types lz_plt, jpeg, zlib_glz, and jpeg_alpha are
>> +           completely unimplemented */
>> diff --git a/ui/js/spice/ticket.js b/ui/js/spice/ticket.js
>> new file mode 100644
>> index 0000000..96577a3
>> --- /dev/null
>> +++ b/ui/js/spice/ticket.js
>> @@ -0,0 +1,250 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +var SHA_DIGEST_LENGTH = 20;
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  General ticket RSA encryption functions - just good enough to
>> +**      support what we need to send back an encrypted ticket.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  OAEP padding functions.  Inspired by the OpenSSL implementation.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function MGF1(mask, seed)
>> +{
>> +    var i, j, outlen;
>> +    for (i = 0, outlen = 0; outlen < mask.length; i++)
>> +    {
>> +        var combo_buf = new String;
>> +
>> +        for (j = 0; j < seed.length; j++)
>> +            combo_buf += String.fromCharCode(seed[j]);
>> +        combo_buf += String.fromCharCode((i >> 24) & 255);
>> +        combo_buf += String.fromCharCode((i >> 16) & 255);
>> +        combo_buf += String.fromCharCode((i >> 8)  & 255);
>> +        combo_buf += String.fromCharCode((i)       & 255);
>> +
>> +        var combo_hash = rstr_sha1(combo_buf);
>> +        for (j = 0; j < combo_hash.length && outlen < mask.length; 
>> j++, outlen++)
>> +        {
>> +            mask[outlen] = combo_hash.charCodeAt(j);
>> +        }
>> +    }
>> +}
>> +
>> +
>> +function RSA_padding_add_PKCS1_OAEP(tolen, from, param)
>> +{
>> +    var seed = new Array(SHA_DIGEST_LENGTH);
>> +    var rand = new SecureRandom();
>> +    rand.nextBytes(seed);
>> +
>> +    var dblen = tolen - 1 - seed.length;
>> +    var db = new Array(dblen);
>> +    var padlen = dblen - from.length - 1;
>> +    var i;
>> +
>> +    if (param === undefined)
>> +        param = "";
>> +
>> +    if (padlen < SHA_DIGEST_LENGTH)
>> +    {
>> +        console.log("Error - data too large for key size.");
>> +        return null;
>> +    }
>> +
>> +    for (i = 0; i < padlen; i++)
>> +        db[i] = 0;
>> +
>> +    var param_hash = rstr_sha1(param);
>> +    for (i = 0; i < param_hash.length; i++)
>> +        db[i] = param_hash.charCodeAt(i);
>> +
>> +    db[padlen] = 1;
>> +    for (i = 0; i < from.length; i++)
>> +        db[i + padlen + 1] = from.charCodeAt(i);
>> +
>> +    var dbmask = new Array(dblen);
>> +    if (MGF1(dbmask, seed) < 0)
>> +            return null;
>> +
>> +    for (i = 0; i < dbmask.length; i++)
>> +        db[i] ^= dbmask[i];
>> +
>> +
>> +    var seedmask = Array(SHA_DIGEST_LENGTH);
>> +    if (MGF1(seedmask, db) < 0)
>> +            return null;
>> +
>> +    for (i = 0; i < seedmask.length; i++)
>> +        seed[i] ^= seedmask[i];
>> +
>> +    var ret = new String;
>> +    ret += String.fromCharCode(0);
>> +    for (i = 0; i < seed.length; i++)
>> +        ret += String.fromCharCode(seed[i]);
>> +    for (i = 0; i < db.length; i++)
>> +        ret += String.fromCharCode(db[i]);
>> +    return ret;
>> +}
>> +
>> +
>> +function asn_get_length(u8, at)
>> +{
>> +    var len = u8[at++];
>> +    if (len > 0x80)
>> +    {
>> +        if (len != 0x81)
>> +        {
>> +            console.log("Error:  we lazily don't support keys bigger 
>> than 255 bytes.  It'd be easy to fix.");
>> +            return null;
>> +        }
>> +        len = u8[at++];
>> +    }
>> +
>> +    return [ at, len];
>> +}
>> +
>> +function find_sequence(u8, at)
>> +{
>> +    var lenblock;
>> +    at = at || 0;
>> +    if (u8[at++] != 0x30)
>> +    {
>> +        console.log("Error:  public key should start with a sequence 
>> flag.");
>> +        return null;
>> +    }
>> +
>> +    lenblock = asn_get_length(u8, at);
>> +    if (! lenblock)
>> +        return null;
>> +    return lenblock;
>> +}
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  Extract an RSA key from a memory buffer
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function create_rsa_from_mb(mb, at)
>> +{
>> +    var u8 = new Uint8Array(mb);
>> +    var lenblock;
>> +    var seq;
>> +    var ba;
>> +    var i;
>> +    var ret;
>> +
>> +    /* We have a sequence which contains a sequence followed by a 
>> bit string */
>> +    seq = find_sequence(u8, at);
>> +    if (! seq)
>> +        return null;
>> +
>> +    at = seq[0];
>> +    seq = find_sequence(u8, at);
>> +    if (! seq)
>> +        return null;
>> +
>> +    /* Skip over the contained sequence */
>> +    at = seq[0] + seq[1];
>> +    if (u8[at++] != 0x3)
>> +    {
>> +        console.log("Error: expecting bit string next.");
>> +        return null;
>> +    }
>> +
>> +    /* Get the bit string, which is *itself* a sequence. Having fun 
>> yet? */
>> +    lenblock = asn_get_length(u8, at);
>> +    if (! lenblock)
>> +        return null;
>> +
>> +    at = lenblock[0];
>> +    if (u8[at] != 0 && u8[at + 1] != 0x30)
>> +    {
>> +        console.log("Error: unexpected values in bit string.");
>> +        return null;
>> +    }
>> +
>> +    /* Okay, now we have a sequence of two binary values, we hope. */
>> +    seq = find_sequence(u8, at + 1);
>> +    if (! seq)
>> +        return null;
>> +
>> +    at = seq[0];
>> +    if (u8[at++] != 0x02)
>> +    {
>> +        console.log("Error: expecting integer n next.");
>> +        return null;
>> +    }
>> +    lenblock = asn_get_length(u8, at);
>> +    if (! lenblock)
>> +        return null;
>> +    at = lenblock[0];
>> +
>> +    ba = new Array(lenblock[1]);
>> +    for (i = 0; i < lenblock[1]; i++)
>> +        ba[i] = u8[at + i];
>> +
>> +    ret = new RSAKey();
>> +    ret.n = new BigInteger(ba);
>> +
>> +    at += lenblock[1];
>> +
>> +    if (u8[at++] != 0x02)
>> +    {
>> +        console.log("Error: expecting integer e next.");
>> +        return null;
>> +    }
>> +    lenblock = asn_get_length(u8, at);
>> +    if (! lenblock)
>> +        return null;
>> +    at = lenblock[0];
>> +
>> +    ret.e = u8[at++];
>> +    for (i = 1; i < lenblock[1]; i++)
>> +    {
>> +        ret.e <<= 8;
>> +        ret.e |= u8[at++];
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +function rsa_encrypt(rsa, str)
>> +{
>> +    var i;
>> +    var ret = [];
>> +    var oaep = RSA_padding_add_PKCS1_OAEP((rsa.n.bitLength()+7)>>3, 
>> str);
>> +    if (! oaep)
>> +        return null;
>> +
>> +    var ba = new Array(oaep.length);
>> +
>> +    for (i = 0; i < oaep.length; i++)
>> +        ba[i] = oaep.charCodeAt(i);
>> +    var bigint = new BigInteger(ba);
>> +    var enc = rsa.doPublic(bigint);
>> +    var h = enc.toString(16);
>> +    if ((h.length & 1) != 0)
>> +        h = "0" + h;
>> +    for (i = 0; i < h.length; i += 2)
>> +        ret[i / 2] = parseInt(h.substring(i, i + 2), 16);
>> +    return ret;
>> +}
>> diff --git a/ui/js/spice/utils.js b/ui/js/spice/utils.js
>> new file mode 100644
>> index 0000000..5ef23d6
>> --- /dev/null
>> +++ b/ui/js/spice/utils.js
>> @@ -0,0 +1,261 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  Utility settings and functions for Spice
>> +**--------------------------------------------------------------------------*/ 
>>
>> +var DEBUG = 0;
>> +var DUMP_DRAWS = false;
>> +var DUMP_CANVASES = false;
>> +
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  combine_array_buffers
>> +**      Combine two array buffers.
>> +**      FIXME - this can't be optimal.  See wire.js about 
>> eliminating the need.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function combine_array_buffers(a1, a2)
>> +{
>> +    var in1 = new Uint8Array(a1);
>> +    var in2 = new Uint8Array(a2);
>> +    var ret = new ArrayBuffer(a1.byteLength + a2.byteLength);
>> +    var out = new Uint8Array(ret);
>> +    var o = 0;
>> +    var i;
>> +    for (i = 0; i < in1.length; i++)
>> +        out[o++] = in1[i];
>> +    for (i = 0; i < in2.length; i++)
>> +        out[o++] = in2[i];
>> +
>> +    return ret;
>> +}
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +**  hexdump_buffer
>> +**--------------------------------------------------------------------------*/ 
>>
>> +function hexdump_buffer(a)
>> +{
>> +    var mg = new Uint8Array(a);
>> +    var hex = "";
>> +    var str = "";
>> +    var last_zeros = 0;
>> +    for (var i = 0; i < mg.length; i++)
>> +    {
>> +        var h = Number(mg[i]).toString(16);
>> +        if (h.length == 1)
>> +            hex += "0";
>> +        hex += h + " ";
>> +
>> +        str += String.fromCharCode(mg[i]);
>> +
>> +        if ((i % 16 == 15) || (i == (mg.length - 1)))
>> +        {
>> +            while (i % 16 != 15)
>> +            {
>> +                hex += "   ";
>> +                i++;
>> +            }
>> +
>> +            if (last_zeros == 0)
>> +                console.log(hex + " | " + str);
>> +
>> +            if (hex == "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
>> 00 ")
>> +            {
>> +                if (last_zeros == 1)
>> +                {
>> +                    console.log(".");
>> +                    last_zeros++;
>> +                }
>> +                else if (last_zeros == 0)
>> +                    last_zeros++;
>> +            }
>> +            else
>> +                last_zeros = 0;
>> +
>> +            hex = str = "";
>> +        }
>> +    }
>> +}
>> +
>> +/*---------------------------------------------------------------------------- 
>>
>> +** Converting keycodes to AT scancodes is very hard.
>> +** luckly there are some resources on the web and in the Xorg driver 
>> that help
>> +** us figure out what browser depenend keycodes match to what 
>> scancodes.
>> +**
>> +** This will most likely not work for non US keyboard and browsers 
>> other than
>> +** modern Chrome and FireFox.
>> +**--------------------------------------------------------------------------*/ 
>>
>> +var common_scanmap = [];
>> +common_scanmap['Q'.charCodeAt(0)]  = KEY_Q;
>> +common_scanmap['W'.charCodeAt(0)]  = KEY_W;
>> +common_scanmap['E'.charCodeAt(0)]  = KEY_E;
>> +common_scanmap['R'.charCodeAt(0)]  = KEY_R;
>> +common_scanmap['T'.charCodeAt(0)]  = KEY_T;
>> +common_scanmap['Y'.charCodeAt(0)]  = KEY_Y;
>> +common_scanmap['U'.charCodeAt(0)]  = KEY_U;
>> +common_scanmap['I'.charCodeAt(0)]  = KEY_I;
>> +common_scanmap['O'.charCodeAt(0)]  = KEY_O;
>> +common_scanmap['P'.charCodeAt(0)]  = KEY_P;
>> +common_scanmap['A'.charCodeAt(0)]  = KEY_A;
>> +common_scanmap['S'.charCodeAt(0)]  = KEY_S;
>> +common_scanmap['D'.charCodeAt(0)]  = KEY_D;
>> +common_scanmap['F'.charCodeAt(0)]  = KEY_F;
>> +common_scanmap['G'.charCodeAt(0)]  = KEY_G;
>> +common_scanmap['H'.charCodeAt(0)]  = KEY_H;
>> +common_scanmap['J'.charCodeAt(0)]  = KEY_J;
>> +common_scanmap['K'.charCodeAt(0)]  = KEY_K;
>> +common_scanmap['L'.charCodeAt(0)]  = KEY_L;
>> +common_scanmap['Z'.charCodeAt(0)]  = KEY_Z;
>> +common_scanmap['X'.charCodeAt(0)]  = KEY_X;
>> +common_scanmap['C'.charCodeAt(0)]  = KEY_C;
>> +common_scanmap['V'.charCodeAt(0)]  = KEY_V;
>> +common_scanmap['B'.charCodeAt(0)]  = KEY_B;
>> +common_scanmap['N'.charCodeAt(0)]  = KEY_N;
>> +common_scanmap['M'.charCodeAt(0)]  = KEY_M;
>> +common_scanmap[' '.charCodeAt(0)]  = KEY_Space;
>> +common_scanmap[13]                 = KEY_Enter;
>> +common_scanmap[27]                 = KEY_Escape;
>> +common_scanmap[8]                  = KEY_BackSpace;
>> +common_scanmap[9]                  = KEY_Tab;
>> +common_scanmap[16]                 = KEY_ShiftL;
>> +common_scanmap[17]                 = KEY_LCtrl;
>> +common_scanmap[18]                 = KEY_Alt;
>> +common_scanmap[20]                 = KEY_CapsLock;
>> +common_scanmap[144]                = KEY_NumLock;
>> +common_scanmap[112]                = KEY_F1;
>> +common_scanmap[113]                = KEY_F2;
>> +common_scanmap[114]                = KEY_F3;
>> +common_scanmap[115]                = KEY_F4;
>> +common_scanmap[116]                = KEY_F5;
>> +common_scanmap[117]                = KEY_F6;
>> +common_scanmap[118]                = KEY_F7;
>> +common_scanmap[119]                = KEY_F8;
>> +common_scanmap[120]                = KEY_F9;
>> +common_scanmap[121]                = KEY_F10;
>> +common_scanmap[122]                = KEY_F11;
>> +common_scanmap[123]                = KEY_F12;
>> +
>> +/* These externded scancodes do not line up with values from 
>> atKeynames */
>> +common_scanmap[42]                 = 99;
>> +common_scanmap[19]                 = 101;    // Break
>> +common_scanmap[111]                = 0xE035; // KP_Divide
>> +common_scanmap[106]                = 0xE037; // KP_Multiply
>> +common_scanmap[36]                 = 0xE047; // Home
>> +common_scanmap[38]                 = 0xE048; // Up
>> +common_scanmap[33]                 = 0xE049; // PgUp
>> +common_scanmap[37]                 = 0xE04B; // Left
>> +common_scanmap[39]                 = 0xE04D; // Right
>> +common_scanmap[35]                 = 0xE04F; // End
>> +common_scanmap[40]                 = 0xE050; // Down
>> +common_scanmap[34]                 = 0xE051; // PgDown
>> +common_scanmap[45]                 = 0xE052; // Insert
>> +common_scanmap[46]                 = 0xE053; // Delete
>> +common_scanmap[44]                 = 0x2A37; // Print
>> +
>> +/* These are not common between ALL browsers but are between Firefox 
>> and DOM3 */
>> +common_scanmap['1'.charCodeAt(0)]  = KEY_1;
>> +common_scanmap['2'.charCodeAt(0)]  = KEY_2;
>> +common_scanmap['3'.charCodeAt(0)]  = KEY_3;
>> +common_scanmap['4'.charCodeAt(0)]  = KEY_4;
>> +common_scanmap['5'.charCodeAt(0)]  = KEY_5;
>> +common_scanmap['6'.charCodeAt(0)]  = KEY_6;
>> +common_scanmap['7'.charCodeAt(0)]  = KEY_7;
>> +common_scanmap['8'.charCodeAt(0)]  = KEY_8;
>> +common_scanmap['9'.charCodeAt(0)]  = KEY_9;
>> +common_scanmap['0'.charCodeAt(0)]  = KEY_0;
>> +common_scanmap[145]                = KEY_ScrollLock;
>> +common_scanmap[103]                = KEY_KP_7;
>> +common_scanmap[104]                = KEY_KP_8;
>> +common_scanmap[105]                = KEY_KP_9;
>> +common_scanmap[100]                = KEY_KP_4;
>> +common_scanmap[101]                = KEY_KP_5;
>> +common_scanmap[102]                = KEY_KP_6;
>> +common_scanmap[107]                = KEY_KP_Plus;
>> +common_scanmap[97]                 = KEY_KP_1;
>> +common_scanmap[98]                 = KEY_KP_2;
>> +common_scanmap[99]                 = KEY_KP_3;
>> +common_scanmap[96]                 = KEY_KP_0;
>> +common_scanmap[110]                = KEY_KP_Decimal;
>> +common_scanmap[191]                = KEY_Slash;
>> +common_scanmap[190]                = KEY_Period;
>> +common_scanmap[188]                = KEY_Comma;
>> +common_scanmap[220]                = KEY_BSlash;
>> +common_scanmap[192]                = KEY_Tilde;
>> +common_scanmap[222]                = KEY_Quote;
>> +common_scanmap[219]                = KEY_LBrace;
>> +common_scanmap[221]                = KEY_RBrace;
>> +
>> +common_scanmap[91]                 = 0xE05B; //KEY_LMeta
>> +common_scanmap[92]                 = 0xE05C; //KEY_RMeta
>> +common_scanmap[93]                 = 0xE05D; //KEY_Menu
>> +
>> +/* Firefox/Mozilla codes */
>> +var firefox_scanmap = [];
>> +firefox_scanmap[109]                = KEY_Minus;
>> +firefox_scanmap[61]                 = KEY_Equal;
>> +firefox_scanmap[59]                 = KEY_SemiColon;
>> +
>> +/* DOM3 codes */
>> +var DOM_scanmap = [];
>> +DOM_scanmap[189]                = KEY_Minus;
>> +DOM_scanmap[187]                = KEY_Equal;
>> +DOM_scanmap[186]                = KEY_SemiColon;
>> +
>> +function get_scancode(code)
>> +{
>> +    if (common_scanmap[code] === undefined)
>> +    {
>> +        if (navigator.userAgent.indexOf("Firefox") != -1)
>> +            return firefox_scanmap[code];
>> +        else
>> +            return DOM_scanmap[code];
>> +    }
>> +    else
>> +        return common_scanmap[code];
>> +}
>> +
>> +function keycode_to_start_scan(code)
>> +{
>> +    var scancode = get_scancode(code);
>> +    if (scancode === undefined)
>> +    {
>> +        alert('no map for ' + code);
>> +        return 0;
>> +    }
>> +
>> +    if (scancode < 0x100) {
>> +        return scancode;
>> +    } else {
>> +        return 0xe0 | ((scancode - 0x100) << 8);
>> +    }
>> +}
>> +
>> +function keycode_to_end_scan(code)
>> +{
>> +    var scancode = get_scancode(code);
>> +    if (scancode === undefined)
>> +        return 0;
>> +
>> +    if (scancode < 0x100) {
>> +        return scancode | 0x80;
>> +    } else {
>> +        return 0x80e0 | ((scancode - 0x100) << 8);
>> +    }
>> +}
>> diff --git a/ui/js/spice/wire.js b/ui/js/spice/wire.js
>> new file mode 100644
>> index 0000000..2c7f096
>> --- /dev/null
>> +++ b/ui/js/spice/wire.js
>> @@ -0,0 +1,123 @@
>> +"use strict";
>> +/*
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +/*-------------------------------------------------------------------------------------- 
>>
>> +**  SpiceWireReader
>> +**      This class will receive messages from a WebSocket and relay 
>> it to a given
>> +**  callback.  It will optionally save and pass along a header, 
>> useful in processing
>> +**  the mini message format.
>> +**--------------------------------------------------------------------------------------*/ 
>>
>> +function SpiceWireReader(sc, callback)
>> +{
>> +    this.sc = sc;
>> +    this.callback = callback;
>> +    this.needed = 0;
>> +
>> +    this.buffers = [];
>> +
>> +    this.sc.ws.wire_reader = this;
>> +    this.sc.ws.binaryType = "arraybuffer";
>> +    this.sc.ws.addEventListener('message', wire_blob_catcher);
>> +}
>> +
>> +SpiceWireReader.prototype =
>> +{
>> +
>> + 
>> /*------------------------------------------------------------------------
>> +    **  Process messages coming in from our WebSocket
>> + 
>> **----------------------------------------------------------------------*/
>> +    inbound: function (mb)
>> +    {
>> +        var at;
>> +
>> +        /* Just buffer if we don't need anything yet */
>> +        if (this.needed == 0)
>> +        {
>> +            this.buffers.push(mb);
>> +            return;
>> +        }
>> +
>> +        /* Optimization - if we have just one inbound block, and it's
>> +            suitable for our needs, just use it.  */
>> +        if (this.buffers.length == 0 && mb.byteLength >= this.needed)
>> +        {
>> +            if (mb.byteLength > this.needed)
>> +            {
>> +                this.buffers.push(mb.slice(this.needed));
>> +                mb = mb.slice(0, this.needed);
>> +            }
>> +            this.callback.call(this.sc, mb,
>> +                        this.saved_msg_header || undefined);
>> +        }
>> +        else
>> +        {
>> +            this.buffers.push(mb);
>> +        }
>> +
>> +
>> +        /* If we have fragments that add up to what we need, combine 
>> them */
>> +        /*  FIXME - it would be faster to revise the processing code 
>> to handle
>> +        **          multiple fragments directly.  Essentially, we 
>> should be
>> +        **          able to do this without any slice() or 
>> combine_array_buffers() calls */
>> +        while (this.buffers.length > 1 && this.buffers[0].byteLength 
>> < this.needed)
>> +        {
>> +            var mb1 = this.buffers.shift();
>> +            var mb2 = this.buffers.shift();
>> +
>> +            this.buffers.unshift(combine_array_buffers(mb1, mb2));
>> +        }
>> +
>> +
>> +        while (this.buffers.length > 0 && this.buffers[0].byteLength 
>> >= this.needed)
>> +        {
>> +            mb = this.buffers.shift();
>> +            if (mb.byteLength > this.needed)
>> +            {
>> +                this.buffers.unshift(mb.slice(this.needed));
>> +                mb = mb.slice(0, this.needed);
>> +            }
>> +            this.callback.call(this.sc, mb,
>> +                        this.saved_msg_header || undefined);
>> +        }
>> +
>> +    },
>> +
>> +    request: function(n)
>> +    {
>> +        this.needed = n;
>> +    },
>> +
>> +    save_header: function(h)
>> +    {
>> +        this.saved_msg_header = h;
>> +    },
>> +
>> +    clear_header: function()
>> +    {
>> +        this.saved_msg_header = undefined;
>> +    },
>> +}
>> +
>> +function wire_blob_catcher(e)
>> +{
>> +    DEBUG > 1 && console.log(">> WebSockets.onmessage");
>> +    DEBUG > 1 && console.log("id " + 
>> this.wire_reader.sc.connection_id +"; type " + 
>> this.wire_reader.sc.type);
>> +    SpiceWireReader.prototype.inbound.call(this.wire_reader, e.data);
>> +}
>> diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
>> index fbcf4a2..a18288f 100644
>> --- a/ui/js/src/kimchi.api.js
>> +++ b/ui/js/src/kimchi.api.js
>> @@ -309,6 +309,27 @@ var kimchi = {
>>           });
>>       },
>>
>> +    spiceToVM : function(vm) {
>> +        kimchi.requestJSON({
>> +            url : '/config',
>> +            type : 'GET',
>> +            dataType : 'json'
>> +        }).done(function(data, textStatus, xhr) {
>> +            http_port = data['http_port'];
>> +            kimchi.requestJSON({
>> +                url : "/vms/" + encodeURIComponent(vm) + "/connect",
>> +                type : "POST",
>> +                dataType : "json"
>> +            }).done(function(data, textStatus, xhr) {
>> +                url = 'http://' + location.hostname + ':' + http_port;
>> +                url += "/spice.html?port=" + data.graphics.port + 
>> "&listen=" + data.graphics.listen;
>> +                window.open(url);
>> +            });
>> +        }).error(function() {
>> +            kimchi.message.error(i18n['msg.fail.get.config']);
>> +        });
>> +    },
>> +
>>       listVMs : function(suc, err) {
>>           kimchi.requestJSON({
>>               url : kimchi.url + 'vms',
>> diff --git a/ui/js/src/kimchi.guest_main.js 
>> b/ui/js/src/kimchi.guest_main.js
>> index a36c59c..bff5d8f 100644
>> --- a/ui/js/src/kimchi.guest_main.js
>> +++ b/ui/js/src/kimchi.guest_main.js
>> @@ -119,6 +119,10 @@ kimchi.initVmButtonsAction = function() {
>>           kimchi.vncToVM($(this).data('vm'));
>>       });
>>
>> +    $(".vm-spice").on("click", function(event) {
>> +        kimchi.spiceToVM($(this).data('vm'));
>> +    });
>> +
>>       kimchi.init_button_stat();
>>
>>   };
>> @@ -127,13 +131,25 @@ kimchi.init_button_stat = function() {
>>       $('.vm-action').each(function() {
>>           var vm_action = $(this);
>>           var vm_vnc = vm_action.find('.vm-vnc');
>> -        if ((vm_action.data('graphics') === 'vnc')
>> -                && (vm_action.data('vmstate') === 'running')) {
>> -            vm_vnc.removeAttr('disabled');
>
>> +        var vm_spice = vm_action.find('.vm-spice');
>> +        if (vm_action.data('graphics') === 'vnc') {
>> +            vm_spice.hide();
>> +            if (vm_action.data('vmstate') === 'running') {
>> +                vm_vnc.removeAttr('disabled');
>> +            } else {
>> +                vm_vnc.attr('disabled', 'disabled');
>> +            }
>> +        } else if (vm_action.data('graphics') === 'spice') {
>> +            vm_vnc.hide();
>> +            if (vm_action.data('vmstate') === 'running') {
>> +                vm_spice.removeAttr('disabled');
>> +            } else {
>> +                vm_spice.attr('disabled', 'disabled');
>> +            }
>
> What about?
>
> if (vm_action.data('graphics') === 'vnc') {
>     vm_spice.hide();
>     vm_graphics = vm_vnc;
> } else if (vm_action.data('graphics') === 'spice') {
>     vm_vnc.hide();
>     vm_graphics = vm_spice
> }
>
> if (vm_action.data('vmstate') === 'running') {
>     vm_graphics.removeAttr('disabled');
> } else {
>     vm_graphics.attr('disabled', 'disabled');
> }
>
> So we avoid duplicating code while checking vm status
yes, that is a better way, very clever.  Thanks. I'll change it.
>
>>           } else {
>> -            vm_vnc.attr('disabled', 'disabled');
>> +            vm_vnc.hide();
>> +            vm_spice.hide();
>>           }
>> -
>>           var editButton = vm_action.find('.vm-edit');
>>           editButton.prop('disabled', vm_action.data('vmstate') !== 
>> 'shutoff');
>>       })
>> diff --git a/ui/pages/guest.html.tmpl b/ui/pages/guest.html.tmpl
>> index 6bd6853..6d83d57 100644
>> --- a/ui/pages/guest.html.tmpl
>> +++ b/ui/pages/guest.html.tmpl
>> @@ -58,6 +58,7 @@
>>                           <span 
>> class="text">$_("Actions")</span><span class="arrow"></span>
>>                           <div class="popover actionsheet right-side" 
>> style="width: 250px">
>>                               <button class="button-big vm-vnc" 
>> data-vm="{name}"><span class="text">VNC</span></button>
>> +                            <button class="button-big vm-spice" 
>> data-vm="{name}"><span class="text">SPICE</span></button>
>>                               <button class="button-big vm-edit" 
>> data-vm="{name}"><span class="text">$_("Edit")</span></button>
>>                               <a class="button-big red vm-delete" 
>> data-vm="{name}">$_("Delete")</a>
>>                           </div>
>> diff --git a/ui/pages/spice.html.tmpl b/ui/pages/spice.html.tmpl
>> new file mode 100644
>> index 0000000..07e7510
>> --- /dev/null
>> +++ b/ui/pages/spice.html.tmpl
>> @@ -0,0 +1,138 @@
>> +<!--
>> +   Copyright (C) 2012 by Jeremy P. White <jwhite at codeweavers.com>
>> +
>> +   This file is part of spice-html5.
>> +
>> +   spice-html5 is free software: you can redistribute it and/or modify
>> +   it under the terms of the GNU Lesser General Public License as 
>> published by
>> +   the Free Software Foundation, either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   spice-html5 is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public 
>> License
>> +   along with spice-html5.  If not, see <http://www.gnu.org/licenses/>.
>> +
>> +   --------------------------------------------------
>> +    Spice Javascript client template.
>> +    Refer to main.js for more detailed information
>> +   --------------------------------------------------
>> +
>> +-->
>> +<!DOCTYPE html>
>> +<html>
>> +    <head>
>> +        <meta http-equiv="content-type" content="text/html; 
>> charset=ISO-8859-1">
>> +        <title>Spice Javascript client</title>
>> +        <script src="js/spice/enums.js"></script>
>> +        <script src="js/spice/atKeynames.js"></script>
>> +        <script src="js/spice/utils.js"></script>
>> +        <script src="js/spice/png.js"></script>
>> +        <script src="js/spice/lz.js"></script>
>> +        <script src="js/spice/quic.js"></script>
>> +        <script src="js/spice/bitmap.js"></script>
>> +        <script src="js/spice/spicedataview.js"></script>
>> +        <script src="js/spice/spicetype.js"></script>
>> +        <script src="js/spice/spicemsg.js"></script>
>> +        <script src="js/spice/wire.js"></script>
>> +        <script src="js/spice/spiceconn.js"></script>
>> +        <script src="js/spice/display.js"></script>
>> +        <script src="js/spice/main.js"></script>
>> +        <script src="js/spice/inputs.js"></script>
>> +        <script src="js/spice/cursor.js"></script>
>> +        <script src="js/spice/jsbn.js"></script>
>> +        <script src="js/spice/rsa.js"></script>
>> +        <script src="js/spice/prng4.js"></script>
>> +        <script src="js/spice/rng.js"></script>
>> +        <script src="js/spice/sha1.js"></script>
>> +        <script src="js/spice/ticket.js"></script>
>> +        <link rel="stylesheet" type="text/css" 
>> href="css/spice/spice.css">
>> +    </head>
>> +    <script>
>> +        var host = null, port = null;
>> +        var sc;
>> +        function spice_error(e) {
>> +            disconnect();
>> +        }
>> +
>> +        function connect() {
>> +            var host, port, password, scheme = "ws://", uri;
>> +            host = getParameter("listen");
>> +            port = getParameter("port");
>> +            document.getElementById("host").value = host;
>> +            document.getElementById("port").value = port;
>> +            if ((!host) || (!port)) {
>> +                console.log("must set host and port");
>> +                return;
>> +            }
>> +
>> +            if (sc) {
>> +                sc.stop();
>> +            }
>> +
>> +            uri = scheme + host + ":" + port;
>> +            try {
>> +                sc = new SpiceMainConn({
>> +                    uri : uri,
>> +                    screen_id : "spice-screen",
>> +                    dump_id : "debug-div",
>> +                    message_id : "message-div",
>> +                    password : "",
>> +                    onerror : spice_error
>> +                });
>> +            } catch (e) {
>> +                alert(e.toString());
>> +                disconnect();
>> +            }
>> +
>> +        }
>> +
>> +        function disconnect()
>> +        {
>> +            console.log(">> disconnect");
>> +            if (sc) {
>> +                sc.stop();
>> +            }
>> +            console.log("<< disconnect");
>> +        }
>> +
>> +        function getParameter(name) {
>> +            var paramStr = location.search;
>> +            if (paramStr.length == 0)
>> +                return null;
>> +            if (paramStr.charAt(0) != '?')
>> +                return null;
>> +            paramStr = unescape(paramStr);
>> +            paramStr = paramStr.substring(1);
>> +            if (paramStr.length == 0)
>> +                return null;
>> +            var params = paramStr.split('&');
>> +            for ( var i = 0; i < params.length; i++) {
>> +                var parts = params[i].split('=', 2);
>> +                if (parts[0] == name) {
>> +                    if (parts.length < 2 || typeof (parts[1]) == 
>> "undefined" || parts[1] == "undefined" || parts[1] == "null")
>> +                        return "";
>> +                    return parts[1];
>> +                }
>> +            }
>> +            return null;
>> +        }
>> +    </script>
>> +    <body onload="connect();" onunload="disconnect();">
>> +        <div id="login">
>> +            <span class="logo">SPICE</span>
>> +            <label for="host">Host:</label> <input id="host" 
>> value="localhost" type="text" disabled="disabled"> <!-- localhost -->
>> +            <label for="port">Port:</label> <input id="port" 
>> value="5959" type="text" disabled="disabled">
>> +        </div>
>> +        <div id="spice-area">
>> +            <div id="spice-screen" class="spice-screen"></div>
>> +        </div>
>> +        <div id="message-div" class="spice-message"></div>
>> +        <div id="debug-div">
>> +        <!-- If DUMPXXX is turned on, dumped images will go here -->
>> +        </div>
>> +    </body>
>> +</html>
>> \ No newline at end of file
>
>




More information about the Kimchi-devel mailing list