mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 23:25:24 +00:00
spice-html5 updated.
This commit is contained in:
parent
12c80c5021
commit
b05a252d7c
36 changed files with 1356 additions and 1415 deletions
|
@ -31,6 +31,9 @@
|
||||||
<script src="{% static "js/spice-html5/resize.js" %}"></script>
|
<script src="{% static "js/spice-html5/resize.js" %}"></script>
|
||||||
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
|
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
|
||||||
|
<script src="{% static "js/spice-html5/port.js" %}"></script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
@ -61,7 +64,7 @@
|
||||||
{
|
{
|
||||||
console.log(e);
|
console.log(e);
|
||||||
disconnect();
|
disconnect();
|
||||||
if (e.message != undefined) {
|
if (e.message !== undefined) {
|
||||||
log_error(e.message);
|
log_error(e.message);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -73,6 +76,7 @@
|
||||||
log_info(msg);
|
log_info(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function connect(uri,password)
|
function connect(uri,password)
|
||||||
{
|
{
|
||||||
// If a token variable is passed in, set the parameter in a cookie.
|
// If a token variable is passed in, set the parameter in a cookie.
|
||||||
|
@ -90,6 +94,7 @@
|
||||||
{
|
{
|
||||||
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container",
|
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container",
|
||||||
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
|
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
|
|
0
static/js/spice-html5/atKeynames.js
Normal file → Executable file
0
static/js/spice-html5/atKeynames.js
Normal file → Executable file
20
static/js/spice-html5/bitmap.js
Normal file → Executable file
20
static/js/spice-html5/bitmap.js
Normal file → Executable file
|
@ -26,25 +26,31 @@
|
||||||
function convert_spice_bitmap_to_web(context, spice_bitmap)
|
function convert_spice_bitmap_to_web(context, spice_bitmap)
|
||||||
{
|
{
|
||||||
var ret;
|
var ret;
|
||||||
var offset, x;
|
var offset, x, src_offset = 0, src_dec = 0;
|
||||||
var u8 = new Uint8Array(spice_bitmap.data);
|
var u8 = new Uint8Array(spice_bitmap.data);
|
||||||
if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT &&
|
if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT &&
|
||||||
spice_bitmap.format != SPICE_BITMAP_FMT_RGBA)
|
spice_bitmap.format != SPICE_BITMAP_FMT_RGBA)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
if (!(spice_bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN))
|
||||||
|
{
|
||||||
|
src_offset = (spice_bitmap.y - 1 ) * spice_bitmap.stride;
|
||||||
|
src_dec = 2 * spice_bitmap.stride;
|
||||||
|
}
|
||||||
|
|
||||||
ret = context.createImageData(spice_bitmap.x, spice_bitmap.y);
|
ret = context.createImageData(spice_bitmap.x, spice_bitmap.y);
|
||||||
for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); )
|
for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); src_offset -= src_dec)
|
||||||
for (x = 0; x < spice_bitmap.x; x++, offset += 4)
|
for (x = 0; x < spice_bitmap.x; x++, offset += 4, src_offset += 4)
|
||||||
{
|
{
|
||||||
ret.data[offset + 0 ] = u8[offset + 2];
|
ret.data[offset + 0 ] = u8[src_offset + 2];
|
||||||
ret.data[offset + 1 ] = u8[offset + 1];
|
ret.data[offset + 1 ] = u8[src_offset + 1];
|
||||||
ret.data[offset + 2 ] = u8[offset + 0];
|
ret.data[offset + 2 ] = u8[src_offset + 0];
|
||||||
|
|
||||||
// FIXME - We effectively treat all images as having SPICE_IMAGE_FLAGS_HIGH_BITS_SET
|
// FIXME - We effectively treat all images as having SPICE_IMAGE_FLAGS_HIGH_BITS_SET
|
||||||
if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT)
|
if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT)
|
||||||
ret.data[offset + 3] = 255;
|
ret.data[offset + 3] = 255;
|
||||||
else
|
else
|
||||||
ret.data[offset + 3] = u8[offset];
|
ret.data[offset + 3] = u8[src_offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
18
static/js/spice-html5/cursor.js
Normal file → Executable file
18
static/js/spice-html5/cursor.js
Normal file → Executable file
|
@ -73,6 +73,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_CURSOR_MOVE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Cursor Move");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_CURSOR_HIDE)
|
if (msg.type == SPICE_MSG_CURSOR_HIDE)
|
||||||
{
|
{
|
||||||
DEBUG > 1 && console.log("SpiceMsgCursorHide");
|
DEBUG > 1 && console.log("SpiceMsgCursorHide");
|
||||||
|
@ -80,6 +86,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_CURSOR_TRAIL)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Cursor Trail");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_CURSOR_RESET)
|
if (msg.type == SPICE_MSG_CURSOR_RESET)
|
||||||
{
|
{
|
||||||
DEBUG > 1 && console.log("SpiceMsgCursorReset");
|
DEBUG > 1 && console.log("SpiceMsgCursorReset");
|
||||||
|
@ -87,6 +99,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_CURSOR_INVAL_ONE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Cursor Inval One");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL)
|
if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL)
|
||||||
{
|
{
|
||||||
DEBUG > 1 && console.log("SpiceMsgCursorInvalAll");
|
DEBUG > 1 && console.log("SpiceMsgCursorInvalAll");
|
||||||
|
|
494
static/js/spice-html5/display.js
Normal file → Executable file
494
static/js/spice-html5/display.js
Normal file → Executable file
|
@ -62,6 +62,12 @@ function SpiceDisplayConn()
|
||||||
SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype);
|
SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype);
|
||||||
SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
{
|
{
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_MODE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Mode");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_MARK)
|
if (msg.type == SPICE_MSG_DISPLAY_MARK)
|
||||||
{
|
{
|
||||||
// FIXME - DISPLAY_MARK not implemented (may be hard or impossible)
|
// FIXME - DISPLAY_MARK not implemented (may be hard or impossible)
|
||||||
|
@ -171,8 +177,6 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
has_alpha: this.surfaces[draw_copy.data.src_bitmap.surface_id].format == SPICE_SURFACE_FMT_32_xRGB ? false : true,
|
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
|
descriptor : draw_copy.data.src_bitmap.descriptor
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG)
|
else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG)
|
||||||
{
|
{
|
||||||
|
@ -284,9 +288,6 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
return false;
|
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,
|
var source_img = convert_spice_lz_to_web(canvas.context,
|
||||||
draw_copy.data.src_bitmap.lz_rgb);
|
draw_copy.data.src_bitmap.lz_rgb);
|
||||||
if (! source_img)
|
if (! source_img)
|
||||||
|
@ -366,6 +367,60 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_OPAQUE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Opaque");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_BLEND)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Blend");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_BLACKNESS)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Blackness");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_WHITENESS)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Whiteness");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_INVERS)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Invers");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_ROP3)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw ROP3");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_STROKE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Stroke");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_TRANSPARENT)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Transparent");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Alpha Blend");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS)
|
if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS)
|
||||||
{
|
{
|
||||||
var copy_bits = new SpiceMsgDisplayCopyBits(msg.data);
|
var copy_bits = new SpiceMsgDisplayCopyBits(msg.data);
|
||||||
|
@ -402,6 +457,18 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Inval All Pixmaps");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_INVAL_PALETTE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Inval Palette");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES)
|
if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES)
|
||||||
{
|
{
|
||||||
this.known_unimplemented(msg.type, "Inval All Palettes");
|
this.known_unimplemented(msg.type, "Inval All Palettes");
|
||||||
|
@ -465,58 +532,105 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE)
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE)
|
||||||
{
|
{
|
||||||
var m = new SpiceMsgDisplayStreamCreate(msg.data);
|
var m = new SpiceMsgDisplayStreamCreate(msg.data);
|
||||||
DEBUG > 1 && console.log(this.type + ": MsgStreamCreate id" + m.id);
|
STREAM_DEBUG > 0 && console.log(this.type + ": MsgStreamCreate id" + m.id + "; type " + m.codec_type +
|
||||||
|
"; width " + m.stream_width + "; height " + m.stream_height +
|
||||||
|
"; left " + m.dest.left + "; top " + m.dest.top
|
||||||
|
);
|
||||||
if (!this.streams)
|
if (!this.streams)
|
||||||
this.streams = new Array();
|
this.streams = new Array();
|
||||||
if (this.streams[m.id])
|
if (this.streams[m.id])
|
||||||
console.log("Stream already exists");
|
console.log("Stream " + m.id + " already exists");
|
||||||
else
|
else
|
||||||
this.streams[m.id] = m;
|
this.streams[m.id] = m;
|
||||||
if (m.codec_type != SPICE_VIDEO_CODEC_TYPE_MJPEG)
|
|
||||||
|
if (m.codec_type == SPICE_VIDEO_CODEC_TYPE_VP8)
|
||||||
|
{
|
||||||
|
var media = new MediaSource();
|
||||||
|
var v = document.createElement("video");
|
||||||
|
v.src = window.URL.createObjectURL(media);
|
||||||
|
|
||||||
|
v.setAttribute('autoplay', true);
|
||||||
|
v.setAttribute('width', m.stream_width);
|
||||||
|
v.setAttribute('height', m.stream_height);
|
||||||
|
|
||||||
|
var left = m.dest.left;
|
||||||
|
var top = m.dest.top;
|
||||||
|
if (this.surfaces[m.surface_id] !== undefined)
|
||||||
|
{
|
||||||
|
left += this.surfaces[m.surface_id].canvas.offsetLeft;
|
||||||
|
top += this.surfaces[m.surface_id].canvas.offsetTop;
|
||||||
|
}
|
||||||
|
document.getElementById(this.parent.screen_id).appendChild(v);
|
||||||
|
v.setAttribute('style', "position: absolute; top:" + top + "px; left:" + left + "px;");
|
||||||
|
|
||||||
|
media.addEventListener('sourceopen', handle_video_source_open, false);
|
||||||
|
media.addEventListener('sourceended', handle_video_source_ended, false);
|
||||||
|
media.addEventListener('sourceclosed', handle_video_source_closed, false);
|
||||||
|
|
||||||
|
var s = this.streams[m.id];
|
||||||
|
s.video = v;
|
||||||
|
s.media = media;
|
||||||
|
s.queue = new Array();
|
||||||
|
s.start_time = 0;
|
||||||
|
s.cluster_time = 0;
|
||||||
|
s.append_okay = false;
|
||||||
|
|
||||||
|
media.stream = s;
|
||||||
|
media.spiceconn = this;
|
||||||
|
v.spice_stream = s;
|
||||||
|
}
|
||||||
|
else if (m.codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG)
|
||||||
|
this.streams[m.id].frames_loading = 0;
|
||||||
|
else
|
||||||
console.log("Unhandled stream codec: "+m.codec_type);
|
console.log("Unhandled stream codec: "+m.codec_type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA)
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA ||
|
||||||
|
msg.type == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED)
|
||||||
{
|
{
|
||||||
var m = new SpiceMsgDisplayStreamData(msg.data);
|
var m;
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED)
|
||||||
|
m = new SpiceMsgDisplayStreamDataSized(msg.data);
|
||||||
|
else
|
||||||
|
m = new SpiceMsgDisplayStreamData(msg.data);
|
||||||
|
|
||||||
if (!this.streams[m.base.id])
|
if (!this.streams[m.base.id])
|
||||||
{
|
{
|
||||||
console.log("no stream for data");
|
console.log("no stream for data");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var time_until_due = m.base.multi_media_time - this.parent.relative_now();
|
||||||
|
|
||||||
if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
|
if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
|
||||||
|
process_mjpeg_stream_data(this, m, time_until_due);
|
||||||
|
|
||||||
|
if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_VP8)
|
||||||
|
process_video_stream_data(this.streams[m.base.id], m);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT)
|
||||||
|
{
|
||||||
|
var m = new SpiceMsgDisplayStreamActivateReport(msg.data);
|
||||||
|
|
||||||
|
var report = new SpiceMsgcDisplayStreamReport(m.stream_id, m.unique_id);
|
||||||
|
if (this.streams[m.stream_id])
|
||||||
{
|
{
|
||||||
var tmpstr = "data:image/jpeg,";
|
this.streams[m.stream_id].report = report;
|
||||||
var img = new Image;
|
this.streams[m.stream_id].max_window_size = m.max_window_size;
|
||||||
var i;
|
this.streams[m.stream_id].timeout_ms = m.timeout_ms
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
|
||||||
{
|
{
|
||||||
var m = new SpiceMsgDisplayStreamClip(msg.data);
|
var m = new SpiceMsgDisplayStreamClip(msg.data);
|
||||||
DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + m.id);
|
STREAM_DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + m.id);
|
||||||
this.streams[m.id].clip = m.clip;
|
this.streams[m.id].clip = m.clip;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -524,10 +638,25 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY)
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY)
|
||||||
{
|
{
|
||||||
var m = new SpiceMsgDisplayStreamDestroy(msg.data);
|
var m = new SpiceMsgDisplayStreamDestroy(msg.data);
|
||||||
DEBUG > 1 && console.log(this.type + ": MsgStreamDestroy id" + m.id);
|
STREAM_DEBUG > 0 && console.log(this.type + ": MsgStreamDestroy id" + m.id);
|
||||||
|
|
||||||
|
if (this.streams[m.id].codec_type == SPICE_VIDEO_CODEC_TYPE_VP8)
|
||||||
|
{
|
||||||
|
document.getElementById(this.parent.screen_id).removeChild(this.streams[m.id].video);
|
||||||
|
this.streams[m.id].source_buffer = null;
|
||||||
|
this.streams[m.id].media = null;
|
||||||
|
this.streams[m.id].video = null;
|
||||||
|
}
|
||||||
this.streams[m.id] = undefined;
|
this.streams[m.id] = undefined;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Stream Destroy All");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_DISPLAY_INVAL_LIST)
|
if (msg.type == SPICE_MSG_DISPLAY_INVAL_LIST)
|
||||||
{
|
{
|
||||||
var m = new SpiceMsgDisplayInvalList(msg.data);
|
var m = new SpiceMsgDisplayInvalList(msg.data);
|
||||||
|
@ -539,6 +668,18 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_MONITORS_CONFIG)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Monitors Config");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_DISPLAY_DRAW_COMPOSITE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Display Draw Composite");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +883,9 @@ function handle_draw_jpeg_onload()
|
||||||
var temp_canvas = null;
|
var temp_canvas = null;
|
||||||
var context;
|
var context;
|
||||||
|
|
||||||
|
if (this.o.sc.streams[this.o.id])
|
||||||
|
this.o.sc.streams[this.o.id].frames_loading--;
|
||||||
|
|
||||||
/*------------------------------------------------------------
|
/*------------------------------------------------------------
|
||||||
** FIXME:
|
** FIXME:
|
||||||
** The helper should be extended to be able to handle actual HtmlImageElements
|
** The helper should be extended to be able to handle actual HtmlImageElements
|
||||||
|
@ -791,7 +935,8 @@ function handle_draw_jpeg_onload()
|
||||||
|
|
||||||
// Give the Garbage collector a clue to recycle this; avoids
|
// Give the Garbage collector a clue to recycle this; avoids
|
||||||
// fairly massive memory leaks during video playback
|
// fairly massive memory leaks during video playback
|
||||||
this.src = null;
|
this.onload = undefined;
|
||||||
|
this.src = EMPTY_GIF_IMAGE;
|
||||||
|
|
||||||
if (this.o.descriptor &&
|
if (this.o.descriptor &&
|
||||||
(this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
|
(this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
|
||||||
|
@ -820,4 +965,283 @@ function handle_draw_jpeg_onload()
|
||||||
|
|
||||||
this.o.sc.surfaces[this.o.base.surface_id].draw_count++;
|
this.o.sc.surfaces[this.o.base.surface_id].draw_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.o.sc.streams[this.o.id] && "report" in this.o.sc.streams[this.o.id])
|
||||||
|
process_stream_data_report(this.o.sc, this.o.id, this.o.msg_mmtime, this.o.msg_mmtime - this.o.sc.parent.relative_now());
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_mjpeg_stream_data(sc, m, time_until_due)
|
||||||
|
{
|
||||||
|
/* If we are currently processing an mjpeg frame when a new one arrives,
|
||||||
|
and the new one is 'late', drop the new frame. This helps the browsers
|
||||||
|
keep up, and provides rate control feedback as well */
|
||||||
|
if (time_until_due < 0 && sc.streams[m.base.id].frames_loading > 0)
|
||||||
|
{
|
||||||
|
if ("report" in sc.streams[m.base.id])
|
||||||
|
sc.streams[m.base.id].report.num_drops++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = sc.streams[m.base.id].surface_id;
|
||||||
|
strm_base.box = m.dest || sc.streams[m.base.id].dest;
|
||||||
|
strm_base.clip = sc.streams[m.base.id].clip;
|
||||||
|
img.o =
|
||||||
|
{ base: strm_base,
|
||||||
|
tag: "mjpeg." + m.base.id,
|
||||||
|
descriptor: null,
|
||||||
|
sc : sc,
|
||||||
|
id : m.base.id,
|
||||||
|
msg_mmtime : m.base.multi_media_time,
|
||||||
|
};
|
||||||
|
img.onload = handle_draw_jpeg_onload;
|
||||||
|
img.src = tmpstr;
|
||||||
|
|
||||||
|
sc.streams[m.base.id].frames_loading++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_stream_data_report(sc, id, msg_mmtime, time_until_due)
|
||||||
|
{
|
||||||
|
sc.streams[id].report.num_frames++;
|
||||||
|
if (sc.streams[id].report.start_frame_mm_time == 0)
|
||||||
|
sc.streams[id].report.start_frame_mm_time = msg_mmtime;
|
||||||
|
|
||||||
|
if (sc.streams[id].report.num_frames > sc.streams[id].max_window_size ||
|
||||||
|
(msg_mmtime - sc.streams[id].report.start_frame_mm_time) > sc.streams[id].timeout_ms)
|
||||||
|
{
|
||||||
|
sc.streams[id].report.end_frame_mm_time = msg_mmtime;
|
||||||
|
sc.streams[id].report.last_frame_delay = time_until_due;
|
||||||
|
|
||||||
|
var msg = new SpiceMiniData();
|
||||||
|
msg.build_msg(SPICE_MSGC_DISPLAY_STREAM_REPORT, sc.streams[id].report);
|
||||||
|
sc.send_msg(msg);
|
||||||
|
|
||||||
|
sc.streams[id].report.start_frame_mm_time = 0;
|
||||||
|
sc.streams[id].report.num_frames = 0;
|
||||||
|
sc.streams[id].report.num_drops = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_video_source_open(e)
|
||||||
|
{
|
||||||
|
var stream = this.stream;
|
||||||
|
var p = this.spiceconn;
|
||||||
|
|
||||||
|
if (stream.source_buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var s = this.addSourceBuffer(SPICE_VP8_CODEC);
|
||||||
|
if (! s)
|
||||||
|
{
|
||||||
|
p.log_err('Codec ' + SPICE_VP8_CODEC + ' not available.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.source_buffer = s;
|
||||||
|
s.spiceconn = p;
|
||||||
|
s.stream = stream;
|
||||||
|
|
||||||
|
listen_for_video_events(stream);
|
||||||
|
|
||||||
|
var h = new webm_Header();
|
||||||
|
var te = new webm_VideoTrackEntry(this.stream.stream_width, this.stream.stream_height);
|
||||||
|
var t = new webm_Tracks(te);
|
||||||
|
|
||||||
|
var mb = new ArrayBuffer(h.buffer_size() + t.buffer_size())
|
||||||
|
|
||||||
|
var b = h.to_buffer(mb);
|
||||||
|
t.to_buffer(mb, b);
|
||||||
|
|
||||||
|
s.addEventListener('error', handle_video_buffer_error, false);
|
||||||
|
s.addEventListener('updateend', handle_append_video_buffer_done, false);
|
||||||
|
|
||||||
|
append_video_buffer(s, mb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_video_source_ended(e)
|
||||||
|
{
|
||||||
|
var p = this.spiceconn;
|
||||||
|
p.log_err('Video source unexpectedly ended.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_video_source_closed(e)
|
||||||
|
{
|
||||||
|
var p = this.spiceconn;
|
||||||
|
p.log_err('Video source unexpectedly closed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function append_video_buffer(sb, mb)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sb.stream.append_okay = false;
|
||||||
|
sb.appendBuffer(mb);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
var p = sb.spiceconn;
|
||||||
|
p.log_err("Error invoking appendBuffer: " + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_append_video_buffer_done(e)
|
||||||
|
{
|
||||||
|
var stream = this.stream;
|
||||||
|
|
||||||
|
if (stream.current_frame && "report" in stream)
|
||||||
|
{
|
||||||
|
var sc = this.stream.media.spiceconn;
|
||||||
|
var t = this.stream.current_frame.msg_mmtime;
|
||||||
|
process_stream_data_report(sc, stream.id, t, t - sc.parent.relative_now());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.queue.length > 0)
|
||||||
|
{
|
||||||
|
stream.current_frame = stream.queue.shift();
|
||||||
|
append_video_buffer(stream.source_buffer, stream.current_frame.mb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream.append_okay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stream.video)
|
||||||
|
{
|
||||||
|
if (STREAM_DEBUG > 0)
|
||||||
|
console.log("Stream id " + stream.id + " received updateend after video is gone.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.video.buffered.length > 0 &&
|
||||||
|
stream.video.currentTime < stream.video.buffered.start(stream.video.buffered.length - 1))
|
||||||
|
{
|
||||||
|
console.log("Video appears to have fallen behind; advancing to " +
|
||||||
|
stream.video.buffered.start(stream.video.buffered.length - 1));
|
||||||
|
stream.video.currentTime = stream.video.buffered.start(stream.video.buffered.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STREAM_DEBUG > 1)
|
||||||
|
console.log(stream.video.currentTime + ":id " + stream.id + " updateend " + dump_media_element(stream.video));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_video_buffer_error(e)
|
||||||
|
{
|
||||||
|
var p = this.spiceconn;
|
||||||
|
p.log_err('source_buffer error ' + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function push_or_queue(stream, msg, mb)
|
||||||
|
{
|
||||||
|
var frame =
|
||||||
|
{
|
||||||
|
msg_mmtime : msg.base.multi_media_time,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stream.append_okay)
|
||||||
|
{
|
||||||
|
stream.current_frame = frame;
|
||||||
|
append_video_buffer(stream.source_buffer, mb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frame.mb = mb;
|
||||||
|
stream.queue.push(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function video_simple_block(stream, msg, keyframe)
|
||||||
|
{
|
||||||
|
var simple = new webm_SimpleBlock(msg.base.multi_media_time - stream.cluster_time, msg.data, keyframe);
|
||||||
|
var mb = new ArrayBuffer(simple.buffer_size());
|
||||||
|
simple.to_buffer(mb);
|
||||||
|
|
||||||
|
push_or_queue(stream, msg, mb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function new_video_cluster(stream, msg)
|
||||||
|
{
|
||||||
|
stream.cluster_time = msg.base.multi_media_time;
|
||||||
|
var c = new webm_Cluster(stream.cluster_time - stream.start_time, msg.data);
|
||||||
|
|
||||||
|
var mb = new ArrayBuffer(c.buffer_size());
|
||||||
|
c.to_buffer(mb);
|
||||||
|
|
||||||
|
push_or_queue(stream, msg, mb);
|
||||||
|
|
||||||
|
video_simple_block(stream, msg, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_video_stream_data(stream, msg)
|
||||||
|
{
|
||||||
|
if (stream.start_time == 0)
|
||||||
|
{
|
||||||
|
stream.start_time = msg.base.multi_media_time;
|
||||||
|
new_video_cluster(stream, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (msg.base.multi_media_time - stream.cluster_time >= MAX_CLUSTER_TIME)
|
||||||
|
new_video_cluster(stream, msg);
|
||||||
|
else
|
||||||
|
video_simple_block(stream, msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function video_handle_event_debug(e)
|
||||||
|
{
|
||||||
|
var s = this.spice_stream;
|
||||||
|
if (s.video)
|
||||||
|
{
|
||||||
|
if (STREAM_DEBUG > 0 || s.video.buffered.len > 1)
|
||||||
|
console.log(s.video.currentTime + ":id " + s.id + " event " + e.type +
|
||||||
|
dump_media_element(s.video));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STREAM_DEBUG > 1 && s.media)
|
||||||
|
console.log(" media_source " + dump_media_source(s.media));
|
||||||
|
|
||||||
|
if (STREAM_DEBUG > 1 && s.source_buffer)
|
||||||
|
console.log(" source_buffer " + dump_source_buffer(s.source_buffer));
|
||||||
|
|
||||||
|
if (STREAM_DEBUG > 1 || s.queue.length > 1)
|
||||||
|
console.log(' queue len ' + s.queue.length + '; append_okay: ' + s.append_okay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function video_debug_listen_for_one_event(name)
|
||||||
|
{
|
||||||
|
this.addEventListener(name, video_handle_event_debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
function listen_for_video_events(stream)
|
||||||
|
{
|
||||||
|
var video_0_events = [
|
||||||
|
"abort", "error"
|
||||||
|
];
|
||||||
|
|
||||||
|
var video_1_events = [
|
||||||
|
"loadstart", "suspend", "emptied", "stalled", "loadedmetadata", "loadeddata", "canplay",
|
||||||
|
"canplaythrough", "playing", "waiting", "seeking", "seeked", "ended", "durationchange",
|
||||||
|
"play", "pause", "ratechange"
|
||||||
|
];
|
||||||
|
|
||||||
|
var video_2_events = [
|
||||||
|
"timeupdate",
|
||||||
|
"progress",
|
||||||
|
"resize",
|
||||||
|
"volumechange"
|
||||||
|
];
|
||||||
|
|
||||||
|
video_0_events.forEach(video_debug_listen_for_one_event, stream.video);
|
||||||
|
if (STREAM_DEBUG > 0)
|
||||||
|
video_1_events.forEach(video_debug_listen_for_one_event, stream.video);
|
||||||
|
if (STREAM_DEBUG > 1)
|
||||||
|
video_2_events.forEach(video_debug_listen_for_one_event, stream.video);
|
||||||
}
|
}
|
||||||
|
|
29
static/js/spice-html5/enums.js
Normal file → Executable file
29
static/js/spice-html5/enums.js
Normal file → Executable file
|
@ -127,8 +127,13 @@ var SPICE_MSG_DISPLAY_DRAW_TRANSPARENT = 312;
|
||||||
var SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND = 313;
|
var SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND = 313;
|
||||||
var SPICE_MSG_DISPLAY_SURFACE_CREATE = 314;
|
var SPICE_MSG_DISPLAY_SURFACE_CREATE = 314;
|
||||||
var SPICE_MSG_DISPLAY_SURFACE_DESTROY = 315;
|
var SPICE_MSG_DISPLAY_SURFACE_DESTROY = 315;
|
||||||
|
var SPICE_MSG_DISPLAY_STREAM_DATA_SIZED = 316;
|
||||||
|
var SPICE_MSG_DISPLAY_MONITORS_CONFIG = 317;
|
||||||
|
var SPICE_MSG_DISPLAY_DRAW_COMPOSITE = 318;
|
||||||
|
var SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT = 319;
|
||||||
|
|
||||||
var SPICE_MSGC_DISPLAY_INIT = 101;
|
var SPICE_MSGC_DISPLAY_INIT = 101;
|
||||||
|
var SPICE_MSGC_DISPLAY_STREAM_REPORT = 102;
|
||||||
|
|
||||||
var SPICE_MSG_INPUTS_INIT = 101;
|
var SPICE_MSG_INPUTS_INIT = 101;
|
||||||
var SPICE_MSG_INPUTS_KEY_MODIFIERS = 102;
|
var SPICE_MSG_INPUTS_KEY_MODIFIERS = 102;
|
||||||
|
@ -161,6 +166,15 @@ var SPICE_MSG_PLAYBACK_VOLUME = 105;
|
||||||
var SPICE_MSG_PLAYBACK_MUTE = 106;
|
var SPICE_MSG_PLAYBACK_MUTE = 106;
|
||||||
var SPICE_MSG_PLAYBACK_LATENCY = 107;
|
var SPICE_MSG_PLAYBACK_LATENCY = 107;
|
||||||
|
|
||||||
|
var SPICE_MSG_SPICEVMC_DATA = 101;
|
||||||
|
var SPICE_MSG_PORT_INIT = 201;
|
||||||
|
var SPICE_MSG_PORT_EVENT = 202;
|
||||||
|
var SPICE_MSG_END_PORT = 203;
|
||||||
|
|
||||||
|
var SPICE_MSGC_SPICEVMC_DATA = 101;
|
||||||
|
var SPICE_MSGC_PORT_EVENT = 201;
|
||||||
|
var SPICE_MSGC_END_PORT = 202;
|
||||||
|
|
||||||
var SPICE_PLAYBACK_CAP_CELT_0_5_1 = 0;
|
var SPICE_PLAYBACK_CAP_CELT_0_5_1 = 0;
|
||||||
var SPICE_PLAYBACK_CAP_VOLUME = 1;
|
var SPICE_PLAYBACK_CAP_VOLUME = 1;
|
||||||
var SPICE_PLAYBACK_CAP_LATENCY = 2;
|
var SPICE_PLAYBACK_CAP_LATENCY = 2;
|
||||||
|
@ -171,6 +185,18 @@ var SPICE_MAIN_CAP_NAME_AND_UUID = 1;
|
||||||
var SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS = 2;
|
var SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS = 2;
|
||||||
var SPICE_MAIN_CAP_SEAMLESS_MIGRATE = 3;
|
var SPICE_MAIN_CAP_SEAMLESS_MIGRATE = 3;
|
||||||
|
|
||||||
|
var SPICE_DISPLAY_CAP_SIZED_STREAM = 0;
|
||||||
|
var SPICE_DISPLAY_CAP_MONITORS_CONFIG = 1;
|
||||||
|
var SPICE_DISPLAY_CAP_COMPOSITE = 2;
|
||||||
|
var SPICE_DISPLAY_CAP_A8_SURFACE = 3;
|
||||||
|
var SPICE_DISPLAY_CAP_STREAM_REPORT = 4;
|
||||||
|
var SPICE_DISPLAY_CAP_LZ4_COMPRESSION = 5;
|
||||||
|
var SPICE_DISPLAY_CAP_PREF_COMPRESSION = 6;
|
||||||
|
var SPICE_DISPLAY_CAP_GL_SCANOUT = 7;
|
||||||
|
var SPICE_DISPLAY_CAP_MULTI_CODEC = 8;
|
||||||
|
var SPICE_DISPLAY_CAP_CODEC_MJPEG = 9;
|
||||||
|
var SPICE_DISPLAY_CAP_CODEC_VP8 = 10;
|
||||||
|
|
||||||
var SPICE_AUDIO_DATA_MODE_INVALID = 0;
|
var SPICE_AUDIO_DATA_MODE_INVALID = 0;
|
||||||
var SPICE_AUDIO_DATA_MODE_RAW = 1;
|
var SPICE_AUDIO_DATA_MODE_RAW = 1;
|
||||||
var SPICE_AUDIO_DATA_MODE_CELT_0_5_1 = 2;
|
var SPICE_AUDIO_DATA_MODE_CELT_0_5_1 = 2;
|
||||||
|
@ -188,6 +214,8 @@ var SPICE_CHANNEL_RECORD = 6;
|
||||||
var SPICE_CHANNEL_TUNNEL = 7;
|
var SPICE_CHANNEL_TUNNEL = 7;
|
||||||
var SPICE_CHANNEL_SMARTCARD = 8;
|
var SPICE_CHANNEL_SMARTCARD = 8;
|
||||||
var SPICE_CHANNEL_USBREDIR = 9;
|
var SPICE_CHANNEL_USBREDIR = 9;
|
||||||
|
var SPICE_CHANNEL_PORT = 10;
|
||||||
|
var SPICE_CHANNEL_WEBDAV = 11;
|
||||||
|
|
||||||
var SPICE_SURFACE_FLAGS_PRIMARY = (1 << 0);
|
var SPICE_SURFACE_FLAGS_PRIMARY = (1 << 0);
|
||||||
|
|
||||||
|
@ -310,6 +338,7 @@ var SPICE_CURSOR_TYPE_ALPHA = 0,
|
||||||
SPICE_CURSOR_TYPE_COLOR32 = 6;
|
SPICE_CURSOR_TYPE_COLOR32 = 6;
|
||||||
|
|
||||||
var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1;
|
var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1;
|
||||||
|
var SPICE_VIDEO_CODEC_TYPE_VP8 = 2;
|
||||||
|
|
||||||
var VD_AGENT_PROTOCOL = 1;
|
var VD_AGENT_PROTOCOL = 1;
|
||||||
var VD_AGENT_MAX_DATA_SIZE = 2048;
|
var VD_AGENT_MAX_DATA_SIZE = 2048;
|
||||||
|
|
2
static/js/spice-html5/filexfer.js
Normal file → Executable file
2
static/js/spice-html5/filexfer.js
Normal file → Executable file
|
@ -81,7 +81,7 @@ function handle_file_drop(e)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
for (var i = files.length - 1; i >= 0; i--)
|
for (var i = files.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (files[i].type); // do not copy a directory
|
if (files[i].type) // do not copy a directory
|
||||||
sc.file_xfer_start(files[i]);
|
sc.file_xfer_start(files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
static/js/spice-html5/inputs.js
Normal file → Executable file
0
static/js/spice-html5/inputs.js
Normal file → Executable file
|
@ -1,589 +0,0 @@
|
||||||
// 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);
|
|
16
static/js/spice-html5/lz.js
Normal file → Executable file
16
static/js/spice-html5/lz.js
Normal file → Executable file
|
@ -141,6 +141,19 @@ function lz_rgb32_decompress(in_buf, at, out_buf, type, default_alpha)
|
||||||
return encoder - 1;
|
return encoder - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flip_image_data(img)
|
||||||
|
{
|
||||||
|
var wb = img.width * 4;
|
||||||
|
var h = img.height;
|
||||||
|
var temp_h = h;
|
||||||
|
var buff = new Uint8Array(img.width * img.height * 4);
|
||||||
|
while (temp_h--)
|
||||||
|
{
|
||||||
|
buff.set(img.data.subarray(temp_h * wb, (temp_h + 1) * wb), (h - temp_h - 1) * wb);
|
||||||
|
}
|
||||||
|
img.data.set(buff);
|
||||||
|
}
|
||||||
|
|
||||||
function convert_spice_lz_to_web(context, lz_image)
|
function convert_spice_lz_to_web(context, lz_image)
|
||||||
{
|
{
|
||||||
var at;
|
var at;
|
||||||
|
@ -150,6 +163,9 @@ function convert_spice_lz_to_web(context, lz_image)
|
||||||
var ret = context.createImageData(lz_image.width, lz_image.height);
|
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);
|
at = lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGB32, lz_image.type != LZ_IMAGE_TYPE_RGBA);
|
||||||
|
if (!lz_image.top_down)
|
||||||
|
flip_image_data(ret);
|
||||||
|
|
||||||
if (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);
|
lz_rgb32_decompress(u8, at, ret.data, LZ_IMAGE_TYPE_RGBA, false);
|
||||||
}
|
}
|
||||||
|
|
81
static/js/spice-html5/main.js
Normal file → Executable file
81
static/js/spice-html5/main.js
Normal file → Executable file
|
@ -59,11 +59,24 @@ function SpiceMainConn()
|
||||||
this.file_xfer_tasks = {};
|
this.file_xfer_tasks = {};
|
||||||
this.file_xfer_task_id = 0;
|
this.file_xfer_task_id = 0;
|
||||||
this.file_xfer_read_queue = [];
|
this.file_xfer_read_queue = [];
|
||||||
|
this.ports = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
SpiceMainConn.prototype = Object.create(SpiceConn.prototype);
|
SpiceMainConn.prototype = Object.create(SpiceConn.prototype);
|
||||||
SpiceMainConn.prototype.process_channel_message = function(msg)
|
SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
{
|
{
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_BEGIN)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Begin");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_CANCEL)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Cancel");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_MAIN_INIT)
|
if (msg.type == SPICE_MSG_MAIN_INIT)
|
||||||
{
|
{
|
||||||
this.log_info("Connected to " + this.ws.url);
|
this.log_info("Connected to " + this.ws.url);
|
||||||
|
@ -86,6 +99,9 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
" ; ram_hint " + this.main_init.ram_hint);
|
" ; ram_hint " + this.main_init.ram_hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.our_mm_time = Date.now();
|
||||||
|
this.mm_time = this.main_init.multi_media_time;
|
||||||
|
|
||||||
this.handle_mouse_mode(this.main_init.current_mouse_mode,
|
this.handle_mouse_mode(this.main_init.current_mouse_mode,
|
||||||
this.main_init.supported_mouse_modes);
|
this.main_init.supported_mouse_modes);
|
||||||
|
|
||||||
|
@ -107,6 +123,12 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MULTI_MEDIA_TIME)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Multi Media Time");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST)
|
if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST)
|
||||||
{
|
{
|
||||||
var i;
|
var i;
|
||||||
|
@ -123,7 +145,12 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
chan_id : chans.channels[i].id
|
chan_id : chans.channels[i].id
|
||||||
};
|
};
|
||||||
if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY)
|
if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY)
|
||||||
this.display = new SpiceDisplayConn(conn);
|
{
|
||||||
|
if (this.display !== undefined)
|
||||||
|
this.log_warn("The spice-html5 client does not handle multiple heads.");
|
||||||
|
else
|
||||||
|
this.display = new SpiceDisplayConn(conn);
|
||||||
|
}
|
||||||
else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS)
|
else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS)
|
||||||
{
|
{
|
||||||
this.inputs = new SpiceInputsConn(conn);
|
this.inputs = new SpiceInputsConn(conn);
|
||||||
|
@ -133,12 +160,14 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
this.cursor = new SpiceCursorConn(conn);
|
this.cursor = new SpiceCursorConn(conn);
|
||||||
else if (chans.channels[i].type == SPICE_CHANNEL_PLAYBACK)
|
else if (chans.channels[i].type == SPICE_CHANNEL_PLAYBACK)
|
||||||
this.cursor = new SpicePlaybackConn(conn);
|
this.cursor = new SpicePlaybackConn(conn);
|
||||||
|
else if (chans.channels[i].type == SPICE_CHANNEL_PORT)
|
||||||
|
this.ports.push(new SpicePortConn(conn));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.log_err("Channel type " + chans.channels[i].type + " unknown.");
|
|
||||||
if (! ("extra_channels" in this))
|
if (! ("extra_channels" in this))
|
||||||
this.extra_channels = [];
|
this.extra_channels = [];
|
||||||
this.extra_channels[i] = new SpiceConn(conn);
|
this.extra_channels[i] = new SpiceConn(conn);
|
||||||
|
this.log_err("Channel type " + this.extra_channels[i].channel_type() + " not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -201,6 +230,48 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Switch Host");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_END)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate End");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_NAME)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Name");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_UUID)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main UUID");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Begin Seamless");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Dst Seamless ACK");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Main Migrate Dst Seamless NACK");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,3 +486,9 @@ SpiceMainConn.prototype.handle_mouse_mode = function(current, supported)
|
||||||
this.inputs.mouse_mode = current;
|
this.inputs.mouse_mode = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shift current time to attempt to get a time matching that of the server */
|
||||||
|
SpiceMainConn.prototype.relative_now = function()
|
||||||
|
{
|
||||||
|
var ret = (Date.now() - this.our_mm_time) + this.mm_time;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
196
static/js/spice-html5/playback.js
Normal file → Executable file
196
static/js/spice-html5/playback.js
Normal file → Executable file
|
@ -29,8 +29,6 @@ function SpicePlaybackConn()
|
||||||
this.queue = new Array();
|
this.queue = new Array();
|
||||||
this.append_okay = false;
|
this.append_okay = false;
|
||||||
this.start_time = 0;
|
this.start_time = 0;
|
||||||
this.skip_until = 0;
|
|
||||||
this.gap_time = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SpicePlaybackConn.prototype = Object.create(SpiceConn.prototype);
|
SpicePlaybackConn.prototype = Object.create(SpiceConn.prototype);
|
||||||
|
@ -46,7 +44,7 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
|
||||||
{
|
{
|
||||||
var start = new SpiceMsgPlaybackStart(msg.data);
|
var start = new SpiceMsgPlaybackStart(msg.data);
|
||||||
|
|
||||||
DEBUG > 0 && console.log("PlaybackStart; frequency " + start.frequency);
|
PLAYBACK_DEBUG > 0 && console.log("PlaybackStart; frequency " + start.frequency);
|
||||||
|
|
||||||
if (start.frequency != OPUS_FREQUENCY)
|
if (start.frequency != OPUS_FREQUENCY)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +70,7 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
|
||||||
this.media_source.spiceconn = this;
|
this.media_source.spiceconn = this;
|
||||||
|
|
||||||
this.audio = document.createElement("audio");
|
this.audio = document.createElement("audio");
|
||||||
|
this.audio.spiceconn = this;
|
||||||
this.audio.setAttribute('autoplay', true);
|
this.audio.setAttribute('autoplay', true);
|
||||||
this.audio.src = window.URL.createObjectURL(this.media_source);
|
this.audio.src = window.URL.createObjectURL(this.media_source);
|
||||||
document.getElementById(this.parent.screen_id).appendChild(this.audio);
|
document.getElementById(this.parent.screen_id).appendChild(this.audio);
|
||||||
|
@ -90,56 +89,59 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
|
||||||
{
|
{
|
||||||
var data = new SpiceMsgPlaybackData(msg.data);
|
var data = new SpiceMsgPlaybackData(msg.data);
|
||||||
|
|
||||||
// If this packet has the same time as the last, just bump up by one.
|
if (! this.source_buffer)
|
||||||
if (this.last_data_time && data.time <= this.last_data_time)
|
return true;
|
||||||
|
|
||||||
|
if (this.audio.readyState >= 3 && this.audio.buffered.length > 1 &&
|
||||||
|
this.audio.currentTime == this.audio.buffered.end(0) &&
|
||||||
|
this.audio.currentTime < this.audio.buffered.start(this.audio.buffered.length - 1))
|
||||||
{
|
{
|
||||||
// FIXME - this is arguably wrong. But delaying the transmission was worse,
|
console.log("Audio underrun: we appear to have fallen behind; advancing to " +
|
||||||
// in initial testing. Could use more research.
|
this.audio.buffered.start(this.audio.buffered.length - 1));
|
||||||
DEBUG > 1 && console.log("Hacking time of " + data.time + " to " + this.last_data_time + 1);
|
this.audio.currentTime = this.audio.buffered.start(this.audio.buffered.length - 1);
|
||||||
data.time = this.last_data_time + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gap detection: If there has been a delay since our last packet, then audio must
|
/* Around version 45, Firefox started being very particular about the
|
||||||
have paused. Handling that gets tricky. In Chrome, you can seek forward,
|
time stamps put into the Opus stream. The time stamps from the Spice server are
|
||||||
but you cannot in Firefox. And seeking forward in Chrome is nice, as it keeps
|
somewhat irregular. They mostly arrive every 10 ms, but sometimes it is 11, or sometimes
|
||||||
Chrome from being overly cautious in it's buffer strategy.
|
with two time stamps the same in a row. The previous logic resulted in fuzzy and/or
|
||||||
|
distorted audio streams in Firefox in a row.
|
||||||
|
|
||||||
So we do two things. First, we seek forward. Second, we compute how much of a gap
|
In theory, the sequence mode should be appropriate for us, but as of 09/27/2016,
|
||||||
there would have been, and essentially eliminate it.
|
I was unable to make sequence mode work with Firefox.
|
||||||
|
|
||||||
|
Thus, we end up with an inelegant hack. Essentially, we force every packet to have
|
||||||
|
a 10ms time delta, unless there is an obvious gap in time stream, in which case we
|
||||||
|
will resync.
|
||||||
*/
|
*/
|
||||||
if (this.last_data_time && data.time >= (this.last_data_time + GAP_DETECTION_THRESHOLD))
|
|
||||||
|
if (this.start_time != 0 && data.time != (this.last_data_time + EXPECTED_PACKET_DURATION))
|
||||||
{
|
{
|
||||||
this.skip_until = data.time;
|
if (Math.abs(data.time - (EXPECTED_PACKET_DURATION + this.last_data_time)) < MAX_CLUSTER_TIME)
|
||||||
this.gap_time = (data.time - this.start_time) -
|
{
|
||||||
(this.source_buffer.buffered.end(this.source_buffer.buffered.end.length - 1) * 1000.0).toFixed(0);
|
PLAYBACK_DEBUG > 1 && console.log("Hacking time of " + data.time + " to " +
|
||||||
|
(this.last_data_time + EXPECTED_PACKET_DURATION));
|
||||||
|
data.time = this.last_data_time + EXPECTED_PACKET_DURATION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PLAYBACK_DEBUG > 1 && console.log("Apparent gap in audio time; now is " + data.time + " last was " + this.last_data_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.last_data_time = data.time;
|
this.last_data_time = data.time;
|
||||||
|
|
||||||
|
PLAYBACK_DEBUG > 1 && console.log("PlaybackData; time " + data.time + "; length " + data.data.byteLength);
|
||||||
DEBUG > 1 && console.log("PlaybackData; time " + data.time + "; length " + data.data.byteLength);
|
|
||||||
|
|
||||||
if (! this.source_buffer)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (this.start_time == 0)
|
if (this.start_time == 0)
|
||||||
this.start_playback(data);
|
this.start_playback(data);
|
||||||
|
|
||||||
else if (data.time - this.cluster_time >= MAX_CLUSTER_TIME || this.skip_until > 0)
|
else if (data.time - this.cluster_time >= MAX_CLUSTER_TIME)
|
||||||
this.new_cluster(data);
|
this.new_cluster(data);
|
||||||
|
|
||||||
else
|
else
|
||||||
this.simple_block(data, false);
|
this.simple_block(data, false);
|
||||||
|
|
||||||
if (this.skip_until > 0)
|
|
||||||
{
|
|
||||||
this.audio.currentTime = (this.skip_until - this.start_time - this.gap_time) / 1000.0;
|
|
||||||
this.skip_until = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.audio.paused)
|
|
||||||
this.audio.play();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +158,39 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
|
||||||
|
|
||||||
if (msg.type == SPICE_MSG_PLAYBACK_STOP)
|
if (msg.type == SPICE_MSG_PLAYBACK_STOP)
|
||||||
{
|
{
|
||||||
|
PLAYBACK_DEBUG > 0 && console.log("PlaybackStop");
|
||||||
|
if (this.source_buffer)
|
||||||
|
{
|
||||||
|
document.getElementById(this.parent.screen_id).removeChild(this.audio);
|
||||||
|
window.URL.revokeObjectURL(this.audio.src);
|
||||||
|
|
||||||
|
delete this.source_buffer;
|
||||||
|
delete this.media_source;
|
||||||
|
delete this.audio;
|
||||||
|
|
||||||
|
this.append_okay = false;
|
||||||
|
this.queue = new Array();
|
||||||
|
this.start_time = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_PLAYBACK_VOLUME)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Playback Volume");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_PLAYBACK_MUTE)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Playback Mute");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == SPICE_MSG_PLAYBACK_LATENCY)
|
||||||
|
{
|
||||||
|
this.known_unimplemented(msg.type, "Playback Latency");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,10 +202,13 @@ SpicePlaybackConn.prototype.start_playback = function(data)
|
||||||
this.start_time = data.time;
|
this.start_time = data.time;
|
||||||
|
|
||||||
var h = new webm_Header();
|
var h = new webm_Header();
|
||||||
|
var te = new webm_AudioTrackEntry;
|
||||||
|
var t = new webm_Tracks(te);
|
||||||
|
|
||||||
var mb = new ArrayBuffer(h.buffer_size())
|
var mb = new ArrayBuffer(h.buffer_size() + t.buffer_size())
|
||||||
|
|
||||||
this.bytes_written = h.to_buffer(mb);
|
this.bytes_written = h.to_buffer(mb);
|
||||||
|
this.bytes_written = t.to_buffer(mb, this.bytes_written);
|
||||||
|
|
||||||
this.source_buffer.addEventListener('error', handle_sourcebuffer_error, false);
|
this.source_buffer.addEventListener('error', handle_sourcebuffer_error, false);
|
||||||
this.source_buffer.addEventListener('updateend', handle_append_buffer_done, false);
|
this.source_buffer.addEventListener('updateend', handle_append_buffer_done, false);
|
||||||
|
@ -183,7 +221,7 @@ SpicePlaybackConn.prototype.new_cluster = function(data)
|
||||||
{
|
{
|
||||||
this.cluster_time = data.time;
|
this.cluster_time = data.time;
|
||||||
|
|
||||||
var c = new webm_Cluster(data.time - this.start_time - this.gap_time);
|
var c = new webm_Cluster(data.time - this.start_time);
|
||||||
|
|
||||||
var mb = new ArrayBuffer(c.buffer_size());
|
var mb = new ArrayBuffer(c.buffer_size());
|
||||||
this.bytes_written += c.to_buffer(mb);
|
this.bytes_written += c.to_buffer(mb);
|
||||||
|
@ -222,6 +260,12 @@ function handle_source_open(e)
|
||||||
p.log_err('Codec ' + SPICE_PLAYBACK_CODEC + ' not available.');
|
p.log_err('Codec ' + SPICE_PLAYBACK_CODEC + ' not available.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PLAYBACK_DEBUG > 0)
|
||||||
|
playback_handle_event_debug.call(this, e);
|
||||||
|
|
||||||
|
listen_for_audio_events(p);
|
||||||
|
|
||||||
p.source_buffer.spiceconn = p;
|
p.source_buffer.spiceconn = p;
|
||||||
p.source_buffer.mode = "segments";
|
p.source_buffer.mode = "segments";
|
||||||
|
|
||||||
|
@ -245,12 +289,38 @@ function handle_source_closed(e)
|
||||||
p.log_err('Audio source unexpectedly closed.');
|
p.log_err('Audio source unexpectedly closed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_append_buffer_done(b)
|
function condense_playback_queue(queue)
|
||||||
|
{
|
||||||
|
if (queue.length == 1)
|
||||||
|
return queue.shift();
|
||||||
|
|
||||||
|
var len = 0;
|
||||||
|
var i = 0;
|
||||||
|
for (i = 0; i < queue.length; i++)
|
||||||
|
len += queue[i].byteLength;
|
||||||
|
|
||||||
|
var mb = new ArrayBuffer(len);
|
||||||
|
var tmp = new Uint8Array(mb);
|
||||||
|
len = 0;
|
||||||
|
for (i = 0; i < queue.length; i++)
|
||||||
|
{
|
||||||
|
tmp.set(new Uint8Array(queue[i]), len);
|
||||||
|
len += queue[i].byteLength;
|
||||||
|
}
|
||||||
|
queue.length = 0;
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_append_buffer_done(e)
|
||||||
{
|
{
|
||||||
var p = this.spiceconn;
|
var p = this.spiceconn;
|
||||||
|
|
||||||
|
if (PLAYBACK_DEBUG > 1)
|
||||||
|
playback_handle_event_debug.call(this, e);
|
||||||
|
|
||||||
if (p.queue.length > 0)
|
if (p.queue.length > 0)
|
||||||
{
|
{
|
||||||
var mb = p.queue.shift();
|
var mb = condense_playback_queue(p.queue);
|
||||||
playback_append_buffer(p, mb);
|
playback_append_buffer(p, mb);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -276,3 +346,53 @@ function playback_append_buffer(p, b)
|
||||||
p.log_err("Error invoking appendBuffer: " + e.message);
|
p.log_err("Error invoking appendBuffer: " + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function playback_handle_event_debug(e)
|
||||||
|
{
|
||||||
|
var p = this.spiceconn;
|
||||||
|
if (p.audio)
|
||||||
|
{
|
||||||
|
if (PLAYBACK_DEBUG > 0 || p.audio.buffered.len > 1)
|
||||||
|
console.log(p.audio.currentTime + ": event " + e.type +
|
||||||
|
dump_media_element(p.audio));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PLAYBACK_DEBUG > 1 && p.media_source)
|
||||||
|
console.log(" media_source " + dump_media_source(p.media_source));
|
||||||
|
|
||||||
|
if (PLAYBACK_DEBUG > 1 && p.source_buffer)
|
||||||
|
console.log(" source_buffer " + dump_source_buffer(p.source_buffer));
|
||||||
|
|
||||||
|
if (PLAYBACK_DEBUG > 0 || p.queue.length > 1)
|
||||||
|
console.log(' queue len ' + p.queue.length + '; append_okay: ' + p.append_okay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playback_debug_listen_for_one_event(name)
|
||||||
|
{
|
||||||
|
this.addEventListener(name, playback_handle_event_debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
function listen_for_audio_events(spiceconn)
|
||||||
|
{
|
||||||
|
var audio_0_events = [
|
||||||
|
"abort", "error"
|
||||||
|
];
|
||||||
|
|
||||||
|
var audio_1_events = [
|
||||||
|
"loadstart", "suspend", "emptied", "stalled", "loadedmetadata", "loadeddata", "canplay",
|
||||||
|
"canplaythrough", "playing", "waiting", "seeking", "seeked", "ended", "durationchange",
|
||||||
|
"timeupdate", "play", "pause", "ratechange"
|
||||||
|
];
|
||||||
|
|
||||||
|
var audio_2_events = [
|
||||||
|
"progress",
|
||||||
|
"resize",
|
||||||
|
"volumechange"
|
||||||
|
];
|
||||||
|
|
||||||
|
audio_0_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
|
||||||
|
if (PLAYBACK_DEBUG > 0)
|
||||||
|
audio_1_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
|
||||||
|
if (PLAYBACK_DEBUG > 1)
|
||||||
|
audio_2_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
|
||||||
|
}
|
||||||
|
|
0
static/js/spice-html5/png.js
Normal file → Executable file
0
static/js/spice-html5/png.js
Normal file → Executable file
85
static/js/spice-html5/port.js
Executable file
85
static/js/spice-html5/port.js
Executable file
|
@ -0,0 +1,85 @@
|
||||||
|
"use strict";
|
||||||
|
/*
|
||||||
|
Copyright (C) 2016 by Oliver Gutierrez <ogutsua@gmail.com>
|
||||||
|
Miroslav Chodil <mchodil@redhat.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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
** SpicePortConn
|
||||||
|
** Drive the Spice Port Channel
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
function SpicePortConn()
|
||||||
|
{
|
||||||
|
DEBUG > 0 && console.log('SPICE port: created SPICE port channel. Args:', arguments);
|
||||||
|
SpiceConn.apply(this, arguments);
|
||||||
|
this.port_name = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpicePortConn.prototype = Object.create(SpiceConn.prototype);
|
||||||
|
|
||||||
|
SpicePortConn.prototype.process_channel_message = function(msg)
|
||||||
|
{
|
||||||
|
if (msg.type == SPICE_MSG_PORT_INIT)
|
||||||
|
{
|
||||||
|
if (this.port_name === null)
|
||||||
|
{
|
||||||
|
var m = new SpiceMsgPortInit(msg.data);
|
||||||
|
this.portName = arraybuffer_to_str(new Uint8Array(m.name));
|
||||||
|
this.portOpened = m.opened
|
||||||
|
DEBUG > 0 && console.log('SPICE port: Port', this.portName, 'initialized');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG > 0 && console.log('SPICE port: Port', this.port_name, 'is already initialized.');
|
||||||
|
}
|
||||||
|
else if (msg.type == SPICE_MSG_PORT_EVENT)
|
||||||
|
{
|
||||||
|
DEBUG > 0 && console.log('SPICE port: Port event received for', this.portName, msg);
|
||||||
|
var event = new CustomEvent('spice-port-event', {
|
||||||
|
detail: {
|
||||||
|
channel: this,
|
||||||
|
spiceEvent: new Uint8Array(msg.data)
|
||||||
|
},
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (msg.type == SPICE_MSG_SPICEVMC_DATA)
|
||||||
|
{
|
||||||
|
DEBUG > 0 && console.log('SPICE port: Data received in port', this.portName, msg);
|
||||||
|
var event = new CustomEvent('spice-port-data', {
|
||||||
|
detail: {
|
||||||
|
channel: this,
|
||||||
|
data: msg.data
|
||||||
|
},
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG > 0 && console.log('SPICE port: SPICE message type not recognized:', msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
|
@ -1,79 +0,0 @@
|
||||||
// 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;
|
|
1
static/js/spice-html5/quic.js
Normal file → Executable file
1
static/js/spice-html5/quic.js
Normal file → Executable file
|
@ -280,6 +280,7 @@ function QuicModel(bpc)
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
console.log("quic: findmodelparams(): evol value obsolete!!!\n");
|
console.log("quic: findmodelparams(): evol value obsolete!!!\n");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("quic: findmodelparams(): evol out of range!!!\n");
|
console.log("quic: findmodelparams(): evol out of range!!!\n");
|
||||||
}
|
}
|
||||||
|
|
20
static/js/spice-html5/resize.js
Normal file → Executable file
20
static/js/spice-html5/resize.js
Normal file → Executable file
|
@ -33,17 +33,29 @@
|
||||||
function resize_helper(sc)
|
function resize_helper(sc)
|
||||||
{
|
{
|
||||||
var w = document.getElementById(sc.screen_id).clientWidth;
|
var w = document.getElementById(sc.screen_id).clientWidth;
|
||||||
var h = document.getElementById(sc.screen_id).clientHeight;
|
|
||||||
|
|
||||||
var m = document.getElementById(sc.message_id);
|
var m = document.getElementById(sc.message_id);
|
||||||
|
|
||||||
/* Resize vertically; basically we leave a 20 pixel margin
|
/* Resize vertically; basically we leave a 20 pixel margin
|
||||||
at the bottom, and use the position of the message window
|
at the bottom, and use the position of the message window
|
||||||
to figure out how to resize */
|
to figure out how to resize */
|
||||||
var hd = window.innerHeight - m.offsetHeight - m.offsetTop - 20;
|
|
||||||
|
var h = window.innerHeight - 20;
|
||||||
|
|
||||||
|
/* Screen height based on debug console visibility */
|
||||||
|
if (window.getComputedStyle(m).getPropertyValue("display") == 'none')
|
||||||
|
{
|
||||||
|
/* Get console height from spice.css .spice-message */
|
||||||
|
var mh = parseInt(window.getComputedStyle(m).getPropertyValue("height"), 10);
|
||||||
|
h = h - mh;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Show both div elements - spice-area and message-div */
|
||||||
|
h = h - m.offsetHeight - m.clientHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Xorg requires height be a multiple of 8; round up */
|
/* Xorg requires height be a multiple of 8; round up */
|
||||||
h = h + hd;
|
|
||||||
if (h % 8 > 0)
|
if (h % 8 > 0)
|
||||||
h += (8 - (h % 8));
|
h += (8 - (h % 8));
|
||||||
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
// 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;
|
|
|
@ -1,146 +0,0 @@
|
||||||
// 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;
|
|
|
@ -1,346 +0,0 @@
|
||||||
/*
|
|
||||||
* 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));
|
|
||||||
}
|
|
0
static/js/spice-html5/simulatecursor.js
Normal file → Executable file
0
static/js/spice-html5/simulatecursor.js
Normal file → Executable file
117
static/js/spice-html5/spice.css
Executable file
117
static/js/spice-html5/spice.css
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
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: 95%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
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: 2px 10px 2px 20px;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
height: 100%;
|
||||||
|
width: 95%;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border: solid #222222 1px;
|
||||||
|
-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-screen
|
||||||
|
{
|
||||||
|
min-height: 600px;
|
||||||
|
height: 100%;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
0
static/js/spice-html5/spicearraybuffer.js
Normal file → Executable file
0
static/js/spice-html5/spicearraybuffer.js
Normal file → Executable file
53
static/js/spice-html5/spiceconn.js
Normal file → Executable file
53
static/js/spice-html5/spiceconn.js
Normal file → Executable file
|
@ -119,20 +119,36 @@ SpiceConn.prototype =
|
||||||
|
|
||||||
msg.connection_id = this.connection_id;
|
msg.connection_id = this.connection_id;
|
||||||
msg.channel_type = this.type;
|
msg.channel_type = this.type;
|
||||||
// FIXME - we're not setting a channel_id...
|
msg.channel_id = this.chan_id;
|
||||||
|
|
||||||
msg.common_caps.push(
|
msg.common_caps.push(
|
||||||
(1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION) |
|
(1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION) |
|
||||||
(1 << SPICE_COMMON_CAP_MINI_HEADER)
|
(1 << SPICE_COMMON_CAP_MINI_HEADER)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (msg.channel_type == SPICE_CHANNEL_PLAYBACK)
|
if (msg.channel_type == SPICE_CHANNEL_PLAYBACK)
|
||||||
msg.channel_caps.push(
|
{
|
||||||
(1 << SPICE_PLAYBACK_CAP_OPUS)
|
var caps = 0;
|
||||||
);
|
if ('MediaSource' in window && MediaSource.isTypeSupported(SPICE_PLAYBACK_CODEC))
|
||||||
|
caps |= (1 << SPICE_PLAYBACK_CAP_OPUS);
|
||||||
|
msg.channel_caps.push(caps);
|
||||||
|
}
|
||||||
else if (msg.channel_type == SPICE_CHANNEL_MAIN)
|
else if (msg.channel_type == SPICE_CHANNEL_MAIN)
|
||||||
|
{
|
||||||
msg.channel_caps.push(
|
msg.channel_caps.push(
|
||||||
(1 << SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)
|
(1 << SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else if (msg.channel_type == SPICE_CHANNEL_DISPLAY)
|
||||||
|
{
|
||||||
|
var caps = (1 << SPICE_DISPLAY_CAP_SIZED_STREAM) |
|
||||||
|
(1 << SPICE_DISPLAY_CAP_STREAM_REPORT) |
|
||||||
|
(1 << SPICE_DISPLAY_CAP_MULTI_CODEC) |
|
||||||
|
(1 << SPICE_DISPLAY_CAP_CODEC_MJPEG);
|
||||||
|
if ('MediaSource' in window && MediaSource.isTypeSupported(SPICE_VP8_CODEC))
|
||||||
|
caps |= (1 << SPICE_DISPLAY_CAP_CODEC_VP8);
|
||||||
|
msg.channel_caps.push(caps);
|
||||||
|
}
|
||||||
|
|
||||||
hdr.size = msg.buffer_size();
|
hdr.size = msg.buffer_size();
|
||||||
|
|
||||||
|
@ -180,8 +196,11 @@ SpiceConn.prototype =
|
||||||
|
|
||||||
if (msg.type > 500)
|
if (msg.type > 500)
|
||||||
{
|
{
|
||||||
alert("Something has gone very wrong; we think we have message of type " + msg.type);
|
if (DEBUG > 0)
|
||||||
debugger;
|
{
|
||||||
|
alert("Something has gone very wrong; we think we have message of type " + msg.type);
|
||||||
|
debugger;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.size == 0)
|
if (msg.size == 0)
|
||||||
|
@ -329,6 +348,7 @@ SpiceConn.prototype =
|
||||||
process_message: function(msg)
|
process_message: function(msg)
|
||||||
{
|
{
|
||||||
var rc;
|
var rc;
|
||||||
|
var start = Date.now();
|
||||||
DEBUG > 0 && console.log("<< hdr " + this.channel_type() + " type " + msg.type + " size " + (msg.data && msg.data.byteLength));
|
DEBUG > 0 && console.log("<< hdr " + this.channel_type() + " type " + msg.type + " size " + (msg.data && msg.data.byteLength));
|
||||||
rc = this.process_common_messages(msg);
|
rc = this.process_common_messages(msg);
|
||||||
if (! rc)
|
if (! rc)
|
||||||
|
@ -337,10 +357,10 @@ SpiceConn.prototype =
|
||||||
{
|
{
|
||||||
rc = this.process_channel_message(msg);
|
rc = this.process_channel_message(msg);
|
||||||
if (! rc)
|
if (! rc)
|
||||||
this.log_warn(this.type + ": Unknown message type " + msg.type + "!");
|
this.log_warn(this.channel_type() + ": Unknown message type " + msg.type + "!");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.log_err(this.type + ": No message handlers for this channel; message " + msg.type);
|
this.log_err(this.channel_type() + ": No message handlers for this channel; message " + msg.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.msgs_until_ack !== undefined && this.ack_window)
|
if (this.msgs_until_ack !== undefined && this.ack_window)
|
||||||
|
@ -356,6 +376,9 @@ SpiceConn.prototype =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var delta = Date.now() - start;
|
||||||
|
if (DEBUG > 0 || delta > GAP_DETECTION_THRESHOLD)
|
||||||
|
console.log("delta " + this.channel_type() + ":" + msg.type + " " + delta);
|
||||||
return rc;
|
return rc;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -369,6 +392,20 @@ SpiceConn.prototype =
|
||||||
return "inputs";
|
return "inputs";
|
||||||
else if (this.type == SPICE_CHANNEL_CURSOR)
|
else if (this.type == SPICE_CHANNEL_CURSOR)
|
||||||
return "cursor";
|
return "cursor";
|
||||||
|
else if (this.type == SPICE_CHANNEL_PLAYBACK)
|
||||||
|
return "playback";
|
||||||
|
else if (this.type == SPICE_CHANNEL_RECORD)
|
||||||
|
return "record";
|
||||||
|
else if (this.type == SPICE_CHANNEL_TUNNEL)
|
||||||
|
return "tunnel";
|
||||||
|
else if (this.type == SPICE_CHANNEL_SMARTCARD)
|
||||||
|
return "smartcard";
|
||||||
|
else if (this.type == SPICE_CHANNEL_USBREDIR)
|
||||||
|
return "usbredir";
|
||||||
|
else if (this.type == SPICE_CHANNEL_PORT)
|
||||||
|
return "port";
|
||||||
|
else if (this.type == SPICE_CHANNEL_WEBDAV)
|
||||||
|
return "webdav";
|
||||||
return "unknown-" + this.type;
|
return "unknown-" + this.type;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
0
static/js/spice-html5/spicedataview.js
Normal file → Executable file
0
static/js/spice-html5/spicedataview.js
Normal file → Executable file
95
static/js/spice-html5/spicemsg.js
Normal file → Executable file
95
static/js/spice-html5/spicemsg.js
Normal file → Executable file
|
@ -1146,6 +1146,29 @@ SpiceMsgDisplayStreamData.prototype =
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SpiceMsgDisplayStreamDataSized(a, at)
|
||||||
|
{
|
||||||
|
this.from_buffer(a, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiceMsgDisplayStreamDataSized.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.width = dv.getUint32(at, true); at += 4;
|
||||||
|
this.height = dv.getUint32(at, true); at += 4;
|
||||||
|
this.dest = new SpiceRect;
|
||||||
|
at = this.dest.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)
|
function SpiceMsgDisplayStreamClip(a, at)
|
||||||
{
|
{
|
||||||
this.from_buffer(a, at);
|
this.from_buffer(a, at);
|
||||||
|
@ -1178,6 +1201,60 @@ SpiceMsgDisplayStreamDestroy.prototype =
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SpiceMsgDisplayStreamActivateReport(a, at)
|
||||||
|
{
|
||||||
|
this.from_buffer(a, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiceMsgDisplayStreamActivateReport.prototype =
|
||||||
|
{
|
||||||
|
from_buffer: function(a, at)
|
||||||
|
{
|
||||||
|
at = at || 0;
|
||||||
|
var dv = new SpiceDataView(a);
|
||||||
|
this.stream_id = dv.getUint32(at, true); at += 4;
|
||||||
|
this.unique_id = dv.getUint32(at, true); at += 4;
|
||||||
|
this.max_window_size = dv.getUint32(at, true); at += 4;
|
||||||
|
this.timeout_ms = dv.getUint32(at, true); at += 4;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function SpiceMsgcDisplayStreamReport(stream_id, unique_id)
|
||||||
|
{
|
||||||
|
this.stream_id = stream_id;
|
||||||
|
this.unique_id = unique_id;
|
||||||
|
this.start_frame_mm_time = 0;
|
||||||
|
this.end_frame_mm_time = 0;
|
||||||
|
this.num_frames = 0;
|
||||||
|
this.num_drops = 0;
|
||||||
|
this.last_frame_delay = 0;
|
||||||
|
|
||||||
|
// TODO - Implement audio delay
|
||||||
|
this.audio_delay = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiceMsgcDisplayStreamReport.prototype =
|
||||||
|
{
|
||||||
|
to_buffer: function(a, at)
|
||||||
|
{
|
||||||
|
at = at || 0;
|
||||||
|
var dv = new SpiceDataView(a);
|
||||||
|
dv.setUint32(at, this.stream_id, true); at += 4;
|
||||||
|
dv.setUint32(at, this.unique_id, true); at += 4;
|
||||||
|
dv.setUint32(at, this.start_frame_mm_time, true); at += 4;
|
||||||
|
dv.setUint32(at, this.end_frame_mm_time, true); at += 4;
|
||||||
|
dv.setUint32(at, this.num_frames, true); at += 4;
|
||||||
|
dv.setUint32(at, this.num_drops, true); at += 4;
|
||||||
|
dv.setUint32(at, this.last_frame_delay, true); at += 4;
|
||||||
|
dv.setUint32(at, this.audio_delay, true); at += 4;
|
||||||
|
return at;
|
||||||
|
},
|
||||||
|
buffer_size: function()
|
||||||
|
{
|
||||||
|
return 8 * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function SpiceMsgDisplayInvalList(a, at)
|
function SpiceMsgDisplayInvalList(a, at)
|
||||||
{
|
{
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
|
@ -1201,3 +1278,21 @@ SpiceMsgDisplayInvalList.prototype =
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SpiceMsgPortInit(a, at)
|
||||||
|
{
|
||||||
|
this.from_buffer(a,at);
|
||||||
|
};
|
||||||
|
|
||||||
|
SpiceMsgPortInit.prototype =
|
||||||
|
{
|
||||||
|
from_buffer: function (a, at)
|
||||||
|
{
|
||||||
|
at = at || 0;
|
||||||
|
var dv = new SpiceDataView(a);
|
||||||
|
var namesize = dv.getUint32(at, true); at += 4;
|
||||||
|
var offset = dv.getUint32(at, true); at += 4;
|
||||||
|
this.opened = dv.getUint8(at, true); at += 1;
|
||||||
|
this.name = a.slice(offset, offset + namesize - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
0
static/js/spice-html5/spicetype.js
Normal file → Executable file
0
static/js/spice-html5/spicetype.js
Normal file → Executable file
0
static/js/spice-html5/thirdparty/jsbn.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/jsbn.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/prng4.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/prng4.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/rng.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/rng.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/rsa.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/rsa.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/sha1.js
vendored
Normal file → Executable file
0
static/js/spice-html5/thirdparty/sha1.js
vendored
Normal file → Executable file
0
static/js/spice-html5/ticket.js
Normal file → Executable file
0
static/js/spice-html5/ticket.js
Normal file → Executable file
71
static/js/spice-html5/utils.js
Normal file → Executable file
71
static/js/spice-html5/utils.js
Normal file → Executable file
|
@ -22,9 +22,16 @@
|
||||||
** Utility settings and functions for Spice
|
** Utility settings and functions for Spice
|
||||||
**--------------------------------------------------------------------------*/
|
**--------------------------------------------------------------------------*/
|
||||||
var DEBUG = 0;
|
var DEBUG = 0;
|
||||||
|
var PLAYBACK_DEBUG = 0;
|
||||||
|
var STREAM_DEBUG = 0;
|
||||||
var DUMP_DRAWS = false;
|
var DUMP_DRAWS = false;
|
||||||
var DUMP_CANVASES = false;
|
var DUMP_CANVASES = false;
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
** We use an Image temporarily, and the image/src does not get garbage
|
||||||
|
** collected as quickly as we might like. This blank image helps with that.
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
var EMPTY_GIF_IMAGE = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
** combine_array_buffers
|
** combine_array_buffers
|
||||||
|
@ -97,10 +104,17 @@ function hexdump_buffer(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
** Convert arraybuffer to string
|
||||||
|
**--------------------------------------------------------------------------*/
|
||||||
|
function arraybuffer_to_str(buf) {
|
||||||
|
return String.fromCharCode.apply(null, new Uint16Array(buf));
|
||||||
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
** Converting keycodes to AT scancodes is very hard.
|
** Converting keycodes to AT scancodes is very hard.
|
||||||
** luckly there are some resources on the web and in the Xorg driver that help
|
** 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.
|
** us figure out what browser dependent keycodes match to what scancodes.
|
||||||
**
|
**
|
||||||
** This will most likely not work for non US keyboard and browsers other than
|
** This will most likely not work for non US keyboard and browsers other than
|
||||||
** modern Chrome and FireFox.
|
** modern Chrome and FireFox.
|
||||||
|
@ -155,7 +169,7 @@ common_scanmap[121] = KEY_F10;
|
||||||
common_scanmap[122] = KEY_F11;
|
common_scanmap[122] = KEY_F11;
|
||||||
common_scanmap[123] = KEY_F12;
|
common_scanmap[123] = KEY_F12;
|
||||||
|
|
||||||
/* These externded scancodes do not line up with values from atKeynames */
|
/* These extended scancodes do not line up with values from atKeynames */
|
||||||
common_scanmap[42] = 99;
|
common_scanmap[42] = 99;
|
||||||
common_scanmap[19] = 101; // Break
|
common_scanmap[19] = 101; // Break
|
||||||
common_scanmap[111] = 0xE035; // KP_Divide
|
common_scanmap[111] = 0xE035; // KP_Divide
|
||||||
|
@ -263,3 +277,56 @@ function keycode_to_end_scan(code)
|
||||||
return 0x80e0 | ((scancode - 0x100) << 8);
|
return 0x80e0 | ((scancode - 0x100) << 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dump_media_element(m)
|
||||||
|
{
|
||||||
|
var ret =
|
||||||
|
"[networkState " + m.networkState +
|
||||||
|
"|readyState " + m.readyState +
|
||||||
|
"|error " + m.error +
|
||||||
|
"|seeking " + m.seeking +
|
||||||
|
"|duration " + m.duration +
|
||||||
|
"|paused " + m.paused +
|
||||||
|
"|ended " + m.error +
|
||||||
|
"|buffered " + dump_timerange(m.buffered) +
|
||||||
|
"]";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dump_media_source(ms)
|
||||||
|
{
|
||||||
|
var ret =
|
||||||
|
"[duration " + ms.duration +
|
||||||
|
"|readyState " + ms.readyState + "]";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dump_source_buffer(sb)
|
||||||
|
{
|
||||||
|
var ret =
|
||||||
|
"[appendWindowStart " + sb.appendWindowStart +
|
||||||
|
"|appendWindowEnd " + sb.appendWindowEnd +
|
||||||
|
"|buffered " + dump_timerange(sb.buffered) +
|
||||||
|
"|timeStampOffset " + sb.timeStampOffset +
|
||||||
|
"|updating " + sb.updating +
|
||||||
|
"]";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dump_timerange(tr)
|
||||||
|
{
|
||||||
|
var ret;
|
||||||
|
|
||||||
|
if (tr)
|
||||||
|
{
|
||||||
|
var i = tr.length;
|
||||||
|
ret = "{len " + i;
|
||||||
|
if (i > 0)
|
||||||
|
ret += "; start " + tr.start(0) + "; end " + tr.end(i - 1);
|
||||||
|
ret += "}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = "N/A";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
110
static/js/spice-html5/webm.js
Normal file → Executable file
110
static/js/spice-html5/webm.js
Normal file → Executable file
|
@ -61,6 +61,10 @@ var WEBM_CODEC_DELAY = [ 0x56, 0xAA ];
|
||||||
var WEBM_CODEC_PRIVATE = [ 0x63, 0xA2 ];
|
var WEBM_CODEC_PRIVATE = [ 0x63, 0xA2 ];
|
||||||
var WEBM_CODEC_ID = [ 0x86 ];
|
var WEBM_CODEC_ID = [ 0x86 ];
|
||||||
|
|
||||||
|
var WEBM_VIDEO = [ 0xE0 ] ;
|
||||||
|
var WEBM_PIXEL_WIDTH = [ 0xB0 ] ;
|
||||||
|
var WEBM_PIXEL_HEIGHT = [ 0xBA ] ;
|
||||||
|
|
||||||
var WEBM_AUDIO = [ 0xE1 ] ;
|
var WEBM_AUDIO = [ 0xE1 ] ;
|
||||||
var WEBM_SAMPLING_FREQUENCY = [ 0xB5 ] ;
|
var WEBM_SAMPLING_FREQUENCY = [ 0xB5 ] ;
|
||||||
var WEBM_CHANNELS = [ 0x9F ] ;
|
var WEBM_CHANNELS = [ 0x9F ] ;
|
||||||
|
@ -80,8 +84,11 @@ var OPUS_CHANNELS = 2;
|
||||||
var SPICE_PLAYBACK_CODEC = 'audio/webm; codecs="opus"';
|
var SPICE_PLAYBACK_CODEC = 'audio/webm; codecs="opus"';
|
||||||
var MAX_CLUSTER_TIME = 1000;
|
var MAX_CLUSTER_TIME = 1000;
|
||||||
|
|
||||||
|
var EXPECTED_PACKET_DURATION = 10;
|
||||||
var GAP_DETECTION_THRESHOLD = 50;
|
var GAP_DETECTION_THRESHOLD = 50;
|
||||||
|
|
||||||
|
var SPICE_VP8_CODEC = 'video/webm; codecs="vp8"';
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
** EBML utility functions
|
** EBML utility functions
|
||||||
** These classes can create the binary representation of a webm file
|
** These classes can create the binary representation of a webm file
|
||||||
|
@ -291,6 +298,34 @@ webm_Audio.prototype =
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function webm_Video(width, height)
|
||||||
|
{
|
||||||
|
this.id = WEBM_VIDEO;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
webm_Video.prototype =
|
||||||
|
{
|
||||||
|
to_buffer: function(a, at)
|
||||||
|
{
|
||||||
|
at = at || 0;
|
||||||
|
var dv = new DataView(a);
|
||||||
|
at = EBML_write_array(this.id, dv, at);
|
||||||
|
at = EBML_write_u64_data_len(this.buffer_size() - 8 - this.id.length, dv, at);
|
||||||
|
at = EBML_write_u16_value(WEBM_PIXEL_WIDTH, this.width, dv, at)
|
||||||
|
at = EBML_write_u16_value(WEBM_PIXEL_HEIGHT, this.height, dv, at)
|
||||||
|
return at;
|
||||||
|
},
|
||||||
|
buffer_size: function()
|
||||||
|
{
|
||||||
|
return this.id.length + 8 +
|
||||||
|
WEBM_PIXEL_WIDTH.length + 1 + 2 +
|
||||||
|
WEBM_PIXEL_HEIGHT.length + 1 + 2;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------
|
/* ---------------------------
|
||||||
SeekHead not currently used. Hopefully not needed.
|
SeekHead not currently used. Hopefully not needed.
|
||||||
|
@ -356,7 +391,7 @@ webm_SeekHead.prototype =
|
||||||
End of Seek Head
|
End of Seek Head
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function webm_TrackEntry()
|
function webm_AudioTrackEntry()
|
||||||
{
|
{
|
||||||
this.id = WEBM_TRACK_ENTRY;
|
this.id = WEBM_TRACK_ENTRY;
|
||||||
this.number = 1;
|
this.number = 1;
|
||||||
|
@ -385,7 +420,7 @@ function webm_TrackEntry()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
webm_TrackEntry.prototype =
|
webm_AudioTrackEntry.prototype =
|
||||||
{
|
{
|
||||||
to_buffer: function(a, at)
|
to_buffer: function(a, at)
|
||||||
{
|
{
|
||||||
|
@ -431,6 +466,70 @@ webm_TrackEntry.prototype =
|
||||||
this.audio.buffer_size();
|
this.audio.buffer_size();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function webm_VideoTrackEntry(width, height)
|
||||||
|
{
|
||||||
|
this.id = WEBM_TRACK_ENTRY;
|
||||||
|
this.number = 1;
|
||||||
|
this.uid = 1;
|
||||||
|
this.type = 1; // Video
|
||||||
|
this.flag_enabled = 1;
|
||||||
|
this.flag_default = 1;
|
||||||
|
this.flag_forced = 1;
|
||||||
|
this.flag_lacing = 0;
|
||||||
|
this.min_cache = 0; // fixme - check
|
||||||
|
this.max_block_addition_id = 0;
|
||||||
|
this.codec_decode_all = 0; // fixme - check
|
||||||
|
this.seek_pre_roll = 0; // 80000000; // fixme - check
|
||||||
|
this.codec_delay = 80000000; // Must match codec_private.preskip
|
||||||
|
this.codec_id = "V_VP8";
|
||||||
|
this.video = new webm_Video(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
webm_VideoTrackEntry.prototype =
|
||||||
|
{
|
||||||
|
to_buffer: function(a, at)
|
||||||
|
{
|
||||||
|
at = at || 0;
|
||||||
|
var dv = new DataView(a);
|
||||||
|
at = EBML_write_array(this.id, dv, at);
|
||||||
|
at = EBML_write_u64_data_len(this.buffer_size() - 8 - this.id.length, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_TRACK_NUMBER, this.number, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_TRACK_UID, this.uid, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_FLAG_ENABLED, this.flag_enabled, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_FLAG_DEFAULT, this.flag_default, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_FLAG_FORCED, this.flag_forced, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_FLAG_LACING, this.flag_lacing, dv, at);
|
||||||
|
at = EBML_write_data(WEBM_CODEC_ID, this.codec_id, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_MIN_CACHE, this.min_cache, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_MAX_BLOCK_ADDITION_ID, this.max_block_addition_id, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_CODEC_DECODE_ALL, this.codec_decode_all, dv, at);
|
||||||
|
at = EBML_write_u32_value(WEBM_CODEC_DELAY, this.codec_delay, dv, at);
|
||||||
|
at = EBML_write_u32_value(WEBM_SEEK_PRE_ROLL, this.seek_pre_roll, dv, at);
|
||||||
|
at = EBML_write_u8_value(WEBM_TRACK_TYPE, this.type, dv, at);
|
||||||
|
at = this.video.to_buffer(a, at);
|
||||||
|
return at;
|
||||||
|
},
|
||||||
|
buffer_size: function()
|
||||||
|
{
|
||||||
|
return this.id.length + 8 +
|
||||||
|
WEBM_TRACK_NUMBER.length + 1 + 1 +
|
||||||
|
WEBM_TRACK_UID.length + 1 + 1 +
|
||||||
|
WEBM_FLAG_ENABLED.length + 1 + 1 +
|
||||||
|
WEBM_FLAG_DEFAULT.length + 1 + 1 +
|
||||||
|
WEBM_FLAG_FORCED.length + 1 + 1 +
|
||||||
|
WEBM_FLAG_LACING.length + 1 + 1 +
|
||||||
|
WEBM_CODEC_ID.length + this.codec_id.length + 1 +
|
||||||
|
WEBM_MIN_CACHE.length + 1 + 1 +
|
||||||
|
WEBM_MAX_BLOCK_ADDITION_ID.length + 1 + 1 +
|
||||||
|
WEBM_CODEC_DECODE_ALL.length + 1 + 1 +
|
||||||
|
WEBM_CODEC_DELAY.length + 1 + 4 +
|
||||||
|
WEBM_SEEK_PRE_ROLL.length + 1 + 4 +
|
||||||
|
WEBM_TRACK_TYPE.length + 1 + 1 +
|
||||||
|
this.video.buffer_size();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
function webm_Tracks(entry)
|
function webm_Tracks(entry)
|
||||||
{
|
{
|
||||||
this.id = WEBM_TRACKS;
|
this.id = WEBM_TRACKS;
|
||||||
|
@ -526,9 +625,6 @@ function webm_Header()
|
||||||
this.info = new webm_SegmentInformation;
|
this.info = new webm_SegmentInformation;
|
||||||
|
|
||||||
this.seek_head.track.pos = this.seek_head.info.pos + this.info.buffer_size();
|
this.seek_head.track.pos = this.seek_head.info.pos + this.info.buffer_size();
|
||||||
|
|
||||||
this.track_entry = new webm_TrackEntry;
|
|
||||||
this.tracks = new webm_Tracks(this.track_entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webm_Header.prototype =
|
webm_Header.prototype =
|
||||||
|
@ -539,7 +635,6 @@ webm_Header.prototype =
|
||||||
at = this.ebml.to_buffer(a, at);
|
at = this.ebml.to_buffer(a, at);
|
||||||
at = this.segment.to_buffer(a, at);
|
at = this.segment.to_buffer(a, at);
|
||||||
at = this.info.to_buffer(a, at);
|
at = this.info.to_buffer(a, at);
|
||||||
at = this.tracks.to_buffer(a, at);
|
|
||||||
|
|
||||||
return at;
|
return at;
|
||||||
},
|
},
|
||||||
|
@ -547,7 +642,6 @@ webm_Header.prototype =
|
||||||
{
|
{
|
||||||
return this.ebml.buffer_size() +
|
return this.ebml.buffer_size() +
|
||||||
this.segment.buffer_size() +
|
this.segment.buffer_size() +
|
||||||
this.info.buffer_size() +
|
this.info.buffer_size();
|
||||||
this.tracks.buffer_size();
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
0
static/js/spice-html5/wire.js
Normal file → Executable file
0
static/js/spice-html5/wire.js
Normal file → Executable file
Loading…
Reference in a new issue