Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ceec05260e | |||
| cbb55df039 | |||
| af6e9e2408 | |||
| cf068be2d8 | |||
| fc003a13e7 | |||
| ca6b3f4cb1 | |||
| 14717eb468 | |||
| 4dd1bb1ecb | |||
| af1527b276 | |||
| e5d5a7d3fd | |||
| d58f8b5144 | |||
| 18232af2a6 | |||
| b50f340674 | |||
| 2090f1af67 | |||
| 9450f132c6 | |||
| 9ebc84f2e9 | |||
| a997c5fd94 | |||
| 12acb663eb | |||
| d5575ef6f3 | |||
| 90f18a8a96 | |||
| bee36506e1 | |||
| cb3ad1b5b5 | |||
| b1d2225eee | |||
| 8d0c7fb5ac | |||
| 9e2351f5eb | |||
| ab44059b8b | |||
| a04087e44c | |||
| 9f54c23629 | |||
| 72a5596e50 | |||
| 34d8b844ae | |||
| a0726a4b56 | |||
| 57044a3c0a | |||
| dc6e501f20 | |||
| e472e6ce3a | |||
| 17bb6ca38e | |||
| df4fa13b89 | |||
| 53dfab7fb7 | |||
| bc28395abf | |||
| a117514183 | |||
| 58ad83878c | |||
| 02d1f19b5f | |||
| 4910600b71 | |||
| ca78183171 | |||
| 901e565503 | |||
| 483157f87f | |||
| dcf1994d3e | |||
| c390011099 | |||
| f8380ff939 | |||
| f736c3316a | |||
| cbd3435a07 | |||
| bd96e91932 | |||
| 0139b2562c | |||
| 4c75210a4d | |||
| f84504bc63 | |||
| c76b3e4b26 | |||
| 6671c7624d | |||
| ce86f5c954 | |||
| c0c20581f5 | |||
| f2a495c944 | |||
| b8dd87c757 | |||
| d065cad99e | |||
| a09a75e8f2 | |||
| 4cd0070a1c | |||
| 35d7574b09 | |||
| d38db74abd | |||
| 0c4f4b598c | |||
| c6f3919cb0 | |||
| 3b302463f8 | |||
| 23ff806774 | |||
| 94bcf652b3 | |||
| 0ce93900ae | |||
| 9e6e6662c7 | |||
| 32f135d730 | |||
| 1af3e54bef | |||
| 6a02f88e4d | |||
| 9b75bcaada | |||
| a14b8fae2a | |||
| 2cedf48397 | |||
| c514fd5e1c | |||
| 5ca5e2d8cd | |||
| 6fbc37489f | |||
| b0ac240f31 | |||
| 523cc4d6ab | |||
| 0fa6748c52 | |||
| a820f1267a | |||
| c577ca2305 | |||
| de84e09854 | |||
| 682f33a790 | |||
| e79917c3db | |||
| a9a7f0e18c | |||
| ff4bfcb773 | |||
| f66bcda31b | |||
| 86ebeef709 | |||
| f2d856767d | |||
| 2fa565b375 | |||
| b688a909b0 | |||
| 06a9ef0c6c | |||
| c3a172b98c | |||
| aa67056785 | |||
| 91b0dc3915 | |||
| 4f545a5ba6 | |||
| 83ed7b8c25 | |||
| 1c92807a8e | |||
| bc71506553 | |||
| 2e7d81ce24 | |||
| fa4607dbf5 | |||
| a538be4350 | |||
| a367e84a92 | |||
| 1e50871599 | |||
| 65db2d8243 | |||
| b29e06f36e | |||
| f4bce78307 | |||
| 5299db1a00 | |||
| 938daad1c9 | |||
| 1709ee3ab7 | |||
| 479bfa9964 | |||
| a7f5589946 | |||
| 6209639f2c | |||
| 7cd6118ce5 | |||
| 490d471c53 | |||
| 859fc7f18f | |||
| 1310606305 | |||
| a7db50597c | |||
| c506a48154 | |||
| a5df24b488 | |||
| e0b23efe95 | |||
| 608e0f52ee | |||
| 7e5f81f255 | |||
| 832c744578 | |||
| b70ce07706 | |||
| 1c3df652de | |||
| 9192cbe39c | |||
| 54e7cbdf8f | |||
| afecb83988 | |||
| 6ea8bece9b | |||
| 01a9eee991 | |||
| 0911173a89 | |||
| 42e04907ff | |||
| d29c54c476 | |||
| 89a76c785c | |||
| a7eb596d89 | |||
| 8e0f008841 | |||
| c1e8b30877 | |||
| 2c6b8a481e | |||
| c327865b4b | |||
| 53fc7392bb | |||
| 1734b5e465 | |||
| 7825b9ee9f | |||
| 1150d0f70f | |||
| 7b10dc8a48 | |||
| 94568bc08d | |||
| fa74a6e60c | |||
| 48f26d799f | |||
| ec40268e55 | |||
| 46c621175c | |||
| 676fbbb662 | |||
| 4245363cf4 | |||
| ce3bdbccc2 | |||
| ad3f762409 | |||
| ac99a1f791 |
@@ -1,30 +1,78 @@
|
|||||||
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
|
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
|
||||||
|
|
||||||
|
The noVNC core library is licensed under the LGPLv3 (GNU Lesser
|
||||||
|
General Public License). The noVNC core library is composed of the
|
||||||
|
Javascript code necessary for full noVNC operation. This includes (but
|
||||||
|
is not limited to):
|
||||||
|
|
||||||
|
include/base64.js
|
||||||
|
include/des.js
|
||||||
|
include/display.js
|
||||||
|
include/input.js
|
||||||
|
include/jsunzip.js
|
||||||
|
include/logo.js
|
||||||
|
include/rfb.js
|
||||||
|
include/ui.js
|
||||||
|
include/util.js
|
||||||
|
include/vnc.js
|
||||||
|
include/websock.js
|
||||||
|
include/webutil.js
|
||||||
|
|
||||||
|
The HTML, CSS, font and images files that included with the noVNC
|
||||||
|
source distibution (or repository) are not considered part of the
|
||||||
|
noVNC core library and are licensed under more permissive licenses.
|
||||||
|
The intent is to allow easy integration of noVNC into existing web
|
||||||
|
sites and web applications.
|
||||||
|
|
||||||
|
The HTML, CSS, font and image files are licensed as follows:
|
||||||
|
|
||||||
|
*.html : 2-Clause BSD license
|
||||||
|
|
||||||
|
include/*.css : 2-Clause BSD license
|
||||||
|
|
||||||
|
include/Orbitron* : SIL Open Font License 1.1
|
||||||
|
(Copyright 2009 Matt McInerney)
|
||||||
|
|
||||||
|
images/ : Creative Commons Attribution-ShareAlike
|
||||||
|
http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
||||||
|
In addition the following file, which is part of the noVNC core
|
||||||
|
library, may be licensed under either the LGPL-2, LGPL-3 or MPL 2.0
|
||||||
|
when it used separately from the noVNC core library.
|
||||||
|
|
||||||
|
include/input.js : LGPL-2 or any later version
|
||||||
|
|
||||||
Some portions of noVNC are copyright to their individual authors.
|
Some portions of noVNC are copyright to their individual authors.
|
||||||
Please refer to the individual source files and/or to the noVNC commit
|
Please refer to the individual source files and/or to the noVNC commit
|
||||||
history: https://github.com/kanaka/noVNC/commits/master
|
history: https://github.com/kanaka/noVNC/commits/master
|
||||||
|
|
||||||
noVNC is licensed under the LGPL (GNU Lesser General Public License)
|
The are several files and projects that have been incorporated into
|
||||||
version 3 with the following exceptions:
|
the noVNC core library. Here is a list of those files and the original
|
||||||
|
licenses (all LGPL-3 compatible):
|
||||||
|
|
||||||
include/input.js : LGPL-2 or any later version
|
include/base64.js : MPL 1.1, GPL-2 or LGPL-2.1
|
||||||
|
|
||||||
include/base64.js : Dual GPL-2 or LGPL-2.1
|
include/des.js : Various BSD style licenses
|
||||||
|
|
||||||
incluee/des.js : Various BSD style licenses
|
|
||||||
|
|
||||||
include/web-socket-js/ : New BSD license. Source code at
|
include/jsunzip.js : zlib/libpng license
|
||||||
|
|
||||||
|
include/web-socket-js/ : New BSD license (3-clause). Source code at
|
||||||
http://github.com/gimite/web-socket-js
|
http://github.com/gimite/web-socket-js
|
||||||
|
|
||||||
images/ : Creative Commons Attribution-ShareAlike
|
The following license texts are included:
|
||||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
||||||
Uses of the work must be attributed
|
|
||||||
to the noVNC project.
|
|
||||||
|
|
||||||
The LGPL-3 license text is included at:
|
|
||||||
docs/LICENSE.LGPL-3 and
|
docs/LICENSE.LGPL-3 and
|
||||||
docs/LICENSE.GPL-3
|
docs/LICENSE.GPL-3
|
||||||
|
docs/LICENSE.OFL-1.1
|
||||||
|
docs/LICENSE.BSD-3-Clause (New BSD)
|
||||||
|
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
|
||||||
|
docs/LICENSE.zlib
|
||||||
|
docs/LICENSE.MPL-2.0
|
||||||
|
|
||||||
|
Or alternatively the license texts may be found here:
|
||||||
|
|
||||||
Or alternatively the LGPL-3 license text may be found here:
|
|
||||||
http://www.gnu.org/licenses/lgpl.html and
|
http://www.gnu.org/licenses/lgpl.html and
|
||||||
http://www.gnu.org/licenses/gpl.html
|
http://www.gnu.org/licenses/gpl.html
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
http://www.mozilla.org/MPL/1.1/
|
||||||
|
http://www.mozilla.org/MPL/2.0/
|
||||||
|
|||||||
@@ -3,69 +3,89 @@
|
|||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
noVNC is a VNC client implemented using HTML5 technologies,
|
noVNC is a HTML5 VNC client that runs well in any modern browser
|
||||||
specifically Canvas and WebSockets (supports 'wss://' encryption).
|
including mobile browsers (iPhone/iPad and Android).
|
||||||
noVNC is licensed under the
|
|
||||||
[LGPLv3](http://www.gnu.org/licenses/lgpl.html).
|
|
||||||
|
|
||||||
Special thanks to [Sentry Data Systems](http://www.sentryds.com) for
|
More than 16 companies/projects have integrated noVNC into their
|
||||||
sponsoring ongoing development of this project (and for employing me).
|
products including [Ganeti Web
|
||||||
|
Manager](http://code.osuosl.org/projects/ganeti-webmgr),
|
||||||
|
[OpenStack](http://www.openstack.org), and
|
||||||
|
[OpenNebula](http://opennebula.org/). See [the Projects and Companies
|
||||||
|
wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC)
|
||||||
|
for more complete list.
|
||||||
|
|
||||||
There are many companies/projects that have integrated noVNC into
|
### News/help/contact
|
||||||
their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/) and [CloudSigma](http://www.cloudsigma.com/). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links.
|
|
||||||
|
|
||||||
Notable commits, announcements and news are posted to
|
Notable commits, announcements and news are posted to
|
||||||
@<a href="http://www.twitter.com/noVNC">noVNC</a>
|
@<a href="http://www.twitter.com/noVNC">noVNC</a>
|
||||||
|
|
||||||
|
If you are a noVNC developer/integrator/user (or want to be) please
|
||||||
|
join the <a
|
||||||
|
href="https://groups.google.com/forum/?fromgroups#!forum/novnc">noVNC
|
||||||
|
discussion group</a>
|
||||||
|
|
||||||
|
Bugs and feature requests can be submitted via [github
|
||||||
|
issues](https://github.com/kanaka/noVNC/issues). If you are looking
|
||||||
|
for a place to start contributing to noVNC, a good place to start
|
||||||
|
would be the issues that I have marked as
|
||||||
|
["patchwelcome"](https://github.com/kanaka/noVNC/issues?labels=patchwelcome).
|
||||||
|
|
||||||
|
If you want to show appreciation for noVNC you could buy something off
|
||||||
|
my [Amazon wishlist](http://www.amazon.com/registry/wishlist/XTXFXK39IA8C/?reveal=unpurchased&sort=priority&layout=compact) or you could donate to a great non-profits such as: [Compassion
|
||||||
|
International](http://www.compassion.com/), [SIL](http://www.sil.org),
|
||||||
|
[Habitat for Humanity](http://www.habitat.org), [Electronic Frontier
|
||||||
|
Foundation](https://www.eff.org/), [Against Malaria
|
||||||
|
Foundation](http://www.againstmalaria.com/), [Nothing But
|
||||||
|
Nets](http://www.nothingbutnets.net/), etc.
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Supports all modern browsers including mobile (iOS, Android)
|
||||||
|
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG
|
||||||
|
* WebSocket SSL/TLS encryption (i.e. "wss://") support
|
||||||
|
* 24-bit true color and 8 bit colour mapped
|
||||||
|
* Supports desktop resize notification/pseudo-encoding
|
||||||
|
* Local or remote cursor
|
||||||
|
* Clipboard copy/paste
|
||||||
|
* Clipping or scolling modes for large remote screens
|
||||||
|
* Easy site integration and theming (3 example themes included)
|
||||||
|
* Licensed under the [LGPLv3](http://www.gnu.org/licenses/lgpl.html)
|
||||||
|
|
||||||
### Screenshots
|
### Screenshots
|
||||||
|
|
||||||
Running in Chrome before and after connecting:
|
Running in Chrome before and after connecting:
|
||||||
|
|
||||||
<img src="http://kanaka.github.com/noVNC/img/noVNC-1.jpg" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-2.jpg" width=400>
|
<img src="http://kanaka.github.com/noVNC/img/noVNC-5.png" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-7.jpg" width=400>
|
||||||
|
|
||||||
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
||||||
|
|
||||||
|
|
||||||
### Browser Requirements
|
### Browser Requirements
|
||||||
|
|
||||||
* HTML5 Canvas: Except for Internet Explorer, most
|
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
|
||||||
browsers have had Canvas support for quite some time. Internet
|
Safari, Opera 11+, Internet Explorer 9+, etc.
|
||||||
Explorer 9 will have Canvas support (finally).
|
|
||||||
|
|
||||||
* HTML5 WebSockets: For browsers that do not have builtin
|
* HTML5 WebSockets: For browsers that do not have builtin
|
||||||
WebSockets support, the project includes
|
WebSockets support, the project includes
|
||||||
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
|
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
|
||||||
a WebSockets emulator using Adobe Flash.
|
a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in
|
||||||
|
WebSocket support.
|
||||||
|
|
||||||
* Fast Javascript Engine: noVNC avoids using new Javascript
|
* Fast Javascript Engine: this is not strictly a requirement, but
|
||||||
functionality so it will run on older browsers, but decode and
|
without a fast Javascript engine, noVNC might be painfully slow.
|
||||||
rendering happen in Javascript, so a slow Javascript engine will
|
|
||||||
mean noVNC is painfully slow.
|
|
||||||
|
|
||||||
* I maintain a more detailed list of browser compatibility <a
|
* I maintain a more detailed browser compatibility list <a
|
||||||
href="https://github.com/kanaka/noVNC/wiki/Browser-support">here</a>.
|
href="https://github.com/kanaka/noVNC/wiki/Browser-support">here</a>.
|
||||||
|
|
||||||
|
|
||||||
### Server Requirements
|
### Server Requirements
|
||||||
|
|
||||||
Unless you are using a VNC server with support for WebSockets
|
Unless you are using a VNC server with support for WebSockets
|
||||||
connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver)
|
connections (such as [x11vnc/libvncserver](http://libvncserver.sourceforge.net/) or
|
||||||
currently), you need to use a WebSockets to TCP socket proxy. There is
|
[PocketVNC](http://www.pocketvnc.com/blog/?page_id=866)),
|
||||||
a python proxy included ('websockify'). One advantage of using the
|
you need to use a WebSockets to TCP socket proxy. There is
|
||||||
proxy is that it has builtin support for SSL/TLS encryption (i.e.
|
a python proxy included ('websockify').
|
||||||
"wss://").
|
|
||||||
|
|
||||||
There a few reasons why a proxy is required:
|
|
||||||
|
|
||||||
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
|
### Quick Start
|
||||||
@@ -83,11 +103,28 @@ There a few reasons why a proxy is required:
|
|||||||
|
|
||||||
### Other Pages
|
### Other Pages
|
||||||
|
|
||||||
* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL
|
* [Encrypted Connections](https://github.com/kanaka/websockify/wiki/Encrypted-Connections). How to setup websockify so that you can use encrypted connections from noVNC.
|
||||||
certificate, starting a VNC server, advanced websockify usage, etc.
|
|
||||||
|
* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Starting a VNC server, advanced websockify usage, etc.
|
||||||
|
|
||||||
* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects.
|
* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects.
|
||||||
|
|
||||||
* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems.
|
* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems.
|
||||||
|
|
||||||
|
|
||||||
|
### Authors/Contributors
|
||||||
|
|
||||||
|
* noVNC : Joel Martin (github.com/kanaka)
|
||||||
|
* UI and Icons : Chris Gordon
|
||||||
|
* Original Logo : Michael Sersen
|
||||||
|
* tight encoding : Michael Tinglof (Mercuri.ca)
|
||||||
|
|
||||||
|
* Included libraries:
|
||||||
|
* web-socket-js : Hiroshi Ichikawa (github.com/gimite/web-socket-js)
|
||||||
|
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
|
||||||
|
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
|
||||||
|
* jsunzip : Erik Moller (github.com/operasoftware/jsunzip),
|
||||||
|
* tinflate : Joergen Ibsen (ibsensoftware.com)
|
||||||
|
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,33 @@
|
|||||||
|
novnc (0.4) maverick; urgency=low
|
||||||
|
|
||||||
|
* Clarify permissive licenses of HTML, CSS, images.
|
||||||
|
* Use render queue and requestAnimationFrame
|
||||||
|
* UltraVNC repeater support
|
||||||
|
|
||||||
|
-- Joel Martin <github@martintribe.org> Fri, 14 Sep 2012 05:00:00 -0600
|
||||||
|
|
||||||
|
novnc (0.3) maverick; urgency=low
|
||||||
|
|
||||||
|
* add tight encoding support
|
||||||
|
* release pressed key when focus lost (fixes locked Alt key)
|
||||||
|
* Support Apple Remote Desktop
|
||||||
|
* Add nova/openstack proxy wrapper
|
||||||
|
* Better connection close handling/reporting
|
||||||
|
|
||||||
|
-- Joel Martin <github@martintribe.org> Fri, 11 May 2012 03:00:00 -0600
|
||||||
|
|
||||||
|
novnc (0.2) maverick; urgency=low
|
||||||
|
|
||||||
|
* Mobile device support with viewport clipping
|
||||||
|
* Much better styling that also works on mobile devices well
|
||||||
|
* Update websockify to support latest WebSocket protocol HyBi 13
|
||||||
|
(i.e. support IETF 6455)
|
||||||
|
* Better support in websockify for python 2.4 through 3.2
|
||||||
|
* Support VMWare ESX and Intel AMT KVM
|
||||||
|
* View only mode
|
||||||
|
|
||||||
|
-- Joel Martin <github@martintribe.org> Tue, 05 Jul 2011 01:00:00 -0600
|
||||||
|
|
||||||
novnc (0.1) maverick; urgency=low
|
novnc (0.1) maverick; urgency=low
|
||||||
|
|
||||||
* First upstream release
|
* First upstream release
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ utils/websockify /usr/share/novnc/utils
|
|||||||
utils/wsproxy.py /usr/share/novnc/utils
|
utils/wsproxy.py /usr/share/novnc/utils
|
||||||
utils/rebind.c /usr/share/novnc/utils
|
utils/rebind.c /usr/share/novnc/utils
|
||||||
utils/rebind.so /usr/share/novnc/utils
|
utils/rebind.so /usr/share/novnc/utils
|
||||||
|
images /usr/share/novnc
|
||||||
images/favicon.ico /usr/share/novnc
|
images/favicon.ico /usr/share/novnc
|
||||||
include/base64.js /usr/share/novnc/include
|
include/base64.js /usr/share/novnc/include
|
||||||
include/black.css /usr/share/novnc/include
|
|
||||||
include/des.js /usr/share/novnc/include
|
include/des.js /usr/share/novnc/include
|
||||||
include/display.js /usr/share/novnc/include
|
include/display.js /usr/share/novnc/include
|
||||||
include/input.js /usr/share/novnc/include
|
include/input.js /usr/share/novnc/include
|
||||||
include/logo.js /usr/share/novnc/include
|
include/logo.js /usr/share/novnc/include
|
||||||
include/plain.css /usr/share/novnc/include
|
include/base.css /usr/share/novnc/include
|
||||||
|
include/blue.css /usr/share/novnc/include
|
||||||
|
include/black.css /usr/share/novnc/include
|
||||||
include/playback.js /usr/share/novnc/include
|
include/playback.js /usr/share/novnc/include
|
||||||
include/rfb.js /usr/share/novnc/include
|
include/rfb.js /usr/share/novnc/include
|
||||||
include/ui.js /usr/share/novnc/include
|
include/ui.js /usr/share/novnc/include
|
||||||
@@ -24,4 +26,5 @@ include/util.js /usr/share/novnc/include
|
|||||||
include/vnc.js /usr/share/novnc/include
|
include/vnc.js /usr/share/novnc/include
|
||||||
include/websock.js /usr/share/novnc/include
|
include/websock.js /usr/share/novnc/include
|
||||||
include/webutil.js /usr/share/novnc/include
|
include/webutil.js /usr/share/novnc/include
|
||||||
|
include/jsunzip.js /usr/share/novnc/include
|
||||||
include/web-socket-js/* /usr/share/novnc/include/web-socket-js
|
include/web-socket-js/* /usr/share/novnc/include/web-socket-js
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) <year>, <copyright holder>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) <year>, <copyright holder>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the <organization> nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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.
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) <year>, <copyright holder>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express
|
||||||
|
or implied warranty. In no event will the authors be
|
||||||
|
held liable for any damages arising from the use of
|
||||||
|
this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software
|
||||||
|
for any purpose, including commercial applications,
|
||||||
|
and to alter it and redistribute it freely, subject to
|
||||||
|
the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be
|
||||||
|
misrepresented; you must not claim that you
|
||||||
|
wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in
|
||||||
|
the product documentation would be appreciated
|
||||||
|
but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked
|
||||||
|
as such, and must not be misrepresented as
|
||||||
|
being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from
|
||||||
|
any source distribution.
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
Short Term:
|
|
||||||
|
|
||||||
- Test normal arrays vs TypedArrays vs imageData arrays.
|
|
||||||
- Blog post about it.
|
|
||||||
|
|
||||||
- VNC performance and regression playback suite.
|
|
||||||
- WebSockets
|
|
||||||
- expand latency test
|
|
||||||
- add absolute timers (every 500 packets)
|
|
||||||
- try 1 ms delay
|
|
||||||
- stop at 4000 packets
|
|
||||||
- small and large packets test
|
|
||||||
|
|
||||||
- JavaScript
|
|
||||||
- just base64 decode
|
|
||||||
- everything except Canvas
|
|
||||||
|
|
||||||
- Full test
|
|
||||||
- Without WebSockets
|
|
||||||
- With replay from python tester
|
|
||||||
- add higher-resolution multi test
|
|
||||||
|
|
||||||
- websockify test with echo and playback functionality
|
|
||||||
- choosen by client test page on connect
|
|
||||||
|
|
||||||
|
|
||||||
- Keyboard layout/internationalization support
|
|
||||||
- convert keyCode into proper charCode
|
|
||||||
|
|
||||||
- IE 9 improvements.
|
|
||||||
- https://github.com/gimite/web-socket-js/issues#issue/41
|
|
||||||
- try window.atob instead of decode for better performance
|
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
Medium Term:
|
|
||||||
|
|
||||||
- Viewport support
|
|
||||||
|
|
||||||
- Touchscreen testing/support.
|
|
||||||
@@ -1 +1 @@
|
|||||||
0.1
|
0.4
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
- Update and commit docs/VERSION and debian/changelog
|
||||||
|
- Create version tag and tarball from tag
|
||||||
|
WVER=0.3
|
||||||
|
git tag v${WVER}
|
||||||
|
git push origin master v${WVER}
|
||||||
|
git archive --format=tar --prefix=novnc-${WVER}/ v${WVER} > novnc-${WVER}.tar
|
||||||
|
gzip novnc-${WVER}.tar
|
||||||
|
- Upload tarball to repo
|
||||||
@@ -1,687 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
id="svg2"
|
|
||||||
sodipodi:version="0.32"
|
|
||||||
inkscape:version="0.47pre4 r22446"
|
|
||||||
version="1.0"
|
|
||||||
sodipodi:docname="noVNC_Logo_13.svg"
|
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_Logo12.png"
|
|
||||||
inkscape:export-xdpi="90"
|
|
||||||
inkscape:export-ydpi="90"
|
|
||||||
style="display:inline">
|
|
||||||
<defs
|
|
||||||
id="defs4">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient7584">
|
|
||||||
<stop
|
|
||||||
id="stop7586"
|
|
||||||
offset="0"
|
|
||||||
style="stop-color:#171717;stop-opacity:1;" />
|
|
||||||
<stop
|
|
||||||
id="stop7588"
|
|
||||||
offset="1"
|
|
||||||
style="stop-color:#000000;stop-opacity:1;" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient5965">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#030303;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop5967" />
|
|
||||||
<stop
|
|
||||||
id="stop5973"
|
|
||||||
offset="0.5"
|
|
||||||
style="stop-color:#090909;stop-opacity:1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop5969" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient5932">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:0.87450981;"
|
|
||||||
offset="0"
|
|
||||||
id="stop5934" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop5936" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient5333">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0.05;"
|
|
||||||
offset="0"
|
|
||||||
id="stop5335" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#4c4c4c;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop5337" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4694">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0.03529412;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4696" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#4c4c4c;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4698" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6989">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop6991" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#0f0f0f;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop6993" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6790">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#434343;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop6792" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop6794" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6736">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop6738" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop6740" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6720">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#303030;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop6722" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop6724" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6702">
|
|
||||||
<stop
|
|
||||||
id="stop4688"
|
|
||||||
offset="0"
|
|
||||||
style="stop-color:#000000;stop-opacity:0;" />
|
|
||||||
<stop
|
|
||||||
id="stop6714"
|
|
||||||
offset="1"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient6662">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#010000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop6664" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop6666" />
|
|
||||||
</linearGradient>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Send"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="Arrow1Send"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path4730"
|
|
||||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
|
||||||
transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Mstart"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="Arrow1Mstart"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path4721"
|
|
||||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
|
||||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow1Lstart"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="Arrow1Lstart"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
id="path4715"
|
|
||||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
|
||||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
|
||||||
transform="matrix(0.8,0,0,0.8,10,0)" />
|
|
||||||
</marker>
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="-24.105461 : 505.9325 : 1"
|
|
||||||
inkscape:vp_y="6.1230318e-14 : 1000 : 0"
|
|
||||||
inkscape:vp_z="719.98902 : 505.9325 : 1"
|
|
||||||
inkscape:persp3d-origin="347.94178 : 330.5388 : 1"
|
|
||||||
id="perspective10" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient6720"
|
|
||||||
id="linearGradient6742"
|
|
||||||
x1="341.66687"
|
|
||||||
y1="-253.40732"
|
|
||||||
x2="470.19839"
|
|
||||||
y2="-253.40732"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.874106,0,0,1.0453135,43.679874,11.482781)" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient6790"
|
|
||||||
id="linearGradient6796"
|
|
||||||
x1="-32.526917"
|
|
||||||
y1="294.91821"
|
|
||||||
x2="653.36664"
|
|
||||||
y2="294.91821"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient7584"
|
|
||||||
id="linearGradient6987"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.6520109,0,0,1.5543006,133.44336,136.96388)"
|
|
||||||
x1="341.66687"
|
|
||||||
y1="-253.40732"
|
|
||||||
x2="470.19839"
|
|
||||||
y2="-253.40732" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient6702"
|
|
||||||
id="linearGradient4686"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="159.54247"
|
|
||||||
y1="443.13766"
|
|
||||||
x2="430.90103"
|
|
||||||
y2="443.13766" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4694"
|
|
||||||
id="linearGradient4700"
|
|
||||||
x1="229.03033"
|
|
||||||
y1="461.81128"
|
|
||||||
x2="393.28391"
|
|
||||||
y2="461.81128"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4694"
|
|
||||||
id="linearGradient4966"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="229.03033"
|
|
||||||
y1="461.81128"
|
|
||||||
x2="393.28391"
|
|
||||||
y2="461.81128"
|
|
||||||
spreadMethod="reflect" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5333"
|
|
||||||
id="linearGradient5339"
|
|
||||||
x1="271.28635"
|
|
||||||
y1="294.12686"
|
|
||||||
x2="490.23206"
|
|
||||||
y2="294.12686"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5932"
|
|
||||||
id="linearGradient5938"
|
|
||||||
x1="-245.06445"
|
|
||||||
y1="7.705513"
|
|
||||||
x2="-62.328537"
|
|
||||||
y2="7.705513"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(2.5245666,0,0,2.6282551,644.92825,162.55033)" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5965"
|
|
||||||
id="linearGradient5971"
|
|
||||||
x1="159.54247"
|
|
||||||
y1="443.13766"
|
|
||||||
x2="430.90103"
|
|
||||||
y2="443.13766"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
spreadMethod="pad" />
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient6720"
|
|
||||||
id="radialGradient7582"
|
|
||||||
cx="295.22174"
|
|
||||||
cy="443.13766"
|
|
||||||
fx="295.22174"
|
|
||||||
fy="443.13766"
|
|
||||||
r="135.67928"
|
|
||||||
gradientTransform="matrix(1,0,0,0.1356784,0,383.01346)"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient6720"
|
|
||||||
id="radialGradient7611"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1,0,0,0.1356784,0,383.01346)"
|
|
||||||
cx="295.22174"
|
|
||||||
cy="443.13766"
|
|
||||||
fx="295.22174"
|
|
||||||
fy="443.13766"
|
|
||||||
r="135.67928" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4694"
|
|
||||||
id="linearGradient7613"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
spreadMethod="reflect"
|
|
||||||
x1="229.03033"
|
|
||||||
y1="461.81128"
|
|
||||||
x2="393.28391"
|
|
||||||
y2="461.81128" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5333"
|
|
||||||
id="linearGradient2913"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="271.28635"
|
|
||||||
y1="294.12686"
|
|
||||||
x2="490.23206"
|
|
||||||
y2="294.12686"
|
|
||||||
gradientTransform="matrix(1.0217389,0,0,1.0105488,-8.1719712,-6.8195787)" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5932"
|
|
||||||
id="linearGradient2916"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(2.5891218,0,0,2.7050059,653.14704,159.9845)"
|
|
||||||
x1="-245.06445"
|
|
||||||
y1="7.705513"
|
|
||||||
x2="-62.328537"
|
|
||||||
y2="7.705513" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective2920"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
gridtolerance="10000"
|
|
||||||
guidetolerance="10"
|
|
||||||
objecttolerance="10"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="1"
|
|
||||||
inkscape:cx="198.11269"
|
|
||||||
inkscape:cy="276.37194"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="svg2"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="1026"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
showborder="false"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer2"
|
|
||||||
inkscape:label="Screen"
|
|
||||||
style="display:inline">
|
|
||||||
<rect
|
|
||||||
style="fill:#303030;fill-opacity:1;stroke:none;display:inline"
|
|
||||||
id="rect6684"
|
|
||||||
width="473.59906"
|
|
||||||
height="321.92017"
|
|
||||||
x="18.594318"
|
|
||||||
y="19.852146"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer15"
|
|
||||||
inkscape:label="Favicon_Box"
|
|
||||||
style="display:none">
|
|
||||||
<rect
|
|
||||||
style="opacity:0;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
|
||||||
id="rect3899"
|
|
||||||
width="342.5874"
|
|
||||||
height="342.62888"
|
|
||||||
x="86.710419"
|
|
||||||
y="14.318709"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
|
||||||
id="rect3899-5"
|
|
||||||
width="342.5874"
|
|
||||||
height="342.62888"
|
|
||||||
x="86.710419"
|
|
||||||
y="14.31871"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer6"
|
|
||||||
inkscape:label="Base"
|
|
||||||
style="display:inline">
|
|
||||||
<path
|
|
||||||
sodipodi:type="arc"
|
|
||||||
style="fill:url(#linearGradient5971);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path6944"
|
|
||||||
sodipodi:cx="295.22174"
|
|
||||||
sodipodi:cy="443.13766"
|
|
||||||
sodipodi:rx="135.67928"
|
|
||||||
sodipodi:ry="18.408747"
|
|
||||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
|
||||||
transform="matrix(0.01302703,-0.3700754,8.066322,-0.0237182,-3322.837,581.39212)" />
|
|
||||||
<path
|
|
||||||
sodipodi:type="arc"
|
|
||||||
style="fill:url(#linearGradient4686);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
|
||||||
id="path4684"
|
|
||||||
sodipodi:cx="295.22174"
|
|
||||||
sodipodi:cy="443.13766"
|
|
||||||
sodipodi:rx="135.67928"
|
|
||||||
sodipodi:ry="18.408747"
|
|
||||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
|
||||||
transform="matrix(0.01219366,-0.2777519,7.5503003,-0.01780117,-2984.4223,654.88677)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer14"
|
|
||||||
inkscape:label="BaseHighlight"
|
|
||||||
style="display:inline">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#0f0626;stroke-width:3.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.9254902;stroke-dasharray:none;marker-mid:none"
|
|
||||||
d="m 113.22547,451.27721 c -6.48144,25.47482 27.77284,36.00307 33.46034,37.48107 75.48368,19.61581 142.61203,18.18957 201.82073,2.0728 19.20235,-5.22693 38.29352,-12.81118 45.54545,-22.87742 8.91645,-12.37672 -0.92272,-23.26137 -0.92272,-23.26137"
|
|
||||||
id="path5997"
|
|
||||||
sodipodi:nodetypes="csssc" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer10"
|
|
||||||
inkscape:label="BaseShadow_1"
|
|
||||||
style="display:inline">
|
|
||||||
<g
|
|
||||||
id="g7591"
|
|
||||||
transform="translate(0,-2.1213203)">
|
|
||||||
<path
|
|
||||||
transform="matrix(0.01219366,-0.2777519,7.5503003,-0.01780117,-3095.3084,545.23419)"
|
|
||||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
|
||||||
sodipodi:ry="18.408747"
|
|
||||||
sodipodi:rx="135.67928"
|
|
||||||
sodipodi:cy="443.13766"
|
|
||||||
sodipodi:cx="295.22174"
|
|
||||||
id="path6716"
|
|
||||||
style="fill:url(#radialGradient7611);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cscc"
|
|
||||||
id="path4964"
|
|
||||||
d="m 316.59702,421.17478 c 0,0 58.23544,7.94555 73.90264,26.394 24.79157,29.19259 -118.30811,67.5501 -242.96933,33.90089 213.50314,-1.39087 169.09283,-60.31728 169.09283,-60.31728"
|
|
||||||
style="fill:url(#linearGradient7613);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer5"
|
|
||||||
inkscape:label="Support"
|
|
||||||
style="display:inline">
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0.98453603;fill-rule:nonzero;stroke:url(#linearGradient6987);stroke-width:30;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
|
||||||
id="rect6686"
|
|
||||||
width="72.335358"
|
|
||||||
height="30.264278"
|
|
||||||
x="361.94818"
|
|
||||||
y="-272.03949"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer13"
|
|
||||||
inkscape:label="ScreenShade"
|
|
||||||
style="display:inline">
|
|
||||||
<rect
|
|
||||||
style="fill:url(#linearGradient2916);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="rect5930"
|
|
||||||
width="473.12555"
|
|
||||||
height="321.87091"
|
|
||||||
x="18.64533"
|
|
||||||
y="19.892525"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
<path
|
|
||||||
style="fill:url(#linearGradient2913);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
|
|
||||||
d="m 492.23643,341.65103 -222.74382,-0.21858 c 0,0 42.71861,-8.28882 90.56766,-25.15824 28.31396,-9.98224 57.59439,-24.1182 83.13673,-38.1986 45.98594,-25.35007 49.03937,-38.90668 49.03937,-38.90668 l 6e-5,102.4821 z"
|
|
||||||
id="path5331"
|
|
||||||
sodipodi:nodetypes="ccsscc"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer9"
|
|
||||||
inkscape:label="noVNC_Black"
|
|
||||||
style="display:inline">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:22.07362747;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 416.69139,186.74564 c -77.95059,0 -77.95059,0 -77.95059,0 l 0,73.23957 80.00192,0"
|
|
||||||
id="path5134"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:22.07362747;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 221.52501,259.96552 0,-74.30876 75.89924,74.67652 0,-73.6827"
|
|
||||||
id="path5132"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
<g
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:25.32735443;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
id="g5128"
|
|
||||||
transform="matrix(1.0637273,0,0,0.7140646,-87.846677,11.859872)"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
id="path5130"
|
|
||||||
d="m 181.27307,243.94727 0,42.42561 69.52825,61.54333 0,-103.59063"
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:25.32735443;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:21.4314537;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 106.33831,159.40077 0,-72.146954 73.69116,72.504004 0,-71.539108"
|
|
||||||
id="path5136"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:21.4314537;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 219.23536,92.259872 0,71.123858 77.68903,0 -2.0092,-71.123858 c -75.6828,0 -75.67983,0 -75.67983,0 z"
|
|
||||||
id="path5138"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
|
||||||
inkscape:export-xdpi="121.61401"
|
|
||||||
inkscape:export-ydpi="121.61401" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer4"
|
|
||||||
inkscape:label="noVNC"
|
|
||||||
style="display:inline">
|
|
||||||
<g
|
|
||||||
style="fill:none;display:inline"
|
|
||||||
id="g5142"
|
|
||||||
transform="matrix(1.0637273,0,0,0.7140646,-87.924897,11.633929)"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
id="path5144"
|
|
||||||
d="m 181.27307,243.94727 0,42.42561 69.52825,61.54333 0,-103.59063"
|
|
||||||
style="fill:none;stroke:#f2eb41;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#f2eb41;stroke-width:17.29026794;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 221.44679,259.73958 0,-74.30877 75.89925,74.67651 0,-73.68269"
|
|
||||||
id="path5146"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#f2eb41;stroke-width:17.29026794;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 416.61319,186.51969 c -77.95061,0 -77.95061,0 -77.95061,0 l 0,73.23958 80.00193,0"
|
|
||||||
id="path5148"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
style="fill:none;stroke:#00df00;stroke-width:16.78725243;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 106.26238,159.1814 0,-72.146955 73.69115,72.503995 0,-71.539084"
|
|
||||||
id="path5150"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#00df00;stroke-width:16.78725243;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 219.15941,92.040508 0,71.123862 77.68904,0 -2.00919,-71.123862 c -75.68283,0 -75.67985,0 -75.67985,0 z"
|
|
||||||
id="path5152"
|
|
||||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
|
||||||
inkscape:export-xdpi="4.1999998"
|
|
||||||
inkscape:export-ydpi="4.1999998" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer7"
|
|
||||||
inkscape:label="noVNC_Save"
|
|
||||||
style="display:none">
|
|
||||||
<g
|
|
||||||
style="display:inline"
|
|
||||||
id="g3903"
|
|
||||||
transform="matrix(0.9560363,0,0,0.9560363,-6.710991,-337.27245)">
|
|
||||||
<g
|
|
||||||
transform="matrix(1.0081199,0,0,1.0081199,-11.487266,-121.24134)"
|
|
||||||
id="g3905"
|
|
||||||
style="fill:none">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
d="m 83.455645,199.50003 101.674295,0"
|
|
||||||
id="path3907"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
d="m 81.687109,243.94727 99.585961,0 0,42.42561 79.06591,61.54333 0,-103.59063"
|
|
||||||
id="path3909"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
d="M 87.323416,283.46826 175.10772,160.81511"
|
|
||||||
id="path3911"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
id="path3913"
|
|
||||||
d="m 289.84756,229.03542 0,-104.90947 71.93153,105.42866 0,-104.02559"
|
|
||||||
style="fill:none;stroke:#00ff00;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
id="path3915"
|
|
||||||
d="m 472.59255,125.66322 c -73.87564,0 -73.87564,0 -73.87564,0 l 0,103.4 75.81973,0"
|
|
||||||
style="fill:none;stroke:#00ff00;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer3"
|
|
||||||
inkscape:label="Bezel"
|
|
||||||
style="display:inline">
|
|
||||||
<rect
|
|
||||||
style="fill:none;stroke:#040404;stroke-width:24.66699982;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
|
||||||
id="rect6660"
|
|
||||||
width="485.7326"
|
|
||||||
height="337.33023"
|
|
||||||
x="14.179501"
|
|
||||||
y="14.179514" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer8"
|
|
||||||
inkscape:label="BezelHighlight"
|
|
||||||
style="display:inline">
|
|
||||||
<rect
|
|
||||||
style="fill:none;stroke:#100626;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.89803922;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
|
||||||
id="rect3888"
|
|
||||||
width="485.7326"
|
|
||||||
height="337.33023"
|
|
||||||
x="13.819346"
|
|
||||||
y="14.179423" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 501 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 963 B |
|
Before Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 511 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 513 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* noVNC base CSS
|
||||||
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* noVNC is licensed under the LGPL-3 (see LICENSE.txt)
|
||||||
|
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
font-family: Helvetica;
|
||||||
|
/*Background image with light grey curve.*/
|
||||||
|
background-color:#494949;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:right bottom;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_controls ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
#noVNC_controls li {
|
||||||
|
padding-bottom:8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_host {
|
||||||
|
width:150px;
|
||||||
|
}
|
||||||
|
#noVNC_port {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
#noVNC_password {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
#noVNC_encrypt {
|
||||||
|
}
|
||||||
|
#noVNC_connectTimeout {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
#noVNC_path {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
#noVNC_connect_button {
|
||||||
|
width: 110px;
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#noVNC_view_drag_button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#sendCtrlAltDelButton {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#noVNC_mobile_buttons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC-buttons-left {
|
||||||
|
float: left;
|
||||||
|
padding-left:10px;
|
||||||
|
padding-top:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC-buttons-right {
|
||||||
|
float:right;
|
||||||
|
right: 0px;
|
||||||
|
padding-right:10px;
|
||||||
|
padding-top:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_status_bar {
|
||||||
|
margin-top: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_status_bar div {
|
||||||
|
font-size: 12px;
|
||||||
|
padding-top: 4px;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_status {
|
||||||
|
height:20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#noVNC_settings_menu {
|
||||||
|
margin: 3px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#noVNC_settings_menu ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_apply {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_status_normal {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
.noVNC_status_error {
|
||||||
|
background: #f44;
|
||||||
|
}
|
||||||
|
.noVNC_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 */
|
||||||
|
#noVNC_screen_pad {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
#noVNC_screen {
|
||||||
|
text-align: center;
|
||||||
|
display: table;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
background-color:#313131;
|
||||||
|
border-bottom-right-radius: 800px 600px;
|
||||||
|
/*border-top-left-radius: 800px 600px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_container, #noVNC_canvas {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_canvas {
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#VNC_clipboard_clear_button {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
#VNC_clipboard_text {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_clipboard_clear_button {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Bubble contents divs*/
|
||||||
|
#noVNC_settings {
|
||||||
|
display:none;
|
||||||
|
margin-top:77px;
|
||||||
|
right:20px;
|
||||||
|
position:fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_controls {
|
||||||
|
display:none;
|
||||||
|
margin-top:77px;
|
||||||
|
right:12px;
|
||||||
|
position:fixed;
|
||||||
|
}
|
||||||
|
#noVNC_controls.top:after {
|
||||||
|
right:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_description {
|
||||||
|
display:none;
|
||||||
|
position:fixed;
|
||||||
|
|
||||||
|
margin-top:77px;
|
||||||
|
right:20px;
|
||||||
|
left:20px;
|
||||||
|
padding:15px;
|
||||||
|
color:#000;
|
||||||
|
background:#eee; /* default background for browsers without gradient support */
|
||||||
|
|
||||||
|
border:2px solid #E0E0E0;
|
||||||
|
-webkit-border-radius:10px;
|
||||||
|
-moz-border-radius:10px;
|
||||||
|
border-radius:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_clipboard {
|
||||||
|
display:none;
|
||||||
|
margin-top:77px;
|
||||||
|
right:30px;
|
||||||
|
position:fixed;
|
||||||
|
}
|
||||||
|
#noVNC_clipboard.top:after {
|
||||||
|
right:85px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyboardinput {
|
||||||
|
width:1px;
|
||||||
|
height:1px;
|
||||||
|
background-color:#fff;
|
||||||
|
color:#fff;
|
||||||
|
border:0;
|
||||||
|
position: relative;
|
||||||
|
left: -40px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_status_warn {
|
||||||
|
background-color:yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Advanced Styling
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Control bar */
|
||||||
|
#noVNC-control-bar {
|
||||||
|
position:fixed;
|
||||||
|
background: #b2bdcd; /* Old browsers */
|
||||||
|
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||||
|
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||||
|
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||||
|
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||||
|
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||||
|
|
||||||
|
display:block;
|
||||||
|
height:44px;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
width:100%;
|
||||||
|
z-index:200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_status_button {
|
||||||
|
padding: 4px 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border:1px solid #869dbc;
|
||||||
|
-webkit-border-radius: 6px;
|
||||||
|
-moz-border-radius: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #b2bdcd; /* Old browsers */
|
||||||
|
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||||
|
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||||
|
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||||
|
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */
|
||||||
|
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||||
|
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_status_button_selected {
|
||||||
|
padding: 4px 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border:1px solid #4366a9;
|
||||||
|
-webkit-border-radius: 6px;
|
||||||
|
-moz-border-radius: 6px;
|
||||||
|
background: #779ced; /* Old browsers */
|
||||||
|
background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */
|
||||||
|
background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */
|
||||||
|
background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */
|
||||||
|
background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */
|
||||||
|
background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */
|
||||||
|
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*Settings Bubble*/
|
||||||
|
.triangle-right {
|
||||||
|
position:relative;
|
||||||
|
padding:15px;
|
||||||
|
margin:1em 0 3em;
|
||||||
|
color:#fff;
|
||||||
|
background:#fff; /* default background for browsers without gradient support */
|
||||||
|
/* css3 */
|
||||||
|
/*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698));
|
||||||
|
background:-moz-linear-gradient(#2e88c4, #075698);
|
||||||
|
background:-o-linear-gradient(#2e88c4, #075698);
|
||||||
|
background:linear-gradient(#2e88c4, #075698);*/
|
||||||
|
-webkit-border-radius:10px;
|
||||||
|
-moz-border-radius:10px;
|
||||||
|
border-radius:10px;
|
||||||
|
color:#000;
|
||||||
|
border:2px solid #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle-right.top:after {
|
||||||
|
border-color: transparent #E0E0E0;
|
||||||
|
border-width: 20px 20px 0 0;
|
||||||
|
bottom: auto;
|
||||||
|
left: auto;
|
||||||
|
right: 50px;
|
||||||
|
top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle-right:after {
|
||||||
|
content:"";
|
||||||
|
position:absolute;
|
||||||
|
bottom:-20px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
left:50px; /* controls horizontal position */
|
||||||
|
border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */
|
||||||
|
border-style:solid;
|
||||||
|
border-color:#E0E0E0 transparent;
|
||||||
|
/* reduce the damage in FF3.0 */
|
||||||
|
display:block;
|
||||||
|
width:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle-right.top:after {
|
||||||
|
top:-40px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
right:50px; /* controls horizontal position */
|
||||||
|
bottom:auto;
|
||||||
|
left:auto;
|
||||||
|
border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */
|
||||||
|
border-color:transparent #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Default noVNC logo.*/
|
||||||
|
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Orbitron';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('?'), url('Orbitron700.woff') format('woff'),
|
||||||
|
url('Orbitron700.ttf') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_logo {
|
||||||
|
margin-top: 170px;
|
||||||
|
margin-left: 10px;
|
||||||
|
color:yellow;
|
||||||
|
text-align:left;
|
||||||
|
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
|
||||||
|
line-height:90%;
|
||||||
|
text-shadow:
|
||||||
|
5px 5px 0 #000,
|
||||||
|
-1px -1px 0 #000,
|
||||||
|
1px -1px 0 #000,
|
||||||
|
-1px 1px 0 #000,
|
||||||
|
1px 1px 0 #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#noVNC_logo span{
|
||||||
|
color:green;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------
|
||||||
|
* Media sizing
|
||||||
|
* ----------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
.noVNC_status_button {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_clipboard_text {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_logo {
|
||||||
|
font-size: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 481px) and (max-width: 640px) {
|
||||||
|
.noVNC_status_button {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
#noVNC_clipboard_text {
|
||||||
|
width: 410px;
|
||||||
|
}
|
||||||
|
#noVNC_logo {
|
||||||
|
font-size: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 321px) and (max-width: 480px) {
|
||||||
|
.noVNC_status_button {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
#noVNC_clipboard_text {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
#noVNC_logo {
|
||||||
|
font-size: 110px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 320px) {
|
||||||
|
.noVNC_status_button {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
#noVNC_clipboard_text {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
#noVNC_logo {
|
||||||
|
font-size: 90px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -116,7 +116,7 @@ decode: function (data, offset) {
|
|||||||
padding = (data.charAt(i) === pad);
|
padding = (data.charAt(i) === pad);
|
||||||
// Skip illegal characters and whitespace
|
// Skip illegal characters and whitespace
|
||||||
if (c === -1) {
|
if (c === -1) {
|
||||||
console.error("Illegal character '" + data.charCodeAt(i) + "'");
|
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +1,52 @@
|
|||||||
body {
|
/*
|
||||||
margin: 0px;
|
* noVNC base CSS
|
||||||
font-size: 13px;
|
* Copyright (C) 2012 Joel Martin
|
||||||
color: #111;
|
* noVNC is licensed under the LGPL-3 (see LICENSE.txt)
|
||||||
font-family: "Helvetica";
|
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#keyboardinput {
|
||||||
|
background-color:#000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#VNC_controls {
|
#noVNC-control-bar {
|
||||||
background: #111;
|
background: #4c4c4c; /* Old browsers */
|
||||||
line-height: 1em;
|
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||||
color: #FFF;
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||||
overflow: hidden;
|
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||||
padding: 4px 24px;
|
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||||
|
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||||
|
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||||
}
|
}
|
||||||
|
|
||||||
#VNC_controls ul {
|
.triangle-right {
|
||||||
list-style:none;
|
border:2px solid #fff;
|
||||||
list-style-position: outside;
|
background:#000;
|
||||||
margin: 0px;
|
color:#fff;
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#VNC_controls li {
|
|
||||||
margin-right: 15px;
|
|
||||||
padding: 2px 0px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
#VNC_controls li input[type=text],
|
|
||||||
#VNC_controls li input[type=password] {
|
|
||||||
border: 2px solid #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#VNC_host {
|
.noVNC_status_button {
|
||||||
width: 100px;
|
font-size: 12px;
|
||||||
}
|
vertical-align: middle;
|
||||||
#VNC_port {
|
border:1px solid #4c4c4c;
|
||||||
width: 50px;
|
|
||||||
}
|
background: #4c4c4c; /* Old browsers */
|
||||||
#VNC_password {
|
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||||
width: 80px;
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||||
}
|
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||||
#VNC_encrypt {
|
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||||
}
|
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||||
#VNC_connect_button {
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
|
||||||
width: 100px;
|
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||||
}
|
}
|
||||||
|
|
||||||
#VNC_status_bar td {
|
.noVNC_status_button_selected {
|
||||||
padding: 0px;
|
background: #9dd53a; /* Old browsers */
|
||||||
margin: 0px;
|
background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */
|
||||||
}
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */
|
||||||
#VNC_status_bar div {
|
background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */
|
||||||
font-size: 12px;
|
background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */
|
||||||
font-weight: bold;
|
background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */
|
||||||
text-align: center;
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */
|
||||||
margin: 0px;
|
background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */
|
||||||
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: 13em;
|
|
||||||
border: 1px solid #888;
|
|
||||||
color: #111;
|
|
||||||
font-weight: normal;
|
|
||||||
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: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* noVNC base CSS
|
||||||
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* noVNC is licensed under the LGPL-3 (see LICENSE.txt)
|
||||||
|
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#noVNC-control-bar {
|
||||||
|
background-color:#04073d;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
left bottom,
|
||||||
|
left top,
|
||||||
|
color-stop(0.54, rgb(10,15,79)),
|
||||||
|
color-stop(0.5, rgb(4,7,61))
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center bottom,
|
||||||
|
rgb(10,15,79) 54%,
|
||||||
|
rgb(4,7,61) 50%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle-right {
|
||||||
|
border:2px solid #fff;
|
||||||
|
background:#04073d;
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyboardinput {
|
||||||
|
background-color:#04073d;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
@@ -19,19 +19,26 @@ var that = {}, // Public API methods
|
|||||||
c_ctx = null,
|
c_ctx = null,
|
||||||
c_forceCanvas = false,
|
c_forceCanvas = false,
|
||||||
|
|
||||||
c_imageData, c_rgbxImage, c_cmapImage,
|
// Queued drawing actions for in-order rendering
|
||||||
|
renderQ = [],
|
||||||
|
|
||||||
// Predefine function variables (jslint)
|
// Predefine function variables (jslint)
|
||||||
imageDataCreate, imageDataGet, rgbxImageData, cmapImageData,
|
imageDataGet, rgbImageData, bgrxImageData, cmapImageData,
|
||||||
rgbxImageFill, cmapImageFill, setFillColor, rescale, flush,
|
setFillColor, rescale, scan_renderQ,
|
||||||
|
|
||||||
c_width = 0,
|
// The full frame buffer (logical canvas) size
|
||||||
c_height = 0,
|
fb_width = 0,
|
||||||
|
fb_height = 0,
|
||||||
|
// The visible "physical canvas" viewport
|
||||||
|
viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 },
|
||||||
|
cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1},
|
||||||
|
|
||||||
c_prevStyle = "",
|
c_prevStyle = "",
|
||||||
|
tile = null,
|
||||||
|
tile16x16 = null,
|
||||||
|
tile_x = 0,
|
||||||
|
tile_y = 0;
|
||||||
|
|
||||||
c_webkit_bug = false,
|
|
||||||
c_flush_timer = null;
|
|
||||||
|
|
||||||
// Configuration attributes
|
// Configuration attributes
|
||||||
Util.conf_defaults(conf, that, defaults, [
|
Util.conf_defaults(conf, that, defaults, [
|
||||||
@@ -41,6 +48,7 @@ Util.conf_defaults(conf, that, defaults, [
|
|||||||
['true_color', 'rw', 'bool', true, 'Use true-color pixel data'],
|
['true_color', 'rw', 'bool', true, 'Use true-color pixel data'],
|
||||||
['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'],
|
['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'],
|
||||||
['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'],
|
['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'],
|
||||||
|
['viewport', 'rw', 'bool', false, 'Use a viewport set with viewportChange()'],
|
||||||
['width', 'rw', 'int', null, 'Display area width'],
|
['width', 'rw', 'int', null, 'Display area width'],
|
||||||
['height', 'rw', 'int', null, 'Display area height'],
|
['height', 'rw', 'int', null, 'Display area height'],
|
||||||
|
|
||||||
@@ -55,20 +63,11 @@ that.get_context = function () { return c_ctx; };
|
|||||||
|
|
||||||
that.set_scale = function(scale) { rescale(scale); };
|
that.set_scale = function(scale) { rescale(scale); };
|
||||||
|
|
||||||
that.set_width = function (val) { that.resize(val, c_height); };
|
that.set_width = function (val) { that.resize(val, fb_height); };
|
||||||
that.get_width = function() { return c_width; };
|
that.get_width = function() { return fb_width; };
|
||||||
|
|
||||||
that.set_height = function (val) { that.resize(c_width, val); };
|
that.set_height = function (val) { that.resize(fb_width, val); };
|
||||||
that.get_height = function() { return c_height; };
|
that.get_height = function() { return fb_height; };
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ that.set_prefer_js = function(val) {
|
|||||||
function constructor() {
|
function constructor() {
|
||||||
Util.Debug(">> Display.constructor");
|
Util.Debug(">> Display.constructor");
|
||||||
|
|
||||||
var c, func, imgTest, tval, i, curDat, curSave,
|
var c, func, i, curDat, curSave,
|
||||||
has_imageData = false, UE = Util.Engine;
|
has_imageData = false, UE = Util.Engine;
|
||||||
|
|
||||||
if (! conf.target) { throw("target must be set"); }
|
if (! conf.target) { throw("target must be set"); }
|
||||||
@@ -103,70 +102,19 @@ function constructor() {
|
|||||||
|
|
||||||
that.clear();
|
that.clear();
|
||||||
|
|
||||||
/*
|
// Check canvas features
|
||||||
* Determine browser Canvas feature support
|
if ('createImageData' in c_ctx) {
|
||||||
* and select fastest rendering methods
|
conf.render_mode = "canvas rendering";
|
||||||
*/
|
|
||||||
tval = 0;
|
|
||||||
try {
|
|
||||||
imgTest = c_ctx.getImageData(0, 0, 1,1);
|
|
||||||
imgTest.data[0] = 123;
|
|
||||||
imgTest.data[3] = 255;
|
|
||||||
c_ctx.putImageData(imgTest, 0, 0);
|
|
||||||
tval = c_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 (c_ctx.createImageData) {
|
|
||||||
// If it's there, it's faster
|
|
||||||
Util.Info("Using Canvas createImageData");
|
|
||||||
conf.render_mode = "createImageData rendering";
|
|
||||||
c_imageData = imageDataCreate;
|
|
||||||
} else if (c_ctx.getImageData) {
|
|
||||||
// I think this is mostly just Opera
|
|
||||||
Util.Info("Using Canvas getImageData");
|
|
||||||
conf.render_mode = "getImageData rendering";
|
|
||||||
c_imageData = imageDataGet;
|
|
||||||
}
|
|
||||||
Util.Info("Prefering javascript operations");
|
|
||||||
if (conf.prefer_js === null) {
|
|
||||||
conf.prefer_js = true;
|
|
||||||
}
|
|
||||||
c_rgbxImage = rgbxImageData;
|
|
||||||
c_cmapImage = cmapImageData;
|
|
||||||
} else {
|
} else {
|
||||||
Util.Warn("Canvas lacks imageData, using fillRect (slow)");
|
throw("Canvas does not support createImageData");
|
||||||
conf.render_mode = "fillRect rendering (slow)";
|
}
|
||||||
c_forceCanvas = true;
|
if (conf.prefer_js === null) {
|
||||||
conf.prefer_js = false;
|
Util.Info("Prefering javascript operations");
|
||||||
c_rgbxImage = rgbxImageFill;
|
conf.prefer_js = true;
|
||||||
c_cmapImage = cmapImageFill;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) {
|
// Initialize cached tile imageData
|
||||||
// Workaround WebKit canvas rendering bug #46319
|
tile16x16 = c_ctx.createImageData(16, 16);
|
||||||
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(flush, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine browser support for setting the cursor via data URI
|
* Determine browser support for setting the cursor via data URI
|
||||||
@@ -217,7 +165,10 @@ rescale = function(factor) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (factor > 1.0) {
|
|
||||||
|
if (typeof(factor) === "undefined") {
|
||||||
|
factor = conf.scale;
|
||||||
|
} else if (factor > 1.0) {
|
||||||
factor = 1.0;
|
factor = 1.0;
|
||||||
} else if (factor < 0.1) {
|
} else if (factor < 0.1) {
|
||||||
factor = 0.1;
|
factor = 0.1;
|
||||||
@@ -234,26 +185,14 @@ rescale = function(factor) {
|
|||||||
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
|
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
|
||||||
};
|
};
|
||||||
|
|
||||||
// Force canvas redraw (for webkit bug #46319 workaround)
|
|
||||||
flush = function() {
|
|
||||||
var old_val;
|
|
||||||
//Util.Debug(">> flush");
|
|
||||||
old_val = conf.target.style.marginRight;
|
|
||||||
conf.target.style.marginRight = "1px";
|
|
||||||
c_flush_timer = null;
|
|
||||||
setTimeout(function () {
|
|
||||||
conf.target.style.marginRight = old_val;
|
|
||||||
}, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
setFillColor = function(color) {
|
setFillColor = function(color) {
|
||||||
var rgb, newStyle;
|
var bgr, newStyle;
|
||||||
if (conf.true_color) {
|
if (conf.true_color) {
|
||||||
rgb = color;
|
bgr = color;
|
||||||
} else {
|
} else {
|
||||||
rgb = conf.colourMap[color[0]];
|
bgr = conf.colourMap[color[0]];
|
||||||
}
|
}
|
||||||
newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
|
newStyle = "rgb(" + bgr[2] + "," + bgr[1] + "," + bgr[0] + ")";
|
||||||
if (newStyle !== c_prevStyle) {
|
if (newStyle !== c_prevStyle) {
|
||||||
c_ctx.fillStyle = newStyle;
|
c_ctx.fillStyle = newStyle;
|
||||||
c_prevStyle = newStyle;
|
c_prevStyle = newStyle;
|
||||||
@@ -265,18 +204,205 @@ setFillColor = function(color) {
|
|||||||
// Public API interface functions
|
// Public API interface functions
|
||||||
//
|
//
|
||||||
|
|
||||||
that.resize = function(width, height) {
|
// Shift and/or resize the visible viewport
|
||||||
var c = conf.target;
|
that.viewportChange = function(deltaX, deltaY, width, height) {
|
||||||
|
var c = conf.target, v = viewport, cr = cleanRect,
|
||||||
|
saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h;
|
||||||
|
|
||||||
|
if (!conf.viewport) {
|
||||||
|
Util.Debug("Setting viewport to full display region");
|
||||||
|
deltaX = -v.w; // Clamped later if out of bounds
|
||||||
|
deltaY = -v.h; // Clamped later if out of bounds
|
||||||
|
width = fb_width;
|
||||||
|
height = fb_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(deltaX) === "undefined") { deltaX = 0; }
|
||||||
|
if (typeof(deltaY) === "undefined") { deltaY = 0; }
|
||||||
|
if (typeof(width) === "undefined") { width = v.w; }
|
||||||
|
if (typeof(height) === "undefined") { height = v.h; }
|
||||||
|
|
||||||
|
// Size change
|
||||||
|
|
||||||
|
if (width > fb_width) { width = fb_width; }
|
||||||
|
if (height > fb_height) { height = fb_height; }
|
||||||
|
|
||||||
|
if ((v.w !== width) || (v.h !== height)) {
|
||||||
|
// Change width
|
||||||
|
if ((width < v.w) && (cr.x2 > v.x + width -1)) {
|
||||||
|
cr.x2 = v.x + width - 1;
|
||||||
|
}
|
||||||
|
v.w = width;
|
||||||
|
|
||||||
|
// Change height
|
||||||
|
if ((height < v.h) && (cr.y2 > v.y + height -1)) {
|
||||||
|
cr.y2 = v.y + height - 1;
|
||||||
|
}
|
||||||
|
v.h = height;
|
||||||
|
|
||||||
|
|
||||||
|
if (v.w > 0 && v.h > 0 && c.width > 0 && c.height > 0) {
|
||||||
|
saveImg = c_ctx.getImageData(0, 0,
|
||||||
|
(c.width < v.w) ? c.width : v.w,
|
||||||
|
(c.height < v.h) ? c.height : v.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.width = v.w;
|
||||||
|
c.height = v.h;
|
||||||
|
|
||||||
|
if (saveImg) {
|
||||||
|
c_ctx.putImageData(saveImg, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vx2 = v.x + v.w - 1;
|
||||||
|
vy2 = v.y + v.h - 1;
|
||||||
|
|
||||||
|
|
||||||
|
// Position change
|
||||||
|
|
||||||
|
if ((deltaX < 0) && ((v.x + deltaX) < 0)) {
|
||||||
|
deltaX = - v.x;
|
||||||
|
}
|
||||||
|
if ((vx2 + deltaX) >= fb_width) {
|
||||||
|
deltaX -= ((vx2 + deltaX) - fb_width + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((v.y + deltaY) < 0) {
|
||||||
|
deltaY = - v.y;
|
||||||
|
}
|
||||||
|
if ((vy2 + deltaY) >= fb_height) {
|
||||||
|
deltaY -= ((vy2 + deltaY) - fb_height + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((deltaX === 0) && (deltaY === 0)) {
|
||||||
|
//Util.Debug("skipping viewport change");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||||
|
|
||||||
|
v.x += deltaX;
|
||||||
|
vx2 += deltaX;
|
||||||
|
v.y += deltaY;
|
||||||
|
vy2 += deltaY;
|
||||||
|
|
||||||
|
// Update the clean rectangle
|
||||||
|
if (v.x > cr.x1) {
|
||||||
|
cr.x1 = v.x;
|
||||||
|
}
|
||||||
|
if (vx2 < cr.x2) {
|
||||||
|
cr.x2 = vx2;
|
||||||
|
}
|
||||||
|
if (v.y > cr.y1) {
|
||||||
|
cr.y1 = v.y;
|
||||||
|
}
|
||||||
|
if (vy2 < cr.y2) {
|
||||||
|
cr.y2 = vy2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaX < 0) {
|
||||||
|
// Shift viewport left, redraw left section
|
||||||
|
x1 = 0;
|
||||||
|
w = - deltaX;
|
||||||
|
} else {
|
||||||
|
// Shift viewport right, redraw right section
|
||||||
|
x1 = v.w - deltaX;
|
||||||
|
w = deltaX;
|
||||||
|
}
|
||||||
|
if (deltaY < 0) {
|
||||||
|
// Shift viewport up, redraw top section
|
||||||
|
y1 = 0;
|
||||||
|
h = - deltaY;
|
||||||
|
} else {
|
||||||
|
// Shift viewport down, redraw bottom section
|
||||||
|
y1 = v.h - deltaY;
|
||||||
|
h = deltaY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the valid part of the viewport to the shifted location
|
||||||
|
saveStyle = c_ctx.fillStyle;
|
||||||
|
c_ctx.fillStyle = "rgb(255,255,255)";
|
||||||
|
if (deltaX !== 0) {
|
||||||
|
//that.copyImage(0, 0, -deltaX, 0, v.w, v.h);
|
||||||
|
//that.fillRect(x1, 0, w, v.h, [255,255,255]);
|
||||||
|
c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h);
|
||||||
|
c_ctx.fillRect(x1, 0, w, v.h);
|
||||||
|
}
|
||||||
|
if (deltaY !== 0) {
|
||||||
|
//that.copyImage(0, 0, 0, -deltaY, v.w, v.h);
|
||||||
|
//that.fillRect(0, y1, v.w, h, [255,255,255]);
|
||||||
|
c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h);
|
||||||
|
c_ctx.fillRect(0, y1, v.w, h);
|
||||||
|
}
|
||||||
|
c_ctx.fillStyle = saveStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Return a map of clean and dirty areas of the viewport and reset the
|
||||||
|
// tracking of clean and dirty areas.
|
||||||
|
//
|
||||||
|
// Returns: {'cleanBox': {'x': x, 'y': y, 'w': w, 'h': h},
|
||||||
|
// 'dirtyBoxes': [{'x': x, 'y': y, 'w': w, 'h': h}, ...]}
|
||||||
|
that.getCleanDirtyReset = function() {
|
||||||
|
var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [],
|
||||||
|
vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1;
|
||||||
|
|
||||||
|
|
||||||
|
// Copy the cleanRect
|
||||||
|
cleanBox = {'x': c.x1, 'y': c.y1,
|
||||||
|
'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1};
|
||||||
|
|
||||||
|
if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) {
|
||||||
|
// Whole viewport is dirty
|
||||||
|
dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h});
|
||||||
|
} else {
|
||||||
|
// Redraw dirty regions
|
||||||
|
if (v.x < c.x1) {
|
||||||
|
// left side dirty region
|
||||||
|
dirtyBoxes.push({'x': v.x, 'y': v.y,
|
||||||
|
'w': c.x1 - v.x + 1, 'h': v.h});
|
||||||
|
}
|
||||||
|
if (vx2 > c.x2) {
|
||||||
|
// right side dirty region
|
||||||
|
dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y,
|
||||||
|
'w': vx2 - c.x2, 'h': v.h});
|
||||||
|
}
|
||||||
|
if (v.y < c.y1) {
|
||||||
|
// top/middle dirty region
|
||||||
|
dirtyBoxes.push({'x': c.x1, 'y': v.y,
|
||||||
|
'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y});
|
||||||
|
}
|
||||||
|
if (vy2 > c.y2) {
|
||||||
|
// bottom/middle dirty region
|
||||||
|
dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1,
|
||||||
|
'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the cleanRect to the whole viewport
|
||||||
|
cleanRect = {'x1': v.x, 'y1': v.y,
|
||||||
|
'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1};
|
||||||
|
|
||||||
|
return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Translate viewport coordinates to absolute coordinates
|
||||||
|
that.absX = function(x) {
|
||||||
|
return x + viewport.x;
|
||||||
|
};
|
||||||
|
that.absY = function(y) {
|
||||||
|
return y + viewport.y;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
that.resize = function(width, height) {
|
||||||
c_prevStyle = "";
|
c_prevStyle = "";
|
||||||
|
|
||||||
c.width = width;
|
fb_width = width;
|
||||||
c.height = height;
|
fb_height = height;
|
||||||
|
|
||||||
c_width = c.offsetWidth;
|
|
||||||
c_height = c.offsetHeight;
|
|
||||||
|
|
||||||
rescale(conf.scale);
|
rescale(conf.scale);
|
||||||
|
that.viewportChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
that.clear = function() {
|
that.clear = function() {
|
||||||
@@ -286,67 +412,72 @@ that.clear = function() {
|
|||||||
that.blitStringImage(conf.logo.data, 0, 0);
|
that.blitStringImage(conf.logo.data, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
that.resize(640, 20);
|
that.resize(640, 20);
|
||||||
c_ctx.clearRect(0, 0, c_width, c_height);
|
c_ctx.clearRect(0, 0, viewport.w, viewport.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderQ = [];
|
||||||
|
|
||||||
// No benefit over default ("source-over") in Chrome and firefox
|
// No benefit over default ("source-over") in Chrome and firefox
|
||||||
//c_ctx.globalCompositeOperation = "copy";
|
//c_ctx.globalCompositeOperation = "copy";
|
||||||
};
|
};
|
||||||
|
|
||||||
that.fillRect = function(x, y, width, height, color) {
|
that.fillRect = function(x, y, width, height, color) {
|
||||||
setFillColor(color);
|
setFillColor(color);
|
||||||
c_ctx.fillRect(x, y, width, height);
|
c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.copyImage = function(old_x, old_y, new_x, new_y, width, height) {
|
that.copyImage = function(old_x, old_y, new_x, new_y, w, h) {
|
||||||
c_ctx.drawImage(conf.target, old_x, old_y, width, height,
|
var x1 = old_x - viewport.x, y1 = old_y - viewport.y,
|
||||||
new_x, new_y, width, height);
|
x2 = new_x - viewport.x, y2 = new_y - viewport.y;
|
||||||
|
c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Tile rendering functions optimized for rendering engines.
|
// Start updating a tile
|
||||||
*
|
that.startTile = function(x, y, width, height, color) {
|
||||||
* - In Chrome/webkit, Javascript image data array manipulations are
|
var data, bgr, red, green, blue, i;
|
||||||
* faster than direct Canvas fillStyle, fillRect rendering. In
|
tile_x = x;
|
||||||
* gecko, Javascript array handling is much slower.
|
tile_y = y;
|
||||||
*/
|
if ((width === 16) && (height === 16)) {
|
||||||
that.getTile = function(x, y, width, height, color) {
|
tile = tile16x16;
|
||||||
var img, data = [], rgb, red, green, blue, i;
|
} else {
|
||||||
img = {'x': x, 'y': y, 'width': width, 'height': height,
|
tile = c_ctx.createImageData(width, height);
|
||||||
'data': data};
|
}
|
||||||
|
data = tile.data;
|
||||||
if (conf.prefer_js) {
|
if (conf.prefer_js) {
|
||||||
if (conf.true_color) {
|
if (conf.true_color) {
|
||||||
rgb = color;
|
bgr = color;
|
||||||
} else {
|
} else {
|
||||||
rgb = conf.colourMap[color[0]];
|
bgr = conf.colourMap[color[0]];
|
||||||
}
|
}
|
||||||
red = rgb[0];
|
red = bgr[2];
|
||||||
green = rgb[1];
|
green = bgr[1];
|
||||||
blue = rgb[2];
|
blue = bgr[0];
|
||||||
for (i = 0; i < (width * height * 4); i+=4) {
|
for (i = 0; i < (width * height * 4); i+=4) {
|
||||||
data[i ] = red;
|
data[i ] = red;
|
||||||
data[i + 1] = green;
|
data[i + 1] = green;
|
||||||
data[i + 2] = blue;
|
data[i + 2] = blue;
|
||||||
|
data[i + 3] = 255;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
that.fillRect(x, y, width, height, color);
|
that.fillRect(x, y, width, height, color);
|
||||||
}
|
}
|
||||||
return img;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.setSubTile = function(img, x, y, w, h, color) {
|
// Update sub-rectangle of the current tile
|
||||||
var data, p, rgb, red, green, blue, width, j, i, xend, yend;
|
that.subTile = function(x, y, w, h, color) {
|
||||||
|
var data, p, bgr, red, green, blue, width, j, i, xend, yend;
|
||||||
if (conf.prefer_js) {
|
if (conf.prefer_js) {
|
||||||
data = img.data;
|
data = tile.data;
|
||||||
width = img.width;
|
width = tile.width;
|
||||||
if (conf.true_color) {
|
if (conf.true_color) {
|
||||||
rgb = color;
|
bgr = color;
|
||||||
} else {
|
} else {
|
||||||
rgb = conf.colourMap[color[0]];
|
bgr = conf.colourMap[color[0]];
|
||||||
}
|
}
|
||||||
red = rgb[0];
|
red = bgr[2];
|
||||||
green = rgb[1];
|
green = bgr[1];
|
||||||
blue = rgb[2];
|
blue = bgr[0];
|
||||||
xend = x + w;
|
xend = x + w;
|
||||||
yend = y + h;
|
yend = y + h;
|
||||||
for (j = y; j < yend; j += 1) {
|
for (j = y; j < yend; j += 1) {
|
||||||
@@ -355,96 +486,154 @@ that.setSubTile = function(img, x, y, w, h, color) {
|
|||||||
data[p ] = red;
|
data[p ] = red;
|
||||||
data[p + 1] = green;
|
data[p + 1] = green;
|
||||||
data[p + 2] = blue;
|
data[p + 2] = blue;
|
||||||
|
data[p + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
that.fillRect(img.x + x, img.y + y, w, h, color);
|
that.fillRect(tile_x + x, tile_y + y, w, h, color);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.putTile = function(img) {
|
// Draw the current tile to the screen
|
||||||
|
that.finishTile = function() {
|
||||||
if (conf.prefer_js) {
|
if (conf.prefer_js) {
|
||||||
c_rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
|
c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y);
|
||||||
}
|
}
|
||||||
// else: No-op, under gecko already done by setSubTile
|
// else: No-op, if not prefer_js then already done by setSubTile
|
||||||
};
|
};
|
||||||
|
|
||||||
imageDataGet = function(width, height) {
|
rgbImageData = function(x, y, width, height, arr, offset) {
|
||||||
return c_ctx.getImageData(0, 0, width, height);
|
var img, i, j, data, v = viewport;
|
||||||
};
|
/*
|
||||||
imageDataCreate = function(width, height) {
|
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
|
||||||
return c_ctx.createImageData(width, height);
|
(x - v.x + width < 0) || (y - v.y + height < 0)) {
|
||||||
};
|
// Skipping because outside of viewport
|
||||||
|
return;
|
||||||
rgbxImageData = function(x, y, width, height, arr, offset) {
|
}
|
||||||
var img, i, j, data;
|
*/
|
||||||
img = c_imageData(width, height);
|
img = c_ctx.createImageData(width, height);
|
||||||
data = img.data;
|
data = img.data;
|
||||||
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
|
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+3) {
|
||||||
data[i ] = arr[j ];
|
data[i ] = arr[j ];
|
||||||
data[i + 1] = arr[j + 1];
|
data[i + 1] = arr[j + 1];
|
||||||
data[i + 2] = arr[j + 2];
|
data[i + 2] = arr[j + 2];
|
||||||
data[i + 3] = 255; // Set Alpha
|
data[i + 3] = 255; // Set Alpha
|
||||||
}
|
}
|
||||||
c_ctx.putImageData(img, x, y);
|
c_ctx.putImageData(img, x - v.x, y - v.y);
|
||||||
};
|
};
|
||||||
|
|
||||||
// really slow fallback if we don't have imageData
|
bgrxImageData = function(x, y, width, height, arr, offset) {
|
||||||
rgbxImageFill = function(x, y, width, height, arr, offset) {
|
var img, i, j, data, v = viewport;
|
||||||
var i, j, sx = 0, sy = 0;
|
/*
|
||||||
for (i=0, j=offset; i < (width * height); i+=1, j+=4) {
|
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
|
||||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j], arr[j+1], arr[j+2]]);
|
(x - v.x + width < 0) || (y - v.y + height < 0)) {
|
||||||
sx += 1;
|
// Skipping because outside of viewport
|
||||||
if ((sx % width) === 0) {
|
return;
|
||||||
sx = 0;
|
|
||||||
sy += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
img = c_ctx.createImageData(width, height);
|
||||||
|
data = img.data;
|
||||||
|
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
|
||||||
|
data[i ] = arr[j + 2];
|
||||||
|
data[i + 1] = arr[j + 1];
|
||||||
|
data[i + 2] = arr[j ];
|
||||||
|
data[i + 3] = 255; // Set Alpha
|
||||||
|
}
|
||||||
|
c_ctx.putImageData(img, x - v.x, y - v.y);
|
||||||
};
|
};
|
||||||
|
|
||||||
cmapImageData = function(x, y, width, height, arr, offset) {
|
cmapImageData = function(x, y, width, height, arr, offset) {
|
||||||
var img, i, j, data, rgb, cmap;
|
var img, i, j, data, bgr, cmap;
|
||||||
img = c_imageData(width, height);
|
img = c_ctx.createImageData(width, height);
|
||||||
data = img.data;
|
data = img.data;
|
||||||
cmap = conf.colourMap;
|
cmap = conf.colourMap;
|
||||||
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
|
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
|
||||||
rgb = cmap[arr[j]];
|
bgr = cmap[arr[j]];
|
||||||
data[i ] = rgb[0];
|
data[i ] = bgr[2];
|
||||||
data[i + 1] = rgb[1];
|
data[i + 1] = bgr[1];
|
||||||
data[i + 2] = rgb[2];
|
data[i + 2] = bgr[0];
|
||||||
data[i + 3] = 255; // Set Alpha
|
data[i + 3] = 255; // Set Alpha
|
||||||
}
|
}
|
||||||
c_ctx.putImageData(img, x, y);
|
c_ctx.putImageData(img, x - viewport.x, y - viewport.y);
|
||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
that.blitImage = function(x, y, width, height, arr, offset) {
|
||||||
if (conf.true_color) {
|
if (conf.true_color) {
|
||||||
c_rgbxImage(x, y, width, height, arr, offset);
|
bgrxImageData(x, y, width, height, arr, offset);
|
||||||
} else {
|
} else {
|
||||||
c_cmapImage(x, y, width, height, arr, offset);
|
cmapImageData(x, y, width, height, arr, offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.blitRgbImage = function(x, y, width, height, arr, offset) {
|
||||||
|
if (conf.true_color) {
|
||||||
|
rgbImageData(x, y, width, height, arr, offset);
|
||||||
|
} else {
|
||||||
|
// prolly wrong...
|
||||||
|
cmapImageData(x, y, width, height, arr, offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.blitStringImage = function(str, x, y) {
|
that.blitStringImage = function(str, x, y) {
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.onload = function () { c_ctx.drawImage(img, x, y); };
|
img.onload = function () {
|
||||||
|
c_ctx.drawImage(img, x - viewport.x, y - viewport.y);
|
||||||
|
};
|
||||||
img.src = str;
|
img.src = str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wrap ctx.drawImage but relative to viewport
|
||||||
|
that.drawImage = function(img, x, y) {
|
||||||
|
c_ctx.drawImage(img, x - viewport.x, y - viewport.y);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.renderQ_push = function(action) {
|
||||||
|
renderQ.push(action);
|
||||||
|
if (renderQ.length === 1) {
|
||||||
|
// If this can be rendered immediately it will be, otherwise
|
||||||
|
// the scanner will start polling the queue (every
|
||||||
|
// requestAnimationFrame interval)
|
||||||
|
scan_renderQ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scan_renderQ = function() {
|
||||||
|
var a, ready = true;
|
||||||
|
while (ready && renderQ.length > 0) {
|
||||||
|
a = renderQ[0];
|
||||||
|
switch (a.type) {
|
||||||
|
case 'copy':
|
||||||
|
that.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height);
|
||||||
|
break;
|
||||||
|
case 'fill':
|
||||||
|
that.fillRect(a.x, a.y, a.width, a.height, a.color);
|
||||||
|
break;
|
||||||
|
case 'blit':
|
||||||
|
that.blitImage(a.x, a.y, a.width, a.height, a.data, 0);
|
||||||
|
break;
|
||||||
|
case 'blitRgb':
|
||||||
|
that.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0);
|
||||||
|
break;
|
||||||
|
case 'img':
|
||||||
|
if (a.img.complete) {
|
||||||
|
that.drawImage(a.img, a.x, a.y);
|
||||||
|
} else {
|
||||||
|
// We need to wait for this image to 'load'
|
||||||
|
// to keep things in-order
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ready) {
|
||||||
|
a = renderQ.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (renderQ.length > 0) {
|
||||||
|
requestAnimFrame(scan_renderQ);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
that.changeCursor = function(pixels, mask, hotx, hoty, w, h) {
|
that.changeCursor = function(pixels, mask, hotx, hoty, w, h) {
|
||||||
if (conf.cursor_uri === false) {
|
if (conf.cursor_uri === false) {
|
||||||
Util.Warn("changeCursor called but no cursor data URI support");
|
Util.Warn("changeCursor called but no cursor data URI support");
|
||||||
|
|||||||
@@ -412,6 +412,26 @@ function onKeyUp(e) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function allKeysUp() {
|
||||||
|
Util.Debug(">> Keyboard.allKeysUp");
|
||||||
|
if (keyDownList.length > 0) {
|
||||||
|
Util.Info("Releasing pressed/down keys");
|
||||||
|
}
|
||||||
|
var i, keysym, fevt = null;
|
||||||
|
for (i = keyDownList.length-1; i >= 0; i--) {
|
||||||
|
fevt = keyDownList.splice(i, 1)[0];
|
||||||
|
keysym = fevt.keysym;
|
||||||
|
if (conf.onKeyPress && (keysym > 0)) {
|
||||||
|
Util.Debug("allKeysUp, keysym: " + keysym +
|
||||||
|
" (keyCode: " + fevt.keyCode +
|
||||||
|
", which: " + fevt.which + ")");
|
||||||
|
conf.onKeyPress(keysym, 0, fevt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Util.Debug("<< Keyboard.allKeysUp");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Public API interface functions
|
// Public API interface functions
|
||||||
//
|
//
|
||||||
@@ -424,6 +444,9 @@ that.grab = function() {
|
|||||||
Util.addEvent(c, 'keyup', onKeyUp);
|
Util.addEvent(c, 'keyup', onKeyUp);
|
||||||
Util.addEvent(c, 'keypress', onKeyPress);
|
Util.addEvent(c, 'keypress', onKeyPress);
|
||||||
|
|
||||||
|
// Release (key up) if window loses focus
|
||||||
|
Util.addEvent(window, 'blur', allKeysUp);
|
||||||
|
|
||||||
//Util.Debug("<< Keyboard.grab");
|
//Util.Debug("<< Keyboard.grab");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -434,6 +457,10 @@ that.ungrab = function() {
|
|||||||
Util.removeEvent(c, 'keydown', onKeyDown);
|
Util.removeEvent(c, 'keydown', onKeyDown);
|
||||||
Util.removeEvent(c, 'keyup', onKeyUp);
|
Util.removeEvent(c, 'keyup', onKeyUp);
|
||||||
Util.removeEvent(c, 'keypress', onKeyPress);
|
Util.removeEvent(c, 'keypress', onKeyPress);
|
||||||
|
Util.removeEvent(window, 'blur', allKeysUp);
|
||||||
|
|
||||||
|
// Release (key up) all keys that are in a down state
|
||||||
|
allKeysUp();
|
||||||
|
|
||||||
//Util.Debug(">> Keyboard.ungrab");
|
//Util.Debug(">> Keyboard.ungrab");
|
||||||
};
|
};
|
||||||
@@ -460,7 +487,8 @@ Util.conf_defaults(conf, that, defaults, [
|
|||||||
['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'],
|
['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'],
|
||||||
|
|
||||||
['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'],
|
['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'],
|
||||||
['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement']
|
['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'],
|
||||||
|
['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -475,7 +503,11 @@ function onMouseButton(e, down) {
|
|||||||
}
|
}
|
||||||
evt = (e ? e : window.event);
|
evt = (e ? e : window.event);
|
||||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
||||||
if (evt.which) {
|
if (e.touches || e.changedTouches) {
|
||||||
|
// Touch device
|
||||||
|
bmask = conf.touchButton;
|
||||||
|
// If bmask is set
|
||||||
|
} else if (evt.which) {
|
||||||
/* everything except IE */
|
/* everything except IE */
|
||||||
bmask = 1 << evt.button;
|
bmask = 1 << evt.button;
|
||||||
} else {
|
} else {
|
||||||
@@ -486,7 +518,7 @@ function onMouseButton(e, down) {
|
|||||||
}
|
}
|
||||||
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
|
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
|
||||||
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
|
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
|
||||||
if (conf.onMouseButton) {
|
if (bmask > 0 && conf.onMouseButton) {
|
||||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||||
conf.onMouseButton(pos.x, pos.y, down, bmask);
|
conf.onMouseButton(pos.x, pos.y, down, bmask);
|
||||||
@@ -536,6 +568,8 @@ function onMouseMove(e) {
|
|||||||
if (conf.onMouseMove) {
|
if (conf.onMouseMove) {
|
||||||
conf.onMouseMove(pos.x, pos.y);
|
conf.onMouseMove(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
|
Util.stopEvent(e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseDisable(e) {
|
function onMouseDisable(e) {
|
||||||
@@ -565,11 +599,17 @@ that.grab = function() {
|
|||||||
//Util.Debug(">> Mouse.grab");
|
//Util.Debug(">> Mouse.grab");
|
||||||
var c = conf.target;
|
var c = conf.target;
|
||||||
|
|
||||||
Util.addEvent(c, 'mousedown', onMouseDown);
|
if ('ontouchstart' in document.documentElement) {
|
||||||
Util.addEvent(c, 'mouseup', onMouseUp);
|
Util.addEvent(c, 'touchstart', onMouseDown);
|
||||||
Util.addEvent(c, 'mousemove', onMouseMove);
|
Util.addEvent(c, 'touchend', onMouseUp);
|
||||||
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
Util.addEvent(c, 'touchmove', onMouseMove);
|
||||||
onMouseWheel);
|
} else {
|
||||||
|
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 */
|
/* Work around right and middle click browser behaviors */
|
||||||
Util.addEvent(document, 'click', onMouseDisable);
|
Util.addEvent(document, 'click', onMouseDisable);
|
||||||
@@ -582,11 +622,17 @@ that.ungrab = function() {
|
|||||||
//Util.Debug(">> Mouse.ungrab");
|
//Util.Debug(">> Mouse.ungrab");
|
||||||
var c = conf.target;
|
var c = conf.target;
|
||||||
|
|
||||||
Util.removeEvent(c, 'mousedown', onMouseDown);
|
if ('ontouchstart' in document.documentElement) {
|
||||||
Util.removeEvent(c, 'mouseup', onMouseUp);
|
Util.removeEvent(c, 'touchstart', onMouseDown);
|
||||||
Util.removeEvent(c, 'mousemove', onMouseMove);
|
Util.removeEvent(c, 'touchend', onMouseUp);
|
||||||
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
Util.removeEvent(c, 'touchmove', onMouseMove);
|
||||||
onMouseWheel);
|
} else {
|
||||||
|
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 */
|
/* Work around right and middle click browser behaviors */
|
||||||
Util.removeEvent(document, 'click', onMouseDisable);
|
Util.removeEvent(document, 'click', onMouseDisable);
|
||||||
@@ -1121,14 +1167,14 @@ unicodeTable = {
|
|||||||
0x21D4 : 0x08cd,
|
0x21D4 : 0x08cd,
|
||||||
0x21D2 : 0x08ce,
|
0x21D2 : 0x08ce,
|
||||||
0x2261 : 0x08cf,
|
0x2261 : 0x08cf,
|
||||||
0x221A : 0x08d6,
|
//0x221A : 0x08d6,
|
||||||
0x2282 : 0x08da,
|
0x2282 : 0x08da,
|
||||||
0x2283 : 0x08db,
|
0x2283 : 0x08db,
|
||||||
0x2229 : 0x08dc,
|
0x2229 : 0x08dc,
|
||||||
0x222A : 0x08dd,
|
0x222A : 0x08dd,
|
||||||
0x2227 : 0x08de,
|
0x2227 : 0x08de,
|
||||||
0x2228 : 0x08df,
|
0x2228 : 0x08df,
|
||||||
0x2202 : 0x08ef,
|
//0x2202 : 0x08ef,
|
||||||
0x0192 : 0x08f6,
|
0x0192 : 0x08f6,
|
||||||
0x2190 : 0x08fb,
|
0x2190 : 0x08fb,
|
||||||
0x2191 : 0x08fc,
|
0x2191 : 0x08fc,
|
||||||
@@ -1463,7 +1509,7 @@ unicodeTable = {
|
|||||||
0x012D : 0x100012d,
|
0x012D : 0x100012d,
|
||||||
0x01B6 : 0x10001b6,
|
0x01B6 : 0x10001b6,
|
||||||
0x01E7 : 0x10001e7,
|
0x01E7 : 0x10001e7,
|
||||||
0x01D2 : 0x10001d2,
|
//0x01D2 : 0x10001d2,
|
||||||
0x0275 : 0x1000275,
|
0x0275 : 0x1000275,
|
||||||
0x018F : 0x100018f,
|
0x018F : 0x100018f,
|
||||||
0x0259 : 0x1000259,
|
0x0259 : 0x1000259,
|
||||||
@@ -1833,7 +1879,7 @@ unicodeTable = {
|
|||||||
0x28e0 : 0x10028e0,
|
0x28e0 : 0x10028e0,
|
||||||
0x28e1 : 0x10028e1,
|
0x28e1 : 0x10028e1,
|
||||||
0x28e2 : 0x10028e2,
|
0x28e2 : 0x10028e2,
|
||||||
0x28e3 : 0x10028e3,
|
0x28e3 : 0x10028e3,
|
||||||
0x28e4 : 0x10028e4,
|
0x28e4 : 0x10028e4,
|
||||||
0x28e5 : 0x10028e5,
|
0x28e5 : 0x10028e5,
|
||||||
0x28e6 : 0x10028e6,
|
0x28e6 : 0x10028e6,
|
||||||
|
|||||||
@@ -0,0 +1,668 @@
|
|||||||
|
/*
|
||||||
|
* JSUnzip
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 by Erik Moller
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express
|
||||||
|
* or implied warranty. In no event will the authors be
|
||||||
|
* held liable for any damages arising from the use of
|
||||||
|
* this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software
|
||||||
|
* for any purpose, including commercial applications,
|
||||||
|
* and to alter it and redistribute it freely, subject to
|
||||||
|
* the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be
|
||||||
|
* misrepresented; you must not claim that you
|
||||||
|
* wrote the original software. If you use this
|
||||||
|
* software in a product, an acknowledgment in
|
||||||
|
* the product documentation would be appreciated
|
||||||
|
* but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked
|
||||||
|
* as such, and must not be misrepresented as
|
||||||
|
* being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from
|
||||||
|
* any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var tinf;
|
||||||
|
|
||||||
|
function JSUnzip() {
|
||||||
|
|
||||||
|
this.getInt = function(offset, size) {
|
||||||
|
switch (size) {
|
||||||
|
case 4:
|
||||||
|
return (this.data.charCodeAt(offset + 3) & 0xff) << 24 |
|
||||||
|
(this.data.charCodeAt(offset + 2) & 0xff) << 16 |
|
||||||
|
(this.data.charCodeAt(offset + 1) & 0xff) << 8 |
|
||||||
|
(this.data.charCodeAt(offset + 0) & 0xff);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
return (this.data.charCodeAt(offset + 1) & 0xff) << 8 |
|
||||||
|
(this.data.charCodeAt(offset + 0) & 0xff);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return this.data.charCodeAt(offset) & 0xff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getDOSDate = function(dosdate, dostime) {
|
||||||
|
var day = dosdate & 0x1f;
|
||||||
|
var month = ((dosdate >> 5) & 0xf) - 1;
|
||||||
|
var year = 1980 + ((dosdate >> 9) & 0x7f)
|
||||||
|
var second = (dostime & 0x1f) * 2;
|
||||||
|
var minute = (dostime >> 5) & 0x3f;
|
||||||
|
hour = (dostime >> 11) & 0x1f;
|
||||||
|
return new Date(year, month, day, hour, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.open = function(data) {
|
||||||
|
this.data = data;
|
||||||
|
this.files = [];
|
||||||
|
|
||||||
|
if (this.data.length < 22)
|
||||||
|
return { 'status' : false, 'error' : 'Invalid data' };
|
||||||
|
var endOfCentralDirectory = this.data.length - 22;
|
||||||
|
while (endOfCentralDirectory >= 0 && this.getInt(endOfCentralDirectory, 4) != 0x06054b50)
|
||||||
|
--endOfCentralDirectory;
|
||||||
|
if (endOfCentralDirectory < 0)
|
||||||
|
return { 'status' : false, 'error' : 'Invalid data' };
|
||||||
|
if (this.getInt(endOfCentralDirectory + 4, 2) != 0 || this.getInt(endOfCentralDirectory + 6, 2) != 0)
|
||||||
|
return { 'status' : false, 'error' : 'No multidisk support' };
|
||||||
|
|
||||||
|
var entriesInThisDisk = this.getInt(endOfCentralDirectory + 8, 2);
|
||||||
|
var centralDirectoryOffset = this.getInt(endOfCentralDirectory + 16, 4);
|
||||||
|
var globalCommentLength = this.getInt(endOfCentralDirectory + 20, 2);
|
||||||
|
this.comment = this.data.slice(endOfCentralDirectory + 22, endOfCentralDirectory + 22 + globalCommentLength);
|
||||||
|
|
||||||
|
var fileOffset = centralDirectoryOffset;
|
||||||
|
|
||||||
|
for (var i = 0; i < entriesInThisDisk; ++i) {
|
||||||
|
if (this.getInt(fileOffset + 0, 4) != 0x02014b50)
|
||||||
|
return { 'status' : false, 'error' : 'Invalid data' };
|
||||||
|
if (this.getInt(fileOffset + 6, 2) > 20)
|
||||||
|
return { 'status' : false, 'error' : 'Unsupported version' };
|
||||||
|
if (this.getInt(fileOffset + 8, 2) & 1)
|
||||||
|
return { 'status' : false, 'error' : 'Encryption not implemented' };
|
||||||
|
|
||||||
|
var compressionMethod = this.getInt(fileOffset + 10, 2);
|
||||||
|
if (compressionMethod != 0 && compressionMethod != 8)
|
||||||
|
return { 'status' : false, 'error' : 'Unsupported compression method' };
|
||||||
|
|
||||||
|
var lastModFileTime = this.getInt(fileOffset + 12, 2);
|
||||||
|
var lastModFileDate = this.getInt(fileOffset + 14, 2);
|
||||||
|
var lastModifiedDate = this.getDOSDate(lastModFileDate, lastModFileTime);
|
||||||
|
|
||||||
|
var crc = this.getInt(fileOffset + 16, 4);
|
||||||
|
// TODO: crc
|
||||||
|
|
||||||
|
var compressedSize = this.getInt(fileOffset + 20, 4);
|
||||||
|
var uncompressedSize = this.getInt(fileOffset + 24, 4);
|
||||||
|
|
||||||
|
var fileNameLength = this.getInt(fileOffset + 28, 2);
|
||||||
|
var extraFieldLength = this.getInt(fileOffset + 30, 2);
|
||||||
|
var fileCommentLength = this.getInt(fileOffset + 32, 2);
|
||||||
|
|
||||||
|
var relativeOffsetOfLocalHeader = this.getInt(fileOffset + 42, 4);
|
||||||
|
|
||||||
|
var fileName = this.data.slice(fileOffset + 46, fileOffset + 46 + fileNameLength);
|
||||||
|
var fileComment = this.data.slice(fileOffset + 46 + fileNameLength + extraFieldLength, fileOffset + 46 + fileNameLength + extraFieldLength + fileCommentLength);
|
||||||
|
|
||||||
|
if (this.getInt(relativeOffsetOfLocalHeader + 0, 4) != 0x04034b50)
|
||||||
|
return { 'status' : false, 'error' : 'Invalid data' };
|
||||||
|
var localFileNameLength = this.getInt(relativeOffsetOfLocalHeader + 26, 2);
|
||||||
|
var localExtraFieldLength = this.getInt(relativeOffsetOfLocalHeader + 28, 2);
|
||||||
|
var localFileContent = relativeOffsetOfLocalHeader + 30 + localFileNameLength + localExtraFieldLength;
|
||||||
|
|
||||||
|
this.files[fileName] =
|
||||||
|
{
|
||||||
|
'fileComment' : fileComment,
|
||||||
|
'compressionMethod' : compressionMethod,
|
||||||
|
'compressedSize' : compressedSize,
|
||||||
|
'uncompressedSize' : uncompressedSize,
|
||||||
|
'localFileContent' : localFileContent,
|
||||||
|
'lastModifiedDate' : lastModifiedDate
|
||||||
|
};
|
||||||
|
|
||||||
|
fileOffset += 46 + fileNameLength + extraFieldLength + fileCommentLength;
|
||||||
|
}
|
||||||
|
return { 'status' : true }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.read = function(fileName) {
|
||||||
|
var fileInfo = this.files[fileName];
|
||||||
|
if (fileInfo) {
|
||||||
|
if (fileInfo.compressionMethod == 8) {
|
||||||
|
if (!tinf) {
|
||||||
|
tinf = new TINF();
|
||||||
|
tinf.init();
|
||||||
|
}
|
||||||
|
var result = tinf.uncompress(this.data, fileInfo.localFileContent);
|
||||||
|
if (result.status == tinf.OK)
|
||||||
|
return { 'status' : true, 'data' : result.data };
|
||||||
|
else
|
||||||
|
return { 'status' : false, 'error' : result.error };
|
||||||
|
} else {
|
||||||
|
return { 'status' : true, 'data' : this.data.slice(fileInfo.localFileContent, fileInfo.localFileContent + fileInfo.uncompressedSize) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { 'status' : false, 'error' : "File '" + fileName + "' doesn't exist in zip" };
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tinflate - tiny inflate
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003 by Joergen Ibsen / Jibz
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* http://www.ibsensoftware.com/
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express
|
||||||
|
* or implied warranty. In no event will the authors be
|
||||||
|
* held liable for any damages arising from the use of
|
||||||
|
* this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software
|
||||||
|
* for any purpose, including commercial applications,
|
||||||
|
* and to alter it and redistribute it freely, subject to
|
||||||
|
* the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be
|
||||||
|
* misrepresented; you must not claim that you
|
||||||
|
* wrote the original software. If you use this
|
||||||
|
* software in a product, an acknowledgment in
|
||||||
|
* the product documentation would be appreciated
|
||||||
|
* but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked
|
||||||
|
* as such, and must not be misrepresented as
|
||||||
|
* being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from
|
||||||
|
* any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tinflate javascript port by Erik Moller in May 2011.
|
||||||
|
* emoller@opera.com
|
||||||
|
*
|
||||||
|
* read_bits() patched by mike@imidio.com to allow
|
||||||
|
* reading more then 8 bits (needed in some zlib streams)
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function TINF() {
|
||||||
|
|
||||||
|
this.OK = 0;
|
||||||
|
this.DATA_ERROR = (-3);
|
||||||
|
this.WINDOW_SIZE = 32768;
|
||||||
|
|
||||||
|
/* ------------------------------ *
|
||||||
|
* -- internal data structures -- *
|
||||||
|
* ------------------------------ */
|
||||||
|
|
||||||
|
this.TREE = function() {
|
||||||
|
this.table = new Array(16); /* table of code length counts */
|
||||||
|
this.trans = new Array(288); /* code -> symbol translation table */
|
||||||
|
};
|
||||||
|
|
||||||
|
this.DATA = function(that) {
|
||||||
|
this.source = '';
|
||||||
|
this.sourceIndex = 0;
|
||||||
|
this.tag = 0;
|
||||||
|
this.bitcount = 0;
|
||||||
|
|
||||||
|
this.dest = [];
|
||||||
|
|
||||||
|
this.history = [];
|
||||||
|
|
||||||
|
this.ltree = new that.TREE(); /* dynamic length/symbol tree */
|
||||||
|
this.dtree = new that.TREE(); /* dynamic distance tree */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* --------------------------------------------------- *
|
||||||
|
* -- uninitialized global data (static structures) -- *
|
||||||
|
* --------------------------------------------------- */
|
||||||
|
|
||||||
|
this.sltree = new this.TREE(); /* fixed length/symbol tree */
|
||||||
|
this.sdtree = new this.TREE(); /* fixed distance tree */
|
||||||
|
|
||||||
|
/* extra bits and base tables for length codes */
|
||||||
|
this.length_bits = new Array(30);
|
||||||
|
this.length_base = new Array(30);
|
||||||
|
|
||||||
|
/* extra bits and base tables for distance codes */
|
||||||
|
this.dist_bits = new Array(30);
|
||||||
|
this.dist_base = new Array(30);
|
||||||
|
|
||||||
|
/* special ordering of code length codes */
|
||||||
|
this.clcidx = [
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6,
|
||||||
|
10, 5, 11, 4, 12, 3, 13, 2,
|
||||||
|
14, 1, 15
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ----------------------- *
|
||||||
|
* -- utility functions -- *
|
||||||
|
* ----------------------- */
|
||||||
|
|
||||||
|
/* build extra bits and base tables */
|
||||||
|
this.build_bits_base = function(bits, base, delta, first)
|
||||||
|
{
|
||||||
|
var i, sum;
|
||||||
|
|
||||||
|
/* build bits table */
|
||||||
|
for (i = 0; i < delta; ++i) bits[i] = 0;
|
||||||
|
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = Math.floor(i / delta);
|
||||||
|
|
||||||
|
/* build base table */
|
||||||
|
for (sum = first, i = 0; i < 30; ++i)
|
||||||
|
{
|
||||||
|
base[i] = sum;
|
||||||
|
sum += 1 << bits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the fixed huffman trees */
|
||||||
|
this.build_fixed_trees = function(lt, dt)
|
||||||
|
{
|
||||||
|
var i;
|
||||||
|
|
||||||
|
/* build fixed length tree */
|
||||||
|
for (i = 0; i < 7; ++i) lt.table[i] = 0;
|
||||||
|
|
||||||
|
lt.table[7] = 24;
|
||||||
|
lt.table[8] = 152;
|
||||||
|
lt.table[9] = 112;
|
||||||
|
|
||||||
|
for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i;
|
||||||
|
for (i = 0; i < 144; ++i) lt.trans[24 + i] = i;
|
||||||
|
for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i;
|
||||||
|
for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i;
|
||||||
|
|
||||||
|
/* build fixed distance tree */
|
||||||
|
for (i = 0; i < 5; ++i) dt.table[i] = 0;
|
||||||
|
|
||||||
|
dt.table[5] = 32;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; ++i) dt.trans[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given an array of code lengths, build a tree */
|
||||||
|
this.build_tree = function(t, lengths, loffset, num)
|
||||||
|
{
|
||||||
|
var offs = new Array(16);
|
||||||
|
var i, sum;
|
||||||
|
|
||||||
|
/* clear code length count table */
|
||||||
|
for (i = 0; i < 16; ++i) t.table[i] = 0;
|
||||||
|
|
||||||
|
/* scan symbol lengths, and sum code length counts */
|
||||||
|
for (i = 0; i < num; ++i) t.table[lengths[loffset + i]]++;
|
||||||
|
|
||||||
|
t.table[0] = 0;
|
||||||
|
|
||||||
|
/* compute offset table for distribution sort */
|
||||||
|
for (sum = 0, i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
offs[i] = sum;
|
||||||
|
sum += t.table[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create code->symbol translation table (symbols sorted by code) */
|
||||||
|
for (i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
if (lengths[loffset + i]) t.trans[offs[lengths[loffset + i]]++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------- *
|
||||||
|
* -- decode functions -- *
|
||||||
|
* ---------------------- */
|
||||||
|
|
||||||
|
/* get one bit from source stream */
|
||||||
|
this.getbit = function(d)
|
||||||
|
{
|
||||||
|
var bit;
|
||||||
|
|
||||||
|
/* check if tag is empty */
|
||||||
|
if (!d.bitcount--)
|
||||||
|
{
|
||||||
|
/* load next tag */
|
||||||
|
d.tag = d.source[d.sourceIndex++] & 0xff;
|
||||||
|
d.bitcount = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shift bit out of tag */
|
||||||
|
bit = d.tag & 0x01;
|
||||||
|
d.tag >>= 1;
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read a num bit value from a stream and add base */
|
||||||
|
this.read_bits = function(d, num, base)
|
||||||
|
{
|
||||||
|
if (!num)
|
||||||
|
return base;
|
||||||
|
|
||||||
|
var val = 0;
|
||||||
|
while (d.bitcount < 24) {
|
||||||
|
d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount;
|
||||||
|
d.bitcount += 8;
|
||||||
|
}
|
||||||
|
val = d.tag & (0xffff >> (16 - num));
|
||||||
|
d.tag >>= num;
|
||||||
|
d.bitcount -= num;
|
||||||
|
return val + base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given a data stream and a tree, decode a symbol */
|
||||||
|
this.decode_symbol = function(d, t)
|
||||||
|
{
|
||||||
|
while (d.bitcount < 16) {
|
||||||
|
d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount;
|
||||||
|
d.bitcount += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum = 0, cur = 0, len = 0;
|
||||||
|
do {
|
||||||
|
cur = 2 * cur + ((d.tag & (1 << len)) >> len);
|
||||||
|
|
||||||
|
++len;
|
||||||
|
|
||||||
|
sum += t.table[len];
|
||||||
|
cur -= t.table[len];
|
||||||
|
|
||||||
|
} while (cur >= 0);
|
||||||
|
|
||||||
|
d.tag >>= len;
|
||||||
|
d.bitcount -= len;
|
||||||
|
|
||||||
|
return t.trans[sum + cur];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given a data stream, decode dynamic trees from it */
|
||||||
|
this.decode_trees = function(d, lt, dt)
|
||||||
|
{
|
||||||
|
var code_tree = new this.TREE();
|
||||||
|
var lengths = new Array(288+32);
|
||||||
|
var hlit, hdist, hclen;
|
||||||
|
var i, num, length;
|
||||||
|
|
||||||
|
/* get 5 bits HLIT (257-286) */
|
||||||
|
hlit = this.read_bits(d, 5, 257);
|
||||||
|
|
||||||
|
/* get 5 bits HDIST (1-32) */
|
||||||
|
hdist = this.read_bits(d, 5, 1);
|
||||||
|
|
||||||
|
/* get 4 bits HCLEN (4-19) */
|
||||||
|
hclen = this.read_bits(d, 4, 4);
|
||||||
|
|
||||||
|
for (i = 0; i < 19; ++i) lengths[i] = 0;
|
||||||
|
|
||||||
|
/* read code lengths for code length alphabet */
|
||||||
|
for (i = 0; i < hclen; ++i)
|
||||||
|
{
|
||||||
|
/* get 3 bits code length (0-7) */
|
||||||
|
var clen = this.read_bits(d, 3, 0);
|
||||||
|
|
||||||
|
lengths[this.clcidx[i]] = clen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build code length tree */
|
||||||
|
this.build_tree(code_tree, lengths, 0, 19);
|
||||||
|
|
||||||
|
/* decode code lengths for the dynamic trees */
|
||||||
|
for (num = 0; num < hlit + hdist; )
|
||||||
|
{
|
||||||
|
var sym = this.decode_symbol(d, code_tree);
|
||||||
|
|
||||||
|
switch (sym)
|
||||||
|
{
|
||||||
|
case 16:
|
||||||
|
/* copy previous code length 3-6 times (read 2 bits) */
|
||||||
|
{
|
||||||
|
var prev = lengths[num - 1];
|
||||||
|
for (length = this.read_bits(d, 2, 3); length; --length)
|
||||||
|
{
|
||||||
|
lengths[num++] = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
/* repeat code length 0 for 3-10 times (read 3 bits) */
|
||||||
|
for (length = this.read_bits(d, 3, 3); length; --length)
|
||||||
|
{
|
||||||
|
lengths[num++] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
/* repeat code length 0 for 11-138 times (read 7 bits) */
|
||||||
|
for (length = this.read_bits(d, 7, 11); length; --length)
|
||||||
|
{
|
||||||
|
lengths[num++] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* values 0-15 represent the actual code lengths */
|
||||||
|
lengths[num++] = sym;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build dynamic trees */
|
||||||
|
this.build_tree(lt, lengths, 0, hlit);
|
||||||
|
this.build_tree(dt, lengths, hlit, hdist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- *
|
||||||
|
* -- block inflate functions -- *
|
||||||
|
* ----------------------------- */
|
||||||
|
|
||||||
|
/* given a stream and two trees, inflate a block of data */
|
||||||
|
this.inflate_block_data = function(d, lt, dt)
|
||||||
|
{
|
||||||
|
// js optimization.
|
||||||
|
var ddest = d.dest;
|
||||||
|
var ddestlength = ddest.length;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
var sym = this.decode_symbol(d, lt);
|
||||||
|
|
||||||
|
/* check for end of block */
|
||||||
|
if (sym == 256)
|
||||||
|
{
|
||||||
|
return this.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym < 256)
|
||||||
|
{
|
||||||
|
ddest[ddestlength++] = sym; // ? String.fromCharCode(sym);
|
||||||
|
d.history.push(sym);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var length, dist, offs;
|
||||||
|
var i;
|
||||||
|
|
||||||
|
sym -= 257;
|
||||||
|
|
||||||
|
/* possibly get more bits from length code */
|
||||||
|
length = this.read_bits(d, this.length_bits[sym], this.length_base[sym]);
|
||||||
|
|
||||||
|
dist = this.decode_symbol(d, dt);
|
||||||
|
|
||||||
|
/* possibly get more bits from distance code */
|
||||||
|
offs = d.history.length - this.read_bits(d, this.dist_bits[dist], this.dist_base[dist]);
|
||||||
|
|
||||||
|
if (offs < 0)
|
||||||
|
throw ("Invalid zlib offset " + offs);
|
||||||
|
|
||||||
|
/* copy match */
|
||||||
|
for (i = offs; i < offs + length; ++i) {
|
||||||
|
//ddest[ddestlength++] = ddest[i];
|
||||||
|
ddest[ddestlength++] = d.history[i];
|
||||||
|
d.history.push(d.history[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate an uncompressed block of data */
|
||||||
|
this.inflate_uncompressed_block = function(d)
|
||||||
|
{
|
||||||
|
var length, invlength;
|
||||||
|
var i;
|
||||||
|
|
||||||
|
if (d.bitcount > 7) {
|
||||||
|
var overflow = Math.floor(d.bitcount / 8);
|
||||||
|
d.sourceIndex -= overflow;
|
||||||
|
d.bitcount = 0;
|
||||||
|
d.tag = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get length */
|
||||||
|
length = d.source[d.sourceIndex+1];
|
||||||
|
length = 256*length + d.source[d.sourceIndex];
|
||||||
|
|
||||||
|
/* get one's complement of length */
|
||||||
|
invlength = d.source[d.sourceIndex+3];
|
||||||
|
invlength = 256*invlength + d.source[d.sourceIndex+2];
|
||||||
|
|
||||||
|
/* check length */
|
||||||
|
if (length != (~invlength & 0x0000ffff)) return this.DATA_ERROR;
|
||||||
|
|
||||||
|
d.sourceIndex += 4;
|
||||||
|
|
||||||
|
/* copy block */
|
||||||
|
for (i = length; i; --i) {
|
||||||
|
d.history.push(d.source[d.sourceIndex]);
|
||||||
|
d.dest[d.dest.length] = d.source[d.sourceIndex++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure we start next block on a byte boundary */
|
||||||
|
d.bitcount = 0;
|
||||||
|
|
||||||
|
return this.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate a block of data compressed with fixed huffman trees */
|
||||||
|
this.inflate_fixed_block = function(d)
|
||||||
|
{
|
||||||
|
/* decode block using fixed trees */
|
||||||
|
return this.inflate_block_data(d, this.sltree, this.sdtree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate a block of data compressed with dynamic huffman trees */
|
||||||
|
this.inflate_dynamic_block = function(d)
|
||||||
|
{
|
||||||
|
/* decode trees from stream */
|
||||||
|
this.decode_trees(d, d.ltree, d.dtree);
|
||||||
|
|
||||||
|
/* decode block using decoded trees */
|
||||||
|
return this.inflate_block_data(d, d.ltree, d.dtree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------- *
|
||||||
|
* -- public functions -- *
|
||||||
|
* ---------------------- */
|
||||||
|
|
||||||
|
/* initialize global (static) data */
|
||||||
|
this.init = function()
|
||||||
|
{
|
||||||
|
/* build fixed huffman trees */
|
||||||
|
this.build_fixed_trees(this.sltree, this.sdtree);
|
||||||
|
|
||||||
|
/* build extra bits and base tables */
|
||||||
|
this.build_bits_base(this.length_bits, this.length_base, 4, 3);
|
||||||
|
this.build_bits_base(this.dist_bits, this.dist_base, 2, 1);
|
||||||
|
|
||||||
|
/* fix a special case */
|
||||||
|
this.length_bits[28] = 0;
|
||||||
|
this.length_base[28] = 258;
|
||||||
|
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reset = function()
|
||||||
|
{
|
||||||
|
this.d = new this.DATA(this);
|
||||||
|
delete this.header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inflate stream from source to dest */
|
||||||
|
this.uncompress = function(source, offset)
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = this.d;
|
||||||
|
var bfinal;
|
||||||
|
|
||||||
|
/* initialise data */
|
||||||
|
d.source = source;
|
||||||
|
d.sourceIndex = offset;
|
||||||
|
d.bitcount = 0;
|
||||||
|
|
||||||
|
d.dest = [];
|
||||||
|
|
||||||
|
// Skip zlib header at start of stream
|
||||||
|
if (typeof this.header == 'undefined') {
|
||||||
|
this.header = this.read_bits(d, 16, 0);
|
||||||
|
/* byte 0: 0x78, 7 = 32k window size, 8 = deflate */
|
||||||
|
/* byte 1: check bits for header and other flags */
|
||||||
|
}
|
||||||
|
|
||||||
|
var blocks = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
var btype;
|
||||||
|
var res;
|
||||||
|
|
||||||
|
/* read final block flag */
|
||||||
|
bfinal = this.getbit(d);
|
||||||
|
|
||||||
|
/* read block type (2 bits) */
|
||||||
|
btype = this.read_bits(d, 2, 0);
|
||||||
|
|
||||||
|
/* decompress block */
|
||||||
|
switch (btype)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
/* decompress uncompressed block */
|
||||||
|
res = this.inflate_uncompressed_block(d);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* decompress block with fixed huffman trees */
|
||||||
|
res = this.inflate_fixed_block(d);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* decompress block with dynamic huffman trees */
|
||||||
|
res = this.inflate_dynamic_block(d);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return { 'status' : this.DATA_ERROR };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != this.OK) return { 'status' : this.DATA_ERROR };
|
||||||
|
blocks++;
|
||||||
|
|
||||||
|
} while (!bfinal && d.sourceIndex < d.source.length);
|
||||||
|
|
||||||
|
d.history = d.history.slice(-this.WINDOW_SIZE);
|
||||||
|
|
||||||
|
return { 'status' : this.OK, 'data' : d.dest };
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
#VNC_controls {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#VNC_controls ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#VNC_controls li {
|
|
||||||
float: left;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#VNC_host {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
#VNC_port {
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
#VNC_password {
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
#VNC_encrypt {
|
|
||||||
}
|
|
||||||
#VNC_connectTimeout {
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
#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: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
|
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
* TIGHT decoder portion:
|
||||||
|
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jslint white: false, browser: true, bitwise: false, plusplus: false */
|
/*jslint white: false, browser: true, bitwise: false, plusplus: false */
|
||||||
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
|
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */
|
||||||
|
|
||||||
|
|
||||||
function RFB(defaults) {
|
function RFB(defaults) {
|
||||||
@@ -20,10 +23,10 @@ var that = {}, // Public API methods
|
|||||||
init_vars, updateState, fail, handle_message,
|
init_vars, updateState, fail, handle_message,
|
||||||
init_msg, normal_msg, framebufferUpdate, print_stats,
|
init_msg, normal_msg, framebufferUpdate, print_stats,
|
||||||
|
|
||||||
pixelFormat, clientEncodings, fbUpdateRequest,
|
pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests,
|
||||||
keyEvent, pointerEvent, clientCutText,
|
keyEvent, pointerEvent, clientCutText,
|
||||||
|
|
||||||
extract_data_uri, scan_tight_imgQ,
|
getTightCLength, extract_data_uri,
|
||||||
keyPress, mouseButton, mouseMove,
|
keyPress, mouseButton, mouseMove,
|
||||||
|
|
||||||
checkEvents, // Overridable for testing
|
checkEvents, // Overridable for testing
|
||||||
@@ -35,7 +38,7 @@ var that = {}, // Public API methods
|
|||||||
rfb_host = '',
|
rfb_host = '',
|
||||||
rfb_port = 5900,
|
rfb_port = 5900,
|
||||||
rfb_password = '',
|
rfb_password = '',
|
||||||
rfb_uri = '',
|
rfb_path = '',
|
||||||
|
|
||||||
rfb_state = 'disconnected',
|
rfb_state = 'disconnected',
|
||||||
rfb_version = 0,
|
rfb_version = 0,
|
||||||
@@ -46,6 +49,7 @@ var that = {}, // Public API methods
|
|||||||
// In preference order
|
// In preference order
|
||||||
encodings = [
|
encodings = [
|
||||||
['COPYRECT', 0x01 ],
|
['COPYRECT', 0x01 ],
|
||||||
|
['TIGHT', 0x07 ],
|
||||||
['TIGHT_PNG', -260 ],
|
['TIGHT_PNG', -260 ],
|
||||||
['HEXTILE', 0x05 ],
|
['HEXTILE', 0x05 ],
|
||||||
['RRE', 0x02 ],
|
['RRE', 0x02 ],
|
||||||
@@ -54,10 +58,12 @@ var that = {}, // Public API methods
|
|||||||
['Cursor', -239 ],
|
['Cursor', -239 ],
|
||||||
|
|
||||||
// Psuedo-encoding settings
|
// Psuedo-encoding settings
|
||||||
['JPEG_quality_lo', -32 ],
|
//['JPEG_quality_lo', -32 ],
|
||||||
|
['JPEG_quality_med', -26 ],
|
||||||
//['JPEG_quality_hi', -23 ],
|
//['JPEG_quality_hi', -23 ],
|
||||||
['compress_lo', -255 ]
|
//['compress_lo', -255 ],
|
||||||
//['compress_hi', -247 ]
|
['compress_hi', -247 ],
|
||||||
|
['last_rect', -224 ]
|
||||||
],
|
],
|
||||||
|
|
||||||
encHandlers = {},
|
encHandlers = {},
|
||||||
@@ -87,7 +93,7 @@ var that = {}, // Public API methods
|
|||||||
encoding : 0,
|
encoding : 0,
|
||||||
subencoding : -1,
|
subencoding : -1,
|
||||||
background : null,
|
background : null,
|
||||||
imgQ : [] // TIGHT_PNG image queue
|
zlibs : [] // TIGHT zlib streams
|
||||||
},
|
},
|
||||||
|
|
||||||
fb_Bpp = 4,
|
fb_Bpp = 4,
|
||||||
@@ -96,7 +102,6 @@ var that = {}, // Public API methods
|
|||||||
fb_height = 0,
|
fb_height = 0,
|
||||||
fb_name = "",
|
fb_name = "",
|
||||||
|
|
||||||
scan_imgQ_rate = 40, // 25 times per second or so
|
|
||||||
last_req_time = 0,
|
last_req_time = 0,
|
||||||
rre_chunk_sz = 100,
|
rre_chunk_sz = 100,
|
||||||
|
|
||||||
@@ -109,7 +114,8 @@ var that = {}, // Public API methods
|
|||||||
|
|
||||||
fbu_rt_start : 0,
|
fbu_rt_start : 0,
|
||||||
fbu_rt_total : 0,
|
fbu_rt_total : 0,
|
||||||
fbu_rt_cnt : 0
|
fbu_rt_cnt : 0,
|
||||||
|
pixels : 0
|
||||||
},
|
},
|
||||||
|
|
||||||
test_mode = false,
|
test_mode = false,
|
||||||
@@ -118,7 +124,9 @@ var that = {}, // Public API methods
|
|||||||
|
|
||||||
/* Mouse state */
|
/* Mouse state */
|
||||||
mouse_buttonMask = 0,
|
mouse_buttonMask = 0,
|
||||||
mouse_arr = [];
|
mouse_arr = [],
|
||||||
|
viewportDragging = false,
|
||||||
|
viewportDragPos = {};
|
||||||
|
|
||||||
// Configuration attributes
|
// Configuration attributes
|
||||||
Util.conf_defaults(conf, that, defaults, [
|
Util.conf_defaults(conf, that, defaults, [
|
||||||
@@ -129,10 +137,16 @@ Util.conf_defaults(conf, that, defaults, [
|
|||||||
['true_color', 'rw', 'bool', true, 'Request true color pixel data'],
|
['true_color', 'rw', 'bool', true, 'Request true color pixel data'],
|
||||||
['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'],
|
['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'],
|
||||||
['shared', 'rw', 'bool', true, 'Request shared mode'],
|
['shared', 'rw', 'bool', true, 'Request shared mode'],
|
||||||
|
['view_only', 'rw', 'bool', false, 'Disable client mouse/keyboard'],
|
||||||
|
|
||||||
['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'],
|
['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'],
|
||||||
['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
|
['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
|
||||||
|
|
||||||
|
// UltraVNC repeater ID to connect to
|
||||||
|
['repeaterID', 'rw', 'str', '', 'RepeaterID to connect to'],
|
||||||
|
|
||||||
|
['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'],
|
||||||
|
|
||||||
['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
|
['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
|
||||||
['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
|
['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
|
||||||
|
|
||||||
@@ -211,10 +225,6 @@ function constructor() {
|
|||||||
|
|
||||||
rmode = display.get_render_mode();
|
rmode = display.get_render_mode();
|
||||||
|
|
||||||
if (typeof noVNC_logo !== 'undefined') {
|
|
||||||
display.set_logo(noVNC_logo);
|
|
||||||
}
|
|
||||||
|
|
||||||
ws = new Websock();
|
ws = new Websock();
|
||||||
ws.on('message', handle_message);
|
ws.on('message', handle_message);
|
||||||
ws.on('open', function() {
|
ws.on('open', function() {
|
||||||
@@ -224,19 +234,29 @@ function constructor() {
|
|||||||
fail("Got unexpected WebSockets connection");
|
fail("Got unexpected WebSockets connection");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ws.on('close', function() {
|
ws.on('close', function(e) {
|
||||||
|
Util.Warn("WebSocket on-close event");
|
||||||
|
var msg = "";
|
||||||
|
if (e.code) {
|
||||||
|
msg = " (code: " + e.code;
|
||||||
|
if (e.reason) {
|
||||||
|
msg += ", reason: " + e.reason;
|
||||||
|
}
|
||||||
|
msg += ")";
|
||||||
|
}
|
||||||
if (rfb_state === 'disconnect') {
|
if (rfb_state === 'disconnect') {
|
||||||
updateState('disconnected', 'VNC disconnected');
|
updateState('disconnected', 'VNC disconnected' + msg);
|
||||||
} else if (rfb_state === 'ProtocolVersion') {
|
} else if (rfb_state === 'ProtocolVersion') {
|
||||||
fail('Failed to connect to server');
|
fail('Failed to connect to server' + msg);
|
||||||
} else if (rfb_state in {'failed':1, 'disconnected':1}) {
|
} else if (rfb_state in {'failed':1, 'disconnected':1}) {
|
||||||
Util.Error("Received onclose while disconnected");
|
Util.Error("Received onclose while disconnected" + msg);
|
||||||
} else {
|
} else {
|
||||||
fail('Server disconnected');
|
fail('Server disconnected' + msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ws.on('error', function(e) {
|
ws.on('error', function(e) {
|
||||||
fail("WebSock error: " + e);
|
Util.Warn("WebSocket on-error event");
|
||||||
|
//fail("WebSock reported an error");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -266,14 +286,18 @@ function constructor() {
|
|||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
Util.Debug(">> RFB.connect");
|
Util.Debug(">> RFB.connect");
|
||||||
|
var uri;
|
||||||
var uri = "";
|
|
||||||
if (conf.encrypt) {
|
if (typeof UsingSocketIO !== "undefined") {
|
||||||
uri = "wss://";
|
uri = "http://" + rfb_host + ":" + rfb_port + "/" + rfb_path;
|
||||||
} else {
|
} else {
|
||||||
uri = "ws://";
|
if (conf.encrypt) {
|
||||||
|
uri = "wss://";
|
||||||
|
} else {
|
||||||
|
uri = "ws://";
|
||||||
|
}
|
||||||
|
uri += rfb_host + ":" + rfb_port + "/" + rfb_path;
|
||||||
}
|
}
|
||||||
uri += rfb_host + ":" + rfb_port + "/" + rfb_uri;
|
|
||||||
Util.Info("connecting to " + uri);
|
Util.Info("connecting to " + uri);
|
||||||
ws.open(uri);
|
ws.open(uri);
|
||||||
|
|
||||||
@@ -291,7 +315,7 @@ init_vars = function() {
|
|||||||
FBU.subrects = 0; // RRE and HEXTILE
|
FBU.subrects = 0; // RRE and HEXTILE
|
||||||
FBU.lines = 0; // RAW
|
FBU.lines = 0; // RAW
|
||||||
FBU.tiles = 0; // HEXTILE
|
FBU.tiles = 0; // HEXTILE
|
||||||
FBU.imgQ = []; // TIGHT_PNG image queue
|
FBU.zlibs = []; // TIGHT zlib encoders
|
||||||
mouse_buttonMask = 0;
|
mouse_buttonMask = 0;
|
||||||
mouse_arr = [];
|
mouse_arr = [];
|
||||||
|
|
||||||
@@ -299,6 +323,12 @@ init_vars = function() {
|
|||||||
for (i=0; i < encodings.length; i+=1) {
|
for (i=0; i < encodings.length; i+=1) {
|
||||||
encStats[encodings[i][1]][0] = 0;
|
encStats[encodings[i][1]][0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i=0; i < 4; i++) {
|
||||||
|
//FBU.zlibs[i] = new InflateStream();
|
||||||
|
FBU.zlibs[i] = new TINF();
|
||||||
|
FBU.zlibs[i].init();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Print statistics
|
// Print statistics
|
||||||
@@ -396,16 +426,16 @@ updateState = function(state, statusMsg) {
|
|||||||
func = Util.Warn;
|
func = Util.Warn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
|
||||||
|
func("New state '" + state + "', was '" + oldstate + "'." + cmsg);
|
||||||
|
|
||||||
if ((oldstate === 'failed') && (state === 'disconnected')) {
|
if ((oldstate === 'failed') && (state === 'disconnected')) {
|
||||||
// Do disconnect action, but stay in failed state.
|
// Do disconnect action, but stay in failed state
|
||||||
rfb_state = 'failed';
|
rfb_state = 'failed';
|
||||||
} else {
|
} else {
|
||||||
rfb_state = state;
|
rfb_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
|
|
||||||
func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg);
|
|
||||||
|
|
||||||
if (connTimer && (rfb_state !== 'connect')) {
|
if (connTimer && (rfb_state !== 'connect')) {
|
||||||
Util.Debug("Clearing connect timer");
|
Util.Debug("Clearing connect timer");
|
||||||
clearInterval(connTimer);
|
clearInterval(connTimer);
|
||||||
@@ -535,10 +565,10 @@ function genDES(password, challenge) {
|
|||||||
|
|
||||||
function flushClient() {
|
function flushClient() {
|
||||||
if (mouse_arr.length > 0) {
|
if (mouse_arr.length > 0) {
|
||||||
//send(mouse_arr.concat(fbUpdateRequest(1)));
|
//send(mouse_arr.concat(fbUpdateRequests()));
|
||||||
ws.send(mouse_arr);
|
ws.send(mouse_arr);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
ws.send(fbUpdateRequest(1));
|
ws.send(fbUpdateRequests());
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
mouse_arr = [];
|
mouse_arr = [];
|
||||||
@@ -551,12 +581,12 @@ function flushClient() {
|
|||||||
// overridable for testing
|
// overridable for testing
|
||||||
checkEvents = function() {
|
checkEvents = function() {
|
||||||
var now;
|
var now;
|
||||||
if (rfb_state === 'normal') {
|
if (rfb_state === 'normal' && !viewportDragging) {
|
||||||
if (! flushClient()) {
|
if (! flushClient()) {
|
||||||
now = new Date().getTime();
|
now = new Date().getTime();
|
||||||
if (now > last_req_time + conf.fbu_req_rate) {
|
if (now > last_req_time + conf.fbu_req_rate) {
|
||||||
last_req_time = now;
|
last_req_time = now;
|
||||||
ws.send(fbUpdateRequest(1));
|
ws.send(fbUpdateRequests());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,8 +595,11 @@ checkEvents = function() {
|
|||||||
|
|
||||||
keyPress = function(keysym, down) {
|
keyPress = function(keysym, down) {
|
||||||
var arr;
|
var arr;
|
||||||
|
|
||||||
|
if (conf.view_only) { return; } // View only, skip keyboard events
|
||||||
|
|
||||||
arr = keyEvent(keysym, down);
|
arr = keyEvent(keysym, down);
|
||||||
arr = arr.concat(fbUpdateRequest(1));
|
arr = arr.concat(fbUpdateRequests());
|
||||||
ws.send(arr);
|
ws.send(arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -576,13 +609,48 @@ mouseButton = function(x, y, down, bmask) {
|
|||||||
} else {
|
} else {
|
||||||
mouse_buttonMask ^= bmask;
|
mouse_buttonMask ^= bmask;
|
||||||
}
|
}
|
||||||
mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
|
|
||||||
|
if (conf.viewportDrag) {
|
||||||
|
if (down && !viewportDragging) {
|
||||||
|
viewportDragging = true;
|
||||||
|
viewportDragPos = {'x': x, 'y': y};
|
||||||
|
|
||||||
|
// Skip sending mouse events
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
viewportDragging = false;
|
||||||
|
ws.send(fbUpdateRequests()); // Force immediate redraw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf.view_only) { return; } // View only, skip mouse events
|
||||||
|
|
||||||
|
mouse_arr = mouse_arr.concat(
|
||||||
|
pointerEvent(display.absX(x), display.absY(y)) );
|
||||||
flushClient();
|
flushClient();
|
||||||
};
|
};
|
||||||
|
|
||||||
mouseMove = function(x, y) {
|
mouseMove = function(x, y) {
|
||||||
//Util.Debug('>> mouseMove ' + x + "," + y);
|
//Util.Debug('>> mouseMove ' + x + "," + y);
|
||||||
mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
|
var deltaX, deltaY;
|
||||||
|
|
||||||
|
if (viewportDragging) {
|
||||||
|
//deltaX = x - viewportDragPos.x; // drag viewport
|
||||||
|
deltaX = viewportDragPos.x - x; // drag frame buffer
|
||||||
|
//deltaY = y - viewportDragPos.y; // drag viewport
|
||||||
|
deltaY = viewportDragPos.y - y; // drag frame buffer
|
||||||
|
viewportDragPos = {'x': x, 'y': y};
|
||||||
|
|
||||||
|
display.viewportChange(deltaX, deltaY);
|
||||||
|
|
||||||
|
// Skip sending mouse events
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf.view_only) { return; } // View only, skip mouse events
|
||||||
|
|
||||||
|
mouse_arr = mouse_arr.concat(
|
||||||
|
pointerEvent(display.absX(x), display.absY(y)) );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -594,10 +662,10 @@ mouseMove = function(x, y) {
|
|||||||
init_msg = function() {
|
init_msg = function() {
|
||||||
//Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
|
//Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
|
||||||
|
|
||||||
var strlen, reason, length, sversion, cversion,
|
var strlen, reason, length, sversion, cversion, repeaterID,
|
||||||
i, types, num_types, challenge, response, bpp, depth,
|
i, types, num_types, challenge, response, bpp, depth,
|
||||||
big_endian, red_max, green_max, blue_max, red_shift,
|
big_endian, red_max, green_max, blue_max, red_shift,
|
||||||
green_shift, blue_shift, true_color, name_length;
|
green_shift, blue_shift, true_color, name_length, is_repeater;
|
||||||
|
|
||||||
//Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));
|
//Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));
|
||||||
switch (rfb_state) {
|
switch (rfb_state) {
|
||||||
@@ -608,14 +676,27 @@ init_msg = function() {
|
|||||||
}
|
}
|
||||||
sversion = ws.rQshiftStr(12).substr(4,7);
|
sversion = ws.rQshiftStr(12).substr(4,7);
|
||||||
Util.Info("Server ProtocolVersion: " + sversion);
|
Util.Info("Server ProtocolVersion: " + sversion);
|
||||||
|
is_repeater = 0;
|
||||||
switch (sversion) {
|
switch (sversion) {
|
||||||
|
case "000.000": is_repeater = 1; break; // UltraVNC repeater
|
||||||
case "003.003": rfb_version = 3.3; break;
|
case "003.003": rfb_version = 3.3; break;
|
||||||
case "003.006": rfb_version = 3.3; break; // UltraVNC
|
case "003.006": rfb_version = 3.3; break; // UltraVNC
|
||||||
|
case "003.889": rfb_version = 3.3; break; // Apple Remote Desktop
|
||||||
case "003.007": rfb_version = 3.7; break;
|
case "003.007": rfb_version = 3.7; break;
|
||||||
case "003.008": rfb_version = 3.8; break;
|
case "003.008": rfb_version = 3.8; break;
|
||||||
|
case "004.000": rfb_version = 3.8; break; // Intel AMT KVM
|
||||||
|
case "004.001": rfb_version = 3.8; break; // RealVNC 4.6
|
||||||
default:
|
default:
|
||||||
return fail("Invalid server version " + sversion);
|
return fail("Invalid server version " + sversion);
|
||||||
}
|
}
|
||||||
|
if (is_repeater) {
|
||||||
|
repeaterID = conf.repeaterID;
|
||||||
|
while (repeaterID.length < 250) {
|
||||||
|
repeaterID += "\0";
|
||||||
|
}
|
||||||
|
ws.send_string(repeaterID);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (rfb_version > rfb_max_version) {
|
if (rfb_version > rfb_max_version) {
|
||||||
rfb_version = rfb_max_version;
|
rfb_version = rfb_max_version;
|
||||||
}
|
}
|
||||||
@@ -776,9 +857,25 @@ init_msg = function() {
|
|||||||
", green_shift: " + green_shift +
|
", green_shift: " + green_shift +
|
||||||
", blue_shift: " + blue_shift);
|
", blue_shift: " + blue_shift);
|
||||||
|
|
||||||
|
if (big_endian !== 0) {
|
||||||
|
Util.Warn("Server native endian is not little endian");
|
||||||
|
}
|
||||||
|
if (red_shift !== 16) {
|
||||||
|
Util.Warn("Server native red-shift is not 16");
|
||||||
|
}
|
||||||
|
if (blue_shift !== 0) {
|
||||||
|
Util.Warn("Server native blue-shift is not 0");
|
||||||
|
}
|
||||||
|
|
||||||
/* Connection name/title */
|
/* Connection name/title */
|
||||||
name_length = ws.rQshift32();
|
name_length = ws.rQshift32();
|
||||||
fb_name = ws.rQshiftStr(name_length);
|
fb_name = ws.rQshiftStr(name_length);
|
||||||
|
|
||||||
|
if (conf.true_color && fb_name === "Intel(r) AMT KVM")
|
||||||
|
{
|
||||||
|
Util.Warn("Intel AMT KVM only support 8/16 bit depths. Disabling true color");
|
||||||
|
conf.true_color = false;
|
||||||
|
}
|
||||||
|
|
||||||
display.set_true_color(conf.true_color);
|
display.set_true_color(conf.true_color);
|
||||||
display.resize(fb_width, fb_height);
|
display.resize(fb_width, fb_height);
|
||||||
@@ -795,13 +892,13 @@ init_msg = function() {
|
|||||||
|
|
||||||
response = pixelFormat();
|
response = pixelFormat();
|
||||||
response = response.concat(clientEncodings());
|
response = response.concat(clientEncodings());
|
||||||
response = response.concat(fbUpdateRequest(0));
|
response = response.concat(fbUpdateRequests());
|
||||||
timing.fbu_rt_start = (new Date()).getTime();
|
timing.fbu_rt_start = (new Date()).getTime();
|
||||||
|
timing.pixels = 0;
|
||||||
ws.send(response);
|
ws.send(response);
|
||||||
|
|
||||||
/* Start pushing/polling */
|
/* Start pushing/polling */
|
||||||
setTimeout(checkEvents, conf.check_rate);
|
setTimeout(checkEvents, conf.check_rate);
|
||||||
setTimeout(scan_tight_imgQ, scan_imgQ_rate);
|
|
||||||
|
|
||||||
if (conf.encrypt) {
|
if (conf.encrypt) {
|
||||||
updateState('normal', "Connected (encrypted) to: " + fb_name);
|
updateState('normal', "Connected (encrypted) to: " + fb_name);
|
||||||
@@ -835,6 +932,8 @@ normal_msg = function() {
|
|||||||
ws.rQshift8(); // Padding
|
ws.rQshift8(); // Padding
|
||||||
first_colour = ws.rQshift16(); // First colour
|
first_colour = ws.rQshift16(); // First colour
|
||||||
num_colours = ws.rQshift16();
|
num_colours = ws.rQshift16();
|
||||||
|
if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }
|
||||||
|
|
||||||
for (c=0; c < num_colours; c+=1) {
|
for (c=0; c < num_colours; c+=1) {
|
||||||
red = ws.rQshift16();
|
red = ws.rQshift16();
|
||||||
//Util.Debug("red before: " + red);
|
//Util.Debug("red before: " + red);
|
||||||
@@ -842,9 +941,9 @@ normal_msg = function() {
|
|||||||
//Util.Debug("red after: " + red);
|
//Util.Debug("red after: " + red);
|
||||||
green = parseInt(ws.rQshift16() / 256, 10);
|
green = parseInt(ws.rQshift16() / 256, 10);
|
||||||
blue = parseInt(ws.rQshift16() / 256, 10);
|
blue = parseInt(ws.rQshift16() / 256, 10);
|
||||||
Util.Debug("*** colourMap: " + display.get_colourMap());
|
display.set_colourMap([blue, green, red], first_colour + c);
|
||||||
display.set_colourMap([red, green, blue], first_colour + c);
|
|
||||||
}
|
}
|
||||||
|
Util.Debug("colourMap: " + display.get_colourMap());
|
||||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
Util.Info("Registered " + num_colours + " colourMap entries");
|
||||||
//Util.Debug("colourMap: " + display.get_colourMap());
|
//Util.Debug("colourMap: " + display.get_colourMap());
|
||||||
break;
|
break;
|
||||||
@@ -943,9 +1042,10 @@ framebufferUpdate = function() {
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
encStats[FBU.encoding][0] += 1;
|
encStats[FBU.encoding][0] += 1;
|
||||||
encStats[FBU.encoding][1] += 1;
|
encStats[FBU.encoding][1] += 1;
|
||||||
|
timing.pixels += FBU.width * FBU.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FBU.rects === 0) {
|
if (timing.pixels >= (fb_width * fb_height)) {
|
||||||
if (((FBU.width === fb_width) &&
|
if (((FBU.width === fb_width) &&
|
||||||
(FBU.height === fb_height)) ||
|
(FBU.height === fb_height)) ||
|
||||||
(timing.fbu_rt_start > 0)) {
|
(timing.fbu_rt_start > 0)) {
|
||||||
@@ -1023,9 +1123,14 @@ encHandlers.COPYRECT = function display_copy_rect() {
|
|||||||
var old_x, old_y;
|
var old_x, old_y;
|
||||||
|
|
||||||
if (ws.rQwait("COPYRECT", 4)) { return false; }
|
if (ws.rQwait("COPYRECT", 4)) { return false; }
|
||||||
old_x = ws.rQshift16();
|
display.renderQ_push({
|
||||||
old_y = ws.rQshift16();
|
'type': 'copy',
|
||||||
display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
|
'old_x': ws.rQshift16(),
|
||||||
|
'old_y': ws.rQshift16(),
|
||||||
|
'x': FBU.x,
|
||||||
|
'y': FBU.y,
|
||||||
|
'width': FBU.width,
|
||||||
|
'height': FBU.height});
|
||||||
FBU.rects -= 1;
|
FBU.rects -= 1;
|
||||||
FBU.bytes = 0;
|
FBU.bytes = 0;
|
||||||
return true;
|
return true;
|
||||||
@@ -1066,7 +1171,7 @@ encHandlers.RRE = function display_rre() {
|
|||||||
|
|
||||||
encHandlers.HEXTILE = function display_hextile() {
|
encHandlers.HEXTILE = function display_hextile() {
|
||||||
//Util.Debug(">> display_hextile");
|
//Util.Debug(">> display_hextile");
|
||||||
var subencoding, subrects, tile, color, cur_tile,
|
var subencoding, subrects, color, cur_tile,
|
||||||
tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,
|
tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,
|
||||||
rQ = ws.get_rQ(), rQi = ws.get_rQi();
|
rQ = ws.get_rQ(), rQi = ws.get_rQi();
|
||||||
|
|
||||||
@@ -1155,7 +1260,7 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||||||
rQi += fb_Bpp;
|
rQi += fb_Bpp;
|
||||||
}
|
}
|
||||||
|
|
||||||
tile = display.getTile(x, y, w, h, FBU.background);
|
display.startTile(x, y, w, h, FBU.background);
|
||||||
if (FBU.subencoding & 0x08) { // AnySubrects
|
if (FBU.subencoding & 0x08) { // AnySubrects
|
||||||
subrects = rQ[rQi];
|
subrects = rQ[rQi];
|
||||||
rQi += 1;
|
rQi += 1;
|
||||||
@@ -1176,10 +1281,10 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||||||
sw = (wh >> 4) + 1;
|
sw = (wh >> 4) + 1;
|
||||||
sh = (wh & 0x0f) + 1;
|
sh = (wh & 0x0f) + 1;
|
||||||
|
|
||||||
display.setSubTile(tile, sx, sy, sw, sh, color);
|
display.subTile(sx, sy, sw, sh, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
display.putTile(tile);
|
display.finishTile();
|
||||||
}
|
}
|
||||||
ws.set_rQi(rQi);
|
ws.set_rQi(rQi);
|
||||||
FBU.lastsubencoding = FBU.subencoding;
|
FBU.lastsubencoding = FBU.subencoding;
|
||||||
@@ -1196,42 +1301,197 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
encHandlers.TIGHT_PNG = function display_tight_png() {
|
// Get 'compact length' header and data size
|
||||||
//Util.Debug(">> display_tight_png");
|
getTightCLength = function (arr) {
|
||||||
var ctl, cmode, clength, getCLength, color, img;
|
var header = 1, data = 0;
|
||||||
//Util.Debug(" FBU.rects: " + FBU.rects);
|
data += arr[0] & 0x7f;
|
||||||
//Util.Debug(" starting ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
|
if (arr[0] & 0x80) {
|
||||||
|
header += 1;
|
||||||
|
data += (arr[1] & 0x7f) << 7;
|
||||||
|
if (arr[1] & 0x80) {
|
||||||
|
header += 1;
|
||||||
|
data += arr[2] << 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [header, data];
|
||||||
|
};
|
||||||
|
|
||||||
|
function display_tight(isTightPNG) {
|
||||||
|
//Util.Debug(">> display_tight");
|
||||||
|
|
||||||
|
if (fb_depth === 1) {
|
||||||
|
fail("Tight protocol handler only implements true color mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctl, cmode, clength, color, img, data;
|
||||||
|
var filterId = -1, resetStreams = 0, streamId = -1;
|
||||||
|
var rQ = ws.get_rQ(), rQi = ws.get_rQi();
|
||||||
|
|
||||||
FBU.bytes = 1; // compression-control byte
|
FBU.bytes = 1; // compression-control byte
|
||||||
if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }
|
if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }
|
||||||
|
|
||||||
// Get 'compact length' header and data size
|
var checksum = function(data) {
|
||||||
getCLength = function (arr) {
|
var sum=0, i;
|
||||||
var header = 1, data = 0;
|
for (i=0; i<data.length;i++) {
|
||||||
data += arr[0] & 0x7f;
|
sum += data[i];
|
||||||
if (arr[0] & 0x80) {
|
if (sum > 65536) sum -= 65536;
|
||||||
header += 1;
|
}
|
||||||
data += (arr[1] & 0x7f) << 7;
|
return sum;
|
||||||
if (arr[1] & 0x80) {
|
}
|
||||||
header += 1;
|
|
||||||
data += arr[2] << 14;
|
var decompress = function(data) {
|
||||||
|
for (var i=0; i<4; i++) {
|
||||||
|
if ((resetStreams >> i) & 1) {
|
||||||
|
FBU.zlibs[i].reset();
|
||||||
|
Util.Info("Reset zlib stream " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [header, data];
|
var uncompressed = FBU.zlibs[streamId].uncompress(data, 0);
|
||||||
};
|
if (uncompressed.status !== 0) {
|
||||||
|
Util.Error("Invalid data in zlib stream");
|
||||||
|
}
|
||||||
|
//Util.Warn("Decompressed " + data.length + " to " +
|
||||||
|
// uncompressed.data.length + " checksums " +
|
||||||
|
// checksum(data) + ":" + checksum(uncompressed.data));
|
||||||
|
|
||||||
|
return uncompressed.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlePalette = function() {
|
||||||
|
var numColors = rQ[rQi + 2] + 1;
|
||||||
|
var paletteSize = numColors * fb_depth;
|
||||||
|
FBU.bytes += paletteSize;
|
||||||
|
if (ws.rQwait("TIGHT palette " + cmode, FBU.bytes)) { return false; }
|
||||||
|
|
||||||
|
var bpp = (numColors <= 2) ? 1 : 8;
|
||||||
|
var rowSize = Math.floor((FBU.width * bpp + 7) / 8);
|
||||||
|
var raw = false;
|
||||||
|
if (rowSize * FBU.height < 12) {
|
||||||
|
raw = true;
|
||||||
|
clength = [0, rowSize * FBU.height];
|
||||||
|
} else {
|
||||||
|
clength = getTightCLength(ws.rQslice(3 + paletteSize,
|
||||||
|
3 + paletteSize + 3));
|
||||||
|
}
|
||||||
|
FBU.bytes += clength[0] + clength[1];
|
||||||
|
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
||||||
|
|
||||||
|
// Shift ctl, filter id, num colors, palette entries, and clength off
|
||||||
|
ws.rQshiftBytes(3);
|
||||||
|
var palette = ws.rQshiftBytes(paletteSize);
|
||||||
|
ws.rQshiftBytes(clength[0]);
|
||||||
|
|
||||||
|
if (raw) {
|
||||||
|
data = ws.rQshiftBytes(clength[1]);
|
||||||
|
} else {
|
||||||
|
data = decompress(ws.rQshiftBytes(clength[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert indexed (palette based) image data to RGB
|
||||||
|
// TODO: reduce number of calculations inside loop
|
||||||
|
var dest = [];
|
||||||
|
var x, y, b, w, w1, dp, sp;
|
||||||
|
if (numColors === 2) {
|
||||||
|
w = Math.floor((FBU.width + 7) / 8);
|
||||||
|
w1 = Math.floor(FBU.width / 8);
|
||||||
|
for (y = 0; y < FBU.height; y++) {
|
||||||
|
for (x = 0; x < w1; x++) {
|
||||||
|
for (b = 7; b >= 0; b--) {
|
||||||
|
dp = (y*FBU.width + x*8 + 7-b) * 3;
|
||||||
|
sp = (data[y*w + x] >> b & 1) * 3;
|
||||||
|
dest[dp ] = palette[sp ];
|
||||||
|
dest[dp+1] = palette[sp+1];
|
||||||
|
dest[dp+2] = palette[sp+2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (b = 7; b >= 8 - FBU.width % 8; b--) {
|
||||||
|
dp = (y*FBU.width + x*8 + 7-b) * 3;
|
||||||
|
sp = (data[y*w + x] >> b & 1) * 3;
|
||||||
|
dest[dp ] = palette[sp ];
|
||||||
|
dest[dp+1] = palette[sp+1];
|
||||||
|
dest[dp+2] = palette[sp+2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < FBU.height; y++) {
|
||||||
|
for (x = 0; x < FBU.width; x++) {
|
||||||
|
dp = (y*FBU.width + x) * 3;
|
||||||
|
sp = data[y*FBU.width + x] * 3;
|
||||||
|
dest[dp ] = palette[sp ];
|
||||||
|
dest[dp+1] = palette[sp+1];
|
||||||
|
dest[dp+2] = palette[sp+2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display.renderQ_push({
|
||||||
|
'type': 'blitRgb',
|
||||||
|
'data': dest,
|
||||||
|
'x': FBU.x,
|
||||||
|
'y': FBU.y,
|
||||||
|
'width': FBU.width,
|
||||||
|
'height': FBU.height});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handleCopy = function() {
|
||||||
|
var raw = false;
|
||||||
|
var uncompressedSize = FBU.width * FBU.height * fb_depth;
|
||||||
|
if (uncompressedSize < 12) {
|
||||||
|
raw = true;
|
||||||
|
clength = [0, uncompressedSize];
|
||||||
|
} else {
|
||||||
|
clength = getTightCLength(ws.rQslice(1, 4));
|
||||||
|
}
|
||||||
|
FBU.bytes = 1 + clength[0] + clength[1];
|
||||||
|
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
||||||
|
|
||||||
|
// Shift ctl, clength off
|
||||||
|
ws.rQshiftBytes(1 + clength[0]);
|
||||||
|
|
||||||
|
if (raw) {
|
||||||
|
data = ws.rQshiftBytes(clength[1]);
|
||||||
|
} else {
|
||||||
|
data = decompress(ws.rQshiftBytes(clength[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
display.renderQ_push({
|
||||||
|
'type': 'blitRgb',
|
||||||
|
'data': data,
|
||||||
|
'x': FBU.x,
|
||||||
|
'y': FBU.y,
|
||||||
|
'width': FBU.width,
|
||||||
|
'height': FBU.height});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ctl = ws.rQpeek8();
|
ctl = ws.rQpeek8();
|
||||||
switch (ctl >> 4) {
|
|
||||||
case 0x08: cmode = "fill"; break;
|
// Keep tight reset bits
|
||||||
case 0x09: cmode = "jpeg"; break;
|
resetStreams = ctl & 0xF;
|
||||||
case 0x0A: cmode = "png"; break;
|
|
||||||
default: throw("Illegal basic compression received, ctl: " + ctl);
|
// Figure out filter
|
||||||
|
ctl = ctl >> 4;
|
||||||
|
streamId = ctl & 0x3;
|
||||||
|
|
||||||
|
if (ctl === 0x08) cmode = "fill";
|
||||||
|
else if (ctl === 0x09) cmode = "jpeg";
|
||||||
|
else if (ctl === 0x0A) cmode = "png";
|
||||||
|
else if (ctl & 0x04) cmode = "filter";
|
||||||
|
else if (ctl < 0x04) cmode = "copy";
|
||||||
|
else return fail("Illegal tight compression received, ctl: " + ctl);
|
||||||
|
|
||||||
|
if (isTightPNG && (cmode === "filter" || cmode === "copy")) {
|
||||||
|
return fail("filter/copy received in tightPNG mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cmode) {
|
switch (cmode) {
|
||||||
// fill uses fb_depth because TPIXELs drop the padding byte
|
// fill uses fb_depth because TPIXELs drop the padding byte
|
||||||
case "fill": FBU.bytes += fb_depth; break; // TPIXEL
|
case "fill": FBU.bytes += fb_depth; break; // TPIXEL
|
||||||
case "jpeg": FBU.bytes += 3; break; // max clength
|
case "jpeg": FBU.bytes += 3; break; // max clength
|
||||||
case "png": FBU.bytes += 3; break; // max clength
|
case "png": FBU.bytes += 3; break; // max clength
|
||||||
|
case "filter": FBU.bytes += 2; break; // filter id + num colors if palette
|
||||||
|
case "copy": break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
||||||
@@ -1244,42 +1504,55 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
|
|||||||
case "fill":
|
case "fill":
|
||||||
ws.rQshift8(); // shift off ctl
|
ws.rQshift8(); // shift off ctl
|
||||||
color = ws.rQshiftBytes(fb_depth);
|
color = ws.rQshiftBytes(fb_depth);
|
||||||
FBU.imgQ.push({
|
display.renderQ_push({
|
||||||
'type': 'fill',
|
'type': 'fill',
|
||||||
'img': {'complete': true},
|
|
||||||
'x': FBU.x,
|
'x': FBU.x,
|
||||||
'y': FBU.y,
|
'y': FBU.y,
|
||||||
'width': FBU.width,
|
'width': FBU.width,
|
||||||
'height': FBU.height,
|
'height': FBU.height,
|
||||||
'color': color});
|
'color': [color[2], color[1], color[0]] });
|
||||||
break;
|
break;
|
||||||
case "jpeg":
|
|
||||||
case "png":
|
case "png":
|
||||||
clength = getCLength(ws.rQslice(1, 4));
|
case "jpeg":
|
||||||
|
clength = getTightCLength(ws.rQslice(1, 4));
|
||||||
FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
|
FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
|
||||||
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
|
||||||
|
|
||||||
// We have everything, render it
|
// We have everything, render it
|
||||||
//Util.Debug(" png, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]);
|
//Util.Debug(" jpeg, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " +
|
||||||
|
// clength[0] + ", clength[1]: " + clength[1]);
|
||||||
ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
|
ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
|
||||||
img = new Image();
|
img = new Image();
|
||||||
//img.onload = scan_tight_imgQ;
|
img.src = "data:image/" + cmode +
|
||||||
FBU.imgQ.push({
|
extract_data_uri(ws.rQshiftBytes(clength[1]));
|
||||||
|
display.renderQ_push({
|
||||||
'type': 'img',
|
'type': 'img',
|
||||||
'img': img,
|
'img': img,
|
||||||
'x': FBU.x,
|
'x': FBU.x,
|
||||||
'y': FBU.y});
|
'y': FBU.y});
|
||||||
img.src = "data:image/" + cmode +
|
|
||||||
extract_data_uri(ws.rQshiftBytes(clength[1]));
|
|
||||||
img = null;
|
img = null;
|
||||||
break;
|
break;
|
||||||
|
case "filter":
|
||||||
|
filterId = rQ[rQi + 1];
|
||||||
|
if (filterId === 1) {
|
||||||
|
if (!handlePalette()) { return false; }
|
||||||
|
} else {
|
||||||
|
// Filter 0, Copy could be valid here, but servers don't send it as an explicit filter
|
||||||
|
// Filter 2, Gradient is valid but not used if jpeg is enabled
|
||||||
|
throw("Unsupported tight subencoding received, filter: " + filterId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "copy":
|
||||||
|
if (!handleCopy()) { return false; }
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
FBU.bytes = 0;
|
FBU.bytes = 0;
|
||||||
FBU.rects -= 1;
|
FBU.rects -= 1;
|
||||||
//Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
|
//Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
|
||||||
//Util.Debug("<< display_tight_png");
|
//Util.Debug("<< display_tight_png");
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
extract_data_uri = function(arr) {
|
extract_data_uri = function(arr) {
|
||||||
//var i, stra = [];
|
//var i, stra = [];
|
||||||
@@ -1290,21 +1563,14 @@ extract_data_uri = function(arr) {
|
|||||||
return ";base64," + Base64.encode(arr);
|
return ";base64," + Base64.encode(arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
scan_tight_imgQ = function() {
|
encHandlers.TIGHT = function () { return display_tight(false); };
|
||||||
var data, imgQ, ctx;
|
encHandlers.TIGHT_PNG = function () { return display_tight(true); };
|
||||||
ctx = display.get_context();
|
|
||||||
if (rfb_state === 'normal') {
|
encHandlers.last_rect = function last_rect() {
|
||||||
imgQ = FBU.imgQ;
|
//Util.Debug(">> last_rect");
|
||||||
while ((imgQ.length > 0) && (imgQ[0].img.complete)) {
|
FBU.rects = 0;
|
||||||
data = imgQ.shift();
|
//Util.Debug("<< last_rect");
|
||||||
if (data['type'] === 'fill') {
|
return true;
|
||||||
display.fillRect(data.x, data.y, data.width, data.height, data.color);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(data.img, data.x, data.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTimeout(scan_tight_imgQ, scan_imgQ_rate);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
encHandlers.DesktopSize = function set_desktopsize() {
|
encHandlers.DesktopSize = function set_desktopsize() {
|
||||||
@@ -1314,7 +1580,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
|
|||||||
display.resize(fb_width, fb_height);
|
display.resize(fb_width, fb_height);
|
||||||
timing.fbu_rt_start = (new Date()).getTime();
|
timing.fbu_rt_start = (new Date()).getTime();
|
||||||
// Send a new non-incremental request
|
// Send a new non-incremental request
|
||||||
ws.send(fbUpdateRequest(0));
|
ws.send(fbUpdateRequests());
|
||||||
|
|
||||||
FBU.bytes = 0;
|
FBU.bytes = 0;
|
||||||
FBU.rects -= 1;
|
FBU.rects -= 1;
|
||||||
@@ -1325,7 +1591,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
|
|||||||
|
|
||||||
encHandlers.Cursor = function set_cursor() {
|
encHandlers.Cursor = function set_cursor() {
|
||||||
var x, y, w, h, pixelslength, masklength;
|
var x, y, w, h, pixelslength, masklength;
|
||||||
//Util.Debug(">> set_cursor");
|
Util.Debug(">> set_cursor");
|
||||||
x = FBU.x; // hotspot-x
|
x = FBU.x; // hotspot-x
|
||||||
y = FBU.y; // hotspot-y
|
y = FBU.y; // hotspot-y
|
||||||
w = FBU.width;
|
w = FBU.width;
|
||||||
@@ -1346,7 +1612,7 @@ encHandlers.Cursor = function set_cursor() {
|
|||||||
FBU.bytes = 0;
|
FBU.bytes = 0;
|
||||||
FBU.rects -= 1;
|
FBU.rects -= 1;
|
||||||
|
|
||||||
//Util.Debug("<< set_cursor");
|
Util.Debug("<< set_cursor");
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1378,9 +1644,9 @@ pixelFormat = function() {
|
|||||||
arr.push16(255); // red-max
|
arr.push16(255); // red-max
|
||||||
arr.push16(255); // green-max
|
arr.push16(255); // green-max
|
||||||
arr.push16(255); // blue-max
|
arr.push16(255); // blue-max
|
||||||
arr.push8(0); // red-shift
|
arr.push8(16); // red-shift
|
||||||
arr.push8(8); // green-shift
|
arr.push8(8); // green-shift
|
||||||
arr.push8(16); // blue-shift
|
arr.push8(0); // blue-shift
|
||||||
|
|
||||||
arr.push8(0); // padding
|
arr.push8(0); // padding
|
||||||
arr.push8(0); // padding
|
arr.push8(0); // padding
|
||||||
@@ -1416,10 +1682,10 @@ clientEncodings = function() {
|
|||||||
|
|
||||||
fbUpdateRequest = function(incremental, x, y, xw, yw) {
|
fbUpdateRequest = function(incremental, x, y, xw, yw) {
|
||||||
//Util.Debug(">> fbUpdateRequest");
|
//Util.Debug(">> fbUpdateRequest");
|
||||||
if (!x) { x = 0; }
|
if (typeof(x) === "undefined") { x = 0; }
|
||||||
if (!y) { y = 0; }
|
if (typeof(y) === "undefined") { y = 0; }
|
||||||
if (!xw) { xw = fb_width; }
|
if (typeof(xw) === "undefined") { xw = fb_width; }
|
||||||
if (!yw) { yw = fb_height; }
|
if (typeof(yw) === "undefined") { yw = fb_height; }
|
||||||
var arr;
|
var arr;
|
||||||
arr = [3]; // msg-type
|
arr = [3]; // msg-type
|
||||||
arr.push8(incremental);
|
arr.push8(incremental);
|
||||||
@@ -1431,6 +1697,26 @@ fbUpdateRequest = function(incremental, x, y, xw, yw) {
|
|||||||
return arr;
|
return arr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Based on clean/dirty areas, generate requests to send
|
||||||
|
fbUpdateRequests = function() {
|
||||||
|
var cleanDirty = display.getCleanDirtyReset(),
|
||||||
|
arr = [], i, cb, db;
|
||||||
|
|
||||||
|
cb = cleanDirty.cleanBox;
|
||||||
|
if (cb.w > 0 && cb.h > 0) {
|
||||||
|
// Request incremental for clean box
|
||||||
|
arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h));
|
||||||
|
}
|
||||||
|
for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
|
||||||
|
db = cleanDirty.dirtyBoxes[i];
|
||||||
|
// Force all (non-incremental for dirty box
|
||||||
|
arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
keyEvent = function(keysym, down) {
|
keyEvent = function(keysym, down) {
|
||||||
//Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
|
//Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
|
||||||
var arr;
|
var arr;
|
||||||
@@ -1476,13 +1762,13 @@ clientCutText = function(text) {
|
|||||||
// Public API interface functions
|
// Public API interface functions
|
||||||
//
|
//
|
||||||
|
|
||||||
that.connect = function(host, port, password, uri) {
|
that.connect = function(host, port, password, path) {
|
||||||
//Util.Debug(">> connect");
|
//Util.Debug(">> connect");
|
||||||
|
|
||||||
rfb_host = host;
|
rfb_host = host;
|
||||||
rfb_port = port;
|
rfb_port = port;
|
||||||
rfb_password = (password !== undefined) ? password : "";
|
rfb_password = (password !== undefined) ? password : "";
|
||||||
rfb_uri = (uri !== undefined) ? uri : "";
|
rfb_path = (path !== undefined) ? path : "";
|
||||||
|
|
||||||
if ((!rfb_host) || (!rfb_port)) {
|
if ((!rfb_host) || (!rfb_port)) {
|
||||||
return fail("Must set host and port");
|
return fail("Must set host and port");
|
||||||
@@ -1506,7 +1792,7 @@ that.sendPassword = function(passwd) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
that.sendCtrlAltDel = function() {
|
that.sendCtrlAltDel = function() {
|
||||||
if (rfb_state !== "normal") { return false; }
|
if (rfb_state !== "normal" || conf.view_only) { return false; }
|
||||||
Util.Info("Sending Ctrl-Alt-Del");
|
Util.Info("Sending Ctrl-Alt-Del");
|
||||||
var arr = [];
|
var arr = [];
|
||||||
arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
|
arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
|
||||||
@@ -1515,14 +1801,14 @@ that.sendCtrlAltDel = function() {
|
|||||||
arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
|
arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
|
||||||
arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
|
arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
|
||||||
arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
|
arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
|
||||||
arr = arr.concat(fbUpdateRequest(1));
|
arr = arr.concat(fbUpdateRequests());
|
||||||
ws.send(arr);
|
ws.send(arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send a key press. If 'down' is not specified then send a down key
|
// Send a key press. If 'down' is not specified then send a down key
|
||||||
// followed by an up key.
|
// followed by an up key.
|
||||||
that.sendKey = function(code, down) {
|
that.sendKey = function(code, down) {
|
||||||
if (rfb_state !== "normal") { return false; }
|
if (rfb_state !== "normal" || conf.view_only) { return false; }
|
||||||
var arr = [];
|
var arr = [];
|
||||||
if (typeof down !== 'undefined') {
|
if (typeof down !== 'undefined') {
|
||||||
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
|
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
|
||||||
@@ -1532,7 +1818,7 @@ that.sendKey = function(code, down) {
|
|||||||
arr = arr.concat(keyEvent(code, 1));
|
arr = arr.concat(keyEvent(code, 1));
|
||||||
arr = arr.concat(keyEvent(code, 0));
|
arr = arr.concat(keyEvent(code, 0));
|
||||||
}
|
}
|
||||||
arr = arr.concat(fbUpdateRequest(1));
|
arr = arr.concat(fbUpdateRequests());
|
||||||
ws.send(arr);
|
ws.send(arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
@@ -12,150 +12,110 @@
|
|||||||
|
|
||||||
var UI = {
|
var UI = {
|
||||||
|
|
||||||
|
rfb_state : 'loaded',
|
||||||
settingsOpen : false,
|
settingsOpen : false,
|
||||||
|
connSettingsOpen : false,
|
||||||
|
clipboardOpen: false,
|
||||||
|
keyboardVisible: false,
|
||||||
|
|
||||||
// Render default UI and initialize settings menu
|
// Render default UI and initialize settings menu
|
||||||
load: function(target) {
|
load: function() {
|
||||||
var html = '', i, sheet, sheets, llevels;
|
var html = '', i, sheet, sheets, llevels;
|
||||||
|
|
||||||
/* Populate the 'target' DOM element with default UI */
|
|
||||||
if (!target) {
|
|
||||||
target = $D('vnc');
|
|
||||||
} else if (typeof target === 'string') {
|
|
||||||
target = $D(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!document.createElement('canvas').getContext) &&
|
|
||||||
window.ActiveXObject) {
|
|
||||||
// Suggest Chrome frame for Internet Explorer users
|
|
||||||
html += '<center><div style="text-align: left; width: 400px">';
|
|
||||||
html += ' You are using a version of Internet Explorer ';
|
|
||||||
html += ' that does not have HTML5 Canvas support. ';
|
|
||||||
html += ' To use noVNC you must use a browser with HTML5 ';
|
|
||||||
html += ' Canvas support or install ';
|
|
||||||
html += ' <a href="http://google.com/chromeframe" target="cframe">';
|
|
||||||
html += ' Google Chrome Frame.</a>';
|
|
||||||
html += '</div></center>';
|
|
||||||
target.innerHTML = html;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '<div id="VNC_controls">';
|
|
||||||
html += ' <ul>';
|
|
||||||
html += ' <li>Host: <input id="VNC_host"></li>';
|
|
||||||
html += ' <li>Port: <input id="VNC_port"></li>';
|
|
||||||
html += ' <li>Password: <input id="VNC_password"';
|
|
||||||
html += ' type="password"></li>';
|
|
||||||
html += ' <li><input id="VNC_connect_button" type="button"';
|
|
||||||
html += ' value="Loading" disabled></li>';
|
|
||||||
html += ' </ul>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '<div id="VNC_screen">';
|
|
||||||
html += ' <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">';
|
|
||||||
html += ' <table border=0 width=100%><tr>';
|
|
||||||
html += ' <td><div id="VNC_status">Loading</div></td>';
|
|
||||||
html += ' <td width=1%><div class="VNC_buttons_right">';
|
|
||||||
html += ' <input type=button class="VNC_status_button" value="Settings"';
|
|
||||||
html += ' id="menuButton"';
|
|
||||||
html += ' onclick="UI.clickSettingsMenu();">';
|
|
||||||
html += ' <span id="VNC_settings_menu"';
|
|
||||||
html += ' onmouseover="UI.displayBlur();"';
|
|
||||||
html += ' onmouseout="UI.displayFocus();">';
|
|
||||||
html += ' <ul>';
|
|
||||||
html += ' <li><input id="VNC_encrypt"';
|
|
||||||
html += ' type="checkbox"> Encrypt</li>';
|
|
||||||
html += ' <li><input id="VNC_true_color"';
|
|
||||||
html += ' type="checkbox" checked> True Color</li>';
|
|
||||||
html += ' <li><input id="VNC_cursor"';
|
|
||||||
html += ' type="checkbox"> Local Cursor</li>';
|
|
||||||
html += ' <li><input id="VNC_shared"';
|
|
||||||
html += ' type="checkbox"> Shared Mode</li>';
|
|
||||||
html += ' <li><input id="VNC_connectTimeout"';
|
|
||||||
html += ' type="input"> Connect Timeout (s)</li>';
|
|
||||||
html += ' <hr>';
|
|
||||||
|
|
||||||
// Stylesheet selection dropdown
|
// Stylesheet selection dropdown
|
||||||
html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
|
|
||||||
html += ' <option value="default">default</option>';
|
|
||||||
sheet = WebUtil.selectStylesheet();
|
sheet = WebUtil.selectStylesheet();
|
||||||
sheets = WebUtil.getStylesheets();
|
sheets = WebUtil.getStylesheets();
|
||||||
for (i = 0; i < sheets.length; i += 1) {
|
for (i = 0; i < sheets.length; i += 1) {
|
||||||
html += '<option value="' + sheets[i].title + '">' + sheets[i].title + '</option>';
|
UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
|
||||||
}
|
}
|
||||||
html += ' </select> Style</li>';
|
|
||||||
|
|
||||||
// Logging selection dropdown
|
// Logging selection dropdown
|
||||||
html += ' <li><select id="VNC_logging" name="vncLogging">';
|
|
||||||
llevels = ['error', 'warn', 'info', 'debug'];
|
llevels = ['error', 'warn', 'info', 'debug'];
|
||||||
for (i = 0; i < llevels.length; i += 1) {
|
for (i = 0; i < llevels.length; i += 1) {
|
||||||
html += '<option value="' + llevels[i] + '">' + llevels[i] + '</option>';
|
UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
|
||||||
}
|
}
|
||||||
html += ' </select> Logging</li>';
|
|
||||||
|
|
||||||
html += ' <hr>';
|
|
||||||
html += ' <li><input type="button" id="VNC_apply" value="Apply"';
|
|
||||||
html += ' onclick="UI.settingsApply()"></li>';
|
|
||||||
html += ' </ul>';
|
|
||||||
html += ' </span></div></td>';
|
|
||||||
html += ' <td width=1%><div class="VNC_buttons_right">';
|
|
||||||
html += ' <input type=button class="VNC_status_button" value="Send CtrlAltDel"';
|
|
||||||
html += ' id="sendCtrlAltDelButton"';
|
|
||||||
html += ' onclick="UI.sendCtrlAltDel();"></div></td>';
|
|
||||||
html += ' </tr></table>';
|
|
||||||
html += ' </div>';
|
|
||||||
html += ' <canvas id="VNC_canvas" width="640px" height="20px">';
|
|
||||||
html += ' Canvas not supported.';
|
|
||||||
html += ' </canvas>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '<br><br>';
|
|
||||||
html += '<div id="VNC_clipboard">';
|
|
||||||
html += ' VNC Clipboard:';
|
|
||||||
html += ' <input id="VNC_clipboard_clear_button"';
|
|
||||||
html += ' type="button" value="Clear"';
|
|
||||||
html += ' onclick="UI.clipClear();">';
|
|
||||||
html += ' <br>';
|
|
||||||
html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5';
|
|
||||||
html += ' onfocus="UI.displayBlur();"';
|
|
||||||
html += ' onblur="UI.displayFocus();"';
|
|
||||||
html += ' onchange="UI.clipSend();"></textarea>';
|
|
||||||
html += '</div>';
|
|
||||||
target.innerHTML = html;
|
|
||||||
|
|
||||||
// Settings with immediate effects
|
// Settings with immediate effects
|
||||||
UI.initSetting('logging', 'warn');
|
UI.initSetting('logging', 'warn');
|
||||||
WebUtil.init_logging(UI.getSetting('logging'));
|
WebUtil.init_logging(UI.getSetting('logging'));
|
||||||
UI.initSetting('stylesheet', 'default');
|
|
||||||
|
|
||||||
WebUtil.selectStylesheet(null); // call twice to get around webkit bug
|
UI.initSetting('stylesheet', 'default');
|
||||||
|
WebUtil.selectStylesheet(null);
|
||||||
|
// call twice to get around webkit bug
|
||||||
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
||||||
|
|
||||||
/* Populate the controls if defaults are provided in the URL */
|
/* Populate the controls if defaults are provided in the URL */
|
||||||
UI.initSetting('host', '');
|
UI.initSetting('host', window.location.hostname);
|
||||||
UI.initSetting('port', '');
|
UI.initSetting('port', window.location.port);
|
||||||
UI.initSetting('password', '');
|
UI.initSetting('password', '');
|
||||||
UI.initSetting('encrypt', false);
|
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||||
UI.initSetting('true_color', true);
|
UI.initSetting('true_color', true);
|
||||||
UI.initSetting('cursor', false);
|
UI.initSetting('cursor', false);
|
||||||
UI.initSetting('shared', true);
|
UI.initSetting('shared', true);
|
||||||
|
UI.initSetting('view_only', false);
|
||||||
UI.initSetting('connectTimeout', 2);
|
UI.initSetting('connectTimeout', 2);
|
||||||
|
UI.initSetting('path', 'websockify');
|
||||||
|
UI.initSetting('repeaterID', '');
|
||||||
|
|
||||||
UI.rfb = RFB({'target': $D('VNC_canvas'),
|
UI.rfb = RFB({'target': $D('noVNC_canvas'),
|
||||||
'onUpdateState': UI.updateState,
|
'onUpdateState': UI.updateState,
|
||||||
'onClipboard': UI.clipReceive});
|
'onClipboard': UI.clipReceive});
|
||||||
|
UI.updateVisualState();
|
||||||
|
|
||||||
// Unfocus clipboard when over the VNC area
|
// Unfocus clipboard when over the VNC area
|
||||||
$D('VNC_screen').onmousemove = function () {
|
//$D('VNC_screen').onmousemove = function () {
|
||||||
var keyboard = UI.rfb.get_keyboard();
|
// var keyboard = UI.rfb.get_keyboard();
|
||||||
if ((! keyboard) || (! keyboard.get_focused())) {
|
// if ((! keyboard) || (! keyboard.get_focused())) {
|
||||||
$D('VNC_clipboard_text').blur();
|
// $D('VNC_clipboard_text').blur();
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
// Show mouse selector buttons on touch screen devices
|
||||||
|
if ('ontouchstart' in document.documentElement) {
|
||||||
|
// Show mobile buttons
|
||||||
|
$D('noVNC_mobile_buttons').style.display = "inline";
|
||||||
|
UI.setMouseButton();
|
||||||
|
// Remove the address bar
|
||||||
|
setTimeout(function() { window.scrollTo(0, 1); }, 100);
|
||||||
|
UI.forceSetting('clip', true);
|
||||||
|
$D('noVNC_clip').disabled = true;
|
||||||
|
} else {
|
||||||
|
UI.initSetting('clip', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//iOS Safari does not support CSS position:fixed.
|
||||||
|
//This detects iOS devices and enables javascript workaround.
|
||||||
|
if ((navigator.userAgent.match(/iPhone/i)) ||
|
||||||
|
(navigator.userAgent.match(/iPod/i)) ||
|
||||||
|
(navigator.userAgent.match(/iPad/i))) {
|
||||||
|
//UI.setOnscroll();
|
||||||
|
//UI.setResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
$D('noVNC_host').focus();
|
||||||
|
|
||||||
|
UI.setViewClip();
|
||||||
|
Util.addEvent(window, 'resize', UI.setViewClip);
|
||||||
|
|
||||||
|
Util.addEvent(window, 'beforeunload', function () {
|
||||||
|
if (UI.rfb_state === 'normal') {
|
||||||
|
return "You are currently connected.";
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Show description by default when hosted at for kanaka.github.com
|
||||||
|
if (location.host === "kanaka.github.com") {
|
||||||
|
// Open the description dialog
|
||||||
|
$D('noVNC_description').style.display = "block";
|
||||||
|
} else {
|
||||||
|
// Open the connect panel on first load
|
||||||
|
UI.toggleConnectPanel();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Read form control compatible setting from cookie
|
// Read form control compatible setting from cookie
|
||||||
getSetting: function(name) {
|
getSetting: function(name) {
|
||||||
var val, ctrl = $D('VNC_' + name);
|
var val, ctrl = $D('noVNC_' + name);
|
||||||
val = WebUtil.readCookie(name);
|
val = WebUtil.readCookie(name);
|
||||||
if (ctrl.type === 'checkbox') {
|
if (ctrl.type === 'checkbox') {
|
||||||
if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
|
if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
|
||||||
@@ -170,7 +130,8 @@ getSetting: function(name) {
|
|||||||
// Update cookie and form control setting. If value is not set, then
|
// Update cookie and form control setting. If value is not set, then
|
||||||
// updates from control to current cookie setting.
|
// updates from control to current cookie setting.
|
||||||
updateSetting: function(name, value) {
|
updateSetting: function(name, value) {
|
||||||
var i, ctrl = $D('VNC_' + name);
|
|
||||||
|
var i, ctrl = $D('noVNC_' + name);
|
||||||
// Save the cookie for this session
|
// Save the cookie for this session
|
||||||
if (typeof value !== 'undefined') {
|
if (typeof value !== 'undefined') {
|
||||||
WebUtil.createCookie(name, value);
|
WebUtil.createCookie(name, value);
|
||||||
@@ -178,8 +139,10 @@ updateSetting: function(name, value) {
|
|||||||
|
|
||||||
// Update the settings control
|
// Update the settings control
|
||||||
value = UI.getSetting(name);
|
value = UI.getSetting(name);
|
||||||
|
|
||||||
if (ctrl.type === 'checkbox') {
|
if (ctrl.type === 'checkbox') {
|
||||||
ctrl.checked = value;
|
ctrl.checked = value;
|
||||||
|
|
||||||
} else if (typeof ctrl.options !== 'undefined') {
|
} else if (typeof ctrl.options !== 'undefined') {
|
||||||
for (i = 0; i < ctrl.options.length; i += 1) {
|
for (i = 0; i < ctrl.options.length; i += 1) {
|
||||||
if (ctrl.options[i].value === value) {
|
if (ctrl.options[i].value === value) {
|
||||||
@@ -188,13 +151,18 @@ updateSetting: function(name, value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/*Weird IE9 error leads to 'null' appearring
|
||||||
|
in textboxes instead of ''.*/
|
||||||
|
if (value === null) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
ctrl.value = value;
|
ctrl.value = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Save control setting to cookie
|
// Save control setting to cookie
|
||||||
saveSetting: function(name) {
|
saveSetting: function(name) {
|
||||||
var val, ctrl = $D('VNC_' + name);
|
var val, ctrl = $D('noVNC_' + name);
|
||||||
if (ctrl.type === 'checkbox') {
|
if (ctrl.type === 'checkbox') {
|
||||||
val = ctrl.checked;
|
val = ctrl.checked;
|
||||||
} else if (typeof ctrl.options !== 'undefined') {
|
} else if (typeof ctrl.options !== 'undefined') {
|
||||||
@@ -217,18 +185,77 @@ initSetting: function(name, defVal) {
|
|||||||
val = WebUtil.readCookie(name, defVal);
|
val = WebUtil.readCookie(name, defVal);
|
||||||
}
|
}
|
||||||
UI.updateSetting(name, val);
|
UI.updateSetting(name, val);
|
||||||
//Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
|
//Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Force a setting to be a certain value
|
||||||
|
forceSetting: function(name, val) {
|
||||||
|
UI.updateSetting(name, val);
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Show the clipboard panel
|
||||||
|
toggleClipboardPanel: function() {
|
||||||
|
// Close the description panel
|
||||||
|
$D('noVNC_description').style.display = "none";
|
||||||
|
//Close settings if open
|
||||||
|
if (UI.settingsOpen === true) {
|
||||||
|
UI.settingsApply();
|
||||||
|
UI.closeSettingsMenu();
|
||||||
|
}
|
||||||
|
//Close connection settings if open
|
||||||
|
if (UI.connSettingsOpen === true) {
|
||||||
|
UI.toggleConnectPanel();
|
||||||
|
}
|
||||||
|
//Toggle Clipboard Panel
|
||||||
|
if (UI.clipboardOpen === true) {
|
||||||
|
$D('noVNC_clipboard').style.display = "none";
|
||||||
|
$D('clipboardButton').className = "noVNC_status_button";
|
||||||
|
UI.clipboardOpen = false;
|
||||||
|
} else {
|
||||||
|
$D('noVNC_clipboard').style.display = "block";
|
||||||
|
$D('clipboardButton').className = "noVNC_status_button_selected";
|
||||||
|
UI.clipboardOpen = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Show the connection settings panel/menu
|
||||||
|
toggleConnectPanel: function() {
|
||||||
|
// Close the description panel
|
||||||
|
$D('noVNC_description').style.display = "none";
|
||||||
|
//Close connection settings if open
|
||||||
|
if (UI.settingsOpen === true) {
|
||||||
|
UI.settingsApply();
|
||||||
|
UI.closeSettingsMenu();
|
||||||
|
$D('connectButton').className = "noVNC_status_button";
|
||||||
|
}
|
||||||
|
if (UI.clipboardOpen === true) {
|
||||||
|
UI.toggleClipboardPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Toggle Connection Panel
|
||||||
|
if (UI.connSettingsOpen === true) {
|
||||||
|
$D('noVNC_controls').style.display = "none";
|
||||||
|
$D('connectButton').className = "noVNC_status_button";
|
||||||
|
UI.connSettingsOpen = false;
|
||||||
|
} else {
|
||||||
|
$D('noVNC_controls').style.display = "block";
|
||||||
|
$D('connectButton').className = "noVNC_status_button_selected";
|
||||||
|
UI.connSettingsOpen = true;
|
||||||
|
$D('noVNC_host').focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Toggle the settings menu:
|
// Toggle the settings menu:
|
||||||
// On open, settings are refreshed from saved cookies.
|
// On open, settings are refreshed from saved cookies.
|
||||||
// On close, settings are applied
|
// On close, settings are applied
|
||||||
clickSettingsMenu: function() {
|
toggleSettingsPanel: function() {
|
||||||
|
// Close the description panel
|
||||||
|
$D('noVNC_description').style.display = "none";
|
||||||
if (UI.settingsOpen) {
|
if (UI.settingsOpen) {
|
||||||
UI.settingsApply();
|
UI.settingsApply();
|
||||||
|
|
||||||
UI.closeSettingsMenu();
|
UI.closeSettingsMenu();
|
||||||
} else {
|
} else {
|
||||||
UI.updateSetting('encrypt');
|
UI.updateSetting('encrypt');
|
||||||
@@ -237,10 +264,14 @@ clickSettingsMenu: function() {
|
|||||||
UI.updateSetting('cursor');
|
UI.updateSetting('cursor');
|
||||||
} else {
|
} else {
|
||||||
UI.updateSetting('cursor', false);
|
UI.updateSetting('cursor', false);
|
||||||
$D('VNC_cursor').disabled = true;
|
$D('noVNC_cursor').disabled = true;
|
||||||
}
|
}
|
||||||
|
UI.updateSetting('clip');
|
||||||
UI.updateSetting('shared');
|
UI.updateSetting('shared');
|
||||||
|
UI.updateSetting('view_only');
|
||||||
UI.updateSetting('connectTimeout');
|
UI.updateSetting('connectTimeout');
|
||||||
|
UI.updateSetting('path');
|
||||||
|
UI.updateSetting('repeaterID');
|
||||||
UI.updateSetting('stylesheet');
|
UI.updateSetting('stylesheet');
|
||||||
UI.updateSetting('logging');
|
UI.updateSetting('logging');
|
||||||
|
|
||||||
@@ -250,32 +281,27 @@ clickSettingsMenu: function() {
|
|||||||
|
|
||||||
// Open menu
|
// Open menu
|
||||||
openSettingsMenu: function() {
|
openSettingsMenu: function() {
|
||||||
$D('VNC_settings_menu').style.display = "block";
|
// Close the description panel
|
||||||
|
$D('noVNC_description').style.display = "none";
|
||||||
|
if (UI.clipboardOpen === true) {
|
||||||
|
UI.toggleClipboardPanel();
|
||||||
|
}
|
||||||
|
//Close connection settings if open
|
||||||
|
if (UI.connSettingsOpen === true) {
|
||||||
|
UI.toggleConnectPanel();
|
||||||
|
}
|
||||||
|
$D('noVNC_settings').style.display = "block";
|
||||||
|
$D('settingsButton').className = "noVNC_status_button_selected";
|
||||||
UI.settingsOpen = true;
|
UI.settingsOpen = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Close menu (without applying settings)
|
// Close menu (without applying settings)
|
||||||
closeSettingsMenu: function() {
|
closeSettingsMenu: function() {
|
||||||
$D('VNC_settings_menu').style.display = "none";
|
$D('noVNC_settings').style.display = "none";
|
||||||
|
$D('settingsButton').className = "noVNC_status_button";
|
||||||
UI.settingsOpen = false;
|
UI.settingsOpen = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Disable/enable controls depending on connection state
|
|
||||||
settingsDisabled: function(disabled, rfb) {
|
|
||||||
//Util.Debug(">> settingsDisabled");
|
|
||||||
$D('VNC_encrypt').disabled = disabled;
|
|
||||||
$D('VNC_true_color').disabled = disabled;
|
|
||||||
if (rfb && rfb.get_display() && rfb.get_display().get_cursor_uri()) {
|
|
||||||
$D('VNC_cursor').disabled = disabled;
|
|
||||||
} else {
|
|
||||||
UI.updateSetting('cursor', false);
|
|
||||||
$D('VNC_cursor').disabled = true;
|
|
||||||
}
|
|
||||||
$D('VNC_shared').disabled = disabled;
|
|
||||||
$D('VNC_connectTimeout').disabled = disabled;
|
|
||||||
//Util.Debug("<< settingsDisabled");
|
|
||||||
},
|
|
||||||
|
|
||||||
// Save/apply settings when 'Apply' button is pressed
|
// Save/apply settings when 'Apply' button is pressed
|
||||||
settingsApply: function() {
|
settingsApply: function() {
|
||||||
//Util.Debug(">> settingsApply");
|
//Util.Debug(">> settingsApply");
|
||||||
@@ -284,22 +310,32 @@ settingsApply: function() {
|
|||||||
if (UI.rfb.get_display().get_cursor_uri()) {
|
if (UI.rfb.get_display().get_cursor_uri()) {
|
||||||
UI.saveSetting('cursor');
|
UI.saveSetting('cursor');
|
||||||
}
|
}
|
||||||
|
UI.saveSetting('clip');
|
||||||
UI.saveSetting('shared');
|
UI.saveSetting('shared');
|
||||||
|
UI.saveSetting('view_only');
|
||||||
UI.saveSetting('connectTimeout');
|
UI.saveSetting('connectTimeout');
|
||||||
|
UI.saveSetting('path');
|
||||||
|
UI.saveSetting('repeaterID');
|
||||||
UI.saveSetting('stylesheet');
|
UI.saveSetting('stylesheet');
|
||||||
UI.saveSetting('logging');
|
UI.saveSetting('logging');
|
||||||
|
|
||||||
// Settings with immediate (non-connected related) effect
|
// Settings with immediate (non-connected related) effect
|
||||||
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
||||||
WebUtil.init_logging(UI.getSetting('logging'));
|
WebUtil.init_logging(UI.getSetting('logging'));
|
||||||
|
UI.setViewClip();
|
||||||
|
UI.setViewDrag(UI.rfb.get_viewportDrag());
|
||||||
//Util.Debug("<< settingsApply");
|
//Util.Debug("<< settingsApply");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setPassword: function() {
|
setPassword: function() {
|
||||||
UI.rfb.sendPassword($D('VNC_password').value);
|
UI.rfb.sendPassword($D('noVNC_password').value);
|
||||||
|
//Reset connect button.
|
||||||
|
$D('noVNC_connect_button').value = "Connect";
|
||||||
|
$D('noVNC_connect_button').onclick = UI.Connect;
|
||||||
|
//Hide connection panel.
|
||||||
|
UI.toggleConnectPanel();
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -307,52 +343,63 @@ sendCtrlAltDel: function() {
|
|||||||
UI.rfb.sendCtrlAltDel();
|
UI.rfb.sendCtrlAltDel();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setMouseButton: function(num) {
|
||||||
|
var b, blist = [0, 1,2,4], button;
|
||||||
|
|
||||||
|
if (typeof num === 'undefined') {
|
||||||
|
// Disable mouse buttons
|
||||||
|
num = -1;
|
||||||
|
}
|
||||||
|
if (UI.rfb) {
|
||||||
|
UI.rfb.get_mouse().set_touchButton(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (b = 0; b < blist.length; b++) {
|
||||||
|
button = $D('noVNC_mouse_button' + blist[b]);
|
||||||
|
if (blist[b] === num) {
|
||||||
|
button.style.display = "";
|
||||||
|
} else {
|
||||||
|
button.style.display = "none";
|
||||||
|
/*
|
||||||
|
button.style.backgroundColor = "black";
|
||||||
|
button.style.color = "lightgray";
|
||||||
|
button.style.backgroundColor = "";
|
||||||
|
button.style.color = "";
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateState: function(rfb, state, oldstate, msg) {
|
updateState: function(rfb, state, oldstate, msg) {
|
||||||
var s, sb, c, cad, klass;
|
var s, sb, c, d, cad, vd, klass;
|
||||||
s = $D('VNC_status');
|
UI.rfb_state = state;
|
||||||
sb = $D('VNC_status_bar');
|
s = $D('noVNC_status');
|
||||||
c = $D('VNC_connect_button');
|
sb = $D('noVNC_status_bar');
|
||||||
cad = $D('sendCtrlAltDelButton');
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'failed':
|
case 'failed':
|
||||||
case 'fatal':
|
case 'fatal':
|
||||||
c.disabled = true;
|
klass = "noVNC_status_error";
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_error";
|
|
||||||
break;
|
break;
|
||||||
case 'normal':
|
case 'normal':
|
||||||
c.value = "Disconnect";
|
klass = "noVNC_status_normal";
|
||||||
c.onclick = UI.disconnect;
|
|
||||||
c.disabled = false;
|
|
||||||
cad.disabled = false;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
break;
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
|
$D('noVNC_logo').style.display = "block";
|
||||||
|
// Fall through
|
||||||
case 'loaded':
|
case 'loaded':
|
||||||
c.value = "Connect";
|
klass = "noVNC_status_normal";
|
||||||
c.onclick = UI.connect;
|
|
||||||
|
|
||||||
c.disabled = false;
|
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(false, rfb);
|
|
||||||
klass = "VNC_status_normal";
|
|
||||||
break;
|
break;
|
||||||
case 'password':
|
case 'password':
|
||||||
c.value = "Send Password";
|
UI.toggleConnectPanel();
|
||||||
c.onclick = UI.setPassword;
|
|
||||||
|
|
||||||
c.disabled = false;
|
$D('noVNC_connect_button').value = "Send Password";
|
||||||
cad.disabled = true;
|
$D('noVNC_connect_button').onclick = UI.setPassword;
|
||||||
UI.settingsDisabled(true, rfb);
|
$D('noVNC_password').focus();
|
||||||
klass = "VNC_status_warn";
|
|
||||||
|
klass = "noVNC_status_warn";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
c.disabled = true;
|
klass = "noVNC_status_warn";
|
||||||
cad.disabled = true;
|
|
||||||
UI.settingsDisabled(true, rfb);
|
|
||||||
klass = "VNC_status_warn";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,23 +409,80 @@ updateState: function(rfb, state, oldstate, msg) {
|
|||||||
s.innerHTML = msg;
|
s.innerHTML = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UI.updateVisualState();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Disable/enable controls depending on connection state
|
||||||
|
updateVisualState: function() {
|
||||||
|
var connected = UI.rfb_state === 'normal' ? true : false;
|
||||||
|
|
||||||
|
//Util.Debug(">> updateVisualState");
|
||||||
|
$D('noVNC_encrypt').disabled = connected;
|
||||||
|
$D('noVNC_true_color').disabled = connected;
|
||||||
|
if (UI.rfb && UI.rfb.get_display() &&
|
||||||
|
UI.rfb.get_display().get_cursor_uri()) {
|
||||||
|
$D('noVNC_cursor').disabled = connected;
|
||||||
|
} else {
|
||||||
|
UI.updateSetting('cursor', false);
|
||||||
|
$D('noVNC_cursor').disabled = true;
|
||||||
|
}
|
||||||
|
$D('noVNC_shared').disabled = connected;
|
||||||
|
$D('noVNC_view_only').disabled = connected;
|
||||||
|
$D('noVNC_connectTimeout').disabled = connected;
|
||||||
|
$D('noVNC_path').disabled = connected;
|
||||||
|
$D('noVNC_repeaterID').disabled = connected;
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
UI.setViewClip();
|
||||||
|
UI.setMouseButton(1);
|
||||||
|
$D('clipboardButton').style.display = "inline";
|
||||||
|
$D('showKeyboard').style.display = "inline";
|
||||||
|
$D('sendCtrlAltDelButton').style.display = "inline";
|
||||||
|
} else {
|
||||||
|
UI.setMouseButton();
|
||||||
|
$D('clipboardButton').style.display = "none";
|
||||||
|
$D('showKeyboard').style.display = "none";
|
||||||
|
$D('sendCtrlAltDelButton').style.display = "none";
|
||||||
|
}
|
||||||
|
// State change disables viewport dragging.
|
||||||
|
// It is enabled (toggled) by direct click on the button
|
||||||
|
UI.setViewDrag(false);
|
||||||
|
|
||||||
|
switch (UI.rfb_state) {
|
||||||
|
case 'fatal':
|
||||||
|
case 'failed':
|
||||||
|
case 'loaded':
|
||||||
|
case 'disconnected':
|
||||||
|
$D('connectButton').style.display = "";
|
||||||
|
$D('disconnectButton').style.display = "none";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$D('connectButton').style.display = "none";
|
||||||
|
$D('disconnectButton').style.display = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Util.Debug("<< updateVisualState");
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
clipReceive: function(rfb, text) {
|
clipReceive: function(rfb, text) {
|
||||||
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
|
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
|
||||||
$D('VNC_clipboard_text').value = text;
|
$D('noVNC_clipboard_text').value = text;
|
||||||
Util.Debug("<< UI.clipReceive");
|
Util.Debug("<< UI.clipReceive");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
connect: function() {
|
connect: function() {
|
||||||
var host, port, password;
|
var host, port, password, path;
|
||||||
|
|
||||||
UI.closeSettingsMenu();
|
UI.closeSettingsMenu();
|
||||||
|
UI.toggleConnectPanel();
|
||||||
|
|
||||||
host = $D('VNC_host').value;
|
host = $D('noVNC_host').value;
|
||||||
port = $D('VNC_port').value;
|
port = $D('noVNC_port').value;
|
||||||
password = $D('VNC_password').value;
|
password = $D('noVNC_password').value;
|
||||||
|
path = $D('noVNC_path').value;
|
||||||
if ((!host) || (!port)) {
|
if ((!host) || (!port)) {
|
||||||
throw("Must set host and port");
|
throw("Must set host and port");
|
||||||
}
|
}
|
||||||
@@ -387,15 +491,24 @@ connect: function() {
|
|||||||
UI.rfb.set_true_color(UI.getSetting('true_color'));
|
UI.rfb.set_true_color(UI.getSetting('true_color'));
|
||||||
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
|
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
|
||||||
UI.rfb.set_shared(UI.getSetting('shared'));
|
UI.rfb.set_shared(UI.getSetting('shared'));
|
||||||
|
UI.rfb.set_view_only(UI.getSetting('view_only'));
|
||||||
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
|
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
|
||||||
|
UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
|
||||||
|
|
||||||
UI.rfb.connect(host, port, password);
|
UI.rfb.connect(host, port, password, path);
|
||||||
|
|
||||||
|
//Close dialog.
|
||||||
|
setTimeout(UI.setBarPosition, 100);
|
||||||
|
$D('noVNC_logo').style.display = "none";
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect: function() {
|
disconnect: function() {
|
||||||
UI.closeSettingsMenu();
|
UI.closeSettingsMenu();
|
||||||
|
|
||||||
UI.rfb.disconnect();
|
UI.rfb.disconnect();
|
||||||
|
|
||||||
|
$D('noVNC_logo').style.display = "block";
|
||||||
|
UI.connSettingsOpen = false;
|
||||||
|
UI.toggleConnectPanel();
|
||||||
},
|
},
|
||||||
|
|
||||||
displayBlur: function() {
|
displayBlur: function() {
|
||||||
@@ -409,15 +522,138 @@ displayFocus: function() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
clipClear: function() {
|
clipClear: function() {
|
||||||
$D('VNC_clipboard_text').value = "";
|
$D('noVNC_clipboard_text').value = "";
|
||||||
UI.rfb.clipboardPasteFrom("");
|
UI.rfb.clipboardPasteFrom("");
|
||||||
},
|
},
|
||||||
|
|
||||||
clipSend: function() {
|
clipSend: function() {
|
||||||
var text = $D('VNC_clipboard_text').value;
|
var text = $D('noVNC_clipboard_text').value;
|
||||||
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
|
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
|
||||||
UI.rfb.clipboardPasteFrom(text);
|
UI.rfb.clipboardPasteFrom(text);
|
||||||
Util.Debug("<< UI.clipSend");
|
Util.Debug("<< UI.clipSend");
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Enable/disable and configure viewport clipping
|
||||||
|
setViewClip: function(clip) {
|
||||||
|
var display, cur_clip, pos, new_w, new_h;
|
||||||
|
|
||||||
|
if (UI.rfb) {
|
||||||
|
display = UI.rfb.get_display();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_clip = display.get_viewport();
|
||||||
|
|
||||||
|
if (typeof(clip) !== 'boolean') {
|
||||||
|
// Use current setting
|
||||||
|
clip = UI.getSetting('clip');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clip && !cur_clip) {
|
||||||
|
// Turn clipping on
|
||||||
|
UI.updateSetting('clip', true);
|
||||||
|
} else if (!clip && cur_clip) {
|
||||||
|
// Turn clipping off
|
||||||
|
UI.updateSetting('clip', false);
|
||||||
|
display.set_viewport(false);
|
||||||
|
$D('noVNC_canvas').style.position = 'static';
|
||||||
|
display.viewportChange();
|
||||||
|
}
|
||||||
|
if (UI.getSetting('clip')) {
|
||||||
|
// If clipping, update clipping settings
|
||||||
|
$D('noVNC_canvas').style.position = 'absolute';
|
||||||
|
pos = Util.getPosition($D('noVNC_canvas'));
|
||||||
|
new_w = window.innerWidth - pos.x;
|
||||||
|
new_h = window.innerHeight - pos.y;
|
||||||
|
display.set_viewport(true);
|
||||||
|
display.viewportChange(0, 0, new_w, new_h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Toggle/set/unset the viewport drag/move button
|
||||||
|
setViewDrag: function(drag) {
|
||||||
|
var vmb = $D('noVNC_view_drag_button');
|
||||||
|
if (!UI.rfb) { return; }
|
||||||
|
|
||||||
|
if (UI.rfb_state === 'normal' &&
|
||||||
|
UI.rfb.get_display().get_viewport()) {
|
||||||
|
vmb.style.display = "inline";
|
||||||
|
} else {
|
||||||
|
vmb.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(drag) === "undefined") {
|
||||||
|
// If not specified, then toggle
|
||||||
|
drag = !UI.rfb.get_viewportDrag();
|
||||||
|
}
|
||||||
|
if (drag) {
|
||||||
|
vmb.className = "noVNC_status_button_selected";
|
||||||
|
UI.rfb.set_viewportDrag(true);
|
||||||
|
} else {
|
||||||
|
vmb.className = "noVNC_status_button";
|
||||||
|
UI.rfb.set_viewportDrag(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// On touch devices, show the OS keyboard
|
||||||
|
showKeyboard: function() {
|
||||||
|
if(UI.keyboardVisible === false) {
|
||||||
|
$D('keyboardinput').focus();
|
||||||
|
UI.keyboardVisible = true;
|
||||||
|
$D('showKeyboard').className = "noVNC_status_button_selected";
|
||||||
|
} else if(UI.keyboardVisible === true) {
|
||||||
|
$D('keyboardinput').blur();
|
||||||
|
$D('showKeyboard').className = "noVNC_status_button";
|
||||||
|
UI.keyboardVisible = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
keyInputBlur: function() {
|
||||||
|
$D('showKeyboard').className = "noVNC_status_button";
|
||||||
|
//Weird bug in iOS if you change keyboardVisible
|
||||||
|
//here it does not actually occur so next time
|
||||||
|
//you click keyboard icon it doesnt work.
|
||||||
|
setTimeout(function() { UI.setKeyboard(); },100);
|
||||||
|
},
|
||||||
|
|
||||||
|
setKeyboard: function() {
|
||||||
|
UI.keyboardVisible = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// iOS < Version 5 does not support position fixed. Javascript workaround:
|
||||||
|
setOnscroll: function() {
|
||||||
|
window.onscroll = function() {
|
||||||
|
UI.setBarPosition();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
setResize: function () {
|
||||||
|
window.onResize = function() {
|
||||||
|
UI.setBarPosition();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
//Helper to add options to dropdown.
|
||||||
|
addOption: function(selectbox,text,value )
|
||||||
|
{
|
||||||
|
var optn = document.createElement("OPTION");
|
||||||
|
optn.text = text;
|
||||||
|
optn.value = value;
|
||||||
|
selectbox.options.add(optn);
|
||||||
|
},
|
||||||
|
|
||||||
|
setBarPosition: function() {
|
||||||
|
$D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
|
||||||
|
$D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
|
||||||
|
|
||||||
|
var vncwidth = $D('noVNC_screen').style.offsetWidth;
|
||||||
|
$D('noVNC-control-bar').style.width = vncwidth + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
@@ -33,6 +33,45 @@ Array.prototype.push32 = function (num) {
|
|||||||
(num ) & 0xFF );
|
(num ) & 0xFF );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// IE does not support map (even in IE9)
|
||||||
|
//This prototype is provided by the Mozilla foundation and
|
||||||
|
//is distributed under the MIT license.
|
||||||
|
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||||
|
if (!Array.prototype.map)
|
||||||
|
{
|
||||||
|
Array.prototype.map = function(fun /*, thisp*/)
|
||||||
|
{
|
||||||
|
var len = this.length;
|
||||||
|
if (typeof fun != "function")
|
||||||
|
throw new TypeError();
|
||||||
|
|
||||||
|
var res = new Array(len);
|
||||||
|
var thisp = arguments[1];
|
||||||
|
for (var i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (i in this)
|
||||||
|
res[i] = fun.call(thisp, this[i], i, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// requestAnimationFrame shim with setTimeout fallback
|
||||||
|
//
|
||||||
|
|
||||||
|
window.requestAnimFrame = (function(){
|
||||||
|
return window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
window.oRequestAnimationFrame ||
|
||||||
|
window.msRequestAnimationFrame ||
|
||||||
|
function(callback){
|
||||||
|
window.setTimeout(callback, 1000 / 60);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ------------------------------------------------------
|
* ------------------------------------------------------
|
||||||
* Namespaced in Util
|
* Namespaced in Util
|
||||||
@@ -107,6 +146,8 @@ Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
|
|||||||
}
|
}
|
||||||
} else if (type in {'integer':1, 'int':1}) {
|
} else if (type in {'integer':1, 'int':1}) {
|
||||||
val = parseInt(val, 10);
|
val = parseInt(val, 10);
|
||||||
|
} else if (type === 'str') {
|
||||||
|
val = String(val);
|
||||||
} else if (type === 'func') {
|
} else if (type === 'func') {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
val = function () {};
|
val = function () {};
|
||||||
@@ -159,7 +200,7 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
|
|||||||
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
|
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
|
||||||
arr[i][2], arr[i][3], arr[i][4]);
|
arr[i][2], arr[i][3], arr[i][4]);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -184,6 +225,7 @@ Util.getEventPosition = function (e, obj, scale) {
|
|||||||
var evt, docX, docY, pos;
|
var evt, docX, docY, pos;
|
||||||
//if (!e) evt = window.event;
|
//if (!e) evt = window.event;
|
||||||
evt = (e ? e : window.event);
|
evt = (e ? e : window.event);
|
||||||
|
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
|
||||||
if (evt.pageX || evt.pageY) {
|
if (evt.pageX || evt.pageY) {
|
||||||
docX = evt.pageX;
|
docX = evt.pageX;
|
||||||
docY = evt.pageY;
|
docY = evt.pageY;
|
||||||
@@ -239,8 +281,11 @@ Util.stopEvent = function(e) {
|
|||||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||||
|
|
||||||
Util.Engine = {
|
Util.Engine = {
|
||||||
'presto': (function() {
|
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||||
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
//'presto': (function() {
|
||||||
|
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||||
|
'presto': (function() { return (!window.opera) ? false : true; }()),
|
||||||
|
|
||||||
'trident': (function() {
|
'trident': (function() {
|
||||||
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
||||||
'webkit': (function() {
|
'webkit': (function() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
@@ -30,13 +30,13 @@ function get_INCLUDE_URI() {
|
|||||||
|
|
||||||
extra += start + "util.js" + end;
|
extra += start + "util.js" + end;
|
||||||
extra += start + "webutil.js" + end;
|
extra += start + "webutil.js" + end;
|
||||||
extra += start + "logo.js" + end;
|
|
||||||
extra += start + "base64.js" + end;
|
extra += start + "base64.js" + end;
|
||||||
extra += start + "websock.js" + end;
|
extra += start + "websock.js" + end;
|
||||||
extra += start + "des.js" + end;
|
extra += start + "des.js" + end;
|
||||||
extra += start + "input.js" + end;
|
extra += start + "input.js" + end;
|
||||||
extra += start + "display.js" + end;
|
extra += start + "display.js" + end;
|
||||||
extra += start + "rfb.js" + end;
|
extra += start + "rfb.js" + end;
|
||||||
|
extra += start + "jsunzip.js" + end;
|
||||||
|
|
||||||
document.write(extra);
|
document.write(extra);
|
||||||
}());
|
}());
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
if (window.WebSocket) return;
|
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) return;
|
||||||
|
|
||||||
var console = window.console;
|
var console = window.console;
|
||||||
if (!console || !console.log || !console.error) {
|
if (!console || !console.log || !console.error) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Websock: high-performance binary WebSockets
|
* Websock: high-performance binary WebSockets
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* Websock is similar to the standard WebSocket object but Websock
|
* Websock is similar to the standard WebSocket object but Websock
|
||||||
@@ -14,13 +14,25 @@
|
|||||||
* read binary data off of the receive queue.
|
* read binary data off of the receive queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*jslint browser: true, bitwise: false, plusplus: false */
|
||||||
|
/*global Util, Base64 */
|
||||||
|
|
||||||
|
|
||||||
// Load Flash WebSocket emulator if needed
|
// Load Flash WebSocket emulator if needed
|
||||||
|
|
||||||
if (window.WebSocket) {
|
// To force WebSocket emulator even when native WebSocket available
|
||||||
|
//window.WEB_SOCKET_FORCE_FLASH = true;
|
||||||
|
// To enable WebSocket emulator debug:
|
||||||
|
//window.WEB_SOCKET_DEBUG=1;
|
||||||
|
|
||||||
|
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
Websock_native = true;
|
Websock_native = true;
|
||||||
|
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
|
Websock_native = true;
|
||||||
|
window.WebSocket = window.MozWebSocket;
|
||||||
} else {
|
} else {
|
||||||
/* no builtin WebSocket so load web_socket.js */
|
/* no builtin WebSocket so load web_socket.js */
|
||||||
|
|
||||||
Websock_native = false;
|
Websock_native = false;
|
||||||
(function () {
|
(function () {
|
||||||
function get_INCLUDE_URI() {
|
function get_INCLUDE_URI() {
|
||||||
@@ -31,11 +43,11 @@ if (window.WebSocket) {
|
|||||||
var start = "<script src='" + get_INCLUDE_URI(),
|
var start = "<script src='" + get_INCLUDE_URI(),
|
||||||
end = "'><\/script>", extra = "";
|
end = "'><\/script>", extra = "";
|
||||||
|
|
||||||
WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
||||||
"web-socket-js/WebSocketMain.swf";
|
"web-socket-js/WebSocketMain.swf";
|
||||||
if (Util.Engine.trident) {
|
if (Util.Engine.trident) {
|
||||||
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
||||||
WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
||||||
}
|
}
|
||||||
extra += start + "web-socket-js/swfobject.js" + end;
|
extra += start + "web-socket-js/swfobject.js" + end;
|
||||||
extra += start + "web-socket-js/web_socket.js" + end;
|
extra += start + "web-socket-js/web_socket.js" + end;
|
||||||
@@ -80,7 +92,7 @@ function get_rQi() {
|
|||||||
}
|
}
|
||||||
function set_rQi(val) {
|
function set_rQi(val) {
|
||||||
rQi = val;
|
rQi = val;
|
||||||
};
|
}
|
||||||
|
|
||||||
function rQlen() {
|
function rQlen() {
|
||||||
return rQ.length - rQi;
|
return rQ.length - rQi;
|
||||||
@@ -112,6 +124,7 @@ function rQshift32() {
|
|||||||
(rQ[rQi++] );
|
(rQ[rQi++] );
|
||||||
}
|
}
|
||||||
function rQshiftStr(len) {
|
function rQshiftStr(len) {
|
||||||
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
var arr = rQ.slice(rQi, rQi + len);
|
var arr = rQ.slice(rQi, rQi + len);
|
||||||
rQi += len;
|
rQi += len;
|
||||||
return arr.map(function (num) {
|
return arr.map(function (num) {
|
||||||
@@ -119,6 +132,7 @@ function rQshiftStr(len) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
function rQshiftBytes(len) {
|
function rQshiftBytes(len) {
|
||||||
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
rQi += len;
|
rQi += len;
|
||||||
return rQ.slice(rQi-len, rQi);
|
return rQ.slice(rQi-len, rQi);
|
||||||
}
|
}
|
||||||
@@ -268,6 +282,8 @@ function open(uri) {
|
|||||||
Util.Debug(">> WebSock.onopen");
|
Util.Debug(">> WebSock.onopen");
|
||||||
if (websocket.protocol) {
|
if (websocket.protocol) {
|
||||||
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
||||||
|
} else {
|
||||||
|
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
||||||
}
|
}
|
||||||
eventHandlers.open();
|
eventHandlers.open();
|
||||||
Util.Debug("<< WebSock.onopen");
|
Util.Debug("<< WebSock.onopen");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
/*jslint bitwise: false, white: false */
|
/*jslint bitwise: false, white: false */
|
||||||
/*global window, document */
|
/*global Util, window, document */
|
||||||
|
|
||||||
// Globals defined here
|
// Globals defined here
|
||||||
var WebUtil = {}, $D;
|
var WebUtil = {}, $D;
|
||||||
@@ -17,7 +17,7 @@ var WebUtil = {}, $D;
|
|||||||
* Simple DOM selector by ID
|
* Simple DOM selector by ID
|
||||||
*/
|
*/
|
||||||
if (!window.$D) {
|
if (!window.$D) {
|
||||||
$D = function (id) {
|
window.$D = function (id) {
|
||||||
if (document.getElementById) {
|
if (document.getElementById) {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
} else if (document.all) {
|
} else if (document.all) {
|
||||||
@@ -42,8 +42,8 @@ WebUtil.init_logging = function() {
|
|||||||
/logging=([A-Za-z0-9\._\-]*)/) ||
|
/logging=([A-Za-z0-9\._\-]*)/) ||
|
||||||
['', Util._log_level])[1];
|
['', Util._log_level])[1];
|
||||||
|
|
||||||
Util.init_logging()
|
Util.init_logging();
|
||||||
}
|
};
|
||||||
WebUtil.init_logging();
|
WebUtil.init_logging();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<script src="../include/util.js"></script>
|
<script src="../include/util.js"></script>
|
||||||
<script src="../include/webutil.js"></script>
|
<script src="../include/webutil.js"></script>
|
||||||
<script src="../include/base64.js"></script>
|
<script src="../include/base64.js"></script>
|
||||||
<script src="../include/canvas.js"></script>
|
<script src="../include/display.js"></script>
|
||||||
<script src="face.png.js"></script>
|
<script src="face.png.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
var msg_cnt = 0;
|
var msg_cnt = 0;
|
||||||
var start_width = 300, start_height = 100;
|
var display, start_width = 300, start_height = 100;
|
||||||
var iterations;
|
var iterations;
|
||||||
|
|
||||||
function message(str) {
|
function message(str) {
|
||||||
@@ -48,12 +48,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_functions () {
|
function test_functions () {
|
||||||
var img, x, y, w, h, ctx = canvas.getContext();
|
var img, x, y, w, h, ctx = display.get_context();
|
||||||
w = canvas.get_width();
|
w = display.get_width();
|
||||||
h = canvas.get_height();
|
h = display.get_height();
|
||||||
canvas.fillRect(0, 0, w, h, [240,240,240]);
|
display.fillRect(0, 0, w, h, [240,240,240]);
|
||||||
|
|
||||||
canvas.blitStringImage("data:image/png;base64," + face64, 150, 10);
|
display.blitStringImage("data:image/png;base64," + face64, 150, 10);
|
||||||
|
|
||||||
var himg = new Image();
|
var himg = new Image();
|
||||||
himg.onload = function () {
|
himg.onload = function () {
|
||||||
@@ -70,14 +70,14 @@
|
|||||||
data[(y*50 + x)*4 + 3] = 255;
|
data[(y*50 + x)*4 + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
canvas.blitImage(30, 10, 50, 50, data, 0);
|
display.blitImage(30, 10, 50, 50, data, 0);
|
||||||
|
|
||||||
img = canvas.getTile(5,5,16,16,[0,128,128]);
|
img = display.getTile(5,5,16,16,[0,128,128]);
|
||||||
canvas.putTile(img);
|
display.putTile(img);
|
||||||
|
|
||||||
img = canvas.getTile(90,15,16,16,[0,0,0]);
|
img = display.getTile(90,15,16,16,[0,0,0]);
|
||||||
canvas.setSubTile(img, 0,0,16,16,[128,128,0]);
|
display.setSubTile(img, 0,0,16,16,[128,128,0]);
|
||||||
canvas.putTile(img);
|
display.putTile(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
function begin () {
|
function begin () {
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
function start_delayed () {
|
function start_delayed () {
|
||||||
var ret;
|
var ret;
|
||||||
|
|
||||||
ret = canvas.set_prefer_js(true);
|
ret = display.set_prefer_js(true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
message("Running test: prefer Javascript ops");
|
message("Running test: prefer Javascript ops");
|
||||||
var time1 = run_test();
|
var time1 = run_test();
|
||||||
@@ -100,14 +100,14 @@
|
|||||||
message("Could not run: prefer Javascript ops");
|
message("Could not run: prefer Javascript ops");
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.set_prefer_js(false);
|
display.set_prefer_js(false);
|
||||||
message("Running test: prefer Canvas ops");
|
message("Running test: prefer Canvas ops");
|
||||||
var time2 = run_test();
|
var time2 = run_test();
|
||||||
message("prefer Canvas ops: " + time2 + "ms total, " +
|
message("prefer Canvas ops: " + time2 + "ms total, " +
|
||||||
(time2 / iterations) + "ms per frame");
|
(time2 / iterations) + "ms per frame");
|
||||||
|
|
||||||
if (Util.get_logging() !== 'debug') {
|
if (Util.get_logging() !== 'debug') {
|
||||||
canvas.resize(start_width, start_height, true);
|
display.resize(start_width, start_height, true);
|
||||||
test_functions();
|
test_functions();
|
||||||
}
|
}
|
||||||
$D('startButton').disabled = false;
|
$D('startButton').disabled = false;
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
var width, height;
|
var width, height;
|
||||||
width = $D('width').value;
|
width = $D('width').value;
|
||||||
height = $D('height').value;
|
height = $D('height').value;
|
||||||
canvas.resize(width, height);
|
display.resize(width, height);
|
||||||
var color, start_time = (new Date()).getTime(), w, h;
|
var color, start_time = (new Date()).getTime(), w, h;
|
||||||
for (var i=0; i < iterations; i++) {
|
for (var i=0; i < iterations; i++) {
|
||||||
color = [128, 128, (255 / iterations) * i, 0];
|
color = [128, 128, (255 / iterations) * i, 0];
|
||||||
@@ -126,9 +126,9 @@
|
|||||||
for (var y=0; y < height; y = y + 16) {
|
for (var y=0; y < height; y = y + 16) {
|
||||||
w = Math.min(16, width - x);
|
w = Math.min(16, width - x);
|
||||||
h = Math.min(16, height - y);
|
h = Math.min(16, height - y);
|
||||||
var tile = canvas.getTile(x, y, w, h, color);
|
var tile = display.getTile(x, y, w, h, color);
|
||||||
canvas.setSubTile(tile, 0, 0, w, h, color);
|
display.setSubTile(tile, 0, 0, w, h, color);
|
||||||
canvas.putTile(tile);
|
display.putTile(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,8 +139,8 @@
|
|||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
message("in onload");
|
message("in onload");
|
||||||
$D('iterations').value = 10;
|
$D('iterations').value = 10;
|
||||||
canvas = new Canvas({'target' : $D('canvas')});
|
display = new Display({'target' : $D('canvas')});
|
||||||
canvas.resize(start_width, start_height, true);
|
display.resize(start_width, start_height, true);
|
||||||
message("Canvas initialized");
|
message("Canvas initialized");
|
||||||
test_functions();
|
test_functions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
<body>
|
<body>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
Canvas:<br>
|
Canvas:
|
||||||
|
<span id="button-selection" style="display: none;">
|
||||||
|
<input id="button1" type="button" value="L"><input id="button2" type="button" value="M"><input id="button4" type="button" value="R">
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
<canvas id="canvas" width="640" height="20"
|
<canvas id="canvas" width="640" height="20"
|
||||||
style="border-style: dotted; border-width: 1px;">
|
style="border-style: dotted; border-width: 1px;">
|
||||||
Canvas not supported.
|
Canvas not supported.
|
||||||
@@ -23,11 +27,11 @@
|
|||||||
<script src="../include/webutil.js"></script>
|
<script src="../include/webutil.js"></script>
|
||||||
<script src="../include/base64.js"></script>
|
<script src="../include/base64.js"></script>
|
||||||
<script src="../include/input.js"></script>
|
<script src="../include/input.js"></script>
|
||||||
<script src="../include/canvas.js"></script>
|
<script src="../include/display.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var msg_cnt = 0;
|
var msg_cnt = 0, iterations,
|
||||||
var width = 400, height = 200;
|
width = 400, height = 200,
|
||||||
var iterations;
|
canvas, keyboard, mouse;
|
||||||
|
|
||||||
var newline = "\n";
|
var newline = "\n";
|
||||||
if (Util.Engine.trident) {
|
if (Util.Engine.trident) {
|
||||||
@@ -61,17 +65,54 @@
|
|||||||
message(msg);
|
message(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectButton(num) {
|
||||||
|
var b, blist = [1,2,4];
|
||||||
|
|
||||||
|
if (typeof num === 'undefined') {
|
||||||
|
// Show the default
|
||||||
|
num = mouse.get_touchButton();
|
||||||
|
} else if (num === mouse.get_touchButton()) {
|
||||||
|
// Set all buttons off (no clicks)
|
||||||
|
mouse.set_touchButton(0);
|
||||||
|
num = 0;
|
||||||
|
} else {
|
||||||
|
// Turn on one button
|
||||||
|
mouse.set_touchButton(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (b = 0; b < blist.length; b++) {
|
||||||
|
if (blist[b] === num) {
|
||||||
|
$D('button' + blist[b]).style.backgroundColor = "black";
|
||||||
|
$D('button' + blist[b]).style.color = "lightgray";
|
||||||
|
} else {
|
||||||
|
$D('button' + blist[b]).style.backgroundColor = "";
|
||||||
|
$D('button' + blist[b]).style.color = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
var canvas = new Canvas({'target' : $D('canvas')});
|
canvas = new Display({'target' : $D('canvas')});
|
||||||
keyboard = new Keyboard({'target': document,
|
keyboard = new Keyboard({'target': document,
|
||||||
'keyPress': keyPress});
|
'onKeyPress': keyPress});
|
||||||
mouse = new Mouse({'target': $D('canvas'),
|
mouse = new Mouse({'target': $D('canvas'),
|
||||||
'mouseButton': mouseButton,
|
'onMouseButton': mouseButton,
|
||||||
'mouseMove': mouseMove});
|
'onMouseMove': mouseMove});
|
||||||
|
|
||||||
canvas.resize(width, height, true);
|
canvas.resize(width, height, true);
|
||||||
keyboard.grab();
|
keyboard.grab();
|
||||||
mouse.grab();
|
mouse.grab();
|
||||||
message("Canvas initialized");
|
message("Display initialized");
|
||||||
|
|
||||||
|
if ('ontouchstart' in document.documentElement) {
|
||||||
|
message("Touch device detected");
|
||||||
|
$D('button-selection').style.display = "inline";
|
||||||
|
$D('button1').onclick = function(){ selectButton(1) };
|
||||||
|
$D('button2').onclick = function(){ selectButton(2) };
|
||||||
|
$D('button4').onclick = function(){ selectButton(4) };
|
||||||
|
selectButton();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
html,body {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-layout {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: box;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -moz-box;
|
||||||
|
display: -ms-box;
|
||||||
|
|
||||||
|
box-orient: vertical;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-moz-box-orient: vertical;
|
||||||
|
-ms-box-orient: vertical;
|
||||||
|
|
||||||
|
box-align: stretch;
|
||||||
|
-webkit-box-align: stretch;
|
||||||
|
-moz-box-align: stretch;
|
||||||
|
-ms-box-align: stretch;
|
||||||
|
}
|
||||||
|
.flex-box {
|
||||||
|
box-flex: 1;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-moz-box-flex: 1;
|
||||||
|
-ms-box-flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas {
|
||||||
|
position: absolute;
|
||||||
|
border-style: dotted;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Viewport Test</title>
|
||||||
|
<link rel="stylesheet" href="viewport.css">
|
||||||
|
<!--
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
|
-->
|
||||||
|
<meta name=viewport content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="flex-layout">
|
||||||
|
<div>
|
||||||
|
Canvas:
|
||||||
|
<input id="move-selector" type="button" value="Move"
|
||||||
|
onclick="toggleMove();">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="container flex-box">
|
||||||
|
<canvas id="canvas" class="canvas">
|
||||||
|
Canvas not supported.
|
||||||
|
</canvas>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<br>
|
||||||
|
Results:<br>
|
||||||
|
<textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
|
-->
|
||||||
|
<script src="../include/util.js"></script>
|
||||||
|
<script src="../include/webutil.js"></script>
|
||||||
|
<script src="../include/base64.js"></script>
|
||||||
|
<script src="../include/input.js"></script>
|
||||||
|
<script src="../include/display.js"></script>
|
||||||
|
<script>
|
||||||
|
var msg_cnt = 0, iterations,
|
||||||
|
penDown = false, doMove = false,
|
||||||
|
inMove = false, lastPos = {},
|
||||||
|
padW = 0, padH = 0,
|
||||||
|
display, ctx, keyboard, mouse;
|
||||||
|
|
||||||
|
var newline = "\n";
|
||||||
|
if (Util.Engine.trident) {
|
||||||
|
var newline = "<br>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function message(str) {
|
||||||
|
console.log(str);
|
||||||
|
cell = $D('messages');
|
||||||
|
cell.innerHTML += msg_cnt + ": " + str + newline;
|
||||||
|
cell.scrollTop = cell.scrollHeight;
|
||||||
|
msg_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseButton(x, y, down, bmask) {
|
||||||
|
//msg = 'mouse x,y: ' + x + ',' + y + ' down: ' + down;
|
||||||
|
//msg += ' bmask: ' + bmask;
|
||||||
|
//message(msg);
|
||||||
|
|
||||||
|
if (doMove) {
|
||||||
|
if (down && !inMove) {
|
||||||
|
inMove = true;
|
||||||
|
lastPos = {'x': x, 'y': y};
|
||||||
|
} else if (!down && inMove) {
|
||||||
|
inMove = false;
|
||||||
|
//dirtyRedraw();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (down && ! penDown) {
|
||||||
|
penDown = true;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
} else if (!down && penDown) {
|
||||||
|
penDown = false;
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseMove(x, y) {
|
||||||
|
var deltaX, deltaY;
|
||||||
|
|
||||||
|
if (inMove) {
|
||||||
|
//deltaX = x - lastPos.x; // drag viewport
|
||||||
|
deltaX = lastPos.x - x; // drag frame buffer
|
||||||
|
//deltaY = y - lastPos.y; // drag viewport
|
||||||
|
deltaY = lastPos.y - y; // drag frame buffer
|
||||||
|
lastPos = {'x': x, 'y': y};
|
||||||
|
|
||||||
|
display.viewportChange(deltaX, deltaY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (penDown) {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dirtyRedraw() {
|
||||||
|
if (inMove) {
|
||||||
|
// Wait for user to stop moving viewport
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var d = display.getCleanDirtyReset();
|
||||||
|
|
||||||
|
for (i = 0; i < d.dirtyBoxes.length; i++) {
|
||||||
|
//showBox(d.dirtyBoxes[i], "dirty[" + i + "]: ");
|
||||||
|
drawArea(d.dirtyBoxes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawArea(b) {
|
||||||
|
var data = [], pixel, x, y;
|
||||||
|
|
||||||
|
//message("draw "+b.x+","+b.y+" ("+b.w+","+b.h+")");
|
||||||
|
|
||||||
|
for (var i = 0; i < b.w; i++) {
|
||||||
|
x = b.x + i;
|
||||||
|
for (var j = 0; j < b.h; j++) {
|
||||||
|
y = b.y + j;
|
||||||
|
pixel = (j * b.w * 4 + i * 4);
|
||||||
|
data[pixel + 0] = ((x * y) / 13) % 256;
|
||||||
|
data[pixel + 1] = ((x * y) + 392) % 256;
|
||||||
|
data[pixel + 2] = ((x + y) + 256) % 256;
|
||||||
|
data[pixel + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//message("i: " + i + ", j: " + j + ", pixel: " + pixel);
|
||||||
|
display.blitImage(b.x, b.y, b.w, b.h, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMove() {
|
||||||
|
if (doMove) {
|
||||||
|
doMove = false;
|
||||||
|
$D('move-selector').style.backgroundColor = "";
|
||||||
|
$D('move-selector').style.color = "";
|
||||||
|
} else {
|
||||||
|
doMove = true;
|
||||||
|
$D('move-selector').style.backgroundColor = "black";
|
||||||
|
$D('move-selector').style.color = "lightgray";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectPad() {
|
||||||
|
var c = $D('canvas'), p = c.parentNode;
|
||||||
|
c.width = 10;
|
||||||
|
c.height = 10;
|
||||||
|
padW = c.offsetWidth - 10;
|
||||||
|
padH = c.offsetHeight - 10;
|
||||||
|
message("padW: " + padW + ", padH: " + padH);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doResize() {
|
||||||
|
var p = $D('canvas').parentNode;
|
||||||
|
message("doResize1: [" + (p.offsetWidth - padW) +
|
||||||
|
"," + (p.offsetHeight - padH) + "]");
|
||||||
|
display.viewportChange(0, 0,
|
||||||
|
p.offsetWidth - padW, p.offsetHeight - padH);
|
||||||
|
/*
|
||||||
|
var pos, new_w, new_h;pos
|
||||||
|
pos = Util.getPosition($D('canvas').parentNode);
|
||||||
|
new_w = window.innerWidth - pos.x;
|
||||||
|
new_h = window.innerHeight - pos.y;
|
||||||
|
display.viewportChange(0, 0, new_w, new_h);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
detectPad();
|
||||||
|
|
||||||
|
display = new Display({'target': $D('canvas')});
|
||||||
|
display.resize(1600, 1024);
|
||||||
|
display.set_viewport(true);
|
||||||
|
ctx = display.get_context();
|
||||||
|
|
||||||
|
mouse = new Mouse({'target': $D('canvas'),
|
||||||
|
'onMouseButton': mouseButton,
|
||||||
|
'onMouseMove': mouseMove});
|
||||||
|
mouse.grab();
|
||||||
|
|
||||||
|
|
||||||
|
Util.addEvent(window, 'resize', doResize);
|
||||||
|
// Shrink viewport for first resize call so that the
|
||||||
|
// scrollbars are disabled
|
||||||
|
display.viewportChange(0, 0, 10, 10);
|
||||||
|
setTimeout(doResize, 1);
|
||||||
|
setInterval(dirtyRedraw, 50);
|
||||||
|
|
||||||
|
message("Display initialized");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>VNC Performance Benchmark</title>
|
<title>VNC Performance Benchmark</title>
|
||||||
<link rel="stylesheet" href="../include/plain.css">
|
<link rel="stylesheet" href="../include/base.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="../include/vnc.js"></script>
|
<script src="../include/vnc.js"></script>
|
||||||
<script src="../include/playback.js"></script>
|
<script src="../include/playback.js"></script>
|
||||||
<script src="data/multi.js"></script>
|
<script src="../data/multi.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var start_time, VNC_frame_data, pass, passes, encIdx,
|
var start_time, VNC_frame_data, pass, passes, encIdx,
|
||||||
@@ -126,7 +126,9 @@
|
|||||||
pass += 1;
|
pass += 1;
|
||||||
if (pass > passes) {
|
if (pass > passes) {
|
||||||
// We are finished
|
// We are finished
|
||||||
rfb.get_canvas().stop(); // Shut-off event interception
|
// Shut-off event interception
|
||||||
|
rfb.get_mouse().ungrab();
|
||||||
|
rfb.get_keyboard().ungrab();
|
||||||
$D('startButton').disabled = false;
|
$D('startButton').disabled = false;
|
||||||
$D('startButton').value = "Start";
|
$D('startButton').value = "Start";
|
||||||
finish_passes();
|
finish_passes();
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ done
|
|||||||
which netstat >/dev/null 2>&1 \
|
which netstat >/dev/null 2>&1 \
|
||||||
|| die "Must have netstat installed"
|
|| die "Must have netstat installed"
|
||||||
|
|
||||||
netstat -ltn | grep -qs "${PORT}.*LISTEN" \
|
netstat -ltn | grep -qs "${PORT} .*LISTEN" \
|
||||||
&& die "Port ${PORT} in use. Try --listen PORT"
|
&& die "Port ${PORT} in use. Try --listen PORT"
|
||||||
|
|
||||||
trap "cleanup" TERM QUIT INT EXIT
|
trap "cleanup" TERM QUIT INT EXIT
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright (c) 2012 Openstack, LLC.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
'''
|
||||||
|
Websocket proxy that is compatible with Openstack Nova.
|
||||||
|
Leverages wsproxy.py by Joel Martin
|
||||||
|
'''
|
||||||
|
|
||||||
|
import Cookie
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import wsproxy
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import flags
|
||||||
|
from nova import utils
|
||||||
|
from nova.openstack.common import cfg
|
||||||
|
from nova.openstack.common import rpc
|
||||||
|
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
cfg.BoolOpt('record',
|
||||||
|
default=False,
|
||||||
|
help='Record sessions to FILE.[session_number]'),
|
||||||
|
cfg.BoolOpt('daemon',
|
||||||
|
default=False,
|
||||||
|
help='Become a daemon (background process)'),
|
||||||
|
cfg.BoolOpt('ssl_only',
|
||||||
|
default=False,
|
||||||
|
help='Disallow non-encrypted connections'),
|
||||||
|
cfg.BoolOpt('source_is_ipv6',
|
||||||
|
default=False,
|
||||||
|
help='Source is ipv6'),
|
||||||
|
cfg.StrOpt('cert',
|
||||||
|
default='self.pem',
|
||||||
|
help='SSL certificate file'),
|
||||||
|
cfg.StrOpt('key',
|
||||||
|
default=None,
|
||||||
|
help='SSL key file (if separate from cert)'),
|
||||||
|
cfg.StrOpt('web',
|
||||||
|
default='.',
|
||||||
|
help='Run webserver on same port. Serve files from DIR.'),
|
||||||
|
cfg.StrOpt('novncproxy_host',
|
||||||
|
default='0.0.0.0',
|
||||||
|
help='Host on which to listen for incoming requests'),
|
||||||
|
cfg.IntOpt('novncproxy_port',
|
||||||
|
default=6080,
|
||||||
|
help='Port on which to listen for incoming requests'),
|
||||||
|
]
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
FLAGS.register_cli_opts(opts)
|
||||||
|
|
||||||
|
# As of nova commit 0b11668e64450039dc071a4a123abd02206f865f we must
|
||||||
|
# manually register the rpc library
|
||||||
|
if hasattr(rpc, 'register_opts'):
|
||||||
|
rpc.register_opts(FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
class NovaWebSocketProxy(wsproxy.WebSocketProxy):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
wsproxy.WebSocketProxy.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def new_client(self):
|
||||||
|
"""
|
||||||
|
Called after a new WebSocket connection has been established.
|
||||||
|
"""
|
||||||
|
cookie = Cookie.SimpleCookie()
|
||||||
|
cookie.load(self.headers.getheader('cookie'))
|
||||||
|
token = cookie['token'].value
|
||||||
|
ctxt = context.get_admin_context()
|
||||||
|
connect_info = rpc.call(ctxt, 'consoleauth',
|
||||||
|
{'method': 'check_token',
|
||||||
|
'args': {'token': token}})
|
||||||
|
|
||||||
|
if not connect_info:
|
||||||
|
raise Exception("Invalid Token")
|
||||||
|
|
||||||
|
host = connect_info['host']
|
||||||
|
port = int(connect_info['port'])
|
||||||
|
|
||||||
|
# Connect to the target
|
||||||
|
self.msg("connecting to: %s:%s" % (
|
||||||
|
host, port))
|
||||||
|
tsock = self.socket(host, port,
|
||||||
|
connect=True)
|
||||||
|
|
||||||
|
# Handshake as necessary
|
||||||
|
if connect_info.get('internal_access_path'):
|
||||||
|
tsock.send("CONNECT %s HTTP/1.1\r\n\r\n" %
|
||||||
|
connect_info['internal_access_path'])
|
||||||
|
while True:
|
||||||
|
data = tsock.recv(4096, socket.MSG_PEEK)
|
||||||
|
if data.find("\r\n\r\n") != -1:
|
||||||
|
if not data.split("\r\n")[0].find("200"):
|
||||||
|
raise Exception("Invalid Connection Info")
|
||||||
|
tsock.recv(len(data))
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.verbose and not self.daemon:
|
||||||
|
print(self.traffic_legend)
|
||||||
|
|
||||||
|
# Start proxying
|
||||||
|
try:
|
||||||
|
self.do_proxy(tsock)
|
||||||
|
except:
|
||||||
|
if tsock:
|
||||||
|
tsock.shutdown(socket.SHUT_RDWR)
|
||||||
|
tsock.close()
|
||||||
|
self.vmsg("%s:%s: Target closed" % (host, port))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if FLAGS.ssl_only and not os.path.exists(FLAGS.cert):
|
||||||
|
parser.error("SSL only and %s not found" % FLAGS.cert)
|
||||||
|
|
||||||
|
# Setup flags
|
||||||
|
flags.parse_args(sys.argv)
|
||||||
|
|
||||||
|
# Create and start the NovaWebSockets proxy
|
||||||
|
server = NovaWebSocketProxy(listen_host=FLAGS.novncproxy_host,
|
||||||
|
listen_port=FLAGS.novncproxy_port,
|
||||||
|
source_is_ipv6=FLAGS.source_is_ipv6,
|
||||||
|
verbose=FLAGS.verbose,
|
||||||
|
cert=FLAGS.cert,
|
||||||
|
key=FLAGS.key,
|
||||||
|
ssl_only=FLAGS.ssl_only,
|
||||||
|
daemon=FLAGS.daemon,
|
||||||
|
record=FLAGS.record,
|
||||||
|
web=FLAGS.web,
|
||||||
|
target_host='ignore',
|
||||||
|
target_port='ignore',
|
||||||
|
wrap_mode='exit',
|
||||||
|
wrap_cmd=None)
|
||||||
|
server.start_server()
|
||||||
@@ -8,7 +8,7 @@ Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
|||||||
Supports following protocol versions:
|
Supports following protocol versions:
|
||||||
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
||||||
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
||||||
- http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07
|
- http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
|
||||||
|
|
||||||
You can make a cert/key with openssl using:
|
You can make a cert/key with openssl using:
|
||||||
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
||||||
@@ -16,47 +16,55 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os, sys, time, errno, signal, socket, struct, traceback, select
|
import os, sys, time, errno, signal, socket, traceback, select
|
||||||
|
import array, struct
|
||||||
from cgi import parse_qsl
|
from cgi import parse_qsl
|
||||||
from base64 import b64encode, b64decode
|
from base64 import b64encode, b64decode
|
||||||
|
|
||||||
# Imports that vary by python version
|
# Imports that vary by python version
|
||||||
|
|
||||||
|
# python 3.0 differences
|
||||||
if sys.hexversion > 0x3000000:
|
if sys.hexversion > 0x3000000:
|
||||||
# python >= 3.0
|
|
||||||
from io import StringIO
|
|
||||||
from http.server import SimpleHTTPRequestHandler
|
|
||||||
from urllib.parse import urlsplit
|
|
||||||
b2s = lambda buf: buf.decode('latin_1')
|
b2s = lambda buf: buf.decode('latin_1')
|
||||||
s2b = lambda s: s.encode('latin_1')
|
s2b = lambda s: s.encode('latin_1')
|
||||||
|
s2a = lambda s: s
|
||||||
else:
|
else:
|
||||||
# python 2.X
|
b2s = lambda buf: buf # No-op
|
||||||
from cStringIO import StringIO
|
s2b = lambda s: s # No-op
|
||||||
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
s2a = lambda s: [ord(c) for c in s]
|
||||||
from urlparse import urlsplit
|
try: from io import StringIO
|
||||||
# No-ops
|
except: from cStringIO import StringIO
|
||||||
b2s = lambda buf: buf
|
try: from http.server import SimpleHTTPRequestHandler
|
||||||
s2b = lambda s: s
|
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||||
|
try: from urllib.parse import urlsplit
|
||||||
|
except: from urlparse import urlsplit
|
||||||
|
|
||||||
if sys.hexversion >= 0x2060000:
|
# python 2.6 differences
|
||||||
# python >= 2.6
|
try: from hashlib import md5, sha1
|
||||||
from multiprocessing import Process
|
except: from md5 import md5; from sha import sha as sha1
|
||||||
from hashlib import md5, sha1
|
|
||||||
else:
|
# python 2.5 differences
|
||||||
# python < 2.6
|
try:
|
||||||
Process = None
|
from struct import pack, unpack_from
|
||||||
from md5 import md5
|
except:
|
||||||
from sha import sha as sha1
|
from struct import pack
|
||||||
|
def unpack_from(fmt, buf, offset=0):
|
||||||
|
slice = buffer(buf, offset, struct.calcsize(fmt))
|
||||||
|
return struct.unpack(fmt, slice)
|
||||||
|
|
||||||
# Degraded functionality if these imports are missing
|
# Degraded functionality if these imports are missing
|
||||||
for mod, sup in [('numpy', 'HyBi protocol'),
|
for mod, sup in [('numpy', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
|
||||||
('ctypes', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
|
('multiprocessing', 'Multi-Processing'),
|
||||||
('resource', 'daemonizing')]:
|
('resource', 'daemonizing')]:
|
||||||
try:
|
try:
|
||||||
globals()[mod] = __import__(mod)
|
globals()[mod] = __import__(mod)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
globals()[mod] = None
|
globals()[mod] = None
|
||||||
print("WARNING: no '%s' module, %s support disabled" % (
|
print("WARNING: no '%s' module, %s is slower or disabled" % (
|
||||||
mod, sup))
|
mod, sup))
|
||||||
|
if multiprocessing and sys.platform == 'win32':
|
||||||
|
# make sockets pickle-able/inheritable
|
||||||
|
import multiprocessing.reduction
|
||||||
|
|
||||||
|
|
||||||
class WebSocketServer(object):
|
class WebSocketServer(object):
|
||||||
@@ -84,12 +92,18 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
policy_response = """<cross-domain-policy><allow-access-from domain="*" to-ports="*" /></cross-domain-policy>\n"""
|
policy_response = """<cross-domain-policy><allow-access-from domain="*" to-ports="*" /></cross-domain-policy>\n"""
|
||||||
|
|
||||||
|
# An exception before the WebSocket connection was established
|
||||||
class EClose(Exception):
|
class EClose(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# An exception while the WebSocket client was connected
|
||||||
|
class CClose(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
||||||
verbose=False, cert='', key='', ssl_only=None,
|
verbose=False, cert='', key='', ssl_only=None,
|
||||||
daemon=False, record='', web=''):
|
daemon=False, record='', web='',
|
||||||
|
run_once=False, timeout=0):
|
||||||
|
|
||||||
# settings
|
# settings
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
@@ -97,6 +111,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.listen_port = listen_port
|
self.listen_port = listen_port
|
||||||
self.ssl_only = ssl_only
|
self.ssl_only = ssl_only
|
||||||
self.daemon = daemon
|
self.daemon = daemon
|
||||||
|
self.run_once = run_once
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
self.launch_time = time.time()
|
||||||
|
self.ws_connection = False
|
||||||
self.handler_id = 1
|
self.handler_id = 1
|
||||||
|
|
||||||
# Make paths settings absolute
|
# Make paths settings absolute
|
||||||
@@ -208,6 +227,38 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdout.fileno())
|
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdout.fileno())
|
||||||
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unmask(buf, f):
|
||||||
|
pstart = f['hlen'] + 4
|
||||||
|
pend = pstart + f['length']
|
||||||
|
if numpy:
|
||||||
|
b = c = s2b('')
|
||||||
|
if f['length'] >= 4:
|
||||||
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
|
offset=f['hlen'], count=1)
|
||||||
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
|
offset=pstart, count=int(f['length'] / 4))
|
||||||
|
#b = numpy.bitwise_xor(data, mask).data
|
||||||
|
b = numpy.bitwise_xor(data, mask).tostring()
|
||||||
|
|
||||||
|
if f['length'] % 4:
|
||||||
|
#print("Partial unmask")
|
||||||
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
|
offset=f['hlen'], count=(f['length'] % 4))
|
||||||
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
|
offset=pend - (f['length'] % 4),
|
||||||
|
count=(f['length'] % 4))
|
||||||
|
c = numpy.bitwise_xor(data, mask).tostring()
|
||||||
|
return b + c
|
||||||
|
else:
|
||||||
|
# Slower fallback
|
||||||
|
data = array.array('B')
|
||||||
|
mask = s2a(f['mask'])
|
||||||
|
data.fromstring(buf[pstart:pend])
|
||||||
|
for i in range(len(data)):
|
||||||
|
data[i] ^= mask[i % 4]
|
||||||
|
return data.tostring()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode_hybi(buf, opcode, base64=False):
|
def encode_hybi(buf, opcode, base64=False):
|
||||||
""" Encode a HyBi style WebSocket frame.
|
""" Encode a HyBi style WebSocket frame.
|
||||||
@@ -225,11 +276,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
b1 = 0x80 | (opcode & 0x0f) # FIN + opcode
|
b1 = 0x80 | (opcode & 0x0f) # FIN + opcode
|
||||||
payload_len = len(buf)
|
payload_len = len(buf)
|
||||||
if payload_len <= 125:
|
if payload_len <= 125:
|
||||||
header = struct.pack('>BB', b1, payload_len)
|
header = pack('>BB', b1, payload_len)
|
||||||
elif payload_len > 125 and payload_len <= 65536:
|
elif payload_len > 125 and payload_len < 65536:
|
||||||
header = struct.pack('>BBH', b1, 126, payload_len)
|
header = pack('>BBH', b1, 126, payload_len)
|
||||||
elif payload_len >= 65536:
|
elif payload_len >= 65536:
|
||||||
header = struct.pack('>BBQ', b1, 127, payload_len)
|
header = pack('>BBQ', b1, 127, payload_len)
|
||||||
|
|
||||||
#print("Encoded: %s" % repr(header + buf))
|
#print("Encoded: %s" % repr(header + buf))
|
||||||
|
|
||||||
@@ -257,8 +308,8 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
'length' : 0,
|
'length' : 0,
|
||||||
'payload' : None,
|
'payload' : None,
|
||||||
'left' : 0,
|
'left' : 0,
|
||||||
'close_code' : None,
|
'close_code' : 1000,
|
||||||
'close_reason' : None}
|
'close_reason' : ''}
|
||||||
|
|
||||||
blen = len(buf)
|
blen = len(buf)
|
||||||
f['left'] = blen
|
f['left'] = blen
|
||||||
@@ -266,7 +317,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if blen < f['hlen']:
|
if blen < f['hlen']:
|
||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
|
|
||||||
b1, b2 = struct.unpack_from(">BB", buf)
|
b1, b2 = unpack_from(">BB", buf)
|
||||||
f['opcode'] = b1 & 0x0f
|
f['opcode'] = b1 & 0x0f
|
||||||
f['fin'] = (b1 & 0x80) >> 7
|
f['fin'] = (b1 & 0x80) >> 7
|
||||||
has_mask = (b2 & 0x80) >> 7
|
has_mask = (b2 & 0x80) >> 7
|
||||||
@@ -277,12 +328,12 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
f['hlen'] = 4
|
f['hlen'] = 4
|
||||||
if blen < f['hlen']:
|
if blen < f['hlen']:
|
||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
(f['length'],) = struct.unpack_from('>xxH', buf)
|
(f['length'],) = unpack_from('>xxH', buf)
|
||||||
elif f['length'] == 127:
|
elif f['length'] == 127:
|
||||||
f['hlen'] = 10
|
f['hlen'] = 10
|
||||||
if blen < f['hlen']:
|
if blen < f['hlen']:
|
||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
(f['length'],) = struct.unpack_from('>xxQ', buf)
|
(f['length'],) = unpack_from('>xxQ', buf)
|
||||||
|
|
||||||
full_len = f['hlen'] + has_mask * 4 + f['length']
|
full_len = f['hlen'] + has_mask * 4 + f['length']
|
||||||
|
|
||||||
@@ -296,24 +347,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if has_mask:
|
if has_mask:
|
||||||
# unmask payload
|
# unmask payload
|
||||||
f['mask'] = buf[f['hlen']:f['hlen']+4]
|
f['mask'] = buf[f['hlen']:f['hlen']+4]
|
||||||
b = c = ''
|
f['payload'] = WebSocketServer.unmask(buf, f)
|
||||||
if f['length'] >= 4:
|
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<L4'),
|
|
||||||
offset=f['hlen'], count=1)
|
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('<L4'),
|
|
||||||
offset=f['hlen'] + 4, count=int(f['length'] / 4))
|
|
||||||
#b = numpy.bitwise_xor(data, mask).data
|
|
||||||
b = numpy.bitwise_xor(data, mask).tostring()
|
|
||||||
|
|
||||||
if f['length'] % 4:
|
|
||||||
print("Partial unmask")
|
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
|
||||||
offset=f['hlen'], count=(f['length'] % 4))
|
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
|
||||||
offset=full_len - (f['length'] % 4),
|
|
||||||
count=(f['length'] % 4))
|
|
||||||
c = numpy.bitwise_xor(data, mask).tostring()
|
|
||||||
f['payload'] = b + c
|
|
||||||
else:
|
else:
|
||||||
print("Unmasked frame: %s" % repr(buf))
|
print("Unmasked frame: %s" % repr(buf))
|
||||||
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
|
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
|
||||||
@@ -328,7 +362,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
if f['opcode'] == 0x08:
|
if f['opcode'] == 0x08:
|
||||||
if f['length'] >= 2:
|
if f['length'] >= 2:
|
||||||
f['close_code'] = struct.unpack_from(">H", f['payload'])
|
f['close_code'] = unpack_from(">H", f['payload'])[0]
|
||||||
if f['length'] > 3:
|
if f['length'] > 3:
|
||||||
f['close_reason'] = f['payload'][2:]
|
f['close_reason'] = f['payload'][2:]
|
||||||
|
|
||||||
@@ -358,7 +392,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
|
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
|
||||||
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
|
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
|
||||||
|
|
||||||
return b2s(md5(struct.pack('>II8s',
|
return b2s(md5(pack('>II8s',
|
||||||
int(num1), int(num2), key3)).digest())
|
int(num1), int(num2), key3)).digest())
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -441,7 +475,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
buf = self.client.recv(self.buffer_size)
|
buf = self.client.recv(self.buffer_size)
|
||||||
if len(buf) == 0:
|
if len(buf) == 0:
|
||||||
closed = "Client closed abruptly"
|
closed = {'code': 1000, 'reason': "Client closed abruptly"}
|
||||||
return bufs, closed
|
return bufs, closed
|
||||||
|
|
||||||
if self.recv_part:
|
if self.recv_part:
|
||||||
@@ -463,17 +497,17 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if frame['opcode'] == 0x8: # connection close
|
if frame['opcode'] == 0x8: # connection close
|
||||||
closed = "Client closed, reason: %s - %s" % (
|
closed = {'code': frame['close_code'],
|
||||||
frame['close_code'],
|
'reason': frame['close_reason']}
|
||||||
frame['close_reason'])
|
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if buf[0:2] == '\xff\x00':
|
if buf[0:2] == s2b('\xff\x00'):
|
||||||
closed = "Client sent orderly close frame"
|
closed = {'code': 1000,
|
||||||
|
'reason': "Client sent orderly close frame"}
|
||||||
break
|
break
|
||||||
|
|
||||||
elif buf[0:2] == '\x00\xff':
|
elif buf[0:2] == s2b('\x00\xff'):
|
||||||
buf = buf[2:]
|
buf = buf[2:]
|
||||||
continue # No-op
|
continue # No-op
|
||||||
|
|
||||||
@@ -503,13 +537,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
return bufs, closed
|
return bufs, closed
|
||||||
|
|
||||||
def send_close(self, code=None, reason=''):
|
def send_close(self, code=1000, reason=''):
|
||||||
""" Send a WebSocket orderly close frame. """
|
""" Send a WebSocket orderly close frame. """
|
||||||
|
|
||||||
if self.version.startswith("hybi"):
|
if self.version.startswith("hybi"):
|
||||||
msg = s2b('')
|
msg = pack(">H%ds" % len(reason), code, reason)
|
||||||
if code != None:
|
|
||||||
msg = struct.pack(">H%ds" % (len(reason)), code)
|
|
||||||
|
|
||||||
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
|
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
|
||||||
self.client.send(buf)
|
self.client.send(buf)
|
||||||
@@ -557,7 +589,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
sock.send(s2b(self.policy_response))
|
sock.send(s2b(self.policy_response))
|
||||||
raise self.EClose("Sending flash policy response")
|
raise self.EClose("Sending flash policy response")
|
||||||
|
|
||||||
elif handshake[0] in ("\x16", "\x80"):
|
elif handshake[0] in ("\x16", "\x80", 22, 128):
|
||||||
# SSL wrap the connection
|
# SSL wrap the connection
|
||||||
if not ssl:
|
if not ssl:
|
||||||
raise self.EClose("SSL connection but no 'ssl' module")
|
raise self.EClose("SSL connection but no 'ssl' module")
|
||||||
@@ -574,7 +606,10 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
except ssl.SSLError:
|
except ssl.SSLError:
|
||||||
_, x, _ = sys.exc_info()
|
_, x, _ = sys.exc_info()
|
||||||
if x.args[0] == ssl.SSL_ERROR_EOF:
|
if x.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
raise self.EClose("")
|
if len(x.args) > 1:
|
||||||
|
raise self.EClose(x.args[1])
|
||||||
|
else:
|
||||||
|
raise self.EClose("Got SSL_ERROR_EOF")
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -612,11 +647,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if ver:
|
if ver:
|
||||||
# HyBi/IETF version of the protocol
|
# HyBi/IETF version of the protocol
|
||||||
|
|
||||||
if sys.hexversion < 0x2060000 or not numpy:
|
# HyBi-07 report version 7
|
||||||
raise self.EClose("Python >= 2.6 and numpy module is required for HyBi-07 or greater")
|
# HyBi-08 - HyBi-12 report version 8
|
||||||
|
# HyBi-13 reports version 13
|
||||||
if ver in ['7', '8', '9']:
|
if ver in ['7', '8', '13']:
|
||||||
self.version = "hybi-0" + ver
|
self.version = "hybi-%02d" % int(ver)
|
||||||
else:
|
else:
|
||||||
raise self.EClose('Unsupported protocol version %s' % ver)
|
raise self.EClose('Unsupported protocol version %s' % ver)
|
||||||
|
|
||||||
@@ -633,7 +668,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
# Generate the hash value for the accept header
|
# Generate the hash value for the accept header
|
||||||
accept = b64encode(sha1(s2b(key + self.GUID)).digest())
|
accept = b64encode(sha1(s2b(key + self.GUID)).digest())
|
||||||
|
|
||||||
response = self.server_handshake_hybi % accept
|
response = self.server_handshake_hybi % b2s(accept)
|
||||||
if self.base64:
|
if self.base64:
|
||||||
response += "Sec-WebSocket-Protocol: base64\r\n"
|
response += "Sec-WebSocket-Protocol: base64\r\n"
|
||||||
else:
|
else:
|
||||||
@@ -667,6 +702,9 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.msg("%s: %s WebSocket connection" % (address[0], stype))
|
self.msg("%s: %s WebSocket connection" % (address[0], stype))
|
||||||
self.msg("%s: Version %s, base64: '%s'" % (address[0],
|
self.msg("%s: Version %s, base64: '%s'" % (address[0],
|
||||||
self.version, self.base64))
|
self.version, self.base64))
|
||||||
|
if self.path != '/':
|
||||||
|
self.msg("%s: Path: '%s'" % (address[0], self.path))
|
||||||
|
|
||||||
|
|
||||||
# Send server WebSockets handshake response
|
# Send server WebSockets handshake response
|
||||||
#self.msg("sending response [%s]" % response)
|
#self.msg("sending response [%s]" % response)
|
||||||
@@ -725,7 +763,13 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.rec = open(fname, 'w+')
|
self.rec = open(fname, 'w+')
|
||||||
self.rec.write("var VNC_frame_data = [\n")
|
self.rec.write("var VNC_frame_data = [\n")
|
||||||
|
|
||||||
|
self.ws_connection = True
|
||||||
self.new_client()
|
self.new_client()
|
||||||
|
except self.CClose:
|
||||||
|
# Close the client
|
||||||
|
_, exc, _ = sys.exc_info()
|
||||||
|
if self.client:
|
||||||
|
self.send_close(exc.args[0], exc.args[1])
|
||||||
except self.EClose:
|
except self.EClose:
|
||||||
_, exc, _ = sys.exc_info()
|
_, exc, _ = sys.exc_info()
|
||||||
# Connection was not a WebSockets connection
|
# Connection was not a WebSockets connection
|
||||||
@@ -742,6 +786,8 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.rec.close()
|
self.rec.close()
|
||||||
|
|
||||||
if self.client and self.client != startsock:
|
if self.client and self.client != startsock:
|
||||||
|
# Close the SSL wrapped socket
|
||||||
|
# Original socket closed by caller
|
||||||
self.client.close()
|
self.client.close()
|
||||||
|
|
||||||
def new_client(self):
|
def new_client(self):
|
||||||
@@ -764,7 +810,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
# Allow override of SIGINT
|
# Allow override of SIGINT
|
||||||
signal.signal(signal.SIGINT, self.do_SIGINT)
|
signal.signal(signal.SIGINT, self.do_SIGINT)
|
||||||
if not Process:
|
if not multiprocessing:
|
||||||
# os.fork() (python 2.4) child reaper
|
# os.fork() (python 2.4) child reaper
|
||||||
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
||||||
|
|
||||||
@@ -775,6 +821,12 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
startsock = None
|
startsock = None
|
||||||
pid = err = 0
|
pid = err = 0
|
||||||
|
|
||||||
|
time_elapsed = time.time() - self.launch_time
|
||||||
|
if self.timeout and time_elapsed > self.timeout:
|
||||||
|
self.msg('listener exit due to --timeout %s'
|
||||||
|
% self.timeout)
|
||||||
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.poll()
|
self.poll()
|
||||||
|
|
||||||
@@ -797,9 +849,17 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if Process:
|
if self.run_once:
|
||||||
|
# Run in same process if run_once
|
||||||
|
self.top_new_client(startsock, address)
|
||||||
|
if self.ws_connection :
|
||||||
|
self.msg('%s: exiting due to --run-once'
|
||||||
|
% address[0])
|
||||||
|
break
|
||||||
|
elif multiprocessing:
|
||||||
self.vmsg('%s: new handler Process' % address[0])
|
self.vmsg('%s: new handler Process' % address[0])
|
||||||
p = Process(target=self.top_new_client,
|
p = multiprocessing.Process(
|
||||||
|
target=self.top_new_client,
|
||||||
args=(startsock, address))
|
args=(startsock, address))
|
||||||
p.start()
|
p.start()
|
||||||
# child will not return
|
# child will not return
|
||||||
|
|||||||
@@ -190,7 +190,8 @@ Traffic Legend:
|
|||||||
if target in ins:
|
if target in ins:
|
||||||
# Receive target data, encode it and queue for client
|
# Receive target data, encode it and queue for client
|
||||||
buf = target.recv(self.buffer_size)
|
buf = target.recv(self.buffer_size)
|
||||||
if len(buf) == 0: raise self.EClose("Target closed")
|
if len(buf) == 0:
|
||||||
|
raise self.CClose(1000, "Target closed")
|
||||||
|
|
||||||
cqueue.append(buf)
|
cqueue.append(buf)
|
||||||
self.traffic("{")
|
self.traffic("{")
|
||||||
@@ -210,10 +211,9 @@ Traffic Legend:
|
|||||||
|
|
||||||
if closed:
|
if closed:
|
||||||
# TODO: What about blocking on client socket?
|
# TODO: What about blocking on client socket?
|
||||||
self.send_close()
|
raise self.CClose(closed['code'], closed['reason'])
|
||||||
raise self.EClose(closed)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def websockify_init():
|
||||||
usage = "\n %prog [options]"
|
usage = "\n %prog [options]"
|
||||||
usage += " [source_addr:]source_port target_addr:target_port"
|
usage += " [source_addr:]source_port target_addr:target_port"
|
||||||
usage += "\n %prog [options]"
|
usage += "\n %prog [options]"
|
||||||
@@ -226,6 +226,10 @@ if __name__ == '__main__':
|
|||||||
parser.add_option("--daemon", "-D",
|
parser.add_option("--daemon", "-D",
|
||||||
dest="daemon", action="store_true",
|
dest="daemon", action="store_true",
|
||||||
help="become a daemon (background process)")
|
help="become a daemon (background process)")
|
||||||
|
parser.add_option("--run-once", action="store_true",
|
||||||
|
help="handle a single WebSocket connection and exit")
|
||||||
|
parser.add_option("--timeout", type=int, default=0,
|
||||||
|
help="after TIMEOUT seconds exit when not connected")
|
||||||
parser.add_option("--cert", default="self.pem",
|
parser.add_option("--cert", default="self.pem",
|
||||||
help="SSL certificate file")
|
help="SSL certificate file")
|
||||||
parser.add_option("--key", default=None,
|
parser.add_option("--key", default=None,
|
||||||
@@ -255,7 +259,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Parse host:port and convert ports to numbers
|
# Parse host:port and convert ports to numbers
|
||||||
if args[0].count(':') > 0:
|
if args[0].count(':') > 0:
|
||||||
opts.listen_host, sep, opts.listen_port = args[0].rpartition(':')
|
opts.listen_host, opts.listen_port = args[0].rsplit(':', 1)
|
||||||
else:
|
else:
|
||||||
opts.listen_host, opts.listen_port = '', args[0]
|
opts.listen_host, opts.listen_port = '', args[0]
|
||||||
|
|
||||||
@@ -267,7 +271,7 @@ if __name__ == '__main__':
|
|||||||
opts.target_port = None
|
opts.target_port = None
|
||||||
else:
|
else:
|
||||||
if args[1].count(':') > 0:
|
if args[1].count(':') > 0:
|
||||||
opts.target_host, sep, opts.target_port = args[1].rpartition(':')
|
opts.target_host, opts.target_port = args[1].rsplit(':', 1)
|
||||||
else:
|
else:
|
||||||
parser.error("Error parsing target")
|
parser.error("Error parsing target")
|
||||||
try: opts.target_port = int(opts.target_port)
|
try: opts.target_port = int(opts.target_port)
|
||||||
@@ -276,3 +280,6 @@ if __name__ == '__main__':
|
|||||||
# Create and start the WebSockets proxy
|
# Create and start the WebSockets proxy
|
||||||
server = WebSocketProxy(**opts.__dict__)
|
server = WebSocketProxy(**opts.__dict__)
|
||||||
server.start_server()
|
server.start_server()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
websockify_init()
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
websockify
|
||||||
@@ -1,31 +1,200 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.1//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile11.dtd">
|
||||||
<html>
|
<html>
|
||||||
<!--
|
<head>
|
||||||
|
|
||||||
|
<!--
|
||||||
noVNC example: simple example using default UI
|
noVNC example: simple example using default UI
|
||||||
Copyright (C) 2011 Joel Martin
|
Copyright (C) 2012 Joel Martin
|
||||||
Licensed under LGPL-3 (see LICENSE.txt)
|
noVNC is licensed under the LGPL-3 (see LICENSE.txt)
|
||||||
|
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
-->
|
-->
|
||||||
<head>
|
<title>noVNC</title>
|
||||||
<title>noVNC</title>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
||||||
<link rel="stylesheet" href="include/plain.css">
|
|
||||||
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black">
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="include/vnc.js"></script>
|
|
||||||
<script src="include/ui.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<meta charset="utf-8">
|
||||||
<div id='vnc'>Loading</div>
|
|
||||||
|
|
||||||
<script>
|
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
|
||||||
window.onload = function () {
|
Remove this if you use the .htaccess -->
|
||||||
UI.load('vnc');
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
<!-- Apple iOS Safari settings -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta names="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
|
<!-- App Start Icon -->
|
||||||
|
<link rel="apple-touch-startup-image" href="images/screen_320x460.png" />
|
||||||
|
<!-- For iOS devices set the icon to use if user bookmarks app on their homescreen -->
|
||||||
|
<link rel="apple-touch-icon" href="images/screen_57x57.png">
|
||||||
|
<!--
|
||||||
|
<link rel="apple-touch-icon-precomposed" href="images/screen_57x57.png" />
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="include/base.css" />
|
||||||
|
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black" />
|
||||||
|
<link rel="alternate stylesheet" href="include/blue.css" TITLE="Blue" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script src="include/vnc.js"></script>
|
||||||
|
<script src="include/ui.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="noVNC-control-bar">
|
||||||
|
<!--noVNC Mobile Device only Buttons-->
|
||||||
|
<div class="noVNC-buttons-left">
|
||||||
|
<input type="image" src="images/drag.png"
|
||||||
|
id="noVNC_view_drag_button" class="noVNC_status_button"
|
||||||
|
title="Move/Drag Viewport"
|
||||||
|
onclick="UI.setViewDrag();">
|
||||||
|
<div id="noVNC_mobile_buttons">
|
||||||
|
<input type="image" src="images/mouse_none.png"
|
||||||
|
id="noVNC_mouse_button0" class="noVNC_status_button"
|
||||||
|
onclick="UI.setMouseButton(1);">
|
||||||
|
<input type="image" src="images/mouse_left.png"
|
||||||
|
id="noVNC_mouse_button1" class="noVNC_status_button"
|
||||||
|
onclick="UI.setMouseButton(2);">
|
||||||
|
<input type="image" src="images/mouse_middle.png"
|
||||||
|
id="noVNC_mouse_button2" class="noVNC_status_button"
|
||||||
|
onclick="UI.setMouseButton(4);">
|
||||||
|
<input type="image" src="images/mouse_right.png"
|
||||||
|
id="noVNC_mouse_button4" class="noVNC_status_button"
|
||||||
|
onclick="UI.setMouseButton(0);">
|
||||||
|
<input type="image" src="images/keyboard.png"
|
||||||
|
id="showKeyboard" class="noVNC_status_button"
|
||||||
|
value="Keyboard" title="Show Keyboard"
|
||||||
|
onclick="UI.showKeyboard()"/>
|
||||||
|
<input type="email"
|
||||||
|
autocapitalize="off" autocorrect="off"
|
||||||
|
id="keyboardinput" class="noVNC_status_button"
|
||||||
|
onKeyDown="onKeyDown(event);" onblur="UI.keyInputBlur();"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--noVNC Buttons-->
|
||||||
|
<div class="noVNC-buttons-right">
|
||||||
|
<input type="image" src="images/ctrlaltdel.png"
|
||||||
|
id="sendCtrlAltDelButton" class="noVNC_status_button"
|
||||||
|
title="Send Ctrl-Alt-Del"
|
||||||
|
onclick="UI.sendCtrlAltDel();" />
|
||||||
|
<input type="image" src="images/clipboard.png"
|
||||||
|
id="clipboardButton" class="noVNC_status_button"
|
||||||
|
title="Clipboard"
|
||||||
|
onclick="UI.toggleClipboardPanel();" />
|
||||||
|
<input type="image" src="images/settings.png"
|
||||||
|
id="settingsButton" class="noVNC_status_button"
|
||||||
|
title="Settings"
|
||||||
|
onclick="UI.toggleSettingsPanel();" />
|
||||||
|
<input type="image" src="images/connect.png"
|
||||||
|
id="connectButton" class="noVNC_status_button"
|
||||||
|
title="Connect"
|
||||||
|
onclick="UI.toggleConnectPanel()" />
|
||||||
|
<input type="image" src="images/disconnect.png"
|
||||||
|
id="disconnectButton" class="noVNC_status_button"
|
||||||
|
title="Disconnect"
|
||||||
|
onclick="UI.disconnect()" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Description Panel -->
|
||||||
|
<!-- Shown by default when hosted at for kanaka.github.com -->
|
||||||
|
<div id="noVNC_description" style="display:none;" class="">
|
||||||
|
noVNC is a browser based VNC client implemented using HTML5 Canvas
|
||||||
|
and WebSockets. You will either need a VNC server with WebSockets
|
||||||
|
support (such as <a href="http://libvncserver.sourceforge.net/">libvncserver</a>)
|
||||||
|
or you will need to use
|
||||||
|
<a href="https://github.com/kanaka/websockify">websockify</a>
|
||||||
|
to bridge between your browser and VNC server. See the noVNC
|
||||||
|
<a href="https://github.com/kanaka/noVNC">README</a>
|
||||||
|
and <a href="http://kanaka.github.com/noVNC">website</a>
|
||||||
|
for more information.
|
||||||
|
<br />
|
||||||
|
<input type="button" value="Close"
|
||||||
|
onclick="UI.toggleConnectPanel();">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Clipboard Panel -->
|
||||||
|
<div id="noVNC_clipboard" class="triangle-right top">
|
||||||
|
<textarea id="noVNC_clipboard_text" rows=5
|
||||||
|
onfocus="UI.displayBlur();" onblur="UI.displayFocus();"
|
||||||
|
onchange="UI.clipSend();">
|
||||||
|
</textarea>
|
||||||
|
<br />
|
||||||
|
<input id="noVNC_clipboard_clear_button" type="button"
|
||||||
|
value="Clear" onclick="UI.clipClear();">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings Panel -->
|
||||||
|
<div id="noVNC_settings" class="triangle-right top">
|
||||||
|
<span id="noVNC_settings_menu" onmouseover="UI.displayBlur();"
|
||||||
|
onmouseout="UI.displayFocus();">
|
||||||
|
<ul>
|
||||||
|
<li><input id="noVNC_encrypt" type="checkbox"> Encrypt</li>
|
||||||
|
<li><input id="noVNC_true_color" type="checkbox" checked> True Color</li>
|
||||||
|
<li><input id="noVNC_cursor" type="checkbox"> Local Cursor</li>
|
||||||
|
<li><input id="noVNC_clip" type="checkbox"> Clip to Window</li>
|
||||||
|
<li><input id="noVNC_shared" type="checkbox"> Shared Mode</li>
|
||||||
|
<li><input id="noVNC_view_only" type="checkbox"> View Only</li>
|
||||||
|
<li><input id="noVNC_connectTimeout" type="input"> Connect Timeout (s)</li>
|
||||||
|
<li><input id="noVNC_path" type="input" value="websockify"> Path</li>
|
||||||
|
<li><input id="noVNC_repeaterID" type="input" value=""> Repeater ID</li>
|
||||||
|
<hr>
|
||||||
|
<!-- Stylesheet selection dropdown -->
|
||||||
|
<li><label><strong>Style: </strong>
|
||||||
|
<select id="noVNC_stylesheet" name="vncStyle">
|
||||||
|
<option value="default">default</option>
|
||||||
|
</select></label>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Logging selection dropdown -->
|
||||||
|
<li><label><strong>Logging: </strong>
|
||||||
|
<select id="noVNC_logging" name="vncLogging">
|
||||||
|
</select></label>
|
||||||
|
</li>
|
||||||
|
<hr>
|
||||||
|
<li><input type="button" id="noVNC_apply" value="Apply"
|
||||||
|
onclick="UI.settingsApply()"></li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Connection Panel -->
|
||||||
|
<div id="noVNC_controls" class="triangle-right top">
|
||||||
|
<ul>
|
||||||
|
<li><label><strong>Host: </strong><input id="noVNC_host" /></label></li>
|
||||||
|
<li><label><strong>Port: </strong><input id="noVNC_port" /></label></li>
|
||||||
|
<li><label><strong>Password: </strong><input id="noVNC_password" type="password" /></label></li>
|
||||||
|
<li><input id="noVNC_connect_button" type="button" value="Connect" onclick="UI.connect();"></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- End of noVNC-control-bar -->
|
||||||
|
|
||||||
|
|
||||||
|
<div id="noVNC_screen">
|
||||||
|
<div id="noVNC_screen_pad"></div>
|
||||||
|
|
||||||
|
<div id="noVNC_status_bar" class="noVNC_status_bar">
|
||||||
|
<div id="noVNC_status">Loading</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 id="noVNC_logo"><span>no</span><br />VNC</h1>
|
||||||
|
|
||||||
|
<!-- HTML5 Canvas -->
|
||||||
|
<div id="noVNC_container">
|
||||||
|
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||||
|
Canvas not supported.
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = UI.load;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<!--
|
<!--
|
||||||
noVNC Example: Automatically connect on page load.
|
noVNC example: simple example using default UI
|
||||||
Copyright (C) 2011 Joel Martin
|
Copyright (C) 2012 Joel Martin
|
||||||
Licensed under LGPL-3 (see LICENSE.txt)
|
noVNC is licensed under the LGPL-3 (see LICENSE.txt)
|
||||||
|
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
|
|
||||||
Connect parameters are provided in query string:
|
Connect parameters are provided in query string:
|
||||||
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
|
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>noVNC</title>
|
<title>noVNC</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||||
<link rel="stylesheet" href="include/plain.css" title="plain">
|
<link rel="stylesheet" href="include/base.css" title="plain">
|
||||||
<!--
|
<!--
|
||||||
<script type='text/javascript'
|
<script type='text/javascript'
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
@@ -20,17 +21,17 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="margin: 0px;">
|
<body style="margin: 0px;">
|
||||||
<div id="VNC_screen">
|
<div id="noVNC_screen">
|
||||||
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
|
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
|
||||||
<table border=0 width="100%"><tr>
|
<table border=0 width="100%"><tr>
|
||||||
<td><div id="VNC_status">Loading</div></td>
|
<td><div id="noVNC_status">Loading</div></td>
|
||||||
<td width="1%"><div id="VNC_buttons">
|
<td width="1%"><div id="noVNC_buttons">
|
||||||
<input type=button value="Send CtrlAltDel"
|
<input type=button value="Send CtrlAltDel"
|
||||||
id="sendCtrlAltDelButton">
|
id="sendCtrlAltDelButton">
|
||||||
</div></td>
|
</div></td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="VNC_canvas" width="640px" height="20px">
|
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||||
Canvas not supported.
|
Canvas not supported.
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
@@ -47,10 +48,10 @@
|
|||||||
msg = '<form onsubmit="return setPassword();"';
|
msg = '<form onsubmit="return setPassword();"';
|
||||||
msg += ' style="margin-bottom: 0px">';
|
msg += ' style="margin-bottom: 0px">';
|
||||||
msg += 'Password Required: ';
|
msg += 'Password Required: ';
|
||||||
msg += '<input type=password size=10 id="password_input" class="VNC_status">';
|
msg += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
||||||
msg += '<\/form>';
|
msg += '<\/form>';
|
||||||
$D('VNC_status_bar').setAttribute("class", "VNC_status_warn");
|
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
||||||
$D('VNC_status').innerHTML = msg;
|
$D('noVNC_status').innerHTML = msg;
|
||||||
}
|
}
|
||||||
function setPassword() {
|
function setPassword() {
|
||||||
rfb.sendPassword($D('password_input').value);
|
rfb.sendPassword($D('password_input').value);
|
||||||
@@ -62,8 +63,8 @@
|
|||||||
}
|
}
|
||||||
function updateState(rfb, state, oldstate, msg) {
|
function updateState(rfb, state, oldstate, msg) {
|
||||||
var s, sb, cad, level;
|
var s, sb, cad, level;
|
||||||
s = $D('VNC_status');
|
s = $D('noVNC_status');
|
||||||
sb = $D('VNC_status_bar');
|
sb = $D('noVNC_status_bar');
|
||||||
cad = $D('sendCtrlAltDelButton');
|
cad = $D('sendCtrlAltDelButton');
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'failed': level = "error"; break;
|
case 'failed': level = "error"; break;
|
||||||
@@ -78,34 +79,49 @@
|
|||||||
else { cad.disabled = true; }
|
else { cad.disabled = true; }
|
||||||
|
|
||||||
if (typeof(msg) !== 'undefined') {
|
if (typeof(msg) !== 'undefined') {
|
||||||
sb.setAttribute("class", "VNC_status_" + level);
|
sb.setAttribute("class", "noVNC_status_" + level);
|
||||||
s.innerHTML = msg;
|
s.innerHTML = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
var host, port, password;
|
var host, port, password, path, token;
|
||||||
|
|
||||||
|
$D('sendCtrlAltDelButton').style.display = "inline";
|
||||||
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
||||||
|
|
||||||
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
|
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
|
||||||
host = WebUtil.getQueryVar('host', null);
|
// By default, use the host and port of server that served this file
|
||||||
port = WebUtil.getQueryVar('port', null);
|
host = WebUtil.getQueryVar('host', window.location.hostname);
|
||||||
|
port = WebUtil.getQueryVar('port', window.location.port);
|
||||||
|
|
||||||
|
// If a token variable is passed in, set the parameter in a cookie.
|
||||||
|
// This is used by nova-novncproxy.
|
||||||
|
token = WebUtil.getQueryVar('token', null);
|
||||||
|
if (token) {
|
||||||
|
WebUtil.createCookie('token', token, 1)
|
||||||
|
}
|
||||||
|
|
||||||
password = WebUtil.getQueryVar('password', '');
|
password = WebUtil.getQueryVar('password', '');
|
||||||
|
path = WebUtil.getQueryVar('path', 'websockify');
|
||||||
|
|
||||||
if ((!host) || (!port)) {
|
if ((!host) || (!port)) {
|
||||||
updateState('failed',
|
updateState('failed',
|
||||||
"Must specify host and port in URL");
|
"Must specify host and port in URL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
rfb = new RFB({'target': $D('noVNC_canvas'),
|
||||||
'encrypt': WebUtil.getQueryVar('encrypt', false),
|
'encrypt': WebUtil.getQueryVar('encrypt',
|
||||||
|
(window.location.protocol === "https:")),
|
||||||
|
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
|
||||||
'true_color': WebUtil.getQueryVar('true_color', true),
|
'true_color': WebUtil.getQueryVar('true_color', true),
|
||||||
'local_cursor': WebUtil.getQueryVar('cursor', true),
|
'local_cursor': WebUtil.getQueryVar('cursor', true),
|
||||||
'shared': WebUtil.getQueryVar('shared', true),
|
'shared': WebUtil.getQueryVar('shared', true),
|
||||||
|
'view_only': WebUtil.getQueryVar('view_only', false),
|
||||||
'updateState': updateState,
|
'updateState': updateState,
|
||||||
'onPasswordRequired': passwordRequired});
|
'onPasswordRequired': passwordRequired});
|
||||||
rfb.connect(host, port, password);
|
rfb.connect(host, port, password, path);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||