First, primitive stab at SPiCE integration

Launch an Xspice and run:
echo -ne "\033]844;127.0.0.1;9876\007"

This will launch a SPiCE client connecting to 127.0.0.1:9876.

Still need to add all the security stuff and generally be
more defensive in the implementation.
This commit is contained in:
Soren L. Hansen 2021-04-16 06:50:05 -07:00
parent d9fe29e9c7
commit c66ae7b2e4
167 changed files with 41905 additions and 55 deletions

View File

@ -10,9 +10,9 @@ docker:
docker build . -t gotty-bash:$(VERSION)
.PHONY: asset
asset: bindata/static/js/gotty.js bindata/static/index.html bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png server/asset.go
asset: bindata/static/js/gotty.js bindata/static/js/spice.js bindata/static/index.html bindata/static/spice bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png server/asset.go
server/asset.go:
server/asset.go: $(shell find bindata)
go-bindata -prefix bindata -pkg server -ignore=\\.gitkeep -o server/asset.go bindata/...
gofmt -w server/asset.go
@ -25,6 +25,9 @@ bindata:
bindata/static: bindata
mkdir bindata/static
bindata/static/spice:
cp -r js/spice-web-client bindata/static/spice
bindata/static/index.html: bindata/static resources/index.html
cp resources/index.html bindata/static/index.html
@ -56,7 +59,7 @@ js/node_modules/xterm/dist/xterm.css:
cd js && \
npm install
bindata/static/js/gotty.js: js/src/* js/node_modules/webpack
bindata/static/js/gotty.js: js/src/
cd js && \
npx webpack

0
js/index.html Normal file
View File

22
js/package-lock.json generated
View File

@ -1042,6 +1042,17 @@
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
},
"schema-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.6",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -1360,17 +1371,6 @@
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
"dev": true
},
"schema-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.6",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
}
},
"tapable": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",

View File

@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2016 eyeOS S.L.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,99 @@
#Complete Spice Web Client written in HTML5 and Javascript
Full and complete implementation of the SPICE protocol (by Red Hat) written in HTML5 and JavaScript. It allows any standard HTML5-ready Web Browser to connect to remote
virtual sessions just by accessing a single URL.
The client can be deployed through a normal web server to connect to spice sessions. To use it you would need to proxy your spice session through a websockets-to-tcp
proxy like Kanaka, Websockify or similar projects.
NOTE: This project is NOT based on the spice-html5 prototype.
## Features
- Full QXL Support of the entire spice protocol, including clipping, masking, scaling etc (accelerated mode)
- Audio support, but only for raw audio samples, not for celt
- Full KeyBoard support including English, Spanish and Catalan layouts
- Clipboard sharing support with customizble interface
- Video streaming support with excellent performance even at 60fps FHD 1080p
- Extremly high performant LZ decoder with sub <10ms for a FHD 1080P image
- Pure Javascript codec for quic
- Configurable multi core support using webworkers (by default it uses 4 CPU Cores)
- Spice Agent support
- Set resolution support
- Honors spice cache for images, cursors and palettes
- Very low memory footprint for a javascript application like this
- Spice authentication tokens support
- Supports graphic live debugging the spice protocol and to replay packets to fix bugs
##Missing features
There are some SPICE features still to be implemented, the most important ones are:
- Celt or other audio codec
- USB redirection (not possible at browser level, maybe with a plugin?)
##Client System requirements
To get the best result we recommend at least 1GB of ram and at least two cores at 1,5ghz.
It should work decently on 512mb of ram and 1ghz.
We have made tests in raspberry pi 2 with very good results.
##Network requirements
Only Binary websockets are used to send and receive server data, so you should expect similar network requirements than SPICE itself.
for a normal 1080p session the performance is very good up to 150-200ms of latency and 100kb/s bandwidth.
The network consumption of a spice session depends a lot on the usage patterns.
##Performance
Writing a web client for a protocol like spice is challenge because of the limited access to system resources like GPU and the way the javascript VM works.
We have spent almost 2 years profiling the entire project. The lz decoder has been optimized to <10Ms for full hd images. Quic codec has been hacked a lot
to get acceptable performance even being executed in javascript.
We have created a graphic pipeline to remove unnecesary draw operations that are going to be overdrawn at the next known packets. We have minimized the work
for the javascript GC and refined all our canvas operations and all the entire stack to prevent big data structures to be copied.
You should expect a near perfect experience if you meet the client requirements and the network requirements.
##Browser support
We strongly recommend use the spice web client with Chromium/Chrome or Firefox, however it should work at least on:
- Google Chrome
- Firefox
- Internet Explorer 11
- Edge
##How to use it
In order to work you only need to provide the IP address of the websockets proxy and the port
of the websockets proxy.
You can do it permanently editing run.js or through the URL using the parameters:
http://example.com/spice-web-client/index.html?host=IP_ADDRESS_OF_WEBSOCKIFY&port=TCP_PORT_OF_WEBSOCKIFY
By doing this you will connect to the remote spice session and the resolution will be adapted to your browser viewport area.
##Notes For linux sessions
If you are planning to use this to connect to remote linux sessions you should consider disabling compositing on your desktop. The best performance is achieved with
kde with compositing and visual effects disabled.
Always install the spice-vdagent and xorg-qxl to get the best results and to have custom resolutions etc.
##Notes For Windows sessions
Spice web client has a very good performance connecting to remote windows sessions. Always install the spice-agent package including the qxl video driver to get the best results and to have custom resolutions etc.
##More information
For more information about the implementation or questions about roadmap etc contact Jose Carlos Norte (jcarlosn) at jcarlos.norte@gmail.com
##License
Spice Web Client is distributed under the terms of the [MIT license](https://opensource.org/licenses/MIT).

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,242 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.Agent = $.spcExtend(wdi.EventObject.prototype, {
clientTokens:null,
serverTokens: 10,
app: null,
clipboardContent: null,
clipboardGrabbed: false,
clipboardRequestReceived: false,
clipboardPending: false, // to keep clipboard data until spice sends us its request (clipboardRequestReceived)
clipboardEnabled: true,
windows: null,
init: function(c) {
this.superInit();
this.app = c.app;
},
sendInitMessage: function() {
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_START,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.SpiceMsgMainAgentTokens({
num_tokens: this.serverTokens
})
});
this.app.spiceConnection.send(packet);
var mycaps = (1 << wdi.AgentCaps.VD_AGENT_CAP_MONITORS_CONFIG);
if (this.clipboardEnabled) {
mycaps = mycaps | (1 << wdi.AgentCaps.VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
}
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.VDAgentMessage({
protocol: 1, //agent protocol version, should be unhardcoded
type: wdi.AgentMessageTypes.VD_AGENT_ANNOUNCE_CAPABILITIES,
opaque: 0,
data: new wdi.VDAgentAnnounceCapabilities({
request: 0,
caps: mycaps
})
})
});
this.sendAgentPacket(packet);
// //tokens allocation
// packet = new wdi.SpiceMessage({
// messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_TOKEN,
// channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
// args: new wdi.SpiceMsgMainAgentTokens({
// num_tokens: 4294967295 // FF FF FF FF
// })
// });
// app.spiceConnection.send(packet);
},
setResolution: function(width, height) {
//TODO move this to a setting
if(width < 800) {
width = 800;
}
if(height < 600) {
height = 600;
}
//adapt resolution, TODO: this needs to be refractored
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.VDAgentMessage({
protocol: 1, //agent protocol version, should be unhardcoded
type: wdi.AgentMessageTypes.VD_AGENT_MONITORS_CONFIG,
opaque: 0,
data: new wdi.VDAgentMonitorsConfig({
num_of_monitors: 1,
flags: 0,
data: new wdi.VDAgentMonConfig({
width: width,
height: height,
depth: 32,
x: 0,
y: 0
})
})
})
});
this.sendAgentPacket(packet);
},
setClientTokens: function(tokens) {
this.clientTokens = tokens;
},
sendAgentPacket: function(packet) {
this.clientTokens--;
this.app.spiceConnection.send(packet);
},
onAgentData: function(packet) {
this.serverTokens--; //we have just received a server package, we decrement the tokens
if (this.serverTokens == 0) { // we send 10 more tokens to server
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_TOKEN,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.SpiceMsgMainAgentTokens({
num_tokens: 10
})
});
this.app.spiceConnection.send(packet);
this.serverTokens = 10;
}
if(packet.type == wdi.AgentMessageTypes.VD_AGENT_ANNOUNCE_CAPABILITIES) {
//??
} else if(packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_GRAB) {
if(packet.clipboardType == wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT) {
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.VDAgentMessage({
protocol: 1, //agent protocol version, should be unhardcoded
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_REQUEST,
opaque: 0,
data: new wdi.VDAgentClipboardRequest({
type: wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT
})
})
});
this.sendAgentPacket(packet);
}
} else if(packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD) {
this.fire('clipBoardData', packet.clipboardData);
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_REQUEST) {
this.clipboardRequestReceived = true;
if (this.clipboardPending) {
this.clipboardPending = false;
this.sendPaste();
}
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_RELEASE) {
//debugger;// we've never seen this packet... if we receive it sometime, please warn somebody!!
this.clipboardGrabbed = false;
this.clipboardRequestReceived = false;
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_REPLY) {
} else {
console.log('agent ?',packet.type);
}
},
setClipboard: function(text) {
if (text != this.clipboardContent) {
this.clipboardContent = text;
this.sendGrab();
this.sendPaste();
}
this.app.sendShortcut(wdi.keyShortcutsHandled.CTRLV);
},
sendGrab: function() {
if (!this.clipboardGrabbed) {
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.VDAgentMessage({
protocol: 1, //agent protocol version, should be unhardcoded
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_GRAB,
opaque: 0,
data: new wdi.VDAgentClipboardGrab({
types: [wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT]
})
})
});
this.sendAgentPacket(packet);
}
},
/**
* Sends the text received from browser to spice
*
* @param clipboardContent
*/
sendPaste: function() {
if (this.clipboardRequestReceived) {
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.VDAgentMessage({
protocol: 1, //agent protocol version, should be unhardcoded
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD,
opaque: 0,
data: new wdi.VDAgentClipboard({
type: wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT,
data: this.clipboardContent
})
})
});
this.clipboardRequestReceived = false;
this.sendAgentPacket(packet);
} else {
// we still haven't received the request event from server, we keep the clipboard data until then
this.clipboardPending = true;
}
},
disableClipboard: function () {
this.clipboardEnabled = false;
}
});

View File

@ -0,0 +1,483 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
Application = $.spcExtend(wdi.DomainObject, {
spiceConnection: null,
clientGui: null,
agent: null,
externalCallback: null,
keyboardEnabled: true,
packetProcess: null,
inputProcess: null,
multimediaTime: null,
lastMultimediaTime: null,
busConnection: null,
busProcess: null,
timeLapseDetector: null,
init: function (c) {
wdi.GlobalPool.init();
this.spiceConnection = c.spiceConnection || new wdi.SpiceConnection();
this.clientGui = c.clientGui || new wdi.ClientGui();
this.agent = c.agent || new wdi.Agent({
app: this
});
this.inputProcess = c.inputProcess || new wdi.InputProcess({
clientGui: this.clientGui,
spiceConnection: this.spiceConnection
});
this.packetProcess = c.packetProcess;
this.busConnection = c.busConnection || new wdi.BusConnection();
this.busProcess = c.busProcess || new wdi.BusProcess({
clientGui: this.clientGui,
busConnection: this.busConnection
});
this.timeLapseDetector = c.timeLapseDetector || new wdi.TimeLapseDetector();
this.setup();
},
run: function (c) {
if(c.hasOwnProperty('seamlessDesktopIntegration')) {
wdi.SeamlessIntegration = c['seamlessDesktopIntegration'];
}
if (!this.packetProcess) {
var displayProcess = false;
if (c.useWorkers === false) {
displayProcess = new wdi.DisplayProcess({
clientGui: this.clientGui
});
}
this.packetProcess = new wdi.PacketProcess({
app: this,
clientGui: this.clientGui,
agent: this.agent,
spiceConnection: this.spiceConnection,
inputsProcess: this.inputProcess,
displayProcess: displayProcess
})
}
if (window.vdiLoadTest) {
this.spiceConnection.addListener('message', this.onMessage, this);
} else {
this.spiceConnection.addListener('message', this.packetProcess.process, this.packetProcess);
}
this.busConnection.connect(c);
this.timeLapseDetector.startTimer();
if (c['canvasMargin']) {
this.clientGui.setCanvasMargin(c['canvasMargin']);
}
if (c['disableClipboard']) {
this.agent.disableClipboard();
this.clientGui.disableClipboard();
this.enableCtrlV();
}
if(c['layer']) {
this.clientGui.setLayer(c['layer']);
}
if (this.clientGui.checkFeatures()) {
if (wdi.SeamlessIntegration) {
this.disableKeyboard();//keyboard should start disabled is integrated
}
wdi.Keymap.loadKeyMap(c['layout']);
this.setExternalCallback(c['callback'], c['context']);
try {
this.connect({
host: c['host'],
port: c['port'],
protocol: c['protocol'],
vmHost: c['vmHost'],
vmPort: c['vmPort'],
vmInfoToken: c['vmInfoToken'],
busHost: c['busHost'],
token: c['token'],
connectionControl: c['connectionControl'],
heartbeatToken: c['heartbeatToken'],
heartbeatTimeout: c['heartbeatTimeout']
});
} catch (e) {
this.executeExternalCallback('error', 1);
}
this.clientGui.setClientOffset(c['clientOffset']['x'], c['clientOffset']['y']);
}
if (c.hasOwnProperty('externalClipboardHandling')) {
this.externalClipoardHandling = c['externalClipboardHandling'];
}
},
end: function () {
//TODO: end?
},
setup: function () {
this.spiceConnection.addListener('mouseMode', this.onMouseMode, this);
this.spiceConnection.addListener('initAgent', this.onInitAgent, this);
this.spiceConnection.addListener('error', this.onDisconnect, this);
this.spiceConnection.addListener('channelConnected', this.onChannelConnected, this);
this.clientGui.addListener('input', this.onClientInput, this);
this.clientGui.addListener('resolution', this.onResolution, this);
this.clientGui.addListener('paste', this.onPaste, this);
this.clientGui.addListener('startAudio', this.onStartAudio, this);
this.busProcess.addListener('windowCreated', this.onWindowCreated, this);
this.busProcess.addListener('windowClosed', this.onWindowClosed, this);
this.busProcess.addListener('windowMoved', this.onWindowMoved, this);
this.busProcess.addListener('windowResized', this.onWindowResized, this);
this.busProcess.addListener('windowFocused', this.onWindowFocused, this);
this.busProcess.addListener('windowMinimized', this.onWindowMinimized, this);
this.busProcess.addListener('windowRestored', this.onWindowRestored, this);
this.busProcess.addListener('windowMaximized', this.onWindowMaximized, this);
this.busProcess.addListener('busConnected', this.onBusConnected, this);
this.busProcess.addListener('menuResponse', this.onMenuResponse, this);
this.busProcess.addListener('networkDriveResponse', this.onNetworkDriveResponse, this);
this.busProcess.addListener('wrongPathError', this.onWrongPathError, this);
this.busProcess.addListener('applicationLaunchedSuccessfully', this.onApplicationLaunchedSuccessfully, this);
this.agent.addListener('clipBoardData', this.onClipBoardData, this);
this.busConnection.addListener('busMessage', this.onBusMessage, this);
this.busConnection.addListener('error', this.onDisconnect, this);
this.timeLapseDetector.addListener('timeLapseDetected', this.onTimeLapseDetected, this);
},
onChannelConnected: function(params) {
var channel = params;
if (channel === wdi.SpiceVars.SPICE_CHANNEL_INPUTS) {
this.clientGui.releaseAllKeys();
}
},
onNetworkDriveResponse: function(params) {
this.executeExternalCallback('networkDriveResponse', params);
},
onDisconnect: function (params) {
var error = params;
this.executeExternalCallback('error', error);
},
onResolution: function (params) {
this.executeExternalCallback('resolution', params);
},
onClipBoardData: function (params) {
if (this.externalClipoardHandling) {
this.executeExternalCallback('clipboardEvent', params);
} else {
this.clientGui.setClipBoardData(params);
}
},
onWindowMinimized: function (params) {
var window = params;
var params = this.clientGui.resizeSubCanvas(window);
this.executeExternalCallback('windowMinimized', params);
},
onWindowFocused: function (params) {
this.executeExternalCallback('windowFocused', params);
},
onWindowRestored: function (params) {
var window = params;
var params = this.clientGui.resizeSubCanvas(window);
this.executeExternalCallback('windowRestored', params);
},
onWindowMaximized: function (params) {
var window = params;
var params = this.clientGui.resizeSubCanvas(window);
this.executeExternalCallback('windowMaximized', params);
},
onWindowResized: function (params) {
var window = params;
var params = this.clientGui.resizeSubCanvas(window);
this.executeExternalCallback('windowResized', params);
},
onWindowMoved: function (params) {
var window = params;
var params = this.clientGui.moveSubCanvas(window);
this.executeExternalCallback('windowMoved', params);
},
onWindowClosed: function (params) {
var window = params;
var params = this.clientGui.deleteSubCanvas(window);
this.executeExternalCallback('windowClosed', params);
},
onWindowCreated: function (params) {
var window = params;
var params = this.clientGui.createNewSubCanvas(window);
this.executeExternalCallback('windowCreated', params);
},
onMenuResponse: function(params) {
var menuData = params;
this.executeExternalCallback('menuResponse', menuData);
},
//Events
onClientInput: function (params) {
var data = params;
var type = data[0];
this.inputProcess.send(data, type);
},
onMessage: function (params) {
var message = params;
this.packetProcess.process(message);
var self = this;
window.checkResultsTimer && clearTimeout(window.checkResultsTimer);
window.checkResultsTimer = window.setTimeout(function () {
self.executeExternalCallback('checkResults');
window.vdiLoadTest = false;
}, 5000);
},
onBusConnected: function(params) {
if (wdi.SeamlessIntegration) {
this.busProcess.requestWindowList(); //request windows list
}
},
onBusMessage: function (params) {
var message = params;
this.busProcess.process(message);
},
onInitAgent: function (params) {
this.agent.setClientTokens(params);
this.agent.sendInitMessage(this);
this.executeExternalCallback('ready', params);
// this.clientGui.releaseAllKeys();
},
onMouseMode: function (params) {
this.clientGui.setMouseMode(params);
},
onPaste: function (params) {
this.agent.setClipboard(params);
},
onStartAudio: function () {
this.packetProcess.processors[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK].startAudio();
},
onTimeLapseDetected: function (params) {
var elapsedMillis = params;
this.executeExternalCallback('timeLapseDetected', elapsedMillis);
},
connect: function (connectionInfo) {
try {
this.spiceConnection.connect(connectionInfo);
} catch (e) {
this.clientGui.showError(e.message);
}
},
setExternalCallback: function (fn, context) {
this.externalCallback = [fn, context];
},
executeExternalCallback: function (action, params) {
this.externalCallback[0].call(this.externalCallback[1], action, params);
},
sendCommand: function (action, params) {
switch (action) {
case "close":
this.busProcess.closeWindow(params['hwnd']);
break;
case "move":
this.busProcess.moveWindow(params['hwnd'], params['x'], params['y']);
break;
case "minimize":
this.busProcess.minimizeWindow(params['hwnd']);
break;
case "maximize":
this.busProcess.maximizeWindow(params['hwnd']);
break;
case "restore":
this.busProcess.restoreWindow(params['hwnd']);
break;
case "focus":
this.busProcess.focusWindow(params['hwnd']);
break;
case "resize":
this.busProcess.resizeWindow(params['hwnd'], params['width'], params['height']);
break;
case "run":
this.busProcess.executeCommand(params['cmd']);
break;
case "setResolution":
this.agent.setResolution(params['width'], params['height']);
break;
case 'getMenu':
this.busProcess.getMenu();
break;
case 'reMountNetworkDrive':
this.busProcess.reMountNetworkDrive(params['host'], params['username'], params['password']);
break;
}
},
enableKeyboard: function () {
this.clientGui.enableKeyboard();
},
disableKeyboard: function () {
this.clientGui.disableKeyboard();
},
enableCtrlV: function () {
wdi.KeymapES.setCtrlKey(86, 0x2F);
wdi.KeymapUS.setCtrlKey(86, 0x2F);
},
disconnect: function() {
this.busConnection.disconnect();
this.spiceConnection.disconnect();
},
setMultimediaTime: function (time) {
this.multimediaTime = time;
this.lastMultimediaTime = Date.now();
},
sendShortcut: function(shortcut) {
if(shortcut == wdi.keyShortcutsHandled.CTRLV) {
this.inputProcess.send([
"keydown",
[
{
'generated': true,
'type': "keydown",
'keyCode': 17,
'charCode': 0
}
]
], "keydown"); //ctrl down
this.inputProcess.send([
"keydown",
[
{
'generated': true,
'type': "keydown",
'keyCode': 86,
'charCode': 0
}
]
], "keydown"); //v
this.inputProcess.send([
"keyup",
[
{
'generated': true,
'type': "keyup",
'keyCode': 86,
'charCode': 0
}
]
], "keyup"); //v up
this.inputProcess.send([
"keyup",
[
{
'generated': true,
'type': "keyup",
'keyCode': 17,
'charCode': 0
}
]
], "keyup"); //ctrl up
}
},
dispose: function () {
this.disableKeyboard();
this.disconnect();
this.packetProcess.dispose();
},
onWrongPathError: function (params) {
this.executeExternalCallback('wrongPathError', params);
},
onApplicationLaunchedSuccessfully: function (params) {
this.executeExternalCallback('applicationLaunchedSuccessfully', params);
},
getKeyboardHandler: function() {
return this.clientGui.handleKey;
},
getClientGui: function() {
return this.clientGui;
},
setCurrentWindow: function(wnd) {
this.clientGui.inputManager.setCurrentWindow(wnd);
}
});
window['Application'] = Application;
Application.prototype['run'] = Application.prototype.run;
Application.prototype['sendCommand'] = Application.prototype.sendCommand;
Application.prototype['enableKeyboard'] = Application.prototype.enableKeyboard;
Application.prototype['disableKeyboard'] = Application.prototype.disableKeyboard;
Application.prototype['dispose'] = Application.prototype.dispose;
Application.prototype['getKeyboardHandler'] = Application.prototype.getKeyboardHandler;
Application.prototype['getClientGui'] = Application.prototype.getClientGui;
Application.prototype['setCurrentWindow'] = Application.prototype.setCurrentWindow;

View File

@ -0,0 +1,639 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SPICE_INPUT_MOTION_ACK_BUNCH = 8;
wdi.ClientGui = $.spcExtend(wdi.EventObject.prototype, {
width: null,
height: null,
canvas: null,
ack_wait: 0,
mouse_mode: 0,
mouse_status: 0,
eventLayer: null,
counter: 0,
mainCanvas: 0,
firstTime: true,
clientOffsetX: 0,
clientOffsetY: 0,
magnifier: null,
magnifierBackground: null,
firstMove: true,
isMagnified: true,
isMouseDown: false,
soundStarted: false,
canvasMarginY: 0,
canvasMarginX: 0,
stuckKeysHandler: null,
subCanvas: {},
inputManager: null,
clipboardEnabled: true,
layer: null,
init: function(c) {
this.canvas = {};
this.contexts = {};
this.superInit();
this.magnifier = window.$('<canvas/>').attr({
'width': 150,
'height': 150
}).css({
'position': 'absolute',
'left': '0px',
'top': '0px'
});
this.stuckKeysHandler = c.stuckKeysHandler || new wdi.StuckKeysHandler();
this.stuckKeysHandler.addListener('inputStuck', this._sendInput.bind(this), this);
//load magnifier background
this.magnifierBackground = window.$('<img/>');
this.magnifierBackground.attr('src', 'resources/magnifier.png');
this.initSound();
this.inputManager = c.inputManager || new wdi.InputManager({ stuckKeysHandler: this.stuckKeysHandler, window: $(window)});
this.inputManager.setCurrentWindow(window);
},
setLayer: function(layer) {
this.layer = layer;
},
disableClipboard: function () {
this.clipboardEnabled = false;
},
_sendInput: function (params) {
var data = params;
var type = data[0];
var event = data[1];
this.fire('input', [type, event]);
},
releaseAllKeys: function() {
this.stuckKeysHandler.releaseAllKeys();
},
getContext: function(surface_id) {
return this.contexts[surface_id];
},
getCanvas: function(surface_id) {
return this.canvas[surface_id];
},
checkFeatures: function() {
if (!Modernizr.canvas || !Modernizr.websockets) {
alert('Your Browser is not compatible with WDI. Visit ... for a list of compatible browsers');
return false;
}
return true;
},
deleteSubCanvas: function(window) {
var obj = this.subCanvas[window['hwnd']];
this.subCanvas[window['hwnd']] = null;
return obj;
},
moveSubCanvas: function(window) {
var obj = this.subCanvas[window['hwnd']];
obj['info'] = window;
this._fillSubCanvasFromWindow(window);
return obj;
},
resizeSubCanvas: function(window) {
var obj = this.subCanvas[window['hwnd']];
$([obj["canvas"], obj["eventLayer"]]).attr({
'width': window['width'],
'height': window['height']
});
obj['info'] = window;
this._fillSubCanvasFromWindow(window);
return obj;
},
_fillSubCanvasFromWindow: function(window) {
var top = parseInt(window.top, 10);
var left = parseInt(window.left, 10);
var width = parseInt(window.width, 10);
var height = parseInt(window.height, 10);
this.fillSubCanvas({
top: top,
left: left,
right: left + width,
bottom: top + height
});
},
createNewSubCanvas: function(window) {
var evtlayer = this.createEventLayer(window['hwnd'] + '_event', window['width'], window['height']);
this.subCanvas[window['hwnd']] = {
'canvas': $('<canvas/>').attr({
width: window['width'],
height: window['height']
}).css({
display: window['iconic'] ? 'none' : 'block'
})[0],
'eventLayer': evtlayer,
'info': window,
'position': 0
};
//we have the main area drawn?
if (this.canvas[this.mainCanvas]) {
this._fillSubCanvasFromWindow(window);
}
return [this.subCanvas[window['hwnd']]];
},
fillSubCanvas: function(filterPosition) {
var canvas = this.canvas[this.mainCanvas];
var info = null;
for (var i in this.subCanvas) {
if (this.subCanvas[i] != null && this.subCanvas[i] !== undefined && this.subCanvas.hasOwnProperty(i)) {
info = this.subCanvas[i]['info'];
if(filterPosition!= null || filterPosition != undefined) {
var top = parseInt(info['top'], 10);
var left = parseInt(info['left'], 10);
var width = parseInt(info['width'], 10);
var height = parseInt(info['height'], 10);
var position = {
top: top,
left: left,
right: left + width,
bottom: top + height
};
if (wdi.CollisionDetector.thereIsBoxCollision(position, filterPosition)) {
this._doDrawSubCanvas(canvas, this.subCanvas[i], info);
}
} else {
this._doDrawSubCanvas(canvas, this.subCanvas[i], info);
}
}
}
},
_doDrawSubCanvas: function(canvas, subCanvas, info) {
if(this.canvas[this.mainCanvas] == null || this.canvas[this.mainCanvas] == undefined) {
return;
}
var destCtx = null;
if (info['iconic'] === 0) {
var destCanvas = subCanvas['canvas'];
destCtx = destCanvas.getContext("2d");
var x = 0;
var y = 0;
var width = +info.width;
var height = +info.height;
var left = +info['left'];
var top = +info['top'];
if (left < 0) {
width = width + left;
x = -left;
left = 0;
}
if (top < 0) {
height = height + top;
y = -top;
top = 0;
}
try {
// if width or height are less than 1 or a float
// drawImage fails in firefox (ERROR: IndexSizeError)
width = Math.max(1, Math.floor(width));
height = Math.max(1, Math.floor(height));
if (width > canvas.width) width = canvas.width;
if (height > canvas.height) height = canvas.height;
destCtx.drawImage(canvas, left, top, width, height, x, y, width, height);
} catch (err) {
console.log(err)
}
}
},
removeCanvas: function(spiceMessage) {
var surface = spiceMessage.args;
if (surface.surface_id === this.mainCanvas) {
$(this.eventLayer).remove();
this.eventLayer = null;
}
this.canvas[surface.surface_id].keepAlive = false;
delete this.canvas[surface.surface_id];
delete this.contexts[surface.surface_id];
},
drawCanvas: function(spiceMessage) {
var surface = spiceMessage.args;
var cnv = wdi.GlobalPool.create('Canvas');
cnv.keepAlive = true; //prevent this canvas to return to the pool by packetfilter
cnv.id = 'canvas_' + surface.surface_id;
cnv.width = surface.width;
cnv.height = surface.height;
cnv.style.position = 'absolute';
cnv.style.top = this.canvasMarginY + 'px';
cnv.style.left = this.canvasMarginX + 'px';
this.canvas[surface.surface_id] = cnv;
this.contexts[surface.surface_id] = cnv.getContext('2d');
if (surface.flags && !wdi.SeamlessIntegration) {
this.mainCanvas = surface.surface_id;
this.eventLayer = this.createEventLayer('eventLayer', surface.width, surface.height);
var evLayer = $(this.eventLayer).css({
position: 'absolute',
top: this.canvasMarginY + 'px',
left: this.canvasMarginX + 'px'
})[0];
if(this.layer) {
this.layer.appendChild(cnv);
this.layer.appendChild(evLayer);
} else {
document.body.appendChild(cnv);
document.body.appendChild(evLayer);
}
this.enableKeyboard();
}
//this goes here?
if (this.firstTime && this.clipboardEnabled) {
var self = this;
$(document).bind('paste', function(event) {
self.fire('paste', event.originalEvent.clipboardData.getData('text/plain'));
});
this.firstTime = false;
}
//notify about resolution
if (surface.flags) {
this.fire('resolution', [this.canvas[surface.surface_id].width, this.canvas[surface.surface_id].height]);
}
},
disableKeyboard: function() {
var documentDOM = window.$(window.document);
documentDOM.unbind('keydown', this.handleKey);
documentDOM.unbind('keyup', this.handleKey);
documentDOM.unbind('keypress', this.handleKey);
this.inputManager.disable();
},
enableKeyboard: function() {
var self = this,
documentDOM = window.$(window.document);
documentDOM['keydown']([self], this.handleKey);
documentDOM['keypress']([self], this.handleKey);
documentDOM['keyup']([self], this.handleKey);
this.inputManager.enable();
},
setCanvasMargin: function(canvasMargin) {
this.canvasMarginX = canvasMargin.x;
this.canvasMarginY = canvasMargin.y;
},
createEventLayer: function(event_id, width, height) {
var self = this;
var eventLayer = $('<canvas/>').css({
cursor: 'default',
position: 'absolute'
}).attr({
id: event_id,
width: width,
height: height
});
if (window['bowser']['firefox']) {
eventLayer.attr('contentEditable', true);
}
eventLayer.bind('touchstart', function(event) {
event.preventDefault();
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
var x = touch.pageX;
var y = touch.pageY;
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY, self.mouse_status]);
if (event.originalEvent.touches.length === 1) {
self.enabledTouchMove = true;
self.launchRightClick.call(self, x, y);
} else if (event.originalEvent.touches.length === 2) {
self.touchX = x;
self.touchY = y;
self.enabledTouchMove = false;
} else if (event.originalEvent.touches.length === 3) {
self.touchY3 = y;
self.enabledTouchMove = false;
}
});
eventLayer.bind('touchmove', function(event) {
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
var x = touch.pageX;
var y = touch.pageY;
//TODO: ignore first move
if (event.originalEvent.touches.length === 1 && self.enabledTouchMove) {
self.isMagnified = true; //magnified!
clearInterval(self.rightClickTimer); //cancel, this is not a right click
if (!self.isMouseDown) {
clearInterval(self.mouseDownTimer); //cancel, not enough time to send mousedown
self.launchMouseDown(); //fire again
}
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY - 80, self.mouse_status]);
var pos = $(this).offset();
var myX = x - pos.left;
var myY = y - pos.top;
//draw magnifier
if (self.firstMove) {
$('body').append(self.magnifier);//TODO: append to body?
self.firstMove = false;
}
var posX = myX - 75;
var posY = myY - 160;
self.magnifier.css({
'left': posX,
'top': posY
});
//fill magnifier
var ctx = self.magnifier[0].getContext('2d');
ctx.clearRect(0, 0, 150, 150);
ctx.save();
ctx.beginPath();
ctx.arc(75, 75, 75, 0, 2 * Math.PI, false);
ctx.clip();
ctx.drawImage(
self.getCanvas(0),
myX - 50, //-50 because we are going to get
myY - 50 - 80, //100 px and we want the finder to be the center
//-80 becasue the magnifier is 160px up (160/2)
//we need to clean all this after the demo
//is working
100,
100,
0,
0,
150,
150
);
// //draw the background
ctx.drawImage(self.magnifierBackground[0], 0, 0);
ctx.restore();
//empty magnifier
} else if (event.originalEvent.touches.length === 2) {
var delta = self.touchY - y;
if (Math.abs(delta) > 10) {
var button = delta > 0 ? 4 : 3;
self.touchX = x;
self.touchY = y;
self.generateEvent.call(self, 'mousedown', button);
self.generateEvent.call(self, 'mouseup', button);
}
} else if (event.originalEvent.touches.length === 3) {
var delta = self.touchY3 - y;
if (delta > 100) {
document.getElementById('hiddeninput').select();
}
}
event.preventDefault();
});
eventLayer.bind('touchend', function(event) {
if (self.enabledTouchMove) {
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
var x = touch.pageX;
var y = touch.pageY;
if (!self.isMouseDown) {
self.generateEvent.call(self, 'mousedown', 0);
}
self.isMouseDown = false;
self.generateEvent.call(self, 'mouseup', 0);
var pos = $(this).offset();
self.enabledTouchMove = false;
self.firstMove = true;
if (self.isMagnified) {
self.magnifier.remove();
}
self.isMagnified = false;
}
clearInterval(self.rightClickTimer); //cancel, this is not a right click
clearInterval(self.mouseDownTimer); //cancel
});
//if (!Modernizr.touch) {
eventLayer['mouseup'](function(event) {
var button = event.button;
self.generateEvent.call(self, 'mouseup', button);
self.mouse_status = 0;
event.preventDefault();
});
eventLayer['mousedown'](function(event) {
var button = event.button;
self.generateEvent.call(self, 'mousedown', button);
self.mouse_status = 1;
event.preventDefault();
});
eventLayer['mousemove'](function(event) {
var x = event.pageX;
var y = event.pageY;
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY, self.mouse_status]);
event.preventDefault();
});
eventLayer.bind('contextmenu', function(event) {
event.preventDefault();
return false;
});
//}
var mouseEventPause = false;
eventLayer.bind('mousewheel', function(event, delta) {
var button = delta > 0 ? 3 : 4;
self.generateEvent.call(self, 'mousedown', button);
self.generateEvent.call(self, 'mouseup', button);
return false;
});
wdi.VirtualMouse.setEventLayer(eventLayer[0], 0, 0, width, height, 1);
return eventLayer[0];
},
launchRightClick: function(x, y) {
var self = this;
this.rightClickTimer = setTimeout(function() {
self.generateEvent.call(self, 'mousedown', 2);
self.generateEvent.call(self, 'mouseup', 2);
self.enabledTouchMove = false;
}, 400);
},
launchMouseDown: function(x, y) {
var self = this;
this.mouseDownTimer = setTimeout(function() {
self.isMouseDown = true;
self.generateEvent.call(self, 'mousedown', 0);
}, 1500);
},
showError: function(message) {
wdi.Debug.warn(message);
$('<div/>', {
id: 'error'
}).html(message).css({
'background-color': '#ff4141'
}).appendTo('body');
setTimeout("$('#error').remove()", 2000);
},
generateEvent: function(event, params) {
if (event === 'mousemove' || event === 'joystick') {
if (this.ack_wait < wdi.SPICE_INPUT_MOTION_ACK_BUNCH) {
this.ack_wait++;
this.fire('input', [event, params]);
}
} else {
if (event.indexOf('key') > -1) { // it's a keyEvent
this.stuckKeysHandler.checkSpecialKey(event, params[0]['keyCode']);
var val = this.inputManager.getValue();
if (val) {
params = this.inputManager.manageChar(val, params);
}
}
this.fire('input', [event, params]);
}
},
motion_ack: function() {
this.ack_wait = 0;
},
setMouseMode: function(mode) {
this.mouse_mode = mode;
},
handleKey: function(e) {
e.data[0].generateEvent.call(e.data[0], e.type, [e]);
if (wdi.Keymap.isInKeymap(e.keyCode) && e.type !== "keypress") {
e.preventDefault();
}
//e.data[0].stuckKeysHandler.handleStuckKeys(e);
},
setClientOffset: function(x, y) {
this.clientOffsetX = x;
this.clientOffsetY = y;
},
setClipBoardData: function(data) {
//we have received new clipboard data
//show to the user
//TODO: create a new dialog with buttons to copy the data directly
//from the textbox
prompt("New clipboard data available, press ctrl+c to copy it", data);
},
initSound: function() {
var self = this;
// if (!Modernizr.touch) {
this.soundStarted = true;
window.setTimeout(function() {
self.fire('startAudio');
}, 100);
/* } else {
var $button = $('<button>Start</button>', {id: "startAudio"}).css({
padding: "10px 25px",
fontSize: "25px",
fontFamily: "Verdana",
cursor: "pointer",
margin: "0 auto"
}).click(function() {
self.soundStarted = true;
self.fire('startAudio');
$('#soundButtonContainer').remove();
});
var $messageContainer = $('<div id="messageContainer"><p>Click to start using your virtual session:</p></div>').css({
color: "white",
textAlign: "center",
fontSize: "25px",
fontFamily: "Verdana",
marginTop: "75px"
});
var $container = $('<div></div>', {id: "soundButtonContainer"});
$button.appendTo($messageContainer);
$messageContainer.appendTo($container);
$container.appendTo('body');
$container.css({
position: 'absolute',
zIndex: 999999999,
top: 0,
left: 0,
width: "100%",
height: document.height,
backgroundColor: "black"
});
}*/
}
});

View File

@ -0,0 +1,87 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.ImageCache = {
images: {},
cursor: {},
palettes: {},
getImageFrom: function(descriptor, cb) {
//see http://jsperf.com/todataurl-vs-getimagedata-to-base64/7
var cnv = wdi.GlobalPool.create('Canvas');
var imgData = this.images[descriptor.id.toString()];
cnv.width = imgData.width;
cnv.height = imgData.height;
cnv.getContext('2d').putImageData(imgData,0,0);
cb(cnv);
},
isImageInCache: function(descriptor) {
if(descriptor.id.toString() in this.images) {
return true;
}
return false;
},
delImage: function(id) {
delete this.images[id.toString()];
},
addImage: function(descriptor, canvas) {
if(canvas.getContext) {
this.images[descriptor.id.toString()] = canvas.getContext('2d').getImageData(0,0,canvas.width, canvas.height);
} else {
this.images[descriptor.id.toString()] = canvas;
}
},
getCursorFrom: function(cursor) {
return this.cursor[cursor.header.unique.toString()];
},
addCursor: function(cursor, imageData) {
this.cursor[cursor.header.unique.toString()] = imageData;
},
getPalette: function(id) {
return this.palettes[id.toString()];
},
addPalette: function(id, palette) {
this.palettes[id.toString()] = palette;
},
clearPalettes: function() {
this.palettes = {};
}
};

View File

@ -0,0 +1,121 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.InputManager = $.spcExtend(wdi.EventObject.prototype, {
checkFocus: false,
input: null,
window: null,
stuckKeysHandler: null,
init: function (c) {
this.superInit();
this.input = c.input;
this.window = c.window;
this.stuckKeysHandler = c.stuckKeysHandler;
this.$ = c.jQuery || $;
if (!c.disableInput) {
this.inputElement = this.$('<div style="position:absolute"><input type="text" id="inputmanager" style="opacity:0;color:transparent"/></div>');
}
this.currentWindow = null;
},
setCurrentWindow: function(wnd) {
wnd = this.$(wnd);
if(this.currentWindow) {
this.inputElement.remove();
//remove listeners
this.currentWindow.unbind('blur');
}
this.$(wnd[0].document.body).prepend(this.inputElement);
this.input = this.$(wnd[0].document.getElementById('inputmanager'));
//TODO: remove events from the other window
this.addListeners(wnd);
this.currentWindow = wnd;
},
addListeners: function (wnd) {
this._onBlur(wnd);
this._onInput();
},
_onBlur: function (wnd) {
var self = this;
wnd.on('blur', function onBlur (e) {
if (self.checkFocus) {
self.input.focus();
}
self.stuckKeysHandler.releaseSpecialKeysPressed();
});
},
_onInput: function () {
var self = this;
this.input.on('input', function input (e) {
// ctrl-v issue related
var aux = self.input.val();
if (aux.length > 1) {
self.reset();
}
});
},
enable: function () {
this.checkFocus = true;
this.input.select();
},
disable: function () {
this.checkFocus = false;
this.input.blur();
},
reset: function () {
this.input.val("");
},
getValue: function () {
var val = this.input.val();
if (val) {
this.reset();
}
return val;
},
manageChar: function (val, params) {
var res = [Object.create(params[0])];
res[0]['type'] = 'inputmanager';
res[0]['charCode'] = val.charCodeAt(0);
return res;
}
});

View File

@ -0,0 +1,190 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketFactory = {
extract: function(rawSpiceMessage) {
var packet = null;
switch (rawSpiceMessage.channel) {
case wdi.SpiceVars.SPICE_CHANNEL_DISPLAY:
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
var originalData = JSON.stringify(rawSpiceMessage);
}
switch (rawSpiceMessage.header.type) {
case wdi.SpiceVars.SPICE_MSG_DISPLAY_MODE:
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK:
packet = new wdi.SpiceDisplayMark().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET:
packet = new wdi.SpiceDisplayReset().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS:
packet = new wdi.SpiceCopyBits().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST:
packet = new wdi.SpiceResourceList().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS:
//TODO: remove all pixmaps
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_PALETTE:
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES:
packet = new wdi.SpiceDisplayInvalidAllPalettes().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE:
packet = new wdi.SpiceStreamCreate().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA:
packet = new wdi.SpiceStreamData().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP:
packet = new wdi.SpiceStreamClip().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY:
packet = new wdi.SpiceStreamDestroy().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL:
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
packet = new wdi.SpiceDrawFill().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_OPAQUE:
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
// Spice Draw Copy is composed by DisplayBase (surface_id 32, SpiceRect(top 32, left 32, bottom 32, right 32), SpiceClip(type 8 if 1: SpiceClipRects(num_rects 32, vector: SpiceRect(top 32, left 32, bottom 32, right 32)))) and SpiceCopy (offset 32 if not 0: SpiceImage(SpiceImageDescriptor(id 32, type 8, flags 8, width 32, height 32), case descriptor type to parse image), SpiceRect(top 32, left 32, bottom 32, right 32), rop_descriptor 16, scale_mode 8, SpiceQMask)
packet = new wdi.SpiceDrawCopy().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
packet = new wdi.drawBlend().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS:
packet = new wdi.SpiceDrawBlackness().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS:
packet = new wdi.SpiceDrawWhiteness().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS:
packet = new wdi.SpiceDrawInvers().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3:
packet = new wdi.SpiceDrawRop3().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE:
packet = new wdi.SpiceStroke().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT:
packet = new wdi.SpiceDrawText().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
packet = new wdi.drawTransparent().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
packet = new wdi.drawAlphaBlend().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE:
packet = new wdi.SpiceSurface().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY:
packet = new wdi.SpiceSurfaceDestroy().demarshall(rawSpiceMessage.body);
break;
}
break;
case wdi.SpiceVars.SPICE_CHANNEL_INPUTS:
switch (rawSpiceMessage.header.type) {
case wdi.SpiceVars.SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
packet = new Object(); //dummy!
break;
}
break;
case wdi.SpiceVars.SPICE_CHANNEL_MAIN:
switch (rawSpiceMessage.header.type) {
case wdi.SpiceVars.SPICE_MSG_MAIN_INIT:
packet = new wdi.RedMainInit().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DATA:
packet = new wdi.VDAgentMessage().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DISCONNECTED:
packet = new wdi.SpiceMsgMainAgentDisconnected().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_CONNECTED:
packet = new wdi.SpiceMsgMainAgentConnected().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
packet = new wdi.MainMultiMediaTime().demarshall(rawSpiceMessage.body);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_CHANNELS_LIST:
packet = new wdi.MainMChannelsList().demarshall(rawSpiceMessage.body);
break;
}
break;
case wdi.SpiceVars.SPICE_CHANNEL_CURSOR:
switch (rawSpiceMessage.header.type) {
case wdi.SpiceVars.SPICE_MSG_CURSOR_INIT:
packet = new wdi.RedCursorInit().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
case wdi.SpiceVars.SPICE_MSG_CURSOR_SET:
packet = new wdi.RedCursorSet().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
}
break;
case wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK:
switch(rawSpiceMessage.header.type) {
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_MODE:
packet = new wdi.PlaybackMode().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_START:
packet = new wdi.PlaybackStart().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_STOP:
packet = new wdi.PlaybackStop().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_DATA:
packet = new wdi.PlaybackData().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
break;
}
}
if(packet) {
if (wdi.graphicDebug && wdi.graphicDebug.debugMode && originalData) {
packet.originalData = originalData;
}
return new wdi.SpiceMessage({
messageType: rawSpiceMessage.header.type,
channel: rawSpiceMessage.channel,
args: packet
});
}
wdi.Debug.log(rawSpiceMessage.header.type, rawSpiceMessage.header.channel);
return false;
}
};

View File

@ -0,0 +1,110 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketFilter = {
restoreContext: false,
start: null,
filter: function(spiceMessage, fn, scope, clientGui) {
if(wdi.logOperations) {
this.start = Date.now();
}
//TODO: design an architecture for loading
//dynamic filters, instead of filtering here.
//This should be just the entry point for filters.
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
wdi.graphicDebug.printDebugMessageOnFilter(spiceMessage, clientGui);
}
//end of hardcoded filter
// MS Word Benchmark startup
if (wdi.IntegrationBenchmark && wdi.IntegrationBenchmark.benchmarking) {
var date = new Date();
wdi.IntegrationBenchmark.setStartTime(date.getTime());
}
//check clipping
if(spiceMessage.args.base) {
if(spiceMessage.args.base.clip.type === wdi.SpiceClipType.SPICE_CLIP_TYPE_RECTS) {
var context = clientGui.getContext(spiceMessage.args.base.surface_id);
context.save();
context.beginPath();
var rects = spiceMessage.args.base.clip.rects.rects;
var len = rects.length;
while(len--) {
var box = wdi.graphics.getBoxFromSrcArea(rects[len]);
context.rect(box.x, box.y, box.width, box.height);
}
context.clip();
this.restoreContext = spiceMessage.args.base.surface_id;
}
}
fn.call(scope, spiceMessage);
},
notifyEnd: function(spiceMessage, clientGui) {
if(this.restoreContext !== false) {
var context = clientGui.getContext(this.restoreContext);
context.restore();
this.restoreContext = false;
}
if(wdi.SeamlessIntegration) {
var filterPosition = null;
if(spiceMessage.args.base && spiceMessage.args.base.box) {
filterPosition = spiceMessage.args.base.box;
}
clientGui.fillSubCanvas(filterPosition);
}
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
wdi.graphicDebug.printDebugMessageOnNotifyEnd(spiceMessage, clientGui);
}
// MS Word Benchmark
if (wdi.IntegrationBenchmark && wdi.IntegrationBenchmark.benchmarking) {
var date = new Date();
wdi.IntegrationBenchmark.setEndTime(date.getTime());
}
// clear the tmpcanvas
wdi.GlobalPool.cleanPool('Canvas');
wdi.GlobalPool.cleanPool('Image');
if(wdi.logOperations) {
wdi.DataLogger.log(spiceMessage, this.start);
}
}
}

View File

@ -0,0 +1,80 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketProcess = $.spcExtend(wdi.DomainObject, {
processors: {},
init: function(c) {
this.processors[wdi.SpiceVars.SPICE_CHANNEL_MAIN] = c.mainProcess || new wdi.MainProcess({
app: c.app
});
this.processors[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY] = c.displayProcess || new wdi.DisplayPreProcess({
clientGui: c.clientGui
});
this.processors[wdi.SpiceVars.SPICE_CHANNEL_INPUTS] = c.inputsProcess || new wdi.InputProcess({
clientGui: c.clientGui,
spiceConnection: c.spiceConnection
});
this.processors[wdi.SpiceVars.SPICE_CHANNEL_CURSOR] = c.cursorProcess || new wdi.CursorProcess();
this.processors[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK] = c.playbackProcess || new wdi.PlaybackProcess({
app: c.app
});
},
process: function(spiceMessage) {
if(wdi.exceptionHandling) {
return this.processExceptionHandled(spiceMessage);
} else {
return this.processPacket(spiceMessage);
}
},
processExceptionHandled: function(spiceMessage) {
try {
return this.processPacket(spiceMessage);
} catch(e) {
wdi.Debug.error('PacketProcess: Error processing packet', e);
}
},
processPacket: function(spiceMessage) {
if(!spiceMessage || !this.processors[spiceMessage.channel]) {
throw "Invalid channel or null message";
}
this.processors[spiceMessage.channel].process(spiceMessage);
},
dispose: function () {
this.processors[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY].dispose();
}
});

View File

@ -0,0 +1,129 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.RasterOperation = {
process: function(rop, sourceImg, destImg) {//sourceImg could be brush or image (both imageData)
var result = null;
if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_SRC) {
sourceImg = this.invert(sourceImg);
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_BRUSH) {
sourceImg = this.invert(sourceImg);
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_DEST) {
destImg = this.invert(destImg);
}
if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
return sourceImg;
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_OR) {
result = this.boolOp(sourceImg, destImg, 'or');
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_AND) {
result = this.boolOp(sourceImg, destImg, 'and');
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_XOR) {
result = this.boolOp(sourceImg, destImg, 'xor');
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_BLACKNESS) {
result = this.lightness(destImg, 'b');
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_WHITENESS) {
result = this.lightness(destImg);
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_INVERS) {
result = this.invert(destImg);
}
if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_RES) {
return this.invert(result);
} else {
return result;
}
},
flip: function(sourceImg) {
sourceImg = wdi.Flipper.flip(sourceImg);
return sourceImg;
},
invert: function(sourceImg) {
sourceImg = $(sourceImg).pixastic('invert')[0];
return sourceImg;
},
lightness: function(sourceImg, ratio) {
var ratio = ratio==='b'?-100:100;
sourceImg = $(sourceImg).pixastic('hsl', {hue:30,saturation:20,lightness:ratio})[0];
return sourceImg;
},
boolOp: function(sourceImg, destImg, op) {
//or and and xor implemented without globalcomposition
//because it is really buggy
var source = wdi.graphics.getDataFromImage(sourceImg).data;
var dest = wdi.graphics.getDataFromImage(destImg).data;
var length = source.length-1;
var tmp_canvas = wdi.graphics.getNewTmpCanvas(sourceImg.width, sourceImg.height);
var tmp_context = tmp_canvas.getContext('2d');
var resultImageData = tmp_context.createImageData(sourceImg.width, sourceImg.height);
var result = resultImageData.data;
if(op === 'or') {
while(length > 0) {
resultImageData.data[length] = 255;
result[length-1] = source[length-1] | dest[length-1];
result[length-2] = source[length-2] | dest[length-2];
result[length-3] = source[length-3] | dest[length-3];
length-=4;
}
} else if(op === 'and') {
while(length > 0) {
resultImageData.data[length] = 255;
result[length-1] = source[length-1] & dest[length-1];
result[length-2] = source[length-2] & dest[length-2];
result[length-3] = source[length-3] & dest[length-3];
length-=4;
}
} else if(op === 'xor') {
while(length > 0) {
resultImageData.data[length] = 255;
result[length-1] = source[length-1] ^ dest[length-1];
result[length-2] = source[length-2] ^ dest[length-2];
result[length-3] = source[length-3] ^ dest[length-3];
length-=4;
}
}
tmp_context.putImageData(resultImageData, 0, 0);
return tmp_canvas;
}
};

View File

@ -0,0 +1,200 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SpiceConnection = $.spcExtend(wdi.EventObject.prototype, {
channels:null,
connectionId: null,
connectionInfo: null,
runQ: null,
token: null,
connectionControl: null,
init: function(c) {
this.superInit();
this.channels = {};
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN] = c.mainChannel || new wdi.SpiceChannel();
this.channels[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY] = c.displayChannel || new wdi.SpiceChannel();
this.channels[wdi.SpiceVars.SPICE_CHANNEL_INPUTS] = c.inputsChannel || new wdi.SpiceChannel();
this.channels[wdi.SpiceVars.SPICE_CHANNEL_CURSOR] = c.cursorChannel || new wdi.SpiceChannel();
this.channels[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK] = c.playbackChannel || new wdi.SpiceChannel();
this.runQ = c.runQ || new wdi.RunQueue();
this.connectionControl = c.connectionControl || new wdi.ConnectionControl();
this.setup();
},
connect: function(connectionInfo) {
this.connectionInfo = connectionInfo;
if (connectionInfo.connectionControl) {
this.connectionControl.connect(connectionInfo);
}
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].connect(this.connectionInfo, wdi.SpiceVars.SPICE_CHANNEL_MAIN);
},
disconnect: function() {
for (var i in this.channels) {
this.channels[i].disconnect();
this.channels[i] = null;
delete(this.channels[i]);
}
this.connectionControl.disconnect();
},
send: function(spcMessage) {
var data = spcMessage.args.marshall();
if(this.channels[spcMessage.channel]) {
this.channels[spcMessage.channel].sendObject(
data,
spcMessage.messageType
);
} else {
console.error("channel not available", spcMessage.channel);
}
},
//set events to all channels
setup: function() {
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('connectionId', this.onConnectionId, this);
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('channelListAvailable', this.onChannelList, this);
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('mouseMode', this.onMouseMode, this);
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('initAgent', this.onInitAgent, this);
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('notify', this.onNotify, this);
this.connectionControl.addListener('connectionLost', this.onDisconnect, this);
this._setConnectedListeners();
var f = null;
if(wdi.exceptionHandling) {
f = this.onChannelMessageExceptionHandled;
} else {
f = this.processChannelMessage;
}
for(var i in this.channels) {
if(this.channels.hasOwnProperty(i)) {
this.channels[i].addListener('message', f, this);
this.channels[i].addListener('status', this.onStatus, this);
this.channels[i].addListener('error', this.onDisconnect, this);
}
}
},
_setConnectedListeners: function() {
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_MAIN);
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_DISPLAY);
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_INPUTS);
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_CURSOR);
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK);
},
_setConnectedListener: function(channel) {
this.channels[channel].addListener('channelConnected', function () {
this.fire('channelConnected', channel);
}, this);
},
onDisconnect: function(params) {
this.fire("error", params);
},
//events
onConnectionId: function(params) {
this.connectionId = params;
},
onChannelList: function(params) {
this.connectChannels(params);
},
connectChannels: function(channels) {
for(var i in this.channels) {
i = parseInt(i, 10);
if(i != wdi.SpiceVars.SPICE_CHANNEL_MAIN && channels.indexOf(i) != -1) {
this.runQ.add(function(proxy, params) {
this.channels[params].connect(this.connectionInfo, params, this.connectionId, proxy);
}, this, false, i);
}
}
this.runQ.process();
},
onInitAgent: function(params) {
var tokens = params;
this.fire('initAgent', tokens);
},
onMouseMode: function(params) {
var mode = params;
this.fire('mouseMode', mode);
},
onNotify: function(params) {
this.fire('notify');
},
onStatus: function(params) {
/*var status = params[1];
var channel = params[2];
if (status == wdi.CHANNEL_STATUS.idle) {
var self = this;
this.channels[channel].timer = setTimeout(function() {
self.channels[channel].connect(self.host, self.port, channel, self.connectionId);
}, 800);
} else if (status == wdi.CHANNEL_STATUS.establishing) {
clearTimeout(this.channels[channel].timer);
}*/
},
onChannelMessageExceptionHandled: function(params) {
try {
return this.processChannelMessage(params);
} catch(e) {
wdi.Debug.error('SpiceConnection: Packet decodification error', e);
}
},
processChannelMessage: function(params) {
var packet = wdi.PacketFactory.extract(params); //returns domain object
//return ViewQueue to the pool, object is already decoded
wdi.GlobalPool.discard('ViewQueue', params.body);
wdi.GlobalPool.discard('RawSpiceMessage', params);
if(packet) {
this.fire('message', packet);
} else {
wdi.Debug.log('Unknown packet '+params.channel+' '+params.header.type);
wdi.Debug.log(params);
}
}
});

View File

@ -0,0 +1,51 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.Stream = {
streams: {},
addStream: function(id, stream) {
this.streams[id] = stream;
},
deleteStream: function(id) {
this.streams[id] = undefined;
},
getStream: function(id) {
return this.streams[id];
},
clip: function(id, clip) {
this.streams[id].clip = clip;
}
}

View File

@ -0,0 +1,95 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.VirtualMouse = {
eventLayers: [],
mouseData:null,
visible: null,
lastLayer: null,
hotspot: {
x: 0,
y: 0
},
lastMousePosition: {
x: 0,
y: 0,
width: 0,
height: 0
},
setHotspot: function(x, y) {
this.hotspot.x = x;
this.hotspot.y = y;
},
setEventLayer: function(ev, x, y, width, height, position) {
this.eventLayers.push({
layer: ev,
left: x,
top: y,
right: x+width,
bottom: y+height,
position: position
});
},
removeEventLayer: function(ev) {
var len = this.eventLayers.length;
for(var i=0;i<len;i++) {
if(this.eventLayers[i].layer.id === ev.id) {
this.eventLayers[ev.id] = undefined;
}
}
},
getEventLayer: function(x, y) {
var len = this.eventLayers.length;
var layer = null;
for(var i=0;i<len;i++) {
layer = this.eventLayers[i];
if(x >= layer.left && x <= layer.right && y >= layer.top && y <= layer.bottom) {
return layer.layer;
}
}
},
setMouse: function(mouseData, x, y) {
//if(!Modernizr.touch) {
var layer = null;
var len = this.eventLayers.length;
for(var i=0;i<len;i++) {
layer = this.eventLayers[i];
layer.layer.style.cursor = 'url('+mouseData+') ' + x + ' ' + y + ', default';
}
//}
}
}

View File

@ -0,0 +1,117 @@
<!DOCTYPE html>
<!--
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
-->
<html>
<head>
<title></title>
<script type="text/javascript" src="application/WorkerProcess.js"></script>
<script type="text/javascript" src="lib/jquery-2.0.3.js"></script>
<script type="text/javascript" src="lib/base64.js"></script>
<script>
function start() {
//load benchmark data
// $.get('recorded/quic_2658778_1366x854', function(data) {
$.get('recorded/lz_rgba_3616376_1920x901', function(data) {
var width = 1920;
var height = 901;
var decoded = Base64.decode(data);
var arr = new ArrayBuffer(decoded.length);
var u8 = new Uint8Array(arr);
u8.set(decoded);
var result = dispatch(arr, false);
var tmpCanvas = $("<canvas/>")[0];
tmpCanvas.width = width;
tmpCanvas.height = height;
var imgData = tmpCanvas.getContext('2d').createImageData(width, height);
var arrResult = new Uint8Array(result);
imgData.data.set(arrResult);
var info = document.createTextNode('width: '+width+ ' height: '+height);
$('body').append(info);
$('body').append($('<br/>'));
tmpCanvas.getContext('2d').putImageData(imgData, 0, 0, 0, 0, width, height);
$('body').append(tmpCanvas);
$('body').append($('<br/>'));
//create benchmark button!
var btn = $("<input/>").attr({
'type': 'button',
'value': 'start'
});
btn.click(function() {
var start = null;
var end = null;
var max = 0;
var min = 9999999999999999;
var used = null;
var sum = 0;
var loops = 250;
for(var i=0;i<loops;i++) {
start = Date.now();
result = dispatch(arr, false);
end = Date.now();
used = end-start;
sum = sum+(used);
if(used > max) {
max = used;
}
if(used < min) {
min = used;
}
}
console.log('min: ', min);
console.log('max: ', max);
console.log('mean: ', sum/loops);
$('body').append($('<br/>'));
$('body').append(document.createTextNode('min: '+min+ ' max: '+max+ ' mean: '+sum/loops));
});
$('body').append(btn);
});
}
$(document).ready(start);
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
set -u
set -x
# it would be good to not run the build if:
#
# * there are no changes in the source files used in the concatenator.js file
# * there haven't been any changes in this repo
#
# since the last build done by jenkins. To achieve this you might get the date
# of the last jenkins commit in this repo, and do git log --since 'that date'
# and check if there have been any commit or not.
cd "$(dirname "$0")"
echo "Generating spiceproxy.js"
spiceproxy/concatenator.js spice-web-client

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<!--
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
-->
<html>
<head>
<title>eyeOS Spice Web Client</title>
<meta charset="utf-8">
<!-- libs -->
<script src="lib/modernizr.js"></script>
<script src="lib/jquery-2.0.3.js"></script>
<script src="lib/jquery-mousewheel.js"></script>
<script src="lib/jgestures.min.js"></script>
<script src="lib/pixastic.js"></script>
<script src="lib/base64.js"></script>
<script src="lib/biginteger.js"></script>
<script src="lib/virtualjoystick.js"></script>
<script src="lib/prettyprint.js"></script>
<!-- ticketing -->
<script src="lib/jsbn.js"></script>
<script src="lib/jsbn2.js"></script>
<script src="lib/prng4.js"></script>
<script src="lib/rng.js"></script>
<script src="lib/sha1.js"></script>
<script src="lib/encrypt.js"></script>
<!-- end libs -->
<!-- core -->
<script src="swcanvas/swcanvas.js"></script>
<script src="lib/bowser.js"></script>
<script src="lib/utils.js"></script>
<script src="lib/flipper.js"></script>
<script src="lib/CollisionDetector.js"></script>
<script src="lib/GlobalPool.js"></script>
<script src="lib/GenericObjectPool.js"></script>
<script src="lib/AsyncConsumer.js"></script>
<script src="lib/AsyncWorker.js"></script>
<script src="lib/PacketWorkerIdentifier.js"></script>
<script src="spiceobjects/spiceobjects.js"></script>
<script src="spiceobjects/generated/protocol.js"></script>
<script src="lib/graphicdebug.js"></script>
<script src="lib/images/lz.js"></script>
<script src="lib/images/bitmap.js"></script>
<script src="lib/images/png.js"></script>
<script src="lib/runqueue.js"></script>
<script src="lib/graphic.js"></script>
<script src="lib/queue.js"></script>
<script src="lib/ImageUncompressor.js"></script>
<script src="lib/SyncAsyncHandler.js"></script>
<script src="lib/IntegrationBenchmark.js"></script>
<script src="lib/stuckkeyshandler.js"></script>
<script src="lib/timelapsedetector.js"></script>
<script src="lib/displayRouter.js"></script>
<script src="lib/rasterEngine.js"></script>
<script src="lib/DataLogger.js"></script>
<script src="network/socket.js"></script>
<script src="network/clusternodechooser.js"></script>
<script src="network/socketqueue.js"></script>
<script src="network/packetcontroller.js"></script>
<script src="network/packetextractor.js"></script>
<script src="network/packetreassembler.js"></script>
<script src="network/reassemblerfactory.js"></script>
<script src="network/sizedefiner.js"></script>
<script src="network/packetlinkfactory.js"></script>
<script src="network/spicechannel.js"></script>
<script src="network/busconnection.js"></script>
<script src="network/websocketwrapper.js"></script>
<script src="network/connectioncontrol.js"></script>
<script src="application/agent.js"></script>
<script src="application/spiceconnection.js"></script>
<script src="application/clientgui.js"></script>
<script src="application/packetprocess.js"></script>
<script src="application/packetfilter.js"></script>
<script src="application/packetfactory.js"></script>
<script src="application/application.js"></script>
<script src="application/virtualmouse.js"></script>
<script src="application/imagecache.js"></script>
<script src="application/rasteroperation.js"></script>
<script src="application/stream.js"></script>
<script src="application/inputmanager.js"></script>
<script src="process/busprocess.js"></script>
<script src="process/displayprocess.js"></script>
<script src="process/displaypreprocess.js"></script>
<script src="process/inputprocess.js"></script>
<script src="process/cursorprocess.js"></script>
<script src="process/playbackprocess.js"></script>
<script src="process/mainprocess.js"></script>
<script src="keymaps/keymapes.js"></script>
<script src="keymaps/keymapit.js"></script>
<script src="keymaps/keymapus.js"></script>
<script src="keymaps/keymap.js"></script>
<script src="application/WorkerProcess.js"></script>
<script src="run.js"></script>
<!-- end core -->
<meta content="yes" name="apple-mobile-web-app-capable" />
<style type="text/css">
body {
background-color:black;
padding:0;
margin:0;
}
</style>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,129 @@
wdi.keyShortcutsHandled = {
CTRLV: 0
}
wdi.Keymap = {
keymap: {},
ctrlKeymap: {},
charmap: {},
ctrlPressed: false,
twoBytesScanCodes: [0x5B, 0xDB, /*0x38, 0xB8,*/ 0x5C, 0xDC, 0x1D, 0x9D, 0x5D, 0xDD, 0x52, 0xD2, 0x53, 0xD3, 0x4B, 0xCB, 0x47, 0xC9, 0x4F, 0xCF, 0x48, 0xC8, 0x50, 0xD0, 0x49, 0xC9, 0x51, 0xD1, 0x4D, 0xCD, 0x1C, 0x9C],
loadKeyMap: function(layout) {
try {
this.keymap = wdi['Keymap' + layout.toUpperCase()].getKeymap();
this.ctrlKeymap = wdi['Keymap' + layout.toUpperCase()].getCtrlKeymap();
this.reservedCtrlKeymap = wdi['Keymap' + layout.toUpperCase()].getReservedCtrlKeymap();
this.charmap = wdi['Keymap' + layout.toUpperCase()].getCharmap();
} catch(e) {
this.keymap = wdi.KeymapES.getKeymap();
this.ctrlKeymap = wdi.KeymapES.getCtrlKeymap();
this.reservedCtrlKeymap = wdi.KeymapES.getReservedCtrlKeymap();
this.charmap = wdi.KeymapES.getCharmap();
}
},
isInKeymap: function(keycode) {
if (this.keymap[keycode] === undefined) return false;
else return true;
},
/**
* Returns the associated spice key code from the given browser keyboard event
* @param e
* @returns {*}
*/
getScanCodes: function(e) {
if (e['hasScanCode']) {
return e['scanCode'];
} else if (this.handledByCtrlKeyCode(e['type'], e['keyCode'], e['generated'])) {// before doing anything else we check if the event about to be handled has to be intercepted
return this.getScanCodeFromKeyCode(e['keyCode'], e['type'], this.ctrlKeymap, this.reservedCtrlKeymap);
} else if (this.handledByCharmap(e['type'])) {
return this.getScanCodesFromCharCode(e['charCode']);
} else if (this.handledByNormalKeyCode(e['type'], e['keyCode'])) {
return this.getScanCodeFromKeyCode(e['keyCode'], e['type'], this.keymap);
} else {
return [];
}
},
getScanCodeFromKeyCode: function(keyCode, type, keymap, additionalKeymap) {
this.controlPressed(keyCode, type);
var key = null;
if(keyCode in keymap) {
key = keymap[keyCode];
} else {
key = additionalKeymap[keyCode];
}
if (key === undefined) return [];
if (key < 0x100) {
if (type == 'keydown') {
return [this.makeKeymap(key)];
} else if (type == 'keyup') {
return [this.makeKeymap(key | 0x80)];
}
} else {
if (type == 'keydown') {
return [this.makeKeymap(0xe0 | ((key - 0x100) << 8))];
} else if (type == 'keyup') {
return [this.makeKeymap(0x80e0 | ((key - 0x100) << 8))];
}
}
return key;
},
controlPressed: function(keyCode, type) {
if (keyCode === 17 || keyCode === 91) { // Ctrl or CMD key
if (type === 'keydown') this.ctrlPressed = true;
else if (type === 'keyup') this.ctrlPressed = false;
}
},
handledByCtrlKeyCode: function(type, keyCode, generated) {
if (type === 'keydown' || type === 'keyup' || type === 'keypress') {
if (this.ctrlPressed) {
if (type === 'keypress') {
return true;
}
if (this.ctrlKeymap[keyCode]) {
return true; // is the second key in a keyboard shortcut (i.e. the x in Ctrl+x)
}
//check if the event is a fake event generated from our gui or programatically
if(generated && this.reservedCtrlKeymap[keyCode]) {
return true;
}
}
}
return false;
},
handledByNormalKeyCode: function(type, keyCode) {
if (type === 'keydown' || type === 'keyup') {
if (this.keymap[keyCode]) {
return true;
}
}
return false;
},
handledByCharmap: function(type) {
if (type === 'inputmanager') return true;
else return false;
},
getScanCodesFromCharCode: function(charCode) {
var scanCode = this.charmap[String.fromCharCode(charCode)];
if (scanCode === undefined) scanCode = [];
return scanCode;
},
makeKeymap: function(scancode) {
if ($.inArray(scancode, this.twoBytesScanCodes) != -1) {
return [0xE0, scancode, 0, 0];
} else {
return [scancode, 0, 0];
}
}
}

View File

@ -0,0 +1,251 @@
// These tables map the js keyboard keys to the spice equivalent
wdi.KeymapES = function() {
// regular keys with associated chars. The columns means all the event flux to activate the key (i.e. [key up, key down])
// all the js events associated to these keys should have a charKey associated
var charmapES = {};
charmapES['º'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
charmapES['ª'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['\\'] = [[0xE0, 0x38, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['1'] = [[0x2, 0, 0, 0], [0x82, 0, 0, 0]];
charmapES['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['|'] = [[0xE0, 0x38, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
charmapES['"'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['@'] = [[0xE0, 0x38, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
charmapES['·'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['#'] = [[0xE0, 0x38, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
charmapES['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['~'] = [[0xE0, 0x38, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
charmapES['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
charmapES['&'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['¬'] = [[0xE0, 0x38, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
charmapES['/'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
charmapES['('] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
charmapES[')'] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
charmapES['='] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['\''] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
charmapES['?'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['¡'] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
charmapES['¿'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
charmapES['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
charmapES['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['€'] = [[0xE0, 0x38, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
charmapES['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
charmapES['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
charmapES['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
charmapES['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['`'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
charmapES['à'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['À'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['è'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['È'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['ì'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['Ì'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['ò'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['Ò'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['ù'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['Ù'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['â'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['Â'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['ê'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['Ê'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['î'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['Î'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['ô'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['Ô'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['û'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['Û'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['^'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
charmapES['['] = [[0xE0, 0x38, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['+'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
charmapES['*'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES[']'] = [[0xE0, 0x38, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
charmapES['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
charmapES['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
charmapES['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
charmapES['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
charmapES['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
charmapES['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
charmapES['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
charmapES['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['ñ'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
charmapES['Ñ'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['á'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['Á'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['é'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['É'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['í'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['Í'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['ó'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['Ó'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['ú'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['Ú'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['ä'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['Ä'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapES['ë'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['Ë'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapES['ï'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['Ï'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapES['ö'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['Ö'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapES['ü'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['Ü'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapES['{'] = [[0xE0, 0x38, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['ç'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
charmapES['Ç'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['}'] = [[0xE0, 0x38, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapES['<'] = [[0x56, 0, 0, 0], [0xD6, 0, 0, 0]];
charmapES['>'] = [[0x2A, 0, 0, 0], [0x56, 0, 0, 0], [0xD6, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
charmapES['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
charmapES['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
charmapES['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
charmapES['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
charmapES['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
charmapES['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
charmapES['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
charmapES[';'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
charmapES[':'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES['-'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
charmapES['_'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapES[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
// keyboard keys without character associated.
// all the js events associated to these keys should have a keyChar associated
var keymapES = [];
keymapES[27] = 0x1; // ESC
keymapES[9] = 0x0F; // TAB
//keymapES[20] = 0x3A; // BLOQ.MAY. => see the charmap, all the capital letters and shift chars send a shift in their sequence
keymapES[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
keymapES[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL
keymapES[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
//keymapES[32] = 0x39; // SPACE => see the charmap
keymapES[8] = 0x0E; // BACKSPACE
keymapES[13] = 0x1C; // ENTER
//keymapES[225] = 0x38; // RIGHT ALT (ALT GR) => see the charmap, all the altgr chars send a altgr in their sequence
keymapES[18] = 0x38; // LEFT ALT
// keymapES[92] = 0x5C; // RIGHT GUI (WINDOWS)
keymapES[38] = 0x48; // UP ARROW
keymapES[37] = 0x4B; // LEFT ARROW
keymapES[40] = 0x50; // DOWN ARROW
keymapES[39] = 0x4D; // RIGHT ARROW
keymapES[45] = 0x52; // INSERT
keymapES[46] = 0x53; // DELETE
keymapES[36] = 0x47; // HOME
keymapES[35] = 0x4F; // FIN
keymapES[33] = 0x49; // PAGE UP
keymapES[34] = 0x51; // PAGE UP
keymapES[144] = 0x45; // BLOQ.NUM.
keymapES[145] = 0x46; // SCROLL LOCK
keymapES[112] = 0x3B; // F1
keymapES[113] = 0x3C; // F2
keymapES[114] = 0x3D; // F3
keymapES[115] = 0x3E; // F4
keymapES[116] = 0x3F; // F5
keymapES[117] = 0x40; // F6
keymapES[118] = 0x41; // F7
keymapES[119] = 0x42; // F8
keymapES[120] = 0x43; // F9
keymapES[121] = 0x44; // F10
keymapES[122] = 0x57; // F11
keymapES[123] = 0x58; // F12
// combination keys with ctrl
var ctrlKeymapES = [];
ctrlKeymapES[65] = 0x1E; // a
ctrlKeymapES[81] = 0x10; // q
ctrlKeymapES[87] = 0x11; // w
ctrlKeymapES[69] = 0x12; // e
ctrlKeymapES[82] = 0x13; // r
ctrlKeymapES[84] = 0x14; // t
ctrlKeymapES[89] = 0x15; // y
ctrlKeymapES[85] = 0x16; // u
ctrlKeymapES[73] = 0x17; // i
ctrlKeymapES[79] = 0x18; // o
ctrlKeymapES[80] = 0x19; // p
ctrlKeymapES[65] = 0x1E; // a
ctrlKeymapES[83] = 0x1F; // s
ctrlKeymapES[68] = 0x20; // d
ctrlKeymapES[70] = 0x21; // f
ctrlKeymapES[71] = 0x22; // g
ctrlKeymapES[72] = 0x23; // h
ctrlKeymapES[74] = 0x24; // j
ctrlKeymapES[75] = 0x25; // k
ctrlKeymapES[76] = 0x26; // l
ctrlKeymapES[90] = 0x2C; // z
ctrlKeymapES[88] = 0x2D; // x
ctrlKeymapES[67] = 0x2E; // c
//ctrlKeymapES[86] = 0x2F; // v to enable set disableClipboard = true in run.js
ctrlKeymapES[66] = 0x30; // b
ctrlKeymapES[78] = 0x31; // n
ctrlKeymapES[77] = 0x32; // m
// reserved ctrl+? combinations we want to intercept from browser and inject manually to spice
var reservedCtrlKeymap = [];
reservedCtrlKeymap[86] = 0x2F;
return {
getKeymap: function() {
return keymapES;
},
getCtrlKeymap: function() {
return ctrlKeymapES;
},
getReservedCtrlKeymap: function() {
return reservedCtrlKeymap;
},
getCharmap: function() {
return charmapES;
},
setCtrlKey: function (key, val) {
ctrlKeymapES[key] = val;
}
};
}( );

View File

@ -0,0 +1,215 @@
// These tables map the js keyboard keys to the spice equivalent
wdi.KeymapIT = function() {
// regular keys with associated chars. The columns means all the event flux to activate the key (i.e. [key up, key down])
// all the js events associated to these keys should have a charKey associated
var charmapIT = {};
charmapIT['\\'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
charmapIT['|'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['1'] = [[0x2, 0, 0, 0], [0x82, 0, 0, 0]];
charmapIT['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
charmapIT['"'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
charmapIT['£'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
charmapIT['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
charmapIT['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
charmapIT['&'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
charmapIT['/'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
charmapIT['('] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
charmapIT[')'] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
charmapIT['='] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['\''] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
charmapIT['?'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['`'] = [[0xE0, 0x38, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['ì'] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
charmapIT['^'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['~'] = [[0xE0, 0x38, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
charmapIT['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
charmapIT['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapIT['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['€'] = [[0xE0, 0x38, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
charmapIT['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
charmapIT['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
charmapIT['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapIT['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapIT['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapIT['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
charmapIT['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['è'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0]];
charmapIT['é'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['['] = [[0xE0, 0x38, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['{'] = [[0xE0, 0x38, 0, 0], [0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['+'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
charmapIT['*'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT[']'] = [[0xE0, 0x38, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['}'] = [[0xE0, 0x38, 0, 0], [0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapIT['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
charmapIT['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
charmapIT['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
charmapIT['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
charmapIT['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
charmapIT['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
charmapIT['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
charmapIT['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
charmapIT['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['ò'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
charmapIT['ç'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['@'] = [[0xE0, 0x38, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['à'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0]];
charmapIT['°'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['#'] = [[0xE0, 0x38, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
charmapIT['ù'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
charmapIT['§'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['<'] = [[0x56, 0, 0, 0], [0xD6, 0, 0, 0]];
charmapIT['>'] = [[0x2A, 0, 0, 0], [0x56, 0, 0, 0], [0xD6, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
charmapIT['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
charmapIT['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
charmapIT['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
charmapIT['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
charmapIT['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
charmapIT['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
charmapIT['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
charmapIT[';'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
charmapIT[':'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT['-'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
charmapIT['_'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapIT[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
// keyboard keys without character associated.
// all the js events associated to these keys should have a keyChar associated
var keymapIT = [];
keymapIT[27] = 0x1; // ESC
keymapIT[9] = 0x0F; // TAB
//keymapIT[20] = 0x3A; // CAPS LOCK => see the charmap, all the capital letters and shift chars send a shift in their sequence
keymapIT[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
keymapIT[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL (why? 0x5B)
keymapIT[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
//keymapIT[32] = 0x39; // SPACE => see the charmap
keymapIT[8] = 0x0E; // BACKSPACE
keymapIT[12] = 0x4C; // KP_BEGIN (showkey -s: 0x4C, 0xCC)
keymapIT[13] = 0x1C; // ENTER
//keymapIT[225] = 0x38; // RIGHT ALT (ALT GR) => see the charmap, all the altgr chars send a altgr in their sequence
keymapIT[18] = 0x38; // LEFT ALT
//keymapIT[19] = 0x??; // PAUSE (showkey -s: 0xE1 0x1D 0x45, 0xE1 0x9D 0xC5)
//keymapIT[92] = 0x5C; // RIGHT GUI (WINDOWS) (I get 91 for the right too)
keymapIT[93] = 0x5D; // MENU
keymapIT[38] = 0x48; // UP ARROW
keymapIT[37] = 0x4B; // LEFT ARROW
keymapIT[40] = 0x50; // DOWN ARROW
keymapIT[39] = 0x4D; // RIGHT ARROW
//keymapIT[42] = 0x??; // PRINT (showkey -s: 0xE0 0x2A 0xE0 0x37, 0xE0 0xAA 0xE0 0xB7)
keymapIT[45] = 0x52; // INSERT
keymapIT[46] = 0x53; // DELETE
keymapIT[36] = 0x47; // HOME
keymapIT[35] = 0x4F; // END
keymapIT[33] = 0x49; // PAGE UP
keymapIT[34] = 0x51; // PAGE DOWN
keymapIT[144] = 0x45; // NUM LOCK
keymapIT[145] = 0x46; // SCROLL LOCK
keymapIT[112] = 0x3B; // F1
keymapIT[113] = 0x3C; // F2
keymapIT[114] = 0x3D; // F3
keymapIT[115] = 0x3E; // F4
keymapIT[116] = 0x3F; // F5
keymapIT[117] = 0x40; // F6
keymapIT[118] = 0x41; // F7
keymapIT[119] = 0x42; // F8
keymapIT[120] = 0x43; // F9
keymapIT[121] = 0x44; // F10
keymapIT[122] = 0x57; // F11
keymapIT[123] = 0x58; // F12
// combination keys with ctrl
var ctrlKeymapIT = [];
ctrlKeymapIT[65] = 0x1E; // a
ctrlKeymapIT[81] = 0x10; // q
ctrlKeymapIT[87] = 0x11; // w
ctrlKeymapIT[69] = 0x12; // e
ctrlKeymapIT[82] = 0x13; // r
ctrlKeymapIT[84] = 0x14; // t
ctrlKeymapIT[89] = 0x15; // y
ctrlKeymapIT[85] = 0x16; // u
ctrlKeymapIT[73] = 0x17; // i
ctrlKeymapIT[79] = 0x18; // o
ctrlKeymapIT[80] = 0x19; // p
ctrlKeymapIT[65] = 0x1E; // a
ctrlKeymapIT[83] = 0x1F; // s
ctrlKeymapIT[68] = 0x20; // d
ctrlKeymapIT[70] = 0x21; // f
ctrlKeymapIT[71] = 0x22; // g
ctrlKeymapIT[72] = 0x23; // h
ctrlKeymapIT[74] = 0x24; // j
ctrlKeymapIT[75] = 0x25; // k
ctrlKeymapIT[76] = 0x26; // l
ctrlKeymapIT[90] = 0x2C; // z
ctrlKeymapIT[88] = 0x2D; // x
ctrlKeymapIT[67] = 0x2E; // c
//ctrlKeymapIT[86] = 0x2F; // v to enable set disableClipboard = true in run.js
ctrlKeymapIT[66] = 0x30; // b
ctrlKeymapIT[78] = 0x31; // n
ctrlKeymapIT[77] = 0x32; // m
// reserved ctrl+? combinations we want to intercept from browser and inject manually to spice
var reservedCtrlKeymap = [];
reservedCtrlKeymap[86] = 0x2F;
return {
getKeymap: function() {
return keymapIT;
},
getCtrlKeymap: function() {
return ctrlKeymapIT;
},
getReservedCtrlKeymap: function() {
return reservedCtrlKeymap;
},
getCharmap: function() {
return charmapIT;
},
setCtrlKey: function (key, val) {
ctrlKeymapIT[key] = val;
}
};
}( );

View File

@ -0,0 +1,193 @@
wdi.KeymapUS = function() {
var charmapUS = [];
charmapUS['`'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
charmapUS['~'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['1'] = [[0x2, 0, 0, 0],[0x82, 0, 0, 0]];
charmapUS['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
charmapUS['@'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
charmapUS['#'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
charmapUS['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
charmapUS['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
charmapUS['^'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
charmapUS['&'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
charmapUS['*'] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
charmapUS['('] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
charmapUS[')'] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['-'] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
charmapUS['_'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['='] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
charmapUS['+'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
charmapUS['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
charmapUS['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
charmapUS['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
charmapUS['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
charmapUS['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
charmapUS['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
charmapUS['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
charmapUS['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
charmapUS['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
charmapUS['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['['] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0]];
charmapUS['{'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS[']'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
charmapUS['}'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['\\'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
charmapUS['|'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
charmapUS['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
charmapUS['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
charmapUS['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
charmapUS['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
charmapUS['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
charmapUS['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
charmapUS['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
charmapUS['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
charmapUS['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS[';'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
charmapUS[':'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['\''] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0]];
charmapUS['"'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
charmapUS['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
charmapUS['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
charmapUS['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
charmapUS['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
charmapUS['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
charmapUS['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
charmapUS['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
charmapUS['<'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
charmapUS['>'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS['/'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
charmapUS['?'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
charmapUS[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
var keymapUS = [];
keymapUS[27] = 0x1; // ESC
keymapUS[9] = 0x0F; // TAB
//keymapUS[20] = 0x3A; // // BLOQ.MAY. => see the charmap, all the capital letters and shift chars send a shift in their sequence
keymapUS[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
keymapUS[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL
keymapUS[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
keymapUS[32] = 0x39; // SPACE
keymapUS[8] = 0x0E; // BACKSPACE
keymapUS[13] = 0x1C; // ENTER
//keymapUS[0] = 0x38; // RIGHT ALT (ALT GR)
//keymapUS[92] = 0x5C; // RIGHT GUI (WINDOWS)
keymapUS[38] = 0x48; // UP ARROW
keymapUS[37] = 0x4B; // LEFT ARROW
keymapUS[40] = 0x50; // DOWN ARROW
keymapUS[39] = 0x4D; // RIGHT ARROW
keymapUS[45] = 0x52; // INSERT
keymapUS[46] = 0x53; // DELETE
keymapUS[36] = 0x47; // HOME
keymapUS[35] = 0x4F; // FIN
keymapUS[33] = 0x49; // PAGE UP
keymapUS[34] = 0x51; // PAGE UP
keymapUS[144] = 0x45; // BLOQ.NUM.
keymapUS[145] = 0x46; // SCROLL LOCK
keymapUS[112] = 0x3B; // F1
keymapUS[113] = 0x3C; // F2
keymapUS[114] = 0x3D; // F3
keymapUS[115] = 0x3E; // F4
keymapUS[116] = 0x3F; // F5
keymapUS[117] = 0x40; // F6
keymapUS[118] = 0x41; // F7
keymapUS[119] = 0x42; // F8
keymapUS[120] = 0x43; // F9
keymapUS[121] = 0x44; // F10
keymapUS[122] = 0x57; // F11
keymapUS[123] = 0x58; // F12
var ctrlkeymapUS = [];
ctrlkeymapUS[65] = 0x1E; // a
ctrlkeymapUS[81] = 0x10; // q
ctrlkeymapUS[87] = 0x11; // w
ctrlkeymapUS[69] = 0x12; // e
ctrlkeymapUS[82] = 0x13; // r
ctrlkeymapUS[84] = 0x14; // t
ctrlkeymapUS[89] = 0x15; // y
ctrlkeymapUS[85] = 0x16; // u
ctrlkeymapUS[73] = 0x17; // i
ctrlkeymapUS[79] = 0x18; // o
ctrlkeymapUS[80] = 0x19; // p
ctrlkeymapUS[65] = 0x1E; // a
ctrlkeymapUS[83] = 0x1F; // s
ctrlkeymapUS[68] = 0x20; // d
ctrlkeymapUS[70] = 0x21; // f
ctrlkeymapUS[71] = 0x22; // g
ctrlkeymapUS[72] = 0x23; // h
ctrlkeymapUS[74] = 0x24; // j
ctrlkeymapUS[75] = 0x25; // k
ctrlkeymapUS[76] = 0x26; // l
ctrlkeymapUS[90] = 0x2C; // z
ctrlkeymapUS[88] = 0x2D; // x
ctrlkeymapUS[67] = 0x2E; // c
//ctrlkeymapUS[86] = 0x2F; // v to enable set disableClipboard = true in run.js
ctrlkeymapUS[66] = 0x30; // b
ctrlkeymapUS[78] = 0x31; // n
ctrlkeymapUS[77] = 0x32; // m
var reservedCtrlKeymap = [];
reservedCtrlKeymap[86] = 0x2F; //v
return {
getKeymap: function() {
return keymapUS;
},
getCtrlKeymap: function() {
return ctrlkeymapUS;
},
getReservedCtrlKeymap: function() {
return reservedCtrlKeymap;
},
getCharmap: function() {
return charmapUS;
},
setCtrlKey: function (key, val) {
ctrlkeymapUS[key] = val;
}
};
}( );

View File

@ -0,0 +1,206 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.AsyncConsumer = $.spcExtend(wdi.EventObject.prototype, {
worker: null,
task: null,
packetWorkerIdentifier: null,
imageProperties: null,
init: function(c) {
this.superInit();
this.worker = c.AsyncWorker || new wdi.AsyncWorker({script:'application/WorkerProcess.js'});
this.packetWorkerIdentifier = c.packetWorkerIdentifier || new wdi.PacketWorkerIdentifier();
},
consume: function(task) {
this.task = task; //store current task
var message = task.message;
var imageProperties;
//check if the packet is a type of packet that should be intercepted
//this doesn't mean it contains a compressed image, it means that it COULD
var intercept = this.packetWorkerIdentifier.shouldUseWorker(message);
if(intercept == wdi.PacketWorkerIdentifier.processingType.DECOMPRESS) {
//get image properties to check if there is really a compressed image
imageProperties = this.packetWorkerIdentifier.getImageProperties(message);
this.imageProperties = imageProperties;
//compressed images are quic and lz
if(imageProperties && (imageProperties.descriptor.type != wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB &&
imageProperties.descriptor.type != wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC)) {
intercept = 0;
} else if(!imageProperties) {
intercept = 0;
}
}
//the packet is not going to be intercepted by the worker thread.
//mark as procssed.
if(intercept === 0) {
this.taskDone();
return;
}
var data;
var descriptor;
var opaque;
var brush;
var ret;
var arr;
var u8;
if(intercept == wdi.PacketWorkerIdentifier.processingType.DECOMPRESS) {
data = imageProperties.data;
descriptor = imageProperties.descriptor;
opaque = imageProperties.opaque;
brush = imageProperties.brush;
if(descriptor.type === wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB) {
var header = null;
if(!brush) { //brushes are still js arrays
var headerData = data.subarray(0,32).toJSArray();
data = data.subarray(32); //skip the header
header = wdi.LZSS.demarshall_rgb(headerData);
} else {
header = wdi.LZSS.demarshall_rgb(data);
}
arr = new ArrayBuffer(data.length+16);
u8 = new Uint8Array(arr);
u8[0] = 1; //LZ_RGB
u8[1] = opaque;
u8[2] = header.type;
u8[3] = header.top_down; //RESERVED
var number = header.width * header.height * 4;
for (var i = 0;i < 4;i++) {//iterations because of javascript number size
u8[4+i] = number & (255);//Get only the last byte
number = number >> 8;//Remove the last byte
}
var view = new DataView(arr);
view.setUint32(8, header.width);
view.setUint32(12, header.height);
u8.set(data, 16);
//intercept
//var encoded = encodeURIComponent(Base64.encode(u8.toJSArray()));
//$.post('record.php','data='+encoded+'&name=lz_rgba_'+encoded.length+'_'+descriptor.width+'x'+descriptor.height);
this.worker.run(arr, this._workerCompleted, {type: 'lz',top_down: header.top_down, opaque: opaque}, this);
} else if(descriptor.type === wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC) {
var adata = new ArrayBuffer(data.length+4);
var view = new Uint8Array(adata);
view.set(data, 4);
view[1] = opaque?1:0;
view[0] = 0; //quic
//intercept
/*
var jsarray = new Uint8Array(adata);
var encoded = encodeURIComponent(Base64.encode(jsarray.toJSArray()));
var dateat = Date.now() /1000;
$.post('record.php','data='+encoded+'&name=quic_'+encoded.length+'_'+descriptor.width+'x'+descriptor.height);
*/
this.worker.run(adata, this._workerCompleted, {type: 'quic'}, this);
}
} else if(intercept == wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO) {
data = this.packetWorkerIdentifier.getVideoData(message);
arr = new ArrayBuffer(data.length+4);
u8 = new Uint8Array(arr);
u8[0] = 2; //2 means bytestouri
u8[1] = 0;
u8[2] = 0;
u8[3] = 0; //reserved
u8.set(data, 4);
this.worker.run(arr, function(buf, params) {
message.args.data = buf;
this.taskDone();
}, null, this);
}
},
//executed from webworker when processing is finished
_workerCompleted: function(buf, options) {
if(!buf) {
this.taskDone();
return;
}
var descriptor = this.imageProperties.descriptor;
var u8 = new Uint8ClampedArray(buf);
var source_img = new ImageData(u8, descriptor.width, descriptor.height);
//it is strange, but we can't use pooling on the getimagefromdata
//the second argument (optional) tell getimagefromdata to avoid pooling
var myImage = source_img;
if(options.type === 'lz') {
var top_down = options.top_down;
var opaque = options.opaque;
if(!top_down && !opaque) {
myImage = wdi.graphics.getImageFromData(source_img, true);
myImage = wdi.RasterOperation.flip(myImage);
}
}
descriptor.originalType = descriptor.type;
descriptor.type = wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS;
//replace data
if(this.task.message.messageType === wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL) {
this.task.message.args.brush.pattern.imageData = myImage;
this.task.message.args.brush.pattern.image.type = wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS;
} else {
this.task.message.args.image.data = myImage;
}
this.taskDone();
},
taskDone: function() {
this.task.state = 1;
this.fire('done', this);
},
dispose: function () {
this.worker.dispose();
}
});

View File

@ -0,0 +1,63 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.AsyncWorker = $.spcExtend(wdi.EventObject.prototype, {
worker: null,
fn: null,
scope: null,
params: null,
init: function(c) {
this.superInit();
this.worker = new Worker(c.script);
var self = this;
this.worker.addEventListener("message", function (oEvent) {
self.fn.call(self.scope, oEvent.data, self.params);
});
},
run: function(data, fn, params, scope) {
this.fn = fn;
this.scope = scope;
this.params = params;
if (wdi.postMessageW3CCompilant) {
this.worker.postMessage(data, [data]);
} else {
this.worker.postMessage(data);
}
},
dispose: function () {
this.worker.terminate();
}
});

View File

@ -0,0 +1,40 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.CollisionDetector = {
thereIsBoxCollision: function(baseBox, queueBox) {
if(baseBox.bottom < queueBox.top) return false;
if(baseBox.top > queueBox.bottom) return false;
if(baseBox.right < queueBox.left) return false;
return baseBox.left < queueBox.right;
}
};

View File

@ -0,0 +1,208 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.DataLogger = {
testStartTime: 0,
testStopTime: 0,
networkStart:0,
networkTotalTime: 0,
data: {},
routeList: {},
imageTypes: {},
startTimes: [],
init: function() {
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE] = 'drawCanvas';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY] = 'removeCanvas';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY] = 'drawImage';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL] = 'drawFill';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND] = 'drawAlphaBlend';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS] = 'drawWhiteness';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS] = 'drawBlackness';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT] = 'drawTransparent';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS] = 'drawCopyBits';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT] = 'drawText';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE] = 'drawStroke';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3] = 'drawRop3';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS] = 'drawInvers';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE] = 'handleStreamCreate';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY] = 'handleStreamDestroy';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA] = 'handleStreamData';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP] = 'handleStreamClip';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND] = 'drawBlend';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST] = 'invalList';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES] = 'invalPalettes';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK] = 'displayMark';
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET] = 'displayReset';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_BITMAP] = 'bitmap';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC] = 'quic';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_RESERVED] = 'reserved';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_PNG] = 'png';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_PLT] = 'lz_plt';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB] = 'lz_rgb';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_GLZ_RGB] = 'glz_rgb';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_FROM_CACHE] = 'cache';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_SURFACE] = 'surface';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_JPEG] = 'jpeg';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS] = 'cache_lossless';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB] = 'zlib_glz_rgb';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_JPEG_ALPHA] = 'jpeg_alpha';
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS] = 'canvas';
},
setStartTime: function (time) {
this.startTimes.push(time);
},
getSpiceMessageType: function (spiceMessage, prepend, append) {
var type = this.routeList[spiceMessage.messageType];
if (type === 'drawImage') {
type += '_' + this.imageTypes[spiceMessage.args.image.imageDescriptor.type];
}
return (prepend || '') + type + (append || '');
},
setNetworkTimeStart: function (time) {
this.networkStart = this.networkStart || time || Date.now();
},
logNetworkTime: function () {
if (this.networkStart) {
this.networkTotalTime += Date.now() - this.networkStart;
this.networkStart = 0;
}
},
startTestSession: function () {
this.clear();
wdi.logOperations = true;
this.testStartTime = Date.now();
},
stopTestSession: function () {
this.testStopTime = Date.now();
wdi.logOperations = false;
},
log: function(spiceMessage, start, customType, useTimeQueue, prepend, append) {
var end = Date.now();
var type;
if(customType) {
type = customType;
} else {
type = this.getSpiceMessageType(spiceMessage, prepend, append);
}
if(!this.data.hasOwnProperty(type)) {
this.data[type] = [];
}
if (useTimeQueue) {
start = this.startTimes.shift();
}
this.data[type].push({start: start, end: end});
},
clear: function() {
this.data = {};
this.testStartTime = 0;
this.testStopTime = 0;
this.networkTotalTime = 0;
this.networkStart = 0;
},
getData: function() {
return this.data;
},
getStats: function() {
var networkTime = this.networkTotalTime;
var numOperations = 0;
var totalTimeSpent = networkTime;
var totalTime = this.testStopTime - this.testStartTime;
var dataSource = this.data;
var partialTimes = {};
var result = "";
var data;
for(var i in this.data) {
if(this.data.hasOwnProperty(i)) {
data = dataSource[i];
numOperations += data.length;
partialTimes[i] = 0;
for(var x = 0;x< data.length;x++) {
partialTimes[i] += data[x].end - data[x].start;
}
totalTimeSpent += partialTimes[i];
}
}
result += "Total operations by number:\n";
var partial = 0;
for(var i in dataSource) {
if(dataSource.hasOwnProperty(i)) {
partial = (dataSource[i].length / numOperations) * 100;
result += i+': '+(~~partial)+"% (" + dataSource[i].length + ")\n";
}
}
result += "Total numOperations: " + numOperations + "\n";
result += "---------------------------------\n";
result += "\n";
result += "Total Operations by time:\n";
for(i in partialTimes) {
if(partialTimes.hasOwnProperty(i)) {
partial = (partialTimes[i] / totalTime) * 100;
result += i+': '+(~~partial)+"% ("+partialTimes[i]+"ms)\n";
}
}
var idleTime = totalTime - totalTimeSpent;
partial = (idleTime / totalTime) * 100;
result += "Idle: "+(~~partial)+"% ("+idleTime+"ms)\n";
partial = (networkTime / totalTime) * 100;
result += "Network: " + (~~partial) + "% (" + networkTime + "ms)\n";
result += 'Total time: ' + totalTime + 'ms \n';
return "BEGIN OF PERFORMANCE STATS\n" + result + "\nEND OF PERFORMANCE STATS\n";
}
};
wdi.DataLogger.init();

View File

@ -0,0 +1,126 @@
/*
Generic Object Pooling from:
https://github.com/miohtama/objectpool.js/
MIT License
Copyright (C) 2013 Mikko Ohtamaa
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Version: 65c7399c30a3f6f3593bb4bfca3d9cde65675b84 (git commit)
*/
wdi.GenericObjectPool = $.spcExtend(wdi.EventObject.prototype, {
/** How fast we grow */
expandFactor : 0.2,
/** Minimum number of items we grow */
expandMinUnits : 16,
elems : null,
/** List of discarded element indexes in our this.elems pool */
freeElems : null,
allocator: null,
resetor: null,
/**
* Generic object pool for Javascript.
*
* @param {Function} allocator return new empty elements
*
* @param {Function} resetor resetor(obj, index) is called on all new elements when they are (re)allocated from pool.
* This is mostly useful for making object to track its own pool index.
*/
init : function(params) {
var allocator = params[0];
var resetor = params[1];
// Start with one element
this.allocator = allocator;
this.resetor = resetor;
// Set initial state of 1 object
this.elems = [this.allocator()];
this.freeElems = [0];
},
/**
* @return {[type]} [description]
*/
create : function() {
if(!this.freeElems.length) {
this.expand();
}
// See if we have any allocated elements to reuse
var index = this.freeElems.pop();
var elem = this.elems[index];
this.resetor(elem, index);
return elem;
},
/**
* How many allocated units we have
*
* @type {Number}
*/
length : function() {
return this.elems.length - this.freeElems.length;
},
/**
* Make pool bigger by the default growth parameters.
*
*/
expand : function() {
var oldSize = this.elems.length;
var growth = Math.ceil(this.elems.length * this.expandFactor);
if(growth < this.expandMinUnits) {
growth = this.expandMinUnits;
}
this.elems.length = this.elems.length + growth;
for(var i=oldSize; i<this.elems.length; i++) {
this.elems[i] = this.allocator();
this.freeElems.push(i);
}
},
/**
* Deallocate object at index n
*
* @param {Number} n
* @return {Object} discarded object
*/
discard : function(n) {
// Cannot double deallocate
if(this.freeElems.indexOf(n) >= 0) {
throw "GeneircObjectPool: Double-free for element index: "+n;
}
if(this.elems[n].keepAlive) {
return false;
}
this.freeElems.push(n);
return true;
},
/**
* Return object at pool index n
*/
get : function(n) {
return this.elems[n];
}
});

View File

@ -0,0 +1,114 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.GlobalPool = {
pools: {},
retained: null,
init: function() {
this.retained = {};
var self = this;
this.pools['ViewQueue'] = new wdi.GenericObjectPool([function() {
//factory
return new wdi.ViewQueue();
}, function(obj, index) {
//reset
obj.poolIndex = index; //update index at pool
obj.setData([]); //reset the object
}]);
this.pools['RawSpiceMessage'] = new wdi.GenericObjectPool([function() {
//factory
return new wdi.RawSpiceMessage();
}, function(obj, index) {
//reset
obj.poolIndex = index; //update index at pool
obj.set(null, null, null); //reset the object
}]);
this.retained['Image'] = [];
this.pools['Image'] = new wdi.GenericObjectPool([function() {
//factory
return new Image();
}, function(obj, index) {
//reset
obj.poolIndex = index;
obj.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';//Blank image 1x1 pixel (avoids console error GET null image)
obj.onload = null;
obj.keepAlive = false;
self.retained['Image'][index] = obj;
}]);
this.retained['Canvas'] = [];
this.pools['Canvas'] = new wdi.GenericObjectPool([function() {
//factory
return self.createCanvas();
}, function(obj, index) {
//reset
obj.keepAlive = false;
//obj.getContext('2d').clearRect(0, 0, obj.width, obj.height);
obj.poolIndex = index;
self.retained['Canvas'][index] = obj;
}]);
},
createCanvas: function() {
return $('<canvas/>')[0];
},
create: function(objectType) {
return this.pools[objectType].create();
},
discard: function(objectType, obj) {
//check if its an autorelease pool
if(this.retained.hasOwnProperty(objectType)) {
delete this.retained[objectType][obj.poolIndex];
}
return this.pools[objectType].discard(obj.poolIndex);
},
cleanPool: function(objectType) {
if(this.retained.hasOwnProperty(objectType)) {
var pool = this.pools[objectType];
for(var i in this.retained[objectType]) {
if(pool.discard(this.retained[objectType][i].poolIndex)) {
delete this.retained[objectType][i];
}
}
} else {
wdi.Debug.error("GlobalPool: cleanPool called with invalid objectType: ",objectType);
}
}
}

View File

@ -0,0 +1,141 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.ImageUncompressor = $.spcExtend(wdi.EventObject.prototype, {
init: function (c) {
this.syncAsyncHandler = c.syncAsyncHandler || new wdi.SyncAsyncHandler({
isAsync: c.isAsync
});
},
lzHeaderSize: 32,
extractLzHeader: function (imageData, brush) {
var headerData, header;
if (!brush) { //brushes are still js arrays
if (Object.prototype.toString.call(imageData) === "[object Array]") {
headerData = imageData.slice(0, this.lzHeaderSize);
imageData = imageData.slice(this.lzHeaderSize); //skip the header
} else {
headerData = imageData.subarray(0, this.lzHeaderSize).toJSArray();
imageData = imageData.subarray(this.lzHeaderSize); //skip the header
}
header = wdi.LZSS.demarshall_rgb(headerData);
} else {
header = wdi.LZSS.demarshall_rgb(imageData);
}
return {
header: header,
imageData: imageData
};
},
processLz: function (imageData, brush, opaque, clientGui, callback, scope) {
var extractedData, u8, buffer, number, context;
extractedData = this.extractLzHeader(imageData, brush);
imageData = extractedData.imageData;
number = extractedData.header.width * extractedData.header.height * 4;
buffer = new ArrayBuffer(imageData.length + 16);
u8 = new Uint8Array(buffer);
u8[0] = 1; //LZ_RGB
u8[1] = opaque;
u8[2] = extractedData.header.type;
u8[3] = extractedData.header.top_down; //padding
for (var i = 0; i < 4; i++) { //iterations because of javascript number size
u8[4 + i] = number & (255); //Get only the last byte
number = number >> 8; //Remove the last byte
}
var view = new DataView(buffer);
view.setUint32(8, extractedData.header.width);
view.setUint32(12, extractedData.header.height);
u8.set(imageData, 16);
this.syncAsyncHandler.dispatch(buffer, callback, scope);
},
processQuic: function (imageData, opaque, clientGui, callback, scope) {
wdi.Debug.log('Quic decode');
buffer = new ArrayBuffer(imageData.length + 4);
view = new Uint8Array(buffer);
view.set(imageData, 4);
view[3] = opaque ? 1 : 0;
view[0] = 0; //quic
this.syncAsyncHandler.dispatch(buffer, callback, scope);
},
process: function (imageDescriptor, imageData, brush, opaque, clientGui, callback, scope) {
switch(imageDescriptor.type) {
case wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC:
this.processQuic(imageData, opaque, clientGui, callback, scope);
break;
case wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB:
this.processLz(imageData, brush, opaque, clientGui, callback, scope);
break;
}
},
dispose: function () {
this.syncAsyncHandler.dispose();
}
});
var syncInstance;
var asyncInstance;
wdi.ImageUncompressor.getSyncInstance = function () {
if (!syncInstance) {
syncInstance = new wdi.ImageUncompressor({
isAsync: false
});
}
return syncInstance;
};
wdi.ImageUncompressor.getAsyncInstance = function () {
if (!asyncInstance) {
asyncInstance = new wdi.ImageUncompressor({
isAsync: true
});
}
return asyncInstance;
};

View File

@ -0,0 +1,77 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.IntegrationBenchmark = {
benchmarking: false,
startTime: 0,
timeoutInterval: 3000, // in ms, amount of time after it will be considered that
// we have received all packets and can stop counting
timeOutId: undefined,
busConnection: undefined,
setEndTime: function() {
var self = this;
this.timeOutId = setTimeout(function() {
// if 3000 ms have passed since the last packet we assume we have processed them all and can launch MS Word
self.timeOutId = undefined;
self.benchmarking = false;
var now = new Date().getTime();
var elapsed = now - self.startTime - self.timeoutInterval;
self.onEndBenchmarkCallback(elapsed);
var message = {
"type": wdi.BUS_TYPES.killApplicationDoNotUseInProductionEver,
"application": "EXCEL.EXE"
};
self.busConnection.send(message);
}, this.timeoutInterval);
},
setStartTime: function() {
if (this.timeOutId !== undefined) {
clearTimeout(this.timeOutId);
}
},
launchApp: function(busConnection, onEndBenchmarkCallback) {
this.busConnection = busConnection;
wdi.IntegrationBenchmark.benchmarking = true;
wdi.IntegrationBenchmark.setStartTime();
this.onEndBenchmarkCallback = onEndBenchmarkCallback;
this.startTime = new Date().getTime();
var message = {
"type": wdi.BUS_TYPES.launchApplication,
"file": "c:\\Users\\eyeos\\Desktop\\test.xlsx"
};
this.busConnection.send(message);
}
};

View File

@ -0,0 +1,116 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
/*
* Check if a packet should be intercepted in packetpreprocess to be executed
* in parallel.
*/
wdi.PacketWorkerIdentifier = $.spcExtend(wdi.EventObject.prototype, {
init: function(c) {
//default empty constructor
},
shouldUseWorker: function(message) {
switch (message.messageType) {
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
var brush = message.args.brush;
if(brush.type === wdi.SpiceBrushType.SPICE_BRUSH_TYPE_PATTERN) {
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
}
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
//case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA:
// return wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO;
}
return 0;
},
getImageProperties: function(message) {
var props = {
data: null,
descriptor: null,
opaque: true,
brush: null
};
//coupling here, to be cleaned when doing real code
switch (message.messageType) {
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
props.descriptor = message.args.image.imageDescriptor;
props.data = message.args.image.data;
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
props.brush = message.args.brush;
if(props.brush.type === wdi.SpiceBrushType.SPICE_BRUSH_TYPE_PATTERN) {
props.descriptor = props.brush.pattern.image;
props.data = props.brush.pattern.imageData;
} else {
return false;
}
break;
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
props.data = message.args.image.data;
props.descriptor = message.args.image.imageDescriptor;
props.opaque = false;
break;
default:
wdi.Debug.log("PacketWorkerIdentifier: Unknown Packet in getImageProperties");
return false;
}
return props;
},
getVideoData: function(message) {
if(message.messageType !== wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA) {
wdi.Debug.log('PacketWOrkerIdentifier: Invalid packet in getVideoData');
return false;
}
return message.args.data;
}
});
wdi.PacketWorkerIdentifier.processingType = {};
wdi.PacketWorkerIdentifier.processingType.DECOMPRESS = 1;
wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO = 2;

View File

@ -0,0 +1,57 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SyncAsyncHandler = $.spcExtend(wdi.EventObject.prototype, {
init: function (c) {
this.isAsync = !!c.isAsync;
if (this.isAsync) {
this.asyncWorker = c.asyncWorker || new wdi.AsyncWorker({script:'application/WorkerProcess.js'});
}
},
isAsync: null,
dispatch: function(buffer, callback, scope) {
if (this.isAsync) {
this.asyncWorker.run(buffer, callback, scope);
} else {
var result = window['workerDispatch'](buffer, this.isAsync);
callback.call(scope, result);
}
},
dispose: function () {
if (this.isAsync) {
this.asyncWorker.dispose();
}
}
});

View File

@ -0,0 +1,279 @@
/*
* Modified from:
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XML-RPC Client component.
*
* The Initial Developer of the Original Code is
* Digital Creations 2, Inc.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Martijn Pieters <mj@digicool.com> (original author)
* Samuel Sieb <samuel@sieb.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*jslint white: false, bitwise: false, plusplus: false */
/*global console */
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
base64Pad : '=',
encode: function (data) {
"use strict";
var result = '',
chrTable = Base64.toBase64Table.split(''),
pad = Base64.base64Pad,
length = data.length,
i;
// Convert every three bytes to 4 ascii characters.
for (i = 0; i < (length - 2); i += 3) {
result += chrTable[data[i] >> 2];
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += chrTable[data[i+2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
result += chrTable[data[i] >> 2];
if ((length%3) === 2) {
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[(data[i+1] & 0x0f) << 2];
result += pad;
} else {
result += chrTable[(data[i] & 0x03) << 4];
result += pad + pad;
}
}
return result;
},
/* Convert Base64 data to a string */
toBinaryTable : [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var binTable = Base64.toBinaryTable,
pad = Base64.base64Pad,
result, result_length, idx, i, c, padding,
leftbits = 0, // number of bits decoded, but yet to be appended
leftdata = 0, // bits decoded, but yet to be appended
data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5);
result = new Array(result_length);
// Convert one by one.
for (idx = 0, i = offset; i < data.length; i++) {
c = binTable[data.charCodeAt(i) & 0x7f];
padding = (data.charAt(i) === pad);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
continue;
}
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding) {
result[idx++] = (leftdata >> leftbits) & 0xff;
}
leftdata &= (1 << leftbits) - 1;
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
throw {name: 'Base64-Error',
message: 'Corrupted base64 string'};
}
return result;
},
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encodeStr : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decodeStr : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}; /* End of Base64 namespace */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
// Source: https://github.com/ded/bowser
/**
* README!!!!
* To solve the error "MISMATCHED ANONYMOUS DEFINE() MODULES ..." (http://requirejs.org/docs/errors.html)
* It happens when requirejs is defined globally and this script is added
* in the old fashioned way, i.e. with <script src="..."></script>
* -> comment just the follow line if it happens!!
* else if (typeof define == 'function') define(definition)
*/
!function (name, definition) {
if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition()
/*
* problems including this library as classig <script src="..."> in a require
* environment, so commenting out the following line (as stated above) while
* we prepare a better fix involving requirejs shim.
* @kampde, 2014-10-02
*/
//else if (typeof define == 'function') define(definition)
else this[name] = definition()
}('bowser', function () {
/**
* See useragents.js for examples of navigator.userAgent
*/
var t = true
function detect(ua) {
function getFirstMatch(regex) {
var match = ua.match(regex);
return (match && match.length > 1 && match[1]) || '';
}
var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase()
, likeAndroid = /like android/i.test(ua)
, android = !likeAndroid && /android/i.test(ua)
, versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i)
, tablet = /tablet/i.test(ua)
, mobile = !tablet && /[^-]mobi/i.test(ua)
, result
if (/opera|opr/i.test(ua)) {
result = {
name: 'Opera'
, opera: t
, version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)
}
}
else if (/windows phone/i.test(ua)) {
result = {
name: 'Windows Phone'
, windowsphone: t
, msie: t
, version: getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i)
}
}
else if (/msie|trident/i.test(ua)) {
result = {
name: 'Internet Explorer'
, msie: t
, version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i)
}
}
else if (/chrome|crios|crmo/i.test(ua)) {
result = {
name: 'Chrome'
, chrome: t
, version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)
}
}
else if (iosdevice) {
result = {
name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod'
}
// WTF: version is not part of user agent in web apps
if (versionIdentifier) {
result.version = versionIdentifier
}
}
else if (/sailfish/i.test(ua)) {
result = {
name: 'Sailfish'
, sailfish: t
, version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i)
}
}
else if (/seamonkey\//i.test(ua)) {
result = {
name: 'SeaMonkey'
, seamonkey: t
, version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i)
}
}
else if (/firefox|iceweasel/i.test(ua)) {
result = {
name: 'Firefox'
, firefox: t
, version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i)
}
if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) {
result.firefoxos = t
}
}
else if (/silk/i.test(ua)) {
result = {
name: 'Amazon Silk'
, silk: t
, version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i)
}
}
else if (android) {
result = {
name: 'Android'
, version: versionIdentifier
}
}
else if (/phantom/i.test(ua)) {
result = {
name: 'PhantomJS'
, phantom: t
, version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i)
}
}
else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) {
result = {
name: 'BlackBerry'
, blackberry: t
, version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i)
}
}
else if (/(web|hpw)os/i.test(ua)) {
result = {
name: 'WebOS'
, webos: t
, version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)
};
/touchpad\//i.test(ua) && (result.touchpad = t)
}
else if (/bada/i.test(ua)) {
result = {
name: 'Bada'
, bada: t
, version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i)
};
}
else if (/tizen/i.test(ua)) {
result = {
name: 'Tizen'
, tizen: t
, version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier
};
}
else if (/safari/i.test(ua)) {
result = {
name: 'Safari'
, safari: t
, version: versionIdentifier
}
}
else result = {}
// set webkit or gecko flag for browsers based on these engines
if (/(apple)?webkit/i.test(ua)) {
result.name = result.name || "Webkit"
result.webkit = t
if (!result.version && versionIdentifier) {
result.version = versionIdentifier
}
} else if (!result.opera && /gecko\//i.test(ua)) {
result.name = result.name || "Gecko"
result.gecko = t
result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i)
}
// set OS flags for platforms that have multiple browsers
if (android || result.silk) {
result.android = t
} else if (iosdevice) {
result[iosdevice] = t
result.ios = t
}
// OS version extraction
var osVersion = '';
if (iosdevice) {
osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i);
osVersion = osVersion.replace(/[_\s]/g, '.');
} else if (android) {
osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i);
} else if (result.windowsphone) {
osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i);
} else if (result.webos) {
osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i);
} else if (result.blackberry) {
osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i);
} else if (result.bada) {
osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i);
} else if (result.tizen) {
osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i);
}
if (osVersion) {
result.osversion = osVersion;
}
// device type extraction
var osMajorVersion = osVersion.split('.')[0];
if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) {
result.tablet = t
} else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) {
result.mobile = t
}
// Graded Browser Support
// http://developer.yahoo.com/yui/articles/gbs
if ((result.msie && result.version >= 9) ||
(result.chrome && result.version >= 20) ||
(result.firefox && result.version >= 10.0) ||
(result.safari && result.version >= 5) ||
(result.opera && result.version >= 10.0) ||
(result.ios && result.osversion && result.osversion.split(".")[0] >= 6)
) {
result.a = t;
}
else if ((result.msie && result.version < 9) ||
(result.chrome && result.version < 20) ||
(result.firefox && result.version < 10.0) ||
(result.safari && result.version < 5) ||
(result.opera && result.version < 10.0) ||
(result.ios && result.osversion && result.osversion.split(".")[0] < 6)
) {
result.c = t
} else result.x = t
return result
}
var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '')
/*
* Set our detect method to the main bowser object so we can
* reuse it to test other user agents.
* This is needed to implement future tests.
*/
bowser._detect = detect;
/**
* README: all_compiled uses window["bowser]
*/
if (!window['bowser']) {
window['bowser'] = bowser;
}
return bowser
});

View File

@ -0,0 +1,84 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.DisplayRouter = $.spcExtend(wdi.EventObject.prototype, {
init: function(c) {
this.clientGui = c.clientGui;
this.rasterEngine = c.rasterEngine || new wdi.RasterEngine({clientGui: this.clientGui});
if(c.routeList) {
this.routeList = c.routeList;
} else {
this._initRoutes();
}
},
_initRoutes: function() {
this.routeList = {};
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE] = this.rasterEngine.drawCanvas;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY] = this.rasterEngine.removeCanvas;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY] = this.rasterEngine.drawImage;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL] = this.rasterEngine.drawFill;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND] = this.rasterEngine.drawAlphaBlend;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS] = this.rasterEngine.drawWhiteness;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS] = this.rasterEngine.drawBlackness;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT] = this.rasterEngine.drawTransparent;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS] = this.rasterEngine.drawCopyBits;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT] = this.rasterEngine.drawText;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE] = this.rasterEngine.drawStroke;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3] = this.rasterEngine.drawRop3;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS] = this.rasterEngine.drawInvers;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE] = this.rasterEngine.handleStreamCreate;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY] = this.rasterEngine.handleStreamDestroy;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA] = this.rasterEngine.handleStreamData;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP] = this.rasterEngine.handleStreamClip;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND] = this.rasterEngine.drawBlend;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST] = this.rasterEngine.invalList;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES] = this.rasterEngine.invalPalettes;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK] = false;
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET] = false;
},
processPacket: function(spiceMessage) {
//filter out empty messages
if(!spiceMessage) {
wdi.Debug.log('DisplayProcess processPacket: Skipping empty message...');
return;
}
var route = this.routeList[spiceMessage.messageType];
if (route) {
route.call(this.rasterEngine, spiceMessage);
}
}
});

View File

@ -0,0 +1,172 @@
rng = new SecureRandom();
function pack(source)
{
var temp = "";
for (var i = 0; i < source.length; i+=2)
{
temp+= String.fromCharCode(parseInt(source.substring(i, i + 2), 16));
}
return temp;
}
function char2hex(source)
{
var hex = "";
for (var i = 0; i < source.length; i+=1)
{
temp = source[i].toString(16);
switch (temp.length)
{
case 1:
temp = "0" + temp;
break;
case 0:
temp = "00";
}
hex+= temp;
}
return hex;
}
function xor(a, b)
{
length = Math.min(a.length, b.length);
temp = "";
for (var i = 0; i < length; i++)
{
temp+= String.fromCharCode(a.charCodeAt(i) ^ b.charCodeAt(i));
}
length = Math.max(a.length, b.length) - length;
for (var i = 0; i < length; i++)
{
temp+= "\x00";
}
return temp;
}
function mgf1(mgfSeed, maskLen)
{
t = "";
hLen = 20;
count = Math.ceil(maskLen / hLen);
for (var i = 0; i < count; i++)
{
c = String.fromCharCode((i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
t+= pack(sha1Hash(mgfSeed + c));
}
return t.substring(0, maskLen);
}
function rsa_oaep_encrypt(message, n, e) {
// precomputed values
var k = 128; // length of n in bytes
var hLen = 20;
var mLen = message.length;
var lHash = '\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09'; // pack(sha1Hash(""))
var temp = k - mLen - 2 * hLen - 2;
for (var i = 0; i < temp; i++) {
lHash += '\x00';
}
var db = lHash + '\x01' + message;
var seed = '';
for (var i = 0; i < hLen + 4; i += 4) {
temp = new Array(4);
rng.nextBytes(temp);
seed += String.fromCharCode(temp[0], temp[1], temp[2], temp[3]);
}
seed = seed.substring(4 - seed.length % 4);
var dbMask = mgf1(seed, k - hLen - 1);
var maskedDB = xor(db, dbMask);
var seedMask = mgf1(maskedDB, hLen);
var maskedSeed = xor(seed, seedMask);
var em = "\x00" + maskedSeed + maskedDB;
m = new Array();
for (i = 0; i < em.length; i++) {
m[i] = em.charCodeAt(i);
}
m = new encryptionBigInteger(m, 256);
c = m.modPowInt(e, n); // doPublic
c = c.toString(16);
if (c.length & 1)
c = "0" + c;
return c;
}
function RSA_public_encrypt(password, pub_key) {
var keyInChar = new Uint8Array(pub_key);
var rawPubKey = new Array(129); // 00xxx
for (var i = 0; i < 129; i++)
rawPubKey[i] = keyInChar[28 + i];
var n = new encryptionBigInteger(rawPubKey);
var e = new encryptionBigInteger('010001', 16);
var hexRsa = rsa_oaep_encrypt(password + String.fromCharCode(0), n, e);
return hexRsa;
}

View File

@ -0,0 +1,52 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.Flipper = {
flip: function(sourceImg) {
return this._handMadeFlip(sourceImg);
},
_handMadeFlip: function(sourceImg) {
var newCanvas = document.createElement('canvas');
newCanvas.width = sourceImg.width;
newCanvas.height = sourceImg.height;
var ctx = newCanvas.getContext('2d');
ctx.save();
// Multiply the y value by -1 to flip vertically
ctx.scale(1, -1);
// Start at (0, -height), which is now the bottom-left corner
ctx.drawImage(sourceImg, 0, -sourceImg.height);
ctx.restore();
return newCanvas;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.GraphicDebug = $.spcExtend(wdi.DomainObject, {
debugMode: null,
spiceGraphicMessageTypes: [],
cloneSpiceMessage: null,
clientGui: null,
tmpCanvas: null,
tmpContext: null,
originalCanvas: null,
spiceMessageData: null,
endCanvas: null,
currentOperation: null,
init: function(c) {
this.debugMode = c.debugMode;
if (this.debugMode) {
this._generateArray();
this._showDebug();
} else {
this._hideDebug();
}
},
_generateArray: function() {
var self = this;
$.each(wdi.SpiceVars, function(key, value) {
if (key.search("SPICE_MSG_DISPLAY_") != -1) {
self.spiceGraphicMessageTypes[value] = key;
}
});
},
_showDebug: function() {
$('#canvasSpace').show();
$('#graphicDebug').show();
},
_hideDebug: function() {
$('#canvasSpace').hide();
$('#graphicDebug').hide();
},
printDebugMessageOnFilter: function(spiceMessage, clientGui) {
if(spiceMessage.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY && this.debugMode && ($('#logActive').prop('checked'))) {
var surface_id = null;
this.clientGui = clientGui;
this.cloneSpiceMessage = $.extend(true, {}, spiceMessage);
if(this.cloneSpiceMessage.args.base && this.cloneSpiceMessage.args.base.surface_id !== null) {
surface_id = this.cloneSpiceMessage.args.base.surface_id;
var box = wdi.graphics.getBoxFromSrcArea(this.cloneSpiceMessage.args.base.box);
var spiceMessageDiv = $('<div/>')
.append(prettyPrint(this.cloneSpiceMessage))
.hide();
this.originalCanvas = this._copyCanvasFromSurfaceId(surface_id);
this.spiceMessageData = spiceMessage.args.originalData;
this.currentOperation = this.spiceGraphicMessageTypes[this.cloneSpiceMessage.messageType];
$('#debugInfo')
.append($('<br/>'))
.append($('<hr/>'))
.append(this._copyAndHighlightCanvas(surface_id, box))
.append($('<br/>'))
.append($('<div/>')
.append(this.currentOperation + ' (Click to hide/show)')
.css('cursor', 'pointer')
.css('color', 'blue')
.css('text-decoration', 'underline')
.click(function() {
spiceMessageDiv.toggle();
})
).append(spiceMessageDiv);
if (this.cloneSpiceMessage.args.hasOwnProperty('image')) {
this._printImage(spiceMessageDiv);
}
}
}
},
_printImage: function(spiceMessageDiv) {
wdi.graphics.getImageFromSpice(this.cloneSpiceMessage.args.image.imageDescriptor, this.cloneSpiceMessage.args.image.data, this.clientGui, function(srcImg) {
if(srcImg) {
spiceMessageDiv.append(
$('<div/>').css('font-size', '12px')
.append('Image inside spiceMessage:')
.append($('<br/>'))
.css('border', '1px solid black')
.append(srcImg)
);
}
}, this);
},
printDebugMessageOnNotifyEnd: function(spiceMessage, clientGui) {
this.clientGui = clientGui;
if(spiceMessage.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY && this.debugMode && ($('#logActive').prop('checked'))) {
var surface_id = null;
if(spiceMessage.args.base && spiceMessage.args.base.surface_id !== null) {
var self = this;
var createTestClickCallback = function (currentSpiceMessage, originalCanvas, endCanvas, currentOperation) {
return function () {
self.createImageTest(currentSpiceMessage, originalCanvas, endCanvas, currentOperation);
};
};
var createReplayClickCallback = function (currentSpiceMessage, originalCanvas, endCanvas, currentOperation) {
return function () {
self.createReplay(currentSpiceMessage, originalCanvas, endCanvas, currentOperation);
};
};
surface_id = spiceMessage.args.base.surface_id;
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.base.box);
var currentCanvas = this._copyCanvasFromSurfaceId(surface_id);
$('#debugInfo')
.append($('<br/>'))
.append($('<div/>')
.append($('<button>Create test</button>')
.css('cursor', 'pointer')
.click(createTestClickCallback(this.spiceMessageData, this.originalCanvas, currentCanvas, this.currentOperation))
)
.append($('<button>Create replay window</button>')
.css('cursor', 'pointer')
.click(createReplayClickCallback(this.spiceMessageData, this.originalCanvas, currentCanvas, this.currentOperation))
)
).append($('<br/>'))
.append(this._copyAndHighlightCanvas(surface_id, box));
}
}
},
createImageTest: function (spiceMessage, originalCanvas, endCanvas, currentOperation) {
var name = prompt('Name of the test', currentOperation);
var data1 = originalCanvas.toDataURL('image/png');
var data2 = endCanvas.toDataURL('image/png');
var dataObj = {
origin: data1,
expected: data2,
object: spiceMessage,
name: name
};
var data = JSON.stringify(dataObj);
var fileName = name.replace(/\s/g, '_');
$.post('graphictestgenerator.php','data=' + data + '&name=' + fileName).done(function (data,status,xhr) {
alert('Test created');
}).fail(function(jqXHR, textStatus, errorThrown) {
alert('Test creation failed.\n\nGot response: ' + jqXHR.status + ' '
+ jqXHR.statusText + '\n\n' + jqXHR.responseText);
});
},
createReplay: function (spiceMessage, originalCanvas) {
var data1 = originalCanvas.toDataURL('image/png');
var dataObj = {
origin: data1,
object: spiceMessage,
width: originalCanvas.width,
height: originalCanvas.height
};
var data = JSON.stringify(dataObj);
$.post('graphictestgenerator.php','data=' + data + '&replay=true').done(function (data,status,xhr) {
window.open('replay.html', 'replay');
}).fail(function(jqXHR, textStatus, errorThrown) {
alert('Replay failed.\n\nGot response: ' + jqXHR.status + ' '
+ jqXHR.statusText + '\n\n' + jqXHR.responseText);
});
},
_copyCanvasFromSurfaceId: function (surface_id) {
var context = this.clientGui.getContext(surface_id);
this.tmpCanvas = context.canvas;
var myCanvas = document.createElement('canvas');
myCanvas.width = this.tmpCanvas.width;
myCanvas.height = this.tmpCanvas.height;
myCanvas.getContext('2d').drawImage(this.tmpCanvas, 0, 0);
return myCanvas;
},
_copyAndHighlightCanvas: function(surface_id, box) {
var myCanvas = this._copyCanvasFromSurfaceId(surface_id);
context = myCanvas.getContext('2d');
context.fillStyle = "rgba(255,0,0,0.3)";
context.fillRect(box.x, box.y, box.width, box.height);
return myCanvas;
}
});

View File

@ -0,0 +1,173 @@
wdi.BMP2 = $.spcExtend(wdi.SpiceObject, {
objectSize: 0,
mapper: [0, 1, 1, 4, 4, 8, 16, 24, 32, 32],
init: function(imageData) {
var type = this.bytesToInt8(imageData);
var flags = this.bytesToInt8(imageData); // bit 1 => normal, bit 2 => palette cache (...)
var width = this.bytesToInt32(imageData); // width in pixels
var height = this.bytesToInt32(imageData); // height in pixels
var stride = this.bytesToInt32(imageData); // width in bytes including padding
var len;
var bpp = this.mapper[type];
var i;
var paletteSize = 0, unique, paletteData, numEnts = 0;
if (bpp <= 8 && bpp > 0) {
var palette = [];
if (flags & 1) {
var paletteOffset = this.bytesToInt32(imageData); // From the begininig of the spice packet?
len = imageData.length;
paletteSize = 4*Math.pow(2,bpp);
var paletteDataSize = paletteSize + 8 + 2; //palette + unique(64b) + numEnts (16b)
len -= paletteDataSize;
paletteData = imageData.splice(len, paletteDataSize);
unique = this.bytesToInt64(paletteData);
numEnts = this.bytesToInt16(paletteData);
var queue;
for (i = 0; i < numEnts*4; i+=4) {
queue = new wdi.Queue();
queue.setData(paletteData.slice(i, i+4));
palette.push(new wdi.SpiceColor().demarshall(queue));
}
wdi.ImageCache.addPalette(unique, palette);
} else {
//get palette from cache
unique = this.bytesToInt64(imageData);
len = imageData.length;
palette = wdi.ImageCache.getPalette(unique);
var spiceColors;
paletteData = [];
numEnts = palette.length;
for (i =0; i < numEnts; i++ ) {
spiceColors = palette[i].marshall();
spiceColors.push(0);
paletteData = paletteData.concat(spiceColors);
}
}
// imageData = paletteData.concat(imageData);
} else {
// Removing 4 bytes from the image data to fix index out of range error.
var unknown = this.bytesToInt32(imageData);
}
this.setContent({
imageSize: len,
width: width,
height: height,
bpp: bpp,
imageData: imageData,
paletteSize: numEnts * 4,
palette: palette,
stride: stride,
type: type
});
},
setContent: function(c) {
this.imageSize = c.imageSize;
this.width = c.width;
this.height = c.height;
this.bpp = c.bpp;
this.imageData = c.imageData;
this.palette = c.palette;
this.offset = c.paletteSize + 0x36; //0x36 === Current harcoded header size (BMP + DIB)
this.size = this.offset + this.imageSize;
this.stride = c.stride;
this.type = c.type;
},
marshall: function(context) {
var type = this.type;
var palette = this.palette;
var width = this.width;
var height = this.height;
var stride = this.stride;
var data = this.imageData;
var size = data.length;
var pixelsStride = stride * 8/this.bpp;
var bytesStride = pixelsStride * 4;
var buf = new ArrayBuffer(bytesStride * height);
var buf8 = new Uint8ClampedArray(buf);
var buf32 = new Uint32Array(buf);
var topdown = false;
var oct, i, pos, buffPos, spiceColor;
var b;
if (palette) {
buffPos = 0
if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_1BIT_BE) {
spiceColor = palette[1];
var foreColor = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
spiceColor = palette[0];
var backColor = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
var PLT1_MASK = [1, 2, 4, 8, 16, 32, 64, 128];
for (pos = 0; pos < size; pos++) {
oct = data[pos];
for (i = 7; i >= 0; i--) {
if (oct & PLT1_MASK[i]) {
buf32[buffPos++] = foreColor;
} else {
buf32[buffPos++] = backColor;
}
}
}
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_4BIT_BE) {
for (pos = 0; pos < size; pos++) {
oct = data[pos];
spiceColor = palette[oct >>> 4];
buf32[buffPos++] = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
spiceColor = palette[oct & 0x0f];
buf32[buffPos++] = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
}
}
} else {
if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_32BIT) {
for (pos = 0; pos < size; pos += 4) {
b = data[pos];
data[pos] = data[pos + 2];
data[pos + 2] = b;
data[pos + 3] = 255;
}
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_RGBA) {
topdown = true;
for (pos = 0; pos < size; pos+=4) {
b = data[pos];
data[pos] = data[pos+2];
data[pos+2] = b;
}
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_24BIT) {
for (pos = 0; pos < size; pos+=3) {
b = data[pos];
data[pos] = data[pos+2];
data[pos+2] = b;
}
}
buf8 = new Uint8ClampedArray(data);
}
var ret = new ImageData(buf8, pixelsStride, height);
var tmpCanvas = wdi.graphics.getNewTmpCanvas(width, height);
tmpCanvas.getContext('2d').putImageData(ret, 0, 0, 0, 0, width, height);
ret = tmpCanvas;
if(!topdown) {
ret = wdi.RasterOperation.flip(ret);
}
return ret;
}
});

View File

@ -0,0 +1,65 @@
function golomb_code_len(n, l) {
if (n < wdi.nGRcodewords[l]) {
return (n >>> l) + 1 + l;
} else {
return wdi.notGRcwlen[l];
}
}
function golomb_decoding(l, bits, bppmask) {
var cwlen;
var result;
if (bits > wdi.notGRprefixmask[l]) {
var zeroprefix = cnt_l_zeroes(bits);
cwlen = zeroprefix + 1 + l;
result = ( (zeroprefix << l) >>> 0) | ((bits >>> (32 - cwlen)) & bppmask[l]);
} else {
cwlen = wdi.notGRcwlen[l];
result = wdi.nGRcodewords[l] + ((bits) >>> (32 - cwlen) & bppmask[wdi.notGRsuffixlen[l]]);
}
return [result,cwlen];
}
/* update the bucket using just encoded curval */
function real_update_model(state, bucket, curval, bpp) {
var i;
var bestcode;
var bestcodelen;
var ithcodelen;
var pcounters = bucket.pcounters;
bestcode = bpp - 1;
bestcodelen = (pcounters[bestcode] += golomb_code_len(curval, bestcode));
for (i = bpp - 2; i >= 0; i--) {
ithcodelen = (pcounters[i] += golomb_code_len(curval, i));
if (ithcodelen < bestcodelen) {
bestcode = i;
bestcodelen = ithcodelen;
}
}
bucket.bestcode = bestcode;
if (bestcodelen > state.wm_trigger) {
for (i = 0; i < bpp; i++) {
pcounters[i] >>>= 1;
}
}
}
function UPDATE_MODEL(index, encoder, bpp, correlate_row_r, correlate_row_g, correlate_row_b) {
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[0],
correlate_row_r[index - 1]), correlate_row_r[index], bpp);
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[1],
correlate_row_g[index - 1]), correlate_row_g[index], bpp);
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[2],
correlate_row_b[index - 1]), correlate_row_b[index], bpp);
}
function find_bucket(channel, val) {
if(val===undefined) {
val=channel.oldFirst;
}
return channel._buckets_ptrs[val];
}

View File

@ -0,0 +1,324 @@
function QUIC_UNCOMPRESS_RGBA(encoder, channels, bpc, type) {
quic_uncompress_row0(encoder, channels, bpc, type);
quic_four_uncompress_row0(encoder, encoder.channels[3], bpc, type);
encoder.row_completed();
var height = encoder.height;
var rgb_state = encoder.rgb_state;
for (var row = 1; row < height; row++) {
quic_uncompress_row(encoder, channels, bpc, type, rgb_state);
quic_four_uncompress_row(encoder, encoder.channels[3], bpc, type);
encoder.row_completed();
}
}
function quic_four_uncompress_row(encoder, channel, bpc, type) {
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
var pos = 0;
var width = encoder.width;
while ((wdi.JSQuic.wmimax > channel.state.wmidx) && (channel.state.wmileft <= width)) {
if (channel.state.wmileft) {
uncompress_four_row_seg(
encoder,
channel,
pos,
pos + channel.state.wmileft,
bpc,
bpc_mask,
type
);
pos += channel.state.wmileft;
width -= channel.state.wmileft;
}
channel.state.wmidx++;
wdi.JSQuic.set_wm_trigger(channel.state);
channel.state.wmileft = wdi.JSQuic.wminext;
}
if (width) {
uncompress_four_row_seg(
encoder,
channel,
pos,
pos + width,
bpc,
bpc_mask,
type
);
if (wdi.JSQuic.wmimax > channel.state.wmidx) {
channel.state.wmileft -= width;
}
}
}
function uncompress_four_row_seg(encoder, channel, i, end, bpc, bpc_mask, type) {
var correlate_row = channel.correlate_row;
var stopidx = 0;
var waitmask = wdi.JSQuic.bppmask[channel.state.wmidx];
var run_index = 0;
var run_end = 0;
var rle = false;
var computedWidth = encoder.computedWidth;
var rows_completed = encoder.rows_completed;
var offset = ((encoder.rows_completed-1) * computedWidth);
var data;
var eatbits = encoder.eatbits;
var appendAlpha = encoder.appendAlpha;
var alpha;
var ret, codewordlen;
var bppmask = wdi.JSQuic.bppmask;
if (!i) {
alpha = UNCOMPRESS_ONE_0_A(channel, encoder, bpc_mask, offset);
if (channel.state.waitcnt) {
--channel.state.waitcnt;
} else {
channel.state.waitcnt = (wdi.JSQuic.tabrand(channel.state) & waitmask);
real_update_model(channel.state, find_bucket(channel,
correlate_row[-1]), correlate_row[0], bpc);
}
stopidx = ++i + channel.state.waitcnt;
} else {
stopidx = i + channel.state.waitcnt;
alpha = encoder.result[encoder.alphaPos-4];
}
for(;;) {
while (stopidx < end) {
for (; i <= stopidx; i++) {
rle = RLE_PRED_A(i, encoder, run_index, computedWidth, rows_completed);
if(rle) break;
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
encoder.io_word, bppmask);
data = ret[0];
codewordlen = ret[1];
correlate_row[i] = data;
alpha = (((wdi.xlatL2U[data] +
((alpha + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0);
appendAlpha.call(encoder, alpha);
eatbits.call(encoder, codewordlen);
}
if(!rle) {
real_update_model(channel.state, find_bucket(channel,
correlate_row[stopidx-1]), correlate_row[stopidx], bpc);
stopidx = i + (wdi.JSQuic.tabrand(channel.state) & waitmask);
} else {
break;
}
}
if(!rle) {
for (; i < end; i++) {
rle = RLE_PRED_A(i, encoder, run_index, computedWidth, rows_completed);
if(rle) break;
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
encoder.io_word, bppmask);
data = ret[0];
codewordlen = ret[1];
correlate_row[i] = data;
alpha = (((wdi.xlatL2U[data] +
((alpha + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0);
appendAlpha.call(encoder, alpha);
eatbits.call(encoder, codewordlen);
}
if(!rle) {
channel.state.waitcnt = stopidx - end;
return;
}
}
//RLE
channel.state.waitcnt = stopidx - i;
run_index = i;
run_end = i + wdi.JSQuic.decode_channel_run(encoder, channel);
var cpos = ((encoder.rows_completed) * (encoder.width*4)) + (i*4);
var a = encoder.result[cpos-1];
for (; i < run_end; i++) {
//TODO: how to append?
appendAlpha.call(encoder, a);
}
if (i === end) {
return;
}
stopidx = i + channel.state.waitcnt;
rle = false;
//END RLE
}
}
function quic_four_uncompress_row0(encoder, channel, bpc, type) {
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
var pos = 0;
var width = encoder.width;
while ((wdi.JSQuic.wmimax > channel.state.wmidx) && (channel.state.wmileft <= width)) {
if (channel.state.wmileft) {
uncompress_four_row0_seg(
encoder,
channel,
pos,
pos + channel.wmileft,
wdi.JSQuic.bppmask[channel.state.wmidx],
bpc,
bpc_mask,
type
);
pos += channel.state.wmileft;
width -= channel.state.wmileft;
}
channel.state.wmidx++;
wdi.JSQuic.set_wm_trigger(channel.state);
channel.state.wmileft = wdi.JSQuic.wminext;
}
if (width) {
uncompress_four_row0_seg(
encoder,
channel,
pos,
pos + width,
wdi.JSQuic.bppmask[channel.state.wmidx],
bpc,
bpc_mask,
type
);
if (wdi.JSQuic.wmimax > channel.state.wmidx) {
channel.state.wmileft -= width;
}
}
}
function uncompress_four_row0_seg(encoder, channel, i, end, waitmask, bpc, bpc_mask, type) {
var correlate_row = channel.correlate_row;
var stopidx = 0;
if (!i) {
UNCOMPRESS_ONE_ROW0_0_A(channel);
if (channel.state.waitcnt) {
--channel.state.waitcnt;
} else {
channel.state.waitcnt = (wdi.JSQuic.tabrand(channel.state) & waitmask);
real_update_model(channel.state, find_bucket(channel,
correlate_row[-1]), correlate_row[0], bpc);
}
stopidx = ++i + channel.state.waitcnt;
} else {
stopidx = i + channel.state.waitcnt;
}
while (stopidx < end) {
for (; i <= stopidx; i++) {
UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row);
}
real_update_model(channel.state, find_bucket(channel,
correlate_row[stopidx-1]), correlate_row[stopidx], bpc);
stopidx = i + (wdi.JSQuic.tabrand(channel.state) & waitmask);
}
for (; i < end; i++) {
UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row);
}
channel.state.waitcnt = stopidx - end;
}
function SAME_PIXEL_A(i, result) {
if(result[i-1] === result[i+3]) {
return true;
}
return false;
}
function RLE_PRED_A(i, encoder, run_index, width, rows_completed) {
var pr = ((rows_completed-1) * width) + (i*4); //prev r
if(run_index !== i && i > 2) {
if(SAME_PIXEL_A(pr, encoder.result)) {
pr = ((rows_completed) * width) + ((i-1)*4); // cur r
if(SAME_PIXEL_A(pr, encoder.result)) {
return true;
}
}
}
return false;
}
function UNCOMPRESS_ONE_0_A(channel, encoder, bpc_mask, offset) {
var ret, codewordlen;
channel.oldFirst = channel.correlate_row[0];
ret = golomb_decoding(find_bucket(channel,
channel.correlate_row[-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
channel.correlate_row[0] = ret[0];
codewordlen = ret[1];
var residuum = wdi.xlatL2U[channel.correlate_row[0]];
var prev = PIXEL_B(channel, encoder, 0, offset);
var resultpixel = ((residuum + prev) & bpc_mask) >>> 0;
encoder.appendAlpha(resultpixel);
encoder.eatbits(codewordlen);
return resultpixel;
}
function UNCOMPRESS_ONE_A(channel, i, bpc_mask, encoder, correlate_row, offset) {
var ret, codewordlen;
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
encoder.io_word, wdi.JSQuic.bppmask);
var data = ret[0];
codewordlen = ret[1];
correlate_row[i] = data;
encoder.appendAlpha((((wdi.xlatL2U[data] +
((PIXEL_A_A(encoder) + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0));
encoder.eatbits(codewordlen);
}
function UNCOMPRESS_ONE_ROW0_0_A(channel) {
var ret, codewordlen;
var encoder = channel.encoder;
ret = golomb_decoding(find_bucket(channel, 0).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
channel.correlate_row[0] = ret[0];
codewordlen = ret[1];
encoder.appendAlpha(wdi.xlatL2U[channel.correlate_row[0]]);
encoder.eatbits(codewordlen);
}
function UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row) {
var ret, codewordlen;
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
var data = ret[0];
codewordlen = ret[1];
correlate_row[i] = data;
encoder.appendAlpha(CORELATE_0_A(encoder, channel, i, bpc_mask, correlate_row));
encoder.eatbits(codewordlen);
}
function CORELATE_0_A(encoder, channel, curr, bpc_mask, correlate_row) {
return ((wdi.xlatL2U[correlate_row[curr]] + PIXEL_A_A(encoder)) & bpc_mask) >>> 0;
}
function PIXEL_A_A(encoder) {
return encoder.result[encoder.alphaPos - 4];
}

View File

@ -0,0 +1,511 @@
function QUIC_UNCOMPRESS_RGB(encoder, channels, bpc, type) {
quic_uncompress_row0(encoder, channels, bpc, type);
encoder.row_completed();
var rgb_state = encoder.rgb_state;
var height = encoder.height;
for (var row = 1; row < height; row++) {
quic_uncompress_row(encoder, channels, bpc, type, rgb_state);
encoder.row_completed();
}
}
function quic_uncompress_row(encoder, channels, bpc, type, rgb_state) {
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
var pos = 0;
var width = encoder.width;
while ((wdi.JSQuic.wmimax > rgb_state.wmidx) && (rgb_state.wmileft <= width)) {
if (rgb_state.wmileft) {
uncompress_row_seg(
encoder,
pos,
pos + rgb_state.wmileft,
bpc,
bpc_mask,
type,
rgb_state
);
pos += rgb_state.wmileft;
width -= rgb_state.wmileft;
}
rgb_state.wmidx++;
wdi.JSQuic.set_wm_trigger(rgb_state);
rgb_state.wmileft = wdi.JSQuic.wminext;
}
if (width) {
uncompress_row_seg(
encoder,
pos,
pos + width,
bpc,
bpc_mask,
type,
rgb_state
);
if (wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) {
rgb_state.wmileft -= width;
}
}
}
function SAME_PIXEL(i, result) {
if(result[i-4] === result[i] && result[i-3] === result[i+1] &&
result[i-2] === result[i+2]) {
return true;
}
return false;
}
function RLE_PRED(i, encoder, offset, currentOffset, run_index) {
if(run_index !== i && i > 2) {
if(SAME_PIXEL(offset, encoder.result)) { //fila de arriba
var pr = currentOffset + ((i-1)*4);
if(SAME_PIXEL(pr, encoder.result)) { //pixel de la izquierda
return true;
}
}
}
return false;
}
function uncompress_row_seg(encoder, i, end, bpc, bpc_mask, type, rgb_state) {
var channel_r = encoder.channels[0];
var channel_g = encoder.channels[1];
var channel_b = encoder.channels[2];
var buckets_ptrs_r = channel_r._buckets_ptrs;
var buckets_ptrs_g = channel_g._buckets_ptrs;
var buckets_ptrs_b = channel_b._buckets_ptrs;
var correlate_row_r = channel_r.correlate_row;
var correlate_row_g = channel_g.correlate_row;
var correlate_row_b = channel_b.correlate_row;
var stopidx = 0;
var waitmask = wdi.JSQuic.bppmask[rgb_state.wmidx];
var run_index = 0;
var run_end = 0;
var rle = false;
var computedWidth = encoder.computedWidth;
var offset = ((encoder.rows_completed-1) * computedWidth);
var currentOffset = ((encoder.rows_completed) * computedWidth);
var result = encoder.result;
var resultData = encoder.resultData;
var i_1,i_4; //for performance improvments
var xlatL2U = wdi.xlatL2U;
var pr, pg, pb;
var prevCorrelatedR, prevCorrelatedG, prevCorrelatedB;
var eatbits = encoder.eatbits;
var tabrand = wdi.JSQuic.tabrand;
var decode_run = wdi.JSQuic.decode_run;
var ret, codewordlen;
var cnt = encoder.cnt;
var pxCnt = encoder.pxCnt;
var bppmask = wdi.JSQuic.bppmask;
if (!i) {
pr = UNCOMPRESS_ONE_0(channel_r, encoder, bpc_mask, offset);
pg = UNCOMPRESS_ONE_0(channel_g, encoder, bpc_mask, offset);
pb = UNCOMPRESS_ONE_0(channel_b, encoder, bpc_mask, offset);
prevCorrelatedR = correlate_row_r[0];
prevCorrelatedG = correlate_row_g[0];
prevCorrelatedB = correlate_row_b[0];
//inlined appendPixel
resultData[pxCnt++] =
(255 << 24) | // alpha
(pb << 16) | // blue
(pg << 8) | // green
pr; // red
cnt += 4;
if (rgb_state.waitcnt) {
--rgb_state.waitcnt;
} else {
rgb_state.waitcnt = (tabrand.call(wdi.JSQuic, rgb_state) & waitmask);
UPDATE_MODEL(0, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
}
stopidx = ++i + rgb_state.waitcnt;
} else {
stopidx = i + rgb_state.waitcnt;
pr = result[cnt-4];
pg = result[cnt-3];
pb = result[cnt-2];
prevCorrelatedR = correlate_row_r[i-1];
prevCorrelatedG = correlate_row_g[i-1];
prevCorrelatedB = correlate_row_b[i-1];
}
while(true) {
while (stopidx < end) {
i_4 = offset + i*4;
for (; i <= stopidx; i++) {
rle = RLE_PRED(i, encoder, i_4, currentOffset, run_index);
if(rle) break;
i_1 = i-1;
/////////////////////// INLINING UNCOMPRESS_ONE
//r
ret = golomb_decoding(buckets_ptrs_r[prevCorrelatedR].bestcode,
encoder.io_word, bppmask);
prevCorrelatedR = ret[0];
codewordlen = ret[1];
correlate_row_r[i] = prevCorrelatedR;
pr = ((((xlatL2U[prevCorrelatedR] +
((pr +
result[i_4]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
//g
ret = golomb_decoding(buckets_ptrs_g[prevCorrelatedG].bestcode,
encoder.io_word, bppmask);
prevCorrelatedG = ret[0];
codewordlen = ret[1];
correlate_row_g[i] = prevCorrelatedG;
pg = ((((xlatL2U[prevCorrelatedG] +
((pg +
result[i_4 + 1]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
//b
ret = golomb_decoding(buckets_ptrs_b[prevCorrelatedB].bestcode,
encoder.io_word, bppmask);
prevCorrelatedB = ret[0];
codewordlen = ret[1];
correlate_row_b[i] = prevCorrelatedB;
pb = ((((xlatL2U[prevCorrelatedB] +
((pb +
result[i_4 + 2]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
////////////////////// END INLINING
/**
pr = UNCOMPRESS_ONE(channel_r, i, i_1, i_4, bpc_mask, encoder, correlate_row_r, offset, result, 0, xlatL2U, buckets_ptrs_r, pr);
pg = UNCOMPRESS_ONE(channel_g, i, i_1, i_4, bpc_mask, encoder, correlate_row_g, offset, result, 1, xlatL2U, buckets_ptrs_g, pg);
pb = UNCOMPRESS_ONE(channel_b, i, i_1, i_4, bpc_mask, encoder, correlate_row_b, offset, result, 2, xlatL2U, buckets_ptrs_b, pb);
**/
//this is inlined appendPixel
resultData[pxCnt++] =
(255 << 24) | // alpha
(pb << 16) | // blue
(pg << 8) | // green
pr; // red
cnt += 4;
i_4 += 4;
}
if(!rle) {
UPDATE_MODEL(stopidx, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
stopidx = i + (tabrand.call(wdi.JSQuic, rgb_state) & waitmask);
} else {
break;
}
}
if(!rle) {
i_4 = offset + i*4;
for (; i < end; i++) {
rle = RLE_PRED(i, encoder, i_4, currentOffset, run_index);
if(rle) break;
i_1 = i-1;
////////////////////// INLINING UNCOMPRESS_ONE
//r
ret = golomb_decoding(buckets_ptrs_r[prevCorrelatedR].bestcode, encoder.io_word, bppmask);
prevCorrelatedR = ret[0];
codewordlen = ret[1];
correlate_row_r[i] = prevCorrelatedR;
pr = ((((xlatL2U[prevCorrelatedR] +
((pr +
result[i_4]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
//g
ret = golomb_decoding(buckets_ptrs_g[prevCorrelatedG].bestcode, encoder.io_word, bppmask);
prevCorrelatedG = ret[0];
codewordlen = ret[1];
correlate_row_g[i] = prevCorrelatedG;
pg = ((((xlatL2U[prevCorrelatedG] +
((pg +
result[i_4 + 1]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
//b
ret = golomb_decoding(buckets_ptrs_b[prevCorrelatedB].bestcode, encoder.io_word, bppmask);
prevCorrelatedB = ret[0];
codewordlen = ret[1];
correlate_row_b[i] = prevCorrelatedB;
pb = ((((xlatL2U[prevCorrelatedB] +
((pb +
result[i_4 + 2]) >>> 1))
& bpc_mask
)));
eatbits.call(encoder, codewordlen);
///////////////////// END INLINING
/**
pr = UNCOMPRESS_ONE(channel_r, i, i_1, i_4, bpc_mask, encoder, correlate_row_r, offset, result, 0, xlatL2U, buckets_ptrs_r, pr);
pg = UNCOMPRESS_ONE(channel_g, i, i_1, i_4, bpc_mask, encoder, correlate_row_g, offset, result, 1, xlatL2U, buckets_ptrs_g, pg);
pb = UNCOMPRESS_ONE(channel_b, i, i_1, i_4, bpc_mask, encoder, correlate_row_b, offset, result, 2, xlatL2U, buckets_ptrs_b, pb);
**/
//this is inlined apendPixel
resultData[pxCnt++] =
(255 << 24) | // alpha
(pb << 16) | // blue
(pg << 8) | // green
pr; // red
cnt += 4;
i_4 += 4;
}
if(!rle) {
rgb_state.waitcnt = stopidx - end;
encoder.cnt = cnt;
encoder.pxCnt = pxCnt;
return;
}
}
//RLE
rgb_state.waitcnt = stopidx - i;
run_index = i;
run_end = i + decode_run.call(wdi.JSQuic, encoder);
for (; i < run_end; i++) {
//this is inlined appendPixel
resultData[pxCnt++] =
(255 << 24) | // alpha
(pb << 16) | // blue
(pg << 8) | // green
pr; // red
cnt += 4;
}
if (i === end) {
encoder.cnt = cnt;
encoder.pxCnt = pxCnt;
return;
}
i_1 = i-1;
prevCorrelatedR = correlate_row_r[i_1];
prevCorrelatedG = correlate_row_g[i_1];
prevCorrelatedB = correlate_row_b[i_1];
stopidx = i + rgb_state.waitcnt;
rle = false;
//END RLE
}
}
function quic_uncompress_row0(encoder, channels, bpc, type) {
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
var pos = 0;
var width = encoder.width;
while ((wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) && (encoder.rgb_state.wmileft <= width)) {
if (encoder.rgb_state.wmileft) {
uncompress_row0_seg(
encoder,
pos,
pos + encoder.rgb_state.wmileft,
wdi.JSQuic.bppmask[encoder.rgb_state.wmidx],
bpc,
bpc_mask,
type
);
pos += encoder.rgb_state.wmileft;
width -= encoder.rgb_state.wmileft;
}
encoder.rgb_state.wmidx++;
wdi.JSQuic.set_wm_trigger(encoder.rgb_state);
encoder.rgb_state.wmileft = wdi.JSQuic.wminext;
}
if (width) {
uncompress_row0_seg(
encoder,
pos,
pos + width,
wdi.JSQuic.bppmask[encoder.rgb_state.wmidx],
bpc,
bpc_mask,
type
);
if (wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) {
encoder.rgb_state.wmileft -= width;
}
}
}
function uncompress_row0_seg(encoder, i, end, waitmask, bpc, bpc_mask, type) {
var channel_r = encoder.channels[0];
var channel_g = encoder.channels[1];
var channel_b = encoder.channels[2];
var correlate_row_r = channel_r.correlate_row;
var correlate_row_g = channel_g.correlate_row;
var correlate_row_b = channel_b.correlate_row;
var stopidx = 0;
var pr, pg, pb;
if (!i) {
pr = UNCOMPRESS_ONE_ROW0_0(channel_r);
pg = UNCOMPRESS_ONE_ROW0_0(channel_g);
pb = UNCOMPRESS_ONE_ROW0_0(channel_b);
encoder.appendPixel(pr, pg, pb);
if (encoder.rgb_state.waitcnt) {
--encoder.rgb_state.waitcnt;
} else {
encoder.rgb_state.waitcnt = (wdi.JSQuic.tabrand(encoder.rgb_state) & waitmask);
UPDATE_MODEL(0, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
}
stopidx = ++i + encoder.rgb_state.waitcnt;
} else {
stopidx = i + encoder.rgb_state.waitcnt;
}
while (stopidx < end) {
for (; i <= stopidx; i++) {
pr = UNCOMPRESS_ONE_ROW0(channel_r, i, bpc_mask, encoder, correlate_row_r, pr);
pg = UNCOMPRESS_ONE_ROW0(channel_g, i, bpc_mask, encoder, correlate_row_g, pg);
pb = UNCOMPRESS_ONE_ROW0(channel_b, i, bpc_mask, encoder, correlate_row_b, pb);
encoder.appendPixel(pr, pg, pb);
}
UPDATE_MODEL(stopidx, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
stopidx = i + (wdi.JSQuic.tabrand(encoder.rgb_state) & waitmask);
}
for (; i < end; i++) {
pr = UNCOMPRESS_ONE_ROW0(channel_r, i, bpc_mask, encoder, correlate_row_r, pr);
pg = UNCOMPRESS_ONE_ROW0(channel_g, i, bpc_mask, encoder, correlate_row_g, pg);
pb =UNCOMPRESS_ONE_ROW0(channel_b, i, bpc_mask, encoder, correlate_row_b, pb);
encoder.appendPixel(pr, pg, pb);
}
encoder.rgb_state.waitcnt = stopidx - end;
}
function UNCOMPRESS_ONE_0(channel, encoder, bpc_mask, offset) {
var ret, codewordlen;
channel.oldFirst = channel.correlate_row[0];
ret = golomb_decoding(find_bucket(channel,
channel.correlate_row[0]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
channel.correlate_row[0] = ret[0];
codewordlen = ret[1];
var residuum = wdi.xlatL2U[channel.correlate_row[0]];
var prev = encoder.result[offset + channel.num_channel]; //PIXEL_B
var resultpixel = ((residuum + prev) & bpc_mask);
encoder.eatbits(codewordlen);
return resultpixel;
}
function UNCOMPRESS_ONE(channel, i, i_1, i_4, bpc_mask, encoder, correlate_row, offset, result, num_channel, xlatL2U, buckets_ptrs, prev_pixel) {
var ret, codewordlen;
ret = golomb_decoding(buckets_ptrs[correlate_row[i_1]].bestcode, encoder.io_word, wdi.JSQuic.bppmask);
var data = ret[0];
codewordlen = ret[1];
correlate_row[i] = data;
var ret = ((((xlatL2U[data] +
((prev_pixel +
result[offset + (i_4) + num_channel]) >>> 1))
& bpc_mask
)));
encoder.eatbits(codewordlen);
return ret;
}
function UNCOMPRESS_ONE_ROW0_0(channel) {
var ret, codewordlen;
var encoder = channel.encoder;
ret = golomb_decoding(find_bucket(channel, 0).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
channel.correlate_row[0] = ret[0]
codewordlen = ret[1];
var ret = wdi.xlatL2U[channel.correlate_row[0]];
encoder.eatbits(codewordlen);
return ret;
}
function UNCOMPRESS_ONE_ROW0(channel, i, bpc_mask, encoder, correlate_row, prev_pixel) {
var ret, codewordlen;
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
correlate_row[i] = ret[0];
codewordlen = ret[1];
var ret = CORELATE_0(encoder, channel, i, bpc_mask, correlate_row, prev_pixel);
encoder.eatbits(codewordlen);
return ret;
}
function CORELATE_0(encoder, channel, curr, bpc_mask, correlate_row, prev_pixel) {
return ((wdi.xlatL2U[correlate_row[curr]] + prev_pixel) & bpc_mask);
}
function PIXEL_A(encoder) {
return encoder.result[encoder.cnt - 4];
}
function PIXEL_B(channel, encoder, pos, offset) {
return encoder.result[offset + (pos*4) + channel.num_channel];
}

View File

@ -0,0 +1,698 @@
wdi.LZSS = {
LZ_IMAGE_TYPE_INVALID: 0,
LZ_IMAGE_TYPE_PLT1_LE: 1,
LZ_IMAGE_TYPE_PLT1_BE: 2,
LZ_IMAGE_TYPE_PLT4_LE: 3,
LZ_IMAGE_TYPE_PLT4_BE: 4,
LZ_IMAGE_TYPE_PLT8: 5,
LZ_IMAGE_TYPE_RGB16: 6,
LZ_IMAGE_TYPE_RGB24: 7,
LZ_IMAGE_TYPE_RGB32: 8,
LZ_IMAGE_TYPE_RGBA: 9,
LZ_IMAGE_TYPE_XXXA: 10,
LZPALETTE_FLAG_PAL_CACHE_ME: 1,
LZPALETTE_FLAG_PAL_FROM_CACHE: 2,
LZPALETTE_FLAG_TOP_DOWN: 4,
PLT_PIXELS_PER_BYTE: [0, 8, 8, 2, 2, 1],
PLT1_MASK: [1, 2, 4, 8, 16, 32, 64, 128],
copy_pixel: function(op, color, out_buf) {
out_buf[(op) + 0] = color.r;
out_buf[(op) + 1] = color.g;
out_buf[(op) + 2] = color.b;
},
lz_rgb32_decompress_rgb: function(arr) {
//TODO: global alpha and uncouple code
var encoder = 0;
var op = 0;
var ctrl;
var in_buf = new Uint8Array(arr);
var format = in_buf[encoder++];
var opaque = in_buf[encoder++];
var type = in_buf[encoder++];
encoder++; //padding
var low = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
encoder += 2;
var high = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
encoder += 2;
var len = high*Math.pow(16, 4)+low;
var buf = new ArrayBuffer(len);
var buf8 = new Uint8Array(buf);
var data = new Uint32Array(buf);
var out_buf_len = len/4;
var code, ref, len, ofs, ref_4, b_4, b;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
ref = op;
len = ctrl >> 5;
ofs = ((ctrl & 31) << 8);
if (ctrl > 31) { //>=32
len--;
if (len === 6) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 1;
ofs += 1;
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
b = ref;
b_4 = b*4;
for (; len; --len) {
data[op] =
(255 << 24) | // alpha
(buf8[(b_4)+2] << 16) | // blue
(buf8[(b_4)+1] << 8) | // green
buf8[(b_4)]; // red
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
ref_4 = ref*4;
data[op] =
(255 << 24) | // alpha
(buf8[(ref_4)+2] << 16) | // blue
(buf8[(ref_4)+1] << 8) | // green
buf8[(ref_4)]; // red
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
data[op] =
(255 << 24) | // alpha
(in_buf[encoder] << 16) | // blue
(in_buf[encoder + 1] << 8) | // green
in_buf[encoder + 2]; // red
encoder += 3;
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
data[op] =
(255 << 24) | // alpha
(in_buf[encoder] << 16) | // blue
(in_buf[encoder + 1] << 8) | // green
in_buf[encoder + 2]; // red
encoder += 3;
op++;
}
}
}
if (type === this.LZ_IMAGE_TYPE_RGBA && !opaque) {
op = 0;
ctrl = null;
encoder--;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
var ref = op;
var len = ctrl >> 5;
var ofs = ((ctrl & 31) << 8);
var op_4 = op*4;
if (ctrl >= 32) {
var code;
len--;
if (len === 7 - 1) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 3;
ofs += 1;
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
var b = ref;
for (; len; --len) {
op_4 = op*4;
//COPY_PIXEL
buf8[(op_4) + 3] = buf8[(b*4)+3];
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
op_4 = op*4;
buf8[(op_4) + 3] = buf8[(ref*4)+3];
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
buf8[(op_4) + 3] = in_buf[encoder++];
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
op_4 = op*4; // faster?
buf8[(op_4) + 3] = in_buf[encoder++];
op++;
}
}
}
}
return buf;
},
lz_rgb32_decompress: function(in_buf, at, out_buf, type, default_alpha, palette, opaque) {
//TODO: global alpha and uncouple code
var encoder = at;
var op = 0;
var ctrl;
var out_buf_len = out_buf.length/4;
var is_rgba = type === this.LZ_IMAGE_TYPE_RGBA?true:false;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
var ref = op;
var len = ctrl >> 5;
var ofs = ((ctrl & 31) << 8);
var op_4 = op*4;
if (ctrl >= 32) {
var code;
len--;
if (len === 7 - 1) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 1;
if (is_rgba || palette)
len += 2;
ofs += 1;
//CAST_PLT_DISTANCE ofs and len
if (type === this.LZ_IMAGE_TYPE_PLT4_LE || type === this.LZ_IMAGE_TYPE_PLT4_BE) {
ofs = ofs*2;
len = len*2;
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE || type === this.LZ_IMAGE_TYPE_PLT1_LE) {
ofs = ofs*8;
len = len*8;
}
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
var b = ref;
for (; len; --len) {
op_4 = op*4;
//COPY_PIXEL
if (is_rgba)
{
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = out_buf[(b*4)+3];
}
}
else
{
for (var i = 0; i < 4; i++)
out_buf[(op_4) + i] = out_buf[(b*4)+i];
}
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
op_4 = op*4;
if (is_rgba)
{
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = out_buf[(ref*4)+3];
}
}
else
{
for (i = 0; i < 4; i++)
out_buf[(op_4) + i] = out_buf[(ref*4)+i];
}
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
if (is_rgba) {
if(opaque) {
out_buf[(op_4) + 3] = 255;encoder++;
} else {
out_buf[(op_4) + 3] = in_buf[encoder++];
}
} else if (palette) {
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 0; i < 8; i++) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i < 7)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 7; i >= 0; i--) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i > 0)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
var oct = in_buf[encoder++];
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
var oct = in_buf[encoder++];
var bits1 = ((oct >>> 4) & 0x0f);
var bits2 = oct & 0x0f;
var spiceColor = palette[bits1];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[bits2];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
var posPal = in_buf[encoder++];
var spiceColor = palette[posPal];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
}
} else {
out_buf[(op_4) + 0] = in_buf[encoder + 2];
out_buf[(op_4) + 1] = in_buf[encoder + 1];
out_buf[(op_4) + 2] = in_buf[encoder + 0];
if (default_alpha)
out_buf[(op_4) + 3] = 255;
encoder += 3;
}
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
op_4 = op*4; // faster?
if (is_rgba) {
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = in_buf[encoder++];
}
} else if (palette) {
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 0; i < 8; i++) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i < 7)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 7; i >=0; i--) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i > 0)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
var oct = in_buf[encoder++];
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
var oct = in_buf[encoder++];
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
var posPal = in_buf[encoder++];
var spiceColor = palette[posPal];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
}
} else {
out_buf[(op_4) + 0] = in_buf[encoder + 2];
out_buf[(op_4) + 1] = in_buf[encoder + 1];
out_buf[(op_4) + 2] = in_buf[encoder + 0];
if (default_alpha)
out_buf[(op_4) + 3] = 255;
encoder += 3;
}
op++;
}
}
}
return encoder - 1;
},
convert_spice_lz_to_web: function(context, data, imageDescriptor, opaque) { //TODO: refactor this shit code
// var aux = data.toJSArray();
var format = imageDescriptor.type;
data = data.toJSArray(); //this old functions has no support for typed arrays...
if (format === wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_PLT) {
var flags = wdi.SpiceObject.bytesToInt8(data.splice(0, 1));
if (flags === this.LZPALETTE_FLAG_PAL_FROM_CACHE) {
var header = data.splice(0, 12);
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var palette_id = wdi.SpiceObject.bytesToInt64(header.splice(0, 8));
header = data;
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
} else if (flags === this.LZPALETTE_FLAG_PAL_CACHE_ME) {
var imageHeaders = imageDescriptor.offset + 1; //+1 because of the Flags byte
var currentHeaders = 36;
var header = data.splice(0, currentHeaders);
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var palette_offset = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var spliceInit = palette_offset-imageHeaders-currentHeaders;
//LZ Compression headers with its magic
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var palette_id = wdi.SpiceObject.bytesToInt64(data.splice(spliceInit, 8));
var num_palettes = wdi.SpiceObject.bytesToInt16(data.splice(spliceInit, 2));
var palette = [];
for (var i = 0; i < num_palettes; i++) {
var queue = new wdi.Queue();
queue.setData(data.splice(spliceInit, 4));
palette.push(new wdi.SpiceColor().demarshall(queue));
}
wdi.ImageCache.addPalette(palette_id, palette);
} else {
wdi.Debug.error('Unimplemented lz palette top down');
}
var palette = wdi.ImageCache.getPalette(palette_id);
}
if (type !== this.LZ_IMAGE_TYPE_RGB32 && type !== this.LZ_IMAGE_TYPE_RGBA &&
type !== this.LZ_IMAGE_TYPE_RGB24 && type !== this.LZ_IMAGE_TYPE_PLT8 &&
type !== this.LZ_IMAGE_TYPE_PLT1_LE && type !== this.LZ_IMAGE_TYPE_PLT1_BE &&
type !== this.LZ_IMAGE_TYPE_PLT4_LE && type !== this.LZ_IMAGE_TYPE_PLT4_BE) {
return false;
}
if (palette) {
var ret = context.createImageData(stride*this.PLT_PIXELS_PER_BYTE[type], height);
// if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
// this.lz_rgb32_plt1_be_decompress(data, ret.data, palette);
// } else {
this.lz_rgb32_decompress(data, 0, ret.data, type, type !== this.LZ_IMAGE_TYPE_RGBA, palette);
// }
var tmpCanvas = wdi.graphics.getNewTmpCanvas(width, height);
tmpCanvas.getContext('2d').putImageData(ret, 0, 0, 0, 0, width, height);
ret = tmpCanvas;
} else {
var arr = new ArrayBuffer(data.length+8);
var u8 = new Uint8Array(arr);
u8[0] = 1;
u8[1] = opaque;
u8[2] = type;
u8[3] = 0;
var number = ret.data.length;
for (var i = 0;i < 4;i++) {//iterations because of javascript number size
u8[4+i] = number & (255);//Get only the last byte
number = number >> 8;//Remove the last byte
}
u8.set(data, 8);
var result = new Uint8ClampedArray(this.lz_rgb32_decompress_rgb(arr));
ret = new ImageData(result, width, height);
ret = wdi.graphics.getImageFromData(ret);
}
if(!top_down) {
//TODO: PERFORMANCE:
ret = wdi.RasterOperation.flip(ret);
}
return ret;
},
demarshall_rgb: function(data) {
var header = data.splice(0, 32);
return {
length: wdi.SpiceObject.bytesToInt32(header.splice(0,4)),
magic: wdi.SpiceObject.bytesToStringBE(header.splice(0,4)),
version: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
type: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
width: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
height: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
stride: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
top_down: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4))
}
},
lz_rgb32_plt1_be_decompress: function(in_buf, out_buf, palette) {
var encoder = 0;
var op = 0;
var ctrl;
var out_buf_len = out_buf.length/4;
var ref, len, ofs, next, ref_4, oct, foreColor, backColor, i;
var type = this.LZ_IMAGE_TYPE_PLT1_BE;
var pix_per_byte = this.PLT_PIXELS_PER_BYTE[type];
var pre_255_24 = 255 << 24;
var pre_31_8_plus255 = (31 << 8) + 255; //8191 === 13 bits to 1
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++]) {
ref = op;
len = ctrl >> 5;
ofs = ((ctrl & 31) << 8);
if (ctrl > 31) {
if (len === 7) {
do {
next = in_buf[encoder++];
len += next;
} while (next === 255);
}
ofs += in_buf[encoder++];
if (ofs === pre_31_8_plus255) {
ofs += in_buf[encoder++] << 8 + in_buf[encoder++];
}
//CAST_PLT_DISTANCE ofs and len
len = (len + 2) * pix_per_byte;
ref -= (ofs + 1) * pix_per_byte;
if (ref === (op - 1)) {
ref_4 = ref * 4;
while (len-- !== 0) {
//COPY_PIXEL
op_4 = op * 4;
out_buf[op_4] = out_buf[ref_4];
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
op++;
}
} else {
while (len-- !== 0) {
//COPY_REF_PIXEL
op_4 = op * 4;
ref_4 = ref * 4;
out_buf[op_4] = out_buf[ref_4];
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
while (ctrl-- !== -1) {
//COPY_COMP_PIXEL
op_4 = op * 4; // faster?
oct = in_buf[encoder++];
foreColor = palette[1];
backColor = palette[0];
for (i = 7; i >=0; i--) {
op_4 = op * 4;
if (oct & this.PLT1_MASK[i]) {
out_buf[op_4 + 0] = foreColor.r;
out_buf[op_4 + 1] = foreColor.g;
out_buf[op_4 + 2] = foreColor.b;
} else {
out_buf[op_4 + 0] = backColor.r;
out_buf[op_4 + 1] = backColor.g;
out_buf[op_4 + 2] = backColor.b;
}
out_buf[(op_4) + 3] = 255;
op++;
}
}
}
}
}
};

View File

@ -0,0 +1,256 @@
"use strict";
/*
Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
This file is part of spice-html5.
spice-html5 is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
spice-html5 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
*/
/*----------------------------------------------------------------------------
** crc logic from rfc2083 ported to Javascript
**--------------------------------------------------------------------------*/
var rfc2083_crc_table = Array(256);
var rfc2083_crc_table_computed = 0;
/* Make the table for a fast CRC. */
function rfc2083_make_crc_table()
{
var c;
var n, k;
for (n = 0; n < 256; n++)
{
c = n;
for (k = 0; k < 8; k++)
{
if (c & 1)
c = ((0xedb88320 ^ (c >>> 1)) >>> 0) & 0xffffffff;
else
c = c >>> 1;
}
rfc2083_crc_table[n] = c;
}
rfc2083_crc_table_computed = 1;
}
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
function rfc2083_update_crc(crc, u8buf, at, len)
{
var c = crc;
var n;
if (!rfc2083_crc_table_computed)
rfc2083_make_crc_table();
for (n = 0; n < len; n++)
{
c = rfc2083_crc_table[(c ^ u8buf[at + n]) & 0xff] ^ (c >>> 8);
}
return c;
}
function rfc2083_crc(u8buf, at, len)
{
return rfc2083_update_crc(0xffffffff, u8buf, at, len) ^ 0xffffffff;
}
function crc32(mb, at, len)
{
var u8 = new Uint8Array(mb);
return rfc2083_crc(u8, at, len);
}
function PngIHDR(width, height)
{
this.width = width;
this.height = height;
this.depth = 8;
this.type = 6;
this.compression = 0;
this.filter = 0;
this.interlace = 0;
}
PngIHDR.prototype =
{
to_buffer: function(a, at)
{
at = at || 0;
var orig = at;
var dv = new SpiceDataView(a);
dv.setUint32(at, this.buffer_size() - 12); at += 4;
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
dv.setUint8(at, 'H'.charCodeAt(0)); at++;
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
dv.setUint8(at, 'R'.charCodeAt(0)); at++;
dv.setUint32(at, this.width); at += 4;
dv.setUint32(at, this.height); at += 4;
dv.setUint8(at, this.depth); at++;
dv.setUint8(at, this.type); at++;
dv.setUint8(at, this.compression); at++;
dv.setUint8(at, this.filter); at++;
dv.setUint8(at, this.interlace); at++;
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
return at;
},
buffer_size: function()
{
return 12 + 13;
}
}
function adler()
{
this.s1 = 1;
this.s2 = 0;
}
adler.prototype.update = function(b)
{
this.s1 += b;
this.s1 %= 65521;
this.s2 += this.s1;
this.s2 %= 65521;
}
function PngIDAT(width, height, bytes)
{
if (bytes.byteLength > 65535)
{
throw new Error("Cannot handle more than 64K");
}
this.data = bytes;
this.width = width;
this.height = height;
}
PngIDAT.prototype =
{
to_buffer: function(a, at)
{
at = at || 0;
var orig = at;
var x, y, i, j;
var dv = new SpiceDataView(a);
var zsum = new adler();
dv.setUint32(at, this.buffer_size() - 12); at += 4;
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
dv.setUint8(at, 'A'.charCodeAt(0)); at++;
dv.setUint8(at, 'T'.charCodeAt(0)); at++;
/* zlib header. */
dv.setUint8(at, 0x78); at++;
dv.setUint8(at, 0x01); at++;
/* Deflate header. Specifies uncompressed, final bit */
dv.setUint8(at, 0x80); at++;
dv.setUint16(at, this.data.byteLength + this.height); at += 2;
dv.setUint16(at, ~(this.data.byteLength + this.height)); at += 2;
var u8 = new Uint8Array(this.data);
for (i = 0, y = 0; y < this.height; y++)
{
/* Filter type 0 - uncompressed */
dv.setUint8(at, 0); at++;
zsum.update(0);
for (x = 0; x < this.width && i < this.data.byteLength; x++)
{
zsum.update(u8[i]);
dv.setUint8(at, u8[i++]); at++;
zsum.update(u8[i]);
dv.setUint8(at, u8[i++]); at++;
zsum.update(u8[i]);
dv.setUint8(at, u8[i++]); at++;
zsum.update(u8[i]);
dv.setUint8(at, u8[i++]); at++;
}
}
/* zlib checksum. */
dv.setUint16(at, zsum.s2); at+=2;
dv.setUint16(at, zsum.s1); at+=2;
/* FIXME - something is not quite right with the zlib code;
you get an error from libpng if you open the image in
gimp. But it works, so it's good enough for now... */
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
return at;
},
buffer_size: function()
{
return 12 + this.data.byteLength + this.height + 4 + 2 + 1 + 2 + 2;
}
}
function PngIEND()
{
}
PngIEND.prototype =
{
to_buffer: function(a, at)
{
at = at || 0;
var orig = at;
var i;
var dv = new SpiceDataView(a);
dv.setUint32(at, this.buffer_size() - 12); at += 4;
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
dv.setUint8(at, 'E'.charCodeAt(0)); at++;
dv.setUint8(at, 'N'.charCodeAt(0)); at++;
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
return at;
},
buffer_size: function()
{
return 12;
}
}
function create_rgba_png(width, height, bytes)
{
var i;
var ihdr = new PngIHDR(width, height);
var idat = new PngIDAT(width, height, bytes);
var iend = new PngIEND;
var mb = new ArrayBuffer(ihdr.buffer_size() + idat.buffer_size() + iend.buffer_size());
var at = ihdr.to_buffer(mb);
at = idat.to_buffer(mb, at);
at = iend.to_buffer(mb, at);
var u8 = new Uint8Array(mb);
var str = "";
for (i = 0; i < at; i++)
{
str += "%";
if (u8[i] < 16)
str += "0";
str += u8[i].toString(16);
}
return "%89PNG%0D%0A%1A%0A" + str;
}

File diff suppressed because one or more lines are too long

8829
js/spice-web-client/lib/jquery-2.0.3.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
* Licensed under the MIT License (LICENSE.txt).
*
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
* Thanks to: Seamus Leahy for adding deltaX and deltaY
*
* Version: 3.0.6
*
* Requires: 1.2.2+
*/
(function($) {
var types = ['DOMMouseScroll', 'mousewheel'];
if ($.event.fixHooks) {
for ( var i=types.length; i; ) {
$.event.fixHooks[ types[--i] ] = $.event.mouseHooks;
}
}
$.event.special.mousewheel = {
setup: function() {
if ( this.addEventListener ) {
for ( var i=types.length; i; ) {
this.addEventListener( types[--i], handler, false );
}
} else {
this.onmousewheel = handler;
}
},
teardown: function() {
if ( this.removeEventListener ) {
for ( var i=types.length; i; ) {
this.removeEventListener( types[--i], handler, false );
}
} else {
this.onmousewheel = null;
}
}
};
$.fn.extend({
mousewheel: function(fn) {
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
},
unmousewheel: function(fn) {
return this.unbind("mousewheel", fn);
}
});
function handler(event) {
var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
event = $.event.fix(orgEvent);
event.type = "mousewheel";
// Old school scrollwheel delta
if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta/120; }
if ( orgEvent.detail ) { delta = -orgEvent.detail/3; }
// New school multidimensional scroll (touchpads) deltas
deltaY = delta;
// Gecko
if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
deltaY = 0;
deltaX = -1*delta;
}
// Webkit
if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
// Add event and delta to the front of the arguments
args.unshift(event, delta, deltaX, deltaY);
return ($.event.dispatch || $.event.handle).apply(this, args);
}
})(jQuery);

View File

@ -0,0 +1,559 @@
// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// 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 encryptionBigInteger(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 encryptionBigInteger
function nbi() { return new encryptionBigInteger(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")) {
encryptionBigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
encryptionBigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
encryptionBigInteger.prototype.am = am3;
dbits = 28;
}
encryptionBigInteger.prototype.DB = dbits;
encryptionBigInteger.prototype.DM = ((1<<dbits)-1);
encryptionBigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
encryptionBigInteger.prototype.FV = Math.pow(2,BI_FP);
encryptionBigInteger.prototype.F1 = BI_FP-dbits;
encryptionBigInteger.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) encryptionBigInteger.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(); encryptionBigInteger.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 (this.s<0)?-r: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) encryptionBigInteger.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);
}
encryptionBigInteger.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) encryptionBigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) encryptionBigInteger.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(encryptionBigInteger.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(encryptionBigInteger.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 encryptionBigInteger.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
encryptionBigInteger.prototype.copyTo = bnpCopyTo;
encryptionBigInteger.prototype.fromInt = bnpFromInt;
encryptionBigInteger.prototype.fromString = bnpFromString;
encryptionBigInteger.prototype.clamp = bnpClamp;
encryptionBigInteger.prototype.dlShiftTo = bnpDLShiftTo;
encryptionBigInteger.prototype.drShiftTo = bnpDRShiftTo;
encryptionBigInteger.prototype.lShiftTo = bnpLShiftTo;
encryptionBigInteger.prototype.rShiftTo = bnpRShiftTo;
encryptionBigInteger.prototype.subTo = bnpSubTo;
encryptionBigInteger.prototype.multiplyTo = bnpMultiplyTo;
encryptionBigInteger.prototype.squareTo = bnpSquareTo;
encryptionBigInteger.prototype.divRemTo = bnpDivRemTo;
encryptionBigInteger.prototype.invDigit = bnpInvDigit;
encryptionBigInteger.prototype.isEven = bnpIsEven;
encryptionBigInteger.prototype.exp = bnpExp;
// public
encryptionBigInteger.prototype.toString = bnToString;
encryptionBigInteger.prototype.negate = bnNegate;
encryptionBigInteger.prototype.abs = bnAbs;
encryptionBigInteger.prototype.compareTo = bnCompareTo;
encryptionBigInteger.prototype.bitLength = bnBitLength;
encryptionBigInteger.prototype.mod = bnMod;
encryptionBigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
encryptionBigInteger.ZERO = nbv(0);
encryptionBigInteger.ONE = nbv(1);

View File

@ -0,0 +1,656 @@
// Copyright (c) 2005-2009 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Extended JavaScript BN functions, required for RSA private ops.
// Version 1.1: new encryptionBigInteger("0", 10) returns "proper" zero
// Version 1.2: square() API, isProbablePrime fix
// (public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
// (public) return value as integer
function bnIntValue() {
if(this.s < 0) {
if(this.t == 1) return this[0]-this.DV;
else if(this.t == 0) return -1;
}
else if(this.t == 1) return this[0];
else if(this.t == 0) return 0;
// assumes 16 < DB < 32
return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
}
// (public) return value as byte
function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }
// (public) return value as short (assumes DB>=16)
function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; }
// (protected) return x s.t. r^x < DV
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }
// (public) 0 if this == 0, 1 if this > 0
function bnSigNum() {
if(this.s < 0) return -1;
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
else return 1;
}
// (protected) convert to radix string
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
return z.intValue().toString(b) + r;
}
// (protected) convert from radix string
function bnpFromRadix(s,b) {
this.fromInt(0);
if(b == null) b = 10;
var cs = this.chunkSize(b);
var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
for(var i = 0; i < s.length; ++i) {
var x = intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
this.dAddOffset(w,0);
}
if(mi) encryptionBigInteger.ZERO.subTo(this,this);
}
// (protected) alternate constructor
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new encryptionBigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(encryptionBigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(encryptionBigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new encryptionBigInteger(int,RNG)
var x = new Array(), t = a&7;
x.length = (a>>3)+1;
b.nextBytes(x);
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
this.fromString(x,256);
}
}
// (public) convert to bigendian byte array
function bnToByteArray() {
var i = this.t, r = new Array();
r[0] = this.s;
var p = this.DB-(i*this.DB)%8, d, k = 0;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
r[k++] = d|(this.s<<(this.DB-p));
while(i >= 0) {
if(p < 8) {
d = (this[i]&((1<<p)-1))<<(8-p);
d |= this[--i]>>(p+=this.DB-8);
}
else {
d = (this[i]>>(p-=8))&0xff;
if(p <= 0) { p += this.DB; --i; }
}
if((d&0x80) != 0) d |= -256;
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
if(k > 0 || d != this.s) r[k++] = d;
}
}
return r;
}
function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
// (protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
if(a.t < this.t) {
f = a.s&this.DM;
for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
r.t = this.t;
}
else {
f = this.s&this.DM;
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}
// (public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
// (public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
// (public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
// (public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
// (public) ~this
function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
r.t = this.t;
r.s = ~this.s;
return r;
}
// (public) this << n
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}
// (public) this >> n
function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}
// return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
if(x == 0) return -1;
var r = 0;
if((x&0xffff) == 0) { x >>= 16; r += 16; }
if((x&0xff) == 0) { x >>= 8; r += 8; }
if((x&0xf) == 0) { x >>= 4; r += 4; }
if((x&3) == 0) { x >>= 2; r += 2; }
if((x&1) == 0) ++r;
return r;
}
// (public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
if(this[i] != 0) return i*this.DB+lbit(this[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}
// return number of 1 bits in x
function cbit(x) {
var r = 0;
while(x != 0) { x &= x-1; ++r; }
return r;
}
// (public) return number of set bits
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
return r;
}
// (public) true iff nth bit is set
function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this[j]&(1<<(n%this.DB)))!=0);
}
// (protected) this op (1<<n)
function bnpChangeBit(n,op) {
var r = encryptionBigInteger.ONE.shiftLeft(n);
this.bitwiseTo(r,op,r);
return r;
}
// (public) this | (1<<n)
function bnSetBit(n) { return this.changeBit(n,op_or); }
// (public) this & ~(1<<n)
function bnClearBit(n) { return this.changeBit(n,op_andnot); }
// (public) this ^ (1<<n)
function bnFlipBit(n) { return this.changeBit(n,op_xor); }
// (protected) r = this + a
function bnpAddTo(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 > 0) r[i++] = c;
else if(c < -1) r[i++] = this.DV+c;
r.t = i;
r.clamp();
}
// (public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
// (public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
// (public) this^2
function bnSquare() { var r = nbi(); this.squareTo(r); return r; }
// (public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
// (public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
// (public) [this/a,this%a]
function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}
// (protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
this[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}
// (protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
if(n == 0) return;
while(this.t <= w) this[this.t++] = 0;
this[w] += n;
while(this[w] >= this.DV) {
this[w] -= this.DV;
if(++w >= this.t) this[this.t++] = 0;
++this[w];
}
}
// A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }
NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;
// (public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }
// (protected) r = lower n words of "this * a", a.t <= n
// "this" should be the larger one if appropriate.
function bnpMultiplyLowerTo(a,n,r) {
var i = Math.min(this.t+a.t,n);
r.s = 0; // assumes a,this >= 0
r.t = i;
while(i > 0) r[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
r.clamp();
}
// (protected) r = "this * a" without lower n words, n > 0
// "this" should be the larger one if appropriate.
function bnpMultiplyUpperTo(a,n,r) {
--n;
var i = r.t = this.t+a.t-n;
r.s = 0; // assumes a,this >= 0
while(--i >= 0) r[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,r);
}
// Barrett modular reduction
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
encryptionBigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}
function barrettRevert(x) { return x; }
// x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;
// (public) this^e % m (HAC 14.85)
function bnModPow(e,m) {
var i = e.bitLength(), k, r = nbv(1), z;
if(i <= 0) return r;
else if(i < 18) k = 1;
else if(i < 48) k = 3;
else if(i < 144) k = 4;
else if(i < 768) k = 5;
else k = 6;
if(i < 8)
z = new Classic(m);
else if(m.isEven())
z = new Barrett(m);
else
z = new Montgomery(m);
// precomputation
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
g[1] = z.convert(this);
if(k > 1) {
var g2 = nbi();
z.sqrTo(g[1],g2);
while(n <= km) {
g[n] = nbi();
z.mulTo(g2,g[n-2],g[n]);
n += 2;
}
}
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
i = nbits(e[j])-1;
while(j >= 0) {
if(i >= k1) w = (e[j]>>(i-k1))&km;
else {
w = (e[j]&((1<<(i+1))-1))<<(k1-i);
if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
}
n = k;
while((w&1) == 0) { w >>= 1; --n; }
if((i -= n) < 0) { i += this.DB; --j; }
if(is1) { // ret == 1, don't bother squaring or multiplying it
g[w].copyTo(r);
is1 = false;
}
else {
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
z.mulTo(r2,g[w],r);
}
while(j >= 0 && (e[j]&(1<<i)) == 0) {
z.sqrTo(r,r2); t = r; r = r2; r2 = t;
if(--i < 0) { i = this.DB-1; --j; }
}
}
return z.revert(r);
}
// (public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
if(x.compareTo(y) >= 0) {
x.subTo(y,x);
x.rShiftTo(1,x);
}
else {
y.subTo(x,y);
y.rShiftTo(1,y);
}
}
if(g > 0) y.lShiftTo(g,y);
return y;
}
// (protected) this % n, n < 2^26
function bnpModInt(n) {
if(n <= 0) return 0;
var d = this.DV%n, r = (this.s<0)?n-1:0;
if(this.t > 0)
if(d == 0) r = this[0]%n;
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
return r;
}
// (public) 1/this % m (HAC 14.61)
function bnModInverse(m) {
var ac = m.isEven();
if((this.isEven() && ac) || m.signum() == 0) return encryptionBigInteger.ZERO;
var u = m.clone(), v = this.clone();
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
while(u.signum() != 0) {
while(u.isEven()) {
u.rShiftTo(1,u);
if(ac) {
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
a.rShiftTo(1,a);
}
else if(!b.isEven()) b.subTo(m,b);
b.rShiftTo(1,b);
}
while(v.isEven()) {
v.rShiftTo(1,v);
if(ac) {
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
c.rShiftTo(1,c);
}
else if(!d.isEven()) d.subTo(m,d);
d.rShiftTo(1,d);
}
if(u.compareTo(v) >= 0) {
u.subTo(v,u);
if(ac) a.subTo(c,a);
b.subTo(d,b);
}
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(encryptionBigInteger.ONE) != 0) return encryptionBigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
// (public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
for(i = 0; i < lowprimes.length; ++i)
if(x[0] == lowprimes[i]) return true;
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(encryptionBigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(encryptionBigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(encryptionBigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}
// protected
encryptionBigInteger.prototype.chunkSize = bnpChunkSize;
encryptionBigInteger.prototype.toRadix = bnpToRadix;
encryptionBigInteger.prototype.fromRadix = bnpFromRadix;
encryptionBigInteger.prototype.fromNumber = bnpFromNumber;
encryptionBigInteger.prototype.bitwiseTo = bnpBitwiseTo;
encryptionBigInteger.prototype.changeBit = bnpChangeBit;
encryptionBigInteger.prototype.addTo = bnpAddTo;
encryptionBigInteger.prototype.dMultiply = bnpDMultiply;
encryptionBigInteger.prototype.dAddOffset = bnpDAddOffset;
encryptionBigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
encryptionBigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
encryptionBigInteger.prototype.modInt = bnpModInt;
encryptionBigInteger.prototype.millerRabin = bnpMillerRabin;
// public
encryptionBigInteger.prototype.clone = bnClone;
encryptionBigInteger.prototype.intValue = bnIntValue;
encryptionBigInteger.prototype.byteValue = bnByteValue;
encryptionBigInteger.prototype.shortValue = bnShortValue;
encryptionBigInteger.prototype.signum = bnSigNum;
encryptionBigInteger.prototype.toByteArray = bnToByteArray;
encryptionBigInteger.prototype.equals = bnEquals;
encryptionBigInteger.prototype.min = bnMin;
encryptionBigInteger.prototype.max = bnMax;
encryptionBigInteger.prototype.and = bnAnd;
encryptionBigInteger.prototype.or = bnOr;
encryptionBigInteger.prototype.xor = bnXor;
encryptionBigInteger.prototype.andNot = bnAndNot;
encryptionBigInteger.prototype.not = bnNot;
encryptionBigInteger.prototype.shiftLeft = bnShiftLeft;
encryptionBigInteger.prototype.shiftRight = bnShiftRight;
encryptionBigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
encryptionBigInteger.prototype.bitCount = bnBitCount;
encryptionBigInteger.prototype.testBit = bnTestBit;
encryptionBigInteger.prototype.setBit = bnSetBit;
encryptionBigInteger.prototype.clearBit = bnClearBit;
encryptionBigInteger.prototype.flipBit = bnFlipBit;
encryptionBigInteger.prototype.add = bnAdd;
encryptionBigInteger.prototype.subtract = bnSubtract;
encryptionBigInteger.prototype.multiply = bnMultiply;
encryptionBigInteger.prototype.divide = bnDivide;
encryptionBigInteger.prototype.remainder = bnRemainder;
encryptionBigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
encryptionBigInteger.prototype.modPow = bnModPow;
encryptionBigInteger.prototype.modInverse = bnModInverse;
encryptionBigInteger.prototype.pow = bnPow;
encryptionBigInteger.prototype.gcd = bnGCD;
encryptionBigInteger.prototype.isProbablePrime = bnIsProbablePrime;
// JSBN-specific extension
encryptionBigInteger.prototype.square = bnSquare;
// encryptionBigInteger interfaces not implemented in jsbn:
// encryptionBigInteger(int signum, byte[] magnitude)
// double doubleValue()
// float floatValue()
// int hashCode()
// long longValue()
// static encryptionBigInteger valueOf(long val)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,775 @@
/*
Copyright (c) 2009 James Padolsey. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. 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.
THIS SOFTWARE IS PROVIDED BY James Padolsey ``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 James Padolsey 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.
The views and conclusions contained in the software and documentation are
those of the authors and should not be interpreted as representing official
policies, either expressed or implied, of James Padolsey.
AUTHOR James Padolsey (http://james.padolsey.com)
VERSION 1.03.0
UPDATED 29-10-2011
CONTRIBUTORS
David Waller
Benjamin Drucker
*/
var prettyPrint = (function(){
/* These "util" functions are not part of the core
functionality but are all necessary - mostly DOM helpers */
var util = {
el: function(type, attrs) {
/* Create new element */
var el = document.createElement(type), attr;
/*Copy to single object */
attrs = util.merge({}, attrs);
/* Add attributes to el */
if (attrs && attrs.style) {
var styles = attrs.style;
util.applyCSS( el, attrs.style );
delete attrs.style;
}
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
el[attr] = attrs[attr];
}
}
return el;
},
applyCSS: function(el, styles) {
/* Applies CSS to a single element */
for (var prop in styles) {
if (styles.hasOwnProperty(prop)) {
try{
/* Yes, IE6 SUCKS! */
el.style[prop] = styles[prop];
}catch(e){}
}
}
},
txt: function(t) {
/* Create text node */
return document.createTextNode(t);
},
row: function(cells, type, cellType) {
/* Creates new <tr> */
cellType = cellType || 'td';
/* colSpan is calculated by length of null items in array */
var colSpan = util.count(cells, null) + 1,
tr = util.el('tr'), td,
attrs = {
style: util.getStyles(cellType, type),
colSpan: colSpan,
onmouseover: function() {
var tds = this.parentNode.childNodes;
util.forEach(tds, function(cell){
if (cell.nodeName.toLowerCase() !== 'td') { return; }
util.applyCSS(cell, util.getStyles('td_hover', type));
});
},
onmouseout: function() {
var tds = this.parentNode.childNodes;
util.forEach(tds, function(cell){
if (cell.nodeName.toLowerCase() !== 'td') { return; }
util.applyCSS(cell, util.getStyles('td', type));
});
}
};
util.forEach(cells, function(cell){
if (cell === null) { return; }
/* Default cell type is <td> */
td = util.el(cellType, attrs);
if (cell.nodeType) {
/* IsDomElement */
td.appendChild(cell);
} else {
/* IsString */
td.innerHTML = util.shorten(cell.toString());
}
tr.appendChild(td);
});
return tr;
},
hRow: function(cells, type){
/* Return new <th> */
return util.row(cells, type, 'th');
},
table: function(headings, type){
headings = headings || [];
/* Creates new table: */
var attrs = {
thead: {
style:util.getStyles('thead',type)
},
tbody: {
style:util.getStyles('tbody',type)
},
table: {
style:util.getStyles('table',type)
}
},
tbl = util.el('table', attrs.table),
thead = util.el('thead', attrs.thead),
tbody = util.el('tbody', attrs.tbody);
if (headings.length) {
tbl.appendChild(thead);
thead.appendChild( util.hRow(headings, type) );
}
tbl.appendChild(tbody);
return {
/* Facade for dealing with table/tbody
Actual table node is this.node: */
node: tbl,
tbody: tbody,
thead: thead,
appendChild: function(node) {
this.tbody.appendChild(node);
},
addRow: function(cells, _type, cellType){
this.appendChild(util.row.call(util, cells, (_type || type), cellType));
return this;
}
};
},
shorten: function(str) {
var max = 40;
str = str.replace(/^\s\s*|\s\s*$|\n/g,'');
return str.length > max ? (str.substring(0, max-1) + '...') : str;
},
htmlentities: function(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
},
merge: function(target, source) {
/* Merges two (or more) objects,
giving the last one precedence */
if ( typeof target !== 'object' ) {
target = {};
}
for (var property in source) {
if ( source.hasOwnProperty(property) ) {
var sourceProperty = source[ property ];
if ( typeof sourceProperty === 'object' ) {
target[ property ] = util.merge( target[ property ], sourceProperty );
continue;
}
target[ property ] = sourceProperty;
}
}
for (var a = 2, l = arguments.length; a < l; a++) {
util.merge(target, arguments[a]);
}
return target;
},
count: function(arr, item) {
var count = 0;
for (var i = 0, l = arr.length; i< l; i++) {
if (arr[i] === item) {
count++;
}
}
return count;
},
thead: function(tbl) {
return tbl.getElementsByTagName('thead')[0];
},
forEach: function(arr, max, fn) {
if (!fn) {
fn = max;
}
/* Helper: iteration */
var len = arr.length,
index = -1;
while (++index < len) {
if(fn( arr[index], index, arr ) === false) {
break;
}
}
return true;
},
type: function(v){
try {
/* Returns type, e.g. "string", "number", "array" etc.
Note, this is only used for precise typing. */
if (v === null) { return 'null'; }
if (v === undefined) { return 'undefined'; }
var oType = Object.prototype.toString.call(v).match(/\s(.+?)\]/)[1].toLowerCase();
if (v.nodeType) {
if (v.nodeType === 1) {
return 'domelement';
}
return 'domnode';
}
if (/^(string|number|array|regexp|function|date|boolean)$/.test(oType)) {
return oType;
}
if (typeof v === 'object') {
return v.jquery && typeof v.jquery === 'string' ? 'jquery' : 'object';
}
if (v === window || v === document) {
return 'object';
}
return 'default';
} catch(e) {
return 'default';
}
},
within: function(ref) {
/* Check existence of a val within an object
RETURNS KEY */
return {
is: function(o) {
for (var i in ref) {
if (ref[i] === o) {
return i;
}
}
return '';
}
};
},
common: {
circRef: function(obj, key, settings) {
return util.expander(
'[POINTS BACK TO <strong>' + (key) + '</strong>]',
'Click to show this item anyway',
function() {
this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
}
);
},
depthReached: function(obj, settings) {
return util.expander(
'[DEPTH REACHED]',
'Click to show this item anyway',
function() {
try {
this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
} catch(e) {
this.parentNode.appendChild(
util.table(['ERROR OCCURED DURING OBJECT RETRIEVAL'],'error').addRow([e.message]).node
);
}
}
);
}
},
getStyles: function(el, type) {
type = prettyPrintThis.settings.styles[type] || {};
return util.merge(
{}, prettyPrintThis.settings.styles['default'][el], type[el]
);
},
expander: function(text, title, clickFn) {
return util.el('a', {
innerHTML: util.shorten(text) + ' <b style="visibility:hidden;">[+]</b>',
title: title,
onmouseover: function() {
this.getElementsByTagName('b')[0].style.visibility = 'visible';
},
onmouseout: function() {
this.getElementsByTagName('b')[0].style.visibility = 'hidden';
},
onclick: function() {
this.style.display = 'none';
clickFn.call(this);
return false;
},
style: {
cursor: 'pointer'
}
});
},
stringify: function(obj) {
/* Bit of an ugly duckling!
- This fn returns an ATTEMPT at converting an object/array/anyType
into a string, kinda like a JSON-deParser
- This is used for when |settings.expanded === false| */
var type = util.type(obj),
str, first = true;
if ( type === 'array' ) {
str = '[';
util.forEach(obj, function(item,i){
str += (i===0?'':', ') + util.stringify(item);
});
return str + ']';
}
if (typeof obj === 'object') {
str = '{';
for (var i in obj){
if (obj.hasOwnProperty(i)) {
str += (first?'':', ') + i + ':' + util.stringify(obj[i]);
first = false;
}
}
return str + '}';
}
if (type === 'regexp') {
return '/' + obj.source + '/';
}
if (type === 'string') {
return '"' + obj.replace(/"/g,'\\"') + '"';
}
return obj.toString();
},
headerGradient: (function(){
var canvas = document.createElement('canvas');
if (!canvas.getContext) { return ''; }
var cx = canvas.getContext('2d');
canvas.height = 30;
canvas.width = 1;
var linearGrad = cx.createLinearGradient(0,0,0,30);
linearGrad.addColorStop(0,'rgba(0,0,0,0)');
linearGrad.addColorStop(1,'rgba(0,0,0,0.25)');
cx.fillStyle = linearGrad;
cx.fillRect(0,0,1,30);
var dataURL = canvas.toDataURL && canvas.toDataURL();
return 'url(' + (dataURL || '') + ')';
})()
};
// Main..
var prettyPrintThis = function(obj, options) {
/*
* obj :: Object to be printed
* options :: Options (merged with config)
*/
options = options || {};
var settings = util.merge( {}, prettyPrintThis.config, options ),
container = util.el('div'),
config = prettyPrintThis.config,
currentDepth = 0,
stack = {},
hasRunOnce = false;
/* Expose per-call settings.
Note: "config" is overwritten (where necessary) by options/"settings"
So, if you need to access/change *DEFAULT* settings then go via ".config" */
prettyPrintThis.settings = settings;
var typeDealer = {
string : function(item){
return util.txt('"' + util.shorten(item.replace(/"/g,'\\"')) + '"');
},
number : function(item) {
return util.txt(item);
},
regexp : function(item) {
var miniTable = util.table(['RegExp',null], 'regexp');
var flags = util.table();
var span = util.expander(
'/' + item.source + '/',
'Click to show more',
function() {
this.parentNode.appendChild(miniTable.node);
}
);
flags
.addRow(['g', item.global])
.addRow(['i', item.ignoreCase])
.addRow(['m', item.multiline]);
miniTable
.addRow(['source', '/' + item.source + '/'])
.addRow(['flags', flags.node])
.addRow(['lastIndex', item.lastIndex]);
return settings.expanded ? miniTable.node : span;
},
domelement : function(element, depth) {
var miniTable = util.table(['DOMElement',null], 'domelement'),
props = ['id', 'className', 'innerHTML', 'src', 'href'], elname = element.nodeName || '';
miniTable.addRow(['tag', '&lt;' + elname.toLowerCase() + '&gt;']);
util.forEach(props, function(prop){
if ( element[prop] ) {
miniTable.addRow([ prop, util.htmlentities(element[prop]) ]);
}
});
return settings.expanded ? miniTable.node : util.expander(
'DOMElement (' + elname.toLowerCase() + ')',
'Click to show more',
function() {
this.parentNode.appendChild(miniTable.node);
}
);
},
domnode : function(node){
/* Deals with all DOMNodes that aren't elements (nodeType !== 1) */
var miniTable = util.table(['DOMNode',null], 'domelement'),
data = util.htmlentities( (node.data || 'UNDEFINED').replace(/\n/g,'\\n') );
miniTable
.addRow(['nodeType', node.nodeType + ' (' + node.nodeName + ')'])
.addRow(['data', data]);
return settings.expanded ? miniTable.node : util.expander(
'DOMNode',
'Click to show more',
function() {
this.parentNode.appendChild(miniTable.node);
}
);
},
jquery : function(obj, depth, key) {
return typeDealer['array'](obj, depth, key, true);
},
object : function(obj, depth, key) {
/* Checking depth + circular refs */
/* Note, check for circular refs before depth; just makes more sense */
var stackKey = util.within(stack).is(obj);
if ( stackKey ) {
return util.common.circRef(obj, stackKey, settings);
}
stack[key||'TOP'] = obj;
if (depth === settings.maxDepth) {
return util.common.depthReached(obj, settings);
}
var table = util.table(['Object', null],'object'),
isEmpty = true;
for (var i in obj) {
if (!obj.hasOwnProperty || obj.hasOwnProperty(i)) {
var item = obj[i],
type = util.type(item);
isEmpty = false;
try {
table.addRow([i, typeDealer[ type ](item, depth+1, i)], type);
} catch(e) {
/* Security errors are thrown on certain Window/DOM properties */
if (window.console && window.console.log) {
console.log(e.message);
}
}
}
}
if (isEmpty) {
table.addRow(['<small>[empty]</small>']);
} else {
table.thead.appendChild(
util.hRow(['key','value'], 'colHeader')
);
}
var ret = (settings.expanded || hasRunOnce) ? table.node : util.expander(
util.stringify(obj),
'Click to show more',
function() {
this.parentNode.appendChild(table.node);
}
);
hasRunOnce = true;
return ret;
},
array : function(arr, depth, key, jquery) {
/* Checking depth + circular refs */
/* Note, check for circular refs before depth; just makes more sense */
var stackKey = util.within(stack).is(arr);
if ( stackKey ) {
return util.common.circRef(arr, stackKey);
}
stack[key||'TOP'] = arr;
if (depth === settings.maxDepth) {
return util.common.depthReached(arr);
}
/* Accepts a table and modifies it */
var me = jquery ? 'jQuery' : 'Array', table = util.table([me + '(' + arr.length + ')', null], jquery ? 'jquery' : me.toLowerCase()),
isEmpty = true,
count = 0;
if (jquery){
table.addRow(['selector',arr.selector]);
}
util.forEach(arr, function(item,i){
if (settings.maxArray >= 0 && ++count > settings.maxArray) {
table.addRow([
i + '..' + (arr.length-1),
typeDealer[ util.type(item) ]('...', depth+1, i)
]);
return false;
}
isEmpty = false;
table.addRow([i, typeDealer[ util.type(item) ](item, depth+1, i)]);
});
if (!jquery){
if (isEmpty) {
table.addRow(['<small>[empty]</small>']);
} else {
table.thead.appendChild( util.hRow(['index','value'], 'colHeader') );
}
}
return settings.expanded ? table.node : util.expander(
util.stringify(arr),
'Click to show more',
function() {
this.parentNode.appendChild(table.node);
}
);
},
'function' : function(fn, depth, key) {
/* Checking JUST circular refs */
var stackKey = util.within(stack).is(fn);
if ( stackKey ) { return util.common.circRef(fn, stackKey); }
stack[key||'TOP'] = fn;
var miniTable = util.table(['Function',null], 'function'),
argsTable = util.table(['Arguments']),
args = fn.toString().match(/\((.+?)\)/),
body = fn.toString().match(/\(.*?\)\s+?\{?([\S\s]+)/)[1].replace(/\}?$/,'');
miniTable
.addRow(['arguments', args ? args[1].replace(/[^\w_,\s]/g,'') : '<small>[none/native]</small>'])
.addRow(['body', body]);
return settings.expanded ? miniTable.node : util.expander(
'function(){...}',
'Click to see more about this function.',
function(){
this.parentNode.appendChild(miniTable.node);
}
);
},
'date' : function(date) {
var miniTable = util.table(['Date',null], 'date'),
sDate = date.toString().split(/\s/);
/* TODO: Make this work well in IE! */
miniTable
.addRow(['Time', sDate[4]])
.addRow(['Date', sDate.slice(0,4).join('-')]);
return settings.expanded ? miniTable.node : util.expander(
'Date (timestamp): ' + (+date),
'Click to see a little more info about this date',
function() {
this.parentNode.appendChild(miniTable.node);
}
);
},
'boolean' : function(bool) {
return util.txt( bool.toString().toUpperCase() );
},
'undefined' : function() {
return util.txt('UNDEFINED');
},
'null' : function() {
return util.txt('NULL');
},
'default' : function() {
/* When a type cannot be found */
return util.txt('prettyPrint: TypeNotFound Error');
}
};
container.appendChild( typeDealer[ (settings.forceObject) ? 'object' : util.type(obj) ](obj, currentDepth) );
return container;
};
/* Configuration */
/* All items can be overwridden by passing an
"options" object when calling prettyPrint */
prettyPrintThis.config = {
/* Try setting this to false to save space */
expanded: true,
forceObject: false,
maxDepth: 3,
maxArray: -1, // default is unlimited
styles: {
array: {
th: {
backgroundColor: '#6DBD2A',
color: 'white'
}
},
'function': {
th: {
backgroundColor: '#D82525'
}
},
regexp: {
th: {
backgroundColor: '#E2F3FB',
color: '#000'
}
},
object: {
th: {
backgroundColor: '#1F96CF'
}
},
jquery : {
th: {
backgroundColor: '#FBF315'
}
},
error: {
th: {
backgroundColor: 'red',
color: 'yellow'
}
},
domelement: {
th: {
backgroundColor: '#F3801E'
}
},
date: {
th: {
backgroundColor: '#A725D8'
}
},
colHeader: {
th: {
backgroundColor: '#EEE',
color: '#000',
textTransform: 'uppercase'
}
},
'default': {
table: {
borderCollapse: 'collapse',
width: '100%'
},
td: {
padding: '5px',
fontSize: '12px',
backgroundColor: '#FFF',
color: '#222',
border: '1px solid #000',
verticalAlign: 'top',
fontFamily: '"Consolas","Lucida Console",Courier,mono',
whiteSpace: 'nowrap'
},
td_hover: {
/* Styles defined here will apply to all tr:hover > td,
- Be aware that "inheritable" properties (e.g. fontWeight) WILL BE INHERITED */
},
th: {
padding: '5px',
fontSize: '12px',
backgroundColor: '#222',
color: '#EEE',
textAlign: 'left',
border: '1px solid #000',
verticalAlign: 'top',
fontFamily: '"Consolas","Lucida Console",Courier,mono',
backgroundImage: util.headerGradient,
backgroundRepeat: 'repeat-x'
}
}
}
};
return prettyPrintThis;
})();

View File

@ -0,0 +1,45 @@
// 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;

View File

@ -0,0 +1,272 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
Uint8Array.prototype.toJSArray = function() {
if(this.length == 1) {
return [this[0]];
}
var len = this.length;
var arr = new Array(len);
for(var i=0;i<len;i++) {
arr[i] = this[i];
}
return arr;
}
wdi.FixedQueue = $.spcExtend(wdi.DomainObject, {
q: null,
size: 1024*1024*10,
grow: 1024*1024*10,
woffset: 0,
roffset: 0,
init: function(c) {
this.q = new Uint8Array(this.size);
},
setData: function(q) {
this.woffset = q.length;
this.roffset = 0;
this.q.set(q);
},
shift: function(elements) {
if(this.roffset + elements > this.woffset) {
throw "Not enough queue to read";
}
var toreturn = this.q.subarray(this.roffset, this.roffset + elements);
this.roffset = this.roffset + elements;
if(this.woffset == this.roffset) {
this.woffset = 0;
this.roffset = 0;
}
return toreturn;
},
push: function(collection) {
if(this.woffset + collection.byteLength > this.size) {
//we need to make the queue bigger...
var oldq = this.q;
this.size += this.grow;
this.q = new Uint8Array(this.size);
this.q.set(oldq);
}
this.q.set(collection, this.woffset);
this.woffset += collection.byteLength;
},
getLength: function() {
return this.woffset-this.roffset;
}
});
wdi.Queue = $.spcExtend(wdi.DomainObject, {
q: null,
raw: false,
init: function(c) {
if(c.raw) {
this.raw = c.raw;
}
this.q = new Uint8Array(0);
},
getData: function() {
return this.toJSArray(this.q);
},
setData: function(q) {
this.q = new Uint8Array(q.length);
this.q.set(q);
},
shift: function() {
var elements = arguments[0] || this.getLength();
if (elements === this.q.length) {
var toreturn = this.q;
this.q = new Uint8Array(0);
} else {
var toreturn = this.q.subarray(0, elements);
this.q = this.q.subarray(elements);
}
return this.toJSArray(toreturn)
},
peek: function(begin, end) {
var tmp = null;
if(begin == 0 && !end) {
tmp = this.q; //read the entire queue
} else {
tmp = this.q.subarray(begin, end);
}
return this.toJSArray(tmp);
},
push: function(collection) {
if (typeof collection == 'string') {
var len = collection.length;
var newq = new Uint8Array(this.q.length+len);
newq.set(this.q);
for(var i=0;i<len;i++) {
newq[i+this.q.length] = collection[i];
}
this.q = newq;
} else {
if(this.getLength() === 0) {
this.q = new Uint8Array(collection.length);
this.q.set(collection);
} else {
var newq = new Uint8Array(collection.length+this.q.length);
newq.set(this.q);
newq.set(collection, this.q.length);
this.q = newq;
}
}
},
getLength: function() {
return this.q.length;
},
toJSArray: function(data) {
if(this.raw) {
return data;
}
return data.toJSArray();
}
});
wdi.ViewQueue = $.spcExtend(wdi.DomainObject, {
q: null,
at: null,
init: function() {
this.q = new Uint8Array();
this.at = 0;
},
getData: function() {
return this.toJSArray(this.q.subarray(this.at));
},
getDataOffset: function(pos) {
return this.toJSArray(this.q.subarray(pos));
},
getRawData: function() {
return this.q.subarray(this.at);
},
getRawDataOffset: function(pos) {
return this.q.subarray(pos);
},
setData: function(q) {
this.q = new Uint8Array(q.length);
this.q.set(q);
this.at = 0;
},
shift: function(length) {
var elements = length || this.getLength();
if(elements > this.getLength()) {
elements = this.getLength();
}
var ret = this.q.subarray(0+this.at, elements+this.at);
this.at += elements;
return this.toJSArray(ret);
},
eatBytes: function(bytes) {
this.at += bytes;
},
getByte: function(pos) {
return this.q[pos+this.at];
},
peek: function(begin, end) {
var tmp = null;
if(begin == 0 && !end) {
tmp = this.q; //read the entire queue
} else {
if(end) {
end += this.at;
}
tmp = this.q.subarray(begin+this.at, end);
}
return this.toJSArray(tmp);
},
push: function(collection) {
if (typeof collection == 'string') {
var len = collection.length;
var newq = new Uint8Array(this.q.length+len);
newq.set(this.q);
for(var i=0;i<len;i++) {
newq[i+this.q.length] = collection[i];
}
this.q = newq;
} else {
if(this.getLength() === 0) {
this.q = new Uint8Array(collection.length);
this.q.set(collection);
} else {
var newq = new Uint8Array(collection.length+this.q.length);
newq.set(this.q);
newq.set(collection, this.q.length);
this.q = newq;
}
}
},
getLength: function() {
return this.q.length-this.at;
},
getPosition: function() {
return this.at;
},
toJSArray: function(data) {
if(data.length == 1) {
return [data[0]];
}
return data.toJSArray();
}
});

View File

@ -0,0 +1,542 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.RasterEngine = $.spcExtend(wdi.EventObject.prototype, {
init: function(c) {
this.clientGui = c.clientGui;
},
drawCanvas: function(spiceMessage) {
return this.clientGui.drawCanvas(spiceMessage);
},
removeCanvas: function(spiceMessage) {
return this.clientGui.removeCanvas(spiceMessage);
},
invalList: function(spiceMessage) {
var items = spiceMessage.args.items;
var item = null;
for(var i in items) {
item = items[i];
wdi.ImageCache.delImage(item.id);
}
},
handleStreamCreate: function(spiceMessage) {
var stream = spiceMessage.args;
stream.computedBox = wdi.graphics.getBoxFromSrcArea(stream.rect);
wdi.Stream.addStream(spiceMessage.args.id, stream);
},
handleStreamData: function(spiceMessage) {
var imageData = spiceMessage.args.data; //jpeg string encoded
var stream = wdi.Stream.getStream(spiceMessage.args.id); //recover the stream
var context = this.clientGui.getContext(stream.surface_id);
var img = wdi.GlobalPool.create('Image'); //auto-release pool
wdi.ExecutionControl.sync = true;
var url;
img.onload = function() {
URL.revokeObjectURL(url);
var box = stream.computedBox;
// we only rotate the stream if spice tells us so through the TOP_DOWN flag mask
if (!stream.flags & wdi.SpiceStreamFlags.SPICE_STREAM_FLAGS_TOP_DOWN) {
var offsetX = box.x + (this.width/2);
var offsetY = box.y + (this.height/2);
context.save();
context.translate(offsetX, offsetY);
context.rotate(Math.PI);
context.scale(-1,1);
context.drawImage(this, box.x-offsetX, box.y-offsetY, box.width, box.height);
context.restore();
} else {
context.drawImage(this, box.x, box.y, box.width, box.height);
}
};
img.onerror = function() {
URL.revokeObjectURL(url)
};
url = wdi.SpiceObject.bytesToURI(imageData);
img.src = url;
},
handleStreamClip: function(spiceMessage) {
wdi.Stream.clip(spiceMessage.args.id, spiceMessage.args.clip)
},
handleStreamDestroy: function(spiceMessage) {
wdi.Stream.deleteStream(spiceMessage.args.id);
},
drawRop3: function(spiceMessage) {
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.base.box);
var context = this.clientGui.getContext(spiceMessage.args.base.surface_id);
var destImg = context.getImageData(box.x, box.y, box.width, box.height);
var clientGui = this.clientGui;
var brush = spiceMessage.args.brush;
var rop = spiceMessage.args.rop_descriptor;
var srcArea = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
wdi.graphics.getImageFromSpice(spiceMessage.args.src_image.imageDescriptor, spiceMessage.args.src_image.data, this.clientGui, function (sourceCanvas) {
if (sourceCanvas) {
//Get source image data (image coming from the packet)
var sourceContext = sourceCanvas.getContext('2d');
var srcImg = sourceContext.getImageData(srcArea.x, srcArea.y, srcArea.width, srcArea.height);
var srcImgData = srcImg.data; //this
//Get pattern image data
//brush
var tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height);
var tmpcontext = tmpcanvas.getContext('2d');
var brushBox = {
width: box.width,
height: box.height,
x: 0,
y: 0
};
wdi.graphics.setBrush(clientGui, tmpcontext, brush, brushBox, wdi.SpiceRopd.SPICE_ROPD_OP_PUT);//Without alpha?
var pattern = tmpcontext.getImageData(0, 0, box.width, box.height);
var patImgData = pattern.data; //this
//Get dest image data
var destImgData = destImg.data;
//Get result image data
tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height);
tmpcontext = tmpcanvas.getContext('2d');
var result = tmpcontext.createImageData(box.width, box.height);
var resultData = result.data;
if ((srcImg.width != pattern.width || srcImg.width != destImg.width) || (srcImg.height != pattern.height || srcImg.height != destImg.height)) {
//TODO: resize
}
//Do the Ternary Raster Operation
var length = destImgData.length;//Could be anyone
var func = wdi.Rop3[rop];
for (var i = 0;i<length;i+=4) {
resultData[i] = func(patImgData[i], srcImgData[i], destImgData[i]) & 255;
resultData[i+1] = func(patImgData[i+1], srcImgData[i+1], destImgData[i+1]) & 255;
resultData[i+2] = func(patImgData[i+2], srcImgData[i+2], destImgData[i+2]) & 255;
resultData[i+3] = 255;
}
tmpcontext.putImageData(result, 0, 0);
this.drawClip(tmpcanvas, box, context);
} else {
wdi.Debug.log('Unable to get image!');
}
}, this);
},
drawInvers: function(spiceMessage) {
var drawBase = spiceMessage.args.base;
var box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
var surface_id = drawBase.surface_id;
var context = this.clientGui.getContext(surface_id);
var destImg = wdi.graphics.getRect(box, context.canvas);
var imageData = wdi.RasterOperation.process(wdi.SpiceRopd.SPICE_ROPD_OP_INVERS, null, destImg);//this operation modifies destination
context.drawImage(imageData, box.x, box.y, box.width, box.height);
},
drawStroke: function(spiceMessage) {
var stroke = spiceMessage.args,
context = this.clientGui.getContext(spiceMessage.args.base.surface_id),
color = stroke.brush.color.html_color,
lineWidth = 1,
pointsLength,
firstPoint,
i,
j,
length = stroke.path.num_segments,
seg;
if (stroke.attr.flags & wdi.SpiceLineFlags.SPICE_LINE_FLAGS_STYLED) {
wdi.Debug.log('SPICE_LINE_FLAGS_STYLED');
}
for (var i = 0;i < length; i++) {
seg = stroke.path.segments[i];
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_BEGIN) {
context.beginPath();
context.moveTo(seg.points[0].x, seg.points[0].y);
context.strokeStyle = color;
context.lineWidth = lineWidth;
}
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_BEZIER) {
pointsLength = seg.points.length;
if (pointsLength % 3 == 0) {
for (j = 0; j < pointsLength; j += 3) {
context.bezierCurveTo(
seg.points[j].x, seg.points[j].y,
seg.points[j+1].x, seg.points[j+1].y,
seg.points[j+2].x, seg.points[j+2].y
);
}
}
} else {
pointsLength = seg.points.length;
for (j = 0; j < pointsLength; j++) {
if (j == 0) firstPoint = seg.points[j];
context.lineTo(seg.points[j].x + (lineWidth / 2), seg.points[j].y + (lineWidth / 2));
}
}
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_END) {
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_CLOSE) {
context.lineTo(firstPoint.x + (lineWidth / 2), firstPoint.y + (lineWidth / 2));
}
context.stroke();
context.closePath();
}
}
},
drawImage: function(spiceMessage) {
var args = spiceMessage.args;
var drawBase = args.base;
var surface_id = drawBase.surface_id;
var rop = args.rop_descriptor;
var scale = args.scale_mode;
//calculate src_area box
var box_origin = wdi.graphics.getBoxFromSrcArea(args.src_area);
var box_dest = wdi.graphics.getBoxFromSrcArea(drawBase.box);
//depending on the rop, we can avoid to get destImg
if (rop === wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
var destImg = null;
} else {
//get the destination image, there is a ROP
var destImg = wdi.graphics.getRect(box_dest, this.clientGui.getCanvas(surface_id));
}
if (window.vdiLoadTest && window.firstImage === undefined) {
window.firstImage = true;
}
//get the image in imagedata format
wdi.graphics.getImageFromSpice(args.image.imageDescriptor, args.image.data, this.clientGui, function(srcImg) {
//we have image?
if(srcImg) {
if (window.firstImage) {
var data;
if(srcImg.getContext) {
data = srcImg.getContext('2d').getImageData(0, 0, srcImg.width, srcImg.height).data.buffer.slice(0)
} else {
data = srcImg.data.buffer.slice(0);
}
window.firstImageData = data;
window.firstImage = false;
}
//adapt to src_area
srcImg = wdi.graphics.getRect(box_origin, srcImg);
if(box_origin.width !== box_dest.width && box_origin.height !== box_dest.height) {
srcImg = wdi.graphics.getImageFromData(srcImg);
var newSrcImg = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
var tmpcontext = newSrcImg.getContext('2d');
tmpcontext.drawImage(srcImg, 0, 0, box_origin.width, box_origin.height, 0, 0, box_dest.width, box_dest.height);
srcImg = newSrcImg;
}
//rop
srcImg = wdi.RasterOperation.process(rop, srcImg, destImg);
var context = this.clientGui.getContext(surface_id);
//TODO: swcanvas do not support clipping
if(args.base.clip.type === wdi.SpiceClipType.SPICE_CLIP_TYPE_RECTS) {
srcImg = wdi.graphics.getImageFromData(srcImg);
}
if(srcImg instanceof ImageData) {
context.putImageData(srcImg, box_dest.x, box_dest.y, 0, 0, box_dest.width, box_dest.height);
} else {
context.drawImage(srcImg, box_dest.x, box_dest.y, box_dest.width, box_dest.height);
}
} else {
//failed to get image, cache error?
wdi.Debug.log('Unable to get image!');
}
}, this, {'opaque':true, 'brush': args.brush, 'raw': false});
},
drawClip: function(srcImg, box, context) {
context.drawImage(srcImg, box.x, box.y, box.width, box.height);
},
drawFill: function(spiceMessage) {
var args = spiceMessage.args;
var context = this.clientGui.getContext(args.base.surface_id);
var box = wdi.graphics.getBoxFromSrcArea(args.base.box);
var brush = args.brush;
var ropd = args.rop_descriptor;
wdi.graphics.setBrush(this.clientGui, context, brush, box, ropd);
},
drawCopyBits: function(spiceMessage) {
var drawBase = spiceMessage.args.base;
var surface_id = drawBase.surface_id;
var src_position = spiceMessage.args.src_position;
var context = this.clientGui.getContext(surface_id);
var box = drawBase.box;
var width = box.right - box.left;
var height = box.bottom - box.top;
context.drawImage(context.canvas, src_position.x, src_position.y, width,
height, drawBase.box.left, drawBase.box.top, width, height);
},
drawBlend: function(spiceMessage) {
//TODO: alpha_flags
//TODO: resize
var descriptor = spiceMessage.args.image.imageDescriptor;
var drawBase = spiceMessage.args.base;
var imgData = spiceMessage.args.image.data;
var surface_id = spiceMessage.args.base.surface_id;
var rop_desc = spiceMessage.args.rop_descriptor;
var flags = spiceMessage.args.flags;
wdi.graphics.getImageFromSpice(descriptor, imgData, this.clientGui, function(srcImg) {
if (!srcImg) {
wdi.Debug.log('There is no image on Blend');
return;
}
//get box from src area
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
//adapt to src_area
srcImg = wdi.graphics.getRect(box, srcImg);
//destination box
var dest_box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
var destImg = wdi.graphics.getRect(dest_box, this.clientGui.getCanvas(surface_id));
var result = wdi.RasterOperation.process(rop_desc, srcImg, destImg);
this.clientGui.getCanvas(surface_id).getContext('2d').drawImage(result, dest_box.x, dest_box.y, dest_box.width, dest_box.height);
}, this);
},
drawAlphaBlend: function(spiceMessage) {
//TODO: alpha_flags
//TODO: resize
var descriptor = spiceMessage.args.image.imageDescriptor;
var drawBase = spiceMessage.args.base;
var imgData = spiceMessage.args.image.data;
var surface_id = spiceMessage.args.base.surface_id;
var flags = spiceMessage.args.alpha_flags;
var alpha = spiceMessage.args.alpha;
wdi.graphics.getImageFromSpice(descriptor, imgData, this.clientGui, function(srcImg) {
if (!srcImg) {
wdi.Debug.log('There is no image on drawAlphaBlend');
}
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
//adapt to src_area
srcImg = wdi.graphics.getRect(box, srcImg);
//destination box
var box_dest = wdi.graphics.getBoxFromSrcArea(drawBase.box);
var destImg = wdi.graphics.getRect(box_dest, this.clientGui.getCanvas(surface_id));
if(box.width !== box_dest.width && box.height !== box_dest.height) {
var tmpcanvas = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
var tmpcontext = tmpcanvas.getContext('2d');
tmpcontext.drawImage(srcImg, 0, 0, box.width, box.height, 0, 0, box_dest.width, box_dest.height);
srcImg = tmpcanvas;
}
var src = wdi.graphics.getDataFromImage(srcImg).data;
var dst = wdi.graphics.getDataFromImage(destImg).data;
var length = src.length-1;
//create a new imagedata to store result
var imageResult = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
var context = imageResult.getContext('2d');
var resultImageData = context.createImageData(box_dest.width, box_dest.height);
var result = resultImageData.data;
var rS, rD;
var gS, gD;
var bS, bD;
var aS;
for (var px=0;px<length;px+=4) {
rS = src[px];
gS = src[px+1];
bS = src[px+2];
if(flags || alpha === 255) {
aS = src[px+3];
} else {
aS = alpha;
}
rD = dst[px];
gD = dst[px+1];
bD = dst[px+2];
if(aS > 30 && alpha === 255) {
//formula from reactos, this is premultiplied alpha values
result[px] = ((rD * (255 - aS)) / 255 + rS) & 0xff;
result[px+1] = ((gD * (255 - aS)) / 255 + gS) & 0xff;
result[px+2] = ((bD * (255 - aS)) / 255 + bS) & 0xff;
} else {
//homemade blend function, this is the typical blend function simplified
result[px] = (( (rS*aS)+(rD*(255-aS)) ) / 255) & 0xff;
result[px+1] = (( (gS*aS)+(gD*(255-aS)) ) / 255) & 0xff;
result[px+2] = (( (bS*aS)+(bD*(255-aS)) ) / 255) & 0xff;
}
result[px+3] = 255;
}
imageResult.getContext('2d').putImageData(resultImageData, 0, 0);
this.drawClip(imageResult, box_dest, this.clientGui.getContext(surface_id));
}, this);
},
drawWhiteness: function(spiceMessage) {
//TODO: mask
var base = spiceMessage.args.base;
var context = this.clientGui.getContext(base.surface_id);
var box = wdi.graphics.getBoxFromSrcArea(base.box);
context.fillStyle = "white";
context.fillRect(box.x, box.y, box.width, box.height);
},
drawBlackness: function(spiceMessage) {
//TODO: mask
var base = spiceMessage.args.base;
var context = this.clientGui.getContext(base.surface_id);
var box = wdi.graphics.getBoxFromSrcArea(base.box);
context.fillStyle = "black";
context.fillRect(box.x, box.y, box.width, box.height);
},
drawTransparent: function(spiceMessage) {
var drawBase = spiceMessage.args.base;
var surface_id = drawBase.surface_id;
//calculate src_area box
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
var dest_box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
//get destination iamge, in imagedata format because is what we need
var destImg = this.clientGui.getContext(surface_id).getImageData(dest_box.x, dest_box.y,
dest_box.width, dest_box.height);
wdi.graphics.getImageFromSpice(spiceMessage.args.image.imageDescriptor, spiceMessage.args.image.data, this.clientGui, function(srcImg) {
if(srcImg) {
//adapt to src_area
srcImg = wdi.graphics.getRect(box, srcImg);
var source = wdi.graphics.getDataFromImage(srcImg).data;
var dest = destImg.data;
var length = source.length-1;
var resultImageData = this.clientGui.getContext(surface_id).createImageData(dest_box.width, dest_box.height);
var color = spiceMessage.args.transparent_true_color;
while(length>0) {
resultImageData.data[length] = 255; //alpha
if(source[length-1] === color.b && source[length-2] === color.g
&& source[length-3] === color.r) {
resultImageData.data[length-1] = dest[length-1]; //b
resultImageData.data[length-2] = dest[length-2]; //g
resultImageData.data[length-3] = dest[length-3]; //r
} else {
resultImageData.data[length-1] = source[length-1]; //b
resultImageData.data[length-2] = source[length-2]; //g
resultImageData.data[length-3] = source[length-3]; //r
}
length-=4;
}
var resultImage = wdi.graphics.getImageFromData(resultImageData);
this.drawClip(resultImage, dest_box, this.clientGui.getContext(surface_id));
} else {
//failed to get image, cache error?
wdi.Debug.log('Unable to get image!');
}
}, this);
},
drawText: function(spiceMessage) {
var context = this.clientGui.getContext(spiceMessage.args.base.surface_id);
var bbox = spiceMessage.args.base.box;
var clip = spiceMessage.args.base.clip;
var text = spiceMessage.args;
var string = text.glyph_string;
var bpp = string.flags === 1 ? 1 : string.flags * 2;
if (text.back_mode !== 0) {
wdi.graphics.drawBackText(this.clientGui, context, text);
}
wdi.graphics.drawString(context, string, bpp, text.fore_brush, clip.type, this);
},
/**
* Clears all color palettes
* @param spiceMessage
* @param app
*/
invalPalettes: function(spiceMessage) {
wdi.ImageCache.clearPalettes();
}
});

View File

@ -0,0 +1,68 @@
// 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;

View File

@ -0,0 +1,115 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.RunQueue = $.spcExtend(wdi.DomainObject, {
tasks: null,
isRunning: false,
init: function() {
this.tasks = [];
},
getTasksLength: function() {
return this.tasks.length;
},
add: function(fn, scope, endCallback, params) {
this.tasks.push({
fn: fn,
scope: scope,
fnFinish: endCallback,
params: params
});
return this;
},
clear: function() {
this.tasks = [];
return this;
},
_process: function() {
wdi.ExecutionControl.sync = true;
var proxy, self = this;
this.isRunning = true;
var task = this.tasks.shift();
if (!task) {
this.isRunning = false;
return;
}
proxy = {
end: function() {
if(task.fnFinish) {
task.fnFinish.call(task.scope);
}
self._process();
}
};
try {
task.fn.call(task.scope, proxy, task.params);
} catch(e) {
wdi.Debug.error(e.message);
proxy.end();
}
return this;
},
process: function() {
if (!this.isRunning) {
this._process();
} else {
return;
}
}
});
//wdi.ExecutionControl = $.spcExtend(wdi.DomainObject, {
// currentProxy: null,
// sync: true,
// runQ: null,
// init: function(c) {
// this.runQ = c.runQ || new wdi.RunQueue();
// }
//});
//TODO: make an instance of it on each channel
wdi.ExecutionControl = {
currentProxy: null,
sync: true,
runQ: new wdi.RunQueue()
};

View File

@ -0,0 +1,105 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* SHA-1 implementation in JavaScript (c) Chris Veness 2002-2009 */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function sha1Hash(msg)
{
// constants [§4.2.1]
var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
// PREPROCESSING
msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
// convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + 1 + appended length
var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints
var M = new Array(N);
for (var i=0; i<N; i++) {
M[i] = new Array(16);
for (var j=0; j<16; j++) { // encode 4 chars per integer, big-endian encoding
M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) |
(msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
}
}
// add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1]
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14])
M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;
// set initial hash value [§5.3.1]
var H0 = 0x67452301;
var H1 = 0xefcdab89;
var H2 = 0x98badcfe;
var H3 = 0x10325476;
var H4 = 0xc3d2e1f0;
// HASH COMPUTATION [§6.1.2]
var W = new Array(80); var a, b, c, d, e;
for (var i=0; i<N; i++) {
// 1 - prepare message schedule 'W'
for (var t=0; t<16; t++) W[t] = M[i][t];
for (var t=16; t<80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
// 2 - initialise five working variables a, b, c, d, e with previous hash value
a = H0; b = H1; c = H2; d = H3; e = H4;
// 3 - main loop
for (var t=0; t<80; t++) {
var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
var T = (ROTL(a,5) + f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
e = d;
d = c;
c = ROTL(b, 30);
b = a;
a = T;
}
// 4 - compute the new intermediate hash value
H0 = (H0+a) & 0xffffffff; // note 'addition modulo 2^32'
H1 = (H1+b) & 0xffffffff;
H2 = (H2+c) & 0xffffffff;
H3 = (H3+d) & 0xffffffff;
H4 = (H4+e) & 0xffffffff;
}
return H0.toHexStr() + H1.toHexStr() + H2.toHexStr() + H3.toHexStr() + H4.toHexStr();
}
//
// function 'f' [§4.1.1]
//
function f(s, x, y, z)
{
switch (s) {
case 0: return (x & y) ^ (~x & z); // Ch()
case 1: return x ^ y ^ z; // Parity()
case 2: return (x & y) ^ (x & z) ^ (y & z); // Maj()
case 3: return x ^ y ^ z; // Parity()
}
}
//
// rotate left (circular left shift) value x by n positions [§3.2.5]
//
function ROTL(x, n)
{
return (x<<n) | (x>>>(32-n));
}
//
// extend Number class with a tailored hex-string method
// (note toString(16) is implementation-dependant, and
// in IE returns signed numbers when used on full words)
//
Number.prototype.toHexStr = function()
{
var s="", v;
for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); }
return s;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

View File

@ -0,0 +1,122 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.StuckKeysHandler = $.spcExtend(wdi.EventObject.prototype, {
ctrlTimeoutId: null,
altTimeoutId: null,
shiftTimeoutId: null,
shiftKeyPressed: false,
ctrlKeyPressed: false,
altKeyPressed: false,
handleStuckKeys: function (jqueryEvent) {
if (jqueryEvent) {
switch (jqueryEvent.keyCode) {
case 16:
this._handleKey('shiftTimeoutId', jqueryEvent.type, 16);
break;
case 17:
this._handleKey('ctrlTimeoutId', jqueryEvent.type, 17);
break;
case 18:
this._handleKey('altTimeoutId', jqueryEvent.type, 18);
break;
}
}
},
releaseAllKeys: function releaseAllKeys () {
var e;
var i;
for (i = 0; i < 300; i++) {
this.releaseKeyPressed(i);
}
},
_handleKey: function (variable, type, keyCode) {
if (type === 'keydown') {
this[variable] = this._configureTimeout(keyCode);
} else if (type === 'keyup') {
clearTimeout(this[variable]);
}
},
_configureTimeout: function (keyCode) {
var self = this;
return setTimeout(function keyPressedTimeout () {
// added the 'window' for the jQuery call for testing.
self.releaseKeyPressed(keyCode);
}, wdi.StuckKeysHandler.defaultTimeout);
},
releaseKeyPressed: function (keyCode) {
var e = window.jQuery.Event("keyup");
e["which"] = keyCode;
e["keyCode"] = keyCode;
e["charCode"] = 0;
e["generated"] = true;
this.fire('inputStuck', ['keyup', [e]]);
},
checkSpecialKey: function (event, keyCode) {
switch (keyCode) {
case 16:
this.shiftKeyPressed = event === 'keydown';
break;
case 17:
this.ctrlKeyPressed = event === 'keydown';
break;
case 18:
this.altKeyPressed = event === 'keydown';
break;
}
},
releaseSpecialKeysPressed: function () {
if (this.shiftKeyPressed) {
this.releaseKeyPressed(16);
this.shiftKeyPressed = false;
}
if (this.ctrlKeyPressed) {
this.releaseKeyPressed(17);
this.ctrlKeyPressed = false;
}
if (this.altKeyPressed) {
this.releaseKeyPressed(18);
this.altKeyPressed = false;
}
}
});
wdi.StuckKeysHandler.defaultTimeout = 2000;

View File

@ -0,0 +1,72 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.TimeLapseDetector = $.spcExtend(wdi.EventObject.prototype, {
lastTime: null,
init: function timeLapseDetector_Init (c) {
this.superInit();
},
startTimer: function timeLapseDetector_startTimer () {
var self = this;
this.lastTime = Date.now();
window.setInterval(
function timeLapseDetectorInterval () {
var now = Date.now();
// this.constructor == access to the class itself, so you
// can access to static properties without writing/knowing
// the class name
var elapsed = now - self.lastTime;
if (elapsed >= self.constructor.maxIntervalAllowed) {
self.fire('timeLapseDetected', elapsed);
}
self.lastTime = now;
},
wdi.TimeLapseDetector.defaultInterval
);
},
getLastTime: function timeLapseDetector_getLastTime () {
return this.lastTime;
},
setLastTime: function timeLapseDetector_setLastTime (lastTime) {
this.lastTime = lastTime;
return this;
}
});
wdi.TimeLapseDetector.defaultInterval = 5000;
wdi.TimeLapseDetector.maxIntervalAllowed = wdi.TimeLapseDetector.defaultInterval * 3;

View File

@ -0,0 +1,337 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
//If we are in NODE
if (typeof module !== "undefined" && module.exports) {
jQuery = $ = {
isArray: function (obj) {
return Object.prototype.toString.apply(obj) === "[object Array]"
},
isPlainObject: function( obj ) {
var key;
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor &&
!core_hasOwn.call(obj, "constructor") &&
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
for ( key in obj ) {}
return key === undefined || core_hasOwn.call( obj, key );
},
extend: function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// extend jQuery itself if only one argument is passed
if ( length === i ) {
target = this;
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( this.isPlainObject(copy) || (copyIsArray = this.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && this.isArray(src) ? src : [];
} else {
clone = src && this.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = this.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
}
};
}
$.extend({
spcExtend: function(obj) {
var f = function(c) {
if(typeof this['init'] !== 'undefined') {
this.init(c || {});
}
};
f.prototype.superInit = obj.init;
var args = [];
args.push(f.prototype);
var length = arguments.length;
for(var i =0; i<length;i++) {
args.push(arguments[i]);
}
$.extend.apply($, args);
return f;
}
});
$.extend(String.prototype, {
lpad: function(padString, length) {
var str = this;
while (str.length < length)
str = padString + str;
return str;
},
rpad: function(padString, length) {
var str = this;
while (str.length < length)
str = str + padString;
return str;
}
});
wdi = {};
wdi.DomainObject = {};
wdi.RawMessage = $.spcExtend(wdi.DomainObject, {
status: null,
data: null,
init: function(c) {
this.status = c.status;
this.data = c.data;
}
});
wdi.RawSpiceMessage = $.spcExtend(wdi.DomainObject, {
header: null,
body: null,
channel: null,
set: function(header, body, channel) {
this.header = header;
this.body = body;
this.channel = channel;
}
});
wdi.SpiceMessage = $.spcExtend(wdi.DomainObject, {
messageType: null,
args: null,
channel: null,
init: function(c) {
this.channel = c.channel;
this.messageType = c.messageType;
this.args = c.args;
}
});
wdi.EventObject = $.spcExtend(wdi.DomainObject, {
events: null,
init: function() {
this.eyeEvents = {};
},
getListenersLength: function(eventName) {
if (this.eyeEvents[eventName] == undefined) {
this.eyeEvents[eventName] = [];
}
return this.eyeEvents[eventName].length;
},
addListener: function(eventName, fn, scope) {
scope = scope || this;
if (this.eyeEvents[eventName] == undefined) {
this.eyeEvents[eventName] = [];
}
this.eyeEvents[eventName].push({
fn: fn,
scope: scope
});
},
removeEvent: function(eventName) {
this.eyeEvents[eventName] = undefined;
},
clearEvents: function() {
this.eyeEvents = {};
},
fire: function(eventName, params) {
var listeners = this.eyeEvents[eventName];
if(listeners) {
var size = listeners.length;
while(size--) {
listeners[size].fn.call(listeners[size].scope, params);
}
}
}
});
wdi.CHANNEL_STATUS = {
disconnected:-1,
idle:0,
establishing:1,
established:2
};
wdi.Debug = {
debug: false,
/* these logging functions accept multiple parameters, and will be passed
* directly to console.{log,info,warn,error}(), so we can have better
* messages.
*
* Call them with multiple params instead of concatenating:
* YES: wdi.Debug.log("something happened: ", whatever);
* NO : wdi.Debug.log("something happened: " + whatever);
*/
log: function(variable_list_of_args /* , ... */) {
if (this.debug) {
console.log.apply(console, Array.prototype.slice.call(arguments));
}
},
warn: function(variable_list_of_args /* , ... */) {
console.warn.apply(console, Array.prototype.slice.call(arguments));
},
info: function(variable_list_of_args /* , ... */) {
if (this.debug) {
console.info.apply(console, Array.prototype.slice.call(arguments));
}
},
error: function(variable_list_of_args /* , ... */) {
console.error.apply(console, Array.prototype.slice.call(arguments));
}
};
wdi.Utils = {
generateWebSocketUrl: function(protocol, host, port, destHost, destPort, type, destInfoToken) {
/**
* Generates websockify URL.
* If destHost and destPort are available, they are used to form explicit URL with host and port.
* If not, an URL with destInfoToken is generated, host and port are resolved by backend service.
*/
if ( ! destHost || ! destPort ) {
url = `${protocol}://${window.location.host}/ws/${host}/${port}`;
} else {
url = protocol + '://' + host + ':' + port + '/websockify/host/' + destHost + '/port/' + destPort + '/type/' + type;
}
return url;
}
};
wdi.postMessageW3CCompilant = typeof window !== "undefined" && window['bowser'] && !(window['bowser']['msie'] && window['bowser']['version'] >= 10);
wdi.Exception = $.spcExtend(wdi.DomainObject, {
errorCode: null,
message: null,
init: function(c) {
this.message = c.message || '';
this.errorCode = c.errorCode || 0;
}
});
try {
new ImageData(1,1);
} catch(e) {
if (typeof window !== 'undefined') {//Just in case it is nodejs
window.ImageData = function(arr, width, height) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var imgData = context.createImageData(width, height);
imgData.data.set(arr);
return imgData;
}
}
}
wdi.bppMask = [];
wdi.bppMask[1] = [128, 64, 32, 16, 8, 4, 2, 1];
wdi.bppMask[4] = [240, 15];
wdi.bppMask[8] = [255];
wdi.SeamlessIntegration = true;
wdi.Debug.debug = false;
wdi.exceptionHandling = true;
wdi.IntegrationBenchmarkEnabled = false; // MS Excel loading time benchmark
wdi.useWorkers = true;
wdi.logOperations = false;

View File

@ -0,0 +1,351 @@
var VirtualJoystick = function(opts)
{
opts = opts || {};
this._container = opts.container || document.body;
this._stickEl = opts.stickElement || this._buildJoystickStick();
this._baseEl = opts.baseElement || this._buildJoystickBase();
this._mouseLeft = opts.mouseLeft || this._buildJoystickButton();
this._mouseSupport = 'mouseSupport' in opts? opts.mouseSupport : false;
this._range = opts.range || 60;
this._timer = null;
this._callback = 'callback' in opts? opts.callback : false;
this._callbackcontext = 'callbackcontext' in opts? opts.callbackcontext: this;
this._callbackButton = 'callbackbutton' in opts? opts.callbackbutton : false;
this._referenceItem = opts.referenceItem;
this._container.style.position = "relative";
this._container.appendChild(this._baseEl);
this._container.appendChild(this._mouseLeft);
this._baseEl.style.position = "absolute"
this._baseEl.style.display = "block";
this._mouseLeft.style.position = "absolute"
this._mouseLeft.style.display = "block";
//calculate positions
var jcnt = $(this._referenceItem);
var baseTop = jcnt.height()-jcnt.offset().top-130;
var baseLeft = 20;
var baseRight = 20;
this._baseEl.style.top = baseTop+'px';
this._baseEl.style.left = baseLeft+'px';
this._container.appendChild(this._stickEl);
this._stickEl.style.position = "absolute"
this._stickEl.style.display = "none";
this._mouseLeft.style.top = baseTop+20+'px';
this._mouseLeft.style.right = baseRight+'px';
this._pressed = false;
this._baseX = 0;
this._baseY = 0;
this._stickX = 0;
this._stickY = 0;
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this._$onTouchStart = __bind(this._onTouchStart , this);
this._$onTouchEnd = __bind(this._onTouchEnd , this);
this._$onTouchMove = __bind(this._onTouchMove , this);
this._$onTouchStartButton = __bind(this._onTouchStartButton, this);
this._$onTouchEndButton = __bind(this._onTouchEndButton, this);
this._baseEl.addEventListener( 'touchstart' , this._$onTouchStart , false );
this._mouseLeft.addEventListener( 'touchstart' , this._$onTouchStartButton , false );
this._mouseLeft.addEventListener( 'touchend' , this._$onTouchEndButton , false );
this._container.addEventListener( 'touchend' , this._$onTouchEnd , false );
this._container.addEventListener( 'touchmove' , this._$onTouchMove , false );
if( this._mouseSupport ){
this._$onMouseDown = __bind(this._onMouseDown , this);
this._$onMouseUp = __bind(this._onMouseUp , this);
this._$onMouseMove = __bind(this._onMouseMove , this);
this._$onMouseDownButton = __bind(this._onMouseDownButton, this);
this._$onMouseUpButton = __bind(this._onMouseUpButton, this);
this._baseEl.addEventListener( 'mousedown' , this._$onMouseDown , false );
this._container.addEventListener( 'mouseup' , this._$onMouseUp , false );
this._mouseLeft.addEventListener( 'mousedown' , this._$onTouchStartButton , false );
this._mouseLeft.addEventListener( 'mouseup' , this._$onTouchEndButton , false );
this._container.addEventListener( 'mousemove' , this._$onMouseMove , false );
}
}
VirtualJoystick.prototype.destroy = function()
{
this._container.removeChild(this._baseEl);
this._container.removeChild(this._stickEl);
this._mouseLeft.removeEventListener( 'touchstart' , this._$onTouchStartButton , false );
this._mouseLeft.removeEventListener( 'touchend' , this._$onTouchEndButton , false );
this._container.removeChild(this._mouseLeft);
this._container.removeEventListener( 'touchstart' , this._$onTouchStart , false );
this._container.removeEventListener( 'touchend' , this._$onTouchEnd , false );
this._container.removeEventListener( 'touchmove' , this._$onTouchMove , false );
if( this._mouseSupport ){
this._container.removeEventListener( 'mouseup' , this._$onMouseUp , false );
this._container.removeEventListener( 'mousedown' , this._$onMouseDown , false );
this._container.removeEventListener( 'mousemove' , this._$onMouseMove , false );
}
}
/**
* @returns {Boolean} true if touchscreen is currently available, false otherwise
*/
VirtualJoystick.touchScreenAvailable = function()
{
return 'createTouch' in document ? true : false;
}
//////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////////////////////////////////////////
VirtualJoystick.prototype.deltaX = function(){ return this._stickX - this._baseX; }
VirtualJoystick.prototype.deltaY = function(){ return this._stickY - this._baseY; }
VirtualJoystick.prototype.up = function(){
if( this._pressed === false ) return false;
var deltaX = this.deltaX();
var deltaY = this.deltaY();
if( deltaY >= 0 ) return false;
if( Math.abs(deltaY) < this._range && Math.abs(deltaY) < Math.abs(deltaX) ){
return false;
}
return true;
}
VirtualJoystick.prototype.down = function(){
if( this._pressed === false ) return false;
var deltaX = this.deltaX();
var deltaY = this.deltaY();
if( deltaY <= 0 ) return false;
if( Math.abs(deltaY) < this._range && Math.abs(deltaY) < Math.abs(deltaX) ){
return false;
}
return true;
}
VirtualJoystick.prototype.right = function(){
if( this._pressed === false ) return false;
var deltaX = this.deltaX();
var deltaY = this.deltaY();
if( deltaX <= 0 ) return false;
if( Math.abs(deltaX) < this._range && Math.abs(deltaY) > Math.abs(deltaX) ){
return false;
}
return true;
}
VirtualJoystick.prototype.left = function(){
if( this._pressed === false ) return false;
var deltaX = this.deltaX();
var deltaY = this.deltaY();
if( deltaX >= 0 ) return false;
if( Math.abs(deltaX) < this._range && Math.abs(deltaY) > Math.abs(deltaX) ){
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////////////////////////////////////////
VirtualJoystick.prototype._onUp = function()
{
this._pressed = false;
this._stickEl.style.display = "none";
//this._baseEl.style.display = "none";
this._baseX = this._baseY = 0;
this._stickX = this._stickY = 0;
this._eraseInterval();
}
VirtualJoystick.prototype._onDown = function(x, y)
{
this._pressed = true;
this._stickX = x;
this._stickY = y;
this._stickEl.style.display = "";
this._stickEl.style.left = (this._stickX - this._stickEl.width /2)+"px";
this._stickEl.style.top = (this._stickY - this._stickEl.height/2)+"px";
this._startInterval();
}
VirtualJoystick.prototype._onMove = function(x, y)
{
if( this._pressed === true ){
this._stickX = x;
this._stickY = y;
this._stickEl.style.left = (x - this._stickEl.width /2)+"px";
this._stickEl.style.top = (y - this._stickEl.height/2)+"px";
this._startInterval();
}
}
VirtualJoystick.prototype._startInterval = function(x, y) {
this._eraseInterval();
var centerXBase = $(this._baseEl).offset().left + ($(this._baseEl).width()/2);
var centerXStick = $(this._stickEl).offset().left + ($(this._stickEl).width()/2);
this.offsetX = parseInt((centerXStick - centerXBase) / 5);
//////
var centerYBase = $(this._baseEl).offset().top + ($(this._baseEl).height()/2);
var centerYStick = $(this._stickEl).offset().top + ($(this._stickEl).height()/2);
this.offsetY = parseInt((centerYStick - centerYBase) / 5);
if(this.offsetX > 15) {
this.offsetX = 15;
}
if(this.offsetY > 15) {
this.offsetY = 15;
}
//calculate movements from offsets
var self = this;
this._timer = setInterval(function() {
if(self._callback) {
self._callback.call(self._callbackcontext, [self.offsetX, self.offsetY]);
}
}, 10);
}
VirtualJoystick.prototype._eraseInterval = function() {
if(this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
}
//////////////////////////////////////////////////////////////////////////////////
// bind touch events (and mouse events for debug) //
//////////////////////////////////////////////////////////////////////////////////
VirtualJoystick.prototype._onMouseUp = function(event)
{
return this._onUp();
}
VirtualJoystick.prototype._onMouseDown = function(event)
{
var x = event.pageX;
var y = event.pageY;
return this._onDown(x, y);
}
VirtualJoystick.prototype._onMouseMove = function(event)
{
var x = event.pageX;
var y = event.pageY;
return this._onMove(x, y);
}
VirtualJoystick.prototype._onTouchStart = function(event)
{
if( event.touches.length != 1 ) return;
event.preventDefault();
var x = event.touches[ 0 ].pageX;
var y = event.touches[ 0 ].pageY;
return this._onDown(x, y)
}
VirtualJoystick.prototype._onTouchEnd = function(event)
{
//??????
// no preventDefault to get click event on ios
event.preventDefault();
return this._onUp()
}
VirtualJoystick.prototype._onTouchMove = function(event)
{
if(event.touches && event.touches.length != 1 ) return;
event.preventDefault();
var x = event.touches[ 0 ].pageX;
var y = event.touches[ 0 ].pageY;
return this._onMove(x, y)
}
VirtualJoystick.prototype._onTouchStartButton = function(event)
{
if( event.touches && event.touches.length != 1 ) return;
event.preventDefault();
this._callbackButton.call(this._callbackcontext, ['down']);
}
VirtualJoystick.prototype._onTouchEndButton = function(event)
{
//??????
// no preventDefault to get click event on ios
event.preventDefault();
this._callbackButton.call(this._callbackcontext, ['up']);
}
//////////////////////////////////////////////////////////////////////////////////
// build default stickEl and baseEl //
//////////////////////////////////////////////////////////////////////////////////
VirtualJoystick.prototype._buildJoystickBase = function()
{
var canvas = document.createElement( 'canvas' );
canvas.style.zIndex = 2;
canvas.width = 126;
canvas.height = 126;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "cyan";
ctx.lineWidth = 6;
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "cyan";
ctx.lineWidth = 2;
ctx.arc( canvas.width/2, canvas.width/2, 60, 0, Math.PI*2, true);
ctx.stroke();
return canvas;
}
VirtualJoystick.prototype._buildJoystickStick = function()
{
var canvas = document.createElement( 'canvas' );
canvas.style.zIndex = 2;
canvas.width = 86;
canvas.height = 86;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "cyan";
ctx.lineWidth = 6;
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
ctx.stroke();
return canvas;
}
VirtualJoystick.prototype._buildJoystickButton = function()
{
var canvas = document.createElement( 'canvas' );
canvas.style.zIndex = 2;
canvas.width = 86;
canvas.height = 86;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "green";
ctx.lineWidth = 6;
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
ctx.stroke();
return canvas;
}

View File

@ -0,0 +1,208 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.BusConnection = $.spcExtend(wdi.EventObject.prototype, {
ws: null,
subscriptions: [],
_busUser: null,
_busPass: null,
fileServerBaseUrl: null,
queue: '',
binary: false,
init: function(c) {
this.superInit();
this.ws = c.websocket || new wdi.WebSocketWrapper();
this.clusterNodeChooser = c.clusterNodeChooser || new wdi.ClusterNodeChooser();
this.binary = c.binary || false;
},
connect: function(c) {
if (!c['useBus']) {
wdi.Debug.warn("Not using the bus");
return;
}
this._vdiBusToken = c['vdiBusToken'];
if (!c['busHostList']) {
wdi.Debug.warn("Deprecated: using old busHost & busPort params");
c['busHostList'] = [{
host: c['busHost'],
port: c['busPort']
}];
}
this.clusterNodeChooser.setNodeList(c['busHostList']);
if (Modernizr['websocketsbinary']) {
this.binary = true;
}
this._busUser = c['busUser'];
this._busPass = c['busPass'];
this._websockifyProtocol = c['protocol'];
this._websockifyHost = c['host'];
this._websockifyPort = c['port'];
this.fileServerBaseUrl = c['busFileServerBaseUrl'];
this.subscriptions = c['busSubscriptions'];
this._connectToNextHost();
},
_connectToNextHost: function () {
var busData = this.clusterNodeChooser.getAnother();
// c['protocol'] is the protocol we use to connect to websockify
// ie: ws, wss, https, ...
var url = wdi.Utils.generateWebSocketUrl(
this._websockifyProtocol,
this._websockifyHost,
this._websockifyPort,
busData.host,
busData.port,
'raw',
this._vdiBusToken
);
var websocketProtocol = 'base64';
if (this.binary) {
websocketProtocol = 'binary';
}
this.ws.connect(url, websocketProtocol);
wdi.Debug.log("BusConnection: using protocol: " + websocketProtocol);
if (this.binary) {
this.ws.setBinaryType('arraybuffer');
}
this.setListeners();
},
disconnect: function() {
this.ws.close();
},
setListeners: function() {
var self = this;
this.ws.onOpen(function(e) {
self._send("CONNECT\nlogin:" + self._busUser + "\npasscode:" + self._busPass + "\n\n\x00");
});
this.ws.onMessage(function(e) {
var message;
var result;
if (!self.binary) {
message = Base64.decodeStr(e.data);
} else {
message = String.fromCharCode.apply(null, new Uint8Array(e.data));
// Fix accented chars
// [ http://stackoverflow.com/questions/5396560/how-do-i-convert-special-utf-8-chars-to-their-iso-8859-1-equivalent-using-javasc ]
message = decodeURIComponent(escape(message));
}
var subChunks = message.split("\0");
if (subChunks.length == 1) {
// there is no \0 in the full message, add it to the queue
self.queue += subChunks[0];
} else {
// at least one \0, process all but the last subchunk (that has no \0)
for (var i = 0; i < subChunks.length - 1; i++) {
message = self.queue + subChunks[i];
result = self.parseMessage(message);
self.fire('busMessage', result);
self.queue = '';
}
// last chunk is now the queue
self.queue = subChunks[subChunks.length - 1];
}
});
this.ws.onClose(function(e) {
wdi.Debug.log('BusConnection CLOSED! connecting again in 1 second');
self.fire('error', e);
});
this.ws.onError(function(e) {
wdi.Debug.error('BusConnection ERROR:', e);
});
},
parseMessage: function(message) {
try {
var arr = message.split("\n\n");
var header = arr[0].trim();
var body = arr[1].replace(/\x00/, '').trim();
if (body.length != 0) {
// there is content, so convert to object
body = JSON.parse(body);
} else {
body = null;
}
arr = header.split("\n");
var verb = arr.shift();
header = "{";
var len = arr.length;
for (var i = 0;i < len;i++) {
var headerName = arr[i].split(':')[0];
header += '"' + headerName + '":"' + arr[i].replace(headerName + ':', '') + '"';
if (i != len-1) {
header += ",";
}
}
header += "}";
return {'verb':verb, 'header':JSON.parse(header), 'body':body};
} catch (e) {
wdi.Debug.error("Error parsing Bus Info: ", e);
return {"verb":"ERROR"};
}
},
setSubscriptions: function() {
var len = this.subscriptions.length;
for (var i = 0; i < len;i++) {
this.subscribe(this.subscriptions[i]);
}
},
send: function(message) {
var destination = this.subscriptions[0];
this._send("SEND\ndestination:" + destination + "\ncontent-type:text/plain\n\n" + JSON.stringify(message) + "\x00");
},
subscribe: function(destination) {
//header browser: true for queue's to multiple subscribers
this._send("SUBSCRIBE\ndestination:" + destination + "\n\n\x00");
},
_send: function(message) {
if (!this.binary) {
this.ws.send(Base64.encodeStr(message));
} else {
this.ws.send(message);
}
}
});

View File

@ -0,0 +1,62 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.ClusterNodeChooser = $.spcExtend(wdi.EventObject.prototype, {
init: function (c) {
},
setNodeList: function (nodeList) {
this._nodeList = this._shuffle(nodeList);
this._nodeListLength = this._nodeList.length;
this._currentIndex = 0;
},
getAnother: function () {
var toReturn = this._nodeList[this._currentIndex++ % this._nodeListLength];
return toReturn;
},
// recipe from: http://stackoverflow.com/a/6274398
_shuffle: function (list) {
var counter = list.length,
temp,
index;
while (counter > 0) {
index = Math.floor(Math.random() * counter);
counter--;
temp = list[counter];
list[counter] = list[index];
list[index] = temp;
}
return list;
}
});

View File

@ -0,0 +1,69 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.ConnectionControl = $.spcExtend(wdi.EventObject.prototype, {
socket: null,
pendingTimeToConnectionLost: null,
previousTimeOut: null,
init: function(c) {
this.superInit();
this.socket = c.socket || new wdi.Socket();
},
connect: function(c) {
var url = wdi.Utils.generateWebSocketUrl(c.protocol, c.host, c.port, null, null,'raw', c.heartbeatToken);
this.socket.connect(url);
this.pendingTimeToConnectionLost = c.heartbeatTimeout;
wdi.Debug.log('ConnectionControl: connected');
this.setListeners();
},
disconnect: function() {
if(this.previousTimeOut){
clearTimeout(this.previousTimeOut);
}
this.socket.disconnect();
},
setListeners: function() {
var self = this;
this.socket.setOnMessageCallback(function(e) {
wdi.Debug.log('ConectionControl: beat');
clearTimeout(self.previousTimeOut);
self.previousTimeOut = setTimeout(function() {
wdi.Debug.log('ConnectionControl: firing connectionLost event');
self.fire('connectionLost', e);
}, self.pendingTimeToConnectionLost);
});
}
});

View File

@ -0,0 +1,67 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketController = $.spcExtend(wdi.EventObject.prototype, {
sizeDefiner: null,
packetExtractor: null,
init: function(c) {
this.superInit();
this.sizeDefiner = c.sizeDefiner;
this.packetExtractor = c.packetExtractor;
},
getNextPacket: function(data) {
var self = this;
if (wdi.logOperations) {
wdi.DataLogger.setNetworkTimeStart();
}
var size = this.sizeDefiner.getSize(data);
this.packetExtractor.getBytes(size, function(bytes) {
var status = this.sizeDefiner.getStatus();
this.execute(new wdi.RawMessage({status: status, data: bytes}));
self.getNextPacket(bytes);
}, this);
},
execute: function(message) {
try {
this.fire('chunkComplete', message);
} catch (e) {
console.error('PacketTroller: ', e);
}
}
});

View File

@ -0,0 +1,77 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketExtractor = $.spcExtend(wdi.EventObject.prototype, {
socketQ: null,
numBytes: null,
callback: null,
scope: null,
init: function(c) {
this.superInit();
this.socketQ = c.socketQ;
this.setListener();
},
setListener: function() {
this.socketQ.addListener('message', function() {
if (wdi.logOperations) {
wdi.DataLogger.setNetworkTimeStart();
}
this.getBytes(this.numBytes, this.callback, this.scope);
}, this);
},
getBytes: function(numBytes, callback, scope) {
var retLength = this.socketQ.rQ.getLength();
this.numBytes = numBytes;
this.callback = callback;
this.scope = scope;
if (numBytes !== null && retLength >= numBytes) {
var ret;
if (numBytes) {
ret = this.socketQ.rQ.shift(numBytes);
} else {
ret = new Uint8Array(0);
}
this.numBytes = null;
this.callback = null;
this.scope = null;
callback.call(scope, ret);
} else {
if (wdi.logOperations) {
wdi.DataLogger.logNetworkTime();
}
}
}
});

View File

@ -0,0 +1,77 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketLinkFactory = {
extract: function(header, queue) {
switch (header.type) {
case wdi.SpiceVars.SPICE_MSG_SET_ACK:
return new wdi.RedSetAck().demarshall(queue);
case wdi.SpiceVars.SPICE_MSG_PING:
return new wdi.RedPing().demarshall(queue, header.size);
case wdi.SpiceVars.SPICE_MSG_MIGRATE:
return new wdi.RedMigrate().demarshall(queue);
case wdi.SpiceVars.SPICE_MSG_MIGRATE_DATA:
return new wdi.RedMigrateData().demarshall(queue, header.size);
case wdi.SpiceVars.SPICE_MSG_WAIT_FOR_CHANNELS:
return new wdi.RedWaitForChannels().demarshall(queue);
case wdi.SpiceVars.SPICE_MSG_DISCONNECTING:
return new wdi.RedDisconnect().demarshall(queue);
case wdi.SpiceVars.SPICE_MSG_NOTIFY:
var packet = new wdi.RedNotify().demarshall(queue);
return packet;
case wdi.SpiceVars.SPICE_MSG_MAIN_MOUSE_MODE:
return new wdi.SpiceMouseMode().demarshall(queue);
}
}
};
wdi.PacketLinkProcess = {
process: function(header, packet, channel) {
switch(header.type) {
case wdi.SpiceVars.SPICE_MSG_SET_ACK:
var body = wdi.SpiceObject.numberTo32(packet.generation);
channel.setAckWindow(packet.window)
channel.sendObject(body, wdi.SpiceVars.SPICE_MSGC_ACK_SYNC);
break;
case wdi.SpiceVars.SPICE_MSG_PING:
var body = new wdi.RedPing({id: packet.id, time: packet.time}).marshall();
channel.sendObject(body, wdi.SpiceVars.SPICE_MSGC_PONG);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_MOUSE_MODE:
channel.fire('mouseMode', packet.current_mode);
break;
case wdi.SpiceVars.SPICE_MSG_NOTIFY:
channel.fire('notify');
break;
}
}
};

View File

@ -0,0 +1,80 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.PacketReassembler = $.spcExtend(wdi.EventObject.prototype, {
packetController: null,
currentHeader: null,
statusToString: null,
sizeDefinerConstant: null,
init: function(c) {
this.superInit();
this.packetController = c.packetController;
this.sizeDefinerConstant = wdi.SizeDefiner.prototype;
this.statusToString = [];
this.statusToString[this.sizeDefinerConstant.STATUS_REPLY_BODY] = 'reply';
this.statusToString[this.sizeDefinerConstant.STATUS_ERROR_CODE] = 'errorCode';
this.statusToString[this.sizeDefinerConstant.STATUS_BODY] = 'spicePacket';
this.setListeners();
},
start: function () {
this.packetController.getNextPacket();
},
setListeners: function() {
this.packetController.addListener('chunkComplete', function(e) {
var rawMessage = e;
var status = rawMessage.status;
switch(status) {
case this.sizeDefinerConstant.STATUS_HEADER:
case this.sizeDefinerConstant.STATUS_REPLY:
this.currentHeader = rawMessage;
break;
case this.sizeDefinerConstant.STATUS_REPLY_BODY:
case this.sizeDefinerConstant.STATUS_BODY:
var tmpBuff = new Uint8Array(rawMessage.data.length + this.currentHeader.data.length);
tmpBuff.set(this.currentHeader.data);
tmpBuff.set(rawMessage.data, this.currentHeader.data.length);
rawMessage.data = tmpBuff;
rawMessage.status = this.statusToString[status];
this.fire('packetComplete', rawMessage);
break;
default:
rawMessage.status = this.statusToString[status];
this.fire('packetComplete', rawMessage);
break;
}
}, this);
}
});

View File

@ -0,0 +1,52 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.ReassemblerFactory = {
getPacketReassembler: function(socketQ) {
var pE = this.getPacketExtractor(socketQ);
var sD = this.getSizeDefiner();
var pC = this.getPacketController(pE, sD);
return new wdi.PacketReassembler({packetController: pC});
},
getPacketExtractor: function(socketQ) {
return new wdi.PacketExtractor({socketQ: socketQ});
},
getSizeDefiner: function() {
return new wdi.SizeDefiner();
},
getPacketController: function(packetExtractor, sizeDefiner) {
return new wdi.PacketController({packetExtractor: packetExtractor, sizeDefiner: sizeDefiner});
}
};

View File

@ -0,0 +1,99 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SizeDefiner = $.spcExtend(wdi.DomainObject, {
ERROR_CODE_SIZE: 4,
status: null,
STATUS_READY: 0,
STATUS_REPLY: 1,
STATUS_REPLY_BODY: 2,
STATUS_ERROR_CODE: 3,
STATUS_MESSAGE: 4,
STATUS_HEADER: 5,
STATUS_BODY: 6,
isHeader: false,
init: function(c) {
this.status = this.STATUS_READY;
},
getSize: function(arr) {
if (this.STATUS_READY === this.status) {
this.status++;
return wdi.SpiceLinkHeader.prototype.objectSize;
} else if (this.STATUS_REPLY === this.status) {
this.status++;
return this.getReplyBodySize(arr);
} else if (this.STATUS_REPLY_BODY === this.status) {
this.status++;
return this.ERROR_CODE_SIZE;
} else if (this.STATUS_ERROR_CODE === this.status) {
this.status++;
this.isHeader = true;
return 6; //wdi.SpiceDataHeader.prototype.objectSize access here is slow
} else {
if (this.isHeader) {
this.isHeader = false;
return this.getBodySizeFromArrayHeader(arr);
} else {
this.isHeader = true;
return 6;//wdi.SpiceDataHeader.prototype.objectSize; access here is slow
}
}
},
getReplyBodySize: function (arr) {
var queue = wdi.GlobalPool.create('ViewQueue');
queue.setData(arr);
var header = new wdi.SpiceLinkHeader().demarshall(queue);
wdi.GlobalPool.discard('ViewQueue', queue);
return header.size;
},
getBodySizeFromArrayHeader: function (arr) {
var queue = wdi.GlobalPool.create('ViewQueue');
queue.setData(arr);
var header = new wdi.SpiceDataHeader().demarshall(queue);
wdi.GlobalPool.discard('ViewQueue', queue);
return header.size;
},
getStatus: function() {
if (this.status === this.STATUS_MESSAGE && this.isHeader) {
return this.STATUS_HEADER;
} else if (this.status === this.STATUS_MESSAGE && !this.isHeader) {
return this.STATUS_BODY;
} else {
return this.status;
}
}
});

View File

@ -0,0 +1,131 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.socketStatus = {
'idle':0,
'prepared':1,
'connected':2,
'disconnected':3,
'failed':4
};
//Works only with arrays of bytes (this means each value is a number in 0 to 255)
wdi.Socket = $.spcExtend(wdi.EventObject.prototype, {
websocket: null,
status: wdi.socketStatus.idle,
binary: false,
connect: function(uri) {
var self = this;
var protocol = 'base64'; //default protocol
if(Modernizr['websocketsbinary']) {
protocol = 'binary';
this.binary = true;
}
this.websocket = new WebSocket(uri, protocol);
wdi.Debug.log("Socket: using protocol: "+protocol);
if(this.binary) {
this.websocket.binaryType = 'arraybuffer';
}
this.status = wdi.socketStatus.prepared;
this.websocket.onopen = function() {
self.status = wdi.socketStatus.connected;
self.fire('open');
};
this.websocket.onmessage = function(e) {
self.fire('message', e.data);
};
this.websocket.onclose = function(e) {
self.status = wdi.socketStatus.disconnected;
console.warn('Spice Web Client: ', e.code, e.reason);
self.disconnect();
self.fire('error', e);
};
this.websocket.onerror = function(e) {
self.status = wdi.socketStatus.failed;
self.fire('error', e);
};
},
setOnMessageCallback: function(callback) {
this.websocket.onmessage = callback;
},
send: function(message) {
try {
this.websocket.send(this.encode_message(message));
} catch (err) {
this.status = wdi.socketStatus.failed;
this.fire('error', err);
}
},
disconnect: function() {
if (this.websocket) {
this.websocket.onopen = function() {};
this.websocket.onmessage = function() {};
this.websocket.onclose = function() {};
this.websocket.onerror = function() {};
this.websocket.close();
this.websocket = null;
}
},
setStatus: function(status) {
this.status = status;
this.fire('status', status);
},
getStatus: function() {
return this.status;
},
encode_message: function(mess) {
if(!this.binary) {
var arr = Base64.encode(mess);
return arr;
}
var len = mess.length;
var buffer = new ArrayBuffer(len);
var u8 = new Uint8Array(buffer);
u8.set(mess);
return u8;
}
});

View File

@ -0,0 +1,98 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SocketQueue = $.spcExtend(wdi.EventObject.prototype, {
rQ: null,
sQ: null,
socket: null,
init: function(c) {
this.superInit();
this.socket = c.socket || new wdi.Socket();
this.rQ = c.rQ || new wdi.FixedQueue();
this.sQ = c.sQ || new wdi.Queue();
this.setup();
},
setup: function() {
this.socket.addListener('open', function() {
this.fire('open');
}, this);
this.socket.addListener('message', function(data) {
this.rQ.push(new Uint8Array(data));
this.fire('message');
}, this);
this.socket.addListener('close', function(e) {
this.fire('close', e);
}, this);
this.socket.addListener('error', function(e) {
this.fire('error', e);
}, this);
},
getStatus: function() {
return this.socket.getStatus();
},
connect: function(uri) {
this.socket.connect(uri);
},
disconnect: function() {
this.socket.disconnect();
},
send: function(data, shouldFlush) {
//check for shouldFlush parameter, by default is true
if (shouldFlush === undefined) {
var flush = true;
} else {
var flush = shouldFlush;
}
//performance: avoid passing through the queue if there is no queue and
//we have flush!
if(this.sQ.getLength() == 0 && flush) {
this.socket.send(data);
return;
}
//normal operation, append to buffer and send if flush
this.sQ.push(data);
if (flush) this.flush();
},
flush: function() {
var data = this.sQ.shift();
this.socket.send(data);
}
});

View File

@ -0,0 +1,226 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
//Must fire event types: connectionId and message
wdi.SpiceChannel = $.spcExtend(wdi.EventObject.prototype, {
counter: 0,
ackWindow: 0,
connectionId: 0,
socketQ: null,
packetReassembler: null,
channel: 1,
proxy: null,
token: null,
init: function(c) {
this.superInit();
this.socketQ = c.socketQ || new wdi.SocketQueue();
this.packetReassembler = c.packetReassembler || wdi.ReassemblerFactory.getPacketReassembler(this.socketQ);
this.setListeners();
this.ackWindow = 0;
},
setListeners: function() {
var date;
this.packetReassembler.addListener('packetComplete', function(e) {
var rawMessage = e;
if (rawMessage.status === 'spicePacket') {
if (wdi.logOperations) {
wdi.DataLogger.logNetworkTime();
date = Date.now();
}
var rsm = this.getRawSpiceMessage(rawMessage.data);
if (rsm) {
if (wdi.logOperations && rsm.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY) {
wdi.DataLogger.setStartTime(date);
}
this.fire('message', rsm);
}
} else if (rawMessage.status === 'reply') {
var packet = this.getRedLinkReplyBytes(rawMessage.data);
this.send(packet);
} else if (rawMessage.status === 'errorCode') {
var packet = this.getErrorCodeBytes(rawMessage.data);
if (packet) {
this.send(packet);
}
this.fire('channelConnected');
}
}, this);
this.socketQ.addListener('open', function() {
var packet = this.getRedLinkMessBytes();
this.send(packet);
this.proxy ? this.proxy.end() : false;
}, this);
this.socketQ.addListener('close', function(e) {
if (this.channel === 1) {
this.fire('error', e);
}
this.socketQ.disconnect();
}, this);
this.socketQ.addListener('error', function() {
this.fire('error', 3);
this.socketQ.disconnect();
// throw new wdi.Exception({message:"Socket error", errorCode: 2});
}, this);
},
connect: function(connectionInfo, channel, connectionId, proxy) {
var url = wdi.Utils.generateWebSocketUrl(connectionInfo.protocol, connectionInfo.host, connectionInfo.port, connectionInfo.vmHost, connectionInfo.vmPort, 'spice', connectionInfo.vmInfoToken);
this.channel = channel;
this.connectionId = connectionId || 0;
this.socketQ.connect(url);
this.proxy = proxy;
this.token = connectionInfo.token;
this.packetReassembler.start();
},
disconnect: function () {
this.socketQ.disconnect();
},
send: function(data, flush) {
this.socketQ.send(data, flush);
},
sendObject: function(data, type, flush) {
var packet = new wdi.SpiceDataHeader({
type:type,
size:data.length
}).marshall();
packet = packet.concat(data);
this.send(packet, flush);
},
setAckWindow: function(window) {
this.ackWindow = window;
this.counter = 0;
},
getRawSpiceMessage: function (rawData) {
var headerQueue = wdi.GlobalPool.create('ViewQueue');
var body = wdi.GlobalPool.create('ViewQueue');
var header = new Uint8Array(rawData, 0, wdi.SpiceDataHeader.prototype.objectSize);
headerQueue.setData(header);
var headerObj = new wdi.SpiceDataHeader().demarshall(headerQueue);
wdi.GlobalPool.discard('ViewQueue', headerQueue);
var rawBody = rawData.subarray(wdi.SpiceDataHeader.prototype.objectSize);
body.setData(rawBody);
this.counter++;
if(this.ackWindow && this.counter === this.ackWindow) {
this.counter = 0;
var ack = new wdi.SpiceDataHeader({
type: wdi.SpiceVars.SPICE_MSGC_ACK,
size:0
}).marshall();
this.send(ack);
}
var packet = wdi.PacketLinkFactory.extract(headerObj, body) || false;
if (packet) {
wdi.PacketLinkProcess.process(headerObj, packet, this);
wdi.GlobalPool.discard('ViewQueue', body);
return false;
} else {
var rawSpiceMessage = wdi.GlobalPool.create('RawSpiceMessage');
rawSpiceMessage.set(headerObj, body, this.channel);
return rawSpiceMessage;
}
},
//This functions are to avoid hardcoded values on logic
getRedLinkReplyBytes: function(data) {
if (this.token) {
var newq = new wdi.ViewQueue();
newq.setData(data);
newq.eatBytes(wdi.SpiceLinkHeader.prototype.objectSize)
var myBody = new wdi.SpiceLinkReply().demarshall(newq);
//Returnnig void bytes or encrypted ticket
var key = wdi.SpiceObject.stringHexToBytes(RSA_public_encrypt(this.token, myBody.pub_key));
return key;
} else {
return wdi.SpiceObject.stringToBytesPadding('', 128);
}
},
getRedLinkMessBytes: function() {
var header = new wdi.SpiceLinkHeader({magic:1363428690, major_version:2, minor_version:2, size:22}).marshall();
var body = new wdi.SpiceLinkMess({
connection_id:this.connectionId,
channel_type:this.channel,
caps_offset:18,
num_common_caps: 1,
common_caps: (1 << wdi.SpiceVars.SPICE_COMMON_CAP_MINI_HEADER)
}).marshall();
return header.concat(body);
},
getErrorCodeBytes: function (data) {
var errorQ = wdi.GlobalPool.create('ViewQueue');
errorQ.setData(data);
var errorCode = wdi.SpiceObject.bytesToInt32NoAllocate(errorQ);
wdi.GlobalPool.discard('ViewQueue', errorQ);
if (errorCode === 0) {
if (this.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY) {
var redDisplayInit = new wdi.SpiceDataHeader({type: wdi.SpiceVars.SPICE_MSGC_DISPLAY_INIT, size: 14}).marshall();
//TODO: ultrahardcoded value here, move to configuration
//DUE To high level storage the memory specified for cache
//is 2-3 times bigger than expected.
var cache_size = 0*1024*1024;
var body = new wdi.SpiceCDisplayInit({
pixmap_cache_id:1,
pixmap_cache_size: cache_size,
glz_dictionary_id: 0,
glz_dictionary_window_size: 1
}).marshall();
return redDisplayInit.concat(body);
} else if(this.channel == wdi.SpiceVars.SPICE_CHANNEL_MAIN) {
return new wdi.SpiceDataHeader({type: wdi.SpiceVars.SPICE_MSGC_MAIN_ATTACH_CHANNELS, size: 0}).marshall();
}
} else {
throw new wdi.Exception({message: "Server refused client", errorCode: 2});
}
}
});

View File

@ -0,0 +1,88 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.WebSocketWrapper = $.spcExtend({}, {
ws: {},
onopen: null,
onmessage: null,
onclose: null,
onerror: null,
init: function() {
},
connect: function(url, protocol) {
this.ws = new WebSocket(url, protocol);
},
onOpen: function(callback) {
this.ws.onopen = callback;
},
onMessage: function(callback) {
this.ws.onmessage = callback;
},
onClose: function(callback) {
this.ws.onclose = callback;
},
onError: function(callback) {
this.ws.onerror = callback;
},
setBinaryType: function(type) {
this.ws.binaryType = type;
},
close: function() {
if (!this.ws || !this.ws.close) {
return;
}
this.ws.close();
this.ws.onopen = function () {};
this.ws.onmessage = function () {};
this.ws.onclose = function () {};
this.ws.onerror = function () {};
this.onopen = function() {};
this.onmessage = function() {};
this.onclose = function() {};
this.onerror = function() {};
},
send: function(message) {
this.ws.send(message);
}
});

View File

@ -0,0 +1,16 @@
{
"name": "eyeos-spiceproxy",
"version": "0.0.156",
"description": "spice client network stack library",
"main": "src/spiceproxy.js",
"author": "eyeOS",
"homepage": "http://www.eyeos.com",
"dependencies": {
},
"devDependencies": {
"eyeos-gruntfile": "*",
"grunt": "0.4.5",
"grunt-init": "0.3.2",
"mocha": "2.4.5"
}
}

View File

@ -0,0 +1,247 @@
wdi.BUS_TYPES = {
file: 0, // obsolete
print: 1, // obsolete
launchApplication: 2,
windowManagement: 3,
menu: 5,
networkDriveManagement: 6,
// Messages used during developing (for benchmarks and whatever).
// you should not use them in code for production purposes.
killApplicationDoNotUseInProductionEver: 34423423
};
wdi.BusProcess = $.spcExtend(wdi.EventObject.prototype, {
busConnection: null,
clientGui: null,
init: function(c) {
this.superInit();
this.clientGui = c.clientGui;
this.busConnection = c.busConnection;
},
process: function(message) {
switch(message['verb']) {
case "CONNECTED":
this.busConnection.setSubscriptions();
this.fire('busConnected');
break;
case "MESSAGE":
this.parseMessage(message['body']);
break;
case "ERROR":
console.error("Bus error");
break;
default:
wdi.Debug.warn("Not implemented Stomp Verb: " + message['verb']);
}
},
parseMessage: function(body) {
switch(parseInt(body['type'])) {
case wdi.BUS_TYPES.launchApplication:
this.parseLaunchApplicationMessage(body);
break;
case wdi.BUS_TYPES.killApplicationDoNotUseInProductionEver:
// this is a message we send to the other side of the bus
// so do nothing.
break;
case wdi.BUS_TYPES.windowManagement:
this.parseWindowManagementMessage(body);
break;
case wdi.BUS_TYPES.menu:
this.handleMenuMessage(body);
break;
case wdi.BUS_TYPES.networkDriveManagement:
this._handleNetworkDriveMessage(body);
break;
default:
wdi.Debug.info("Bus type '" + body['type'] + "' not implemented.");
}
},
_handleNetworkDriveMessage : function(message) {
if(message.event != 'reMountNetworkDrive') {
this.fire('networkDriveResponse', message);
}
},
getMenu: function() {
this.busConnection.send(
{
"type": wdi.BUS_TYPES.menu,
"value": false,
"event": 'request'
}
)
},
handleMenuMessage: function(message) {
if(message.event == 'response') {
this.fire('menuResponse', message);
}
},
parseWindowManagementMessage: function(message) {
switch (message['event']) {
case 'windowList':
case 'windowCreated':
case 'windowClosed':
case 'windowMoved':
case 'windowResized':
case 'windowFocused':
case 'windowMinimized':
case 'windowRestored':
case 'windowMaximized':
this.fire(message['event'], message['value']);
break;
default:
wdi.Debug.info("Event '" + message['event'] + "' not implemented.")
}
},
closeWindow: function(hwnd) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'closeWindow',
"hwnd": hwnd
}
)
);
},
moveWindow: function(hwnd, x, y) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'moveWindow',
"hwnd": hwnd,
"left": x,
"top": y
}
)
);
},
minimizeWindow: function(hwnd) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'minimizeWindow',
"hwnd": hwnd
}
)
);
},
maximizeWindow: function(hwnd) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'maximizeWindow',
"hwnd": hwnd
}
)
);
},
restoreWindow: function(hwnd) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'restoreWindow',
"hwnd": hwnd
}
)
);
},
focusWindow: function(hwnd) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'focusWindow',
"hwnd": hwnd
}
)
);
},
resizeWindow: function(hwnd, width, height) {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'resizeWindow',
"hwnd": hwnd,
"width": width,
"height": height
}
)
);
},
requestWindowList: function() {
this.busConnection.send(
this._constructWindowManagementMessage(
{
"event": 'getWindowList'
}
)
)
},
executeCommand: function(cmd) {
this.busConnection.send(
{
"type": wdi.BUS_TYPES.launchApplication,
"application": cmd
}
)
},
_constructWindowManagementMessage: function(obj) {
if (obj['event'] === undefined) {
throw new Error("You should pass an 'event' attribute in the object");
}
var ret = {
'type': wdi.BUS_TYPES.windowManagement,
'event': obj['event'],
'value': {}
};
for (var i in obj) {
if (i != 'event' && obj.hasOwnProperty(i)) {
ret['value'][i] = obj[i];
}
}
return ret;
},
reMountNetworkDrive: function(host, username, password) {
this.busConnection.send(
{
"type": wdi.BUS_TYPES.networkDriveManagement,
"event": "reMountNetworkDrive",
"host": host,
"username": username,
"password": password
}
)
},
parseLaunchApplicationMessage: function (message) {
switch (message['event']) {
case 'applicationLauncherWrongAppPathError':
this.fire('wrongPathError', message);
break;
case 'applicationLaunchedSuccessfully':
this.fire('applicationLaunchedSuccessfully', message);
break;
default:
wdi.Debug.info("Event '" + message['event'] + "' not implemented.")
}
}
});

View File

@ -0,0 +1,83 @@
wdi.CursorProcess = $.spcExtend(wdi.EventObject.prototype, {
imageData: null,
process: function(spiceMessage) {
switch (spiceMessage.messageType) {
case wdi.SpiceVars.SPICE_MSG_CURSOR_INIT:
case wdi.SpiceVars.SPICE_MSG_CURSOR_SET:
var wdiCursor = this.extractCursor(spiceMessage);
if(wdiCursor) {
wdi.VirtualMouse.setHotspot(0, 0);
wdi.VirtualMouse.setMouse(wdiCursor.data, wdiCursor.header.hot_spot_x, wdiCursor.header.hot_spot_y);
}
break;
}
},
_toUrl:function(data) {
var imageData = $('<canvas/>').attr({
'width': data.width,
'height': data.height
})[0];
var ctx = imageData.getContext('2d');
ctx.putImageData(data, 0, 0);
return imageData.toDataURL("image/png");
},
extractCursor: function(spiceMessage) {
var flags = spiceMessage.args.cursor.flags;
var position = spiceMessage.args.position;
var visible = spiceMessage.args.visible;
//if there is no cursor, return null
if(flags & 1) {
return null;
}
var imageData = null;
//cursor from cache?
if(flags & wdi.SpiceCursorFlags.SPICE_CURSOR_FLAGS_FROM_CACHE) {
imageData = wdi.ImageCache.getCursorFrom(spiceMessage.args.cursor);
} else {
//cursor from packet
//any case should return url
switch (spiceMessage.args.cursor.header.type) {
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_ALPHA:
imageData = this._toUrl(wdi.graphics.argbToImageData(spiceMessage.args.cursor.data, spiceMessage.args.cursor.header.width, spiceMessage.args.cursor.header.height));
break;
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_MONO:
imageData = this._toUrl(wdi.graphics.monoToImageData(spiceMessage.args.cursor.data, spiceMessage.args.cursor.header.width, spiceMessage.args.cursor.header.height));
break;
case 8:
imageData = wdi.SpiceObject.bytesToString(spiceMessage.args.cursor.data);
break;
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR4:
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR8:
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR16:
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR24:
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR32:
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_ENUM_END:
break;
}
}
//got no cursor? error!
if(!imageData) {
return null;
}
//we have cursor, cache it?
if(flags & wdi.SpiceCursorFlags.SPICE_CURSOR_FLAGS_CACHE_ME) {
wdi.ImageCache.addCursor(spiceMessage.args.cursor, imageData);
}
return {
data: imageData,
position: position,
visible: visible,
header: spiceMessage.args.cursor.header
};
}
});

View File

@ -0,0 +1,119 @@
wdi.DisplayPreProcess = $.spcExtend(wdi.EventObject.prototype, {
displayProcess: null,
queued: [],
inProcess: [],
idleConsumers : [],
consumers: [],
init: function(c) {
this.superInit();
this.displayProcess = c.displayProcess || new wdi.DisplayProcess({
clientGui: c.clientGui
});
this.clientGui = c.clientGui;
/**
Since javascript do not provide an API to check
the number of cpu cores available, the best case for average computers
and devices is 4.
If the computer doesn't have 4 or more available cores, there is only a little
memory waste creating the threads and a bit of cpu overheat doing context
switching.
There is an ongoing draft in w3c to standarize a way to detect this:
http://www.w3.org/2012/sysapps/device-capabilities/#cpu
**/
if(c.numConsumers == null || c.numConsumers == undefined) c.numConsumers = 4;
var numConsumers = c.numConsumers;
for(var i = 0;i<numConsumers; i++) {
var consumer = new wdi.AsyncConsumer();
this.consumers.push(consumer);
this.idleConsumers.push(consumer);
consumer.addListener('done', this.onConsumerDone, this);
}
},
onConsumerDone: function(e) {
//we don't care about who has finished, only about the
//state of the last item in queue
var waitingTask = this.inProcess[0];
var task = null;
var i = 0;
while(waitingTask && waitingTask.state === 1) {
task = this.inProcess.shift();
try {
this.displayProcess.process(task.message);
} catch(e) {
wdi.Debug.error("DisplayPreProcess error: ", e);
}
waitingTask = this.inProcess[0];
i++;
}
//put the consumer as idle
this.idleConsumers.push(e);
//continue processing!
if(this.queued.length > 0) {
this.executeConsumer();
}
},
process: function(spiceMessage) {
this.addTask(spiceMessage); //first of all, queue it
//it is the only item in the list?
//we are the only message in the queue... process?
this.executeConsumer();
},
addTask: function(spiceMessage) {
this.queued.push({
message: spiceMessage,
clientGui: this.clientGui
});
},
getNextTask : function () {
var task = this.queued.shift();
while(typeof task == 'undefined' && this.queued.length != 0) {
task = this.queued.shift();
}
//we found a task?
if(typeof task == 'undefined') {
return false;
}
task.state = 0;
this.inProcess.push(task); //add the task to the inProcess list
return task;
},
executeConsumer: function() {
//check if there are idle consumers
if(this.idleConsumers.length > 0) {
wdi.Debug.log('DisplayPreProcess: available workers: '+this.idleConsumers.length);
wdi.Debug.log('DisplaypreProcess: pending tasks: '+this.queued.length);
//idle consumer found
var consumer = this.idleConsumers.shift();
//execute the next task in this consumer
var task = this.getNextTask();
if(task) {
consumer.consume(task);
}
}
},
dispose: function () {
this.consumers.forEach(function (consumer) {
consumer.dispose();
});
}
});

View File

@ -0,0 +1,174 @@
wdi.DisplayProcess = $.spcExtend(wdi.EventObject.prototype, {
runQ: null,
packetFilter: null,
init: function(c) {
this.runQ = c.runQ || wdi.ExecutionControl.runQ;
this.packetFilter = c.packetFilter || wdi.PacketFilter;
this.clientGui = c.clientGui;
this.displayRouter = c.displayRouter || new wdi.DisplayRouter({clientGui:this.clientGui});
this.started = false;
this.waitingMessages = [];
this.packetWorkerIdentifier = c.packetWorkerIdentifier || new wdi.PacketWorkerIdentifier();
},
process: function(spiceMessage) {
//this._process(spiceMessage);
//disable requestanimationframe equivalent for the moment
//the remove redundant draws implementation is buggy
//and there are considerations on how it is implemented
var self = this;
this.waitingMessages.push(spiceMessage);
if(!this.started) {
this.timer = setInterval(function() {
self.flush();
}, 50);
this.started = true;
}
},
flush: function() {
if(this.waitingMessages.length === 0) {
return;
}
var i = 0;
var spiceMessage;
//remove redundant draws
this.removeRedundantDraws();
var size = this.waitingMessages.length;
while(i < size) {
spiceMessage = this.waitingMessages[i];
this._process(spiceMessage);
i++;
}
this.waitingMessages = [];
},
removeRedundantDraws: function() {
if(this.waitingMessages.length < 2) {
return;
}
var size = this.waitingMessages.length;
var message, body, imageProperties, rop, base;
var collision_boxes = {};
var to_delete = [];
var deleted = false;
var surface_id;
var packetBox;
var box;
var i;
var x;
while(size--) {
message = this.waitingMessages[size];
//should remove any packet from the past overwritten by this one
body = message.args;
base = body.base;
rop = body.rop_descriptor;
deleted = false;
//TODO TODO TODO: there is need for a special case for draw_copy_bits?!
//we need base to have a box
if(base) {
surface_id = base.surface_id;
packetBox = base.box;
surface_id = base.surface_id;
//check if this packet is occluded by another packet
imageProperties = this.packetWorkerIdentifier.getImageProperties(message);
//if there is no image properties, or there is but cache flags are 0
if(!collision_boxes[surface_id]) {
collision_boxes[surface_id] = [];
}
if((!imageProperties || (imageProperties && !(imageProperties.descriptor.flags & wdi.SpiceImageFlags.SPICE_IMAGE_FLAGS_CACHE_ME))) && surface_id === 0) {
for(i=0; i<collision_boxes[surface_id].length; i++) {
//check if base.box is inside one of the rectangles in collision_boxes
box = collision_boxes[surface_id][i];
if(box.bottom >= packetBox.bottom && box.top <= packetBox.top && box.left <= packetBox.left
&& box.right >= packetBox.right ) {
deleted = true;
to_delete.push(size);
break;
}
}
}
//check if the message is still alive, and if it is, then put its box into collision_boxes if the message
//will overWrite its screen area when painted
//atm only drawcopy and drawfill have overwritescreenarea set
if(!deleted && message.messageType === wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS) {
break;
}
if(!deleted && body.getMessageProperty('overWriteScreenArea', false) && base.clip.type == 0 && rop == wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
collision_boxes[surface_id].push(base.box);
}
}
}
//itareate over messages marked for deletion and remove it from the array
for(x = 0;x < to_delete.length;x++) {
this.waitingMessages.splice(to_delete[x], 1);
}
},
_process: function(spiceMessage) {
if (wdi.logOperations) {
wdi.DataLogger.log(spiceMessage, 0, null, true, '', '_decode');
}
//append the message to the runqueue
//so the packet is not executed until the previous packets
//finished processing
this.runQ.add(function(proxy) {
//pass the message through the packet filter
//so the packet can be filtered, logged, etc
this.packetFilter.filter(spiceMessage, function(message) {
wdi.ExecutionControl.currentProxy = proxy;
//process the packet
this.displayRouter.processPacket(message);
//post process operations
this.postProcess();
}, this, this.clientGui);
//if the packet was synchronous, process next packet
if (wdi.ExecutionControl.sync) {
proxy.end();
}
//Now message could be asynchronous
}, this, function() {
//this is executed when the message has finished processing
//we use processEnd to notify packetFilter about the ending of processing
//the current message
this.processEnd(spiceMessage, this.clientGui);
});
//if this is the first message in the queue, execute it
//if not, this call will have no effect.
this.runQ.process();
},
processEnd: function(spiceMessage, clientGui) {
this.packetFilter.notifyEnd(spiceMessage, clientGui);
},
postProcess: function() {
//TEST METHOD DON'T DELETE
}
});

View File

@ -0,0 +1,86 @@
wdi.InputProcess = $.spcExtend(wdi.EventObject.prototype, {
clientGui: null,
spiceConnection: null,
init: function(c) {
this.superInit();
this.clientGui = c.clientGui;
this.spiceConnection = c.spiceConnection;
},
process: function(spiceMessage) {
switch (spiceMessage.messageType) {
case wdi.SpiceVars.SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
this.clientGui.motion_ack();
break;
}
},
send: function(data, type) {
var packet, scanCodes, i;
if(type == 'mousemove') {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_POSITION,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.RedcMousePosition({
x:data[1][0]+wdi.VirtualMouse.hotspot.x,
y:data[1][1]+wdi.VirtualMouse.hotspot.y,
buttons_state:data[1][2],
display_id:0
})
});
this.spiceConnection.send(packet);
} else if(type == 'mousedown') {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_PRESS,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.RedcMousePress({
button_id:data[1]+1,
buttons_state:1<<data[1]
})
});
this.spiceConnection.send(packet);
} else if(type == 'mouseup') {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_RELEASE,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.RedcMousePress({
button_id:data[1]+1,
buttons_state:0
})
});
this.spiceConnection.send(packet);
} else if (type == 'keydown' || type == 'keypress') {
scanCodes = wdi.Keymap.getScanCodes(data[1][0]);
for (i= 0; i<scanCodes.length;i++) {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_KEY_DOWN,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.SpiceScanCode(scanCodes[i])
});
this.spiceConnection.send(packet);
}
} else if (type == 'keyup') {
scanCodes = wdi.Keymap.getScanCodes(data[1][0]);
for (i= 0; i<scanCodes.length;i++) {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_KEY_UP,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.SpiceScanCode(scanCodes[i])
});
this.spiceConnection.send(packet);
}
} else if(type == 'joystick') {
packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_MOTION,
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
args: new wdi.RedcMouseMotion({
x:data[1][0],
y:data[1][1],
buttons_state:0
})
});
this.spiceConnection.send(packet);
}
}
});

View File

@ -0,0 +1,52 @@
wdi.MainProcess = $.spcExtend(wdi.EventObject.prototype, {
init: function(c) {
this.superInit();
this.app = c.app;
this.spiceConnection = c.app.spiceConnection;
this.agent = c.app.agent;
},
process: function(spiceMessage) {
var channel = this.spiceConnection.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN];
switch(spiceMessage.messageType) {
case wdi.SpiceVars.SPICE_MSG_MAIN_INIT:
channel.connectionId = spiceMessage.args.session_id;
channel.fire('connectionId', channel.connectionId);
if(spiceMessage.args.agent_connected == 1) {
channel.fire('initAgent', spiceMessage.args.agent_tokens);
}
if (spiceMessage.args.current_mouse_mode == 1) {
channel.fire('mouseMode', spiceMessage.args.current_mouse_mode);
}
// the mouse mode must be change both if we have agent or not
this.changeMouseMode();
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DATA:
var packet = spiceMessage.args;
this.agent.onAgentData(packet);
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_CONNECTED:
channel.fire('initAgent', spiceMessage.args.agent_tokens);
this.changeMouseMode();
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
this.app.multimediaTime = spiceMessage.args.multimedia_time;
break;
case wdi.SpiceVars.SPICE_MSG_MAIN_CHANNELS_LIST:
channel.fire('channelListAvailable', spiceMessage.args.channels);
break;
}
},
changeMouseMode: function() {
var packet = new wdi.SpiceMessage({
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST,
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
args: new wdi.SpiceMouseModeRequest({
request_mode: 2
})
});
this.spiceConnection.send(packet);
}
});

View File

@ -0,0 +1,201 @@
wdi.PlaybackProcess = $.spcExtend(wdi.EventObject.prototype, {
_lastApp: null,
started: false,
minBuffSize: 1024*32,
frequency: null,
channels: null,
audioContext: null,
startTime: null, // controls the playback time if no delay occurs
hasAudioSupport: true, //whether the browser supports HTML5 Web Audio API
typedBuffer: null,
position: null,
init: function(c) {
this.app = c.app;
this.audioContext = this.getAudioContext();
if (this.audioContext) {
this.hasAudioSupport = true;
} else {
this.hasAudioSupport = false;
wdi.Debug.warn('The client browser does not support Web Audio API');
}
this.startTime = 0;
this.typedBuffer = new ArrayBuffer(1024*32);
this.position = 0;
},
getAudioContext: function() {
//standard browser object
try {
return new AudioContext();
} catch(e) {
}
//chrome and safari
try {
return new webkitAudioContext();
} catch(e) {
}
return false;
},
process: function(spiceMessage) {
// if (this.hasAudioSupport && !Modernizr.touch) {
if (this.hasAudioSupport) {
switch (spiceMessage.messageType) {
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_MODE:
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_START:
var packet = spiceMessage.args;
this.channels = packet.channels;
this.frequency = packet.frequency;
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_STOP:
this.startTime = 0;
var packet = spiceMessage.args;
this.flush();
break;
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_DATA:
// While we receive data chunks, we store them in a buffer, so than when it is full we play the sound and empty it.
// With this we get a more fluid playback and better overall performance than if we just played the data the moment we got it
var packet = spiceMessage.args;
var dataTimestamp = spiceMessage.args.multimedia_time;
var tmpview = new Uint8Array(this.typedBuffer);
tmpview.set(packet.data, this.position);
this.position += packet.data.length;
this._lastApp = this.app;
if(this.position >= this.minBuffSize) {
// ok, the buffer is full. We send the data to be played and later we can empty it to make room for more audio
this.flush(dataTimestamp);
}
break;
}
} else {
//TODO:
// If the browser doesn't support Web Audio, we could still attach a wav header to the raw PCM we receive from spice and use the more widespread supported audio tag
// Meanwhile, we can skip all the audio packets and gain some performance at least
}
},
/**
* Plays all the audio buffer and empties it
*
* @param app
* @param dataTimestamp
*/
flush: function(dataTimestamp) {
if(this.position > 0) {
if (this.started) {
this.playSound(this.typedBuffer, dataTimestamp);
}
this.position = 0;
this.typedBuffer = new ArrayBuffer(1024*32);
}
},
/**
* Plays the raw pcm data passed as param using HTML5's Web Audio API
*
* @param buffer
*/
playSound: function(buffer, dataTimestamp) {
if(this.channels == 2) {
return this.playSoundStereo(buffer, dataTimestamp);
}
var audio = new Int16Array(buffer);
var channelData = new Array(this.channels);
for(var i = 0;i<this.channels;i++) {
channelData[i] = new Float32Array(audio.length / 2);
}
var channelCounter = 0;
for (var i = 0; i < audio.length; ) {
for(var c = 0; c < this.channels; c++) {
//because the audio data spice gives us is 16 bits signed int (32768) and we wont to get a float out of it (between -1.0' and 1.0)
channelData[c][channelCounter] = audio[i++] / 32768;
}
channelCounter++;
}
var source = this.audioContext['createBufferSource'](); // creates a sound source
var audioBuffer = this.audioContext['createBuffer'](this.channels, channelCounter, this.frequency);
for(var i=0;i < this.channels; i++) {
audioBuffer['getChannelData'](i)['set'](channelData[i]);
}
this._play(source, audioBuffer, dataTimestamp);
},
/**
* Plays the raw pcm STEREO data passed as param using HTML5's Web Audio API
*
* @param buffer
*/
playSoundStereo: function(buffer, dataTimestamp) {
// Each data packet is 16 bits, the first being left channel data and the second being right channel data (LR-LR-LR-LR...)
var audio = new Int16Array(buffer);
// We split the audio buffer in two channels. Float32Array is the type required by Web Audio API
var left = new Float32Array(audio.length / 2);
var right = new Float32Array(audio.length / 2);
var channelCounter = 0;
var audioContext = this.audioContext;
var len = audio.length;
for (var i = 0; i < len; ) {
//because the audio data spice gives us is 16 bits signed int (32768) and we wont to get a float out of it (between -1.0 and 1.0)
left[channelCounter] = audio[i++] / 32768;
right[channelCounter] = audio[i++] / 32768;
channelCounter++;
}
var source = audioContext['createBufferSource'](); // creates a sound source
var audioBuffer = audioContext['createBuffer'](2, channelCounter, this.frequency);
audioBuffer['getChannelData'](0)['set'](left);
audioBuffer['getChannelData'](1)['set'](right);
this._play(source, audioBuffer, dataTimestamp);
},
_play: function(source, audioBuffer, dataTimestamp) {
var wait = 0;
if (dataTimestamp) {
var elapsedTime = Date.now() - this.app.lastMultimediaTime; // time passed since we received the last multimedia time from main channel
var currentMultimediaTime = elapsedTime + this.app.multimediaTime; // total delay we have at the moment
wait = dataTimestamp - currentMultimediaTime;
if (wait < 0) {
wait = 0;
}
}
source['buffer'] = audioBuffer;
source['connect'](this.audioContext['destination']); // connect the source to the context's destination (the speakers)
//if (!Modernizr.touch) {
source['start'](this.startTime + wait); // play the source now
//} else {
// source.noteOn(0);
//}
this.startTime += audioBuffer.duration;
},
startAudio: function () {
this.started = true;
this.flush();
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

177
js/spice-web-client/run.js Normal file
View File

@ -0,0 +1,177 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
function getURLParameter (name) {
return decodeURIComponent(
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)')
.exec(location.search) || [, ""])[1]
.replace(/\+/g, '%20')
) || null;
}
wdi.Debug.debug = false; //enable logging to javascript console
wdi.exceptionHandling = false; //disable "global try catch" to improve debugging
//if enabled, console errors do not include line numbers
//wdi.SeamlessIntegration = false; //enable window integration. (if disabled, full desktop is received)
wdi.IntegrationBenchmarkEnabled = false;// MS Excel loading time benchmark
function start () {
app = new Application();
var f = function (action, params) {
if (action == 'windowClosed') {
$(params.canvas).remove();
$(params.eventLayer).remove();
} else if (action == 'windowMoved') {
$(params.canvas).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
$(params.eventLayer).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
} else if (action == 'init' || action == 'windowCreated') {
var item = null;
var canvas = null;
var eventlayer = null;
var body = $('body');
for (var i in params) {
item = params[i];
var position = item.position * 2;
canvas = $(item.canvas).css({
'zIndex': 10000 - position - 1,
'position': 'absolute',
'top': item.info.top + 'px',
'left': item.info.left + 'px'
});
eventlayer = $(item.eventLayer).css({
'top': item.info.top + 'px',
'left': item.info.left + 'px',
'zIndex': 10000 - position
})
body.append(canvas);
body.append(eventlayer);
}
} else if (action == 'ready') {
var width = $(window).width();
var height = $(window).height();
app.sendCommand('setResolution', {
'width': width,
'height': height
});
} else if (action == 'resolution') {
} else if (action == 'windowMinimized') {
//in eyeos, this should minimize the window, not close it
$(params.canvas).css({'display': 'none'});
$(params.eventLayer).css({'display': 'none'});
} else if (action == 'windowMaximized') {
$(params.canvas).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
$(params.eventLayer).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
} else if (action == 'windowRestored') {
//in eyeos, this should restore the window
$(params.canvas).css({'display': 'block'});
$(params.eventLayer).css({'display': 'block'});
$(params.canvas).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
$(params.eventLayer).css({
'top': params.info.top + 'px',
'left': params.info.left + 'px'
});
} else if (action == 'windowFocused') {
//debugger; //eyeos should move the window to front!
} else if (action == 'timeLapseDetected') {
wdi.Debug.log('Detected time lapse of ', params, 'seconds');
} else if (action == 'error') {
// alert('error');
} else if ("checkResults") {
//
}
};
$(window)['resize'](function () {
app.sendCommand('setResolution', {
'width': $(window).width(),
'height': $(window).height()
});
});
var useWorkers = true;
app.run({
'callback': f,
'context': this,
'host': getURLParameter('host') || '127.0.0.1',
'port': getURLParameter('port') || 8080,
'protocol': getURLParameter('protocol') || 'ws',
'token': '1q2w3e4r',
'vmHost': getURLParameter('vmhost') || false,
'vmPort': getURLParameter('vmport') || false,
'useBus': false,
'busHost': '10.11.12.200',
'busPort': 61613,
'busSubscriptions': ['/topic/00000000-0000-0000-0000-000000000000'],
'busUser': '00000000-0000-0000-0000-000000000000',
'busPass': 'potato',
// Connection Control
'connectionControl': false,
'heartbeatToken': 'heartbeat',
'heartbeatTimeout': 4000,//miliseconds
'busFileServerBaseUrl': 'https://10.11.12.200/fileserver/',
'layout': 'es',
'clientOffset': {
'x': 0,
'y': 0
},
'useWorkers': useWorkers,
'seamlessDesktopIntegration': false,
'externalClipboardHandling': false,
'disableClipboard': true,
'layer': document.getElementById('testVdi'),
'vmInfoToken': getURLParameter('vmInfoToken')
//'language': navigator.language
});
}
$(document).ready(start);

View File

@ -0,0 +1,13 @@
sonar.projectKey=spiceproxy
sonar.projectName=spiceproxy
sonar.projectVersion=1.0.0
sonar.sources=src
sonar.language=js
sonar.dynamicAnalysis=reuseReports
sonar.javascript.jstest.reportsPath=build/reports
sonar.javascript.lcov.reportPath=build/reports/lcov.info
sonar.exclusions=**/node_modules/**/*, \
**/test/**/*, \
**/component-test/**/*, \
**/build/**/*, \
**/doc/**/*

View File

@ -0,0 +1,517 @@
/*
eyeOS Spice Web Client
Copyright (c) 2015 eyeOS S.L.
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3 as published by the
Free Software Foundation.
This program 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 Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program in the file "LICENSE". If not, see
<http://www.gnu.org/licenses/agpl-3.0.txt>.
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License version 3.
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
these Appropriate Legal Notices must retain the display of the "Powered by
eyeos" logo and retain the original copyright notice. If the display of the
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
must display the words "Powered by eyeos" and retain the original copyright notice.
*/
wdi.SpicePubkeyType = {
SPICE_PUBKEY_TYPE_INVALID:0,
SPICE_PUBKEY_TYPE_RSA:1,
SPICE_PUBKEY_TYPE_RSA2:2,
SPICE_PUBKEY_TYPE_DSA:3,
SPICE_PUBKEY_TYPE_DSA1:4,
SPICE_PUBKEY_TYPE_DSA2:5,
SPICE_PUBKEY_TYPE_DSA3:6,
SPICE_PUBKEY_TYPE_DSA4:7,
SPICE_PUBKEY_TYPE_DH:8,
SPICE_PUBKEY_TYPE_EC:9,
SPICE_PUBKEY_TYPE_ENUM_END:10
}
wdi.SpiceWarnCode = {
SPICE_WARN_GENERAL:0,
SPICE_WARN_CODE_ENUM_END:1
}
wdi.SpiceLineFlags = {
SPICE_LINE_FLAGS_START_WITH_GAP:4,
SPICE_LINE_FLAGS_STYLED:8,
SPICE_LINE_FLAGS_MASK:12
}
wdi.SpiceNotifyVisibility = {
SPICE_NOTIFY_VISIBILITY_LOW:0,
SPICE_NOTIFY_VISIBILITY_MEDIUM:1,
SPICE_NOTIFY_VISIBILITY_HIGH:2,
SPICE_NOTIFY_VISIBILITY_ENUM_END:3
}
wdi.SpiceVars = {
SPICE_MSGC_ACK_SYNC:1,
SPICE_MSGC_ACK:2,
SPICE_MSGC_PONG:3,
SPICE_MSGC_MIGRATE_FLUSH_MARK:4,
SPICE_MSGC_MIGRATE_DATA:5,
SPICE_MSGC_DISCONNECTING:6,
SPICE_MSGC_DISPLAY_INIT:101,
SPICE_MSGC_END_DISPLAY:102,
SPICE_CHANNEL_MAIN:1,
SPICE_CHANNEL_DISPLAY:2,
SPICE_CHANNEL_INPUTS:3,
SPICE_CHANNEL_CURSOR:4,
SPICE_CHANNEL_PLAYBACK:5,
SPICE_CHANNEL_RECORD:6,
SPICE_CHANNEL_TUNNEL:7,
SPICE_CHANNEL_SMARTCARD:8,
SPICE_CHANNEL_USBREDIR:9,
SPICE_END_CHANNEL:10,
SPICE_MSG_MIGRATE:1,
SPICE_MSG_MIGRATE_DATA:2,
SPICE_MSG_SET_ACK:3,
SPICE_MSG_PING:4,
SPICE_MSG_WAIT_FOR_CHANNELS:5,
SPICE_MSG_DISCONNECTING:6,
SPICE_MSG_NOTIFY:7,
SPICE_MSG_LIST:8,
SPICE_MSGC_MAIN_CLIENT_INFO:101,
SPICE_MSGC_MAIN_MIGRATE_CONNECTED:102,
SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:103,
SPICE_MSGC_MAIN_ATTACH_CHANNELS:104,
SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:105,
SPICE_MSGC_MAIN_AGENT_START:106,
SPICE_MSGC_MAIN_AGENT_DATA:107,
SPICE_MSGC_MAIN_AGENT_TOKEN:108,
SPICE_MSGC_MAIN_MIGRATE_END:109,
SPICE_MSGC_END_MAIN:110,
SPICE_MSG_DISPLAY_MODE:101,
SPICE_MSG_DISPLAY_MARK:102,
SPICE_MSG_DISPLAY_RESET:103,
SPICE_MSG_DISPLAY_COPY_BITS:104,
SPICE_MSG_DISPLAY_INVAL_LIST:105,
SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS:106,
SPICE_MSG_DISPLAY_INVAL_PALETTE:107,
SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES:108,
SPICE_MSG_DISPLAY_STREAM_CREATE:122,
SPICE_MSG_DISPLAY_STREAM_DATA:123,
SPICE_MSG_DISPLAY_STREAM_CLIP:124,
SPICE_MSG_DISPLAY_STREAM_DESTROY:125,
SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL:126,
SPICE_MSG_DISPLAY_DRAW_FILL:302,
SPICE_MSG_DISPLAY_DRAW_OPAQUE:303,
SPICE_MSG_DISPLAY_DRAW_COPY:304,
SPICE_MSG_DISPLAY_DRAW_BLEND:305,
SPICE_MSG_DISPLAY_DRAW_BLACKNESS:306,
SPICE_MSG_DISPLAY_DRAW_WHITENESS:307,
SPICE_MSG_DISPLAY_DRAW_INVERS:308,
SPICE_MSG_DISPLAY_DRAW_ROP3:309,
SPICE_MSG_DISPLAY_DRAW_STROKE:310,
SPICE_MSG_DISPLAY_DRAW_TEXT:311,
SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:312,
SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:313,
SPICE_MSG_DISPLAY_SURFACE_CREATE:314,
SPICE_MSG_DISPLAY_SURFACE_DESTROY:315,
SPICE_MSG_END_DISPLAY:316,
SPICE_MSG_INPUTS_INIT:101,
SPICE_MSG_INPUTS_KEY_MODIFIERS:102,
SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:111,
SPICE_MSG_END_INPUTS:112,
SPICE_MSGC_INPUTS_KEY_DOWN:101,
SPICE_MSGC_INPUTS_KEY_UP:102,
SPICE_MSGC_INPUTS_KEY_MODIFIERS:103,
SPICE_MSGC_INPUTS_MOUSE_MOTION:111,
SPICE_MSGC_INPUTS_MOUSE_POSITION:112,
SPICE_MSGC_INPUTS_MOUSE_PRESS:113,
SPICE_MSGC_INPUTS_MOUSE_RELEASE:114,
SPICE_MSGC_END_INPUTS:115,
SPICE_MSG_CURSOR_INIT:101,
SPICE_MSG_CURSOR_RESET:102,
SPICE_MSG_CURSOR_SET:103,
SPICE_MSG_CURSOR_MOVE:104,
SPICE_MSG_CURSOR_HIDE:105,
SPICE_MSG_CURSOR_TRAIL:106,
SPICE_MSG_CURSOR_INVAL_ONE:107,
SPICE_MSG_CURSOR_INVAL_ALL:108,
SPICE_MSG_END_CURSOR:109,
SPICE_MSG_RECORD_START:101,
SPICE_MSG_RECORD_STOP:102,
SPICE_MSG_RECORD_VOLUME:103,
SPICE_MSG_RECORD_MUTE:104,
SPICE_MSG_END_RECORD:105,
SPICE_MSGC_SMARTCARD_DATA:101,
SPICE_MSGC_END_SMARTCARD:102,
SPICE_MSGC_SPICEVMC_DATA:101,
SPICE_MSGC_END_SPICEVMC:102,
SPICE_MSG_MAIN_MIGRATE_BEGIN:101,
SPICE_MSG_MAIN_MIGRATE_CANCEL:102,
SPICE_MSG_MAIN_INIT:103,
SPICE_MSG_MAIN_CHANNELS_LIST:104,
SPICE_MSG_MAIN_MOUSE_MODE:105,
SPICE_MSG_MAIN_MULTI_MEDIA_TIME:106,
SPICE_MSG_MAIN_AGENT_CONNECTED:107,
SPICE_MSG_MAIN_AGENT_DISCONNECTED:108,
SPICE_MSG_MAIN_AGENT_DATA:109,
SPICE_MSG_MAIN_AGENT_TOKEN:110,
SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST:111,
SPICE_MSG_MAIN_MIGRATE_END:112,
SPICE_MSG_END_MAIN:113,
SPICE_MSG_PLAYBACK_DATA:101,
SPICE_MSG_PLAYBACK_MODE:102,
SPICE_MSG_PLAYBACK_START:103,
SPICE_MSG_PLAYBACK_STOP:104,
SPICE_MSG_PLAYBACK_VOLUME:105,
SPICE_MSG_PLAYBACK_MUTE:106,
SPICE_MSG_END_PLAYBACK:107,
SPICE_MSGC_RECORD_DATA:101,
SPICE_MSGC_RECORD_MODE:102,
SPICE_MSGC_RECORD_START_MARK:103,
SPICE_MSGC_END_RECORD:104,
SPICE_MSG_TUNNEL_INIT:101,
SPICE_MSG_TUNNEL_SERVICE_IP_MAP:102,
SPICE_MSG_TUNNEL_SOCKET_OPEN:103,
SPICE_MSG_TUNNEL_SOCKET_FIN:104,
SPICE_MSG_TUNNEL_SOCKET_CLOSE:105,
SPICE_MSG_TUNNEL_SOCKET_DATA:106,
SPICE_MSG_TUNNEL_SOCKET_CLOSED_ACK:107,
SPICE_MSG_TUNNEL_SOCKET_TOKEN:108,
SPICE_MSG_END_TUNNEL:109,
SPICE_MSGC_TUNNEL_SERVICE_ADD:101,
SPICE_MSGC_TUNNEL_SERVICE_REMOVE:102,
SPICE_MSGC_TUNNEL_SOCKET_OPEN_ACK:103,
SPICE_MSGC_TUNNEL_SOCKET_OPEN_NACK:104,
SPICE_MSGC_TUNNEL_SOCKET_FIN:105,
SPICE_MSGC_TUNNEL_SOCKET_CLOSED:106,
SPICE_MSGC_TUNNEL_SOCKET_CLOSED_ACK:107,
SPICE_MSGC_TUNNEL_SOCKET_DATA:108,
SPICE_MSGC_TUNNEL_SOCKET_TOKEN:109,
SPICE_MSGC_END_TUNNEL:110,
SPICE_MSG_SMARTCARD_DATA:101,
SPICE_MSG_END_SMARTCARD:102,
SPICE_MSG_SPICEVMC_DATA:101,
SPICE_MSG_END_SPICEVMC:102,
SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION:0,
SPICE_COMMON_CAP_AUTH_SPICE:1,
SPICE_COMMON_CAP_AUTH_SASL:2,
SPICE_COMMON_CAP_MINI_HEADER:3,
SPICE_PLAYBACK_CAP_CELT_0_5_1:0,
SPICE_PLAYBACK_CAP_VOLUME:1,
SPICE_RECORD_CAP_CELT_0_5_1:0,
SPICE_RECORD_CAP_VOLUME:1,
SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE:0
}
wdi.SpiceTunnelServiceType = {
SPICE_TUNNEL_SERVICE_TYPE_INVALID:0,
SPICE_TUNNEL_SERVICE_TYPE_GENERIC:1,
SPICE_TUNNEL_SERVICE_TYPE_IPP:2,
SPICE_TUNNEL_SERVICE_TYPE_ENUM_END:3
}
wdi.SpiceJpegAlphaFlags = {
SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN:1,
SPICE_JPEG_ALPHA_FLAGS_MASK:1
}
wdi.SpiceMaskFlags = {
SPICE_MASK_FLAGS_INVERS:1,
SPICE_MASK_FLAGS_MASK:1
}
wdi.SpiceCursorType = {
SPICE_CURSOR_TYPE_ALPHA:0,
SPICE_CURSOR_TYPE_MONO:1,
SPICE_CURSOR_TYPE_COLOR4:2,
SPICE_CURSOR_TYPE_COLOR8:3,
SPICE_CURSOR_TYPE_COLOR16:4,
SPICE_CURSOR_TYPE_COLOR24:5,
SPICE_CURSOR_TYPE_COLOR32:6,
SPICE_CURSOR_TYPE_ENUM_END:7,
SPICE_CURSOR_TYPE_URL:8
}
wdi.SpiceImageFlags = {
SPICE_IMAGE_FLAGS_CACHE_ME:1,
SPICE_IMAGE_FLAGS_HIGH_BITS_SET:2,
SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME:4,
SPICE_IMAGE_FLAGS_MASK:7
}
wdi.SpiceAudioDataMode = {
SPICE_AUDIO_DATA_MODE_INVALID:0,
SPICE_AUDIO_DATA_MODE_RAW:1,
SPICE_AUDIO_DATA_MODE_CELT_0_5_1:2,
SPICE_AUDIO_DATA_MODE_ENUM_END:3
}
wdi.SpiceAudioFmt = {
SPICE_AUDIO_FMT_INVALID:0,
SPICE_AUDIO_FMT_S16:1,
SPICE_AUDIO_FMT_ENUM_END:2
}
wdi.SpiceBitmapFmt = {
SPICE_BITMAP_FMT_INVALID:0,
SPICE_BITMAP_FMT_1BIT_LE:1,
SPICE_BITMAP_FMT_1BIT_BE:2,
SPICE_BITMAP_FMT_4BIT_LE:3,
SPICE_BITMAP_FMT_4BIT_BE:4,
SPICE_BITMAP_FMT_8BIT:5,
SPICE_BITMAP_FMT_16BIT:6,
SPICE_BITMAP_FMT_24BIT:7,
SPICE_BITMAP_FMT_32BIT:8,
SPICE_BITMAP_FMT_RGBA:9,
SPICE_BITMAP_FMT_ENUM_END:10
}
wdi.SpiceStreamFlags = {
SPICE_STREAM_FLAGS_TOP_DOWN:1,
SPICE_STREAM_FLAGS_MASK:1
}
wdi.SpiceTunnelIpType = {
SPICE_TUNNEL_IP_TYPE_INVALID:0,
SPICE_TUNNEL_IP_TYPE_IPv4:1,
SPICE_TUNNEL_IP_TYPE_ENUM_END:2
}
wdi.SpiceBitmapFlags = {
SPICE_BITMAP_FLAGS_PAL_CACHE_ME:1,
SPICE_BITMAP_FLAGS_PAL_FROM_CACHE:2,
SPICE_BITMAP_FLAGS_TOP_DOWN:4,
SPICE_BITMAP_FLAGS_MASK:7
}
wdi.SpiceStringFlags = {
SPICE_STRING_FLAGS_RASTER_A1:1,
SPICE_STRING_FLAGS_RASTER_A4:2,
SPICE_STRING_FLAGS_RASTER_A8:4,
SPICE_STRING_FLAGS_RASTER_TOP_DOWN:8,
SPICE_STRING_FLAGS_MASK:15
}
wdi.SpiceSurfaceFmt = {
SPICE_SURFACE_FMT_INVALID:0,
SPICE_SURFACE_FMT_1_A:1,
SPICE_SURFACE_FMT_8_A:8,
SPICE_SURFACE_FMT_16_555:16,
SPICE_SURFACE_FMT_32_xRGB:32,
SPICE_SURFACE_FMT_16_565:80,
SPICE_SURFACE_FMT_32_ARGB:96,
SPICE_SURFACE_FMT_ENUM_END:97
}
wdi.SpiceCursorFlags = {
SPICE_CURSOR_FLAGS_NONE:1,
SPICE_CURSOR_FLAGS_CACHE_ME:2,
SPICE_CURSOR_FLAGS_FROM_CACHE:4,
SPICE_CURSOR_FLAGS_MASK:7
}
wdi.SpiceLinkErr = {
SPICE_LINK_ERR_OK:0,
SPICE_LINK_ERR_ERROR:1,
SPICE_LINK_ERR_INVALID_MAGIC:2,
SPICE_LINK_ERR_INVALID_DATA:3,
SPICE_LINK_ERR_VERSION_MISMATCH:4,
SPICE_LINK_ERR_NEED_SECURED:5,
SPICE_LINK_ERR_NEED_UNSECURED:6,
SPICE_LINK_ERR_PERMISSION_DENIED:7,
SPICE_LINK_ERR_BAD_CONNECTION_ID:8,
SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE:9,
SPICE_LINK_ERR_ENUM_END:10
}
wdi.SpiceNotifySeverity = {
SPICE_NOTIFY_SEVERITY_INFO:0,
SPICE_NOTIFY_SEVERITY_WARN:1,
SPICE_NOTIFY_SEVERITY_ERROR:2,
SPICE_NOTIFY_SEVERITY_ENUM_END:3
}
wdi.SpiceBrushType = {
SPICE_BRUSH_TYPE_NONE:0,
SPICE_BRUSH_TYPE_SOLID:1,
SPICE_BRUSH_TYPE_PATTERN:2,
SPICE_BRUSH_TYPE_ENUM_END:3
}
wdi.SpiceAlphaFlags = {
SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA:1,
SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA:2,
SPICE_ALPHA_FLAGS_MASK:3
}
wdi.SpiceSurfaceFlags = {
SPICE_SURFACE_FLAGS_PRIMARY:1,
SPICE_SURFACE_FLAGS_MASK:1
}
wdi.QuicImageType = {
QUIC_IMAGE_TYPE_INVALID: 0,
QUIC_IMAGE_TYPE_GRAY: 1,
QUIC_IMAGE_TYPE_RGB16: 2,
QUIC_IMAGE_TYPE_RGB24: 3,
QUIC_IMAGE_TYPE_RGB32: 4,
QUIC_IMAGE_TYPE_RGBA: 5
}
wdi.SpiceImageType = {
SPICE_IMAGE_TYPE_BITMAP:0,
SPICE_IMAGE_TYPE_QUIC:1,
SPICE_IMAGE_TYPE_RESERVED:2,
SPICE_IMAGE_TYPE_LZ_PLT:100,
SPICE_IMAGE_TYPE_LZ_RGB:101,
SPICE_IMAGE_TYPE_GLZ_RGB:102,
SPICE_IMAGE_TYPE_FROM_CACHE:103,
SPICE_IMAGE_TYPE_SURFACE:104,
SPICE_IMAGE_TYPE_JPEG:105,
SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS:106,
SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB:107,
SPICE_IMAGE_TYPE_JPEG_ALPHA:108,
SPICE_IMAGE_TYPE_CANVAS:109,
SPICE_IMAGE_TYPE_PNG:110,
SPICE_IMAGE_TYPE_ENUM_END:111
}
wdi.SpiceImageScaleMode = {
SPICE_IMAGE_SCALE_MODE_INTERPOLATE:0,
SPICE_IMAGE_SCALE_MODE_NEAREST:1,
SPICE_IMAGE_SCALE_MODE_ENUM_END:2
}
wdi.SpiceResourceType = {
SPICE_RES_TYPE_INVALID:0,
SPICE_RES_TYPE_PIXMAP:1,
SPICE_RESOURCE_TYPE_ENUM_END:2
}
wdi.SpicePathFlags = {
SPICE_PATH_BEGIN:1,
SPICE_PATH_END:2,
SPICE_PATH_CLOSE:8,
SPICE_PATH_BEZIER:16,
SPICE_PATH_FLAGS_MASK:27
}
wdi.SpiceVideoCodecType = {
SPICE_VIDEO_CODEC_TYPE_MJPEG:1,
SPICE_VIDEO_CODEC_TYPE_ENUM_END:2
}
wdi.SpiceRopd = {
SPICE_ROPD_INVERS_SRC:1,
SPICE_ROPD_INVERS_BRUSH:2,
SPICE_ROPD_INVERS_DEST:4,
SPICE_ROPD_OP_PUT:8,
SPICE_ROPD_OP_OR:16,
SPICE_ROPD_OP_AND:32,
SPICE_ROPD_OP_XOR:64,
SPICE_ROPD_OP_BLACKNESS:128,
SPICE_ROPD_OP_WHITENESS:256,
SPICE_ROPD_OP_INVERS:512,
SPICE_ROPD_INVERS_RES:1024,
SPICE_ROPD_MASK:2047
}
wdi.SpiceMigrateFlags = {
SPICE_MIGRATE_NEED_FLUSH:1,
SPICE_MIGRATE_NEED_DATA_TRANSFER:2,
SPICE_MIGRATE_FLAGS_MASK:3
}
wdi.SpiceKeyboardModifierFlags = {
SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK:1,
SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK:2,
SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK:4,
SPICE_KEYBOARD_MODIFIER_FLAGS_MASK:7
}
wdi.SpiceInfoCode = {
SPICE_INFO_GENERAL:0,
SPICE_INFO_CODE_ENUM_END:1
}
wdi.SpiceMouseButton = {
SPICE_MOUSE_BUTTON_INVALID:0,
SPICE_MOUSE_BUTTON_LEFT:1,
SPICE_MOUSE_BUTTON_MIDDLE:2,
SPICE_MOUSE_BUTTON_RIGHT:3,
SPICE_MOUSE_BUTTON_UP:4,
SPICE_MOUSE_BUTTON_DOWN:5,
SPICE_MOUSE_BUTTON_ENUM_END:6
}
wdi.SpiceClipType = {
SPICE_CLIP_TYPE_NONE:0,
SPICE_CLIP_TYPE_RECTS:1,
SPICE_CLIP_TYPE_ENUM_END:2
}
wdi.SpiceMouseButtonMask = {
SPICE_MOUSE_BUTTON_MASK_LEFT:1,
SPICE_MOUSE_BUTTON_MASK_MIDDLE:2,
SPICE_MOUSE_BUTTON_MASK_RIGHT:4,
SPICE_MOUSE_BUTTON_MASK_MASK:7
}
wdi.SpiceMouseModeTypes = {
SPICE_MOUSE_MODE_SERVER:1,
SPICE_MOUSE_MODE_CLIENT:2,
SPICE_MOUSE_MODE_MASK:3
}
wdi.AgentCaps = {
VD_AGENT_CAP_MOUSE_STATE: 0,
VD_AGENT_CAP_MONITORS_CONFIG: 1,
VD_AGENT_CAP_REPLY: 2,
VD_AGENT_CAP_CLIPBOARD: 3,
VD_AGENT_CAP_DISPLAY_CONFIG: 4,
VD_AGENT_CAP_CLIPBOARD_BY_DEMAND: 5,
VD_AGENT_CAP_CLIPBOARD_SELECTION : 6
};
wdi.AgentMessageTypes = {
VD_AGENT_MOUSE_STATE:1,
VD_AGENT_MONITORS_CONFIG: 2,
VD_AGENT_REPLY: 3,
VD_AGENT_CLIPBOARD: 4,
VD_AGENT_DISPLAY_CONFIG: 5,
VD_AGENT_ANNOUNCE_CAPABILITIES: 6,
VD_AGENT_CLIPBOARD_GRAB: 7,
VD_AGENT_CLIPBOARD_REQUEST: 8,
VD_AGENT_CLIPBOARD_RELEASE: 9,
VD_AGENT_GET_WINDOWS_LIST: 10,
VD_AGENT_CLOSE_WINDOW: 11,
VD_AGENT_MOVE_WINDOW: 12,
VD_AGENT_RESIZE_WINDOW: 13,
VD_AGENT_MINIMIZE_WINDOW: 14,
VD_AGENT_RESTORE_WINDOW: 15,
VD_AGENT_MAXIMIZE_WINDOW: 16,
VD_AGENT_FOCUS_WINDOW: 17,
VD_AGENT_EXECUTE_COMMAND: 18
};
wdi.ClipBoardTypes = {
VD_AGENT_CLIPBOARD_NONE: 0,
VD_AGENT_CLIPBOARD_UTF8_TEXT: 1,
VD_AGENT_CLIPBOARD_IMAGE_PNG: 2, /* All clients with image support should support this one */
VD_AGENT_CLIPBOARD_IMAGE_BMP: 3, /* optional */
VD_AGENT_CLIPBOARD_IMAGE_TIFF: 4, /* optional */
VD_AGENT_CLIPBOARD_IMAGE_JPG: 5 /* optional */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
/spiceproxy.js
/*.tgz

View File

@ -0,0 +1,46 @@
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
// change to parent directory so all paths are relative to the top dir
process.chdir(path.dirname(__dirname));
var targetFile = path.join(__dirname, 'spiceproxy.js');
var files = [
'lib/utils.js',
'spiceobjects/spiceobjects.js',
'spiceobjects/generated/protocol.js',
'lib/GlobalPool.js',
'lib/GenericObjectPool.js',
'spiceproxy/socket.js',
'spiceproxy/globalpool.js',
'lib/queue.js',
'network/socketqueue.js',
'network/packetextractor.js',
'network/packetcontroller.js',
'network/sizedefiner.js',
'network/packetreassembler.js',
'network/reassemblerfactory.js',
'lib/biginteger.js',
'spiceproxy/spicechannel.js'
];
var exportString = "\nmodule.exports = wdi; \n";
console.log("Will generate %s", targetFile);
if (fs.existsSync(targetFile)) {
fs.unlinkSync(targetFile);
}
files.forEach(function (file) {
var data = fs.readFileSync(file);
console.log('... appending %s', file);
fs.appendFileSync(targetFile, data);
});
console.log("Done! Appending module.exports line...");
fs.appendFileSync(targetFile, exportString);
console.log("Finish... Everything is stored in %s", targetFile);

Some files were not shown because too many files have changed in this diff Show More