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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)codeweavers.com>
> + * Copyright (C) 2012 by Aric Stewart <aric(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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.