[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