1
0
mirror of https://github.com/lxsang/antd-web-apps synced 2024-11-20 02:18:20 +01:00

using worker for data decoding

This commit is contained in:
Xuan Sang LE 2018-09-20 20:02:22 +02:00
parent e7693f8cc2
commit 50a354ec73
8 changed files with 1828 additions and 225 deletions

View File

@ -17,6 +17,11 @@ js:
- rm assets/scripts/main.* - rm assets/scripts/main.*
for f in $(coffees); do (cat "$${f}"; echo) >> assets/scripts/main.coffee; done for f in $(coffees); do (cat "$${f}"; echo) >> assets/scripts/main.coffee; done
coffee --compile assets/scripts/main.coffee coffee --compile assets/scripts/main.coffee
coffee --compile assets/coffee/decoder.coffee
sed '2d' assets/coffee/decoder.js > assets/scripts/tmp.js
head -n -1 assets/scripts/tmp.js > assets/scripts/decoder.js
-rm assets/coffee/decoder.js
-rm assets/scripts/tmp.js
-rm assets/scripts/main.coffee -rm assets/scripts/main.coffee
clean: clean:

View File

@ -7,127 +7,132 @@ class WVNC extends window.classes.BaseObject
@canvas = undefined @canvas = undefined
@canvas = ($ @args[1])[0] if @args and @args.length > 1 @canvas = ($ @args[1])[0] if @args and @args.length > 1
@buffer = $("<canvas>")[0] @buffer = $("<canvas>")[0]
@counter = 0 @lastPose = { x: 0, y: 0 }
@scale = 0.8
@decoder = new Worker('/assets/scripts/decoder.js')
me = @
@mouseMask = 0
@decoder.onmessage = (e) ->
me.process e.data
init: () -> init: () ->
me = @ me = @
@ready() @ready()
.then () -> .then () ->
$("#stop").click (e) -> me.socket.close() if me.socket $("#stop").click (e) -> me.socket.close() if me.socket
$("#connect").click (e) -> $("#connect").click (e) ->
me.counter = 0
me.openSession() me.openSession()
($ me.canvas).css "cursor","none" me.initInputEvent()
($ me.canvas).mousemove (e) ->
rect = me.canvas.getBoundingClientRect()
x = Math.floor(e.clientX - rect.left)
y = Math.floor(e.clientY - rect.top)
me.sendPointEvent x, y, 0
.catch (m, s) -> .catch (m, s) ->
console.error(m, s) console.error(m, s)
initInputEvent: () ->
me = @
getMousePos = (e) ->
rect = me.canvas.getBoundingClientRect()
pos=
x: Math.floor((e.clientX - rect.left) / me.scale)
y: Math.floor((e.clientY - rect.top) / me.scale)
return pos
sendMouseLocation = (e) ->
p = getMousePos e
me.sendPointEvent p.x, p.y, me.mouseMask
return unless me.canvas
($ me.canvas).css "cursor", "none"
($ me.canvas).contextmenu (e) ->
e.preventDefault()
return false
($ me.canvas).mousemove (e) -> sendMouseLocation e
($ me.canvas).mousedown (e) ->
state = 1 << e.button
me.mouseMask = me.mouseMask | state
sendMouseLocation e
#e.preventDefault()
($ me.canvas).mouseup (e) ->
state = 1 << e.button
me.mouseMask = me.mouseMask & (~state)
sendMouseLocation e
#e.preventDefault()
me.canvas.onkeydown = me.canvas.onkeyup = me.canvas.onkeypress = (e) ->
# get the key code
if e.key is "Shift"
code = 16
else if e.ctrlKey
code = 17
else if e.altKey
code = 18
else if e.metaKey
code = 91
else
code = String.charCodeAt(e.key)
if e.type is "keydown"
me.sendKeyEvent code, 1
else if e.type is "keyup"
me.sendKeyEvent code, 0
e.preventDefault()
# mouse wheel event
hamster = Hamster @canvas
hamster.wheel (event, delta, deltaX, deltaY) ->
p = getMousePos event.originalEvent
if delta > 0
me.sendPointEvent p.x, p.y, 8
me.sendPointEvent p.x, p.y, 0
return
me.sendPointEvent p.x, p.y, 16
me.sendPointEvent p.x, p.y, 0
initCanvas: (w, h , d) -> initCanvas: (w, h , d) ->
me = @ me = @
@depth = d @depth = d
@buffer.width = w @buffer.width = w
@buffer.height = h @buffer.height = h
@resolution =
w: w,
h: h,
depth: @depth
@decoder.postMessage @resolution
ctx = @buffer.getContext('2d') ctx = @buffer.getContext('2d')
data = ctx.createImageData w, h data = ctx.createImageData w, h
ctx.putImageData data, 0, 0 ctx.putImageData data, 0, 0
#@callback = () ->
# me.draw()
@draw()
decodeFB: (d) -> process: (data) ->
# the zlib is slower than expected data.pixels = new Uint8ClampedArray data.pixels
switch d.flag data.pixels = data.pixels.subarray 10 if data.flag is 0 and @resolution.depth is 32
when 0x0 # raw data
@drawRaw d.x, d.y, d.w, d.h, d.pixels
when 0x1 # jpeg data
@drawJPEG d.x, d.y, d.pixels
when 0x2 # raw compress in zlib format
pixels = pako.inflate(d.pixels)
@drawRaw d.x, d.y, d.w, d.h, pixels
when 0x3 # jpeg compress in zlib format
jpeg = pako.inflate(d.pixels)
@drawJPEG d.x, d.y, jpeg
drawJPEG: (x, y, data) ->
me = @
blob = new Blob [data], { type: "image/jpeg" }
reader = new FileReader()
reader.onloadend = () ->
hiddenImage = new Image()
hiddenImage.style.position = "absolute"
hiddenImage.style.left = "-99999px"
document.body.appendChild hiddenImage
hiddenImage.onload = () ->
ctx = me.buffer.getContext '2d'
ctx.drawImage hiddenImage, x, y
document.body.removeChild hiddenImage
me.draw()
hiddenImage.src = reader.result
reader.readAsDataURL blob
drawRaw: (x, y, w, h, pixels) ->
ctx = @buffer.getContext('2d') ctx = @buffer.getContext('2d')
ctx.globalAlpha = 1.0 imgData = ctx.createImageData data.w, data.h
imgData = ctx.createImageData w, h imgData.data.set data.pixels
imgData.data.set @getCanvasImageData(pixels, w, h) ctx.putImageData imgData, data.x, data.y
ctx.putImageData imgData, x, y
@counter = @counter + 1
@draw()
#if @counter > 50
# @draw()
# @couter = 0
getCanvasImageData: (pixels, w, h) -> @draw() if data.x isnt @lastPose.x or data.y > @resolution.h - 10
return pixels if @depth is 32 @lastPose = { x: data.x, y: data.y }
step = @depth / 8
npixels = pixels.length / step
data = new Uint8ClampedArray w * h * 4 setScale: (n) ->
for i in [0..npixels - 1] @scale = n
value = 0 @draw()
value = value | pixels[i * step + j] << (j * 8) for j in [0..step - 1]
pixel = @pixelValue value
data[i * 4] = pixel.r
data[i * 4 + 1] = pixel.g
data[i * 4 + 2] = pixel.b
data[i * 4 + 3] = pixel.a
return data
draw: () -> draw: () ->
if not @socket if not @socket
return return
scale = 1.0
w = @buffer.width * scale w = @buffer.width * @scale
h = @buffer.height * scale h = @buffer.height * @scale
@canvas.width = w @canvas.width = w
@canvas.height = h @canvas.height = h
ctx = @canvas.getContext "2d" ctx = @canvas.getContext "2d"
ctx.save() ctx.save()
ctx.scale scale, scale ctx.scale @scale, @scale
ctx.clearRect 0, 0, w, h ctx.clearRect 0, 0, w, h
ctx.drawImage @buffer, 0, 0 ctx.drawImage @buffer, 0, 0
ctx.restore() ctx.restore()
pixelValue: (value) ->
pixel =
r: 255
g: 255
b: 255
a: 255
#console.log("len is" + arr.length)
if @depth is 24 or @depth is 32
pixel.r = value & 0xFF
pixel.g = (value >> 8) & 0xFF
pixel.b = (value >> 16) & 0xFF
else if @depth is 16
pixel.r = (value & 0x1F) * (255 / 31)
pixel.g = ((value >> 5) & 0x3F) * (255 / 63)
pixel.b = ((value >> 11) & 0x1F) * (255 / 31)
#console.log pixel
return pixel
openSession: () -> openSession: () ->
me = @ me = @
@socket.close() if @socket @socket.close() if @socket
@ -145,20 +150,45 @@ class WVNC extends window.classes.BaseObject
console.log "socket closed" console.log "socket closed"
initConnection: () -> initConnection: () ->
vncserver = "192.168.1.8:5900" vncserver = "localhost:5901"
@socket.send(@buildCommand 0x01, vncserver) data = new Uint8Array vncserver.length + 5
data[0] = 16 # bbp
###
flag:
0: raw data no compress
1: jpeg no compress
2: raw data compressed by zlib
3: jpeg data compressed by zlib
###
data[1] = 2
data[2] = 50 # jpeg quality
## rate in milisecond
rate = 30
data[3] = rate & 0xFF
data[4] = (rate >> 8) & 0xFF
sendPointEvent: (x,y,mask) -> data.set (new TextEncoder()).encode(vncserver), 5
@socket.send(@buildCommand 0x01, data)
sendPointEvent: (x, y, mask) ->
return unless @socket return unless @socket
data = new Uint8Array 5 data = new Uint8Array 5
data[0] = x & 0xFF data[0] = x & 0xFF
data[1] = x >> 8 data[1] = x >> 8
data[2] = y & 0xFF data[2] = y & 0xFF
data[3] = y >> 8 data[3] = y >> 8
data[4] = 0 data[4] = mask
#console.log x,y #console.log x,y
@socket.send( @buildCommand 0x05, data ) @socket.send( @buildCommand 0x05, data )
sendKeyEvent: (code, v) ->
return unless @socket
data = new Uint8Array 2
data[0] = code
data[1] = v
console.log String.fromCharCode(code), v
@socket.send( @buildCommand 0x06, data )
buildCommand: (hex, o) -> buildCommand: (hex, o) ->
data = undefined data = undefined
switch typeof o switch typeof o
@ -187,7 +217,7 @@ class WVNC extends window.classes.BaseObject
console.log "Error", dec.decode(data) console.log "Error", dec.decode(data)
when 0x81 when 0x81
console.log "Request for password" console.log "Request for password"
pass = "sang" pass = "!x$@n9"
@socket.send (@buildCommand 0x02, pass) @socket.send (@buildCommand 0x02, pass)
when 0x82 when 0x82
console.log "Request for login" console.log "Request for login"
@ -208,22 +238,15 @@ class WVNC extends window.classes.BaseObject
@socket.send(@buildCommand 0x04, 1) @socket.send(@buildCommand 0x04, 1)
when 0x84 when 0x84
#console.log "update" #console.log "update"
d = {} @decoder.postMessage data.buffer, [data.buffer]
d.x = data[1] | (data[2]<<8) #@decodeFB d
d.y = data[3] | (data[4]<<8)
d.w = data[5] | (data[6]<<8)
d.h = data[7] | (data[8]<<8)
d.flag = data[9]
#console.log zlib
d.pixels = data.subarray 10
@decodeFB d
# ack # ack
#@socket.send(@buildCommand 0x04, 1) #@socket.send(@buildCommand 0x04, 1)
else else
console.log cmd console.log cmd
WVNC.dependencies = [ WVNC.dependencies = [
"/assets/scripts/pako.min.js" "/assets/scripts/hamster.js"
] ]
makeclass "WVNC", WVNC makeclass "WVNC", WVNC

View File

@ -0,0 +1,84 @@
#zlib library
importScripts('pako.min.js')
# jpeg library
importScripts('jpeg-decoder.js')
resolution = undefined
pixelValue = (value, depth) ->
pixel =
r: 255
g: 255
b: 255
a: 255
#console.log("len is" + arr.length)
if depth is 24 or depth is 32
pixel.r = value & 0xFF
pixel.g = (value >> 8) & 0xFF
pixel.b = (value >> 16) & 0xFF
else if depth is 16
pixel.r = (value & 0x1F) * (255 / 31)
pixel.g = ((value >> 5) & 0x3F) * (255 / 63)
pixel.b = ((value >> 11) & 0x1F) * (255 / 31)
#console.log pixel
return pixel
getImageData = (d) ->
return d.pixels if resolution.depth is 32
step = resolution.depth / 8
npixels = d.pixels.length / step
data = new Uint8ClampedArray d.w * d.h * 4
for i in [0..npixels - 1]
value = 0
value = value | d.pixels[i * step + j] << (j * 8) for j in [0..step - 1]
pixel = pixelValue value, resolution.depth
data[i * 4] = pixel.r
data[i * 4 + 1] = pixel.g
data[i * 4 + 2] = pixel.b
data[i * 4 + 3] = pixel.a
return data
decodeRaw = (d) ->
d.pixels = getImageData d
return d
decodeJPEG = (d) ->
raw = decode d.pixels, { useTArray: true, colorTransform: true }
d.pixels = raw.data
return d
###
blob = new Blob [d.pixels], { type: "image/jpeg" }
reader = new FileReader()
reader.onloadend = () ->
d.pixels = reader.result
postMessage d
reader.readAsDataURL blob
###
update = (msg) ->
d = {}
data = new Uint8Array msg
d.x = data[1] | (data[2]<<8)
d.y = data[3] | (data[4]<<8)
d.w = data[5] | (data[6]<<8)
d.h = data[7] | (data[8]<<8)
d.flag = data[9]
d.pixels = data.subarray 10
# the zlib is slower than expected
switch d.flag
when 0x0 # raw data
raw = decodeRaw d
when 0x1 # jpeg data
raw = decodeJPEG(d)
when 0x2 # raw compress in zlib format
d.pixels = pako.inflate(d.pixels)
raw = decodeRaw d
when 0x3 # jpeg compress in zlib format
d.pixels = pako.inflate(d.pixels)
raw = decodeJPEG(d)
return unless raw
raw.pixels = raw.pixels.buffer
# fill the rectangle
postMessage raw, [raw.pixels]
onmessage = (e) ->
return resolution = e.data if e.data.depth
update e.data

View File

@ -0,0 +1,114 @@
// Generated by CoffeeScript 1.9.3
var decodeJPEG, decodeRaw, getImageData, onmessage, pixelValue, resolution, update;
importScripts('pako.min.js');
importScripts('jpeg-decoder.js');
resolution = void 0;
pixelValue = function(value, depth) {
var pixel;
pixel = {
r: 255,
g: 255,
b: 255,
a: 255
};
if (depth === 24 || depth === 32) {
pixel.r = value & 0xFF;
pixel.g = (value >> 8) & 0xFF;
pixel.b = (value >> 16) & 0xFF;
} else if (depth === 16) {
pixel.r = (value & 0x1F) * (255 / 31);
pixel.g = ((value >> 5) & 0x3F) * (255 / 63);
pixel.b = ((value >> 11) & 0x1F) * (255 / 31);
}
return pixel;
};
getImageData = function(d) {
var data, i, j, k, l, npixels, pixel, ref, ref1, step, value;
if (resolution.depth === 32) {
return d.pixels;
}
step = resolution.depth / 8;
npixels = d.pixels.length / step;
data = new Uint8ClampedArray(d.w * d.h * 4);
for (i = k = 0, ref = npixels - 1; 0 <= ref ? k <= ref : k >= ref; i = 0 <= ref ? ++k : --k) {
value = 0;
for (j = l = 0, ref1 = step - 1; 0 <= ref1 ? l <= ref1 : l >= ref1; j = 0 <= ref1 ? ++l : --l) {
value = value | d.pixels[i * step + j] << (j * 8);
}
pixel = pixelValue(value, resolution.depth);
data[i * 4] = pixel.r;
data[i * 4 + 1] = pixel.g;
data[i * 4 + 2] = pixel.b;
data[i * 4 + 3] = pixel.a;
}
return data;
};
decodeRaw = function(d) {
d.pixels = getImageData(d);
return d;
};
decodeJPEG = function(d) {
var raw;
raw = decode(d.pixels, {
useTArray: true,
colorTransform: true
});
d.pixels = raw.data;
return d;
/*
blob = new Blob [d.pixels], { type: "image/jpeg" }
reader = new FileReader()
reader.onloadend = () ->
d.pixels = reader.result
postMessage d
reader.readAsDataURL blob
*/
};
update = function(msg) {
var d, data, raw;
d = {};
data = new Uint8Array(msg);
d.x = data[1] | (data[2] << 8);
d.y = data[3] | (data[4] << 8);
d.w = data[5] | (data[6] << 8);
d.h = data[7] | (data[8] << 8);
d.flag = data[9];
d.pixels = data.subarray(10);
switch (d.flag) {
case 0x0:
raw = decodeRaw(d);
break;
case 0x1:
raw = decodeJPEG(d);
break;
case 0x2:
d.pixels = pako.inflate(d.pixels);
raw = decodeRaw(d);
break;
case 0x3:
d.pixels = pako.inflate(d.pixels);
raw = decodeJPEG(d);
}
if (!raw) {
return;
}
raw.pixels = raw.pixels.buffer;
return postMessage(raw, [raw.pixels]);
};
onmessage = function(e) {
if (e.data.depth) {
return resolution = e.data;
}
return update(e.data);
};

View File

@ -0,0 +1,327 @@
/*
* Hamster.js v1.1.2
* (c) 2013 Monospaced http://monospaced.com
* License: MIT
*/
(function(window, document){
'use strict';
/**
* Hamster
* use this to create instances
* @returns {Hamster.Instance}
* @constructor
*/
var Hamster = function(element) {
return new Hamster.Instance(element);
};
// default event name
Hamster.SUPPORT = 'wheel';
// default DOM methods
Hamster.ADD_EVENT = 'addEventListener';
Hamster.REMOVE_EVENT = 'removeEventListener';
Hamster.PREFIX = '';
// until browser inconsistencies have been fixed...
Hamster.READY = false;
Hamster.Instance = function(element){
if (!Hamster.READY) {
// fix browser inconsistencies
Hamster.normalise.browser();
// Hamster is ready...!
Hamster.READY = true;
}
this.element = element;
// store attached event handlers
this.handlers = [];
// return instance
return this;
};
/**
* create new hamster instance
* all methods should return the instance itself, so it is chainable.
* @param {HTMLElement} element
* @returns {Hamster.Instance}
* @constructor
*/
Hamster.Instance.prototype = {
/**
* bind events to the instance
* @param {Function} handler
* @param {Boolean} useCapture
* @returns {Hamster.Instance}
*/
wheel: function onEvent(handler, useCapture){
Hamster.event.add(this, Hamster.SUPPORT, handler, useCapture);
// handle MozMousePixelScroll in older Firefox
if (Hamster.SUPPORT === 'DOMMouseScroll') {
Hamster.event.add(this, 'MozMousePixelScroll', handler, useCapture);
}
return this;
},
/**
* unbind events to the instance
* @param {Function} handler
* @param {Boolean} useCapture
* @returns {Hamster.Instance}
*/
unwheel: function offEvent(handler, useCapture){
// if no handler argument,
// unbind the last bound handler (if exists)
if (handler === undefined && (handler = this.handlers.slice(-1)[0])) {
handler = handler.original;
}
Hamster.event.remove(this, Hamster.SUPPORT, handler, useCapture);
// handle MozMousePixelScroll in older Firefox
if (Hamster.SUPPORT === 'DOMMouseScroll') {
Hamster.event.remove(this, 'MozMousePixelScroll', handler, useCapture);
}
return this;
}
};
Hamster.event = {
/**
* cross-browser 'addWheelListener'
* @param {Instance} hamster
* @param {String} eventName
* @param {Function} handler
* @param {Boolean} useCapture
*/
add: function add(hamster, eventName, handler, useCapture){
// store the original handler
var originalHandler = handler;
// redefine the handler
handler = function(originalEvent){
if (!originalEvent) {
originalEvent = window.event;
}
// create a normalised event object,
// and normalise "deltas" of the mouse wheel
var event = Hamster.normalise.event(originalEvent),
delta = Hamster.normalise.delta(originalEvent);
// fire the original handler with normalised arguments
return originalHandler(event, delta[0], delta[1], delta[2]);
};
// cross-browser addEventListener
hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false);
// store original and normalised handlers on the instance
hamster.handlers.push({
original: originalHandler,
normalised: handler
});
},
/**
* removeWheelListener
* @param {Instance} hamster
* @param {String} eventName
* @param {Function} handler
* @param {Boolean} useCapture
*/
remove: function remove(hamster, eventName, handler, useCapture){
// find the normalised handler on the instance
var originalHandler = handler,
lookup = {},
handlers;
for (var i = 0, len = hamster.handlers.length; i < len; ++i) {
lookup[hamster.handlers[i].original] = hamster.handlers[i];
}
handlers = lookup[originalHandler];
handler = handlers.normalised;
// cross-browser removeEventListener
hamster.element[Hamster.REMOVE_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false);
// remove original and normalised handlers from the instance
for (var h in hamster.handlers) {
if (hamster.handlers[h] == handlers) {
hamster.handlers.splice(h, 1);
break;
}
}
}
};
/**
* these hold the lowest deltas,
* used to normalise the delta values
* @type {Number}
*/
var lowestDelta,
lowestDeltaXY;
Hamster.normalise = {
/**
* fix browser inconsistencies
*/
browser: function normaliseBrowser(){
// detect deprecated wheel events
if (!('onwheel' in document || document.documentMode >= 9)) {
Hamster.SUPPORT = document.onmousewheel !== undefined ?
'mousewheel' : // webkit and IE < 9 support at least "mousewheel"
'DOMMouseScroll'; // assume remaining browsers are older Firefox
}
// detect deprecated event model
if (!window.addEventListener) {
// assume IE < 9
Hamster.ADD_EVENT = 'attachEvent';
Hamster.REMOVE_EVENT = 'detachEvent';
Hamster.PREFIX = 'on';
}
},
/**
* create a normalised event object
* @param {Function} originalEvent
* @returns {Object} event
*/
event: function normaliseEvent(originalEvent){
var event = {
// keep a reference to the original event object
originalEvent: originalEvent,
target: originalEvent.target || originalEvent.srcElement,
type: 'wheel',
deltaMode: originalEvent.type === 'MozMousePixelScroll' ? 0 : 1,
deltaX: 0,
deltaZ: 0,
preventDefault: function(){
if (originalEvent.preventDefault) {
originalEvent.preventDefault();
} else {
originalEvent.returnValue = false;
}
},
stopPropagation: function(){
if (originalEvent.stopPropagation) {
originalEvent.stopPropagation();
} else {
originalEvent.cancelBubble = false;
}
}
};
// calculate deltaY (and deltaX) according to the event
// 'mousewheel'
if (originalEvent.wheelDelta) {
event.deltaY = - 1/40 * originalEvent.wheelDelta;
}
// webkit
if (originalEvent.wheelDeltaX) {
event.deltaX = - 1/40 * originalEvent.wheelDeltaX;
}
// 'DomMouseScroll'
if (originalEvent.detail) {
event.deltaY = originalEvent.detail;
}
return event;
},
/**
* normalise 'deltas' of the mouse wheel
* @param {Function} originalEvent
* @returns {Array} deltas
*/
delta: function normaliseDelta(originalEvent){
var delta = 0,
deltaX = 0,
deltaY = 0,
absDelta = 0,
absDeltaXY = 0,
fn;
// normalise deltas according to the event
// 'wheel' event
if (originalEvent.deltaY) {
deltaY = originalEvent.deltaY * -1;
delta = deltaY;
}
if (originalEvent.deltaX) {
deltaX = originalEvent.deltaX;
delta = deltaX * -1;
}
// 'mousewheel' event
if (originalEvent.wheelDelta) {
delta = originalEvent.wheelDelta;
}
// webkit
if (originalEvent.wheelDeltaY) {
deltaY = originalEvent.wheelDeltaY;
}
if (originalEvent.wheelDeltaX) {
deltaX = originalEvent.wheelDeltaX * -1;
}
// 'DomMouseScroll' event
if (originalEvent.detail) {
delta = originalEvent.detail * -1;
}
// Don't return NaN
if (delta === 0) {
return [0, 0, 0];
}
// look for lowest delta to normalize the delta values
absDelta = Math.abs(delta);
if (!lowestDelta || absDelta < lowestDelta) {
lowestDelta = absDelta;
}
absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX));
if (!lowestDeltaXY || absDeltaXY < lowestDeltaXY) {
lowestDeltaXY = absDeltaXY;
}
// convert deltas to whole numbers
fn = delta > 0 ? 'floor' : 'ceil';
delta = Math[fn](delta / lowestDelta);
deltaX = Math[fn](deltaX / lowestDeltaXY);
deltaY = Math[fn](deltaY / lowestDeltaXY);
return [delta, deltaX, deltaY];
}
};
if (typeof window.define === 'function' && window.define.amd) {
// AMD
window.define('hamster', [], function(){
return Hamster;
});
} else if (typeof exports === 'object') {
// CommonJS
module.exports = Hamster;
} else {
// Browser global
window.Hamster = Hamster;
}
})(window, window.document);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7 // Generated by CoffeeScript 1.9.3
(function() { (function() {
var APIManager, BaseObject, MarkOn, WVNC, require, var APIManager, BaseObject, MarkOn, WVNC, require,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
@ -164,6 +164,7 @@
extend(WVNC, superClass); extend(WVNC, superClass);
function WVNC(args) { function WVNC(args) {
var me;
this.args = args; this.args = args;
WVNC.__super__.constructor.call(this, "WVNC"); WVNC.__super__.constructor.call(this, "WVNC");
this.socket = void 0; this.socket = void 0;
@ -176,7 +177,17 @@
this.canvas = ($(this.args[1]))[0]; this.canvas = ($(this.args[1]))[0];
} }
this.buffer = $("<canvas>")[0]; this.buffer = $("<canvas>")[0];
this.counter = 0; this.lastPose = {
x: 0,
y: 0
};
this.scale = 0.8;
this.decoder = new Worker('/assets/scripts/decoder.js');
me = this;
this.mouseMask = 0;
this.decoder.onmessage = function(e) {
return me.process(e.data);
};
} }
WVNC.prototype.init = function() { WVNC.prototype.init = function() {
@ -189,146 +200,146 @@
} }
}); });
$("#connect").click(function(e) { $("#connect").click(function(e) {
me.counter = 0;
return me.openSession(); return me.openSession();
}); });
($(me.canvas)).css("cursor", "none"); return me.initInputEvent();
return ($(me.canvas)).mousemove(function(e) {
var rect, x, y;
rect = me.canvas.getBoundingClientRect();
x = Math.floor(e.clientX - rect.left);
y = Math.floor(e.clientY - rect.top);
return me.sendPointEvent(x, y, 0);
});
})["catch"](function(m, s) { })["catch"](function(m, s) {
return console.error(m, s); return console.error(m, s);
}); });
}; };
WVNC.prototype.initInputEvent = function() {
var getMousePos, hamster, me, sendMouseLocation;
me = this;
getMousePos = function(e) {
var pos, rect;
rect = me.canvas.getBoundingClientRect();
pos = {
x: Math.floor((e.clientX - rect.left) / me.scale),
y: Math.floor((e.clientY - rect.top) / me.scale)
};
return pos;
};
sendMouseLocation = function(e) {
var p;
p = getMousePos(e);
return me.sendPointEvent(p.x, p.y, me.mouseMask);
};
if (!me.canvas) {
return;
}
($(me.canvas)).css("cursor", "none");
($(me.canvas)).contextmenu(function(e) {
e.preventDefault();
return false;
});
($(me.canvas)).mousemove(function(e) {
return sendMouseLocation(e);
});
($(me.canvas)).mousedown(function(e) {
var state;
state = 1 << e.button;
me.mouseMask = me.mouseMask | state;
return sendMouseLocation(e);
});
($(me.canvas)).mouseup(function(e) {
var state;
state = 1 << e.button;
me.mouseMask = me.mouseMask & (~state);
return sendMouseLocation(e);
});
me.canvas.onkeydown = me.canvas.onkeyup = me.canvas.onkeypress = function(e) {
var code;
if (e.key === "Shift") {
code = 16;
} else if (e.ctrlKey) {
code = 17;
} else if (e.altKey) {
code = 18;
} else if (e.metaKey) {
code = 91;
} else {
code = String.charCodeAt(e.key);
}
if (e.type === "keydown") {
me.sendKeyEvent(code, 1);
} else if (e.type === "keyup") {
me.sendKeyEvent(code, 0);
}
return e.preventDefault();
};
hamster = Hamster(this.canvas);
return hamster.wheel(function(event, delta, deltaX, deltaY) {
var p;
p = getMousePos(event.originalEvent);
if (delta > 0) {
me.sendPointEvent(p.x, p.y, 8);
me.sendPointEvent(p.x, p.y, 0);
return;
}
me.sendPointEvent(p.x, p.y, 16);
return me.sendPointEvent(p.x, p.y, 0);
});
};
WVNC.prototype.initCanvas = function(w, h, d) { WVNC.prototype.initCanvas = function(w, h, d) {
var ctx, data, me; var ctx, data, me;
me = this; me = this;
this.depth = d; this.depth = d;
this.buffer.width = w; this.buffer.width = w;
this.buffer.height = h; this.buffer.height = h;
this.resolution = {
w: w,
h: h,
depth: this.depth
};
this.decoder.postMessage(this.resolution);
ctx = this.buffer.getContext('2d'); ctx = this.buffer.getContext('2d');
data = ctx.createImageData(w, h); data = ctx.createImageData(w, h);
ctx.putImageData(data, 0, 0); return ctx.putImageData(data, 0, 0);
return this.draw();
}; };
WVNC.prototype.decodeFB = function(d) { WVNC.prototype.process = function(data) {
var jpeg, pixels;
switch (d.flag) {
case 0x0:
return this.drawRaw(d.x, d.y, d.w, d.h, d.pixels);
case 0x1:
return this.drawJPEG(d.x, d.y, d.pixels);
case 0x2:
pixels = pako.inflate(d.pixels);
return this.drawRaw(d.x, d.y, d.w, d.h, pixels);
case 0x3:
jpeg = pako.inflate(d.pixels);
return this.drawJPEG(d.x, d.y, jpeg);
}
};
WVNC.prototype.drawJPEG = function(x, y, data) {
var blob, me, reader;
me = this;
blob = new Blob([data], {
type: "image/jpeg"
});
reader = new FileReader();
reader.onloadend = function() {
var hiddenImage;
hiddenImage = new Image();
hiddenImage.style.position = "absolute";
hiddenImage.style.left = "-99999px";
document.body.appendChild(hiddenImage);
hiddenImage.onload = function() {
var ctx;
ctx = me.buffer.getContext('2d');
ctx.drawImage(hiddenImage, x, y);
document.body.removeChild(hiddenImage);
return me.draw();
};
return hiddenImage.src = reader.result;
};
return reader.readAsDataURL(blob);
};
WVNC.prototype.drawRaw = function(x, y, w, h, pixels) {
var ctx, imgData; var ctx, imgData;
data.pixels = new Uint8ClampedArray(data.pixels);
if (data.flag === 0 && this.resolution.depth === 32) {
data.pixels = data.pixels.subarray(10);
}
ctx = this.buffer.getContext('2d'); ctx = this.buffer.getContext('2d');
ctx.globalAlpha = 1.0; imgData = ctx.createImageData(data.w, data.h);
imgData = ctx.createImageData(w, h); imgData.data.set(data.pixels);
imgData.data.set(this.getCanvasImageData(pixels, w, h)); ctx.putImageData(imgData, data.x, data.y);
ctx.putImageData(imgData, x, y); if (data.x !== this.lastPose.x || data.y > this.resolution.h - 10) {
this.counter = this.counter + 1; this.draw();
return this.draw(); }
return this.lastPose = {
x: data.x,
y: data.y
};
}; };
WVNC.prototype.getCanvasImageData = function(pixels, w, h) { WVNC.prototype.setScale = function(n) {
var data, i, j, k, npixels, p, pixel, ref, ref1, step, value; this.scale = n;
if (this.depth === 32) { return this.draw();
return pixels;
}
step = this.depth / 8;
npixels = pixels.length / step;
data = new Uint8ClampedArray(w * h * 4);
for (i = k = 0, ref = npixels - 1; 0 <= ref ? k <= ref : k >= ref; i = 0 <= ref ? ++k : --k) {
value = 0;
for (j = p = 0, ref1 = step - 1; 0 <= ref1 ? p <= ref1 : p >= ref1; j = 0 <= ref1 ? ++p : --p) {
value = value | pixels[i * step + j] << (j * 8);
}
pixel = this.pixelValue(value);
data[i * 4] = pixel.r;
data[i * 4 + 1] = pixel.g;
data[i * 4 + 2] = pixel.b;
data[i * 4 + 3] = pixel.a;
}
return data;
}; };
WVNC.prototype.draw = function() { WVNC.prototype.draw = function() {
var ctx, h, scale, w; var ctx, h, w;
if (!this.socket) { if (!this.socket) {
return; return;
} }
scale = 1.0; w = this.buffer.width * this.scale;
w = this.buffer.width * scale; h = this.buffer.height * this.scale;
h = this.buffer.height * scale;
this.canvas.width = w; this.canvas.width = w;
this.canvas.height = h; this.canvas.height = h;
ctx = this.canvas.getContext("2d"); ctx = this.canvas.getContext("2d");
ctx.save(); ctx.save();
ctx.scale(scale, scale); ctx.scale(this.scale, this.scale);
ctx.clearRect(0, 0, w, h); ctx.clearRect(0, 0, w, h);
ctx.drawImage(this.buffer, 0, 0); ctx.drawImage(this.buffer, 0, 0);
return ctx.restore(); return ctx.restore();
}; };
WVNC.prototype.pixelValue = function(value) {
var pixel;
pixel = {
r: 255,
g: 255,
b: 255,
a: 255
};
if (this.depth === 24 || this.depth === 32) {
pixel.r = value & 0xFF;
pixel.g = (value >> 8) & 0xFF;
pixel.b = (value >> 16) & 0xFF;
} else if (this.depth === 16) {
pixel.r = (value & 0x1F) * (255 / 31);
pixel.g = ((value >> 5) & 0x3F) * (255 / 63);
pixel.b = ((value >> 11) & 0x1F) * (255 / 31);
}
return pixel;
};
WVNC.prototype.openSession = function() { WVNC.prototype.openSession = function() {
var me; var me;
me = this; me = this;
@ -354,9 +365,25 @@
}; };
WVNC.prototype.initConnection = function() { WVNC.prototype.initConnection = function() {
var vncserver; var data, rate, vncserver;
vncserver = "192.168.1.8:5900"; vncserver = "localhost:5901";
return this.socket.send(this.buildCommand(0x01, vncserver)); data = new Uint8Array(vncserver.length + 5);
data[0] = 16;
/*
flag:
0: raw data no compress
1: jpeg no compress
2: raw data compressed by zlib
3: jpeg data compressed by zlib
*/
data[1] = 2;
data[2] = 50;
rate = 30;
data[3] = rate & 0xFF;
data[4] = (rate >> 8) & 0xFF;
data.set((new TextEncoder()).encode(vncserver), 5);
return this.socket.send(this.buildCommand(0x01, data));
}; };
WVNC.prototype.sendPointEvent = function(x, y, mask) { WVNC.prototype.sendPointEvent = function(x, y, mask) {
@ -369,10 +396,22 @@
data[1] = x >> 8; data[1] = x >> 8;
data[2] = y & 0xFF; data[2] = y & 0xFF;
data[3] = y >> 8; data[3] = y >> 8;
data[4] = 0; data[4] = mask;
return this.socket.send(this.buildCommand(0x05, data)); return this.socket.send(this.buildCommand(0x05, data));
}; };
WVNC.prototype.sendKeyEvent = function(code, v) {
var data;
if (!this.socket) {
return;
}
data = new Uint8Array(2);
data[0] = code;
data[1] = v;
console.log(String.fromCharCode(code), v);
return this.socket.send(this.buildCommand(0x06, data));
};
WVNC.prototype.buildCommand = function(hex, o) { WVNC.prototype.buildCommand = function(hex, o) {
var cmd, data; var cmd, data;
data = void 0; data = void 0;
@ -395,7 +434,7 @@
}; };
WVNC.prototype.consume = function(e) { WVNC.prototype.consume = function(e) {
var arr, cmd, d, data, dec, depth, h, pass, user, w; var arr, cmd, data, dec, depth, h, pass, user, w;
data = new Uint8Array(e.data); data = new Uint8Array(e.data);
cmd = data[0]; cmd = data[0];
switch (cmd) { switch (cmd) {
@ -405,7 +444,7 @@
return console.log("Error", dec.decode(data)); return console.log("Error", dec.decode(data));
case 0x81: case 0x81:
console.log("Request for password"); console.log("Request for password");
pass = "sang"; pass = "!x$@n9";
return this.socket.send(this.buildCommand(0x02, pass)); return this.socket.send(this.buildCommand(0x02, pass));
case 0x82: case 0x82:
console.log("Request for login"); console.log("Request for login");
@ -424,14 +463,7 @@
this.initCanvas(w, h, depth); this.initCanvas(w, h, depth);
return this.socket.send(this.buildCommand(0x04, 1)); return this.socket.send(this.buildCommand(0x04, 1));
case 0x84: case 0x84:
d = {}; return this.decoder.postMessage(data.buffer, [data.buffer]);
d.x = data[1] | (data[2] << 8);
d.y = data[3] | (data[4] << 8);
d.w = data[5] | (data[6] << 8);
d.h = data[7] | (data[8] << 8);
d.flag = data[9];
d.pixels = data.subarray(10);
return this.decodeFB(d);
default: default:
return console.log(cmd); return console.log(cmd);
} }
@ -441,7 +473,7 @@
})(window.classes.BaseObject); })(window.classes.BaseObject);
WVNC.dependencies = ["/assets/scripts/pako.min.js"]; WVNC.dependencies = ["/assets/scripts/hamster.js"];
makeclass("WVNC", WVNC); makeclass("WVNC", WVNC);

View File

@ -1,3 +1,3 @@
<h1>VNC screen here</h1> <h1>VNC screen here</h1>
<p><a id="connect" href="#">Connect</a><a id="stop" href="#">STOP</a></p> <p><a id="connect" href="#">Connect</a><a id="stop" href="#">STOP</a></p>
<canvas id = "canvas"></canvas> <canvas id = "canvas" tabindex="1"></canvas>