diff --git a/.gitignore b/.gitignore
index 6c1a93c..1a9d961 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
*.pyc
*.o
-wsproxy
-tests/data_*.js
+*.so
diff --git a/LICENSE.txt b/LICENSE.txt
index a2eb8eb..c8242fe 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -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:
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
http://github.com/gimite/web-socket-js
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7dc1bc4
--- /dev/null
+++ b/Makefile
@@ -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
+
diff --git a/README.md b/README.md
index 3e1eb60..20f8e79 100644
--- a/README.md
+++ b/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:
+
+
+
+
Program
+
Language
+
Multiprocess
+
Daemonize
+
SSL/wss
+
Flash Policy Server
+
Session Record
+
Web Server
+
Program Wrap
+
+
websockify
+
python
+
yes
+
yes
+
yes 1
+
yes
+
yes
+
yes
+
yes
+
+
other/websockify
+
C
+
yes
+
yes
+
yes
+
yes
+
no
+
no
+
no
+
+
+
other/websockify.js
+
Node (node.js)
+
yes
+
no
+
no
+
no
+
no
+
no
+
no
+
+
-### 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
-sponsoring ongoing development of this project (and for employing me).
+### Wrap a Program
-Notable commits, announcements and news are posted to
-@noVNC
+In addition to proxying from a source address to a target address
+(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`
-
+The `--wrap-mode` option can be used to indicate what action to take
+when the wrapped program exits or daemonizes.
-See more screenshots here.
+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
- [Datanex Cloud Computing Platform](http://www.sentryds.com/products/datanex/).
+ `sudo ./websockify 2023 --wrap-mode=respawn -- telnetd -debug 2023`
-* [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr):
- Feature [#1935](http://code.osuosl.org/issues/1935).
+The `wstelnet.html` page demonstrates a simple WebSockets based telnet
+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
- 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).
+### Building the Python ssl module (for python 2.5 and older)
+* 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
- browsers have had Canvas support for quite some time. Internet
- Explorer 9 will have Canvas support (finally).
+* Download, build the ssl module and symlink to it:
-* HTML5 WebSockets: For browsers that do not have builtin
- WebSockets support, the project includes
- web-socket-js,
- a WebSockets emulator using Adobe Flash.
+ `cd websockify/`
-* Fast Javascript Engine: noVNC avoids using new Javascript
- 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.
+ `wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz`
-* I maintain a more detailed list of browser compatibility here.
+ `tar xvzf ssl-1.15.tar.gz`
+ `cd ssl-1.15`
-### Server Requirements
+ `make`
-Unless you are using a VNC server with support for WebSockets
-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://").
+ `cd ../`
-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:
-
-
-
-
-
-
-
Loading
-
-
-
-
-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.
diff --git a/docs/TODO b/docs/TODO
index c64cace..1166e5e 100644
--- a/docs/TODO
+++ b/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
-
-Medium Term:
-
-- VNC performance and regression playback suite.
-
-- Viewport support
-
-- Touchscreen testing/support.
-
-- wswrapper:
- - epoll_* support
-
diff --git a/docs/browsers.md b/docs/browsers.md
deleted file mode 100644
index ad52fd3..0000000
--- a/docs/browsers.md
+++ /dev/null
@@ -1,126 +0,0 @@
-## noVNC: Browser Support
-
-### Ubuntu Karmic (9.10)
-
-
-
-
Browser
-
Status
-
Performance/Notes
-
-
Chrome 7.0.510.0
-
Broken
-
WebKit render bug (see note 3)
-
-
Chrome 5.0.375.29
-
Excellent
-
Very fast. Native WebSockets.
-
-
Firefox 4.0 Beta 6
-
Excellent
-
Fast. Native WebSockets. SSL cert hassle (see note 2)
-
-
Firefox 3.6.1
-
Good
-
Slowed by web-socket-js overhead. Local cursor causes segfault.
-
-
Arora 0.10.1
-
Fair
-
Slow due to broken putImageData and web-socket-js.
-
-
Opera 10.60
-
Poor
-
web-socket-js problems, mouse/keyboard issues (see note 1)
-
-
Konqueror 4.3.2
-
Broken
-
web-socket-js never loads
-
-
-
-
-### Ubuntu Jaunty (9.04)
-
-
-
-
Browser
-
Status
-
Performance/Notes
-
-
Chrome 5.0.375.29
-
Excellent
-
Very fast. Native WebSockets.
-
-
Firefox 3.5
-
Good
-
Slowed by web-socket-js overhead.
-
-
Firefox 3.0.17
-
Fair
-
Works fine but is slow.
-
-
Arora 0.5
-
Fair
-
Slow due to broken putImageData and web-socket-js.
-
-
Opera 10.60
-
Poor
-
web-socket-js problems, mouse/keyboard issues (see note 1)
-
-
Konqueror 4.2.2
-
Broken
-
web-socket-js never loads
-
-
-
-
-### Windows XP
-
-
-
-
Browser
-
Status
-
Performance/Notes
-
-
Chrome 5.0.375.99
-
Excellent
-
Very fast. Native WebSockets.
-
-
Safari 5.0
-
Excellent
-
Fast. Native WebSockets.
-
-
Firefox 3.0.19
-
Good
-
Some overhead from web-socket-js.
-
-
IE 6, 7, 8
-
Non-starter
-
No basic Canvas support. Javascript painfully slow.
-
-
-
-
-* 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 #46319.
- The noVNC bug is #28.
-
-
diff --git a/docs/links b/docs/links
deleted file mode 100644
index 8cb4da1..0000000
--- a/docs/links
+++ /dev/null
@@ -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.
diff --git a/docs/rfb_notes b/docs/rfb_notes
deleted file mode 100644
index 643e16c..0000000
--- a/docs/rfb_notes
+++ /dev/null
@@ -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
-
-
-
-
-
diff --git a/docs/rfbproto-3.3.pdf b/docs/rfbproto-3.3.pdf
deleted file mode 100644
index 56b8764..0000000
Binary files a/docs/rfbproto-3.3.pdf and /dev/null differ
diff --git a/docs/rfbproto-3.7.pdf b/docs/rfbproto-3.7.pdf
deleted file mode 100644
index 1ef5462..0000000
Binary files a/docs/rfbproto-3.7.pdf and /dev/null differ
diff --git a/docs/rfbproto-3.8.pdf b/docs/rfbproto-3.8.pdf
deleted file mode 100644
index 8f0730f..0000000
Binary files a/docs/rfbproto-3.8.pdf and /dev/null differ
diff --git a/include/VT100.js b/include/VT100.js
new file mode 100644
index 0000000..6a325e1
--- /dev/null
+++ b/include/VT100.js
@@ -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
+//
+// 2007-08-12 - refresh():
+// - factor out colour code to html_colours_()
+// - fix handling of A_REVERSE | A_DIM
+// - simplify initial 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 ... 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 ... 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
+//
...
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 = "" + end_tag;
+ }
+ if (n_at.mode & VT100.A_STANDOUT)
+ n_at.mode |= VT100.A_BOLD;
+ pair = this.html_colours_(n_at);
+ start_tag += '';
+ stuff += start_tag;
+ end_tag = "" + 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 = "" + stuff + "\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 [{COUNT}A
+ this.move(this.row_ - Math.max(1, this.csi_parms_[0]),
+ this.col_);
+ break;
+ case 'B':
+ // Cursor Down [{COUNT}B
+ this.move(this.row_ + Math.max(1, this.csi_parms_[0]),
+ this.col_);
+ break;
+ case 'C':
+ // Cursor Forward [{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 [{COUNT}D
+ this.move(this.row_,
+ this.col_ - Math.max(1, this.csi_parms_[0]));
+ break;
+ case 'f':
+ case 'H':
+ // Cursor Home [{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[?
+ // Expect a number - the reset type
+ this.csi_parms_ = [ch];
+ this.esc_state_ = 6;
+ break;
+ case 6: // Reset mode handling, saw [?1
+ // Expect a letter - the mode target, example:
+ // [?1l : cursor key mode = cursor
+ // [?1h : save current screen, create new empty
+ // screen and position at 0,0
+ // [?5l : White on blk
+ // XXX: Ignored for now.
+ //dump("Saw reset mode: [?" + 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");
+}
diff --git a/include/black.css b/include/black.css
deleted file mode 100644
index 4cc195e..0000000
--- a/include/black.css
+++ /dev/null
@@ -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;
-}
diff --git a/include/canvas.js b/include/canvas.js
deleted file mode 100644
index 0e15a65..0000000
--- a/include/canvas.js
+++ /dev/null
@@ -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;
-}
-
diff --git a/include/des.js b/include/des.js
deleted file mode 100644
index 1f95285..0000000
--- a/include/des.js
+++ /dev/null
@@ -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 , 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 . 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<>> 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
diff --git a/include/keysym.js b/include/keysym.js
new file mode 100644
index 0000000..b24ac88
--- /dev/null
+++ b/include/keysym.js
@@ -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;
+}
+
diff --git a/include/plain.css b/include/plain.css
deleted file mode 100644
index d8ee942..0000000
--- a/include/plain.css
+++ /dev/null
@@ -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;
-}
-
diff --git a/include/playback.js b/include/playback.js
deleted file mode 100644
index 4a8106b..0000000
--- a/include/playback.js
+++ /dev/null
@@ -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();
-};
-
diff --git a/include/rfb.js b/include/rfb.js
deleted file mode 100644
index 6adccd4..0000000
--- a/include/rfb.js
+++ /dev/null
@@ -1,1622 +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, browser: true, bitwise: false, plusplus: false */
-/*global window, WebSocket, Util, Canvas, VNC_native_ws, Base64, DES */
-
-
-function RFB(conf) {
-
-conf = conf || {}; // Configuration
-var that = {}, // Public API interface
-
- // Pre-declare private functions used before definitions (jslint)
- init_vars, updateState, init_msg, normal_msg, recv_message,
- framebufferUpdate, print_stats,
-
- pixelFormat, clientEncodings, fbUpdateRequest,
- keyEvent, pointerEvent, clientCutText,
-
- extract_data_uri, scan_tight_imgQ,
-
- send_array, checkEvents, // Overridable for testing
-
-
- //
- // Private RFB namespace variables
- //
- rfb_host = '',
- rfb_port = 5900,
- rfb_password = '',
-
- rfb_state = 'disconnected',
- rfb_version = 0,
- rfb_max_version= 3.8,
- rfb_auth_scheme= '',
-
-
- // In preference order
- encodings = [
- ['COPYRECT', 0x01 ],
- ['TIGHT_PNG', -260 ],
- ['HEXTILE', 0x05 ],
- ['RRE', 0x02 ],
- ['RAW', 0x00 ],
- ['DesktopSize', -223 ],
- ['Cursor', -239 ],
-
- // Psuedo-encoding settings
- ['JPEG_quality_lo', -32 ],
- //['JPEG_quality_hi', -23 ],
- ['compress_lo', -255 ]
- //['compress_hi', -247 ]
- ],
-
- encHandlers = {},
- encNames = {},
- encStats = {}, // [rectCnt, rectCntTot]
-
- ws = null, // Web Socket object
- canvas = null, // Canvas object
- sendTimer = null, // Send Queue check timer
- connTimer = null, // connection timer
- disconnTimer = null, // disconnection timer
- msgTimer = null, // queued handle_message timer
-
- // Receive and send queues
- rQ = [], // Receive Queue
- rQi = 0, // Receive Queue Index
- rQmax = 100000, // Max size before compacting
- sQ = [], // Send Queue
-
- // Frame buffer update state
- FBU = {
- rects : 0,
- subrects : 0, // RRE
- lines : 0, // RAW
- tiles : 0, // HEXTILE
- bytes : 0,
- x : 0,
- y : 0,
- width : 0,
- height : 0,
- encoding : 0,
- subencoding : -1,
- background : null,
- imgQ : [] // TIGHT_PNG image queue
- },
-
- fb_Bpp = 4,
- fb_depth = 3,
- fb_width = 0,
- fb_height = 0,
- fb_name = "",
-
- scan_imgQ_rate = 100,
- last_req_time = 0,
- rre_chunk_sz = 100,
-
- timing = {
- last_fbu : 0,
- fbu_total : 0,
- fbu_total_cnt : 0,
- full_fbu_total : 0,
- full_fbu_cnt : 0,
-
- fbu_rt_start : 0,
- fbu_rt_total : 0,
- fbu_rt_cnt : 0
- },
-
- test_mode = false,
-
- /* Mouse state */
- mouse_buttonMask = 0,
- mouse_arr = [];
-
-
-//
-// Configuration settings
-//
-function cdef(v, type, defval, desc) {
- Util.conf_default(conf, that, v, type, defval, desc); }
-
-cdef('target', 'str', null, 'VNC viewport rendering Canvas');
-cdef('focusContainer', 'dom', document, 'Area that traps keyboard input');
-
-cdef('encrypt', 'bool', false, 'Use TLS/SSL/wss encryption');
-cdef('true_color', 'bool', true, 'Request true color pixel data');
-cdef('local_cursor', 'bool', false, 'Request locally rendered cursor');
-cdef('shared', 'bool', true, 'Request shared mode');
-
-cdef('connectTimeout', 'int', 2, 'Time (s) to wait for connection');
-cdef('disconnectTimeout', 'int', 3, 'Time (s) to wait for disconnection');
-cdef('check_rate', 'int', 217, 'Timing (ms) of send/receive check');
-cdef('fbu_req_rate', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests');
-
-cdef('updateState',
- 'func', function() { Util.Debug("updateState stub"); },
- 'callback: state update');
-cdef('clipboardReceive',
- 'func', function() { Util.Debug("clipboardReceive stub"); },
- 'callback: clipboard contents received');
-
-
-// Override/add some specific getters/setters
-that.set_local_cursor = function(cursor) {
- if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
- conf.local_cursor = false;
- } else {
- if (canvas.get_cursor_uri()) {
- conf.local_cursor = true;
- } else {
- Util.Warn("Browser does not support local cursor");
- }
- }
-};
-
-that.get_canvas = function() {
- return canvas;
-};
-
-
-
-
-//
-// Private functions
-//
-
-//
-// Receive Queue functions
-//
-function rQlen() {
- return rQ.length - rQi;
-}
-
-function rQshift16() {
- return (rQ[rQi++] << 8) +
- (rQ[rQi++] );
-}
-function rQshift32() {
- return (rQ[rQi++] << 24) +
- (rQ[rQi++] << 16) +
- (rQ[rQi++] << 8) +
- (rQ[rQi++] );
-}
-function rQshiftStr(len) {
- var arr = rQ.slice(rQi, rQi + len);
- rQi += len;
- return arr.map(function (num) {
- return String.fromCharCode(num); } ).join('');
-
-}
-function rQshiftBytes(len) {
- rQi += len;
- return rQ.slice(rQi-len, rQi);
-}
-
-// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
-// to be available in the receive queue. Return true if we need to
-// wait (and possibly print a debug message), otherwise false.
-function rQwait(msg, num, goback) {
- if (typeof num !== 'number') { num = FBU.bytes; }
- var rQlen = rQ.length - rQi; // Skip rQlen() function call
- if (rQlen < num) {
- if (goback) {
- if (rQi < goback) {
- throw("rQwait cannot backup " + goback + " bytes");
- }
- rQi -= goback;
- }
- //Util.Debug(" waiting for " + (num-rQlen) +
- // " " + msg + " byte(s)");
- return true; // true means need more data
- }
- return false;
-}
-
-
-//
-// Setup routines
-//
-
-// Create the public API interface and initialize
-function constructor() {
- var i, rmode;
- Util.Debug(">> RFB.constructor");
-
- // Create lookup tables based encoding number
- for (i=0; i < encodings.length; i+=1) {
- encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]];
- encNames[encodings[i][1]] = encodings[i][0];
- encStats[encodings[i][1]] = [0, 0];
- }
- // Initialize canvas
- try {
- canvas = new Canvas({'target': conf.target,
- 'focusContainer': conf.focusContainer});
- } catch (exc) {
- Util.Error("Canvas exception: " + exc);
- updateState('fatal', "No working Canvas");
- }
- rmode = canvas.get_render_mode();
-
- init_vars();
-
- /* Check web-socket-js if no builtin WebSocket support */
- if (VNC_native_ws) {
- Util.Info("Using native WebSockets");
- updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
- } else {
- Util.Warn("Using web-socket-js bridge. Flash version: " +
- Util.Flash.version);
- if ((! Util.Flash) ||
- (Util.Flash.version < 9)) {
- updateState('fatal', "WebSockets or Adobe Flash is required");
- } else if (document.location.href.substr(0, 7) === "file://") {
- updateState('fatal',
- "'file://' URL is incompatible with Adobe Flash");
- } else {
- updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
- }
- }
-
- Util.Debug("<< RFB.constructor");
- return that; // Return the public API interface
-}
-
-function init_ws() {
- Util.Debug(">> RFB.init_ws");
-
- var uri = "";
- if (conf.encrypt) {
- uri = "wss://";
- } else {
- uri = "ws://";
- }
- uri += rfb_host + ":" + rfb_port + "/";
- Util.Info("connecting to " + uri);
- ws = new WebSocket(uri);
-
- ws.onmessage = recv_message;
- ws.onopen = function(e) {
- Util.Debug(">> WebSocket.onopen");
- if (rfb_state === "connect") {
- updateState('ProtocolVersion', "Starting VNC handshake");
- } else {
- fail("Got unexpected WebSockets connection");
- }
- Util.Debug("<< WebSocket.onopen");
- };
- ws.onclose = function(e) {
- Util.Debug(">> WebSocket.onclose");
- if (rfb_state === 'disconnect') {
- updateState('disconnected', 'VNC disconnected');
- } else if (rfb_state === 'ProtocolVersion') {
- fail('Failed to connect to server');
- } else if (rfb_state in {'failed':1, 'disconnected':1}) {
- Util.Error("Received onclose while disconnected");
- } else {
- fail('Server disconnected');
- }
- Util.Debug("<< WebSocket.onclose");
- };
- ws.onerror = function(e) {
- Util.Debug(">> WebSocket.onerror");
- fail("WebSocket error");
- Util.Debug("<< WebSocket.onerror");
- };
-
- Util.Debug("<< RFB.init_ws");
-}
-
-init_vars = function() {
- /* Reset state */
- rQ = [];
- rQi = 0;
- sQ = [];
- FBU.rects = 0;
- FBU.subrects = 0; // RRE and HEXTILE
- FBU.lines = 0; // RAW
- FBU.tiles = 0; // HEXTILE
- FBU.imgQ = []; // TIGHT_PNG image queue
- mouse_buttonMask = 0;
- mouse_arr = [];
-
- // Clear the per connection encoding stats
- for (var i=0; i < encodings.length; i+=1) {
- encStats[encodings[i][1]][0] = 0;
- }
-};
-
-// Print statistics
-print_stats = function() {
- var i, encName, s;
- Util.Info("Encoding stats for this connection:");
- for (i=0; i < encodings.length; i+=1) {
- s = encStats[encodings[i][1]];
- if ((s[0] + s[1]) > 0) {
- Util.Info(" " + encodings[i][0] + ": " +
- s[0] + " rects");
- }
- }
- Util.Info("Encoding stats since page load:");
- for (i=0; i < encodings.length; i+=1) {
- s = encStats[encodings[i][1]];
- if ((s[0] + s[1]) > 0) {
- Util.Info(" " + encodings[i][0] + ": "
- + s[1] + " rects");
- }
- }
-};
-
-//
-// Utility routines
-//
-
-
-/*
- * Running states:
- * disconnected - idle state
- * normal - connected
- *
- * Page states:
- * loaded - page load, equivalent to disconnected
- * connect - starting initialization
- * disconnect - starting disconnect
- * failed - abnormal transition to disconnected
- * fatal - failed to load page, or fatal error
- *
- * VNC initialization states:
- * ProtocolVersion
- * Security
- * Authentication
- * password - waiting for password, not part of RFB
- * SecurityResult
- * ClientInitialization - not triggered by server message
- * ServerInitialization
- */
-updateState = function(state, statusMsg) {
- var func, cmsg, oldstate = rfb_state;
-
- if (state === oldstate) {
- /* Already here, ignore */
- Util.Debug("Already in state '" + state + "', ignoring.");
- return;
- }
-
- /*
- * These are disconnected states. A previous connect may
- * asynchronously cause a connection so make sure we are closed.
- */
- if (state in {'disconnected':1, 'loaded':1, 'connect':1,
- 'disconnect':1, 'failed':1, 'fatal':1}) {
- if (sendTimer) {
- clearInterval(sendTimer);
- sendTimer = null;
- }
-
- if (msgTimer) {
- clearInterval(msgTimer);
- msgTimer = null;
- }
-
- if (canvas && canvas.getContext()) {
- canvas.stop();
- if (Util.get_logging() !== 'debug') {
- canvas.clear();
- }
- }
-
- if (ws) {
- if ((ws.readyState === WebSocket.OPEN) ||
- (ws.readyState === WebSocket.CONNECTING)) {
- Util.Info("Closing WebSocket connection");
- ws.close();
- }
- ws.onmessage = function (e) { return; };
- }
- }
-
- if (oldstate === 'fatal') {
- Util.Error("Fatal error, cannot continue");
- }
-
- if ((state === 'failed') || (state === 'fatal')) {
- func = Util.Error;
- } else {
- func = Util.Warn;
- }
-
- if ((oldstate === 'failed') && (state === 'disconnected')) {
- // Do disconnect action, but stay in failed state.
- rfb_state = 'failed';
- } else {
- rfb_state = state;
- }
-
- cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
- func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg);
-
- if (connTimer && (rfb_state !== 'connect')) {
- Util.Debug("Clearing connect timer");
- clearInterval(connTimer);
- connTimer = null;
- }
-
- if (disconnTimer && (rfb_state !== 'disconnect')) {
- Util.Debug("Clearing disconnect timer");
- clearInterval(disconnTimer);
- disconnTimer = null;
- }
-
- switch (state) {
- case 'normal':
- if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
- Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
- }
-
- break;
-
-
- case 'connect':
-
- connTimer = setTimeout(function () {
- fail("Connect timeout");
- }, conf.connectTimeout * 1000);
-
- init_vars();
- init_ws();
-
- // WebSocket.onopen transitions to 'ProtocolVersion'
- break;
-
-
- case 'disconnect':
-
- if (! test_mode) {
- disconnTimer = setTimeout(function () {
- fail("Disconnect timeout");
- }, conf.disconnectTimeout * 1000);
- }
-
- print_stats();
-
- // WebSocket.onclose transitions to 'disconnected'
- break;
-
-
- case 'failed':
- if (oldstate === 'disconnected') {
- Util.Error("Invalid transition from 'disconnected' to 'failed'");
- }
- if (oldstate === 'normal') {
- Util.Error("Error while connected.");
- }
- if (oldstate === 'init') {
- Util.Error("Error while initializing.");
- }
-
- // Make sure we transition to disconnected
- setTimeout(function() { updateState('disconnected'); }, 50);
-
- break;
-
-
- default:
- // No state change action to take
-
- }
-
- if ((oldstate === 'failed') && (state === 'disconnected')) {
- // Leave the failed message
- conf.updateState(that, state, oldstate);
- } else {
- conf.updateState(that, state, oldstate, statusMsg);
- }
-};
-function fail(msg) {
- updateState('failed', msg);
- return false;
-}
-
-function encode_message() {
- /* base64 encode */
- return Base64.encode(sQ);
-}
-
-function decode_message(data) {
- //Util.Debug(">> decode_message: " + data);
- /* base64 decode */
- rQ = rQ.concat(Base64.decode(data, 0));
- //Util.Debug(">> decode_message, rQ: " + rQ);
-}
-
-function handle_message() {
- //Util.Debug("rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + rQlen() + ")");
- if (rQlen() === 0) {
- Util.Warn("handle_message called on empty receive queue");
- return;
- }
- switch (rfb_state) {
- case 'disconnected':
- case 'failed':
- Util.Error("Got data while disconnected");
- break;
- case 'normal':
- if (normal_msg() && rQlen() > 0) {
- // true means we can continue processing
- // Give other events a chance to run
- if (msgTimer === null) {
- Util.Debug("More data to process, creating timer");
- msgTimer = setTimeout(function () {
- msgTimer = null;
- handle_message();
- }, 10);
- } else {
- Util.Debug("More data to process, existing timer");
- }
- }
- // Compact the queue
- if (rQ.length > rQmax) {
- //Util.Debug("Compacting receive queue");
- rQ = rQ.slice(rQi);
- rQi = 0;
- }
- break;
- default:
- init_msg();
- break;
- }
-}
-
-recv_message = function(e) {
- //Util.Debug(">> recv_message: " + e.data.length);
-
- try {
- decode_message(e.data);
- if (rQlen() > 0) {
- handle_message();
- } else {
- Util.Debug("Ignoring empty message");
- }
- } catch (exc) {
- if (typeof exc.stack !== 'undefined') {
- Util.Warn("recv_message, caught exception: " + exc.stack);
- } else if (typeof exc.description !== 'undefined') {
- Util.Warn("recv_message, caught exception: " + exc.description);
- } else {
- Util.Warn("recv_message, caught exception:" + exc);
- }
- if (typeof exc.name !== 'undefined') {
- fail(exc.name + ": " + exc.message);
- } else {
- fail(exc);
- }
- }
- //Util.Debug("<< recv_message");
-};
-
-// overridable for testing
-send_array = function(arr) {
- //Util.Debug(">> send_array: " + arr);
- sQ = sQ.concat(arr);
- if (ws.bufferedAmount === 0) {
- //Util.Debug("arr: " + arr);
- //Util.Debug("sQ: " + sQ);
- ws.send(encode_message(sQ));
- sQ = [];
- } else {
- Util.Debug("Delaying send");
- }
-};
-
-function send_string(str) {
- //Util.Debug(">> send_string: " + str);
- send_array(str.split('').map(
- function (chr) { return chr.charCodeAt(0); } ) );
-}
-
-function genDES(password, challenge) {
- var i, passwd = [], des;
- for (i=0; i < password.length; i += 1) {
- passwd.push(password.charCodeAt(i));
- }
- return (new DES(passwd)).encrypt(challenge);
-}
-
-function flushClient() {
- if (mouse_arr.length > 0) {
- //send_array(mouse_arr.concat(fbUpdateRequest(1)));
- send_array(mouse_arr);
- setTimeout(function() {
- send_array(fbUpdateRequest(1));
- }, 50);
-
- mouse_arr = [];
- return true;
- } else {
- return false;
- }
-}
-
-// overridable for testing
-checkEvents = function() {
- var now;
- if (rfb_state === 'normal') {
- if (! flushClient()) {
- now = new Date().getTime();
- if (now > last_req_time + conf.fbu_req_rate) {
- last_req_time = now;
- send_array(fbUpdateRequest(1));
- }
- }
- }
- setTimeout(checkEvents, conf.check_rate);
-};
-
-function keyPress(keysym, down) {
- var arr;
- arr = keyEvent(keysym, down);
- arr = arr.concat(fbUpdateRequest(1));
- send_array(arr);
-}
-
-function mouseButton(x, y, down, bmask) {
- if (down) {
- mouse_buttonMask |= bmask;
- } else {
- mouse_buttonMask ^= bmask;
- }
- mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
- flushClient();
-}
-
-function mouseMove(x, y) {
- //Util.Debug('>> mouseMove ' + x + "," + y);
- mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
-}
-
-
-//
-// Server message handlers
-//
-
-// RFB/VNC initialisation message handler
-init_msg = function() {
- //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
-
- var strlen, reason, length, sversion, cversion,
- i, types, num_types, challenge, response, bpp, depth,
- big_endian, true_color, name_length;
-
- //Util.Debug("rQ (" + rQlen() + ") " + rQ);
- switch (rfb_state) {
-
- case 'ProtocolVersion' :
- if (rQlen() < 12) {
- return fail("Incomplete protocol version");
- }
- sversion = rQshiftStr(12).substr(4,7);
- Util.Info("Server ProtocolVersion: " + sversion);
- switch (sversion) {
- case "003.003": rfb_version = 3.3; break;
- case "003.006": rfb_version = 3.3; break; // UltraVNC
- case "003.007": rfb_version = 3.7; break;
- case "003.008": rfb_version = 3.8; break;
- default:
- return fail("Invalid server version " + sversion);
- }
- if (rfb_version > rfb_max_version) {
- rfb_version = rfb_max_version;
- }
-
- if (! test_mode) {
- sendTimer = setInterval(function() {
- // Send updates either at a rate of one update
- // every 50ms, or whatever slower rate the network
- // can handle.
- if (ws.bufferedAmount === 0) {
- if (sQ) {
- ws.send(encode_message(sQ));
- sQ = [];
- }
- } else {
- Util.Debug("Delaying send");
- }
- }, 50);
- }
-
- cversion = "00" + parseInt(rfb_version,10) +
- ".00" + ((rfb_version * 10) % 10);
- send_string("RFB " + cversion + "\n");
- updateState('Security', "Sent ProtocolVersion: " + cversion);
- break;
-
- case 'Security' :
- if (rfb_version >= 3.7) {
- // Server sends supported list, client decides
- num_types = rQ[rQi++];
- if (rQwait("security type", num_types, 1)) { return false; }
- if (num_types === 0) {
- strlen = rQshift32();
- reason = rQshiftStr(strlen);
- return fail("Security failure: " + reason);
- }
- rfb_auth_scheme = 0;
- types = rQshiftBytes(num_types);
- Util.Debug("Server security types: " + types);
- for (i=0; i < types.length; i+=1) {
- if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {
- rfb_auth_scheme = types[i];
- }
- }
- if (rfb_auth_scheme === 0) {
- return fail("Unsupported security types: " + types);
- }
-
- send_array([rfb_auth_scheme]);
- } else {
- // Server decides
- if (rQwait("security scheme", 4)) { return false; }
- rfb_auth_scheme = rQshift32();
- }
- updateState('Authentication',
- "Authenticating using scheme: " + rfb_auth_scheme);
- init_msg(); // Recursive fallthrough (workaround JSLint complaint)
- break;
-
- // Triggered by fallthough, not by server message
- case 'Authentication' :
- //Util.Debug("Security auth scheme: " + rfb_auth_scheme);
- switch (rfb_auth_scheme) {
- case 0: // connection failed
- if (rQwait("auth reason", 4)) { return false; }
- strlen = rQshift32();
- reason = rQshiftStr(strlen);
- return fail("Auth failure: " + reason);
- case 1: // no authentication
- if (rfb_version >= 3.8) {
- updateState('SecurityResult');
- return;
- } else {
- // Fall through to ClientInitialisation
- }
- break;
- case 2: // VNC authentication
- if (rfb_password.length === 0) {
- updateState('password', "Password Required");
- return;
- }
- if (rQwait("auth challenge", 16)) { return false; }
- challenge = rQshiftBytes(16);
- //Util.Debug("Password: " + rfb_password);
- //Util.Debug("Challenge: " + challenge +
- // " (" + challenge.length + ")");
- response = genDES(rfb_password, challenge);
- //Util.Debug("Response: " + response +
- // " (" + response.length + ")");
-
- //Util.Debug("Sending DES encrypted auth response");
- send_array(response);
- updateState('SecurityResult');
- return;
- default:
- fail("Unsupported auth scheme: " + rfb_auth_scheme);
- return;
- }
- updateState('ClientInitialisation', "No auth required");
- init_msg(); // Recursive fallthrough (workaround JSLint complaint)
- break;
-
- case 'SecurityResult' :
- if (rQwait("VNC auth response ", 4)) { return false; }
- switch (rQshift32()) {
- case 0: // OK
- // Fall through to ClientInitialisation
- break;
- case 1: // failed
- if (rfb_version >= 3.8) {
- length = rQshift32();
- if (rQwait("SecurityResult reason", length, 8)) {
- return false;
- }
- reason = rQshiftStr(length);
- fail(reason);
- } else {
- fail("Authentication failed");
- }
- return;
- case 2: // too-many
- return fail("Too many auth attempts");
- }
- updateState('ClientInitialisation', "Authentication OK");
- init_msg(); // Recursive fallthrough (workaround JSLint complaint)
- break;
-
- // Triggered by fallthough, not by server message
- case 'ClientInitialisation' :
- send_array([conf.shared ? 1 : 0]); // ClientInitialisation
- updateState('ServerInitialisation', "Authentication OK");
- break;
-
- case 'ServerInitialisation' :
- if (rQwait("server initialization", 24)) { return false; }
-
- /* Screen size */
- fb_width = rQshift16();
- fb_height = rQshift16();
-
- /* PIXEL_FORMAT */
- bpp = rQ[rQi++];
- depth = rQ[rQi++];
- big_endian = rQ[rQi++];
- true_color = rQ[rQi++];
-
- Util.Info("Screen: " + fb_width + "x" + fb_height +
- ", bpp: " + bpp + ", depth: " + depth +
- ", big_endian: " + big_endian +
- ", true_color: " + true_color);
-
- /* Connection name/title */
- rQshiftStr(12);
- name_length = rQshift32();
- fb_name = rQshiftStr(name_length);
-
- canvas.resize(fb_width, fb_height, conf.true_color);
- canvas.start(keyPress, mouseButton, mouseMove);
-
- if (conf.true_color) {
- fb_Bpp = 4;
- fb_depth = 3;
- } else {
- fb_Bpp = 1;
- fb_depth = 1;
- }
-
- response = pixelFormat();
- response = response.concat(clientEncodings());
- response = response.concat(fbUpdateRequest(0));
- timing.fbu_rt_start = (new Date()).getTime();
- send_array(response);
-
- /* Start pushing/polling */
- setTimeout(checkEvents, conf.check_rate);
- setTimeout(scan_tight_imgQ, scan_imgQ_rate);
-
- if (conf.encrypt) {
- updateState('normal', "Connected (encrypted) to: " + fb_name);
- } else {
- updateState('normal', "Connected (unencrypted) to: " + fb_name);
- }
- break;
- }
- //Util.Debug("<< init_msg");
-};
-
-
-/* Normal RFB/VNC server message handler */
-normal_msg = function() {
- //Util.Debug(">> normal_msg");
-
- var ret = true, msg_type, length,
- c, first_colour, num_colours, red, green, blue;
-
- if (FBU.rects > 0) {
- msg_type = 0;
- } else {
- msg_type = rQ[rQi++];
- }
- switch (msg_type) {
- case 0: // FramebufferUpdate
- ret = framebufferUpdate(); // false means need more data
- break;
- case 1: // SetColourMapEntries
- Util.Debug("SetColourMapEntries");
- rQi++; // Padding
- first_colour = rQshift16(); // First colour
- num_colours = rQshift16();
- for (c=0; c < num_colours; c+=1) {
- red = rQshift16();
- //Util.Debug("red before: " + red);
- red = parseInt(red / 256, 10);
- //Util.Debug("red after: " + red);
- green = parseInt(rQshift16() / 256, 10);
- blue = parseInt(rQshift16() / 256, 10);
- canvas.set_colourMap([red, green, blue], first_colour + c);
- }
- Util.Info("Registered " + num_colours + " colourMap entries");
- //Util.Debug("colourMap: " + canvas.get_colourMap());
- break;
- case 2: // Bell
- Util.Warn("Bell (unsupported)");
- break;
- case 3: // ServerCutText
- Util.Debug("ServerCutText");
- if (rQwait("ServerCutText header", 7, 1)) { return false; }
- rQshiftBytes(3); // Padding
- length = rQshift32();
- if (rQwait("ServerCutText", length, 8)) { return false; }
-
- conf.clipboardReceive(that, rQshiftStr(length));
- break;
- default:
- fail("Disconnected: illegal server message type " + msg_type);
- Util.Debug("rQ.slice(0,30):" + rQ.slice(0,30));
- break;
- }
- //Util.Debug("<< normal_msg");
- return ret;
-};
-
-framebufferUpdate = function() {
- var now, hdr, fbu_rt_diff, ret = true;
-
- if (FBU.rects === 0) {
- //Util.Debug("New FBU: rQ.slice(0,20): " + rQ.slice(0,20));
- if (rQwait("FBU header", 3)) {
- if (rQi === 0) {
- rQ.unshift(0); // FBU msg_type
- } else {
- rQi -= 1;
- }
- return false;
- }
- rQi++;
- FBU.rects = rQshift16();
- //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);
- FBU.bytes = 0;
- timing.cur_fbu = 0;
- if (timing.fbu_rt_start > 0) {
- now = (new Date()).getTime();
- Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));
- }
- }
-
- while (FBU.rects > 0) {
- if (rfb_state !== "normal") {
- return false;
- }
- if (rQwait("FBU")) { return false; }
- if (FBU.bytes === 0) {
- if (rQwait("rect header", 12)) { return false; }
- /* New FramebufferUpdate */
-
- hdr = rQshiftBytes(12);
- FBU.x = (hdr[0] << 8) + hdr[1];
- FBU.y = (hdr[2] << 8) + hdr[3];
- FBU.width = (hdr[4] << 8) + hdr[5];
- FBU.height = (hdr[6] << 8) + hdr[7];
- FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
- (hdr[10] << 8) + hdr[11], 10);
-
- if (encNames[FBU.encoding]) {
- // Debug:
- /*
- var msg = "FramebufferUpdate rects:" + FBU.rects;
- msg += " x: " + FBU.x + " y: " + FBU.y;
- msg += " width: " + FBU.width + " height: " + FBU.height;
- msg += " encoding:" + FBU.encoding;
- msg += "(" + encNames[FBU.encoding] + ")";
- msg += ", rQlen(): " + rQlen();
- Util.Debug(msg);
- */
- } else {
- fail("Disconnected: unsupported encoding " +
- FBU.encoding);
- return false;
- }
- }
-
- timing.last_fbu = (new Date()).getTime();
-
- ret = encHandlers[FBU.encoding]();
-
- now = (new Date()).getTime();
- timing.cur_fbu += (now - timing.last_fbu);
-
- if (ret) {
- encStats[FBU.encoding][0] += 1;
- encStats[FBU.encoding][1] += 1;
- }
-
- if (FBU.rects === 0) {
- if (((FBU.width === fb_width) &&
- (FBU.height === fb_height)) ||
- (timing.fbu_rt_start > 0)) {
- timing.full_fbu_total += timing.cur_fbu;
- timing.full_fbu_cnt += 1;
- Util.Info("Timing of full FBU, cur: " +
- timing.cur_fbu + ", total: " +
- timing.full_fbu_total + ", cnt: " +
- timing.full_fbu_cnt + ", avg: " +
- (timing.full_fbu_total /
- timing.full_fbu_cnt));
- }
- if (timing.fbu_rt_start > 0) {
- fbu_rt_diff = now - timing.fbu_rt_start;
- timing.fbu_rt_total += fbu_rt_diff;
- timing.fbu_rt_cnt += 1;
- Util.Info("full FBU round-trip, cur: " +
- fbu_rt_diff + ", total: " +
- timing.fbu_rt_total + ", cnt: " +
- timing.fbu_rt_cnt + ", avg: " +
- (timing.fbu_rt_total /
- timing.fbu_rt_cnt));
- timing.fbu_rt_start = 0;
- }
- }
- if (! ret) {
- return ret; // false ret means need more data
- }
- }
- return true; // We finished this FBU
-};
-
-//
-// FramebufferUpdate encodings
-//
-
-encHandlers.RAW = function display_raw() {
- //Util.Debug(">> display_raw (" + rQlen() + " bytes)");
-
- var cur_y, cur_height;
-
- if (FBU.lines === 0) {
- FBU.lines = FBU.height;
- }
- FBU.bytes = FBU.width * fb_Bpp; // At least a line
- if (rQwait("RAW")) { return false; }
- cur_y = FBU.y + (FBU.height - FBU.lines);
- cur_height = Math.min(FBU.lines,
- Math.floor(rQlen()/(FBU.width * fb_Bpp)));
- canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, rQ, rQi);
- rQshiftBytes(FBU.width * cur_height * fb_Bpp);
- FBU.lines -= cur_height;
-
- if (FBU.lines > 0) {
- FBU.bytes = FBU.width * fb_Bpp; // At least another line
- } else {
- FBU.rects -= 1;
- FBU.bytes = 0;
- }
- //Util.Debug("<< display_raw (" + rQlen() + " bytes)");
- return true;
-};
-
-encHandlers.COPYRECT = function display_copy_rect() {
- //Util.Debug(">> display_copy_rect");
-
- var old_x, old_y;
-
- if (rQwait("COPYRECT", 4)) { return false; }
- old_x = rQshift16();
- old_y = rQshift16();
- canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
- FBU.rects -= 1;
- FBU.bytes = 0;
- return true;
-};
-
-encHandlers.RRE = function display_rre() {
- //Util.Debug(">> display_rre (" + rQlen() + " bytes)");
- var color, x, y, width, height, chunk;
-
- if (FBU.subrects === 0) {
- if (rQwait("RRE", 4+fb_Bpp)) { return false; }
- FBU.subrects = rQshift32();
- color = rQshiftBytes(fb_Bpp); // Background
- canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
- }
- while ((FBU.subrects > 0) && (rQlen() >= (fb_Bpp + 8))) {
- color = rQshiftBytes(fb_Bpp);
- x = rQshift16();
- y = rQshift16();
- width = rQshift16();
- height = rQshift16();
- canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
- FBU.subrects -= 1;
- }
- //Util.Debug(" display_rre: rects: " + FBU.rects +
- // ", FBU.subrects: " + FBU.subrects);
-
- if (FBU.subrects > 0) {
- chunk = Math.min(rre_chunk_sz, FBU.subrects);
- FBU.bytes = (fb_Bpp + 8) * chunk;
- } else {
- FBU.rects -= 1;
- FBU.bytes = 0;
- }
- //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);
- return true;
-};
-
-encHandlers.HEXTILE = function display_hextile() {
- //Util.Debug(">> display_hextile");
- var subencoding, subrects, tile, color, cur_tile,
- tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh;
-
- if (FBU.tiles === 0) {
- FBU.tiles_x = Math.ceil(FBU.width/16);
- FBU.tiles_y = Math.ceil(FBU.height/16);
- FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
- FBU.tiles = FBU.total_tiles;
- }
-
- /* FBU.bytes comes in as 1, rQlen() at least 1 */
- while (FBU.tiles > 0) {
- FBU.bytes = 1;
- if (rQwait("HEXTILE subencoding")) { return false; }
- //Util.Debug(" 2 rQ length: " + rQlen() + " rQ[rQi]: " + rQ[rQi] + " rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + ", FBU.rects: " + FBU.rects + ", FBU.tiles: " + FBU.tiles);
- subencoding = rQ[rQi]; // Peek
- if (subencoding > 30) { // Raw
- fail("Disconnected: illegal hextile subencoding " + subencoding);
- //Util.Debug("rQ.slice(0,30):" + rQ.slice(0,30));
- return false;
- }
- subrects = 0;
- cur_tile = FBU.total_tiles - FBU.tiles;
- tile_x = cur_tile % FBU.tiles_x;
- tile_y = Math.floor(cur_tile / FBU.tiles_x);
- x = FBU.x + tile_x * 16;
- y = FBU.y + tile_y * 16;
- w = Math.min(16, (FBU.x + FBU.width) - x);
- h = Math.min(16, (FBU.y + FBU.height) - y);
-
- /* Figure out how much we are expecting */
- if (subencoding & 0x01) { // Raw
- //Util.Debug(" Raw subencoding");
- FBU.bytes += w * h * fb_Bpp;
- } else {
- if (subencoding & 0x02) { // Background
- FBU.bytes += fb_Bpp;
- }
- if (subencoding & 0x04) { // Foreground
- FBU.bytes += fb_Bpp;
- }
- if (subencoding & 0x08) { // AnySubrects
- FBU.bytes += 1; // Since we aren't shifting it off
- if (rQwait("hextile subrects header")) { return false; }
- subrects = rQ[rQi + FBU.bytes-1]; // Peek
- if (subencoding & 0x10) { // SubrectsColoured
- FBU.bytes += subrects * (fb_Bpp + 2);
- } else {
- FBU.bytes += subrects * 2;
- }
- }
- }
-
- /*
- Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
- " (" + tile_x + "," + tile_y + ")" +
- " [" + x + "," + y + "]@" + w + "x" + h +
- ", subenc:" + subencoding +
- "(last: " + FBU.lastsubencoding + "), subrects:" +
- subrects +
- ", rQlen():" + rQlen() + ", FBU.bytes:" + FBU.bytes +
- " last:" + rQ.slice(FBU.bytes-10, FBU.bytes) +
- " next:" + rQ.slice(FBU.bytes-1, FBU.bytes+10));
- */
- if (rQwait("hextile")) { return false; }
-
- /* We know the encoding and have a whole tile */
- FBU.subencoding = rQ[rQi];
- rQi += 1;
- if (FBU.subencoding === 0) {
- if (FBU.lastsubencoding & 0x01) {
- /* Weird: ignore blanks after RAW */
- Util.Debug(" Ignoring blank after RAW");
- } else {
- canvas.fillRect(x, y, w, h, FBU.background);
- }
- } else if (FBU.subencoding & 0x01) { // Raw
- canvas.blitImage(x, y, w, h, rQ, rQi);
- rQi += FBU.bytes - 1;
- } else {
- if (FBU.subencoding & 0x02) { // Background
- FBU.background = rQ.slice(rQi, rQi + fb_Bpp);
- rQi += fb_Bpp;
- }
- if (FBU.subencoding & 0x04) { // Foreground
- FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);
- rQi += fb_Bpp;
- }
-
- tile = canvas.getTile(x, y, w, h, FBU.background);
- if (FBU.subencoding & 0x08) { // AnySubrects
- subrects = rQ[rQi];
- rQi += 1;
- for (s = 0; s < subrects; s += 1) {
- if (FBU.subencoding & 0x10) { // SubrectsColoured
- color = rQ.slice(rQi, rQi + fb_Bpp);
- rQi += fb_Bpp;
- } else {
- color = FBU.foreground;
- }
- xy = rQ[rQi];
- rQi += 1;
- sx = (xy >> 4);
- sy = (xy & 0x0f);
-
- wh = rQ[rQi];
- rQi += 1;
- sw = (wh >> 4) + 1;
- sh = (wh & 0x0f) + 1;
-
- canvas.setSubTile(tile, sx, sy, sw, sh, color);
- }
- }
- canvas.putTile(tile);
- }
- //rQshiftBytes(FBU.bytes);
- FBU.lastsubencoding = FBU.subencoding;
- FBU.bytes = 0;
- FBU.tiles -= 1;
- }
-
- if (FBU.tiles === 0) {
- FBU.rects -= 1;
- }
-
- //Util.Debug("<< display_hextile");
- return true;
-};
-
-
-encHandlers.TIGHT_PNG = function display_tight_png() {
- //Util.Debug(">> display_tight_png");
- var ctl, cmode, clength, getCLength, color, img;
- //Util.Debug(" FBU.rects: " + FBU.rects);
- //Util.Debug(" starting rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + rQlen() + ")");
-
- FBU.bytes = 1; // compression-control byte
- if (rQwait("TIGHT compression-control")) { return false; }
-
- // Get 'compact length' header and data size
- getCLength = function (arr, offset) {
- var header = 1, data = 0;
- data += arr[offset + 0] & 0x7f;
- if (arr[offset + 0] & 0x80) {
- header += 1;
- data += (arr[offset + 1] & 0x7f) << 7;
- if (arr[offset + 1] & 0x80) {
- header += 1;
- data += arr[offset + 2] << 14;
- }
- }
- return [header, data];
- };
-
- ctl = rQ[rQi];
- switch (ctl >> 4) {
- case 0x08: cmode = "fill"; break;
- case 0x09: cmode = "jpeg"; break;
- case 0x0A: cmode = "png"; break;
- default: throw("Illegal basic compression received, ctl: " + ctl);
- }
- switch (cmode) {
- // fill uses fb_depth because TPIXELs drop the padding byte
- case "fill": FBU.bytes += fb_depth; break; // TPIXEL
- case "jpeg": FBU.bytes += 3; break; // max clength
- case "png": FBU.bytes += 3; break; // max clength
- }
-
- if (rQwait("TIGHT " + cmode)) { return false; }
-
- //Util.Debug(" rQ.slice(0,20): " + rQ.slice(0,20) + " (" + rQlen() + ")");
- //Util.Debug(" cmode: " + cmode);
-
- // Determine FBU.bytes
- switch (cmode) {
- case "fill":
- rQi++; // shift off ctl
- color = rQshiftBytes(fb_depth);
- canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
- break;
- case "jpeg":
- case "png":
- clength = getCLength(rQ, rQi+1);
- FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
- if (rQwait("TIGHT " + cmode)) { return false; }
-
- // We have everything, render it
- //Util.Debug(" png, rQlen(): " + rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]);
- rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
- img = new Image();
- img.onload = scan_tight_imgQ;
- FBU.imgQ.push([img, FBU.x, FBU.y]);
- img.src = "data:image/" + cmode +
- extract_data_uri(rQshiftBytes(clength[1]));
- img = null;
- break;
- }
- FBU.bytes = 0;
- FBU.rects -= 1;
- //Util.Debug(" ending rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + rQlen() + ")");
- //Util.Debug("<< display_tight_png");
- return true;
-};
-
-extract_data_uri = function(arr) {
- //var i, stra = [];
- //for (i=0; i< arr.length; i += 1) {
- // stra.push(String.fromCharCode(arr[i]));
- //}
- //return "," + escape(stra.join(''));
- return ";base64," + Base64.encode(arr);
-};
-
-scan_tight_imgQ = function() {
- var img, imgQ, ctx;
- ctx = canvas.getContext();
- if (rfb_state === 'normal') {
- imgQ = FBU.imgQ;
- while ((imgQ.length > 0) && (imgQ[0][0].complete)) {
- img = imgQ.shift();
- ctx.drawImage(img[0], img[1], img[2]);
- }
- setTimeout(scan_tight_imgQ, scan_imgQ_rate);
- }
-};
-
-encHandlers.DesktopSize = function set_desktopsize() {
- Util.Debug(">> set_desktopsize");
- fb_width = FBU.width;
- fb_height = FBU.height;
- canvas.clear();
- canvas.resize(fb_width, fb_height);
- timing.fbu_rt_start = (new Date()).getTime();
- // Send a new non-incremental request
- send_array(fbUpdateRequest(0));
-
- FBU.bytes = 0;
- FBU.rects -= 1;
-
- Util.Debug("<< set_desktopsize");
- return true;
-};
-
-encHandlers.Cursor = function set_cursor() {
- var x, y, w, h, pixelslength, masklength;
- //Util.Debug(">> set_cursor");
- x = FBU.x; // hotspot-x
- y = FBU.y; // hotspot-y
- w = FBU.width;
- h = FBU.height;
-
- pixelslength = w * h * fb_Bpp;
- masklength = Math.floor((w + 7) / 8) * h;
-
- FBU.bytes = pixelslength + masklength;
- if (rQwait("cursor encoding")) { return false; }
-
- //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
-
- canvas.changeCursor(rQshiftBytes(pixelslength),
- rQshiftBytes(masklength),
- x, y, w, h);
-
- FBU.bytes = 0;
- FBU.rects -= 1;
-
- //Util.Debug("<< set_cursor");
- return true;
-};
-
-encHandlers.JPEG_quality_lo = function set_jpeg_quality() {
- Util.Error("Server sent jpeg_quality pseudo-encoding");
-};
-
-encHandlers.compress_lo = function set_compress_level() {
- Util.Error("Server sent compress level pseudo-encoding");
-};
-
-/*
- * Client message routines
- */
-
-pixelFormat = function() {
- //Util.Debug(">> pixelFormat");
- var arr;
- arr = [0]; // msg-type
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
-
- arr.push8(fb_Bpp * 8); // bits-per-pixel
- arr.push8(fb_depth * 8); // depth
- arr.push8(0); // little-endian
- arr.push8(conf.true_color ? 1 : 0); // true-color
-
- arr.push16(255); // red-max
- arr.push16(255); // green-max
- arr.push16(255); // blue-max
- arr.push8(0); // red-shift
- arr.push8(8); // green-shift
- arr.push8(16); // blue-shift
-
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
- //Util.Debug("<< pixelFormat");
- return arr;
-};
-
-clientEncodings = function() {
- //Util.Debug(">> clientEncodings");
- var arr, i, encList = [];
-
- for (i=0; i> fbUpdateRequest");
- if (!x) { x = 0; }
- if (!y) { y = 0; }
- if (!xw) { xw = fb_width; }
- if (!yw) { yw = fb_height; }
- var arr;
- arr = [3]; // msg-type
- arr.push8(incremental);
- arr.push16(x);
- arr.push16(y);
- arr.push16(xw);
- arr.push16(yw);
- //Util.Debug("<< fbUpdateRequest");
- return arr;
-};
-
-keyEvent = function(keysym, down) {
- //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
- var arr;
- arr = [4]; // msg-type
- arr.push8(down);
- arr.push16(0);
- arr.push32(keysym);
- //Util.Debug("<< keyEvent");
- return arr;
-};
-
-pointerEvent = function(x, y) {
- //Util.Debug(">> pointerEvent, x,y: " + x + "," + y +
- // " , mask: " + mouse_buttonMask);
- var arr;
- arr = [5]; // msg-type
- arr.push8(mouse_buttonMask);
- arr.push16(x);
- arr.push16(y);
- //Util.Debug("<< pointerEvent");
- return arr;
-};
-
-clientCutText = function(text) {
- //Util.Debug(">> clientCutText");
- var arr, i, n;
- arr = [6]; // msg-type
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push32(text.length);
- n = text.length;
- for (i=0; i < n; i+=1) {
- arr.push(text.charCodeAt(i));
- }
- //Util.Debug("<< clientCutText:" + arr);
- return arr;
-};
-
-
-
-//
-// Public API interface functions
-//
-
-that.connect = function(host, port, password) {
- //Util.Debug(">> connect");
-
- rfb_host = host;
- rfb_port = port;
- rfb_password = (password !== undefined) ? password : "";
-
- if ((!rfb_host) || (!rfb_port)) {
- return fail("Must set host and port");
- }
-
- updateState('connect');
- //Util.Debug("<< connect");
-
-};
-
-that.disconnect = function() {
- //Util.Debug(">> disconnect");
- updateState('disconnect', 'Disconnecting');
- //Util.Debug("<< disconnect");
-};
-
-that.sendPassword = function(passwd) {
- rfb_password = passwd;
- rfb_state = "Authentication";
- setTimeout(init_msg, 1);
-};
-
-that.sendCtrlAltDel = function() {
- if (rfb_state !== "normal") { return false; }
- Util.Info("Sending Ctrl-Alt-Del");
- var arr = [];
- arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
- arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt
- arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete
- arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
- arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
- arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
- arr = arr.concat(fbUpdateRequest(1));
- send_array(arr);
-};
-
-// Send a key press. If 'down' is not specified then send a down key
-// followed by an up key.
-that.sendKey = function(code, down) {
- if (rfb_state !== "normal") { return false; }
- var arr = [];
- if (typeof down !== 'undefined') {
- Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
- arr = arr.concat(keyEvent(code, down ? 1 : 0));
- } else {
- Util.Info("Sending key code (down + up): " + code);
- arr = arr.concat(keyEvent(code, 1));
- arr = arr.concat(keyEvent(code, 0));
- }
- arr = arr.concat(fbUpdateRequest(1));
- send_array(arr);
-};
-
-that.clipboardPasteFrom = function(text) {
- if (rfb_state !== "normal") { return; }
- //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
- send_array(clientCutText(text));
- //Util.Debug("<< clipboardPasteFrom");
-};
-
-that.testMode = function(override_send_array) {
- // Overridable internal functions for testing
- test_mode = true;
- send_array = override_send_array;
- that.recv_message = recv_message; // Expose it
-
- checkEvents = function () { /* Stub Out */ };
- that.connect = function(host, port, password) {
- rfb_host = host;
- rfb_port = port;
- rfb_password = password;
- updateState('ProtocolVersion', "Starting VNC handshake");
- };
-};
-
-
-return constructor(); // Return the public API interface
-
-} // End of RFB()
diff --git a/include/ui.js b/include/ui.js
deleted file mode 100644
index 217b8b1..0000000
--- a/include/ui.js
+++ /dev/null
@@ -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 += '
';
- 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 += ' ';
- html += ' Google Chrome Frame.';
- html += '