fork noVNC, rename to websockify, cleanup.
Split of wsproxy from noVNC and rename it websockify.
This commit is contained in:
parent
dd59efb018
commit
0e6c2f5d95
|
@ -1,4 +1,3 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
*.o
|
*.o
|
||||||
wsproxy
|
*.so
|
||||||
tests/data_*.js
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
noVNC is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
||||||
docs/LICENSE.LGPL-3) with the following exceptions:
|
docs/LICENSE.LGPL-3) with the following exceptions:
|
||||||
|
|
||||||
include/base64.js : Dual GPL-2 or LGPL-2.1
|
include/base64.js : Dual GPL-2 or LGPL-2.1
|
||||||
|
|
||||||
incluee/des.js : Various BSD style licenses
|
|
||||||
|
|
||||||
include/web-socket-js/ : New BSD license. Source code at
|
include/web-socket-js/ : New BSD license. Source code at
|
||||||
http://github.com/gimite/web-socket-js
|
http://github.com/gimite/web-socket-js
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
TARGETS=rebind.so
|
||||||
|
CFLAGS += -fPIC
|
||||||
|
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
rebind.so: rebind.o
|
||||||
|
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f rebind.o rebind.so
|
||||||
|
|
318
README.md
318
README.md
|
@ -1,207 +1,165 @@
|
||||||
## noVNC: HTML5 VNC Client
|
## websockify: WebSockets support for any application/server
|
||||||
|
|
||||||
|
websockify was formerly named `wsproxy` and was part of the
|
||||||
|
[noVNC](https://github.com/kanaka/noVNC) project.
|
||||||
|
|
||||||
|
At the most basic level, websockify just translates WebSockets traffic
|
||||||
|
to normal socket traffic. websockify accepts the WebSockets handshake,
|
||||||
|
parses it, and then begins forwarding traffic between the client and
|
||||||
|
the target in both directions. WebSockets payload data is UTF-8
|
||||||
|
encoded so in order to transport binary data it must use an encoding
|
||||||
|
that can be encapsulated within UTF-8. websockify uses base64 to encode
|
||||||
|
all traffic to and from the client. Also, WebSockets traffic starts
|
||||||
|
with '\0' (0) and ends with '\xff' (255). Some buffering is done in
|
||||||
|
case the data from the client is not a full WebSockets frame (i.e.
|
||||||
|
does not end in 255).
|
||||||
|
|
||||||
|
|
||||||
|
### Additional websockify features
|
||||||
|
|
||||||
|
These are not necessary for the basic operation.
|
||||||
|
|
||||||
|
* Daemonizing: When the `-D` option is specified, websockify runs
|
||||||
|
in the background as a daemon process.
|
||||||
|
|
||||||
|
* SSL (the wss:// WebSockets URI): This is detected automatically by
|
||||||
|
websockify by sniffing the first byte sent from the client and then
|
||||||
|
wrapping the socket if the data starts with '\x16' or '\x80'
|
||||||
|
(indicating SSL).
|
||||||
|
|
||||||
|
* Flash security policy: websockify detects flash security policy
|
||||||
|
requests (again by sniffing the first packet) and answers with an
|
||||||
|
appropriate flash security policy response (and then closes the
|
||||||
|
port). This means no separate flash security policy server is needed
|
||||||
|
for supporting the flash WebSockets fallback emulator.
|
||||||
|
|
||||||
|
* Session recording: This feature that allows recording of the traffic
|
||||||
|
sent and received from the client to a file using the `--record`
|
||||||
|
option.
|
||||||
|
|
||||||
|
* Mini-webserver: websockify can detect and respond to normal web
|
||||||
|
requests on the same port as the WebSockets proxy and Flash security
|
||||||
|
policy. This functionality is activate with the `--web DIR` option
|
||||||
|
where DIR is the root of the web directory to serve.
|
||||||
|
|
||||||
|
* Wrap a program: see the "Wrap a Program" section below.
|
||||||
|
|
||||||
|
|
||||||
|
### Implementations of websockify
|
||||||
|
|
||||||
|
The primary implementation of websockify is in python. There are two
|
||||||
|
other implementations of websockify in C, and Node (node.js) in the
|
||||||
|
`other` directory.
|
||||||
|
|
||||||
|
Here is the feature support matrix for the the websockify
|
||||||
|
implementations:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Program</th>
|
||||||
|
<th>Language</th>
|
||||||
|
<th>Multiprocess</th>
|
||||||
|
<th>Daemonize</th>
|
||||||
|
<th>SSL/wss</th>
|
||||||
|
<th>Flash Policy Server</th>
|
||||||
|
<th>Session Record</th>
|
||||||
|
<th>Web Server</th>
|
||||||
|
<th>Program Wrap</th>
|
||||||
|
</tr> <tr>
|
||||||
|
<td>websockify</td>
|
||||||
|
<td>python</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes 1</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
</tr> <tr>
|
||||||
|
<td>other/websockify</td>
|
||||||
|
<td>C</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
</tr>
|
||||||
|
</tr> <tr>
|
||||||
|
<td>other/websockify.js</td>
|
||||||
|
<td>Node (node.js)</td>
|
||||||
|
<td>yes</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
<td>no</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
### Description
|
* Note 1: to use SSL/wss with python 2.5 or older, see the following
|
||||||
|
section on *Building the Python ssl module*.
|
||||||
|
|
||||||
noVNC is a VNC client implemented using HTML5 technologies,
|
|
||||||
specifically Canvas and WebSockets (supports 'wss://' encryption).
|
|
||||||
noVNC is licensed under the
|
|
||||||
[LGPLv3](http://www.gnu.org/licenses/lgpl.html).
|
|
||||||
|
|
||||||
Special thanks to [Sentry Data Systems](http://www.sentryds.com) for
|
### Wrap a Program
|
||||||
sponsoring ongoing development of this project (and for employing me).
|
|
||||||
|
|
||||||
Notable commits, announcements and news are posted to
|
In addition to proxying from a source address to a target address
|
||||||
@<a href="http://www.twitter.com/noVNC">noVNC</a>
|
(which may be on a different system), websockify has the ability to
|
||||||
|
launch a program on the local system and proxy WebSockets traffic to
|
||||||
|
a normal TCP port owned/bound by the program.
|
||||||
|
|
||||||
|
The is accomplished with a small LD_PRELOAD library (`rebind.so`)
|
||||||
|
which intercepts bind() system calls by the program. The specified
|
||||||
|
port is moved to a new localhost/loopback free high port. websockify
|
||||||
|
then proxies WebSockets traffic directed to the original port to the
|
||||||
|
new (moved) port of the program.
|
||||||
|
|
||||||
### Screenshots
|
The program wrap mode is invoked by replacing the target with `--`
|
||||||
|
followed by the program command line to wrap.
|
||||||
|
|
||||||
Running in Chrome before and after connecting:
|
`./websockify 2023 -- PROGRAM ARGS`
|
||||||
|
|
||||||
<img src="http://kanaka.github.com/noVNC/img/noVNC-1.jpg" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-2.jpg" width=400>
|
The `--wrap-mode` option can be used to indicate what action to take
|
||||||
|
when the wrapped program exits or daemonizes.
|
||||||
|
|
||||||
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
Here is an example of using websockify to wrap the vncserver command
|
||||||
|
(which backgrounds itself) for use with
|
||||||
|
[noVNC](https://github.com/kanaka/noVNC):
|
||||||
|
|
||||||
|
`./websockify 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1`
|
||||||
|
|
||||||
### Projects/Companies using noVNC
|
Here is an example of wrapping telnetd (from krb5-telnetd).telnetd
|
||||||
|
exits after the connection closes so the wrap mode is set to respawn
|
||||||
|
the command:
|
||||||
|
|
||||||
* [Sentry Data Systems](http://www.sentryds.com): uses noVNC in the
|
`sudo ./websockify 2023 --wrap-mode=respawn -- telnetd -debug 2023`
|
||||||
[Datanex Cloud Computing Platform](http://www.sentryds.com/products/datanex/).
|
|
||||||
|
|
||||||
* [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr):
|
The `wstelnet.html` page demonstrates a simple WebSockets based telnet
|
||||||
Feature [#1935](http://code.osuosl.org/issues/1935).
|
client.
|
||||||
|
|
||||||
* [Archipel](http://archipelproject.org):
|
|
||||||
[Video demo](http://antoinemercadal.fr/archipelblog/wp-content/themes/ArchipelWPTemplate/video_youtube.php?title=VNC%20Demonstration&id=te_bzW574Zo)
|
|
||||||
|
|
||||||
* [openQRM](http://www.openqrm.com/): VNC plugin available
|
### Building the Python ssl module (for python 2.5 and older)
|
||||||
by request. Probably included in [version
|
|
||||||
4.8](http://www.openqrm.com/?q=node/15). [Video
|
|
||||||
demo](http://www.openqrm-enterprise.com/news/details/article/remote-vm-console-plugin-available.html).
|
|
||||||
|
|
||||||
|
* Install the build dependencies. On Ubuntu use this command:
|
||||||
|
|
||||||
### Browser Requirements
|
`sudo aptitude install python-dev bluetooth-dev`
|
||||||
|
|
||||||
* HTML5 Canvas: Except for Internet Explorer, most
|
* Download, build the ssl module and symlink to it:
|
||||||
browsers have had Canvas support for quite some time. Internet
|
|
||||||
Explorer 9 will have Canvas support (finally).
|
|
||||||
|
|
||||||
* HTML5 WebSockets: For browsers that do not have builtin
|
`cd websockify/`
|
||||||
WebSockets support, the project includes
|
|
||||||
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
|
|
||||||
a WebSockets emulator using Adobe Flash.
|
|
||||||
|
|
||||||
* Fast Javascript Engine: noVNC avoids using new Javascript
|
`wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz`
|
||||||
functionality so it will run on older browsers, but decode and
|
|
||||||
rendering happen in Javascript, so a slow Javascript engine will
|
|
||||||
mean noVNC is painfully slow.
|
|
||||||
|
|
||||||
* I maintain a more detailed list of browser compatibility <a
|
`tar xvzf ssl-1.15.tar.gz`
|
||||||
href="http://github.com/kanaka/noVNC/blob/master/docs/browsers.md">here</a>.
|
|
||||||
|
|
||||||
|
`cd ssl-1.15`
|
||||||
|
|
||||||
### Server Requirements
|
`make`
|
||||||
|
|
||||||
Unless you are using a VNC server with support for WebSockets
|
`cd ../`
|
||||||
connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver)
|
|
||||||
currently), you need to use a WebSockets to TCP socket proxy. There is
|
|
||||||
a python proxy included ('wsproxy'). One advantage of using the proxy
|
|
||||||
is that it has builtin support for SSL/TLS encryption (i.e. "wss://").
|
|
||||||
|
|
||||||
There a few reasons why a proxy is required:
|
`ln -sf ssl-1.15/build/lib.linux-*/ssl ssl`
|
||||||
|
|
||||||
1. WebSockets is not a pure socket protocol. There is an initial HTTP
|
|
||||||
like handshake to allow easy hand-off by web servers and allow
|
|
||||||
some origin policy exchange. Also, each WebSockets frame begins
|
|
||||||
with 0 ('\x00') and ends with 255 ('\xff').
|
|
||||||
|
|
||||||
2. Javascript itself does not have the ability to handle pure byte
|
|
||||||
arrays. The python proxy encodes the data as base64 so that the
|
|
||||||
Javascript client can decode the data as an integer array.
|
|
||||||
|
|
||||||
|
|
||||||
### Quick Start
|
|
||||||
|
|
||||||
* Use the launch script to start a mini-webserver and the WebSockets
|
|
||||||
proxy. The `--vnc` option is used to specify the location of
|
|
||||||
a running VNC server:
|
|
||||||
|
|
||||||
`./utils/launch.sh --vnc localhost:5901`
|
|
||||||
|
|
||||||
* Point your browser to the cut-and-paste URL that is output by the
|
|
||||||
launch script. Enter a password if the VNC server has one
|
|
||||||
configured. Hit the Connect button and enjoy!
|
|
||||||
|
|
||||||
|
|
||||||
### Advanced usage
|
|
||||||
|
|
||||||
* To encrypt the traffic using the WebSocket 'wss://' URI scheme you
|
|
||||||
need to generate a certificate for the proxy to load. By default the
|
|
||||||
proxy loads a certificate file name `self.pem` but the `--cert=CERT`
|
|
||||||
option can override the file name. You can generate a self-signed
|
|
||||||
certificate using openssl. When asked for the common name, use the
|
|
||||||
hostname of the server where the proxy will be running:
|
|
||||||
|
|
||||||
`openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem`
|
|
||||||
|
|
||||||
* `tightvnc` provide a nice startup script that can be used to run
|
|
||||||
a separate X desktop that is served by VNC. To install and run the
|
|
||||||
server under Ubuntu you would do something like this:
|
|
||||||
|
|
||||||
`sudo apt-get install tightvncserver`
|
|
||||||
|
|
||||||
`vncserver :1`
|
|
||||||
|
|
||||||
The VNC server will run in the background. The port that it runs
|
|
||||||
on is the display number + 5900 (i.e. 5901 in the case above).
|
|
||||||
|
|
||||||
* `x11vnc` can be used to share your current X desktop. Note that if
|
|
||||||
you run noVNC on the X desktop you are connecting to via VNC you
|
|
||||||
will get a neat hall of mirrors effect, but the the client and
|
|
||||||
server will fight over the mouse.
|
|
||||||
|
|
||||||
`sudo apt-get install x11vnc`
|
|
||||||
|
|
||||||
`x11vnc -forever -display :0`
|
|
||||||
|
|
||||||
Without the `-forever` option, x11vnc will exit after the first
|
|
||||||
disconnect. The `-display` option indicates the exiting X display to
|
|
||||||
share. The port that it runs on is the display number + 5900 (i.e.
|
|
||||||
5900 in the case above).
|
|
||||||
|
|
||||||
* To run the python proxy directly without using launch script (to
|
|
||||||
pass additional options for example):
|
|
||||||
|
|
||||||
`./utils/wsproxy.py source_port target_addr:target_port`
|
|
||||||
|
|
||||||
`./utils/wsproxy.py 8787 localhost:5901`
|
|
||||||
|
|
||||||
* To activate the mini-webserver in wsproxy.py use the `--web DIR`
|
|
||||||
option:
|
|
||||||
|
|
||||||
`./utils/wsproxy.py --web ./ 8787 localhost:5901`
|
|
||||||
|
|
||||||
|
|
||||||
* Point your web browser at http://localhost:8787/vnc.html. On the
|
|
||||||
page enter the location where the proxy is running (localhost and
|
|
||||||
8787) and the password that the vnc server is using (if any). Hit
|
|
||||||
the Connect button.
|
|
||||||
|
|
||||||
* If you are using python 2.3 or 2.4 and you want wsproxy to support
|
|
||||||
'wss://' (TLS) then see the
|
|
||||||
[wsproxy README](http://github.com/kanaka/noVNC/blob/master/utils/README.md)
|
|
||||||
for instructions on building the ssl module.
|
|
||||||
|
|
||||||
|
|
||||||
### Integration
|
|
||||||
|
|
||||||
The client is designed to be easily integrated with existing web
|
|
||||||
structure and style.
|
|
||||||
|
|
||||||
At a minimum you must include the `vnc.js` and `ui.js` scripts and
|
|
||||||
call UI.load(). For example:
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<script src='include/vnc.js'></script>
|
|
||||||
<script src="include/ui.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='vnc'>Loading</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
UI.load('vnc');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
See `vnc.html` and `vnc_auto.html` for examples. The file
|
|
||||||
`include/plain.css` has a list of stylable elements.
|
|
||||||
|
|
||||||
The `vnc.js` also includes other scripts within the `include`
|
|
||||||
sub-directory. The `VNC_uri_prefix` variable can be use override the
|
|
||||||
URL path to the `include` sub-directory.
|
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
|
||||||
|
|
||||||
You will need console logging support in the browser. Recent Chrome
|
|
||||||
and Opera versions have built in support. Firefox has a nice extension
|
|
||||||
called "firebug" that gives console logging support.
|
|
||||||
|
|
||||||
First, load the noVNC page with `logging=debug` added to the query string.
|
|
||||||
For example `vnc.html?logging=debug`.
|
|
||||||
|
|
||||||
Then, activate the console logger in your browser. With Chrome it can
|
|
||||||
be activate using Ctrl+Shift+J and then switching to the "Console"
|
|
||||||
tab. With firefox+firebug, it can be activated using Ctrl+F12.
|
|
||||||
|
|
||||||
Now reproduce the problem. The console log output will give more
|
|
||||||
information about what is going wrong and where in the code the
|
|
||||||
problem is located.
|
|
||||||
|
|
||||||
If you file a issue/bug, it is very helpful for me to have the last
|
|
||||||
page of console output leading up the problem in the issue report.
|
|
||||||
Other helpful issue/bug information: browser version, OS version,
|
|
||||||
noVNC git version, and VNC server name/version.
|
|
||||||
|
|
34
docs/TODO
34
docs/TODO
|
@ -1,35 +1 @@
|
||||||
Short Term:
|
|
||||||
|
|
||||||
- Keyboard layout/internationalization support
|
|
||||||
- convert keyCode into proper charCode
|
|
||||||
|
|
||||||
- Test on IE 9 preview 3.
|
|
||||||
|
|
||||||
- Status bar menu/buttons:
|
|
||||||
- Explanatory hover text over buttons
|
|
||||||
|
|
||||||
- Configuration menu:
|
|
||||||
- Tunable: speed vs. bandwidth selection
|
|
||||||
- Tunable: CPU use versus latency.
|
|
||||||
- Scaling
|
|
||||||
|
|
||||||
- Keyboard menu:
|
|
||||||
- Ctrl Lock, Alt Lock, SysRq Lock
|
|
||||||
- Highlight menu icon when keys are locked
|
|
||||||
|
|
||||||
- Clipboard button -> popup:
|
|
||||||
- text, clear and send buttons
|
|
||||||
|
|
||||||
- wstelnet: support CSI L and CSI M
|
- wstelnet: support CSI L and CSI M
|
||||||
|
|
||||||
Medium Term:
|
|
||||||
|
|
||||||
- VNC performance and regression playback suite.
|
|
||||||
|
|
||||||
- Viewport support
|
|
||||||
|
|
||||||
- Touchscreen testing/support.
|
|
||||||
|
|
||||||
- wswrapper:
|
|
||||||
- epoll_* support
|
|
||||||
|
|
||||||
|
|
126
docs/browsers.md
126
docs/browsers.md
|
@ -1,126 +0,0 @@
|
||||||
## noVNC: Browser Support
|
|
||||||
|
|
||||||
### Ubuntu Karmic (9.10)
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Browser</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Performance/Notes</th>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Chrome 7.0.510.0</td>
|
|
||||||
<td><strong>Broken</strong></td>
|
|
||||||
<td>WebKit render bug (see note 3)</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Chrome 5.0.375.29</td>
|
|
||||||
<td>Excellent</td>
|
|
||||||
<td>Very fast. Native WebSockets.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Firefox 4.0 Beta 6</td>
|
|
||||||
<td>Excellent</td>
|
|
||||||
<td>Fast. Native WebSockets. SSL cert hassle (see note 2)</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Firefox 3.6.1</td>
|
|
||||||
<td>Good</td>
|
|
||||||
<td>Slowed by web-socket-js overhead. Local cursor causes segfault.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Arora 0.10.1</td>
|
|
||||||
<td>Fair</td>
|
|
||||||
<td>Slow due to broken putImageData and web-socket-js.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Opera 10.60</td>
|
|
||||||
<td>Poor</td>
|
|
||||||
<td>web-socket-js problems, mouse/keyboard issues (see note 1)</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Konqueror 4.3.2</td>
|
|
||||||
<td><strong>Broken</strong></td>
|
|
||||||
<td>web-socket-js never loads</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
### Ubuntu Jaunty (9.04)
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Browser</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Performance/Notes</th>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Chrome 5.0.375.29</td>
|
|
||||||
<td>Excellent</td>
|
|
||||||
<td>Very fast. Native WebSockets.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Firefox 3.5</td>
|
|
||||||
<td>Good</td>
|
|
||||||
<td>Slowed by web-socket-js overhead.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Firefox 3.0.17</td>
|
|
||||||
<td>Fair</td>
|
|
||||||
<td>Works fine but is slow.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Arora 0.5</td>
|
|
||||||
<td>Fair</td>
|
|
||||||
<td>Slow due to broken putImageData and web-socket-js.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Opera 10.60</td>
|
|
||||||
<td>Poor</td>
|
|
||||||
<td>web-socket-js problems, mouse/keyboard issues (see note 1)</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Konqueror 4.2.2</td>
|
|
||||||
<td><strong>Broken</strong></td>
|
|
||||||
<td>web-socket-js never loads</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
### Windows XP
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Browser</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Performance/Notes</th>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Chrome 5.0.375.99</td>
|
|
||||||
<td>Excellent</td>
|
|
||||||
<td>Very fast. Native WebSockets.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Safari 5.0</td>
|
|
||||||
<td>Excellent</td>
|
|
||||||
<td>Fast. Native WebSockets.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>Firefox 3.0.19</td>
|
|
||||||
<td>Good</td>
|
|
||||||
<td>Some overhead from web-socket-js.</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>IE 6, 7, 8</td>
|
|
||||||
<td><strong>Non-starter</strong></td>
|
|
||||||
<td>No basic Canvas support. Javascript painfully slow.</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
* Note 1: Opera interacts poorly with web-socket-js. After two
|
|
||||||
disconnects the browser tab or Flash often hang. Although Javascript
|
|
||||||
is faster than Firefox 3.5, the high variability of web-socket-js
|
|
||||||
performance results in overall performance being lower. Middle mouse
|
|
||||||
clicks and keyboard events need some work to work properly under
|
|
||||||
Opera. Also, Opera does not have support for setting the cursor
|
|
||||||
style url to a data URI scheme, so cursor pseudo-encoding is
|
|
||||||
disabled.
|
|
||||||
|
|
||||||
* Note 2: Firefox 4.0 Beta does not provide a direct way to accept
|
|
||||||
SSL certificates via WebSockets. You can work around this by
|
|
||||||
navigating directly to the WebSockets port using 'https://' and
|
|
||||||
accepting the certificate. Then return to noVNC and connect
|
|
||||||
normally.
|
|
||||||
|
|
||||||
* Note 3: Browsers using WebKit build 66396 through 68867
|
|
||||||
(Chrome/Chromium build 57968 through 61278) have a Canvas rendering
|
|
||||||
bug. The WebKit bug is <a
|
|
||||||
href="https://bugs.webkit.org/show_bug.cgi?id=46319">#46319</a>.
|
|
||||||
The noVNC bug is <a
|
|
||||||
href="http://github.com/kanaka/novnc/issues/#issue/28">#28</a>.
|
|
||||||
|
|
||||||
|
|
70
docs/links
70
docs/links
|
@ -1,70 +0,0 @@
|
||||||
New tight PNG protocol:
|
|
||||||
http://wiki.qemu.org/VNC_Tight_PNG
|
|
||||||
http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling
|
|
||||||
|
|
||||||
RFB protocol and extensions:
|
|
||||||
http://tigervnc.org/cgi-bin/rfbproto
|
|
||||||
|
|
||||||
Canvas Browser Compatibility:
|
|
||||||
http://philip.html5.org/tests/canvas/suite/tests/results.html
|
|
||||||
|
|
||||||
WebSockets API standard:
|
|
||||||
http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket
|
|
||||||
http://dev.w3.org/html5/websockets/
|
|
||||||
http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt
|
|
||||||
|
|
||||||
Browser Keyboard Events detailed:
|
|
||||||
http://unixpapa.com/js/key.html
|
|
||||||
|
|
||||||
ActionScript (Flash) WebSocket implementation:
|
|
||||||
http://github.com/gimite/web-socket-js
|
|
||||||
|
|
||||||
ActionScript (Flash) crypto/TLS library:
|
|
||||||
http://code.google.com/p/as3crypto
|
|
||||||
http://github.com/lyokato/as3crypto_patched
|
|
||||||
|
|
||||||
TLS Protocol:
|
|
||||||
http://en.wikipedia.org/wiki/Transport_Layer_Security
|
|
||||||
|
|
||||||
Generate self-signed certificate:
|
|
||||||
http://docs.python.org/dev/library/ssl.html#certificates
|
|
||||||
|
|
||||||
Cursor appearance/style (for Cursor pseudo-encoding):
|
|
||||||
http://en.wikipedia.org/wiki/ICO_(file_format)
|
|
||||||
http://www.daubnet.com/en/file-format-cur
|
|
||||||
https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
|
||||||
|
|
||||||
|
|
||||||
RDP Protocol specification:
|
|
||||||
http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx
|
|
||||||
|
|
||||||
|
|
||||||
Related projects:
|
|
||||||
|
|
||||||
guacamole: http://guacamole.sourceforge.net/
|
|
||||||
|
|
||||||
- Web client, but Java servlet does pre-processing
|
|
||||||
|
|
||||||
jsvnc: http://code.google.com/p/jsvnc/
|
|
||||||
|
|
||||||
- No releases
|
|
||||||
|
|
||||||
webvnc: http://code.google.com/p/webvnc/
|
|
||||||
|
|
||||||
- Jetty web server gateway, no updates since April 2008.
|
|
||||||
|
|
||||||
RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html
|
|
||||||
|
|
||||||
- Java applet
|
|
||||||
|
|
||||||
Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/
|
|
||||||
|
|
||||||
- Adobe Flash implementation
|
|
||||||
|
|
||||||
FVNC: http://osflash.org/fvnc
|
|
||||||
|
|
||||||
- Adbove Flash implementation
|
|
||||||
|
|
||||||
CanVNC: http://canvnc.sourceforge.net/
|
|
||||||
|
|
||||||
- HTML client with REST to VNC python proxy. Mostly vapor.
|
|
147
docs/rfb_notes
147
docs/rfb_notes
|
@ -1,147 +0,0 @@
|
||||||
5.1.1 ProtocolVersion: 12, 12 bytes
|
|
||||||
|
|
||||||
- Sent by server, max supported
|
|
||||||
12 ascii - "RFB 003.008\n"
|
|
||||||
- Response by client, version to use
|
|
||||||
12 ascii - "RFB 003.003\n"
|
|
||||||
|
|
||||||
5.1.2 Authentication: >=4, [16, 4] bytes
|
|
||||||
|
|
||||||
- Sent by server
|
|
||||||
CARD32 - authentication-scheme
|
|
||||||
0 - connection failed
|
|
||||||
CARD32 - length
|
|
||||||
length - reason
|
|
||||||
1 - no authentication
|
|
||||||
|
|
||||||
2 - VNC authentication
|
|
||||||
16 CARD8 - challenge (random bytes)
|
|
||||||
|
|
||||||
- Response by client (if VNC authentication)
|
|
||||||
16 CARD8 - client encrypts the challenge with DES, using user
|
|
||||||
password as key, sends resulting 16 byte response
|
|
||||||
|
|
||||||
- Response by server (if VNC authentication)
|
|
||||||
CARD32 - 0 - OK
|
|
||||||
1 - failed
|
|
||||||
2 - too-many
|
|
||||||
|
|
||||||
5.1.3 ClientInitialisation: 1 byte
|
|
||||||
- Sent by client
|
|
||||||
CARD8 - shared-flag, 0 exclusive, non-zero shared
|
|
||||||
|
|
||||||
5.1.4 ServerInitialisation: >=24 bytes
|
|
||||||
- Sent by server
|
|
||||||
CARD16 - framebuffer-width
|
|
||||||
CARD16 - framebuffer-height
|
|
||||||
16 byte PIXEL_FORMAT - server-pixel-format
|
|
||||||
CARD8 - bits-per-pixel
|
|
||||||
CARD8 - depth
|
|
||||||
CARD8 - big-endian-flag, non-zero is big endian
|
|
||||||
CARD8 - true-color-flag, non-zero then next 6 apply
|
|
||||||
CARD16 - red-max
|
|
||||||
CARD16 - green-max
|
|
||||||
CARD16 - blue-max
|
|
||||||
CARD8 - red-shift
|
|
||||||
CARD8 - green-shift
|
|
||||||
CARD8 - blue-shift
|
|
||||||
3 bytes - padding
|
|
||||||
CARD32 - name-length
|
|
||||||
|
|
||||||
CARD8[length] - name-string
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Client to Server Messages:
|
|
||||||
|
|
||||||
5.2.1 SetPixelFormat: 20 bytes
|
|
||||||
CARD8: 0 - message-type
|
|
||||||
...
|
|
||||||
|
|
||||||
5.2.2 FixColourMapEntries: >=6 bytes
|
|
||||||
CARD8: 1 - message-type
|
|
||||||
...
|
|
||||||
|
|
||||||
5.2.3 SetEncodings: >=8 bytes
|
|
||||||
CARD8: 2 - message-type
|
|
||||||
CARD8 - padding
|
|
||||||
CARD16 - numer-of-encodings
|
|
||||||
|
|
||||||
CARD32 - encoding-type in preference order
|
|
||||||
0 - raw
|
|
||||||
1 - copy-rectangle
|
|
||||||
2 - RRE
|
|
||||||
4 - CoRRE
|
|
||||||
5 - hextile
|
|
||||||
|
|
||||||
5.2.4 FramebufferUpdateRequest (10 bytes)
|
|
||||||
CARD8: 3 - message-type
|
|
||||||
CARD8 - incremental (0 for full-update, non-zero for incremental)
|
|
||||||
CARD16 - x-position
|
|
||||||
CARD16 - y-position
|
|
||||||
CARD16 - width
|
|
||||||
CARD16 - height
|
|
||||||
|
|
||||||
|
|
||||||
5.2.5 KeyEvent: 8 bytes
|
|
||||||
CARD8: 4 - message-type
|
|
||||||
CARD8 - down-flag
|
|
||||||
2 bytes - padding
|
|
||||||
CARD32 - key (X-Windows keysym values)
|
|
||||||
|
|
||||||
5.2.6 PointerEvent: 6 bytes
|
|
||||||
CARD8: 5 - message-type
|
|
||||||
CARD8 - button-mask
|
|
||||||
CARD16 - x-position
|
|
||||||
CARD16 - y-position
|
|
||||||
|
|
||||||
5.2.7 ClientCutText: >=9 bytes
|
|
||||||
CARD8: 6 - message-type
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
Server to Client Messages:
|
|
||||||
|
|
||||||
5.3.1 FramebufferUpdate
|
|
||||||
CARD8: 0 - message-type
|
|
||||||
1 byte - padding
|
|
||||||
CARD16 - number-of-rectangles
|
|
||||||
|
|
||||||
CARD16 - x-position
|
|
||||||
CARD16 - y-position
|
|
||||||
CARD16 - width
|
|
||||||
CARD16 - height
|
|
||||||
CARD16 - encoding-type:
|
|
||||||
0 - raw
|
|
||||||
1 - copy rectangle
|
|
||||||
2 - RRE
|
|
||||||
4 - CoRRE
|
|
||||||
5 - hextile
|
|
||||||
|
|
||||||
raw:
|
|
||||||
- width x height pixel values
|
|
||||||
|
|
||||||
copy rectangle:
|
|
||||||
CARD16 - src-x-position
|
|
||||||
CARD16 - src-y-position
|
|
||||||
|
|
||||||
RRE:
|
|
||||||
CARD32 - N number-of-subrectangles
|
|
||||||
Nxd bytes - background-pixel-value (d bits-per-pixel)
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
5.3.2 SetColourMapEntries (no support)
|
|
||||||
CARD8: 1 - message-type
|
|
||||||
...
|
|
||||||
|
|
||||||
5.3.3 Bell
|
|
||||||
CARD8: 2 - message-type
|
|
||||||
|
|
||||||
5.3.4 ServerCutText
|
|
||||||
CARD8: 3 - message-type
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,919 @@
|
||||||
|
// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like
|
||||||
|
// interface and a POSIX-like interface. (The POSIX-like calls are
|
||||||
|
// implemented on top of the ncurses-like calls, not the other way round.)
|
||||||
|
//
|
||||||
|
// Released under the GNU LGPL v2.1, by Frank Bi <bi@zompower.tk>
|
||||||
|
//
|
||||||
|
// 2007-08-12 - refresh():
|
||||||
|
// - factor out colour code to html_colours_()
|
||||||
|
// - fix handling of A_REVERSE | A_DIM
|
||||||
|
// - simplify initial <br /> output code
|
||||||
|
// - fix underlining colour
|
||||||
|
// - fix attron() not to turn off attributes
|
||||||
|
// - decouple A_STANDOUT and A_BOLD
|
||||||
|
// 2007-08-11 - getch() now calls refresh()
|
||||||
|
// 2007-08-06 - Safari compat fix -- turn '\r' into '\n' for onkeypress
|
||||||
|
// 2007-08-05 - Opera compat fixes for onkeypress
|
||||||
|
// 2007-07-30 - IE compat fixes:
|
||||||
|
// - change key handling code
|
||||||
|
// - add <br />...<br /> so that 1st and last lines align
|
||||||
|
// 2007-07-28 - change wrapping behaviour -- writing at the right edge no
|
||||||
|
// longer causes the cursor to immediately wrap around
|
||||||
|
// - add <b>...</b> to output to make A_STANDOUT stand out more
|
||||||
|
// - add handling of backspace, tab, return keys
|
||||||
|
// - fix doc. of VT100() constructor
|
||||||
|
// - change from GPL to LGPL
|
||||||
|
// 2007-07-09 - initial release
|
||||||
|
//
|
||||||
|
// class VT100
|
||||||
|
// A_NORMAL, A_UNDERLINE, A_REVERSE, A_BLINK, A_DIM, A_BOLD, A_STANDOUT
|
||||||
|
// =class constants=
|
||||||
|
// Attribute constants.
|
||||||
|
// VT100(wd, ht, scr_id) =constructor=
|
||||||
|
// Creates a virtual terminal with width `wd', and
|
||||||
|
// height `ht'. The terminal will be displayed between
|
||||||
|
// <pre>...</pre> tags which have element ID `scr_id'.
|
||||||
|
// addch(ch [, attr])
|
||||||
|
// Writes out the character `ch'. If `attr' is given,
|
||||||
|
// it specifies the attributes for the character,
|
||||||
|
// otherwise the current attributes are used.
|
||||||
|
// addstr(stuff) Writes out the string `stuff' using the current
|
||||||
|
// attributes.
|
||||||
|
// attroff(mode) Turns off any current options given in mode.
|
||||||
|
// attron(mode) Turns on any options given in mode.
|
||||||
|
// attrset(mode) Sets the current options to mode.
|
||||||
|
// bkgdset(attr) Sets the background attributes to attr.
|
||||||
|
// clear() Clears the terminal using the background attributes,
|
||||||
|
// and homes the cursor.
|
||||||
|
// clrtobol() Clears the portion of the terminal from the cursor
|
||||||
|
// to the bottom.
|
||||||
|
// clrtoeol() Clears the portion of the current line after the
|
||||||
|
// cursor.
|
||||||
|
// curs_set(vis [, grab])
|
||||||
|
// If `vis' is 0, makes the cursor invisible; otherwise
|
||||||
|
// make it visible. If `grab' is given and true, starts
|
||||||
|
// capturing keyboard events (for `getch()'); if given
|
||||||
|
// and false, stops capturing events.
|
||||||
|
// echo() Causes key strokes to be automatically echoed on the
|
||||||
|
// terminal.
|
||||||
|
// erase() Same as `clear()'.
|
||||||
|
// getch(isr) Arranges to call `isr' when a key stroke is
|
||||||
|
// received. The received character and the terminal
|
||||||
|
// object are passed as arguments to `isr'.
|
||||||
|
// getmaxyx() Returns an associative array with the maximum row
|
||||||
|
// (`y') and column (`x') numbers for the terminal.
|
||||||
|
// getyx() Returns an associative array with the current row
|
||||||
|
// (`y') and column (`x') of the cursor.
|
||||||
|
// move(r, c) Moves the cursor to row `r', column `c'.
|
||||||
|
// noecho() Stops automatically echoing key strokes.
|
||||||
|
// refresh() Updates the display.
|
||||||
|
// scroll() Scrolls the terminal up one line.
|
||||||
|
// standend() Same as `attrset(VT100.A_NORMAL)'.
|
||||||
|
// standout() Same as `attron(VT100.A_STANDOUT)'.
|
||||||
|
// write(stuff) Writes `stuff' to the terminal and immediately
|
||||||
|
// updates the display; (some) escape sequences are
|
||||||
|
// interpreted and acted on.
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
function VT100(wd, ht, scr_id)
|
||||||
|
{
|
||||||
|
var r;
|
||||||
|
var c;
|
||||||
|
var scr = document.getElementById(scr_id);
|
||||||
|
this.wd_ = wd;
|
||||||
|
this.ht_ = ht;
|
||||||
|
this.scrolled_ = 0;
|
||||||
|
this.bkgd_ = {
|
||||||
|
mode: VT100.A_NORMAL,
|
||||||
|
fg: VT100.COLOR_WHITE,
|
||||||
|
bg: VT100.COLOR_BLACK
|
||||||
|
};
|
||||||
|
this.c_attr_ = {
|
||||||
|
mode: VT100.A_NORMAL,
|
||||||
|
fg: VT100.COLOR_WHITE,
|
||||||
|
bg: VT100.COLOR_BLACK
|
||||||
|
};
|
||||||
|
this.text_ = new Array(ht);
|
||||||
|
this.attr_ = new Array(ht);
|
||||||
|
for (r = 0; r < ht; ++r) {
|
||||||
|
this.text_[r] = new Array(wd);
|
||||||
|
this.attr_[r] = new Array(wd);
|
||||||
|
}
|
||||||
|
this.scr_ = scr;
|
||||||
|
this.cursor_vis_ = true;
|
||||||
|
this.grab_events_ = false;
|
||||||
|
this.getch_isr_ = undefined;
|
||||||
|
this.key_buf_ = [];
|
||||||
|
this.echo_ = true;
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
// Internal debug setting.
|
||||||
|
this.debug_ = 0;
|
||||||
|
this.clear();
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// public constants -- colours and colour pairs
|
||||||
|
VT100.COLOR_BLACK = 0;
|
||||||
|
VT100.COLOR_BLUE = 1;
|
||||||
|
VT100.COLOR_GREEN = 2;
|
||||||
|
VT100.COLOR_CYAN = 3;
|
||||||
|
VT100.COLOR_RED = 4;
|
||||||
|
VT100.COLOR_MAGENTA = 5;
|
||||||
|
VT100.COLOR_YELLOW = 6;
|
||||||
|
VT100.COLOR_WHITE = 7;
|
||||||
|
VT100.COLOR_PAIRS = 256;
|
||||||
|
VT100.COLORS = 8;
|
||||||
|
// public constants -- attributes
|
||||||
|
VT100.A_NORMAL = 0;
|
||||||
|
VT100.A_UNDERLINE = 1;
|
||||||
|
VT100.A_REVERSE = 2;
|
||||||
|
VT100.A_BLINK = 4;
|
||||||
|
VT100.A_DIM = 8;
|
||||||
|
VT100.A_BOLD = 16;
|
||||||
|
VT100.A_STANDOUT = 32;
|
||||||
|
VT100.A_PROTECT = VT100.A_INVIS = 0; // ?
|
||||||
|
// other public constants
|
||||||
|
VT100.TABSIZE = 8;
|
||||||
|
// private constants
|
||||||
|
VT100.ATTR_FLAGS_ = VT100.A_UNDERLINE | VT100.A_REVERSE | VT100.A_BLINK |
|
||||||
|
VT100.A_DIM | VT100.A_BOLD | VT100.A_STANDOUT |
|
||||||
|
VT100.A_PROTECT | VT100.A_INVIS;
|
||||||
|
VT100.COLOR_SHIFT_ = 6;
|
||||||
|
VT100.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1);
|
||||||
|
VT100.browser_opera_ = (navigator.appName.indexOf("Opera") != -1);
|
||||||
|
// class variables
|
||||||
|
VT100.the_vt_ = undefined;
|
||||||
|
|
||||||
|
// class methods
|
||||||
|
|
||||||
|
// this is actually an event handler
|
||||||
|
VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
|
||||||
|
{
|
||||||
|
var vt = VT100.the_vt_, ch;
|
||||||
|
if (vt === undefined)
|
||||||
|
return true;
|
||||||
|
if (VT100.browser_ie_ || VT100.browser_opera_) {
|
||||||
|
ch = event.keyCode;
|
||||||
|
if (ch == 13)
|
||||||
|
ch = 10;
|
||||||
|
else if (ch > 255 || (ch < 32 && ch != 8))
|
||||||
|
return true;
|
||||||
|
ch = String.fromCharCode(ch);
|
||||||
|
} else {
|
||||||
|
ch = event.charCode;
|
||||||
|
//dump("ch: " + ch + "\n");
|
||||||
|
//dump("ctrl?: " + event.ctrlKey + "\n");
|
||||||
|
vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode);
|
||||||
|
if (ch) {
|
||||||
|
if (ch > 255)
|
||||||
|
return true;
|
||||||
|
if (event.ctrlKey && event.shiftKey) {
|
||||||
|
// Don't send the copy/paste commands.
|
||||||
|
var charStr = String.fromCharCode(ch);
|
||||||
|
if (charStr == 'C' || charStr == 'V') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
ch = String.fromCharCode(ch - 96);
|
||||||
|
} else {
|
||||||
|
ch = String.fromCharCode(ch);
|
||||||
|
if (ch == '\r')
|
||||||
|
ch = '\n';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case event.DOM_VK_BACK_SPACE:
|
||||||
|
ch = '\b';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_TAB:
|
||||||
|
ch = '\t';
|
||||||
|
// Stop tab from moving to another element.
|
||||||
|
event.preventDefault();
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_RETURN:
|
||||||
|
case event.DOM_VK_ENTER:
|
||||||
|
ch = '\n';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_UP:
|
||||||
|
ch = '\x1b[A';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_DOWN:
|
||||||
|
ch = '\x1b[B';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_RIGHT:
|
||||||
|
ch = '\x1b[C';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_LEFT:
|
||||||
|
ch = '\x1b[D';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_DELETE:
|
||||||
|
ch = '\x1b[3~';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_HOME:
|
||||||
|
ch = '\x1b[H';
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_ESCAPE:
|
||||||
|
ch = '\x1bc';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vt.key_buf_.push(ch);
|
||||||
|
setTimeout(VT100.go_getch_, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is actually an event handler
|
||||||
|
VT100.handle_onkeydown_ = function VT100_handle_onkeydown()
|
||||||
|
{
|
||||||
|
var vt = VT100.the_vt_, ch;
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 8:
|
||||||
|
ch = '\b'; break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
vt.key_buf_.push(ch);
|
||||||
|
setTimeout(VT100.go_getch_, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.go_getch_ = function VT100_go_getch()
|
||||||
|
{
|
||||||
|
var vt = VT100.the_vt_;
|
||||||
|
if (vt === undefined)
|
||||||
|
return;
|
||||||
|
var isr = vt.getch_isr_;
|
||||||
|
vt.getch_isr_ = undefined;
|
||||||
|
if (isr === undefined)
|
||||||
|
return;
|
||||||
|
var ch = vt.key_buf_.shift();
|
||||||
|
if (ch === undefined) {
|
||||||
|
vt.getch_isr_ = isr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vt.echo_)
|
||||||
|
vt.addch(ch);
|
||||||
|
isr(ch, vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// object methods
|
||||||
|
|
||||||
|
VT100.prototype.may_scroll_ = function()
|
||||||
|
{
|
||||||
|
var ht = this.ht_, cr = this.row_;
|
||||||
|
while (cr >= ht) {
|
||||||
|
this.scroll();
|
||||||
|
--cr;
|
||||||
|
}
|
||||||
|
this.row_ = cr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.html_colours_ = function(attr)
|
||||||
|
{
|
||||||
|
var fg, bg, co0, co1;
|
||||||
|
fg = attr.fg;
|
||||||
|
bg = attr.bg;
|
||||||
|
switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
|
||||||
|
case 0:
|
||||||
|
case VT100.A_DIM | VT100.A_BOLD:
|
||||||
|
co0 = '00'; co1 = 'c0';
|
||||||
|
break;
|
||||||
|
case VT100.A_BOLD:
|
||||||
|
co0 = '00'; co1 = 'ff';
|
||||||
|
break;
|
||||||
|
case VT100.A_DIM:
|
||||||
|
if (fg == VT100.COLOR_BLACK)
|
||||||
|
co0 = '40';
|
||||||
|
else
|
||||||
|
co0 = '00';
|
||||||
|
co1 = '40';
|
||||||
|
break;
|
||||||
|
case VT100.A_REVERSE:
|
||||||
|
case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
|
||||||
|
co0 = 'c0'; co1 = '40';
|
||||||
|
break;
|
||||||
|
case VT100.A_REVERSE | VT100.A_BOLD:
|
||||||
|
co0 = 'c0'; co1 = '00';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (fg == VT100.COLOR_BLACK)
|
||||||
|
co0 = '80';
|
||||||
|
else
|
||||||
|
co0 = 'c0';
|
||||||
|
co1 = 'c0';
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
f: '#' + (fg & 4 ? co1 : co0) +
|
||||||
|
(fg & 2 ? co1 : co0) +
|
||||||
|
(fg & 1 ? co1 : co0),
|
||||||
|
b: '#' + (bg & 4 ? co1 : co0) +
|
||||||
|
(bg & 2 ? co1 : co0) +
|
||||||
|
(bg & 1 ? co1 : co0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.addch = function(ch, attr)
|
||||||
|
{
|
||||||
|
var cc = this.col_;
|
||||||
|
this.debug("addch:: ch: " + ch + ", attr: " + attr);
|
||||||
|
switch (ch) {
|
||||||
|
case '\b':
|
||||||
|
if (cc != 0)
|
||||||
|
--cc;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
++this.row_;
|
||||||
|
cc = 0;
|
||||||
|
this.clrtoeol();
|
||||||
|
this.may_scroll_();
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
this.may_scroll_();
|
||||||
|
cc = 0;
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
this.may_scroll_();
|
||||||
|
cc += VT100.TABSIZE - cc % VT100.TABSIZE;
|
||||||
|
if (cc >= this.wd_) {
|
||||||
|
++this.row_;
|
||||||
|
cc -= this.wd_;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (attr === undefined)
|
||||||
|
attr = this._cloneAttr(this.c_attr_);
|
||||||
|
if (cc >= this.wd_) {
|
||||||
|
++this.row_;
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
this.may_scroll_();
|
||||||
|
this.text_[this.row_][cc] = ch;
|
||||||
|
this.attr_[this.row_][cc] = attr;
|
||||||
|
++cc;
|
||||||
|
}
|
||||||
|
this.col_ = cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.addstr = function(stuff)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < stuff.length; ++i)
|
||||||
|
this.addch(stuff.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype._cloneAttr = function VT100_cloneAttr(a)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
mode: a.mode,
|
||||||
|
fg: a.fg,
|
||||||
|
bg: a.bg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.attroff = function(a)
|
||||||
|
{
|
||||||
|
//dump("attroff: " + a + "\n");
|
||||||
|
a &= VT100.ATTR_FLAGS_;
|
||||||
|
this.c_attr_.mode &= ~a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.attron = function(a)
|
||||||
|
{
|
||||||
|
//dump("attron: " + a + "\n");
|
||||||
|
a &= VT100.ATTR_FLAGS_;
|
||||||
|
this.c_attr_.mode |= a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.attrset = function(a)
|
||||||
|
{
|
||||||
|
//dump("attrset: " + a + "\n");
|
||||||
|
this.c_attr_.mode = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.fgset = function(fg)
|
||||||
|
{
|
||||||
|
//dump("fgset: " + fg + "\n");
|
||||||
|
this.c_attr_.fg = fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.bgset = function(bg)
|
||||||
|
{
|
||||||
|
//dump("bgset: " + bg + "\n");
|
||||||
|
if (bg !== 0) {
|
||||||
|
this.warn("bgset: " + bg + "\n");
|
||||||
|
}
|
||||||
|
this.c_attr_.bg = bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.bkgdset = function(a)
|
||||||
|
{
|
||||||
|
this.bkgd_ = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clear = function()
|
||||||
|
{
|
||||||
|
this.debug("clear");
|
||||||
|
this.row_ = this.col_ = 0;
|
||||||
|
this.scrolled_ = 0;
|
||||||
|
for (r = 0; r < this.ht_; ++r) {
|
||||||
|
for (c = 0; c < this.wd_; ++c) {
|
||||||
|
this.text_[r][c] = ' ';
|
||||||
|
this.attr_[r][c] = this._cloneAttr(this.bkgd_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clrtobot = function()
|
||||||
|
{
|
||||||
|
this.debug("clrtobot, row: " + this.row_);
|
||||||
|
var ht = this.ht_;
|
||||||
|
var wd = this.wd_;
|
||||||
|
this.clrtoeol();
|
||||||
|
for (var r = this.row_ + 1; r < ht; ++r) {
|
||||||
|
for (var c = 0; c < wd; ++c) {
|
||||||
|
this.text_[r][c] = ' ';
|
||||||
|
this.attr_[r][c] = this.bkgd_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clrtoeol = function()
|
||||||
|
{
|
||||||
|
this.debug("clrtoeol, col: " + this.col_);
|
||||||
|
var r = this.row_;
|
||||||
|
if (r >= this.ht_)
|
||||||
|
return;
|
||||||
|
for (var c = this.col_; c < this.wd_; ++c) {
|
||||||
|
this.text_[r][c] = ' ';
|
||||||
|
this.attr_[r][c] = this.bkgd_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clearpos = function(row, col)
|
||||||
|
{
|
||||||
|
this.debug("clearpos (" + row + ", " + col + ")");
|
||||||
|
if (row < 0 || row >= this.ht_)
|
||||||
|
return;
|
||||||
|
if (col < 0 || col >= this.wd_)
|
||||||
|
return;
|
||||||
|
this.text_[row][col] = ' ';
|
||||||
|
this.attr_[row][col] = this.bkgd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.curs_set = function(vis, grab, eventist)
|
||||||
|
{
|
||||||
|
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
|
||||||
|
if (vis !== undefined)
|
||||||
|
this.cursor_vis_ = (vis > 0);
|
||||||
|
if (eventist === undefined)
|
||||||
|
eventist = window;
|
||||||
|
if (grab === true || grab === false) {
|
||||||
|
if (grab === this.grab_events_)
|
||||||
|
return;
|
||||||
|
if (grab) {
|
||||||
|
this.grab_events_ = true;
|
||||||
|
VT100.the_vt_ = this;
|
||||||
|
eventist.addEventListener("keypress", VT100.handle_onkeypress_, false);
|
||||||
|
if (VT100.browser_ie_)
|
||||||
|
document.onkeydown = VT100.handle_onkeydown_;
|
||||||
|
} else {
|
||||||
|
eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false);
|
||||||
|
if (VT100.browser_ie_)
|
||||||
|
document.onkeydown = VT100.handle_onkeydown_;
|
||||||
|
this.grab_events_ = false;
|
||||||
|
VT100.the_vt_ = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.echo = function()
|
||||||
|
{
|
||||||
|
this.debug("echo on");
|
||||||
|
this.echo_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.erase = VT100.prototype.clear;
|
||||||
|
|
||||||
|
VT100.prototype.getch = function(isr)
|
||||||
|
{
|
||||||
|
this.debug("getch");
|
||||||
|
this.refresh();
|
||||||
|
this.getch_isr_ = isr;
|
||||||
|
setTimeout(VT100.go_getch_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.getmaxyx = function()
|
||||||
|
{
|
||||||
|
return { y: this.ht_ - 1, x: this.wd_ - 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.getyx = function()
|
||||||
|
{
|
||||||
|
return { y: this.row_, x: this.col_ };
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.move = function(r, c)
|
||||||
|
{
|
||||||
|
this.debug("move: (" + r + ", " + c + ")");
|
||||||
|
if (r < 0)
|
||||||
|
r = 0;
|
||||||
|
else if (r >= this.ht_)
|
||||||
|
r = this.ht_ - 1;
|
||||||
|
if (c < 0)
|
||||||
|
c = 0;
|
||||||
|
else if (c >= this.wd_)
|
||||||
|
c = this.wd_ - 1;
|
||||||
|
this.row_ = r;
|
||||||
|
this.col_ = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.noecho = function()
|
||||||
|
{
|
||||||
|
this.debug("echo off");
|
||||||
|
this.echo_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.refresh = function()
|
||||||
|
{
|
||||||
|
this.debug("refresh");
|
||||||
|
var r, c, stuff = "", start_tag = "", end_tag = "", at = -1, n_at, ch,
|
||||||
|
pair, cr, cc, ht, wd, cv, added_end_tag;
|
||||||
|
ht = this.ht_;
|
||||||
|
wd = this.wd_;
|
||||||
|
cr = this.row_;
|
||||||
|
cc = this.col_;
|
||||||
|
cv = this.cursor_vis_;
|
||||||
|
var innerHTML = this.scr_.innerHTML;
|
||||||
|
if (cc >= wd)
|
||||||
|
cc = wd - 1;
|
||||||
|
for (r = 0; r < ht; ++r) {
|
||||||
|
if (r > 0) {
|
||||||
|
stuff += '\n';
|
||||||
|
}
|
||||||
|
for (c = 0; c < wd; ++c) {
|
||||||
|
added_end_tag = false;
|
||||||
|
n_at = this.attr_[r][c];
|
||||||
|
if (cv && r == cr && c == cc) {
|
||||||
|
// Draw the cursor here.
|
||||||
|
n_at = this._cloneAttr(n_at);
|
||||||
|
n_at.mode ^= VT100.A_REVERSE;
|
||||||
|
}
|
||||||
|
// If the attributes changed, make a new span.
|
||||||
|
if (n_at.mode != at.mode || n_at.fg != at.fg || n_at.bg != at.bg) {
|
||||||
|
if (c > 0) {
|
||||||
|
stuff += end_tag;
|
||||||
|
}
|
||||||
|
start_tag = "";
|
||||||
|
end_tag = "";
|
||||||
|
if (n_at.mode & VT100.A_BLINK) {
|
||||||
|
start_tag = "<blink>";
|
||||||
|
end_tag = "</blink>" + end_tag;
|
||||||
|
}
|
||||||
|
if (n_at.mode & VT100.A_STANDOUT)
|
||||||
|
n_at.mode |= VT100.A_BOLD;
|
||||||
|
pair = this.html_colours_(n_at);
|
||||||
|
start_tag += '<span style="color:' + pair.f +
|
||||||
|
';background-color:' + pair.b;
|
||||||
|
if (n_at.mode & VT100.A_UNDERLINE)
|
||||||
|
start_tag += ';text-decoration:underline';
|
||||||
|
start_tag += ';">';
|
||||||
|
stuff += start_tag;
|
||||||
|
end_tag = "</span>" + end_tag;
|
||||||
|
at = n_at;
|
||||||
|
added_end_tag = true;
|
||||||
|
} else if (c == 0) {
|
||||||
|
stuff += start_tag;
|
||||||
|
}
|
||||||
|
ch = this.text_[r][c];
|
||||||
|
switch (ch) {
|
||||||
|
case '&':
|
||||||
|
stuff += '&'; break;
|
||||||
|
case '<':
|
||||||
|
stuff += '<'; break;
|
||||||
|
case '>':
|
||||||
|
stuff += '>'; break;
|
||||||
|
case ' ':
|
||||||
|
//stuff += ' '; break;
|
||||||
|
stuff += ' '; break;
|
||||||
|
default:
|
||||||
|
stuff += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!added_end_tag)
|
||||||
|
stuff += end_tag;
|
||||||
|
}
|
||||||
|
this.scr_.innerHTML = "<b>" + stuff + "</b>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.scroll = function()
|
||||||
|
{
|
||||||
|
this.scrolled_ += 1;
|
||||||
|
this.debug("scrolled: " + this.scrolled_);
|
||||||
|
var n_text = this.text_[0], n_attr = this.attr_[0],
|
||||||
|
ht = this.ht_, wd = this.wd_;
|
||||||
|
for (var r = 1; r < ht; ++r) {
|
||||||
|
this.text_[r - 1] = this.text_[r];
|
||||||
|
this.attr_[r - 1] = this.attr_[r];
|
||||||
|
}
|
||||||
|
this.text_[ht - 1] = n_text;
|
||||||
|
this.attr_[ht - 1] = n_attr;
|
||||||
|
for (var c = 0; c < wd; ++c) {
|
||||||
|
n_text[c] = ' ';
|
||||||
|
n_attr[c] = this.bkgd_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.standend = function()
|
||||||
|
{
|
||||||
|
//this.debug("standend");
|
||||||
|
this.attrset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.standout = function()
|
||||||
|
{
|
||||||
|
//this.debug("standout");
|
||||||
|
this.attron(VT100.A_STANDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.write = function(stuff)
|
||||||
|
{
|
||||||
|
var ch, x, r, c, i, j, yx, myx;
|
||||||
|
for (i = 0; i < stuff.length; ++i) {
|
||||||
|
ch = stuff.charAt(i);
|
||||||
|
if (ch == '\x0D') {
|
||||||
|
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '\\x0D'");
|
||||||
|
} else {
|
||||||
|
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'");
|
||||||
|
}
|
||||||
|
//dump("ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'\n");
|
||||||
|
switch (ch) {
|
||||||
|
case '\x00':
|
||||||
|
case '\x7f':
|
||||||
|
case '\x07': /* bell, ignore it */
|
||||||
|
this.debug("write:: ignoring bell character: " + ch);
|
||||||
|
continue;
|
||||||
|
case '\a':
|
||||||
|
case '\b':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
this.addch(ch);
|
||||||
|
continue;
|
||||||
|
case '\n':
|
||||||
|
case '\v':
|
||||||
|
case '\f': // what a mess
|
||||||
|
yx = this.getyx();
|
||||||
|
myx = this.getmaxyx();
|
||||||
|
if (yx.y >= myx.y) {
|
||||||
|
this.scroll();
|
||||||
|
this.move(myx.y, 0);
|
||||||
|
} else
|
||||||
|
this.move(yx.y + 1, 0);
|
||||||
|
continue;
|
||||||
|
case '\x18':
|
||||||
|
case '\x1a':
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
this.debug("write:: set escape state: 0");
|
||||||
|
continue;
|
||||||
|
case '\x1b':
|
||||||
|
this.esc_state_ = 1;
|
||||||
|
this.debug("write:: set escape state: 1");
|
||||||
|
continue;
|
||||||
|
case '\x9b':
|
||||||
|
this.esc_state_ = 2;
|
||||||
|
this.debug("write:: set escape state: 2");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// not a recognized control character
|
||||||
|
switch (this.esc_state_) {
|
||||||
|
case 0: // not in escape sequence
|
||||||
|
this.addch(ch);
|
||||||
|
break;
|
||||||
|
case 1: // just saw ESC
|
||||||
|
switch (ch) {
|
||||||
|
case '[':
|
||||||
|
this.esc_state_ = 2;
|
||||||
|
this.debug("write:: set escape state: 2");
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
/* Set keypade mode (ignored) */
|
||||||
|
this.debug("write:: set keypade mode: ignored");
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
/* Reset keypade mode (ignored) */
|
||||||
|
this.debug("write:: reset keypade mode: ignored");
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
/* Set tab at cursor column (ignored) */
|
||||||
|
this.debug("write:: set tab cursor column: ignored");
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // just saw CSI
|
||||||
|
switch (ch) {
|
||||||
|
case 'K':
|
||||||
|
/* Erase in Line */
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
this.clrtoeol();
|
||||||
|
continue;
|
||||||
|
case 'H':
|
||||||
|
/* Move to (0,0). */
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
this.move(0, 0);
|
||||||
|
continue;
|
||||||
|
case 'J':
|
||||||
|
/* Clear to the bottom. */
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
this.clrtobot();
|
||||||
|
continue;
|
||||||
|
case '?':
|
||||||
|
/* Special VT100 mode handling. */
|
||||||
|
this.esc_state_ = 5;
|
||||||
|
this.debug("write:: special vt100 mode");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Drop through to next case.
|
||||||
|
this.csi_parms_ = [0];
|
||||||
|
this.debug("write:: set escape state: 3");
|
||||||
|
this.esc_state_ = 3;
|
||||||
|
case 3: // saw CSI and parameters
|
||||||
|
switch (ch) {
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
x = this.csi_parms_.pop();
|
||||||
|
this.csi_parms_.push(x * 10 + ch * 1);
|
||||||
|
this.debug("csi_parms_: " + this.csi_parms_);
|
||||||
|
continue;
|
||||||
|
case ';':
|
||||||
|
if (this.csi_parms_.length < 17)
|
||||||
|
this.csi_parms_.push(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
switch (ch) {
|
||||||
|
case 'A':
|
||||||
|
// Cursor Up <ESC>[{COUNT}A
|
||||||
|
this.move(this.row_ - Math.max(1, this.csi_parms_[0]),
|
||||||
|
this.col_);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
// Cursor Down <ESC>[{COUNT}B
|
||||||
|
this.move(this.row_ + Math.max(1, this.csi_parms_[0]),
|
||||||
|
this.col_);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
// Cursor Forward <ESC>[{COUNT}C
|
||||||
|
this.move(this.row_,
|
||||||
|
this.col_ + Math.max(1, this.csi_parms_[0]));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
this.warn("write:: got TERM query");
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
// Cursor Backward <ESC>[{COUNT}D
|
||||||
|
this.move(this.row_,
|
||||||
|
this.col_ - Math.max(1, this.csi_parms_[0]));
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
case 'H':
|
||||||
|
// Cursor Home <ESC>[{ROW};{COLUMN}H
|
||||||
|
this.csi_parms_.push(0);
|
||||||
|
this.move(this.csi_parms_[0] - 1,
|
||||||
|
this.csi_parms_[1] - 1);
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
switch (this.csi_parms_[0]) {
|
||||||
|
case 0:
|
||||||
|
this.clrtobot();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.clear();
|
||||||
|
this.move(0, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
for (j=0; j<this.csi_parms_.length; ++j) {
|
||||||
|
x = this.csi_parms_[j];
|
||||||
|
switch (x) {
|
||||||
|
case 0:
|
||||||
|
this.standend();
|
||||||
|
this.fgset(this.bkgd_.fg);
|
||||||
|
this.bgset(this.bkgd_.bg);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this.attron(VT100.A_BOLD);
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
this.fgset(VT100.COLOR_BLACK);
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
this.fgset(VT100.COLOR_RED);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
this.fgset(VT100.COLOR_GREEN);
|
||||||
|
break;
|
||||||
|
case 33:
|
||||||
|
this.fgset(VT100.COLOR_YELLOW);
|
||||||
|
break;
|
||||||
|
case 34:
|
||||||
|
this.fgset(VT100.COLOR_BLUE);
|
||||||
|
break;
|
||||||
|
case 35:
|
||||||
|
this.fgset(VT100.COLOR_MAGENTA);
|
||||||
|
break;
|
||||||
|
case 36:
|
||||||
|
this.fgset(VT100.COLOR_CYAN);
|
||||||
|
break;
|
||||||
|
case 37:
|
||||||
|
this.fgset(VT100.COLOR_WHITE);
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
this.bgset(VT100.COLOR_BLACK);
|
||||||
|
break;
|
||||||
|
case 41:
|
||||||
|
this.bgset(VT100.COLOR_RED);
|
||||||
|
break;
|
||||||
|
case 42:
|
||||||
|
this.bgset(VT100.COLOR_GREEN);
|
||||||
|
break;
|
||||||
|
case 44:
|
||||||
|
this.bgset(VT100.COLOR_YELLOW);
|
||||||
|
break;
|
||||||
|
case 44:
|
||||||
|
this.bgset(VT100.COLOR_BLUE);
|
||||||
|
break;
|
||||||
|
case 45:
|
||||||
|
this.bgset(VT100.COLOR_MAGENTA);
|
||||||
|
break;
|
||||||
|
case 46:
|
||||||
|
this.bgset(VT100.COLOR_CYAN);
|
||||||
|
break;
|
||||||
|
case 47:
|
||||||
|
this.bgset(VT100.COLOR_WHITE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
// 1,24r - set scrolling region (ignored)
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
this.debug("write:: set escape state: 4");
|
||||||
|
this.esc_state_ = 4;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
// 0g: clear tab at cursor (ignored)
|
||||||
|
// 3g: clear all tabs (ignored)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.warn("write:: unknown command: " + ch);
|
||||||
|
this.csi_parms_ = [];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: // saw CSI [
|
||||||
|
this.esc_state_ = 0; // gobble char.
|
||||||
|
break;
|
||||||
|
case 5: // Special mode handling, saw <ESC>[?
|
||||||
|
// Expect a number - the reset type
|
||||||
|
this.csi_parms_ = [ch];
|
||||||
|
this.esc_state_ = 6;
|
||||||
|
break;
|
||||||
|
case 6: // Reset mode handling, saw <ESC>[?1
|
||||||
|
// Expect a letter - the mode target, example:
|
||||||
|
// <ESC>[?1l : cursor key mode = cursor
|
||||||
|
// <ESC>[?1h : save current screen, create new empty
|
||||||
|
// screen and position at 0,0
|
||||||
|
// <ESC>[?5l : White on blk
|
||||||
|
// XXX: Ignored for now.
|
||||||
|
//dump("Saw reset mode: <ESC>[?" + this.csi_parms_[0] + ch + "\n");
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
this.debug("write:: set escape state: 0");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.debug = function(message) {
|
||||||
|
if (this.debug_) {
|
||||||
|
dump(message + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.warn = function(message) {
|
||||||
|
dump(message + "\n");
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #111;
|
|
||||||
font-family: "Helvetica";
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_controls {
|
|
||||||
background: #111;
|
|
||||||
line-height: 1em;
|
|
||||||
color: #FFF;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 4px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_controls ul {
|
|
||||||
list-style:none;
|
|
||||||
list-style-position: outside;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
#VNC_controls li {
|
|
||||||
margin-right: 15px;
|
|
||||||
padding: 2px 0;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
#VNC_controls li input[type=text],
|
|
||||||
#VNC_controls li input[type=password] {
|
|
||||||
border: 2px solid #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_host {
|
|
||||||
width: 100;
|
|
||||||
}
|
|
||||||
#VNC_port {
|
|
||||||
width: 50;
|
|
||||||
}
|
|
||||||
#VNC_password {
|
|
||||||
width: 80;
|
|
||||||
}
|
|
||||||
#VNC_encrypt {
|
|
||||||
}
|
|
||||||
#VNC_connect_button {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_status_bar td {
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
#VNC_status_bar div {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
.VNC_status_button {
|
|
||||||
font-size: 10px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#VNC_status {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#VNC_settings_menu {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
width: 12em;
|
|
||||||
border: 1px solid #888;
|
|
||||||
background-color: #f0f2f6;
|
|
||||||
padding: 5px; margin: 3px;
|
|
||||||
z-index: 100; opacity: 1;
|
|
||||||
text-align: left; white-space: normal;
|
|
||||||
}
|
|
||||||
#VNC_settings_menu ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VNC_buttons_right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.VNC_buttons_left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.VNC_status_normal {
|
|
||||||
background: #111;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.VNC_status_error {
|
|
||||||
background: #111;
|
|
||||||
color: #f44;
|
|
||||||
}
|
|
||||||
.VNC_status_warn {
|
|
||||||
background: #111;
|
|
||||||
color: #ff4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_screen {
|
|
||||||
-webkit-border-radius: 10px;
|
|
||||||
-moz-border-radius: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #111;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0 auto;
|
|
||||||
color: #FFF;
|
|
||||||
margin-top: 20px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
/* This causes the width of the outer div(#screen) honor the size of the inner (#vnc) div */
|
|
||||||
display: table;
|
|
||||||
table-layout: auto;
|
|
||||||
}
|
|
||||||
#VNC_canvas {
|
|
||||||
background: #111;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
#VNC_clipboard {
|
|
||||||
display: none;
|
|
||||||
}
|
|
|
@ -1,783 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2010 Joel Martin
|
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/*jslint browser: true, white: false, bitwise: false */
|
|
||||||
/*global window, Util, Base64 */
|
|
||||||
|
|
||||||
Canvas = function(conf) {
|
|
||||||
|
|
||||||
conf = conf || {}; // Configuration
|
|
||||||
var that = {}, // Public API interface
|
|
||||||
|
|
||||||
// Private Canvas namespace variables
|
|
||||||
c_forceCanvas = false,
|
|
||||||
|
|
||||||
c_width = 0,
|
|
||||||
c_height = 0,
|
|
||||||
|
|
||||||
c_prevStyle = "",
|
|
||||||
|
|
||||||
c_keyPress = null,
|
|
||||||
c_mouseButton = null,
|
|
||||||
c_mouseMove = null,
|
|
||||||
|
|
||||||
c_webkit_bug = false,
|
|
||||||
c_flush_timer = null;
|
|
||||||
|
|
||||||
// Configuration settings
|
|
||||||
function cdef(v, type, defval, desc) {
|
|
||||||
Util.conf_default(conf, that, v, type, defval, desc); }
|
|
||||||
|
|
||||||
// Capability settings, default can be overridden
|
|
||||||
cdef('prefer_js', 'raw', null, 'Prefer Javascript over canvas methods');
|
|
||||||
cdef('cursor_uri', 'raw', null, 'Can we render cursor using data URI');
|
|
||||||
|
|
||||||
cdef('target', 'dom', null, 'Canvas element for VNC viewport');
|
|
||||||
cdef('focusContainer', 'dom', document, 'DOM element that traps keyboard input');
|
|
||||||
cdef('true_color', 'bool', true, 'Request true color pixel data');
|
|
||||||
cdef('focused', 'bool', true, 'Capture and send key strokes');
|
|
||||||
cdef('colourMap', 'raw', [], 'Colour map array (not true color)');
|
|
||||||
cdef('scale', 'float', 1, 'VNC viewport scale factor');
|
|
||||||
|
|
||||||
cdef('render_mode', 'str', '', 'Canvas rendering mode (read-only)');
|
|
||||||
|
|
||||||
// Override some specific getters/setters
|
|
||||||
that.set_prefer_js = function(val) {
|
|
||||||
if (val && c_forceCanvas) {
|
|
||||||
Util.Warn("Preferring Javascript to Canvas ops is not supported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
conf.prefer_js = val;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.get_colourMap = function(idx) {
|
|
||||||
if (typeof idx === 'undefined') {
|
|
||||||
return conf.colourMap;
|
|
||||||
} else {
|
|
||||||
return conf.colourMap[idx];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.set_colourMap = function(val, idx) {
|
|
||||||
if (typeof idx === 'undefined') {
|
|
||||||
conf.colourMap = val;
|
|
||||||
} else {
|
|
||||||
conf.colourMap[idx] = val;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.set_render_mode = function () { throw("render_mode is read-only"); };
|
|
||||||
|
|
||||||
// Add some other getters/setters
|
|
||||||
that.get_width = function() {
|
|
||||||
return c_width;
|
|
||||||
};
|
|
||||||
that.get_height = function() {
|
|
||||||
return c_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Private functions
|
|
||||||
//
|
|
||||||
|
|
||||||
// Create the public API interface
|
|
||||||
function constructor() {
|
|
||||||
Util.Debug(">> Canvas.init");
|
|
||||||
|
|
||||||
var c, ctx, func, origfunc, imgTest, tval, i, curDat, curSave,
|
|
||||||
has_imageData = false, UE = Util.Engine;
|
|
||||||
|
|
||||||
if (! conf.target) { throw("target must be set"); }
|
|
||||||
|
|
||||||
if (typeof conf.target === 'string') {
|
|
||||||
throw("target must be a DOM element");
|
|
||||||
}
|
|
||||||
|
|
||||||
c = conf.target;
|
|
||||||
|
|
||||||
if (! c.getContext) { throw("no getContext method"); }
|
|
||||||
|
|
||||||
if (! conf.ctx) { conf.ctx = c.getContext('2d'); }
|
|
||||||
ctx = conf.ctx;
|
|
||||||
|
|
||||||
Util.Debug("User Agent: " + navigator.userAgent);
|
|
||||||
if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); }
|
|
||||||
if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); }
|
|
||||||
if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); }
|
|
||||||
if (UE.presto) { Util.Debug("Browser: presto " + UE.presto); }
|
|
||||||
|
|
||||||
that.clear();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine browser Canvas feature support
|
|
||||||
* and select fastest rendering methods
|
|
||||||
*/
|
|
||||||
tval = 0;
|
|
||||||
try {
|
|
||||||
imgTest = ctx.getImageData(0, 0, 1,1);
|
|
||||||
imgTest.data[0] = 123;
|
|
||||||
imgTest.data[3] = 255;
|
|
||||||
ctx.putImageData(imgTest, 0, 0);
|
|
||||||
tval = ctx.getImageData(0, 0, 1, 1).data[0];
|
|
||||||
if (tval === 123) {
|
|
||||||
has_imageData = true;
|
|
||||||
}
|
|
||||||
} catch (exc1) {}
|
|
||||||
|
|
||||||
if (has_imageData) {
|
|
||||||
Util.Info("Canvas supports imageData");
|
|
||||||
c_forceCanvas = false;
|
|
||||||
if (ctx.createImageData) {
|
|
||||||
// If it's there, it's faster
|
|
||||||
Util.Info("Using Canvas createImageData");
|
|
||||||
conf.render_mode = "createImageData rendering";
|
|
||||||
that.imageData = that.imageDataCreate;
|
|
||||||
} else if (ctx.getImageData) {
|
|
||||||
// I think this is mostly just Opera
|
|
||||||
Util.Info("Using Canvas getImageData");
|
|
||||||
conf.render_mode = "getImageData rendering";
|
|
||||||
that.imageData = that.imageDataGet;
|
|
||||||
}
|
|
||||||
Util.Info("Prefering javascript operations");
|
|
||||||
if (conf.prefer_js === null) {
|
|
||||||
conf.prefer_js = true;
|
|
||||||
}
|
|
||||||
that.rgbxImage = that.rgbxImageData;
|
|
||||||
that.cmapImage = that.cmapImageData;
|
|
||||||
} else {
|
|
||||||
Util.Warn("Canvas lacks imageData, using fillRect (slow)");
|
|
||||||
conf.render_mode = "fillRect rendering (slow)";
|
|
||||||
c_forceCanvas = true;
|
|
||||||
conf.prefer_js = false;
|
|
||||||
that.rgbxImage = that.rgbxImageFill;
|
|
||||||
that.cmapImage = that.cmapImageFill;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) {
|
|
||||||
// Workaround WebKit canvas rendering bug #46319
|
|
||||||
conf.render_mode += ", webkit bug workaround";
|
|
||||||
Util.Debug("Working around WebKit bug #46319");
|
|
||||||
c_webkit_bug = true;
|
|
||||||
for (func in {"fillRect":1, "copyImage":1, "rgbxImage":1,
|
|
||||||
"cmapImage":1, "blitStringImage":1}) {
|
|
||||||
that[func] = (function() {
|
|
||||||
var myfunc = that[func]; // Save original function
|
|
||||||
//Util.Debug("Wrapping " + func);
|
|
||||||
return function() {
|
|
||||||
myfunc.apply(this, arguments);
|
|
||||||
if (!c_flush_timer) {
|
|
||||||
c_flush_timer = setTimeout(that.flush, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine browser support for setting the cursor via data URI
|
|
||||||
* scheme
|
|
||||||
*/
|
|
||||||
curDat = [];
|
|
||||||
for (i=0; i < 8 * 8 * 4; i += 1) {
|
|
||||||
curDat.push(255);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
curSave = c.style.cursor;
|
|
||||||
that.changeCursor(curDat, curDat, 2, 2, 8, 8);
|
|
||||||
if (c.style.cursor) {
|
|
||||||
if (conf.cursor_uri === null) {
|
|
||||||
conf.cursor_uri = true;
|
|
||||||
}
|
|
||||||
Util.Info("Data URI scheme cursor supported");
|
|
||||||
} else {
|
|
||||||
if (conf.cursor_uri === null) {
|
|
||||||
conf.cursor_uri = false;
|
|
||||||
}
|
|
||||||
Util.Warn("Data URI scheme cursor not supported");
|
|
||||||
}
|
|
||||||
c.style.cursor = curSave;
|
|
||||||
} catch (exc2) {
|
|
||||||
Util.Error("Data URI scheme cursor test exception: " + exc2);
|
|
||||||
conf.cursor_uri = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.focused = true;
|
|
||||||
|
|
||||||
Util.Debug("<< Canvas.init");
|
|
||||||
return that ;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseButton(e, down) {
|
|
||||||
var evt, pos, bmask;
|
|
||||||
if (! conf.focused) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
|
||||||
bmask = 1 << evt.button;
|
|
||||||
//Util.Debug('mouse ' + pos.x + "," + pos.y + " down: " + down + " bmask: " + bmask);
|
|
||||||
if (c_mouseButton) {
|
|
||||||
c_mouseButton(pos.x, pos.y, down, bmask);
|
|
||||||
}
|
|
||||||
Util.stopEvent(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseDown(e) {
|
|
||||||
onMouseButton(e, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseUp(e) {
|
|
||||||
onMouseButton(e, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseWheel(e) {
|
|
||||||
var evt, pos, bmask, wheelData;
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
|
||||||
wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
|
|
||||||
if (wheelData > 0) {
|
|
||||||
bmask = 1 << 3;
|
|
||||||
} else {
|
|
||||||
bmask = 1 << 4;
|
|
||||||
}
|
|
||||||
//Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
|
|
||||||
if (c_mouseButton) {
|
|
||||||
c_mouseButton(pos.x, pos.y, 1, bmask);
|
|
||||||
c_mouseButton(pos.x, pos.y, 0, bmask);
|
|
||||||
}
|
|
||||||
Util.stopEvent(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseMove(e) {
|
|
||||||
var evt, pos;
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
|
||||||
//Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
|
|
||||||
if (c_mouseMove) {
|
|
||||||
c_mouseMove(pos.x, pos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeyDown(e) {
|
|
||||||
//Util.Debug("keydown: " + getKeysym(e));
|
|
||||||
if (! conf.focused) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c_keyPress) {
|
|
||||||
c_keyPress(getKeysym(e), 1, e.ctrlKey, e.shiftKey, e.altKey);
|
|
||||||
}
|
|
||||||
Util.stopEvent(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeyUp(e) {
|
|
||||||
//Util.Debug("keyup: " + getKeysym(e));
|
|
||||||
if (! conf.focused) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c_keyPress) {
|
|
||||||
c_keyPress(getKeysym(e), 0, e.ctrlKey, e.shiftKey, e.altKey);
|
|
||||||
}
|
|
||||||
Util.stopEvent(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseDisable(e) {
|
|
||||||
var evt, pos;
|
|
||||||
if (! conf.focused) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
|
||||||
/* Stop propagation if inside canvas area */
|
|
||||||
if ((pos.x >= 0) && (pos.y >= 0) &&
|
|
||||||
(pos.x < c_width) && (pos.y < c_height)) {
|
|
||||||
//Util.Debug("mouse event disabled");
|
|
||||||
Util.stopEvent(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//Util.Debug("mouse event not disabled");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Public API interface functions
|
|
||||||
//
|
|
||||||
|
|
||||||
that.getContext = function () {
|
|
||||||
return conf.ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.start = function(keyPressFunc, mouseButtonFunc, mouseMoveFunc) {
|
|
||||||
var c;
|
|
||||||
Util.Debug(">> Canvas.start");
|
|
||||||
|
|
||||||
c = conf.target;
|
|
||||||
c_keyPress = keyPressFunc || null;
|
|
||||||
c_mouseButton = mouseButtonFunc || null;
|
|
||||||
c_mouseMove = mouseMoveFunc || null;
|
|
||||||
|
|
||||||
Util.addEvent(conf.focusContainer, 'keydown', onKeyDown);
|
|
||||||
Util.addEvent(conf.focusContainer, 'keyup', onKeyUp);
|
|
||||||
Util.addEvent(c, 'mousedown', onMouseDown);
|
|
||||||
Util.addEvent(c, 'mouseup', onMouseUp);
|
|
||||||
Util.addEvent(c, 'mousemove', onMouseMove);
|
|
||||||
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
|
||||||
onMouseWheel);
|
|
||||||
|
|
||||||
/* Work around right and middle click browser behaviors */
|
|
||||||
Util.addEvent(conf.focusContainer, 'click', onMouseDisable);
|
|
||||||
Util.addEvent(conf.focusContainer.body, 'contextmenu', onMouseDisable);
|
|
||||||
|
|
||||||
Util.Debug("<< Canvas.start");
|
|
||||||
};
|
|
||||||
|
|
||||||
that.rescale = function(factor) {
|
|
||||||
var c, tp, x, y,
|
|
||||||
properties = ['transform', 'WebkitTransform', 'MozTransform', null];
|
|
||||||
c = conf.target;
|
|
||||||
tp = properties.shift();
|
|
||||||
while (tp) {
|
|
||||||
if (typeof c.style[tp] !== 'undefined') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tp = properties.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tp === null) {
|
|
||||||
Util.Debug("No scaling support");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.scale === factor) {
|
|
||||||
//Util.Debug("Canvas already scaled to '" + factor + "'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.scale = factor;
|
|
||||||
x = c.width - c.width * factor;
|
|
||||||
y = c.height - c.height * factor;
|
|
||||||
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
|
|
||||||
};
|
|
||||||
|
|
||||||
that.resize = function(width, height, true_color) {
|
|
||||||
var c = conf.target;
|
|
||||||
|
|
||||||
if (typeof true_color !== "undefined") {
|
|
||||||
conf.true_color = true_color;
|
|
||||||
}
|
|
||||||
c_prevStyle = "";
|
|
||||||
|
|
||||||
c.width = width;
|
|
||||||
c.height = height;
|
|
||||||
|
|
||||||
c_width = c.offsetWidth;
|
|
||||||
c_height = c.offsetHeight;
|
|
||||||
|
|
||||||
that.rescale(conf.scale);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.clear = function() {
|
|
||||||
that.resize(640, 20);
|
|
||||||
conf.ctx.clearRect(0, 0, c_width, c_height);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.stop = function() {
|
|
||||||
var c = conf.target;
|
|
||||||
Util.removeEvent(conf.focusContainer, 'keydown', onKeyDown);
|
|
||||||
Util.removeEvent(conf.focusContainer, 'keyup', onKeyUp);
|
|
||||||
Util.removeEvent(c, 'mousedown', onMouseDown);
|
|
||||||
Util.removeEvent(c, 'mouseup', onMouseUp);
|
|
||||||
Util.removeEvent(c, 'mousemove', onMouseMove);
|
|
||||||
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
|
||||||
onMouseWheel);
|
|
||||||
|
|
||||||
/* Work around right and middle click browser behaviors */
|
|
||||||
Util.removeEvent(conf.focusContainer, 'click', onMouseDisable);
|
|
||||||
Util.removeEvent(conf.focusContainer.body, 'contextmenu', onMouseDisable);
|
|
||||||
|
|
||||||
// Turn off cursor rendering
|
|
||||||
if (conf.cursor_uri) {
|
|
||||||
c.style.cursor = "default";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.flush = function() {
|
|
||||||
var old_val;
|
|
||||||
//Util.Debug(">> flush");
|
|
||||||
// Force canvas redraw (for webkit bug #46319 workaround)
|
|
||||||
old_val = conf.target.style.marginRight;
|
|
||||||
conf.target.style.marginRight = "1px";
|
|
||||||
c_flush_timer = null;
|
|
||||||
setTimeout(function () {
|
|
||||||
conf.target.style.marginRight = old_val;
|
|
||||||
}, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.setFillColor = function(color) {
|
|
||||||
var rgb, newStyle;
|
|
||||||
if (conf.true_color) {
|
|
||||||
rgb = color;
|
|
||||||
} else {
|
|
||||||
rgb = conf.colourMap[color[0]];
|
|
||||||
}
|
|
||||||
newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
|
|
||||||
if (newStyle !== c_prevStyle) {
|
|
||||||
conf.ctx.fillStyle = newStyle;
|
|
||||||
c_prevStyle = newStyle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.fillRect = function(x, y, width, height, color) {
|
|
||||||
that.setFillColor(color);
|
|
||||||
conf.ctx.fillRect(x, y, width, height);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.copyImage = function(old_x, old_y, new_x, new_y, width, height) {
|
|
||||||
conf.ctx.drawImage(conf.target, old_x, old_y, width, height,
|
|
||||||
new_x, new_y, width, height);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tile rendering functions optimized for rendering engines.
|
|
||||||
*
|
|
||||||
* - In Chrome/webkit, Javascript image data array manipulations are
|
|
||||||
* faster than direct Canvas fillStyle, fillRect rendering. In
|
|
||||||
* gecko, Javascript array handling is much slower.
|
|
||||||
*/
|
|
||||||
that.getTile = function(x, y, width, height, color) {
|
|
||||||
var img, data = [], p, rgb, red, green, blue, j, i;
|
|
||||||
img = {'x': x, 'y': y, 'width': width, 'height': height,
|
|
||||||
'data': data};
|
|
||||||
if (conf.prefer_js) {
|
|
||||||
if (conf.true_color) {
|
|
||||||
rgb = color;
|
|
||||||
} else {
|
|
||||||
rgb = conf.colourMap[color[0]];
|
|
||||||
}
|
|
||||||
red = rgb[0];
|
|
||||||
green = rgb[1];
|
|
||||||
blue = rgb[2];
|
|
||||||
for (i = 0; i < (width * height * 4); i+=4) {
|
|
||||||
data[i ] = red;
|
|
||||||
data[i + 1] = green;
|
|
||||||
data[i + 2] = blue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
that.fillRect(x, y, width, height, color);
|
|
||||||
}
|
|
||||||
return img;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.setSubTile = function(img, x, y, w, h, color) {
|
|
||||||
var data, p, rgb, red, green, blue, width, j, i, xend, yend;
|
|
||||||
if (conf.prefer_js) {
|
|
||||||
data = img.data;
|
|
||||||
width = img.width;
|
|
||||||
if (conf.true_color) {
|
|
||||||
rgb = color;
|
|
||||||
} else {
|
|
||||||
rgb = conf.colourMap[color[0]];
|
|
||||||
}
|
|
||||||
red = rgb[0];
|
|
||||||
green = rgb[1];
|
|
||||||
blue = rgb[2];
|
|
||||||
xend = x + w;
|
|
||||||
yend = y + h;
|
|
||||||
for (j = y; j < yend; j += 1) {
|
|
||||||
for (i = x; i < xend; i += 1) {
|
|
||||||
p = (i + (j * width) ) * 4;
|
|
||||||
data[p ] = red;
|
|
||||||
data[p + 1] = green;
|
|
||||||
data[p + 2] = blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
that.fillRect(img.x + x, img.y + y, w, h, color);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.putTile = function(img) {
|
|
||||||
if (conf.prefer_js) {
|
|
||||||
that.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
|
|
||||||
} else {
|
|
||||||
// No-op, under gecko already done by setSubTile
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.imageDataGet = function(width, height) {
|
|
||||||
return conf.ctx.getImageData(0, 0, width, height);
|
|
||||||
};
|
|
||||||
that.imageDataCreate = function(width, height) {
|
|
||||||
return conf.ctx.createImageData(width, height);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.rgbxImageData = function(x, y, width, height, arr, offset) {
|
|
||||||
var img, i, j, data;
|
|
||||||
img = that.imageData(width, height);
|
|
||||||
data = img.data;
|
|
||||||
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
|
|
||||||
data[i + 0] = arr[j + 0];
|
|
||||||
data[i + 1] = arr[j + 1];
|
|
||||||
data[i + 2] = arr[j + 2];
|
|
||||||
data[i + 3] = 255; // Set Alpha
|
|
||||||
}
|
|
||||||
conf.ctx.putImageData(img, x, y);
|
|
||||||
};
|
|
||||||
|
|
||||||
// really slow fallback if we don't have imageData
|
|
||||||
that.rgbxImageFill = function(x, y, width, height, arr, offset) {
|
|
||||||
var i, j, sx = 0, sy = 0;
|
|
||||||
for (i=0, j=offset; i < (width * height); i+=1, j+=4) {
|
|
||||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j+0], arr[j+1], arr[j+2]]);
|
|
||||||
sx += 1;
|
|
||||||
if ((sx % width) === 0) {
|
|
||||||
sx = 0;
|
|
||||||
sy += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.cmapImageData = function(x, y, width, height, arr, offset) {
|
|
||||||
var img, i, j, data, rgb, cmap;
|
|
||||||
img = that.imageData(width, height);
|
|
||||||
data = img.data;
|
|
||||||
cmap = conf.colourMap;
|
|
||||||
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
|
|
||||||
rgb = cmap[arr[j]];
|
|
||||||
data[i + 0] = rgb[0];
|
|
||||||
data[i + 1] = rgb[1];
|
|
||||||
data[i + 2] = rgb[2];
|
|
||||||
data[i + 3] = 255; // Set Alpha
|
|
||||||
}
|
|
||||||
conf.ctx.putImageData(img, x, y);
|
|
||||||
};
|
|
||||||
|
|
||||||
that.cmapImageFill = function(x, y, width, height, arr, offset) {
|
|
||||||
var i, j, sx = 0, sy = 0, cmap;
|
|
||||||
cmap = conf.colourMap;
|
|
||||||
for (i=0, j=offset; i < (width * height); i+=1, j+=1) {
|
|
||||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j]]);
|
|
||||||
sx += 1;
|
|
||||||
if ((sx % width) === 0) {
|
|
||||||
sx = 0;
|
|
||||||
sy += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
that.blitImage = function(x, y, width, height, arr, offset) {
|
|
||||||
if (conf.true_color) {
|
|
||||||
that.rgbxImage(x, y, width, height, arr, offset);
|
|
||||||
} else {
|
|
||||||
that.cmapImage(x, y, width, height, arr, offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.blitStringImage = function(str, x, y) {
|
|
||||||
var img = new Image();
|
|
||||||
img.onload = function () { conf.ctx.drawImage(img, x, y); };
|
|
||||||
img.src = str;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.changeCursor = function(pixels, mask, hotx, hoty, w, h) {
|
|
||||||
var cur = [], cmap, rgb, IHDRsz, ANDsz, XORsz, url, idx, alpha, x, y;
|
|
||||||
//Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h);
|
|
||||||
|
|
||||||
if (conf.cursor_uri === false) {
|
|
||||||
Util.Warn("changeCursor called but no cursor data URI support");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push multi-byte little-endian values
|
|
||||||
cur.push16le = function (num) {
|
|
||||||
this.push((num ) & 0xFF,
|
|
||||||
(num >> 8) & 0xFF );
|
|
||||||
};
|
|
||||||
cur.push32le = function (num) {
|
|
||||||
this.push((num ) & 0xFF,
|
|
||||||
(num >> 8) & 0xFF,
|
|
||||||
(num >> 16) & 0xFF,
|
|
||||||
(num >> 24) & 0xFF );
|
|
||||||
};
|
|
||||||
|
|
||||||
cmap = conf.colourMap;
|
|
||||||
IHDRsz = 40;
|
|
||||||
ANDsz = w * h * 4;
|
|
||||||
XORsz = Math.ceil( (w * h) / 8.0 );
|
|
||||||
|
|
||||||
// Main header
|
|
||||||
cur.push16le(0); // Reserved
|
|
||||||
cur.push16le(2); // .CUR type
|
|
||||||
cur.push16le(1); // Number of images, 1 for non-animated ico
|
|
||||||
|
|
||||||
// Cursor #1 header
|
|
||||||
cur.push(w); // width
|
|
||||||
cur.push(h); // height
|
|
||||||
cur.push(0); // colors, 0 -> true-color
|
|
||||||
cur.push(0); // reserved
|
|
||||||
cur.push16le(hotx); // hotspot x coordinate
|
|
||||||
cur.push16le(hoty); // hotspot y coordinate
|
|
||||||
cur.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size
|
|
||||||
cur.push32le(22); // offset of cursor data in the file
|
|
||||||
|
|
||||||
// Cursor #1 InfoHeader
|
|
||||||
cur.push32le(IHDRsz); // Infoheader size
|
|
||||||
cur.push32le(w); // Cursor width
|
|
||||||
cur.push32le(h*2); // XOR+AND height
|
|
||||||
cur.push16le(1); // number of planes
|
|
||||||
cur.push16le(32); // bits per pixel
|
|
||||||
cur.push32le(0); // Type of compression
|
|
||||||
cur.push32le(XORsz + ANDsz); // Size of Image
|
|
||||||
cur.push32le(0);
|
|
||||||
cur.push32le(0);
|
|
||||||
cur.push32le(0);
|
|
||||||
cur.push32le(0);
|
|
||||||
|
|
||||||
// XOR/color data
|
|
||||||
for (y = h-1; y >= 0; y -= 1) {
|
|
||||||
for (x = 0; x < w; x += 1) {
|
|
||||||
idx = y * Math.ceil(w / 8) + Math.floor(x/8);
|
|
||||||
alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
|
|
||||||
|
|
||||||
if (conf.true_color) {
|
|
||||||
idx = ((w * y) + x) * 4;
|
|
||||||
cur.push(pixels[idx + 2]); // blue
|
|
||||||
cur.push(pixels[idx + 1]); // green
|
|
||||||
cur.push(pixels[idx + 0]); // red
|
|
||||||
cur.push(alpha); // red
|
|
||||||
} else {
|
|
||||||
idx = (w * y) + x;
|
|
||||||
rgb = cmap[pixels[idx]];
|
|
||||||
cur.push(rgb[2]); // blue
|
|
||||||
cur.push(rgb[1]); // green
|
|
||||||
cur.push(rgb[0]); // red
|
|
||||||
cur.push(alpha); // alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AND/bitmask data (ignored, just needs to be right size)
|
|
||||||
for (y = 0; y < h; y += 1) {
|
|
||||||
for (x = 0; x < Math.ceil(w / 8); x += 1) {
|
|
||||||
cur.push(0x00);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
url = "data:image/x-icon;base64," + Base64.encode(cur);
|
|
||||||
conf.target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default";
|
|
||||||
//Util.Debug("<< changeCursor, cur.length: " + cur.length);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return constructor(); // Return the public API interface
|
|
||||||
|
|
||||||
} // End of Canvas()
|
|
||||||
|
|
||||||
|
|
||||||
/* Translate DOM key down/up event to keysym value */
|
|
||||||
function getKeysym(e) {
|
|
||||||
var evt, keysym;
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
|
|
||||||
/* Remap modifier and special keys */
|
|
||||||
switch ( evt.keyCode ) {
|
|
||||||
case 8 : keysym = 0xFF08; break; // BACKSPACE
|
|
||||||
case 9 : keysym = 0xFF09; break; // TAB
|
|
||||||
case 13 : keysym = 0xFF0D; break; // ENTER
|
|
||||||
case 27 : keysym = 0xFF1B; break; // ESCAPE
|
|
||||||
case 45 : keysym = 0xFF63; break; // INSERT
|
|
||||||
case 46 : keysym = 0xFFFF; break; // DELETE
|
|
||||||
case 36 : keysym = 0xFF50; break; // HOME
|
|
||||||
case 35 : keysym = 0xFF57; break; // END
|
|
||||||
case 33 : keysym = 0xFF55; break; // PAGE_UP
|
|
||||||
case 34 : keysym = 0xFF56; break; // PAGE_DOWN
|
|
||||||
case 37 : keysym = 0xFF51; break; // LEFT
|
|
||||||
case 38 : keysym = 0xFF52; break; // UP
|
|
||||||
case 39 : keysym = 0xFF53; break; // RIGHT
|
|
||||||
case 40 : keysym = 0xFF54; break; // DOWN
|
|
||||||
case 112 : keysym = 0xFFBE; break; // F1
|
|
||||||
case 113 : keysym = 0xFFBF; break; // F2
|
|
||||||
case 114 : keysym = 0xFFC0; break; // F3
|
|
||||||
case 115 : keysym = 0xFFC1; break; // F4
|
|
||||||
case 116 : keysym = 0xFFC2; break; // F5
|
|
||||||
case 117 : keysym = 0xFFC3; break; // F6
|
|
||||||
case 118 : keysym = 0xFFC4; break; // F7
|
|
||||||
case 119 : keysym = 0xFFC5; break; // F8
|
|
||||||
case 120 : keysym = 0xFFC6; break; // F9
|
|
||||||
case 121 : keysym = 0xFFC7; break; // F10
|
|
||||||
case 122 : keysym = 0xFFC8; break; // F11
|
|
||||||
case 123 : keysym = 0xFFC9; break; // F12
|
|
||||||
case 16 : keysym = 0xFFE1; break; // SHIFT
|
|
||||||
case 17 : keysym = 0xFFE3; break; // CONTROL
|
|
||||||
//case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
|
|
||||||
case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command)
|
|
||||||
default : keysym = evt.keyCode; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remap symbols */
|
|
||||||
switch (keysym) {
|
|
||||||
case 186 : keysym = 59; break; // ; (IE)
|
|
||||||
case 187 : keysym = 61; break; // = (IE)
|
|
||||||
case 188 : keysym = 44; break; // , (Mozilla, IE)
|
|
||||||
case 109 : // - (Mozilla)
|
|
||||||
if (Util.Engine.gecko) {
|
|
||||||
keysym = 45; }
|
|
||||||
break;
|
|
||||||
case 189 : keysym = 45; break; // - (IE)
|
|
||||||
case 190 : keysym = 46; break; // . (Mozilla, IE)
|
|
||||||
case 191 : keysym = 47; break; // / (Mozilla, IE)
|
|
||||||
case 192 : keysym = 96; break; // ` (Mozilla, IE)
|
|
||||||
case 219 : keysym = 91; break; // [ (Mozilla, IE)
|
|
||||||
case 220 : keysym = 92; break; // \ (Mozilla, IE)
|
|
||||||
case 221 : keysym = 93; break; // ] (Mozilla, IE)
|
|
||||||
case 222 : keysym = 39; break; // ' (Mozilla, IE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remap shifted and unshifted keys */
|
|
||||||
if (!!evt.shiftKey) {
|
|
||||||
switch (keysym) {
|
|
||||||
case 48 : keysym = 41 ; break; // ) (shifted 0)
|
|
||||||
case 49 : keysym = 33 ; break; // ! (shifted 1)
|
|
||||||
case 50 : keysym = 64 ; break; // @ (shifted 2)
|
|
||||||
case 51 : keysym = 35 ; break; // # (shifted 3)
|
|
||||||
case 52 : keysym = 36 ; break; // $ (shifted 4)
|
|
||||||
case 53 : keysym = 37 ; break; // % (shifted 5)
|
|
||||||
case 54 : keysym = 94 ; break; // ^ (shifted 6)
|
|
||||||
case 55 : keysym = 38 ; break; // & (shifted 7)
|
|
||||||
case 56 : keysym = 42 ; break; // * (shifted 8)
|
|
||||||
case 57 : keysym = 40 ; break; // ( (shifted 9)
|
|
||||||
|
|
||||||
case 59 : keysym = 58 ; break; // : (shifted `)
|
|
||||||
case 61 : keysym = 43 ; break; // + (shifted ;)
|
|
||||||
case 44 : keysym = 60 ; break; // < (shifted ,)
|
|
||||||
case 45 : keysym = 95 ; break; // _ (shifted -)
|
|
||||||
case 46 : keysym = 62 ; break; // > (shifted .)
|
|
||||||
case 47 : keysym = 63 ; break; // ? (shifted /)
|
|
||||||
case 96 : keysym = 126; break; // ~ (shifted `)
|
|
||||||
case 91 : keysym = 123; break; // { (shifted [)
|
|
||||||
case 92 : keysym = 124; break; // | (shifted \)
|
|
||||||
case 93 : keysym = 125; break; // } (shifted ])
|
|
||||||
case 39 : keysym = 34 ; break; // " (shifted ')
|
|
||||||
}
|
|
||||||
} else if ((keysym >= 65) && (keysym <=90)) {
|
|
||||||
/* Remap unshifted A-Z */
|
|
||||||
keysym += 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keysym;
|
|
||||||
}
|
|
||||||
|
|
273
include/des.js
273
include/des.js
|
@ -1,273 +0,0 @@
|
||||||
/*
|
|
||||||
* Ported from Flashlight VNC ActionScript implementation:
|
|
||||||
* http://www.wizhelp.com/flashlight-vnc/
|
|
||||||
*
|
|
||||||
* Full attribution follows:
|
|
||||||
*
|
|
||||||
* -------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This DES class has been extracted from package Acme.Crypto for use in VNC.
|
|
||||||
* The unnecessary odd parity code has been removed.
|
|
||||||
*
|
|
||||||
* These changes are:
|
|
||||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
|
|
||||||
* DesCipher - the DES encryption method
|
|
||||||
*
|
|
||||||
* The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
|
|
||||||
*
|
|
||||||
* Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software
|
|
||||||
* and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
|
|
||||||
* without fee is hereby granted, provided that this copyright notice is kept
|
|
||||||
* intact.
|
|
||||||
*
|
|
||||||
* WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
|
|
||||||
* OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
||||||
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
|
|
||||||
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
|
|
||||||
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
|
|
||||||
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
|
|
||||||
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
|
|
||||||
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
|
|
||||||
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
|
|
||||||
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
|
|
||||||
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
|
|
||||||
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
|
|
||||||
* HIGH RISK ACTIVITIES.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The rest is:
|
|
||||||
*
|
|
||||||
* Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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.
|
|
||||||
*
|
|
||||||
* Visit the ACME Labs Java page for up-to-date versions of this and other
|
|
||||||
* fine Java utilities: http://www.acme.com/java/
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/*jslint white: false, bitwise: false, plusplus: false */
|
|
||||||
|
|
||||||
function DES(passwd) {
|
|
||||||
|
|
||||||
// Tables, permutations, S-boxes, etc.
|
|
||||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
|
||||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
|
||||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
|
||||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
|
||||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
|
||||||
keys = [];
|
|
||||||
|
|
||||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
|
||||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
|
||||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
|
||||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
|
||||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
|
||||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
|
||||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
|
||||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
|
||||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
|
||||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
|
||||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
|
||||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
|
||||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
|
||||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
|
||||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
|
||||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
|
||||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
|
||||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
|
||||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
|
||||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
|
||||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
|
||||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
|
||||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
|
||||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
|
||||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
|
||||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
|
||||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
|
||||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
|
||||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
|
||||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
|
||||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
|
||||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
|
||||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
|
||||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
|
||||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
|
||||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
|
||||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
|
||||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
|
||||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
|
||||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
|
||||||
|
|
||||||
// Set the key.
|
|
||||||
function setKeys(keyBlock) {
|
|
||||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
|
||||||
raw0, raw1, rawi, KnLi;
|
|
||||||
|
|
||||||
for (j = 0, l = 56; j < 56; ++j, l-=8) {
|
|
||||||
l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1
|
|
||||||
m = l & 0x7;
|
|
||||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 16; ++i) {
|
|
||||||
m = i << 1;
|
|
||||||
n = m + 1;
|
|
||||||
kn[m] = kn[n] = 0;
|
|
||||||
for (o=28; o<59; o+=28) {
|
|
||||||
for (j = o-28; j < o; ++j) {
|
|
||||||
l = j + totrot[i];
|
|
||||||
if (l < o) {
|
|
||||||
pcr[j] = pc1m[l];
|
|
||||||
} else {
|
|
||||||
pcr[j] = pc1m[l - 28];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (j = 0; j < 24; ++j) {
|
|
||||||
if (pcr[PC2[j]] !== 0) {
|
|
||||||
kn[m] |= 1<<(23-j);
|
|
||||||
}
|
|
||||||
if (pcr[PC2[j + 24]] !== 0) {
|
|
||||||
kn[n] |= 1<<(23-j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cookey
|
|
||||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
|
||||||
raw0 = kn[rawi++];
|
|
||||||
raw1 = kn[rawi++];
|
|
||||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
|
||||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
|
||||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
|
||||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
|
||||||
++KnLi;
|
|
||||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
|
||||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
|
||||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
|
||||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
|
||||||
++KnLi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt 8 bytes of text
|
|
||||||
function enc8(text) {
|
|
||||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
|
||||||
l, r, x; // left, right, accumulator
|
|
||||||
|
|
||||||
// Squash 8 bytes to 2 ints
|
|
||||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
|
||||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
|
||||||
|
|
||||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 4);
|
|
||||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 16);
|
|
||||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 2);
|
|
||||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 8);
|
|
||||||
r = (r << 1) | ((r >>> 31) & 1);
|
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
|
||||||
l ^= x;
|
|
||||||
r ^= x;
|
|
||||||
l = (l << 1) | ((l >>> 31) & 1);
|
|
||||||
|
|
||||||
for (i = 0; i < 8; ++i) {
|
|
||||||
x = (r << 28) | (r >>> 4);
|
|
||||||
x ^= keys[keysi++];
|
|
||||||
fval = SP7[x & 0x3f];
|
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
|
||||||
x = r ^ keys[keysi++];
|
|
||||||
fval |= SP8[x & 0x3f];
|
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
|
||||||
l ^= fval;
|
|
||||||
x = (l << 28) | (l >>> 4);
|
|
||||||
x ^= keys[keysi++];
|
|
||||||
fval = SP7[x & 0x3f];
|
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
|
||||||
x = l ^ keys[keysi++];
|
|
||||||
fval |= SP8[x & 0x0000003f];
|
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
|
||||||
r ^= fval;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = (r << 31) | (r >>> 1);
|
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
|
||||||
l ^= x;
|
|
||||||
r ^= x;
|
|
||||||
l = (l << 31) | (l >>> 1);
|
|
||||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 8);
|
|
||||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 2);
|
|
||||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 16);
|
|
||||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 4);
|
|
||||||
|
|
||||||
// Spread ints to bytes
|
|
||||||
x = [r, l];
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256;
|
|
||||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt 16 bytes of text using passwd as key
|
|
||||||
function encrypt(t) {
|
|
||||||
return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
setKeys(passwd); // Setup keys
|
|
||||||
return {'encrypt': encrypt}; // Public interface
|
|
||||||
|
|
||||||
} // function DES
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* from noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2010 Joel Martin
|
||||||
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Translate DOM key down/up event to keysym value */
|
||||||
|
function getKeysym(e) {
|
||||||
|
var evt, keysym;
|
||||||
|
evt = (e ? e : window.event);
|
||||||
|
|
||||||
|
/* Remap modifier and special keys */
|
||||||
|
switch ( evt.keyCode ) {
|
||||||
|
case 8 : keysym = 0xFF08; break; // BACKSPACE
|
||||||
|
case 9 : keysym = 0xFF09; break; // TAB
|
||||||
|
case 13 : keysym = 0xFF0D; break; // ENTER
|
||||||
|
case 27 : keysym = 0xFF1B; break; // ESCAPE
|
||||||
|
case 45 : keysym = 0xFF63; break; // INSERT
|
||||||
|
case 46 : keysym = 0xFFFF; break; // DELETE
|
||||||
|
case 36 : keysym = 0xFF50; break; // HOME
|
||||||
|
case 35 : keysym = 0xFF57; break; // END
|
||||||
|
case 33 : keysym = 0xFF55; break; // PAGE_UP
|
||||||
|
case 34 : keysym = 0xFF56; break; // PAGE_DOWN
|
||||||
|
case 37 : keysym = 0xFF51; break; // LEFT
|
||||||
|
case 38 : keysym = 0xFF52; break; // UP
|
||||||
|
case 39 : keysym = 0xFF53; break; // RIGHT
|
||||||
|
case 40 : keysym = 0xFF54; break; // DOWN
|
||||||
|
case 112 : keysym = 0xFFBE; break; // F1
|
||||||
|
case 113 : keysym = 0xFFBF; break; // F2
|
||||||
|
case 114 : keysym = 0xFFC0; break; // F3
|
||||||
|
case 115 : keysym = 0xFFC1; break; // F4
|
||||||
|
case 116 : keysym = 0xFFC2; break; // F5
|
||||||
|
case 117 : keysym = 0xFFC3; break; // F6
|
||||||
|
case 118 : keysym = 0xFFC4; break; // F7
|
||||||
|
case 119 : keysym = 0xFFC5; break; // F8
|
||||||
|
case 120 : keysym = 0xFFC6; break; // F9
|
||||||
|
case 121 : keysym = 0xFFC7; break; // F10
|
||||||
|
case 122 : keysym = 0xFFC8; break; // F11
|
||||||
|
case 123 : keysym = 0xFFC9; break; // F12
|
||||||
|
case 16 : keysym = 0xFFE1; break; // SHIFT
|
||||||
|
case 17 : keysym = 0xFFE3; break; // CONTROL
|
||||||
|
//case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
|
||||||
|
case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command)
|
||||||
|
default : keysym = evt.keyCode; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remap symbols */
|
||||||
|
switch (keysym) {
|
||||||
|
case 186 : keysym = 59; break; // ; (IE)
|
||||||
|
case 187 : keysym = 61; break; // = (IE)
|
||||||
|
case 188 : keysym = 44; break; // , (Mozilla, IE)
|
||||||
|
case 109 : // - (Mozilla)
|
||||||
|
if (Util.Engine.gecko) {
|
||||||
|
keysym = 45; }
|
||||||
|
break;
|
||||||
|
case 189 : keysym = 45; break; // - (IE)
|
||||||
|
case 190 : keysym = 46; break; // . (Mozilla, IE)
|
||||||
|
case 191 : keysym = 47; break; // / (Mozilla, IE)
|
||||||
|
case 192 : keysym = 96; break; // ` (Mozilla, IE)
|
||||||
|
case 219 : keysym = 91; break; // [ (Mozilla, IE)
|
||||||
|
case 220 : keysym = 92; break; // \ (Mozilla, IE)
|
||||||
|
case 221 : keysym = 93; break; // ] (Mozilla, IE)
|
||||||
|
case 222 : keysym = 39; break; // ' (Mozilla, IE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remap shifted and unshifted keys */
|
||||||
|
if (!!evt.shiftKey) {
|
||||||
|
switch (keysym) {
|
||||||
|
case 48 : keysym = 41 ; break; // ) (shifted 0)
|
||||||
|
case 49 : keysym = 33 ; break; // ! (shifted 1)
|
||||||
|
case 50 : keysym = 64 ; break; // @ (shifted 2)
|
||||||
|
case 51 : keysym = 35 ; break; // # (shifted 3)
|
||||||
|
case 52 : keysym = 36 ; break; // $ (shifted 4)
|
||||||
|
case 53 : keysym = 37 ; break; // % (shifted 5)
|
||||||
|
case 54 : keysym = 94 ; break; // ^ (shifted 6)
|
||||||
|
case 55 : keysym = 38 ; break; // & (shifted 7)
|
||||||
|
case 56 : keysym = 42 ; break; // * (shifted 8)
|
||||||
|
case 57 : keysym = 40 ; break; // ( (shifted 9)
|
||||||
|
|
||||||
|
case 59 : keysym = 58 ; break; // : (shifted `)
|
||||||
|
case 61 : keysym = 43 ; break; // + (shifted ;)
|
||||||
|
case 44 : keysym = 60 ; break; // < (shifted ,)
|
||||||
|
case 45 : keysym = 95 ; break; // _ (shifted -)
|
||||||
|
case 46 : keysym = 62 ; break; // > (shifted .)
|
||||||
|
case 47 : keysym = 63 ; break; // ? (shifted /)
|
||||||
|
case 96 : keysym = 126; break; // ~ (shifted `)
|
||||||
|
case 91 : keysym = 123; break; // { (shifted [)
|
||||||
|
case 92 : keysym = 124; break; // | (shifted \)
|
||||||
|
case 93 : keysym = 125; break; // } (shifted ])
|
||||||
|
case 39 : keysym = 34 ; break; // " (shifted ')
|
||||||
|
}
|
||||||
|
} else if ((keysym >= 65) && (keysym <=90)) {
|
||||||
|
/* Remap unshifted A-Z */
|
||||||
|
keysym += 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keysym;
|
||||||
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
#VNC_controls {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#VNC_controls ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
#VNC_controls li {
|
|
||||||
float: left;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_host {
|
|
||||||
width: 100;
|
|
||||||
}
|
|
||||||
#VNC_port {
|
|
||||||
width: 50;
|
|
||||||
}
|
|
||||||
#VNC_password {
|
|
||||||
width: 80;
|
|
||||||
}
|
|
||||||
#VNC_encrypt {
|
|
||||||
}
|
|
||||||
#VNC_connectTimeout {
|
|
||||||
width: 30;
|
|
||||||
}
|
|
||||||
#VNC_connect_button {
|
|
||||||
width: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_status_bar td {
|
|
||||||
margin-top: 15px;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
#VNC_status_bar div {
|
|
||||||
font-size: 12px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
.VNC_status_button {
|
|
||||||
font-size: 10px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#VNC_status {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#VNC_settings_menu {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
width: 12em;
|
|
||||||
border: 1px solid #888;
|
|
||||||
background-color: #f0f2f6;
|
|
||||||
padding: 5px; margin: 3px;
|
|
||||||
z-index: 100; opacity: 1;
|
|
||||||
text-align: left; white-space: normal;
|
|
||||||
}
|
|
||||||
#VNC_settings_menu ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VNC_buttons_right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.VNC_buttons_left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.VNC_status_normal {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
.VNC_status_error {
|
|
||||||
background: #f44;
|
|
||||||
}
|
|
||||||
.VNC_status_warn {
|
|
||||||
background: #ff4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect
|
|
||||||
* scaling will occur. Canvas resizes to remote VNC settings */
|
|
||||||
#VNC_screen {
|
|
||||||
text-align: center;
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
#VNC_canvas {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_clipboard_clear_button {
|
|
||||||
}
|
|
||||||
#VNC_clipboard_text {
|
|
||||||
font-size: 9;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2010 Joel Martin
|
|
||||||
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/*jslint browser: true, white: false */
|
|
||||||
/*global Util, VNC_frame_data, finish */
|
|
||||||
|
|
||||||
var rfb, mode, test_state, frame_idx, frame_length,
|
|
||||||
iteration, iterations, istart_time,
|
|
||||||
|
|
||||||
// Pre-declarations for jslint
|
|
||||||
send_array, next_iteration, queue_next_packet, do_packet;
|
|
||||||
|
|
||||||
// Override send_array
|
|
||||||
send_array = function (arr) {
|
|
||||||
// Stub out send_array
|
|
||||||
};
|
|
||||||
|
|
||||||
next_iteration = function () {
|
|
||||||
if (iteration === 0) {
|
|
||||||
frame_length = VNC_frame_data.length;
|
|
||||||
test_state = 'running';
|
|
||||||
} else {
|
|
||||||
rfb.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_state !== 'running') { return; }
|
|
||||||
|
|
||||||
iteration += 1;
|
|
||||||
if (iteration > iterations) {
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_idx = 0;
|
|
||||||
istart_time = (new Date()).getTime();
|
|
||||||
rfb.connect('test', 0, "bogus");
|
|
||||||
|
|
||||||
queue_next_packet();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
queue_next_packet = function () {
|
|
||||||
var frame, foffset, toffset, delay;
|
|
||||||
if (test_state !== 'running') { return; }
|
|
||||||
|
|
||||||
frame = VNC_frame_data[frame_idx];
|
|
||||||
while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) {
|
|
||||||
//Util.Debug("Send frame " + frame_idx);
|
|
||||||
frame_idx += 1;
|
|
||||||
frame = VNC_frame_data[frame_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame === 'EOF') {
|
|
||||||
Util.Debug("Finished, found EOF");
|
|
||||||
next_iteration();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (frame_idx >= frame_length) {
|
|
||||||
Util.Debug("Finished, no more frames");
|
|
||||||
next_iteration();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode === 'realtime') {
|
|
||||||
foffset = frame.slice(1, frame.indexOf('{', 1));
|
|
||||||
toffset = (new Date()).getTime() - istart_time;
|
|
||||||
delay = foffset - toffset;
|
|
||||||
if (delay < 1) {
|
|
||||||
delay = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(do_packet, delay);
|
|
||||||
} else {
|
|
||||||
setTimeout(do_packet, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
do_packet = function () {
|
|
||||||
//Util.Debug("Processing frame: " + frame_idx);
|
|
||||||
var frame = VNC_frame_data[frame_idx];
|
|
||||||
rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)});
|
|
||||||
frame_idx += 1;
|
|
||||||
|
|
||||||
queue_next_packet();
|
|
||||||
};
|
|
||||||
|
|
1622
include/rfb.js
1622
include/rfb.js
File diff suppressed because it is too large
Load Diff
420
include/ui.js
420
include/ui.js
|
@ -1,420 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2010 Joel Martin
|
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*/
|
|
||||||
"use strict";
|
|
||||||
/*jslint white: false */
|
|
||||||
/*global $, Util, RFB, Canvas, VNC_uri_prefix, Element, Fx */
|
|
||||||
|
|
||||||
var UI = {
|
|
||||||
|
|
||||||
settingsOpen : false,
|
|
||||||
|
|
||||||
// Render default UI and initialize settings menu
|
|
||||||
load: function(target) {
|
|
||||||
var html = '', i, sheet, sheets, llevels;
|
|
||||||
|
|
||||||
/* Populate the 'target' DOM element with default UI */
|
|
||||||
if (!target) {
|
|
||||||
target = $D('vnc');
|
|
||||||
} else if (typeof target === 'string') {
|
|
||||||
target = $D(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!document.createElement('canvas').getContext) &&
|
|
||||||
window.ActiveXObject) {
|
|
||||||
// Suggest Chrome frame for Internet Explorer users
|
|
||||||
html += '<center><div style="text-align: left; width: 400px">';
|
|
||||||
html += ' You are using a version of Internet Explorer ';
|
|
||||||
html += ' that does not have HTML5 Canvas support. ';
|
|
||||||
html += ' To use noVNC you must use a browser with HTML5 ';
|
|
||||||
html += ' Canvas support or install ';
|
|
||||||
html += ' <a href="http://google.com/chromeframe" target="cframe">';
|
|
||||||
html += ' Google Chrome Frame.</a>';
|
|
||||||
html += '</div></center>';
|
|
||||||
target.innerHTML = html;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '<div id="VNC_controls">';
|
|
||||||
html += ' <ul>';
|
|
||||||
html += ' <li>Host: <input id="VNC_host"></li>';
|
|
||||||
html += ' <li>Port: <input id="VNC_port"></li>';
|
|
||||||
html += ' <li>Password: <input id="VNC_password"';
|
|
||||||
html += ' type="password"></li>';
|
|
||||||
html += ' <li><input id="VNC_connect_button" type="button"';
|
|
||||||
html += ' value="Loading" disabled></li>';
|
|
||||||
html += ' </ul>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '<div id="VNC_screen">';
|
|
||||||
html += ' <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">';
|
|
||||||
html += ' <table border=0 width=100%><tr>';
|
|
||||||
html += ' <td><div id="VNC_status">Loading</div></td>';
|
|
||||||
html += ' <td width=1%><div class="VNC_buttons_right">';
|
|
||||||
html += ' <input type=button class="VNC_status_button" value="Settings"';
|
|
||||||
html += ' id="menuButton"';
|
|
||||||
html += ' onclick="UI.clickSettingsMenu();">';
|
|
||||||
html += ' <span id="VNC_settings_menu"';
|
|
||||||
html += ' onmouseover="UI.canvasBlur();"';
|
|
||||||
html += ' onmouseout="UI.canvasFocus();">';
|
|
||||||
html += ' <ul>';
|
|
||||||
html += ' <li><input id="VNC_encrypt"';
|
|
||||||
html += ' type="checkbox"> Encrypt</li>';
|
|
||||||
html += ' <li><input id="VNC_true_color"';
|
|
||||||
html += ' type="checkbox" checked> True Color</li>';
|
|
||||||
html += ' <li><input id="VNC_cursor"';
|
|
||||||
html += ' type="checkbox"> Local Cursor</li>';
|
|
||||||
html += ' <li><input id="VNC_shared"';
|
|
||||||
html += ' type="checkbox"> Shared Mode</li>';
|
|
||||||
html += ' <li><input id="VNC_connectTimeout"';
|
|
||||||
html += ' type="input"> Connect Timeout (s)</li>';
|
|
||||||
html += ' <hr>';
|
|
||||||
|
|
||||||
// Stylesheet selection dropdown
|
|
||||||
html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
|
|
||||||
html += ' <option value="default">default</option>';
|
|
||||||
sheet = WebUtil.selectStylesheet();
|
|
||||||
sheets = WebUtil.getStylesheets();
|
|
||||||
for (i = 0; i < sheets.length; i += 1) {
|
|
||||||
html += '<option value="' + sheets[i].title + '">' + sheets[i].title + '</option>';
|
|
||||||
}
|
|
||||||
html += ' </select> Style</li>';
|
|
||||||
|
|
||||||
// Logging selection dropdown
|
|
||||||
html += ' <li><select id="VNC_logging" name="vncLogging">';
|
|
||||||
llevels = ['error', 'warn', 'info', 'debug'];
|
|
||||||
for (i = 0; i < llevels.length; i += 1) {
|
|
||||||
html += '<option value="' + llevels[i] + '">' + llevels[i] + '</option>';
|
|
||||||
}
|
|
||||||
html += ' </select> Logging</li>';
|
|
||||||
|
|
||||||
html += ' <hr>';
|
|
||||||
html += ' <li><input type="button" id="VNC_apply" value="Apply"';
|
|
||||||
html += ' onclick="UI.settingsApply()"></li>';
|
|
||||||
html += ' </ul>';
|
|
||||||
html += ' </span></div></td>';
|
|
||||||
html += ' <td width=1%><div class="VNC_buttons_right">';
|
|
||||||
html += ' <input type=button class="VNC_status_button" value="Send CtrlAltDel"';
|
|
||||||
html += ' id="sendCtrlAltDelButton"';
|
|
||||||
html += ' onclick="UI.sendCtrlAltDel();"></div></td>';
|
|
||||||
html += ' </tr></table>';
|
|
||||||
html += ' </div>';
|
|
||||||
html += ' <canvas id="VNC_canvas" width="640px" height="20px">';
|
|
||||||
html += ' Canvas not supported.';
|
|
||||||
html += ' </canvas>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '<br><br>';
|
|
||||||
html += '<div id="VNC_clipboard">';
|
|
||||||
html += ' VNC Clipboard:';
|
|
||||||
html += ' <input id="VNC_clipboard_clear_button"';
|
|
||||||
html += ' type="button" value="Clear"';
|
|
||||||
html += ' onclick="UI.clipClear();">';
|
|
||||||
html += ' <br>';
|
|
||||||
html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5';
|
|
||||||
html += ' onfocus="UI.canvasBlur();"';
|
|
||||||
html += ' onblur="UI.canvasFocus();"';
|
|
||||||
html += ' onchange="UI.clipSend();"></textarea>';
|
|
||||||
html += '</div>';
|
|
||||||
target.innerHTML = html;
|
|
||||||
|
|
||||||
// Settings with immediate effects
|
|
||||||
UI.initSetting('logging', 'warn');
|
|
||||||
WebUtil.init_logging(UI.getSetting('logging'));
|
|
||||||
UI.initSetting('stylesheet', 'default');
|
|
||||||
|
|
||||||
WebUtil.selectStylesheet(null); // call twice to get around webkit bug
|
|
||||||
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
|
||||||
|
|
||||||
/* Populate the controls if defaults are provided in the URL */
|
|
||||||
UI.initSetting('host', '');
|
|
||||||
UI.initSetting('port', '');
|
|
||||||
UI.initSetting('password', '');
|
|
||||||
UI.initSetting('encrypt', false);
|
|
||||||
UI.initSetting('true_color', true);
|
|
||||||
UI.initSetting('cursor', false);
|
|
||||||
UI.initSetting('shared', true);
|
|
||||||
UI.initSetting('connectTimeout', 2);
|
|
||||||
|
|
||||||
UI.rfb = RFB({'target': $D('VNC_canvas'),
|
|
||||||
'updateState': UI.updateState,
|
|
||||||
'clipboardReceive': UI.clipReceive});
|
|
||||||
|
|
||||||
// Unfocus clipboard when over the VNC area
|
|
||||||
$D('VNC_screen').onmousemove = function () {
|
|
||||||
var canvas = UI.rfb.get_canvas();
|
|
||||||
if ((! canvas) || (! canvas.get_focused())) {
|
|
||||||
$D('VNC_clipboard_text').blur();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// Read form control compatible setting from cookie
|
|
||||||
getSetting: function(name) {
|
|
||||||
var val, ctrl = $D('VNC_' + name);
|
|
||||||
val = WebUtil.readCookie(name);
|
|
||||||
if (ctrl.type === 'checkbox') {
|
|
||||||
if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
|
|
||||||
val = false;
|
|
||||||
} else {
|
|
||||||
val = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update cookie and form control setting. If value is not set, then
|
|
||||||
// updates from control to current cookie setting.
|
|
||||||
updateSetting: function(name, value) {
|
|
||||||
var i, ctrl = $D('VNC_' + name);
|
|
||||||
// Save the cookie for this session
|
|
||||||
if (typeof value !== 'undefined') {
|
|
||||||
WebUtil.createCookie(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the settings control
|
|
||||||
value = UI.getSetting(name);
|
|
||||||
if (ctrl.type === 'checkbox') {
|
|
||||||
ctrl.checked = value;
|
|
||||||
} else if (typeof ctrl.options !== 'undefined') {
|
|
||||||
for (i = 0; i < ctrl.options.length; i += 1) {
|
|
||||||
if (ctrl.options[i].value === value) {
|
|
||||||
ctrl.selectedIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctrl.value = value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Save control setting to cookie
|
|
||||||
saveSetting: function(name) {
|
|
||||||
var val, ctrl = $D('VNC_' + name);
|
|
||||||
if (ctrl.type === 'checkbox') {
|
|
||||||
val = ctrl.checked;
|
|
||||||
} else if (typeof ctrl.options !== 'undefined') {
|
|
||||||
val = ctrl.options[ctrl.selectedIndex].value;
|
|
||||||
} else {
|
|
||||||
val = ctrl.value;
|
|
||||||
}
|
|
||||||
WebUtil.createCookie(name, val);
|
|
||||||
//Util.Debug("Setting saved '" + name + "=" + val + "'");
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initial page load read/initialization of settings
|
|
||||||
initSetting: function(name, defVal) {
|
|
||||||
var val;
|
|
||||||
|
|
||||||
// Check Query string followed by cookie
|
|
||||||
val = WebUtil.getQueryVar(name);
|
|
||||||
if (val === null) {
|
|
||||||
val = WebUtil.readCookie(name, defVal);
|
|
||||||
}
|
|
||||||
UI.updateSetting(name, val);
|
|
||||||
//Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// Toggle the settings menu:
|
|
||||||
// On open, settings are refreshed from saved cookies.
|
|
||||||
// On close, settings are applied
|
|
||||||
clickSettingsMenu: function() {
|
|
||||||
if (UI.settingsOpen) {
|
|
||||||
UI.settingsApply();
|
|
||||||
|
|
||||||
UI.closeSettingsMenu();
|
|
||||||
} else {
|
|
||||||
UI.updateSetting('encrypt');
|
|
||||||
UI.updateSetting('true_color');
|
|
||||||
if (UI.rfb.get_canvas().get_cursor_uri()) {
|
|
||||||
UI.updateSetting('cursor');
|
|
||||||
} else {
|
|
||||||
UI.updateSetting('cursor', false);
|
|
||||||
$D('VNC_cursor').disabled = true;
|
|
||||||
}
|
|
||||||
UI.updateSetting('shared');
|
|
||||||
UI.updateSetting('connectTimeout');
|
|
||||||
UI.updateSetting('stylesheet');
|
|
||||||
UI.updateSetting('logging');
|
|
||||||
|
|
||||||
UI.openSettingsMenu();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open menu
|
|
||||||
openSettingsMenu: function() {
|
|
||||||
$D('VNC_settings_menu').style.display = "block";
|
|
||||||
UI.settingsOpen = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Close menu (without applying settings)
|
|
||||||
closeSettingsMenu: function() {
|
|
||||||
$D('VNC_settings_menu').style.display = "none";
|
|
||||||
UI.settingsOpen = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Disable/enable controls depending on connection state
|
|
||||||
settingsDisabled: function(disabled, rfb) {
|
|
||||||
//Util.Debug(">> settingsDisabled");
|
|
||||||
$D('VNC_encrypt').disabled = disabled;
|
|
||||||
$D('VNC_true_color').disabled = disabled;
|
|
||||||
if (rfb && rfb.get_canvas() && rfb.get_canvas().get_cursor_uri()) {
|
|
||||||
$D('VNC_cursor').disabled = disabled;
|
|
||||||
} else {
|
|
||||||
UI.updateSetting('cursor', false);
|
|
||||||
$D('VNC_cursor').disabled = true;
|
|
||||||
}
|
|
||||||
$D('VNC_shared').disabled = disabled;
|
|
||||||
$D('VNC_connectTimeout').disabled = disabled;
|
|
||||||
//Util.Debug("<< settingsDisabled");
|
|
||||||
},
|
|
||||||
|
|
||||||
// Save/apply settings when 'Apply' button is pressed
|
|
||||||
settingsApply: function() {
|
|
||||||
//Util.Debug(">> settingsApply");
|
|
||||||
UI.saveSetting('encrypt');
|
|
||||||
UI.saveSetting('true_color');
|
|
||||||
if (UI.rfb.get_canvas().get_cursor_uri()) {
|
|
||||||
UI.saveSetting('cursor');
|
|
||||||
}
|
|
||||||
UI.saveSetting('shared');
|
|
||||||
UI.saveSetting('connectTimeout');
|
|
||||||
UI.saveSetting('stylesheet');
|
|
||||||
UI.saveSetting('logging');
|
|
||||||
|
|
||||||
// Settings with immediate (non-connected related) effect
|
|
||||||
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
|
||||||
WebUtil.init_logging(UI.getSetting('logging'));
|
|
||||||
|
|
||||||
//Util.Debug("<< settingsApply");
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setPassword: function() {
|
|
||||||
UI.rfb.sendPassword($D('VNC_password').value);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendCtrlAltDel: function() {
|
|
||||||
UI.rfb.sendCtrlAltDel();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateState: function(rfb, state, oldstate, msg) {
|
|
||||||
var s, sb, c, cad, klass;
|
|
||||||
s = $D('VNC_status');
|
|
||||||
sb = $D('VNC_status_bar');
|
|
||||||
c = $D('VNC_connect_button');
|
|
||||||
cad = $D('sendCtrlAltDelButton');
|
|
||||||
switch (state) {
|
|
||||||
case 'failed':
|
|
||||||
case 'fatal':
|
|
||||||
c.disabled = true;
|
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_error";
|
|
||||||
break;
|
|
||||||
case 'normal':
|
|
||||||
c.value = "Disconnect";
|
|
||||||
c.onclick = UI.disconnect;
|
|
||||||
c.disabled = false;
|
|
||||||
cad.disabled = false;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
|
||||||
case 'disconnected':
|
|
||||||
case 'loaded':
|
|
||||||
c.value = "Connect";
|
|
||||||
c.onclick = UI.connect;
|
|
||||||
|
|
||||||
c.disabled = false;
|
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(false, rfb);
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
|
||||||
case 'password':
|
|
||||||
c.value = "Send Password";
|
|
||||||
c.onclick = UI.setPassword;
|
|
||||||
|
|
||||||
c.disabled = false;
|
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_warn";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
c.disabled = true;
|
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_warn";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof(msg) !== 'undefined') {
|
|
||||||
s.setAttribute("class", klass);
|
|
||||||
sb.setAttribute("class", klass);
|
|
||||||
s.innerHTML = msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
clipReceive: function(rfb, text) {
|
|
||||||
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
|
|
||||||
$D('VNC_clipboard_text').value = text;
|
|
||||||
Util.Debug("<< UI.clipReceive");
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
connect: function() {
|
|
||||||
var host, port, password;
|
|
||||||
|
|
||||||
UI.closeSettingsMenu();
|
|
||||||
|
|
||||||
host = $D('VNC_host').value;
|
|
||||||
port = $D('VNC_port').value;
|
|
||||||
password = $D('VNC_password').value;
|
|
||||||
if ((!host) || (!port)) {
|
|
||||||
throw("Must set host and port");
|
|
||||||
}
|
|
||||||
|
|
||||||
UI.rfb.set_encrypt(UI.getSetting('encrypt'));
|
|
||||||
UI.rfb.set_true_color(UI.getSetting('true_color'));
|
|
||||||
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
|
|
||||||
UI.rfb.set_shared(UI.getSetting('shared'));
|
|
||||||
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
|
|
||||||
|
|
||||||
UI.rfb.connect(host, port, password);
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnect: function() {
|
|
||||||
UI.closeSettingsMenu();
|
|
||||||
|
|
||||||
UI.rfb.disconnect();
|
|
||||||
},
|
|
||||||
|
|
||||||
canvasBlur: function() {
|
|
||||||
UI.rfb.get_canvas().set_focused(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
canvasFocus: function() {
|
|
||||||
UI.rfb.get_canvas().set_focused(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
clipClear: function() {
|
|
||||||
$D('VNC_clipboard_text').value = "";
|
|
||||||
UI.rfb.clipboardPasteFrom("");
|
|
||||||
},
|
|
||||||
|
|
||||||
clipSend: function() {
|
|
||||||
var text = $D('VNC_clipboard_text').value;
|
|
||||||
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
|
|
||||||
UI.rfb.clipboardPasteFrom(text);
|
|
||||||
Util.Debug("<< UI.clipSend");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2010 Joel Martin
|
* Copyright (C) 2010 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2010 Joel Martin
|
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/*jslint evil: true */
|
|
||||||
/*global window, document, VNC_uri_prefix */
|
|
||||||
|
|
||||||
// Globals defined here
|
|
||||||
var VNC_native_ws, WEB_SOCKET_SWF_LOCATION;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Load supporting scripts
|
|
||||||
*/
|
|
||||||
function get_VNC_uri_prefix() {
|
|
||||||
return (typeof VNC_uri_prefix !== "undefined") ? VNC_uri_prefix : "include/";
|
|
||||||
}
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
var extra = "", start, end;
|
|
||||||
|
|
||||||
start = "<script src='" + get_VNC_uri_prefix();
|
|
||||||
end = "'><\/script>";
|
|
||||||
|
|
||||||
// Uncomment to activate firebug lite
|
|
||||||
//extra += "<script src='http://getfirebug.com/releases/lite/1.2/" +
|
|
||||||
// "firebug-lite-compressed.js'><\/script>";
|
|
||||||
|
|
||||||
extra += start + "util.js" + end;
|
|
||||||
extra += start + "webutil.js" + end;
|
|
||||||
extra += start + "base64.js" + end;
|
|
||||||
extra += start + "des.js" + end;
|
|
||||||
extra += start + "canvas.js" + end;
|
|
||||||
extra += start + "rfb.js" + end;
|
|
||||||
|
|
||||||
/* If no builtin websockets then load web_socket.js */
|
|
||||||
if (window.WebSocket) {
|
|
||||||
VNC_native_ws = true;
|
|
||||||
} else {
|
|
||||||
VNC_native_ws = false;
|
|
||||||
WEB_SOCKET_SWF_LOCATION = get_VNC_uri_prefix() +
|
|
||||||
"web-socket-js/WebSocketMain.swf";
|
|
||||||
extra += start + "web-socket-js/swfobject.js" + end;
|
|
||||||
extra += start + "web-socket-js/FABridge.js" + end;
|
|
||||||
extra += start + "web-socket-js/web_socket.js" + end;
|
|
||||||
}
|
|
||||||
document.write(extra);
|
|
||||||
}());
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2010 Joel Martin
|
* Copyright (C) 2010 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,25 +1,22 @@
|
||||||
TARGETS=wsproxy wswrapper.so rebind.so
|
TARGETS=websockify wswrapper.so
|
||||||
CFLAGS += -fPIC
|
CFLAGS += -fPIC
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
wsproxy: wsproxy.o websocket.o md5.o
|
websockify: websockify.o websocket.o md5.o
|
||||||
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
|
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
|
||||||
|
|
||||||
wswrapper.o: wswrapper.h
|
wswrapper.o: wswrapper.h
|
||||||
wswrapper.so: wswrapper.o md5.o
|
wswrapper.so: wswrapper.o md5.o
|
||||||
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@
|
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@
|
||||||
|
|
||||||
rebind.so: rebind.o
|
|
||||||
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@
|
|
||||||
|
|
||||||
websocket.o: websocket.c websocket.h md5.h
|
websocket.o: websocket.c websocket.h md5.h
|
||||||
wsproxy.o: wsproxy.c websocket.h
|
websockify.o: websockify.c websocket.h
|
||||||
wswrapper.o: wswrapper.c
|
wswrapper.o: wswrapper.c
|
||||||
$(CC) -c $(CFLAGS) -o $@ $*.c
|
$(CC) -c $(CFLAGS) -o $@ $*.c
|
||||||
md5.o: md5.c md5.h
|
md5.o: md5.c md5.h
|
||||||
$(CC) -c $(CFLAGS) -o $@ $*.c -DHAVE_MEMCPY -DSTDC_HEADERS
|
$(CC) -c $(CFLAGS) -o $@ $*.c -DHAVE_MEMCPY -DSTDC_HEADERS
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f wsproxy wswrapper.so *.o
|
rm -f websockify wswrapper.so *.o
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Canvas Performance Test</title>
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/canvas.js"></script>
|
|
||||||
<script src="face.png.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
Iterations: <input id='iterations' style='width:50' value="100">
|
|
||||||
|
|
||||||
Width: <input id='width' style='width:50' value="640">
|
|
||||||
Height: <input id='height' style='width:50' value="480">
|
|
||||||
|
|
||||||
<input id='startButton' type='button' value='Do Performance Test'
|
|
||||||
style='width:150px' onclick="begin();">
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<b>Canvas</b> (should see three squares and two happy faces):<br>
|
|
||||||
<canvas id="canvas" width="200" height="100"
|
|
||||||
style="border-style: dotted; border-width: 1px;">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
Results:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var msg_cnt = 0;
|
|
||||||
var start_width = 300, start_height = 100;
|
|
||||||
var iterations;
|
|
||||||
|
|
||||||
function message(str) {
|
|
||||||
console.log(str);
|
|
||||||
cell = $D('messages');
|
|
||||||
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
msg_cnt += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_functions () {
|
|
||||||
var img, x, y, w, h, ctx = canvas.getContext();
|
|
||||||
w = canvas.get_width();
|
|
||||||
h = canvas.get_height();
|
|
||||||
canvas.fillRect(0, 0, w, h, [240,240,240]);
|
|
||||||
|
|
||||||
canvas.blitStringImage("data:image/png;base64," + face64, 150, 10);
|
|
||||||
|
|
||||||
var himg = new Image();
|
|
||||||
himg.onload = function () {
|
|
||||||
ctx.drawImage(himg, 200, 40); };
|
|
||||||
himg.src = "face.png";
|
|
||||||
|
|
||||||
/* Test array image data */
|
|
||||||
data = [];
|
|
||||||
for (y=0; y< 50; y++) {
|
|
||||||
for (x=0; x< 50; x++) {
|
|
||||||
data[(y*50 + x)*4 + 0] = 255 - parseInt((255 / 50) * y, 10);
|
|
||||||
data[(y*50 + x)*4 + 1] = parseInt((255 / 50) * y, 10);
|
|
||||||
data[(y*50 + x)*4 + 2] = parseInt((255 / 50) * x, 10);
|
|
||||||
data[(y*50 + x)*4 + 3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
canvas.blitImage(30, 10, 50, 50, data, 0);
|
|
||||||
|
|
||||||
img = canvas.getTile(5,5,16,16,[0,128,128]);
|
|
||||||
canvas.putTile(img);
|
|
||||||
|
|
||||||
img = canvas.getTile(90,15,16,16,[0,0,0]);
|
|
||||||
canvas.setSubTile(img, 0,0,16,16,[128,128,0]);
|
|
||||||
canvas.putTile(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
function begin () {
|
|
||||||
$D('startButton').value = "Running";
|
|
||||||
$D('startButton').disabled = true;
|
|
||||||
setTimeout(start_delayed, 250);
|
|
||||||
iterations = $D('iterations').value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function start_delayed () {
|
|
||||||
var ret;
|
|
||||||
|
|
||||||
ret = canvas.set_prefer_js(true);
|
|
||||||
if (ret) {
|
|
||||||
message("Running test: prefer Javascript ops");
|
|
||||||
var time1 = run_test();
|
|
||||||
message("prefer Javascript ops: " + time1 + "ms total, " +
|
|
||||||
(time1 / iterations) + "ms per frame");
|
|
||||||
} else {
|
|
||||||
message("Could not run: prefer Javascript ops");
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.set_prefer_js(false);
|
|
||||||
message("Running test: prefer Canvas ops");
|
|
||||||
var time2 = run_test();
|
|
||||||
message("prefer Canvas ops: " + time2 + "ms total, " +
|
|
||||||
(time2 / iterations) + "ms per frame");
|
|
||||||
|
|
||||||
if (Util.get_logging() !== 'debug') {
|
|
||||||
canvas.resize(start_width, start_height, true);
|
|
||||||
test_functions();
|
|
||||||
}
|
|
||||||
$D('startButton').disabled = false;
|
|
||||||
$D('startButton').value = "Do Performance Test";
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_test () {
|
|
||||||
var width, height;
|
|
||||||
width = $D('width').value;
|
|
||||||
height = $D('height').value;
|
|
||||||
canvas.resize(width, height);
|
|
||||||
var color, start_time = (new Date()).getTime(), w, h;
|
|
||||||
for (var i=0; i < iterations; i++) {
|
|
||||||
color = [128, 128, (255 / iterations) * i, 0];
|
|
||||||
for (var x=0; x < width; x = x + 16) {
|
|
||||||
for (var y=0; y < height; y = y + 16) {
|
|
||||||
w = Math.min(16, width - x);
|
|
||||||
h = Math.min(16, height - y);
|
|
||||||
var tile = canvas.getTile(x, y, w, h, color);
|
|
||||||
canvas.setSubTile(tile, 0, 0, w, h, color);
|
|
||||||
canvas.putTile(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var end_time = (new Date()).getTime();
|
|
||||||
return (end_time - start_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
message("in onload");
|
|
||||||
$D('iterations').value = 10;
|
|
||||||
canvas = new Canvas({'target' : $D('canvas')});
|
|
||||||
canvas.resize(start_width, start_height, true);
|
|
||||||
message("Canvas initialized");
|
|
||||||
test_functions();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,135 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>Cursor Change test</title>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/canvas.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Roll over the buttons to test cursors</h1>
|
|
||||||
<br>
|
|
||||||
<input id=button1 type="button" value="Cursor from file (smiley face)">
|
|
||||||
<input id=button2 type="button" value="Data URI cursor (crosshair)">
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Debug:<br>
|
|
||||||
<textarea id="debug" style="font-size: 9px;" cols=80 rows=25></textarea>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<canvas id="testcanvas" width="100px" height="20px">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function debug(str) {
|
|
||||||
console.log(str);
|
|
||||||
cell = $D('debug');
|
|
||||||
cell.innerHTML += str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeCursor() {
|
|
||||||
var arr = [], x, y, w = 32, h = 32, hx = 16, hy = 16;
|
|
||||||
|
|
||||||
var IHDRsz = 40;
|
|
||||||
var ANDsz = w * h * 4;
|
|
||||||
var XORsz = Math.ceil( (w * h) / 8.0 );
|
|
||||||
|
|
||||||
// Push multi-byte little-endian values
|
|
||||||
arr.push16le = function (num) {
|
|
||||||
this.push((num ) & 0xFF,
|
|
||||||
(num >> 8) & 0xFF );
|
|
||||||
};
|
|
||||||
arr.push32le = function (num) {
|
|
||||||
this.push((num ) & 0xFF,
|
|
||||||
(num >> 8) & 0xFF,
|
|
||||||
(num >> 16) & 0xFF,
|
|
||||||
(num >> 24) & 0xFF );
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main header
|
|
||||||
arr.push16le(0); // Reserved
|
|
||||||
arr.push16le(2); // .CUR type
|
|
||||||
arr.push16le(1); // Number of images, 1 for non-animated arr
|
|
||||||
|
|
||||||
// Cursor #1
|
|
||||||
arr.push(w); // width
|
|
||||||
arr.push(h); // height
|
|
||||||
arr.push(0); // colors, 0 -> true-color
|
|
||||||
arr.push(0); // reserved
|
|
||||||
arr.push16le(hx); // hotspot x coordinate
|
|
||||||
arr.push16le(hy); // hotspot y coordinate
|
|
||||||
arr.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size
|
|
||||||
arr.push32le(22); // offset of cursor data in the file
|
|
||||||
|
|
||||||
// Infoheader for Cursor #1
|
|
||||||
arr.push32le(IHDRsz); // Infoheader size
|
|
||||||
arr.push32le(w); // Cursor width
|
|
||||||
arr.push32le(h*2); // XOR+AND height
|
|
||||||
arr.push16le(1); // number of planes
|
|
||||||
arr.push16le(32); // bits per pixel
|
|
||||||
arr.push32le(0); // type of compression
|
|
||||||
arr.push32le(XORsz + ANDsz); // Size of Image
|
|
||||||
arr.push32le(0);
|
|
||||||
arr.push32le(0);
|
|
||||||
arr.push32le(0);
|
|
||||||
arr.push32le(0);
|
|
||||||
|
|
||||||
// XOR/color data
|
|
||||||
for (y = h-1; y >= 0; y--) {
|
|
||||||
for (x = 0; x < w; x++) {
|
|
||||||
//if ((x === hx) || (y === (h-hy-1))) {
|
|
||||||
if ((x === hx) || (y === hy)) {
|
|
||||||
arr.push(0xe0); // blue
|
|
||||||
arr.push(0x00); // green
|
|
||||||
arr.push(0x00); // red
|
|
||||||
arr.push(0xff); // alpha
|
|
||||||
} else {
|
|
||||||
arr.push(0x05); // blue
|
|
||||||
arr.push(0xe6); // green
|
|
||||||
arr.push(0x00); // red
|
|
||||||
arr.push(0x80); // alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AND/bitmask data (seems to be ignored)
|
|
||||||
for (y = 0; y < h; y++) {
|
|
||||||
for (x = 0; x < Math.ceil(w / 8); x++) {
|
|
||||||
arr.push(0x00);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("cursor generated");
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
debug("onload");
|
|
||||||
var canvas, cross, cursor, cursor64;
|
|
||||||
|
|
||||||
canvas = new Canvas({'target' : $D("testcanvas")});
|
|
||||||
debug("canvas indicates Data URI cursor support is: " + canvas.get_cursor_uri());
|
|
||||||
|
|
||||||
$D('button1').style.cursor="url(face.png), default";
|
|
||||||
|
|
||||||
cursor = makeCursor();
|
|
||||||
cursor64 = Base64.encode(cursor);
|
|
||||||
//debug("cursor: " + cursor.slice(0,100) + " (" + cursor.length + ")");
|
|
||||||
//debug("cursor64: " + cursor64.slice(0,100) + " (" + cursor64.length + ")");
|
|
||||||
$D('button2').style.cursor="url(data:image/x-icon;base64," + cursor64 + "), default";
|
|
||||||
|
|
||||||
debug("onload complete");
|
|
||||||
}
|
|
||||||
</script>
|
|
BIN
tests/face.png
BIN
tests/face.png
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
|
@ -1 +0,0 @@
|
||||||
var face64 = 'iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAAAA3NCSVQICAjb4U/gAAAAGXRFWHRTb2Z0d2FyZQBnbm9tZS1zY3JlZW5zaG907wO/PgAACJJJREFUSIm1lltsXMUdxr8558zZq9d3OxebJDYhJLhNIAmUWyFKIBUtVaGqSgtUlIJKeahoEahgIZU+VC0oQiVVC60obckDgVIp3KRCQkmhhIhA4oY4wjg2ufmS9drec/bc5vbvw9prJwq85dP/YWfP7Pfb/8w3s8v6339l2fkrbMvGuZQ2mkUTA0bpc4qpyjrX3dTkAATQ5z0WUrqcAwjL/eXirmBqj0yKSTTBwNxMM0+15JuurG/dlClcOH/yWcVEaVBKUR3Eidizr2946Nhr/9q5b//BsudZzDLG5DK4sDt3443XrFm34bkX9x4ZPimkWNBa/+MfrB84+O7rbxz4+JPQD8liljY6n8t9uWfld2/++vp1F3ct6cikU2eSnvr7P7e99OqC9vaTJ0ccMtl8loyJ4igKwzAIK0GglersWv7sM08VCrk4joY/O/rLXz3mTYzmcnnXdZXWcRzHURwEQRCEHUuXdS/vnp4qP/CT2zdvuAKAQwCB4kRse+m1LY//Wojkscd/57opKUQUJ8wyzFaOq7OGGGPcdZ/f/sKbu3YT0YZrr3JT7pq1l3qeH4SBqgRETBljDKXSqXyh/i9PP/W/Q31btz59zVXrUpxb1dYsixUK+c7Fi59/YUdz2yInnbXcLHfTtpu23ZRlu4ZZiRBTp8Z37HjlhW1/evnFZ9/a+VZdLsecFOMpx83ydJanc24q67iuFOr48NC1G6+fKBY7zutIElFNBAC4nN99602XXLzutjvvETqAlcqktVQin0QiLsRxEAUBaRVUfBh1QfcigmzIuw0NTe2LOjNlL07iOArDwA88z0unGWNTk5P1dfkf3XH3BT2r9b23zZKIAHxr81f/uGpF/8G+Fau+VPbKp8ZHpqdKSRiEYWiMMVopJSuVyl+f3UpIQKL34btvvf2BxuZWN5Umo7TWFiNDDHCampob6utLpRKz7Hvv+E5jfR5ELCkNShFXOytOTH7vjrsOfXJ0wcLFF63sXr1mfXtbS6FQB4BZyGYzX7l0TWtrvWVpUGxUMFEa2bv3Q9+bNCaECX2/NFEc3bd/4r19/tR0uLC98c+/3/LVy9fWzhNq56m1pfEPvabnut2OI8EvBMAYAxhgAWz3u3tuvuWeRx/56aYNa0Hy3fc/euiRZx596IZvbF5Dpgw9CdMI0waqaMrEScPgvtdWXH5JzdzC7NElIPQH3GyTk+4ABCgCEpAkMgRGcLb/49WGxqYtTzwNaJDa/tJ7DU1tW558GaYCEwESYGAWwEidTOcWM8tElcGauTP/ivDGd7V3fxv6JGCBIpBDjIMxgIM5B/YfjMJwfGwEMIA40DcQhcn46DGAzX7p6gIwBhj5WUvH8vLYG+nu8+d6qimY2lPXup70GFEEE9baAhRIj5w8cfz4MSESkJw3FLOfnrvSCETqs3xTd2Vyd+1Na/4MmRRt3gBTgfGJKkQhTAQTwgQgv2tpR8X3Vq5YCiiC7lrSXPG9lRe0AmZ2hQxo5jXpspNqEElxPmlOIi5ZThYUgBKYKRgPxgMFMAGM/+D9P2xuLPQ+dBcoAYkHf/bN5sZM74M3gHS1acBUi0gZ4zk8J5NyzdzBGSIJkoANCqsrwgBAg+zN1605Mfw6IIkiUHL9xouODzwBE4ACkKrGBNBkBEgSKSIz39gxRkuRVAduulHLCZtZoARkzybTAFU2m7GjBBSDkmoRJYCc3U5lSBgjAFeJae4Wauan9WSnWlU0aqdtUAXElAicVDNIgfHZaJkZU0pAESgmCJAACUCApJIBKCITg+VVMuWm2+btEwFE1coVLvOKe2HVE8UwUd/OXi0nQZXZ8kH+7HIFoIgoqvKqzWkV9L2zy5jQ6Ig5nX5pOFd/Vc3cmv9zW9eyYfzITmY1giKiMJNtCiYPw1RgPBh/psiHqcAEZAJQBFMlxaDEnyqmc3mjY2NCiy+bHB3Kt2w8I+UzxTPLlAzjygCz6kFBx6qNg/ue84p9M7AZRoWoQhSAqumfacsrnRg6uH9Rd4/RFWafl1RGjLJ5ZknNnIXjh+PQB0BEQkqv9L4sb1t59cMU74GVKxcnhg5sdzN1jQtX5grtqVyj46ZtywIJrUOZeCKYCLxTU+PHkzhZ2vO1XH5MRIfcwvcHP9qRafp5XfN6l3PGGIA5ktJaJEJINXnkvmWrNza0rSBxEFYbnE6veGRq9IPQO54Ep5QItRYAs22Hu1k315QtdDYsuCzf1KHDt0XlbTu3ySuVRo6MNnc/6XLHTbmObc+QotAHIJUSQiSJTKLR4Nh9Pdc+kM44JA+D5RhfBud8ZjeD5WHVMVYHqwAYmGkyUyRPqPDfMnhTxcNW+jKpGj/94NX8eVtTmYWpFHddlzsOABaOzZGkkImQUsrI/1iVfrPq6vszuSyJD0EasGEVmN0KlgXLgYGMT6qkkwEthrQuG53Y2U0icT79YIfb2pup6+Gcp1zOXV4j9VdJxhghpJBSSCmEjL0+XXqsa+0tTYvWQ/aTHJrZW9JEkowwJjYmMjo0OmR8uZ1eNz12+Nih/zgtv0gXVrsur1Jcl1uWNUsK/GoQldZSSCGllEpIGYcndOm36Vyqa/VNmboFRh4ldZR02ZhpMhJwCGnmLGZ8SewXj/bvTkLDW3pT2UUu55w7Lufc5dVNAsCCsf4o8Gqpr8KkUlIqpZRUKim/Y/y/pVLZ1s5V+Zbl3C3Ybp5Iq2RKxhP+xFBxZFAmwi7cmaq/kjuO4zicO9xx5mPOQqrGvYZRWmulldYqGlLBf3X8EfQkSR8A43WMN1nuWid3hZPpcmzbdmzHtmuwarjnkw5FldNIczyljDZKa62NNpoM1QSA1WQx27Jt23Js27It7pzJmLthz/7/nzHOOThcImPoNBIIAMNpJMtiNcBZDZ3PfVIjgtkWsy3riyZ9AaFGMlozhuqCnDsxxv4PC7uS+QV5eeoAAAAASUVORK5CYII=';
|
|
|
@ -1,62 +0,0 @@
|
||||||
<html>
|
|
||||||
<head><title>Input Test</title></head>
|
|
||||||
<body>
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
Canvas:<br>
|
|
||||||
<canvas id="canvas" width="640" height="20"
|
|
||||||
style="border-style: dotted; border-width: 1px;">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
Results:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/canvas.js"></script>
|
|
||||||
<script>
|
|
||||||
var msg_cnt = 0;
|
|
||||||
var width = 400, height = 200;
|
|
||||||
var iterations;
|
|
||||||
|
|
||||||
function message(str) {
|
|
||||||
console.log(str);
|
|
||||||
cell = $D('messages');
|
|
||||||
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseButton(x, y, down, bmask) {
|
|
||||||
msg = 'mouse x,y: ' + x + ',' + y + ' down: ' + down;
|
|
||||||
msg += ' bmask: ' + bmask;
|
|
||||||
console.log(msg);
|
|
||||||
message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseMove(x, y) {
|
|
||||||
msg = 'mouse x,y: ' + x + ',' + y;
|
|
||||||
//console.log(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyPress(keysym, down) {
|
|
||||||
msg = "keyPress keysym: " + keysym + " down: " + down;
|
|
||||||
console.log(msg);
|
|
||||||
message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
var canvas = new Canvas({'target' : $D('canvas')});
|
|
||||||
canvas.resize(width, height, true);
|
|
||||||
canvas.start(keyPress, mouseButton, mouseMove);
|
|
||||||
message("Canvas initialized");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,67 +0,0 @@
|
||||||
<html>
|
|
||||||
<head><title>Input Test</title></head>
|
|
||||||
<body>
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
Canvas:<br>
|
|
||||||
<canvas id="canvas" width="640" height="20"
|
|
||||||
style="border-style: dotted; border-width: 1px;">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
Results:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/canvas.js"></script>
|
|
||||||
<script>
|
|
||||||
var msg_cnt = 0;
|
|
||||||
var width = 400, height = 200;
|
|
||||||
var canvas;
|
|
||||||
|
|
||||||
function message(str) {
|
|
||||||
console.log(str);
|
|
||||||
msg_cnt++;
|
|
||||||
cell = $D('messages');
|
|
||||||
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyDown(evt) {
|
|
||||||
var e = (evt ? evt : window.event);
|
|
||||||
msg = "Dn: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
|
||||||
message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyUp(evt) {
|
|
||||||
var e = (evt ? evt : window.event);
|
|
||||||
msg = "Up: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
|
||||||
message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyPress(evt) {
|
|
||||||
var e = (evt ? evt : window.event);
|
|
||||||
msg = "Pr: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
|
||||||
message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
var c = $D('canvas');
|
|
||||||
canvas = new Canvas({'target' : c});
|
|
||||||
canvas.resize(width, height, true);
|
|
||||||
//canvas.start(keyPress);
|
|
||||||
Util.addEvent(document, 'keydown', keyDown);
|
|
||||||
Util.addEvent(document, 'keyup', keyUp);
|
|
||||||
Util.addEvent(document, 'keypress', keyPress);
|
|
||||||
message("Canvas initialized");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,197 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>VNC Performance Benchmark</title>
|
|
||||||
<link rel="stylesheet" href="include/plain.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
Passes: <input id='passes' style='width:50' value=3>
|
|
||||||
|
|
||||||
<input id='startButton' type='button' value='Start' style='width:100px'
|
|
||||||
onclick="start();" disabled>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
Results:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=15></textarea>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<div id="VNC_screen">
|
|
||||||
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
|
|
||||||
<table border=0 width=100%><tr>
|
|
||||||
<td><div id="VNC_status">Loading</div></td>
|
|
||||||
</tr></table>
|
|
||||||
</div>
|
|
||||||
<canvas id="VNC_canvas" width="640px" height="20px">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script src="include/vnc.js"></script>
|
|
||||||
<script src="include/playback.js"></script>
|
|
||||||
<script src="data_multi.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var start_time, VNC_frame_data, pass, passes, encIdx,
|
|
||||||
encOrder = ['raw', 'rre', 'hextile', 'tightpng', 'copyrect'],
|
|
||||||
encTot = {}, encMin = {}, encMax = {},
|
|
||||||
passCur, passTot, passMin, passMax;
|
|
||||||
|
|
||||||
function msg(str) {
|
|
||||||
console.log(str);
|
|
||||||
var cell = $D('messages');
|
|
||||||
cell.innerHTML += str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
function dbgmsg(str) {
|
|
||||||
if (Util.get_logging() === 'debug') {
|
|
||||||
msg(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateState = function (rfb, state, oldstate, mesg) {
|
|
||||||
switch (state) {
|
|
||||||
case 'failed':
|
|
||||||
case 'fatal':
|
|
||||||
msg("noVNC sent '" + state +
|
|
||||||
"' state during pass " + pass +
|
|
||||||
", iteration " + iteration +
|
|
||||||
" frame " + frame_idx);
|
|
||||||
test_state = 'failed';
|
|
||||||
break;
|
|
||||||
case 'loaded':
|
|
||||||
$D('startButton').disabled = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (typeof mesg !== 'undefined') {
|
|
||||||
$D('VNC_status').innerHTML = mesg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
$D('startButton').value = "Running";
|
|
||||||
$D('startButton').disabled = true;
|
|
||||||
|
|
||||||
mode = 'perftest'; // full-speed
|
|
||||||
passes = $D('passes').value;
|
|
||||||
pass = 1;
|
|
||||||
encIdx = 0;
|
|
||||||
|
|
||||||
// Render each encoding once for each pass
|
|
||||||
iterations = 1;
|
|
||||||
|
|
||||||
// Initialize stats counters
|
|
||||||
for (i = 0; i < encOrder.length; i++) {
|
|
||||||
enc = encOrder[i];
|
|
||||||
encTot[i] = 0;
|
|
||||||
encMin[i] = 2<<23; // Something sufficiently large
|
|
||||||
encMax[i] = 0;
|
|
||||||
}
|
|
||||||
passCur = 0;
|
|
||||||
passTot = 0;
|
|
||||||
passMin = 2<<23;
|
|
||||||
passMax = 0;
|
|
||||||
|
|
||||||
// Fire away
|
|
||||||
next_encoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
function next_encoding() {
|
|
||||||
var encName;
|
|
||||||
|
|
||||||
if (encIdx >= encOrder.length) {
|
|
||||||
// Accumulate pass stats
|
|
||||||
if (passCur < passMin) {
|
|
||||||
passMin = passCur;
|
|
||||||
}
|
|
||||||
if (passCur > passMax) {
|
|
||||||
passMax = passCur;
|
|
||||||
}
|
|
||||||
msg("Pass " + pass + " took " + passCur + " ms");
|
|
||||||
|
|
||||||
passCur = 0;
|
|
||||||
encIdx = 0;
|
|
||||||
pass += 1;
|
|
||||||
if (pass > passes) {
|
|
||||||
// We are finished
|
|
||||||
rfb.get_canvas().stop(); // Shut-off event interception
|
|
||||||
$D('startButton').disabled = false;
|
|
||||||
$D('startButton').value = "Start";
|
|
||||||
finish_passes();
|
|
||||||
return; // We are finished, terminate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encName = encOrder[encIdx];
|
|
||||||
dbgmsg("Rendering pass " + pass + " encoding '" + encName + "'");
|
|
||||||
|
|
||||||
VNC_frame_data = VNC_frame_data_multi[encName];
|
|
||||||
iteration = 0;
|
|
||||||
start_time = (new Date()).getTime();
|
|
||||||
|
|
||||||
next_iteration();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished rendering current encoding
|
|
||||||
function finish() {
|
|
||||||
var total_time, end_time = (new Date()).getTime();
|
|
||||||
total_time = end_time - start_time;
|
|
||||||
|
|
||||||
dbgmsg("Encoding " + encOrder[encIdx] + " took " + total_time + "ms");
|
|
||||||
|
|
||||||
passCur += total_time;
|
|
||||||
passTot += total_time;
|
|
||||||
|
|
||||||
// Accumulate stats
|
|
||||||
encTot[encIdx] += total_time;
|
|
||||||
if (total_time < encMin[encIdx]) {
|
|
||||||
encMin[encIdx] = total_time;
|
|
||||||
}
|
|
||||||
if (total_time > encMax[encIdx]) {
|
|
||||||
encMax[encIdx] = total_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
encIdx += 1;
|
|
||||||
next_encoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish_passes() {
|
|
||||||
var i, enc, avg, passAvg;
|
|
||||||
msg("STATS (for " + passes + " passes)");
|
|
||||||
// Encoding stats
|
|
||||||
for (i = 0; i < encOrder.length; i++) {
|
|
||||||
enc = encOrder[i];
|
|
||||||
avg = (encTot[i] / passes).toFixed(1);
|
|
||||||
msg(" " + enc + ": " + encTot[i] + " ms, " +
|
|
||||||
encMin[i] + "/" + avg + "/" + encMax[i] +
|
|
||||||
" (min/avg/max)");
|
|
||||||
|
|
||||||
}
|
|
||||||
// Print pass stats
|
|
||||||
passAvg = (passTot / passes).toFixed(1);
|
|
||||||
msg("\n All passes: " + passTot + " ms, " +
|
|
||||||
passMin + "/" + passAvg + "/" + passMax +
|
|
||||||
" (min/avg/max)");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
var i, enc;
|
|
||||||
dbgmsg("Frame lengths:");
|
|
||||||
for (i = 0; i < encOrder.length; i++) {
|
|
||||||
enc = encOrder[i];
|
|
||||||
dbgmsg(" " + enc + ": " + VNC_frame_data_multi[enc].length);
|
|
||||||
}
|
|
||||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
|
||||||
'updateState': updateState});
|
|
||||||
rfb.testMode(send_array);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,128 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>VNC Playback</title>
|
|
||||||
<link rel="stylesheet" href="include/plain.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
Iterations: <input id='iterations' style='width:50'>
|
|
||||||
Perftest:<input type='radio' id='mode1' name='mode' checked>
|
|
||||||
Realtime:<input type='radio' id='mode2' name='mode'>
|
|
||||||
|
|
||||||
<input id='startButton' type='button' value='Start' style='width:100px'
|
|
||||||
onclick="start();" disabled>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
Results:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<div id="VNC_screen">
|
|
||||||
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
|
|
||||||
<table border=0 width=100%><tr>
|
|
||||||
<td><div id="VNC_status">Loading</div></td>
|
|
||||||
</tr></table>
|
|
||||||
</div>
|
|
||||||
<canvas id="VNC_canvas" width="640px" height="20px">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script src="include/vnc.js"></script>
|
|
||||||
<script src="include/playback.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var fname, start_time;
|
|
||||||
|
|
||||||
function message(str) {
|
|
||||||
console.log(str);
|
|
||||||
var cell = $D('messages');
|
|
||||||
cell.innerHTML += str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
fname = WebUtil.getQueryVar('data', null);
|
|
||||||
|
|
||||||
if (fname) {
|
|
||||||
message("Loading " + fname);
|
|
||||||
document.write('<script src="' + fname + '"><\/script>');
|
|
||||||
} else {
|
|
||||||
message("Must specify data=FOO in query string.");
|
|
||||||
}
|
|
||||||
|
|
||||||
updateState = function (rfb, state, oldstate, msg) {
|
|
||||||
switch (state) {
|
|
||||||
case 'failed':
|
|
||||||
case 'fatal':
|
|
||||||
message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx);
|
|
||||||
test_state = 'failed';
|
|
||||||
break;
|
|
||||||
case 'loaded':
|
|
||||||
$D('startButton').disabled = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (typeof msg !== 'undefined') {
|
|
||||||
$D('VNC_status').innerHTML = msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
$D('startButton').value = "Running";
|
|
||||||
$D('startButton').disabled = true;
|
|
||||||
|
|
||||||
iterations = $D('iterations').value;
|
|
||||||
iteration = 0;
|
|
||||||
start_time = (new Date()).getTime();
|
|
||||||
|
|
||||||
if ($D('mode1').checked) {
|
|
||||||
message("Starting performance playback (fullspeed) [" + iterations + " iteration(s)]");
|
|
||||||
mode = 'perftest';
|
|
||||||
} else {
|
|
||||||
message("Starting realtime playback [" + iterations + " iteration(s)]");
|
|
||||||
mode = 'realtime';
|
|
||||||
}
|
|
||||||
|
|
||||||
next_iteration();
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish() {
|
|
||||||
// Finished with all iterations
|
|
||||||
var total_time, end_time = (new Date()).getTime();
|
|
||||||
total_time = end_time - start_time;
|
|
||||||
|
|
||||||
iter_time = parseInt(total_time / iterations, 10);
|
|
||||||
message(iterations + " iterations took " + total_time + "ms, " +
|
|
||||||
iter_time + "ms per iteration");
|
|
||||||
rfb.get_canvas().stop(); // Shut-off event interception
|
|
||||||
$D('startButton').disabled = false;
|
|
||||||
$D('startButton').value = "Start";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
iterations = WebUtil.getQueryVar('iterations', 3);
|
|
||||||
$D('iterations').value = iterations;
|
|
||||||
mode = WebUtil.getQueryVar('mode', 3);
|
|
||||||
if (mode === 'realtime') {
|
|
||||||
$D('mode2').checked = true;
|
|
||||||
} else {
|
|
||||||
$D('mode1').checked = true;
|
|
||||||
}
|
|
||||||
if (fname) {
|
|
||||||
message("VNC_frame_data.length: " + VNC_frame_data.length);
|
|
||||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
|
||||||
'updateState': updateState});
|
|
||||||
rfb.testMode(send_array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1 +0,0 @@
|
||||||
../utils/wsecho.html
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>WebSockets Echo Test</title>
|
||||||
|
<script src="include/base64.js"></script>
|
||||||
|
<script src="include/util.js"></script>
|
||||||
|
<script src="include/webutil.js"></script>
|
||||||
|
<!-- Uncomment to activate firebug lite -->
|
||||||
|
<!--
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Host: <input id='host' style='width:100'>
|
||||||
|
Port: <input id='port' style='width:50'>
|
||||||
|
Encrypt: <input id='encrypt' type='checkbox'>
|
||||||
|
<input id='connectButton' type='button' value='Start' style='width:100px'
|
||||||
|
onclick="connect();">
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
Log:<br>
|
||||||
|
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var ws, host = null, port = null,
|
||||||
|
msg_cnt = 0, send_cnt = 1, echoDelay = 500,
|
||||||
|
echo_ref;
|
||||||
|
|
||||||
|
function message(str) {
|
||||||
|
console.log(str);
|
||||||
|
cell = $D('messages');
|
||||||
|
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
||||||
|
cell.scrollTop = cell.scrollHeight;
|
||||||
|
msg_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.pushStr = function (str) {
|
||||||
|
var n = str.length;
|
||||||
|
for (var i=0; i < n; i++) {
|
||||||
|
this.push(str.charCodeAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_msg() {
|
||||||
|
if (ws.bufferedAmount > 0) {
|
||||||
|
console.log("Delaying send");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var str = "Message #" + send_cnt, arr = [];
|
||||||
|
arr.pushStr(str)
|
||||||
|
ws.send(Base64.encode(arr));
|
||||||
|
message("Sent message: '" + str + "'");
|
||||||
|
send_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_stats() {
|
||||||
|
$D('sent').innerHTML = sent;
|
||||||
|
$D('received').innerHTML = received;
|
||||||
|
$D('errors').innerHTML = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function init_ws() {
|
||||||
|
console.log(">> init_ws");
|
||||||
|
console.log("<< init_ws");
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
var host = $D('host').value,
|
||||||
|
port = $D('port').value,
|
||||||
|
scheme = "ws://", uri;
|
||||||
|
|
||||||
|
console.log(">> connect");
|
||||||
|
if ((!host) || (!port)) {
|
||||||
|
console.log("must set host and port");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($D('encrypt').checked) {
|
||||||
|
scheme = "wss://";
|
||||||
|
}
|
||||||
|
uri = scheme + host + ":" + port;
|
||||||
|
message("connecting to " + uri);
|
||||||
|
ws = new WebSocket(uri);
|
||||||
|
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
//console.log(">> WebSockets.onmessage");
|
||||||
|
var arr = Base64.decode(e.data), str = "", i;
|
||||||
|
|
||||||
|
for (i = 0; i < arr.length; i++) {
|
||||||
|
str = str + String.fromCharCode(arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
message("Received message '" + str + "'");
|
||||||
|
//console.log("<< WebSockets.onmessage");
|
||||||
|
};
|
||||||
|
ws.onopen = function(e) {
|
||||||
|
console.log(">> WebSockets.onopen");
|
||||||
|
echo_ref = setInterval(send_msg, echoDelay);
|
||||||
|
console.log("<< WebSockets.onopen");
|
||||||
|
};
|
||||||
|
ws.onclose = function(e) {
|
||||||
|
console.log(">> WebSockets.onclose");
|
||||||
|
if (echo_ref) {
|
||||||
|
clearInterval(echo_ref);
|
||||||
|
echo_ref = null;
|
||||||
|
}
|
||||||
|
console.log("<< WebSockets.onclose");
|
||||||
|
};
|
||||||
|
ws.onerror = function(e) {
|
||||||
|
console.log(">> WebSockets.onerror");
|
||||||
|
if (echo_ref) {
|
||||||
|
clearInterval(echo_ref);
|
||||||
|
echo_ref = null;
|
||||||
|
}
|
||||||
|
console.log("<< WebSockets.onerror");
|
||||||
|
};
|
||||||
|
|
||||||
|
$D('connectButton').value = "Stop";
|
||||||
|
$D('connectButton').onclick = disconnect;
|
||||||
|
console.log("<< connect");
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
console.log(">> disconnect");
|
||||||
|
if (ws) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (echo_ref) {
|
||||||
|
clearInterval(echo_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
$D('connectButton').value = "Start";
|
||||||
|
$D('connectButton').onclick = connect;
|
||||||
|
console.log("<< disconnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If no builtin websockets then load web_socket.js */
|
||||||
|
if (window.WebSocket) {
|
||||||
|
VNC_native_ws = true;
|
||||||
|
} else {
|
||||||
|
VNC_native_ws = false;
|
||||||
|
console.log("Loading web-socket-js flash bridge");
|
||||||
|
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
|
||||||
|
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
|
||||||
|
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
|
||||||
|
document.write(extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
console.log("onload");
|
||||||
|
if (!VNC_native_ws) {
|
||||||
|
console.log("initializing web-socket-js flash bridge");
|
||||||
|
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
|
||||||
|
WebSocket.__initialize();
|
||||||
|
}
|
||||||
|
var url = document.location.href;
|
||||||
|
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
||||||
|
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -11,7 +11,8 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, socket, select
|
import os, sys, socket, select
|
||||||
|
sys.path.insert(0,os.path.dirname(__file__) + "/../")
|
||||||
from websocket import WebSocketServer
|
from websocket import WebSocketServer
|
||||||
|
|
||||||
class WebSocketEcho(WebSocketServer):
|
class WebSocketEcho(WebSocketServer):
|
|
@ -12,7 +12,7 @@ from base64 import b64encode, b64decode
|
||||||
from codecs import utf_8_encode, utf_8_decode
|
from codecs import utf_8_encode, utf_8_decode
|
||||||
from select import select
|
from select import select
|
||||||
|
|
||||||
sys.path.insert(0,os.path.dirname(__file__) + "/../utils/")
|
sys.path.insert(0,os.path.dirname(__file__) + "/../")
|
||||||
from websocket import *
|
from websocket import *
|
||||||
|
|
||||||
buffer_size = 65536
|
buffer_size = 65536
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../utils/wstest.html
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>WebSockets Test</title>
|
||||||
|
<script src="include/base64.js"></script>
|
||||||
|
<script src="include/util.js"></script>
|
||||||
|
<script src="include/webutil.js"></script>
|
||||||
|
<!-- Uncomment to activate firebug lite -->
|
||||||
|
<!--
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Host: <input id='host' style='width:100'>
|
||||||
|
Port: <input id='port' style='width:50'>
|
||||||
|
Encrypt: <input id='encrypt' type='checkbox'>
|
||||||
|
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">
|
||||||
|
<input id='connectButton' type='button' value='Start' style='width:100px'
|
||||||
|
onclick="connect();">
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
<table border=1>
|
||||||
|
<tr>
|
||||||
|
<th align="right">Packets sent:</th>
|
||||||
|
<td align="right"><div id='sent'>0</div></td>
|
||||||
|
</tr><tr>
|
||||||
|
<th align="right">Good Packets Received:</th>
|
||||||
|
<td align="right"><div id='received'>0</div></td>
|
||||||
|
</tr><tr>
|
||||||
|
<th align="right">Errors (Bad Packets Received:)</th>
|
||||||
|
<td align="right"><div id='errors'>0</div></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
Errors:<br>
|
||||||
|
<textarea id="error" style="font-size: 9;" cols=80 rows=25></textarea>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function error(str) {
|
||||||
|
console.error(str);
|
||||||
|
cell = $D('error');
|
||||||
|
cell.innerHTML += errors + ": " + str + "\n";
|
||||||
|
cell.scrollTop = cell.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
var host = null, port = null, sendDelay = 0;
|
||||||
|
var ws = null, update_ref = null, send_ref = null;
|
||||||
|
var sent = 0, received = 0, errors = 0;
|
||||||
|
var max_send = 2000;
|
||||||
|
var recv_seq = 0, send_seq = 0;
|
||||||
|
|
||||||
|
Array.prototype.pushStr = function (str) {
|
||||||
|
var n = str.length;
|
||||||
|
for (var i=0; i < n; i++) {
|
||||||
|
this.push(str.charCodeAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function add (x,y) {
|
||||||
|
return parseInt(x,10)+parseInt(y,10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_respond(data) {
|
||||||
|
//console.log(">> check_respond");
|
||||||
|
var decoded, first, last, str, length, chksum, nums, arr;
|
||||||
|
decoded = Base64.decode(data);
|
||||||
|
first = String.fromCharCode(decoded.shift());
|
||||||
|
last = String.fromCharCode(decoded.pop());
|
||||||
|
|
||||||
|
if (first != "^") {
|
||||||
|
errors++;
|
||||||
|
error("Packet missing start char '^'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (last != "$") {
|
||||||
|
errors++;
|
||||||
|
error("Packet missing end char '$'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
arr = decoded.map(function(num) {
|
||||||
|
return String.fromCharCode(num);
|
||||||
|
} ).join('').split(':');
|
||||||
|
seq = arr[0];
|
||||||
|
length = arr[1];
|
||||||
|
chksum = arr[2];
|
||||||
|
nums = arr[3];
|
||||||
|
|
||||||
|
//console.log(" length:" + length + " chksum:" + chksum + " nums:" + nums);
|
||||||
|
if (seq != recv_seq) {
|
||||||
|
errors++;
|
||||||
|
error("Expected seq " + recv_seq + " but got " + seq);
|
||||||
|
recv_seq = parseInt(seq,10) + 1; // Back on track
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
recv_seq++;
|
||||||
|
if (nums.length != length) {
|
||||||
|
errors++;
|
||||||
|
error("Expected length " + length + " but got " + nums.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//real_chksum = nums.reduce(add);
|
||||||
|
real_chksum = 0;
|
||||||
|
for (var i=0; i < nums.length; i++) {
|
||||||
|
real_chksum += parseInt(nums.charAt(i), 10);
|
||||||
|
}
|
||||||
|
if (real_chksum != chksum) {
|
||||||
|
errors++
|
||||||
|
error("Expected chksum " + chksum + " but real chksum is " + real_chksum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
received++;
|
||||||
|
//console.log(" Packet checks out: length:" + length + " chksum:" + chksum);
|
||||||
|
//console.log("<< check_respond");
|
||||||
|
}
|
||||||
|
|
||||||
|
function send() {
|
||||||
|
if (ws.bufferedAmount > 0) {
|
||||||
|
console.log("Delaying send");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var length = Math.floor(Math.random()*(max_send-9)) + 10; // 10 - max_send
|
||||||
|
var numlist = [], arr = [];
|
||||||
|
for (var i=0; i < length; i++) {
|
||||||
|
numlist.push( Math.floor(Math.random()*10) );
|
||||||
|
}
|
||||||
|
//chksum = numlist.reduce(add);
|
||||||
|
chksum = 0;
|
||||||
|
for (var i=0; i < numlist.length; i++) {
|
||||||
|
chksum += parseInt(numlist[i], 10);
|
||||||
|
}
|
||||||
|
var nums = numlist.join('');
|
||||||
|
arr.pushStr("^" + send_seq + ":" + length + ":" + chksum + ":" + nums + "$")
|
||||||
|
send_seq ++;
|
||||||
|
ws.send(Base64.encode(arr));
|
||||||
|
sent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_stats() {
|
||||||
|
$D('sent').innerHTML = sent;
|
||||||
|
$D('received').innerHTML = received;
|
||||||
|
$D('errors').innerHTML = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function init_ws() {
|
||||||
|
console.log(">> init_ws");
|
||||||
|
var scheme = "ws://";
|
||||||
|
if ($D('encrypt').checked) {
|
||||||
|
scheme = "wss://";
|
||||||
|
}
|
||||||
|
var uri = scheme + host + ":" + port;
|
||||||
|
console.log("connecting to " + uri);
|
||||||
|
ws = new WebSocket(uri);
|
||||||
|
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
//console.log(">> WebSockets.onmessage");
|
||||||
|
check_respond(e.data);
|
||||||
|
//console.log("<< WebSockets.onmessage");
|
||||||
|
};
|
||||||
|
ws.onopen = function(e) {
|
||||||
|
console.log(">> WebSockets.onopen");
|
||||||
|
send_ref = setInterval(send, sendDelay);
|
||||||
|
console.log("<< WebSockets.onopen");
|
||||||
|
};
|
||||||
|
ws.onclose = function(e) {
|
||||||
|
console.log(">> WebSockets.onclose");
|
||||||
|
clearInterval(send_ref);
|
||||||
|
console.log("<< WebSockets.onclose");
|
||||||
|
};
|
||||||
|
ws.onerror = function(e) {
|
||||||
|
console.log(">> WebSockets.onerror");
|
||||||
|
console.log(" " + e);
|
||||||
|
console.log("<< WebSockets.onerror");
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("<< init_ws");
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
console.log(">> connect");
|
||||||
|
host = $D('host').value;
|
||||||
|
port = $D('port').value;
|
||||||
|
sendDelay = parseInt($D('sendDelay').value, 10);
|
||||||
|
if ((!host) || (!port)) {
|
||||||
|
console.log("must set host and port");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
init_ws();
|
||||||
|
update_ref = setInterval(update_stats, 1);
|
||||||
|
|
||||||
|
$D('connectButton').value = "Stop";
|
||||||
|
$D('connectButton').onclick = disconnect;
|
||||||
|
console.log("<< connect");
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
console.log(">> disconnect");
|
||||||
|
if (ws) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInterval(update_ref);
|
||||||
|
update_stats(); // Final numbers
|
||||||
|
recv_seq = 0;
|
||||||
|
send_seq = 0;
|
||||||
|
|
||||||
|
$D('connectButton').value = "Start";
|
||||||
|
$D('connectButton').onclick = connect;
|
||||||
|
console.log("<< disconnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If no builtin websockets then load web_socket.js */
|
||||||
|
if (window.WebSocket) {
|
||||||
|
VNC_native_ws = true;
|
||||||
|
} else {
|
||||||
|
VNC_native_ws = false;
|
||||||
|
console.log("Loading web-socket-js flash bridge");
|
||||||
|
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
|
||||||
|
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
|
||||||
|
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
|
||||||
|
document.write(extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
console.log("onload");
|
||||||
|
if (!VNC_native_ws) {
|
||||||
|
console.log("initializing web-socket-js flash bridge");
|
||||||
|
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
|
||||||
|
WebSocket.__initialize();
|
||||||
|
}
|
||||||
|
var url = document.location.href;
|
||||||
|
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
||||||
|
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1 +0,0 @@
|
||||||
../utils/wstest.py
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
'''
|
||||||
|
WebSocket server-side load test program. Sends and receives traffic
|
||||||
|
that has a random payload (length and content) that is checksummed and
|
||||||
|
given a sequence number. Any errors are reported and counted.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sys, os, socket, ssl, time, traceback
|
||||||
|
import random, time
|
||||||
|
from select import select
|
||||||
|
|
||||||
|
sys.path.insert(0,os.path.dirname(__file__) + "/../")
|
||||||
|
from websocket import WebSocketServer
|
||||||
|
|
||||||
|
|
||||||
|
class WebSocketTest(WebSocketServer):
|
||||||
|
|
||||||
|
buffer_size = 65536
|
||||||
|
max_packet_size = 10000
|
||||||
|
recv_cnt = 0
|
||||||
|
send_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.errors = 0
|
||||||
|
self.delay = kwargs.pop('delay')
|
||||||
|
|
||||||
|
print "Prepopulating random array"
|
||||||
|
self.rand_array = []
|
||||||
|
for i in range(0, self.max_packet_size):
|
||||||
|
self.rand_array.append(random.randint(0, 9))
|
||||||
|
|
||||||
|
WebSocketServer.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def new_client(self, client):
|
||||||
|
self.send_cnt = 0
|
||||||
|
self.recv_cnt = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.responder(client)
|
||||||
|
except:
|
||||||
|
print "accumulated errors:", self.errors
|
||||||
|
self.errors = 0
|
||||||
|
raise
|
||||||
|
|
||||||
|
def responder(self, client):
|
||||||
|
cqueue = []
|
||||||
|
cpartial = ""
|
||||||
|
socks = [client]
|
||||||
|
last_send = time.time() * 1000
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ins, outs, excepts = select(socks, socks, socks, 1)
|
||||||
|
if excepts: raise Exception("Socket exception")
|
||||||
|
|
||||||
|
if client in ins:
|
||||||
|
buf = client.recv(self.buffer_size)
|
||||||
|
if len(buf) == 0:
|
||||||
|
raise self.EClose("Client closed")
|
||||||
|
#print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf))
|
||||||
|
if buf[-1] == '\xff':
|
||||||
|
if cpartial:
|
||||||
|
err = self.check(cpartial + buf)
|
||||||
|
cpartial = ""
|
||||||
|
else:
|
||||||
|
err = self.check(buf)
|
||||||
|
if err:
|
||||||
|
self.traffic("}")
|
||||||
|
self.errors = self.errors + 1
|
||||||
|
print err
|
||||||
|
else:
|
||||||
|
self.traffic(">")
|
||||||
|
else:
|
||||||
|
self.traffic(".>")
|
||||||
|
cpartial = cpartial + buf
|
||||||
|
|
||||||
|
now = time.time() * 1000
|
||||||
|
if client in outs and now > (last_send + self.delay):
|
||||||
|
last_send = now
|
||||||
|
#print "Client send: %s" % repr(cqueue[0])
|
||||||
|
client.send(self.generate())
|
||||||
|
self.traffic("<")
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
length = random.randint(10, self.max_packet_size)
|
||||||
|
numlist = self.rand_array[self.max_packet_size-length:]
|
||||||
|
# Error in length
|
||||||
|
#numlist.append(5)
|
||||||
|
chksum = sum(numlist)
|
||||||
|
# Error in checksum
|
||||||
|
#numlist[0] = 5
|
||||||
|
nums = "".join( [str(n) for n in numlist] )
|
||||||
|
data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums)
|
||||||
|
self.send_cnt += 1
|
||||||
|
|
||||||
|
return WebSocketServer.encode(data)
|
||||||
|
|
||||||
|
|
||||||
|
def check(self, buf):
|
||||||
|
try:
|
||||||
|
data_list = WebSocketServer.decode(buf)
|
||||||
|
except:
|
||||||
|
print "\n<BOF>" + repr(buf) + "<EOF>"
|
||||||
|
return "Failed to decode"
|
||||||
|
|
||||||
|
err = ""
|
||||||
|
for data in data_list:
|
||||||
|
if data.count('$') > 1:
|
||||||
|
raise Exception("Multiple parts within single packet")
|
||||||
|
if len(data) == 0:
|
||||||
|
self.traffic("_")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if data[0] != "^":
|
||||||
|
err += "buf did not start with '^'\n"
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
cnt, length, chksum, nums = data[1:-1].split(':')
|
||||||
|
cnt = int(cnt)
|
||||||
|
length = int(length)
|
||||||
|
chksum = int(chksum)
|
||||||
|
except:
|
||||||
|
print "\n<BOF>" + repr(data) + "<EOF>"
|
||||||
|
err += "Invalid data format\n"
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.recv_cnt != cnt:
|
||||||
|
err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt)
|
||||||
|
self.recv_cnt = cnt + 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.recv_cnt += 1
|
||||||
|
|
||||||
|
if len(nums) != length:
|
||||||
|
err += "Expected length %d but got %d\n" % (length, len(nums))
|
||||||
|
continue
|
||||||
|
|
||||||
|
inv = nums.translate(None, "0123456789")
|
||||||
|
if inv:
|
||||||
|
err += "Invalid characters found: %s\n" % inv
|
||||||
|
continue
|
||||||
|
|
||||||
|
real_chksum = 0
|
||||||
|
for num in nums:
|
||||||
|
real_chksum += int(num)
|
||||||
|
|
||||||
|
if real_chksum != chksum:
|
||||||
|
err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum)
|
||||||
|
return err
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
if len(sys.argv) < 2: raise
|
||||||
|
listen_port = int(sys.argv[1])
|
||||||
|
if len(sys.argv) == 3:
|
||||||
|
delay = int(sys.argv[2])
|
||||||
|
else:
|
||||||
|
delay = 10
|
||||||
|
except:
|
||||||
|
print "Usage: %s <listen_port> [delay_ms]" % sys.argv[0]
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
server = WebSocketTest(
|
||||||
|
listen_port=listen_port,
|
||||||
|
verbose=True,
|
||||||
|
cert='self.pem',
|
||||||
|
web='.',
|
||||||
|
delay=delay)
|
||||||
|
server.start_server()
|
163
utils/README.md
163
utils/README.md
|
@ -1,163 +0,0 @@
|
||||||
## WebSockets Proxy
|
|
||||||
|
|
||||||
|
|
||||||
### wsproxy
|
|
||||||
|
|
||||||
At the most basic level, wsproxy just translates WebSockets traffic
|
|
||||||
to normal socket traffic. wsproxy accepts the WebSockets handshake,
|
|
||||||
parses it, and then begins forwarding traffic between the client and
|
|
||||||
the target in both directions. WebSockets payload data is UTF-8
|
|
||||||
encoded so in order to transport binary data it must use an encoding
|
|
||||||
that can be encapsulated within UTF-8. wsproxy uses base64 to encode
|
|
||||||
all traffic to and from the client. Also, WebSockets traffic starts
|
|
||||||
with '\0' (0) and ends with '\xff' (255). Some buffering is done in
|
|
||||||
case the data from the client is not a full WebSockets frame (i.e.
|
|
||||||
does not end in 255).
|
|
||||||
|
|
||||||
|
|
||||||
#### Additional wsproxy features
|
|
||||||
|
|
||||||
These are not necessary for the basic operation.
|
|
||||||
|
|
||||||
* Daemonizing: When the `-D` option is specified, wsproxy runs
|
|
||||||
in the background as a daemon process.
|
|
||||||
|
|
||||||
* SSL (the wss:// WebSockets URI): This is detected automatically by
|
|
||||||
wsproxy by sniffing the first byte sent from the client and then
|
|
||||||
wrapping the socket if the data starts with '\x16' or '\x80'
|
|
||||||
(indicating SSL).
|
|
||||||
|
|
||||||
* Flash security policy: wsproxy detects flash security policy
|
|
||||||
requests (again by sniffing the first packet) and answers with an
|
|
||||||
appropriate flash security policy response (and then closes the
|
|
||||||
port). This means no separate flash security policy server is needed
|
|
||||||
for supporting the flash WebSockets fallback emulator.
|
|
||||||
|
|
||||||
* Session recording: This feature that allows recording of the traffic
|
|
||||||
sent and received from the client to a file using the `--record`
|
|
||||||
option.
|
|
||||||
|
|
||||||
* Mini-webserver: wsproxy can detect and respond to normal web
|
|
||||||
requests on the same port as the WebSockets proxy and Flash security
|
|
||||||
policy. This functionality is activate with the `--web DIR` option
|
|
||||||
where DIR is the root of the web directory to serve.
|
|
||||||
|
|
||||||
* Wrap a program: see the "Wrap a Program" section below.
|
|
||||||
|
|
||||||
|
|
||||||
#### Implementations of wsproxy
|
|
||||||
|
|
||||||
There are three implementations of wsproxy: python, C, and Node
|
|
||||||
(node.js). wswrapper is only implemented in C.
|
|
||||||
|
|
||||||
Here is the feature support matrix for the the wsproxy
|
|
||||||
implementations:
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Program</th>
|
|
||||||
<th>Language</th>
|
|
||||||
<th>Multiprocess</th>
|
|
||||||
<th>Daemonize</th>
|
|
||||||
<th>SSL/wss</th>
|
|
||||||
<th>Flash Policy Server</th>
|
|
||||||
<th>Session Record</th>
|
|
||||||
<th>Web Server</th>
|
|
||||||
<th>Program Wrap</th>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>wsproxy.py</td>
|
|
||||||
<td>python</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes 1</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>wsproxy</td>
|
|
||||||
<td>C</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
</tr>
|
|
||||||
</tr> <tr>
|
|
||||||
<td>wsproxy.js</td>
|
|
||||||
<td>Node (node.js)</td>
|
|
||||||
<td>yes</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
<td>no</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
* Note 1: to use SSL/wss with python 2.5 or older, see the following
|
|
||||||
section on *Building the Python ssl module*.
|
|
||||||
|
|
||||||
|
|
||||||
### Wrap a Program
|
|
||||||
|
|
||||||
In addition to proxying from a source address to a target address
|
|
||||||
(which may be on a different system), wsproxy has the ability to
|
|
||||||
launch a program on the local system and proxy WebSockets traffic to
|
|
||||||
a normal TCP port owned/bound by the program.
|
|
||||||
|
|
||||||
The is accomplished with a small LD_PRELOAD library (`rebind.so`)
|
|
||||||
which intercepts bind() system calls by the program. The specified
|
|
||||||
port is moved to a new localhost/loopback free high port. wsproxy
|
|
||||||
then proxies WebSockets traffic directed to the original port to the
|
|
||||||
new (moved) port of the program.
|
|
||||||
|
|
||||||
The program wrap mode is invoked by replacing the target with `--`
|
|
||||||
followed by the program command line to wrap.
|
|
||||||
|
|
||||||
`./utils/wsproxy.py 2023 -- PROGRAM ARGS`
|
|
||||||
|
|
||||||
The `--wrap-mode` option can be used to indicate what action to take
|
|
||||||
when the wrapped program exits or daemonizes.
|
|
||||||
|
|
||||||
Here is an example of using wsproxy to wrap the vncserver command
|
|
||||||
(which backgrounds itself):
|
|
||||||
|
|
||||||
`./utils/wsproxy.py 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1`
|
|
||||||
|
|
||||||
Here is an example of wrapping telnetd (from krb5-telnetd).telnetd
|
|
||||||
exits after the connection closes so the wrap mode is set to respawn
|
|
||||||
the command:
|
|
||||||
|
|
||||||
`sudo ./utils/wsproxy.py 2023 --wrap-mode=respawn -- telnetd -debug 2023`
|
|
||||||
|
|
||||||
The `utils/wstelnet.html` page demonstrates a simple WebSockets based
|
|
||||||
telnet client.
|
|
||||||
|
|
||||||
|
|
||||||
### Building the Python ssl module (for python 2.5 and older)
|
|
||||||
|
|
||||||
* Install the build dependencies. On Ubuntu use this command:
|
|
||||||
|
|
||||||
`sudo aptitude install python-dev bluetooth-dev`
|
|
||||||
|
|
||||||
* Download, build the ssl module and symlink to it:
|
|
||||||
|
|
||||||
`cd noVNC/utils`
|
|
||||||
|
|
||||||
`wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz`
|
|
||||||
|
|
||||||
`tar xvzf ssl-1.15.tar.gz`
|
|
||||||
|
|
||||||
`cd ssl-1.15`
|
|
||||||
|
|
||||||
`make`
|
|
||||||
|
|
||||||
`cd ../`
|
|
||||||
|
|
||||||
`ln -sf ssl-1.15/build/lib.linux-*/ssl ssl`
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
VT100-orig.js
|
|
|
@ -1 +0,0 @@
|
||||||
../include
|
|
55
utils/web.py
55
utils/web.py
|
@ -1,55 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
'''
|
|
||||||
A super simple HTTP/HTTPS webserver for python. Automatically detect
|
|
||||||
|
|
||||||
You can make a cert/key with openssl using:
|
|
||||||
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
|
||||||
as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
import traceback, sys
|
|
||||||
import socket
|
|
||||||
import ssl
|
|
||||||
#import http.server as server # python 3.X
|
|
||||||
import SimpleHTTPServer as server # python 2.X
|
|
||||||
|
|
||||||
def do_request(connstream, from_addr):
|
|
||||||
x = object()
|
|
||||||
server.SimpleHTTPRequestHandler(connstream, from_addr, x)
|
|
||||||
connstream.close()
|
|
||||||
|
|
||||||
def serve():
|
|
||||||
bindsocket = socket.socket()
|
|
||||||
bindsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
#bindsocket.bind(('localhost', PORT))
|
|
||||||
bindsocket.bind(('', PORT))
|
|
||||||
bindsocket.listen(5)
|
|
||||||
|
|
||||||
print("serving on port", PORT)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
newsocket, from_addr = bindsocket.accept()
|
|
||||||
peek = newsocket.recv(1024, socket.MSG_PEEK)
|
|
||||||
if peek.startswith("\x16"):
|
|
||||||
connstream = ssl.wrap_socket(
|
|
||||||
newsocket,
|
|
||||||
server_side=True,
|
|
||||||
certfile='self.pem',
|
|
||||||
ssl_version=ssl.PROTOCOL_TLSv1)
|
|
||||||
else:
|
|
||||||
connstream = newsocket
|
|
||||||
|
|
||||||
do_request(connstream, from_addr)
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
try:
|
|
||||||
PORT = int(sys.argv[1])
|
|
||||||
except:
|
|
||||||
print "%s port" % sys.argv[0]
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
serve()
|
|
|
@ -1,176 +0,0 @@
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>WebSockets Echo Test</title>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<!-- Uncomment to activate firebug lite -->
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
Host: <input id='host' style='width:100'>
|
|
||||||
Port: <input id='port' style='width:50'>
|
|
||||||
Encrypt: <input id='encrypt' type='checkbox'>
|
|
||||||
<input id='connectButton' type='button' value='Start' style='width:100px'
|
|
||||||
onclick="connect();">
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
Log:<br>
|
|
||||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var ws, host = null, port = null,
|
|
||||||
msg_cnt = 0, send_cnt = 1, echoDelay = 500,
|
|
||||||
echo_ref;
|
|
||||||
|
|
||||||
function message(str) {
|
|
||||||
console.log(str);
|
|
||||||
cell = $D('messages');
|
|
||||||
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
msg_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array.prototype.pushStr = function (str) {
|
|
||||||
var n = str.length;
|
|
||||||
for (var i=0; i < n; i++) {
|
|
||||||
this.push(str.charCodeAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function send_msg() {
|
|
||||||
if (ws.bufferedAmount > 0) {
|
|
||||||
console.log("Delaying send");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var str = "Message #" + send_cnt, arr = [];
|
|
||||||
arr.pushStr(str)
|
|
||||||
ws.send(Base64.encode(arr));
|
|
||||||
message("Sent message: '" + str + "'");
|
|
||||||
send_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_stats() {
|
|
||||||
$D('sent').innerHTML = sent;
|
|
||||||
$D('received').innerHTML = received;
|
|
||||||
$D('errors').innerHTML = errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function init_ws() {
|
|
||||||
console.log(">> init_ws");
|
|
||||||
console.log("<< init_ws");
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect() {
|
|
||||||
var host = $D('host').value,
|
|
||||||
port = $D('port').value,
|
|
||||||
scheme = "ws://", uri;
|
|
||||||
|
|
||||||
console.log(">> connect");
|
|
||||||
if ((!host) || (!port)) {
|
|
||||||
console.log("must set host and port");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($D('encrypt').checked) {
|
|
||||||
scheme = "wss://";
|
|
||||||
}
|
|
||||||
uri = scheme + host + ":" + port;
|
|
||||||
message("connecting to " + uri);
|
|
||||||
ws = new WebSocket(uri);
|
|
||||||
|
|
||||||
ws.onmessage = function(e) {
|
|
||||||
//console.log(">> WebSockets.onmessage");
|
|
||||||
var arr = Base64.decode(e.data), str = "", i;
|
|
||||||
|
|
||||||
for (i = 0; i < arr.length; i++) {
|
|
||||||
str = str + String.fromCharCode(arr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
message("Received message '" + str + "'");
|
|
||||||
//console.log("<< WebSockets.onmessage");
|
|
||||||
};
|
|
||||||
ws.onopen = function(e) {
|
|
||||||
console.log(">> WebSockets.onopen");
|
|
||||||
echo_ref = setInterval(send_msg, echoDelay);
|
|
||||||
console.log("<< WebSockets.onopen");
|
|
||||||
};
|
|
||||||
ws.onclose = function(e) {
|
|
||||||
console.log(">> WebSockets.onclose");
|
|
||||||
if (echo_ref) {
|
|
||||||
clearInterval(echo_ref);
|
|
||||||
echo_ref = null;
|
|
||||||
}
|
|
||||||
console.log("<< WebSockets.onclose");
|
|
||||||
};
|
|
||||||
ws.onerror = function(e) {
|
|
||||||
console.log(">> WebSockets.onerror");
|
|
||||||
if (echo_ref) {
|
|
||||||
clearInterval(echo_ref);
|
|
||||||
echo_ref = null;
|
|
||||||
}
|
|
||||||
console.log("<< WebSockets.onerror");
|
|
||||||
};
|
|
||||||
|
|
||||||
$D('connectButton').value = "Stop";
|
|
||||||
$D('connectButton').onclick = disconnect;
|
|
||||||
console.log("<< connect");
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnect() {
|
|
||||||
console.log(">> disconnect");
|
|
||||||
if (ws) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (echo_ref) {
|
|
||||||
clearInterval(echo_ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
$D('connectButton').value = "Start";
|
|
||||||
$D('connectButton').onclick = connect;
|
|
||||||
console.log("<< disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* If no builtin websockets then load web_socket.js */
|
|
||||||
if (window.WebSocket) {
|
|
||||||
VNC_native_ws = true;
|
|
||||||
} else {
|
|
||||||
VNC_native_ws = false;
|
|
||||||
console.log("Loading web-socket-js flash bridge");
|
|
||||||
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
|
|
||||||
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
|
|
||||||
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
|
|
||||||
document.write(extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
console.log("onload");
|
|
||||||
if (!VNC_native_ws) {
|
|
||||||
console.log("initializing web-socket-js flash bridge");
|
|
||||||
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
|
|
||||||
WebSocket.__initialize();
|
|
||||||
}
|
|
||||||
var url = document.location.href;
|
|
||||||
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
|
||||||
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,252 +0,0 @@
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>WebSockets Test</title>
|
|
||||||
<script src="include/base64.js"></script>
|
|
||||||
<script src="include/util.js"></script>
|
|
||||||
<script src="include/webutil.js"></script>
|
|
||||||
<!-- Uncomment to activate firebug lite -->
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
Host: <input id='host' style='width:100'>
|
|
||||||
Port: <input id='port' style='width:50'>
|
|
||||||
Encrypt: <input id='encrypt' type='checkbox'>
|
|
||||||
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">
|
|
||||||
<input id='connectButton' type='button' value='Start' style='width:100px'
|
|
||||||
onclick="connect();">
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
<table border=1>
|
|
||||||
<tr>
|
|
||||||
<th align="right">Packets sent:</th>
|
|
||||||
<td align="right"><div id='sent'>0</div></td>
|
|
||||||
</tr><tr>
|
|
||||||
<th align="right">Good Packets Received:</th>
|
|
||||||
<td align="right"><div id='received'>0</div></td>
|
|
||||||
</tr><tr>
|
|
||||||
<th align="right">Errors (Bad Packets Received:)</th>
|
|
||||||
<td align="right"><div id='errors'>0</div></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
Errors:<br>
|
|
||||||
<textarea id="error" style="font-size: 9;" cols=80 rows=25></textarea>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function error(str) {
|
|
||||||
console.error(str);
|
|
||||||
cell = $D('error');
|
|
||||||
cell.innerHTML += errors + ": " + str + "\n";
|
|
||||||
cell.scrollTop = cell.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
var host = null, port = null, sendDelay = 0;
|
|
||||||
var ws = null, update_ref = null, send_ref = null;
|
|
||||||
var sent = 0, received = 0, errors = 0;
|
|
||||||
var max_send = 2000;
|
|
||||||
var recv_seq = 0, send_seq = 0;
|
|
||||||
|
|
||||||
Array.prototype.pushStr = function (str) {
|
|
||||||
var n = str.length;
|
|
||||||
for (var i=0; i < n; i++) {
|
|
||||||
this.push(str.charCodeAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function add (x,y) {
|
|
||||||
return parseInt(x,10)+parseInt(y,10);
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_respond(data) {
|
|
||||||
//console.log(">> check_respond");
|
|
||||||
var decoded, first, last, str, length, chksum, nums, arr;
|
|
||||||
decoded = Base64.decode(data);
|
|
||||||
first = String.fromCharCode(decoded.shift());
|
|
||||||
last = String.fromCharCode(decoded.pop());
|
|
||||||
|
|
||||||
if (first != "^") {
|
|
||||||
errors++;
|
|
||||||
error("Packet missing start char '^'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (last != "$") {
|
|
||||||
errors++;
|
|
||||||
error("Packet missing end char '$'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
arr = decoded.map(function(num) {
|
|
||||||
return String.fromCharCode(num);
|
|
||||||
} ).join('').split(':');
|
|
||||||
seq = arr[0];
|
|
||||||
length = arr[1];
|
|
||||||
chksum = arr[2];
|
|
||||||
nums = arr[3];
|
|
||||||
|
|
||||||
//console.log(" length:" + length + " chksum:" + chksum + " nums:" + nums);
|
|
||||||
if (seq != recv_seq) {
|
|
||||||
errors++;
|
|
||||||
error("Expected seq " + recv_seq + " but got " + seq);
|
|
||||||
recv_seq = parseInt(seq,10) + 1; // Back on track
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
recv_seq++;
|
|
||||||
if (nums.length != length) {
|
|
||||||
errors++;
|
|
||||||
error("Expected length " + length + " but got " + nums.length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//real_chksum = nums.reduce(add);
|
|
||||||
real_chksum = 0;
|
|
||||||
for (var i=0; i < nums.length; i++) {
|
|
||||||
real_chksum += parseInt(nums.charAt(i), 10);
|
|
||||||
}
|
|
||||||
if (real_chksum != chksum) {
|
|
||||||
errors++
|
|
||||||
error("Expected chksum " + chksum + " but real chksum is " + real_chksum);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
received++;
|
|
||||||
//console.log(" Packet checks out: length:" + length + " chksum:" + chksum);
|
|
||||||
//console.log("<< check_respond");
|
|
||||||
}
|
|
||||||
|
|
||||||
function send() {
|
|
||||||
if (ws.bufferedAmount > 0) {
|
|
||||||
console.log("Delaying send");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var length = Math.floor(Math.random()*(max_send-9)) + 10; // 10 - max_send
|
|
||||||
var numlist = [], arr = [];
|
|
||||||
for (var i=0; i < length; i++) {
|
|
||||||
numlist.push( Math.floor(Math.random()*10) );
|
|
||||||
}
|
|
||||||
//chksum = numlist.reduce(add);
|
|
||||||
chksum = 0;
|
|
||||||
for (var i=0; i < numlist.length; i++) {
|
|
||||||
chksum += parseInt(numlist[i], 10);
|
|
||||||
}
|
|
||||||
var nums = numlist.join('');
|
|
||||||
arr.pushStr("^" + send_seq + ":" + length + ":" + chksum + ":" + nums + "$")
|
|
||||||
send_seq ++;
|
|
||||||
ws.send(Base64.encode(arr));
|
|
||||||
sent++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_stats() {
|
|
||||||
$D('sent').innerHTML = sent;
|
|
||||||
$D('received').innerHTML = received;
|
|
||||||
$D('errors').innerHTML = errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function init_ws() {
|
|
||||||
console.log(">> init_ws");
|
|
||||||
var scheme = "ws://";
|
|
||||||
if ($D('encrypt').checked) {
|
|
||||||
scheme = "wss://";
|
|
||||||
}
|
|
||||||
var uri = scheme + host + ":" + port;
|
|
||||||
console.log("connecting to " + uri);
|
|
||||||
ws = new WebSocket(uri);
|
|
||||||
|
|
||||||
ws.onmessage = function(e) {
|
|
||||||
//console.log(">> WebSockets.onmessage");
|
|
||||||
check_respond(e.data);
|
|
||||||
//console.log("<< WebSockets.onmessage");
|
|
||||||
};
|
|
||||||
ws.onopen = function(e) {
|
|
||||||
console.log(">> WebSockets.onopen");
|
|
||||||
send_ref = setInterval(send, sendDelay);
|
|
||||||
console.log("<< WebSockets.onopen");
|
|
||||||
};
|
|
||||||
ws.onclose = function(e) {
|
|
||||||
console.log(">> WebSockets.onclose");
|
|
||||||
clearInterval(send_ref);
|
|
||||||
console.log("<< WebSockets.onclose");
|
|
||||||
};
|
|
||||||
ws.onerror = function(e) {
|
|
||||||
console.log(">> WebSockets.onerror");
|
|
||||||
console.log(" " + e);
|
|
||||||
console.log("<< WebSockets.onerror");
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("<< init_ws");
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect() {
|
|
||||||
console.log(">> connect");
|
|
||||||
host = $D('host').value;
|
|
||||||
port = $D('port').value;
|
|
||||||
sendDelay = parseInt($D('sendDelay').value, 10);
|
|
||||||
if ((!host) || (!port)) {
|
|
||||||
console.log("must set host and port");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
init_ws();
|
|
||||||
update_ref = setInterval(update_stats, 1);
|
|
||||||
|
|
||||||
$D('connectButton').value = "Stop";
|
|
||||||
$D('connectButton').onclick = disconnect;
|
|
||||||
console.log("<< connect");
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnect() {
|
|
||||||
console.log(">> disconnect");
|
|
||||||
if (ws) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(update_ref);
|
|
||||||
update_stats(); // Final numbers
|
|
||||||
recv_seq = 0;
|
|
||||||
send_seq = 0;
|
|
||||||
|
|
||||||
$D('connectButton').value = "Start";
|
|
||||||
$D('connectButton').onclick = connect;
|
|
||||||
console.log("<< disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* If no builtin websockets then load web_socket.js */
|
|
||||||
if (window.WebSocket) {
|
|
||||||
VNC_native_ws = true;
|
|
||||||
} else {
|
|
||||||
VNC_native_ws = false;
|
|
||||||
console.log("Loading web-socket-js flash bridge");
|
|
||||||
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
|
|
||||||
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
|
|
||||||
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
|
|
||||||
document.write(extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
console.log("onload");
|
|
||||||
if (!VNC_native_ws) {
|
|
||||||
console.log("initializing web-socket-js flash bridge");
|
|
||||||
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
|
|
||||||
WebSocket.__initialize();
|
|
||||||
}
|
|
||||||
var url = document.location.href;
|
|
||||||
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
|
||||||
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</html>
|
|
171
utils/wstest.py
171
utils/wstest.py
|
@ -1,171 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
'''
|
|
||||||
WebSocket server-side load test program. Sends and receives traffic
|
|
||||||
that has a random payload (length and content) that is checksummed and
|
|
||||||
given a sequence number. Any errors are reported and counted.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import sys, os, socket, ssl, time, traceback
|
|
||||||
import random, time
|
|
||||||
from select import select
|
|
||||||
|
|
||||||
sys.path.insert(0,os.path.dirname(__file__) + "/../utils/")
|
|
||||||
from websocket import WebSocketServer
|
|
||||||
|
|
||||||
|
|
||||||
class WebSocketTest(WebSocketServer):
|
|
||||||
|
|
||||||
buffer_size = 65536
|
|
||||||
max_packet_size = 10000
|
|
||||||
recv_cnt = 0
|
|
||||||
send_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.errors = 0
|
|
||||||
self.delay = kwargs.pop('delay')
|
|
||||||
|
|
||||||
print "Prepopulating random array"
|
|
||||||
self.rand_array = []
|
|
||||||
for i in range(0, self.max_packet_size):
|
|
||||||
self.rand_array.append(random.randint(0, 9))
|
|
||||||
|
|
||||||
WebSocketServer.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def new_client(self, client):
|
|
||||||
self.send_cnt = 0
|
|
||||||
self.recv_cnt = 0
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.responder(client)
|
|
||||||
except:
|
|
||||||
print "accumulated errors:", self.errors
|
|
||||||
self.errors = 0
|
|
||||||
raise
|
|
||||||
|
|
||||||
def responder(self, client):
|
|
||||||
cqueue = []
|
|
||||||
cpartial = ""
|
|
||||||
socks = [client]
|
|
||||||
last_send = time.time() * 1000
|
|
||||||
|
|
||||||
while True:
|
|
||||||
ins, outs, excepts = select(socks, socks, socks, 1)
|
|
||||||
if excepts: raise Exception("Socket exception")
|
|
||||||
|
|
||||||
if client in ins:
|
|
||||||
buf = client.recv(self.buffer_size)
|
|
||||||
if len(buf) == 0:
|
|
||||||
raise self.EClose("Client closed")
|
|
||||||
#print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf))
|
|
||||||
if buf[-1] == '\xff':
|
|
||||||
if cpartial:
|
|
||||||
err = self.check(cpartial + buf)
|
|
||||||
cpartial = ""
|
|
||||||
else:
|
|
||||||
err = self.check(buf)
|
|
||||||
if err:
|
|
||||||
self.traffic("}")
|
|
||||||
self.errors = self.errors + 1
|
|
||||||
print err
|
|
||||||
else:
|
|
||||||
self.traffic(">")
|
|
||||||
else:
|
|
||||||
self.traffic(".>")
|
|
||||||
cpartial = cpartial + buf
|
|
||||||
|
|
||||||
now = time.time() * 1000
|
|
||||||
if client in outs and now > (last_send + self.delay):
|
|
||||||
last_send = now
|
|
||||||
#print "Client send: %s" % repr(cqueue[0])
|
|
||||||
client.send(self.generate())
|
|
||||||
self.traffic("<")
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
length = random.randint(10, self.max_packet_size)
|
|
||||||
numlist = self.rand_array[self.max_packet_size-length:]
|
|
||||||
# Error in length
|
|
||||||
#numlist.append(5)
|
|
||||||
chksum = sum(numlist)
|
|
||||||
# Error in checksum
|
|
||||||
#numlist[0] = 5
|
|
||||||
nums = "".join( [str(n) for n in numlist] )
|
|
||||||
data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums)
|
|
||||||
self.send_cnt += 1
|
|
||||||
|
|
||||||
return WebSocketServer.encode(data)
|
|
||||||
|
|
||||||
|
|
||||||
def check(self, buf):
|
|
||||||
try:
|
|
||||||
data_list = WebSocketServer.decode(buf)
|
|
||||||
except:
|
|
||||||
print "\n<BOF>" + repr(buf) + "<EOF>"
|
|
||||||
return "Failed to decode"
|
|
||||||
|
|
||||||
err = ""
|
|
||||||
for data in data_list:
|
|
||||||
if data.count('$') > 1:
|
|
||||||
raise Exception("Multiple parts within single packet")
|
|
||||||
if len(data) == 0:
|
|
||||||
self.traffic("_")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if data[0] != "^":
|
|
||||||
err += "buf did not start with '^'\n"
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
cnt, length, chksum, nums = data[1:-1].split(':')
|
|
||||||
cnt = int(cnt)
|
|
||||||
length = int(length)
|
|
||||||
chksum = int(chksum)
|
|
||||||
except:
|
|
||||||
print "\n<BOF>" + repr(data) + "<EOF>"
|
|
||||||
err += "Invalid data format\n"
|
|
||||||
continue
|
|
||||||
|
|
||||||
if self.recv_cnt != cnt:
|
|
||||||
err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt)
|
|
||||||
self.recv_cnt = cnt + 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.recv_cnt += 1
|
|
||||||
|
|
||||||
if len(nums) != length:
|
|
||||||
err += "Expected length %d but got %d\n" % (length, len(nums))
|
|
||||||
continue
|
|
||||||
|
|
||||||
inv = nums.translate(None, "0123456789")
|
|
||||||
if inv:
|
|
||||||
err += "Invalid characters found: %s\n" % inv
|
|
||||||
continue
|
|
||||||
|
|
||||||
real_chksum = 0
|
|
||||||
for num in nums:
|
|
||||||
real_chksum += int(num)
|
|
||||||
|
|
||||||
if real_chksum != chksum:
|
|
||||||
err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum)
|
|
||||||
return err
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try:
|
|
||||||
if len(sys.argv) < 2: raise
|
|
||||||
listen_port = int(sys.argv[1])
|
|
||||||
if len(sys.argv) == 3:
|
|
||||||
delay = int(sys.argv[2])
|
|
||||||
else:
|
|
||||||
delay = 10
|
|
||||||
except:
|
|
||||||
print "Usage: %s <listen_port> [delay_ms]" % sys.argv[0]
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
server = WebSocketTest(
|
|
||||||
listen_port=listen_port,
|
|
||||||
verbose=True,
|
|
||||||
cert='self.pem',
|
|
||||||
web='.',
|
|
||||||
delay=delay)
|
|
||||||
server.start_server()
|
|
30
vnc.html
30
vnc.html
|
@ -1,30 +0,0 @@
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
noVNC example: simple example using default UI
|
|
||||||
Copyright (C) 2010 Joel Martin
|
|
||||||
Licensed under LGPL-3 (see LICENSE.txt)
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<title>noVNC</title>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
||||||
<link rel="stylesheet" href="include/plain.css">
|
|
||||||
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black">
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/vnc.js"></script>
|
|
||||||
<script src="include/ui.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id='vnc'>Loading</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.onload = function () {
|
|
||||||
UI.load('vnc');
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
116
vnc_auto.html
116
vnc_auto.html
|
@ -1,116 +0,0 @@
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
noVNC Example: Automatically connect on page load.
|
|
||||||
Copyright (C) 2010 Joel Martin
|
|
||||||
Licensed under LGPL-3 (see LICENSE.txt)
|
|
||||||
|
|
||||||
Connect parameters are provided in query string:
|
|
||||||
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<title>noVNC</title>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
||||||
<link rel="stylesheet" href="include/plain.css" title="plain">
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/vnc.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body style="margin: 0px;">
|
|
||||||
<div id="VNC_screen">
|
|
||||||
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
|
|
||||||
<table border=0 width="100%"><tr>
|
|
||||||
<td><div id="VNC_status">Loading</div></td>
|
|
||||||
<td width="1%"><div id="VNC_buttons">
|
|
||||||
<input type=button value="Send CtrlAltDel"
|
|
||||||
id="sendCtrlAltDelButton">
|
|
||||||
</div></td>
|
|
||||||
</tr></table>
|
|
||||||
</div>
|
|
||||||
<canvas id="VNC_canvas" width="640px" height="20px">
|
|
||||||
Canvas not supported.
|
|
||||||
</canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/*jslint white: false */
|
|
||||||
/*global window, $, Util, RFB, */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var rfb;
|
|
||||||
|
|
||||||
function setPassword() {
|
|
||||||
rfb.sendPassword($D('password_input').value);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function sendCtrlAltDel() {
|
|
||||||
rfb.sendCtrlAltDel();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function updateState(rfb, state, oldstate, msg) {
|
|
||||||
var s, sb, cad, klass;
|
|
||||||
s = $D('VNC_status');
|
|
||||||
sb = $D('VNC_status_bar');
|
|
||||||
cad = $D('sendCtrlAltDelButton');
|
|
||||||
switch (state) {
|
|
||||||
case 'failed':
|
|
||||||
case 'fatal':
|
|
||||||
klass = "VNC_status_error";
|
|
||||||
break;
|
|
||||||
case 'normal':
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
|
||||||
case 'disconnected':
|
|
||||||
case 'loaded':
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
|
||||||
case 'password':
|
|
||||||
msg = '<form onsubmit="return setPassword();"';
|
|
||||||
msg += ' style="margin-bottom: 0px">';
|
|
||||||
msg += 'Password Required: ';
|
|
||||||
msg += '<input type=password size=10 id="password_input" class="VNC_status">';
|
|
||||||
msg += '<\/form>';
|
|
||||||
klass = "VNC_status_warn";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
klass = "VNC_status_warn";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state === "normal") { cad.disabled = false; }
|
|
||||||
else { cad.disabled = true; }
|
|
||||||
|
|
||||||
if (typeof(msg) !== 'undefined') {
|
|
||||||
sb.setAttribute("class", klass);
|
|
||||||
s.innerHTML = msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
var host, port, password;
|
|
||||||
|
|
||||||
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
|
||||||
|
|
||||||
host = WebUtil.getQueryVar('host', null);
|
|
||||||
port = WebUtil.getQueryVar('port', null);
|
|
||||||
password = WebUtil.getQueryVar('password', '');
|
|
||||||
if ((!host) || (!port)) {
|
|
||||||
updateState('failed',
|
|
||||||
"Must specify host and port in URL");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
|
||||||
'encrypt': WebUtil.getQueryVar('encrypt', false),
|
|
||||||
'true_color': WebUtil.getQueryVar('true_color', true),
|
|
||||||
'local_cursor': WebUtil.getQueryVar('cursor', true),
|
|
||||||
'shared': WebUtil.getQueryVar('shared', true),
|
|
||||||
'updateState': updateState});
|
|
||||||
rfb.connect(host, port, password);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ except:
|
||||||
from urlparse import urlsplit
|
from urlparse import urlsplit
|
||||||
from cgi import parse_qsl
|
from cgi import parse_qsl
|
||||||
|
|
||||||
class WebSocketServer():
|
class WebSocketServer(object):
|
||||||
"""
|
"""
|
||||||
WebSockets server class.
|
WebSockets server class.
|
||||||
Must be sub-classed with new_client method definition.
|
Must be sub-classed with new_client method definition.
|
||||||
|
@ -345,49 +345,51 @@ Connection: Upgrade\r
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
csock = startsock = None
|
|
||||||
pid = err = 0
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.poll()
|
csock = startsock = None
|
||||||
|
pid = err = 0
|
||||||
|
|
||||||
ready = select.select([lsock], [], [], 1)[0];
|
try:
|
||||||
if lsock in ready:
|
self.poll()
|
||||||
startsock, address = lsock.accept()
|
|
||||||
|
ready = select.select([lsock], [], [], 1)[0];
|
||||||
|
if lsock in ready:
|
||||||
|
startsock, address = lsock.accept()
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
except Exception, exc:
|
||||||
|
if hasattr(exc, 'errno'):
|
||||||
|
err = exc.errno
|
||||||
|
else:
|
||||||
|
err = exc[0]
|
||||||
|
if err == errno.EINTR:
|
||||||
|
self.vmsg("Ignoring interrupted syscall")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.vmsg('%s: forking handler' % address[0])
|
||||||
|
pid = os.fork()
|
||||||
|
|
||||||
|
if pid == 0:
|
||||||
|
# handler process
|
||||||
|
csock = self.do_handshake(startsock, address)
|
||||||
|
self.new_client(csock)
|
||||||
else:
|
else:
|
||||||
continue
|
# parent process
|
||||||
|
self.handler_id += 1
|
||||||
|
|
||||||
|
except self.EClose, exc:
|
||||||
|
# Connection was not a WebSockets connection
|
||||||
|
if exc.args[0]:
|
||||||
|
self.msg("%s: %s" % (address[0], exc.args[0]))
|
||||||
|
except KeyboardInterrupt, exc:
|
||||||
|
pass
|
||||||
except Exception, exc:
|
except Exception, exc:
|
||||||
if hasattr(exc, 'errno'):
|
self.msg("handler exception: %s" % str(exc))
|
||||||
err = exc.errno
|
if self.verbose:
|
||||||
elif type(exc) == select.error:
|
self.msg(traceback.format_exc())
|
||||||
err = exc[0]
|
|
||||||
if err == errno.EINTR:
|
|
||||||
self.vmsg("Ignoring interrupted syscall()")
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
self.vmsg('%s: forking handler' % address[0])
|
|
||||||
pid = os.fork()
|
|
||||||
|
|
||||||
if pid == 0:
|
|
||||||
# handler process
|
|
||||||
csock = self.do_handshake(startsock, address)
|
|
||||||
self.new_client(csock)
|
|
||||||
else:
|
|
||||||
# parent process
|
|
||||||
self.handler_id += 1
|
|
||||||
|
|
||||||
except self.EClose, exc:
|
|
||||||
# Connection was not a WebSockets connection
|
|
||||||
if exc.args[0]:
|
|
||||||
self.msg("%s: %s" % (address[0], exc.args[0]))
|
|
||||||
except KeyboardInterrupt, exc:
|
|
||||||
pass
|
|
||||||
except Exception, exc:
|
|
||||||
self.msg("handler exception: %s" % str(exc))
|
|
||||||
if self.verbose:
|
|
||||||
self.msg(traceback.format_exc())
|
|
||||||
finally:
|
finally:
|
||||||
if csock and csock != startsock:
|
if csock and csock != startsock:
|
||||||
csock.close()
|
csock.close()
|
|
@ -5,9 +5,9 @@
|
||||||
<script src="include/base64.js"></script>
|
<script src="include/base64.js"></script>
|
||||||
<script src="include/util.js"></script>
|
<script src="include/util.js"></script>
|
||||||
<script src="include/webutil.js"></script>
|
<script src="include/webutil.js"></script>
|
||||||
<script src="include/canvas.js"></script>
|
<script src="include/keysym.js"></script>
|
||||||
<script src="VT100.js"></script>
|
<script src="include/VT100.js"></script>
|
||||||
<script src="wstelnet.js"></script>
|
<script src="include/wstelnet.js"></script>
|
||||||
<!-- Uncomment to activate firebug lite -->
|
<!-- Uncomment to activate firebug lite -->
|
||||||
<!--
|
<!--
|
||||||
<script type='text/javascript'
|
<script type='text/javascript'
|
Loading…
Reference in New Issue