Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b302463f8 | |||
| 23ff806774 | |||
| 94bcf652b3 | |||
| 0ce93900ae | |||
| 9e6e6662c7 | |||
| 32f135d730 | |||
| 1af3e54bef | |||
| 6a02f88e4d | |||
| 523cc4d6ab | |||
| 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 |
@@ -5,26 +5,29 @@ Please refer to the individual source files and/or to the noVNC commit
|
||||
history: https://github.com/kanaka/noVNC/commits/master
|
||||
|
||||
noVNC is licensed under the LGPL (GNU Lesser General Public License)
|
||||
version 3 with the following exceptions:
|
||||
version 3 with the following exceptions (all LGPL-3 compatible):
|
||||
|
||||
include/input.js : LGPL-2 or any later version
|
||||
|
||||
include/base64.js : Dual GPL-2 or LGPL-2.1
|
||||
|
||||
incluee/des.js : Various BSD style licenses
|
||||
include/des.js : Various BSD style licenses
|
||||
|
||||
include/web-socket-js/ : New BSD license. Source code at
|
||||
http://github.com/gimite/web-socket-js
|
||||
|
||||
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/
|
||||
Uses of the work must be attributed
|
||||
to the noVNC project.
|
||||
|
||||
The LGPL-3 license text is included at:
|
||||
The license texts are included at:
|
||||
docs/LICENSE.LGPL-3 and
|
||||
docs/LICENSE.GPL-3
|
||||
docs/LICENSE.OFL-1.1
|
||||
|
||||
Or alternatively the LGPL-3 license text may be found here:
|
||||
Or alternatively the license texts may be found here:
|
||||
http://www.gnu.org/licenses/lgpl.html and
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
@@ -3,69 +3,63 @@
|
||||
|
||||
### Description
|
||||
|
||||
noVNC is a VNC client implemented using HTML5 technologies,
|
||||
specifically Canvas and WebSockets (supports 'wss://' encryption).
|
||||
noVNC is licensed under the
|
||||
[LGPLv3](http://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
Special thanks to [Sentry Data Systems](http://www.sentryds.com) for
|
||||
sponsoring ongoing development of this project (and for employing me).
|
||||
|
||||
There are many companies/projects that have integrated noVNC into
|
||||
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.
|
||||
noVNC is a HTML5 VNC client that runs well in any modern browser
|
||||
including mobile browsers (iPhone/iPad and Android).
|
||||
|
||||
Notable commits, announcements and news are posted to
|
||||
@<a href="http://www.twitter.com/noVNC">noVNC</a>
|
||||
|
||||
There are many companies/projects that have integrated noVNC into
|
||||
their products including: [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/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), [SlapOS](http://www.slapos.org), [Intel MeshCentral](https://meshcentral.com), [Amahi](http://amahi.org), [Brightbox](http://brightbox.com/), and [LibVNCServer](http://libvncserver.sourceforge.net). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links.
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Supports all modern browsers including mobile (iOS, Android)
|
||||
* Supported VNC encodings: raw, copyrect, rre, hextile, 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
|
||||
|
||||
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>.
|
||||
|
||||
|
||||
### Browser Requirements
|
||||
|
||||
* HTML5 Canvas: Except for Internet Explorer, most
|
||||
browsers have had Canvas support for quite some time. Internet
|
||||
Explorer 9 will have Canvas support (finally).
|
||||
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
|
||||
Safari, Opera 11+, Internet Explorer 9+, etc.
|
||||
|
||||
* HTML5 WebSockets: For browsers that do not have builtin
|
||||
WebSockets support, the project includes
|
||||
<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
|
||||
functionality so it will run on older browsers, but decode and
|
||||
rendering happen in Javascript, so a slow Javascript engine will
|
||||
mean noVNC is painfully slow.
|
||||
* Fast Javascript Engine: this is not strictly a requirement, but
|
||||
without a fast Javascript engine, noVNC might be 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>.
|
||||
|
||||
|
||||
### Server Requirements
|
||||
|
||||
Unless you are using a VNC server with support for WebSockets
|
||||
connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver)
|
||||
currently), you need to use a WebSockets to TCP socket proxy. There is
|
||||
a python proxy included ('websockify'). One advantage of using the
|
||||
proxy is that it has builtin support for SSL/TLS encryption (i.e.
|
||||
"wss://").
|
||||
|
||||
There a few reasons why a proxy is required:
|
||||
|
||||
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.
|
||||
connections (such as [x11vnc/libvncserver](http://libvncserver.sourceforge.net/)),
|
||||
you need to use a WebSockets to TCP socket proxy. There is
|
||||
a python proxy included ('websockify').
|
||||
|
||||
|
||||
### Quick Start
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
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
|
||||
|
||||
* First upstream release
|
||||
|
||||
@@ -9,14 +9,16 @@ utils/websockify /usr/share/novnc/utils
|
||||
utils/wsproxy.py /usr/share/novnc/utils
|
||||
utils/rebind.c /usr/share/novnc/utils
|
||||
utils/rebind.so /usr/share/novnc/utils
|
||||
images /usr/share/novnc
|
||||
images/favicon.ico /usr/share/novnc
|
||||
include/base64.js /usr/share/novnc/include
|
||||
include/black.css /usr/share/novnc/include
|
||||
include/des.js /usr/share/novnc/include
|
||||
include/display.js /usr/share/novnc/include
|
||||
include/input.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/rfb.js /usr/share/novnc/include
|
||||
include/ui.js /usr/share/novnc/include
|
||||
|
||||
@@ -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.
|
||||
@@ -1,53 +1,19 @@
|
||||
Short Term:
|
||||
- Use requestAnimationFrame for frameBufferUpdate polling
|
||||
|
||||
- Test normal arrays vs TypedArrays vs imageData arrays.
|
||||
- Blog post about it.
|
||||
- Use WebSocket binary API when available.
|
||||
|
||||
- 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
|
||||
- Scaling support
|
||||
|
||||
- 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
|
||||
- Keyboard menu/panel:
|
||||
- Move CtrlAltDel button here
|
||||
- direct text entry with Send/Paste button
|
||||
- Ctrl Once/Lock, Alt Once/Lock, SysRq Once/Lock
|
||||
- Highlight menu icon when keys are locked
|
||||
|
||||
- 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.
|
||||
- zlib/tight support (even if slow)
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1
|
||||
0.2
|
||||
|
||||
@@ -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,398 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,45 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
font-size: 13px;
|
||||
color: #111;
|
||||
font-family: "Helvetica";
|
||||
#keyboardinput {
|
||||
background-color:#000;
|
||||
}
|
||||
|
||||
#VNC_controls {
|
||||
background: #111;
|
||||
line-height: 1em;
|
||||
color: #FFF;
|
||||
overflow: hidden;
|
||||
padding: 4px 24px;
|
||||
#noVNC-control-bar {
|
||||
background: #4c4c4c; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
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+ */
|
||||
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 {
|
||||
list-style:none;
|
||||
list-style-position: outside;
|
||||
margin: 0px;
|
||||
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;
|
||||
.triangle-right {
|
||||
border:2px solid #fff;
|
||||
background:#000;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#VNC_host {
|
||||
width: 100px;
|
||||
}
|
||||
#VNC_port {
|
||||
width: 50px;
|
||||
}
|
||||
#VNC_password {
|
||||
width: 80px;
|
||||
}
|
||||
#VNC_encrypt {
|
||||
}
|
||||
#VNC_connect_button {
|
||||
width: 100px;
|
||||
.noVNC_status_button {
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #4c4c4c;
|
||||
|
||||
background: #4c4c4c; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
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+ */
|
||||
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+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||
}
|
||||
|
||||
#VNC_status_bar td {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
#VNC_status_bar div {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin: 0px;
|
||||
padding: 1em;
|
||||
}
|
||||
.VNC_status_button {
|
||||
font-size: 10px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#VNC_status {
|
||||
text-align: center;
|
||||
}
|
||||
#VNC_settings_menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 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;
|
||||
.noVNC_status_button_selected {
|
||||
background: #9dd53a; /* Old browsers */
|
||||
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+ */
|
||||
background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -19,19 +19,23 @@ var that = {}, // Public API methods
|
||||
c_ctx = null,
|
||||
c_forceCanvas = false,
|
||||
|
||||
c_imageData, c_rgbxImage, c_cmapImage,
|
||||
|
||||
// Predefine function variables (jslint)
|
||||
imageDataCreate, imageDataGet, rgbxImageData, cmapImageData,
|
||||
rgbxImageFill, cmapImageFill, setFillColor, rescale, flush,
|
||||
imageDataGet, bgrxImageData, cmapImageData,
|
||||
setFillColor, rescale,
|
||||
|
||||
c_width = 0,
|
||||
c_height = 0,
|
||||
// The full frame buffer (logical canvas) size
|
||||
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 = "",
|
||||
tile = null,
|
||||
tile16x16 = null,
|
||||
tile_x = 0,
|
||||
tile_y = 0;
|
||||
|
||||
c_webkit_bug = false,
|
||||
c_flush_timer = null;
|
||||
|
||||
// Configuration attributes
|
||||
Util.conf_defaults(conf, that, defaults, [
|
||||
@@ -41,6 +45,7 @@ Util.conf_defaults(conf, that, defaults, [
|
||||
['true_color', 'rw', 'bool', true, 'Use true-color pixel data'],
|
||||
['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'],
|
||||
['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'],
|
||||
['height', 'rw', 'int', null, 'Display area height'],
|
||||
|
||||
@@ -55,20 +60,11 @@ that.get_context = function () { return c_ctx; };
|
||||
|
||||
that.set_scale = function(scale) { rescale(scale); };
|
||||
|
||||
that.set_width = function (val) { that.resize(val, c_height); };
|
||||
that.get_width = function() { return c_width; };
|
||||
that.set_width = function (val) { that.resize(val, fb_height); };
|
||||
that.get_width = function() { return fb_width; };
|
||||
|
||||
that.set_height = function (val) { that.resize(c_width, val); };
|
||||
that.get_height = function() { return c_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;
|
||||
};
|
||||
that.set_height = function (val) { that.resize(fb_width, val); };
|
||||
that.get_height = function() { return fb_height; };
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +76,7 @@ that.set_prefer_js = function(val) {
|
||||
function 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;
|
||||
|
||||
if (! conf.target) { throw("target must be set"); }
|
||||
@@ -103,70 +99,19 @@ function constructor() {
|
||||
|
||||
that.clear();
|
||||
|
||||
/*
|
||||
* Determine browser Canvas feature support
|
||||
* and select fastest rendering methods
|
||||
*/
|
||||
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;
|
||||
// Check canvas features
|
||||
if ('createImageData' in c_ctx) {
|
||||
conf.render_mode = "canvas rendering";
|
||||
} else {
|
||||
Util.Warn("Canvas lacks imageData, using fillRect (slow)");
|
||||
conf.render_mode = "fillRect rendering (slow)";
|
||||
c_forceCanvas = true;
|
||||
conf.prefer_js = false;
|
||||
c_rgbxImage = rgbxImageFill;
|
||||
c_cmapImage = cmapImageFill;
|
||||
throw("Canvas does not support createImageData");
|
||||
}
|
||||
if (conf.prefer_js === null) {
|
||||
Util.Info("Prefering javascript operations");
|
||||
conf.prefer_js = true;
|
||||
}
|
||||
|
||||
if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) {
|
||||
// Workaround WebKit canvas rendering bug #46319
|
||||
conf.render_mode += ", webkit bug workaround";
|
||||
Util.Debug("Working around WebKit bug #46319");
|
||||
c_webkit_bug = true;
|
||||
for (func in {"fillRect":1, "copyImage":1, "rgbxImage":1,
|
||||
"cmapImage":1, "blitStringImage":1}) {
|
||||
that[func] = (function() {
|
||||
var myfunc = that[func]; // Save original function
|
||||
//Util.Debug("Wrapping " + func);
|
||||
return function() {
|
||||
myfunc.apply(this, arguments);
|
||||
if (!c_flush_timer) {
|
||||
c_flush_timer = setTimeout(flush, 100);
|
||||
}
|
||||
};
|
||||
}());
|
||||
}
|
||||
}
|
||||
// Initialize cached tile imageData
|
||||
tile16x16 = c_ctx.createImageData(16, 16);
|
||||
|
||||
/*
|
||||
* Determine browser support for setting the cursor via data URI
|
||||
@@ -217,7 +162,10 @@ rescale = function(factor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (factor > 1.0) {
|
||||
|
||||
if (typeof(factor) === "undefined") {
|
||||
factor = conf.scale;
|
||||
} else if (factor > 1.0) {
|
||||
factor = 1.0;
|
||||
} else if (factor < 0.1) {
|
||||
factor = 0.1;
|
||||
@@ -234,26 +182,14 @@ rescale = function(factor) {
|
||||
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) {
|
||||
var rgb, newStyle;
|
||||
var bgr, newStyle;
|
||||
if (conf.true_color) {
|
||||
rgb = color;
|
||||
bgr = color;
|
||||
} 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) {
|
||||
c_ctx.fillStyle = newStyle;
|
||||
c_prevStyle = newStyle;
|
||||
@@ -265,18 +201,205 @@ setFillColor = function(color) {
|
||||
// Public API interface functions
|
||||
//
|
||||
|
||||
that.resize = function(width, height) {
|
||||
var c = conf.target;
|
||||
// Shift and/or resize the visible viewport
|
||||
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.width = width;
|
||||
c.height = height;
|
||||
|
||||
c_width = c.offsetWidth;
|
||||
c_height = c.offsetHeight;
|
||||
fb_width = width;
|
||||
fb_height = height;
|
||||
|
||||
rescale(conf.scale);
|
||||
that.viewportChange();
|
||||
};
|
||||
|
||||
that.clear = function() {
|
||||
@@ -286,7 +409,7 @@ that.clear = function() {
|
||||
that.blitStringImage(conf.logo.data, 0, 0);
|
||||
} else {
|
||||
that.resize(640, 20);
|
||||
c_ctx.clearRect(0, 0, c_width, c_height);
|
||||
c_ctx.clearRect(0, 0, viewport.w, viewport.h);
|
||||
}
|
||||
|
||||
// No benefit over default ("source-over") in Chrome and firefox
|
||||
@@ -295,58 +418,61 @@ that.clear = function() {
|
||||
|
||||
that.fillRect = function(x, y, width, height, 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) {
|
||||
c_ctx.drawImage(conf.target, old_x, old_y, width, height,
|
||||
new_x, new_y, width, height);
|
||||
that.copyImage = function(old_x, old_y, new_x, new_y, w, h) {
|
||||
var x1 = old_x - viewport.x, y1 = old_y - viewport.y,
|
||||
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.
|
||||
*
|
||||
* - In Chrome/webkit, Javascript image data array manipulations are
|
||||
* faster than direct Canvas fillStyle, fillRect rendering. In
|
||||
* gecko, Javascript array handling is much slower.
|
||||
*/
|
||||
that.getTile = function(x, y, width, height, color) {
|
||||
var img, data = [], rgb, red, green, blue, i;
|
||||
img = {'x': x, 'y': y, 'width': width, 'height': height,
|
||||
'data': data};
|
||||
|
||||
// Start updating a tile
|
||||
that.startTile = function(x, y, width, height, color) {
|
||||
var data, bgr, red, green, blue, i;
|
||||
tile_x = x;
|
||||
tile_y = y;
|
||||
if ((width === 16) && (height === 16)) {
|
||||
tile = tile16x16;
|
||||
} else {
|
||||
tile = c_ctx.createImageData(width, height);
|
||||
}
|
||||
data = tile.data;
|
||||
if (conf.prefer_js) {
|
||||
if (conf.true_color) {
|
||||
rgb = color;
|
||||
bgr = color;
|
||||
} else {
|
||||
rgb = conf.colourMap[color[0]];
|
||||
bgr = conf.colourMap[color[0]];
|
||||
}
|
||||
red = rgb[0];
|
||||
green = rgb[1];
|
||||
blue = rgb[2];
|
||||
red = bgr[2];
|
||||
green = bgr[1];
|
||||
blue = bgr[0];
|
||||
for (i = 0; i < (width * height * 4); i+=4) {
|
||||
data[i ] = red;
|
||||
data[i + 1] = green;
|
||||
data[i + 2] = blue;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
that.fillRect(x, y, width, height, color);
|
||||
}
|
||||
return img;
|
||||
};
|
||||
|
||||
that.setSubTile = function(img, x, y, w, h, color) {
|
||||
var data, p, rgb, red, green, blue, width, j, i, xend, yend;
|
||||
// Update sub-rectangle of the current tile
|
||||
that.subTile = function(x, y, w, h, color) {
|
||||
var data, p, bgr, red, green, blue, width, j, i, xend, yend;
|
||||
if (conf.prefer_js) {
|
||||
data = img.data;
|
||||
width = img.width;
|
||||
data = tile.data;
|
||||
width = tile.width;
|
||||
if (conf.true_color) {
|
||||
rgb = color;
|
||||
bgr = color;
|
||||
} else {
|
||||
rgb = conf.colourMap[color[0]];
|
||||
bgr = conf.colourMap[color[0]];
|
||||
}
|
||||
red = rgb[0];
|
||||
green = rgb[1];
|
||||
blue = rgb[2];
|
||||
red = bgr[2];
|
||||
green = bgr[1];
|
||||
blue = bgr[0];
|
||||
xend = x + w;
|
||||
yend = y + h;
|
||||
for (j = y; j < yend; j += 1) {
|
||||
@@ -355,93 +481,70 @@ that.setSubTile = function(img, x, y, w, h, color) {
|
||||
data[p ] = red;
|
||||
data[p + 1] = green;
|
||||
data[p + 2] = blue;
|
||||
data[p + 3] = 255;
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
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) {
|
||||
return c_ctx.getImageData(0, 0, width, height);
|
||||
};
|
||||
imageDataCreate = function(width, height) {
|
||||
return c_ctx.createImageData(width, height);
|
||||
};
|
||||
|
||||
rgbxImageData = function(x, y, width, height, arr, offset) {
|
||||
var img, i, j, data;
|
||||
img = c_imageData(width, height);
|
||||
bgrxImageData = function(x, y, width, height, arr, offset) {
|
||||
var img, i, j, data, v = viewport;
|
||||
/*
|
||||
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
|
||||
(x - v.x + width < 0) || (y - v.y + height < 0)) {
|
||||
// Skipping because outside of viewport
|
||||
return;
|
||||
}
|
||||
*/
|
||||
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 ];
|
||||
data[i ] = arr[j + 2];
|
||||
data[i + 1] = arr[j + 1];
|
||||
data[i + 2] = arr[j + 2];
|
||||
data[i + 2] = arr[j ];
|
||||
data[i + 3] = 255; // Set Alpha
|
||||
}
|
||||
c_ctx.putImageData(img, x, y);
|
||||
};
|
||||
|
||||
// really slow fallback if we don't have imageData
|
||||
rgbxImageFill = function(x, y, width, height, arr, offset) {
|
||||
var i, j, sx = 0, sy = 0;
|
||||
for (i=0, j=offset; i < (width * height); i+=1, j+=4) {
|
||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j], arr[j+1], arr[j+2]]);
|
||||
sx += 1;
|
||||
if ((sx % width) === 0) {
|
||||
sx = 0;
|
||||
sy += 1;
|
||||
}
|
||||
}
|
||||
c_ctx.putImageData(img, x - v.x, y - v.y);
|
||||
};
|
||||
|
||||
cmapImageData = function(x, y, width, height, arr, offset) {
|
||||
var img, i, j, data, rgb, cmap;
|
||||
img = c_imageData(width, height);
|
||||
var img, i, j, data, bgr, cmap;
|
||||
img = c_ctx.createImageData(width, height);
|
||||
data = img.data;
|
||||
cmap = conf.colourMap;
|
||||
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
|
||||
rgb = cmap[arr[j]];
|
||||
data[i ] = rgb[0];
|
||||
data[i + 1] = rgb[1];
|
||||
data[i + 2] = rgb[2];
|
||||
bgr = cmap[arr[j]];
|
||||
data[i ] = bgr[2];
|
||||
data[i + 1] = bgr[1];
|
||||
data[i + 2] = bgr[0];
|
||||
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) {
|
||||
if (conf.true_color) {
|
||||
c_rgbxImage(x, y, width, height, arr, offset);
|
||||
bgrxImageData(x, y, width, height, arr, offset);
|
||||
} else {
|
||||
c_cmapImage(x, y, width, height, arr, offset);
|
||||
cmapImageData(x, y, width, height, arr, offset);
|
||||
}
|
||||
};
|
||||
|
||||
that.blitStringImage = function(str, x, y) {
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -460,7 +460,8 @@ Util.conf_defaults(conf, that, defaults, [
|
||||
['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'],
|
||||
|
||||
['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 +476,11 @@ function onMouseButton(e, down) {
|
||||
}
|
||||
evt = (e ? e : window.event);
|
||||
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 */
|
||||
bmask = 1 << evt.button;
|
||||
} else {
|
||||
@@ -486,7 +491,7 @@ function onMouseButton(e, down) {
|
||||
}
|
||||
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
|
||||
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
|
||||
if (conf.onMouseButton) {
|
||||
if (bmask > 0 && conf.onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
conf.onMouseButton(pos.x, pos.y, down, bmask);
|
||||
@@ -536,6 +541,8 @@ function onMouseMove(e) {
|
||||
if (conf.onMouseMove) {
|
||||
conf.onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
function onMouseDisable(e) {
|
||||
@@ -565,11 +572,17 @@ that.grab = function() {
|
||||
//Util.Debug(">> Mouse.grab");
|
||||
var c = conf.target;
|
||||
|
||||
Util.addEvent(c, 'mousedown', onMouseDown);
|
||||
Util.addEvent(c, 'mouseup', onMouseUp);
|
||||
Util.addEvent(c, 'mousemove', onMouseMove);
|
||||
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
||||
onMouseWheel);
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
Util.addEvent(c, 'touchstart', onMouseDown);
|
||||
Util.addEvent(c, 'touchend', onMouseUp);
|
||||
Util.addEvent(c, 'touchmove', onMouseMove);
|
||||
} 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 */
|
||||
Util.addEvent(document, 'click', onMouseDisable);
|
||||
@@ -582,11 +595,17 @@ that.ungrab = function() {
|
||||
//Util.Debug(">> Mouse.ungrab");
|
||||
var c = conf.target;
|
||||
|
||||
Util.removeEvent(c, 'mousedown', onMouseDown);
|
||||
Util.removeEvent(c, 'mouseup', onMouseUp);
|
||||
Util.removeEvent(c, 'mousemove', onMouseMove);
|
||||
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
||||
onMouseWheel);
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
Util.removeEvent(c, 'touchstart', onMouseDown);
|
||||
Util.removeEvent(c, 'touchend', onMouseUp);
|
||||
Util.removeEvent(c, 'touchmove', onMouseMove);
|
||||
} 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 */
|
||||
Util.removeEvent(document, 'click', onMouseDisable);
|
||||
@@ -1833,7 +1852,7 @@ unicodeTable = {
|
||||
0x28e0 : 0x10028e0,
|
||||
0x28e1 : 0x10028e1,
|
||||
0x28e2 : 0x10028e2,
|
||||
0x28e3 : 0x10028e3,
|
||||
0x28e3 : 0x10028e3,
|
||||
0x28e4 : 0x10028e4,
|
||||
0x28e5 : 0x10028e5,
|
||||
0x28e6 : 0x10028e6,
|
||||
@@ -1862,4 +1881,4 @@ unicodeTable = {
|
||||
0x28fd : 0x10028fd,
|
||||
0x28fe : 0x10028fe,
|
||||
0x28ff : 0x10028ff
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/*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) {
|
||||
@@ -20,7 +20,7 @@ var that = {}, // Public API methods
|
||||
init_vars, updateState, fail, handle_message,
|
||||
init_msg, normal_msg, framebufferUpdate, print_stats,
|
||||
|
||||
pixelFormat, clientEncodings, fbUpdateRequest,
|
||||
pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests,
|
||||
keyEvent, pointerEvent, clientCutText,
|
||||
|
||||
extract_data_uri, scan_tight_imgQ,
|
||||
@@ -35,7 +35,7 @@ var that = {}, // Public API methods
|
||||
rfb_host = '',
|
||||
rfb_port = 5900,
|
||||
rfb_password = '',
|
||||
rfb_uri = '',
|
||||
rfb_path = '',
|
||||
|
||||
rfb_state = 'disconnected',
|
||||
rfb_version = 0,
|
||||
@@ -118,7 +118,9 @@ var that = {}, // Public API methods
|
||||
|
||||
/* Mouse state */
|
||||
mouse_buttonMask = 0,
|
||||
mouse_arr = [];
|
||||
mouse_arr = [],
|
||||
viewportDragging = false,
|
||||
viewportDragPos = {};
|
||||
|
||||
// Configuration attributes
|
||||
Util.conf_defaults(conf, that, defaults, [
|
||||
@@ -129,10 +131,13 @@ Util.conf_defaults(conf, that, defaults, [
|
||||
['true_color', 'rw', 'bool', true, 'Request true color pixel data'],
|
||||
['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'],
|
||||
['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'],
|
||||
['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
|
||||
|
||||
['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'],
|
||||
|
||||
['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
|
||||
['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
|
||||
|
||||
@@ -211,10 +216,6 @@ function constructor() {
|
||||
|
||||
rmode = display.get_render_mode();
|
||||
|
||||
if (typeof noVNC_logo !== 'undefined') {
|
||||
display.set_logo(noVNC_logo);
|
||||
}
|
||||
|
||||
ws = new Websock();
|
||||
ws.on('message', handle_message);
|
||||
ws.on('open', function() {
|
||||
@@ -224,7 +225,10 @@ function constructor() {
|
||||
fail("Got unexpected WebSockets connection");
|
||||
}
|
||||
});
|
||||
ws.on('close', function() {
|
||||
ws.on('close', function(e) {
|
||||
if (e.code) {
|
||||
Util.Info("Close code: " + e.code + ", reason: " + e.reason + ", wasClean: " + e.wasClean);
|
||||
}
|
||||
if (rfb_state === 'disconnect') {
|
||||
updateState('disconnected', 'VNC disconnected');
|
||||
} else if (rfb_state === 'ProtocolVersion') {
|
||||
@@ -273,7 +277,7 @@ function connect() {
|
||||
} else {
|
||||
uri = "ws://";
|
||||
}
|
||||
uri += rfb_host + ":" + rfb_port + "/" + rfb_uri;
|
||||
uri += rfb_host + ":" + rfb_port + "/" + rfb_path;
|
||||
Util.Info("connecting to " + uri);
|
||||
ws.open(uri);
|
||||
|
||||
@@ -535,10 +539,10 @@ function genDES(password, challenge) {
|
||||
|
||||
function flushClient() {
|
||||
if (mouse_arr.length > 0) {
|
||||
//send(mouse_arr.concat(fbUpdateRequest(1)));
|
||||
//send(mouse_arr.concat(fbUpdateRequests()));
|
||||
ws.send(mouse_arr);
|
||||
setTimeout(function() {
|
||||
ws.send(fbUpdateRequest(1));
|
||||
ws.send(fbUpdateRequests());
|
||||
}, 50);
|
||||
|
||||
mouse_arr = [];
|
||||
@@ -551,12 +555,12 @@ function flushClient() {
|
||||
// overridable for testing
|
||||
checkEvents = function() {
|
||||
var now;
|
||||
if (rfb_state === 'normal') {
|
||||
if (rfb_state === 'normal' && !viewportDragging) {
|
||||
if (! flushClient()) {
|
||||
now = new Date().getTime();
|
||||
if (now > last_req_time + conf.fbu_req_rate) {
|
||||
last_req_time = now;
|
||||
ws.send(fbUpdateRequest(1));
|
||||
ws.send(fbUpdateRequests());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,8 +569,11 @@ checkEvents = function() {
|
||||
|
||||
keyPress = function(keysym, down) {
|
||||
var arr;
|
||||
|
||||
if (conf.view_only) { return; } // View only, skip keyboard events
|
||||
|
||||
arr = keyEvent(keysym, down);
|
||||
arr = arr.concat(fbUpdateRequest(1));
|
||||
arr = arr.concat(fbUpdateRequests());
|
||||
ws.send(arr);
|
||||
};
|
||||
|
||||
@@ -576,13 +583,48 @@ mouseButton = function(x, y, down, bmask) {
|
||||
} else {
|
||||
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();
|
||||
};
|
||||
|
||||
mouseMove = function(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)) );
|
||||
};
|
||||
|
||||
|
||||
@@ -613,6 +655,7 @@ init_msg = function() {
|
||||
case "003.006": rfb_version = 3.3; break; // UltraVNC
|
||||
case "003.007": rfb_version = 3.7; break;
|
||||
case "003.008": rfb_version = 3.8; break;
|
||||
case "004.000": rfb_version = 3.8; break; // Intel AMT KVM
|
||||
default:
|
||||
return fail("Invalid server version " + sversion);
|
||||
}
|
||||
@@ -776,9 +819,25 @@ init_msg = function() {
|
||||
", green_shift: " + green_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 */
|
||||
name_length = ws.rQshift32();
|
||||
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.resize(fb_width, fb_height);
|
||||
@@ -795,7 +854,7 @@ init_msg = function() {
|
||||
|
||||
response = pixelFormat();
|
||||
response = response.concat(clientEncodings());
|
||||
response = response.concat(fbUpdateRequest(0));
|
||||
response = response.concat(fbUpdateRequests());
|
||||
timing.fbu_rt_start = (new Date()).getTime();
|
||||
ws.send(response);
|
||||
|
||||
@@ -835,6 +894,8 @@ normal_msg = function() {
|
||||
ws.rQshift8(); // Padding
|
||||
first_colour = ws.rQshift16(); // First colour
|
||||
num_colours = ws.rQshift16();
|
||||
if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }
|
||||
|
||||
for (c=0; c < num_colours; c+=1) {
|
||||
red = ws.rQshift16();
|
||||
//Util.Debug("red before: " + red);
|
||||
@@ -842,9 +903,9 @@ normal_msg = function() {
|
||||
//Util.Debug("red after: " + red);
|
||||
green = parseInt(ws.rQshift16() / 256, 10);
|
||||
blue = parseInt(ws.rQshift16() / 256, 10);
|
||||
Util.Debug("*** colourMap: " + display.get_colourMap());
|
||||
display.set_colourMap([red, green, blue], first_colour + c);
|
||||
display.set_colourMap([blue, green, red], first_colour + c);
|
||||
}
|
||||
Util.Debug("colourMap: " + display.get_colourMap());
|
||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
||||
//Util.Debug("colourMap: " + display.get_colourMap());
|
||||
break;
|
||||
@@ -1066,7 +1127,7 @@ encHandlers.RRE = function display_rre() {
|
||||
|
||||
encHandlers.HEXTILE = function 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,
|
||||
rQ = ws.get_rQ(), rQi = ws.get_rQi();
|
||||
|
||||
@@ -1155,7 +1216,7 @@ encHandlers.HEXTILE = function display_hextile() {
|
||||
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
|
||||
subrects = rQ[rQi];
|
||||
rQi += 1;
|
||||
@@ -1176,10 +1237,10 @@ encHandlers.HEXTILE = function display_hextile() {
|
||||
sw = (wh >> 4) + 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);
|
||||
FBU.lastsubencoding = FBU.subencoding;
|
||||
@@ -1297,7 +1358,7 @@ scan_tight_imgQ = function() {
|
||||
imgQ = FBU.imgQ;
|
||||
while ((imgQ.length > 0) && (imgQ[0].img.complete)) {
|
||||
data = imgQ.shift();
|
||||
if (data['type'] === 'fill') {
|
||||
if (data.type === 'fill') {
|
||||
display.fillRect(data.x, data.y, data.width, data.height, data.color);
|
||||
} else {
|
||||
ctx.drawImage(data.img, data.x, data.y);
|
||||
@@ -1314,7 +1375,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
|
||||
display.resize(fb_width, fb_height);
|
||||
timing.fbu_rt_start = (new Date()).getTime();
|
||||
// Send a new non-incremental request
|
||||
ws.send(fbUpdateRequest(0));
|
||||
ws.send(fbUpdateRequests());
|
||||
|
||||
FBU.bytes = 0;
|
||||
FBU.rects -= 1;
|
||||
@@ -1378,9 +1439,9 @@ pixelFormat = function() {
|
||||
arr.push16(255); // red-max
|
||||
arr.push16(255); // green-max
|
||||
arr.push16(255); // blue-max
|
||||
arr.push8(0); // red-shift
|
||||
arr.push8(16); // red-shift
|
||||
arr.push8(8); // green-shift
|
||||
arr.push8(16); // blue-shift
|
||||
arr.push8(0); // blue-shift
|
||||
|
||||
arr.push8(0); // padding
|
||||
arr.push8(0); // padding
|
||||
@@ -1416,10 +1477,10 @@ clientEncodings = function() {
|
||||
|
||||
fbUpdateRequest = function(incremental, x, y, xw, yw) {
|
||||
//Util.Debug(">> fbUpdateRequest");
|
||||
if (!x) { x = 0; }
|
||||
if (!y) { y = 0; }
|
||||
if (!xw) { xw = fb_width; }
|
||||
if (!yw) { yw = fb_height; }
|
||||
if (typeof(x) === "undefined") { x = 0; }
|
||||
if (typeof(y) === "undefined") { y = 0; }
|
||||
if (typeof(xw) === "undefined") { xw = fb_width; }
|
||||
if (typeof(yw) === "undefined") { yw = fb_height; }
|
||||
var arr;
|
||||
arr = [3]; // msg-type
|
||||
arr.push8(incremental);
|
||||
@@ -1431,6 +1492,26 @@ fbUpdateRequest = function(incremental, x, y, xw, yw) {
|
||||
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) {
|
||||
//Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
|
||||
var arr;
|
||||
@@ -1476,13 +1557,13 @@ clientCutText = function(text) {
|
||||
// Public API interface functions
|
||||
//
|
||||
|
||||
that.connect = function(host, port, password, uri) {
|
||||
that.connect = function(host, port, password, path) {
|
||||
//Util.Debug(">> connect");
|
||||
|
||||
rfb_host = host;
|
||||
rfb_port = port;
|
||||
rfb_password = (password !== undefined) ? password : "";
|
||||
rfb_uri = (uri !== undefined) ? uri : "";
|
||||
rfb_path = (path !== undefined) ? path : "";
|
||||
|
||||
if ((!rfb_host) || (!rfb_port)) {
|
||||
return fail("Must set host and port");
|
||||
@@ -1506,7 +1587,7 @@ that.sendPassword = function(passwd) {
|
||||
};
|
||||
|
||||
that.sendCtrlAltDel = function() {
|
||||
if (rfb_state !== "normal") { return false; }
|
||||
if (rfb_state !== "normal" || conf.view_only) { return false; }
|
||||
Util.Info("Sending Ctrl-Alt-Del");
|
||||
var arr = [];
|
||||
arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
|
||||
@@ -1515,14 +1596,14 @@ that.sendCtrlAltDel = function() {
|
||||
arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
|
||||
arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
|
||||
arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
|
||||
arr = arr.concat(fbUpdateRequest(1));
|
||||
arr = arr.concat(fbUpdateRequests());
|
||||
ws.send(arr);
|
||||
};
|
||||
|
||||
// Send a key press. If 'down' is not specified then send a down key
|
||||
// followed by an up key.
|
||||
that.sendKey = function(code, down) {
|
||||
if (rfb_state !== "normal") { return false; }
|
||||
if (rfb_state !== "normal" || conf.view_only) { return false; }
|
||||
var arr = [];
|
||||
if (typeof down !== 'undefined') {
|
||||
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
|
||||
@@ -1532,7 +1613,7 @@ that.sendKey = function(code, down) {
|
||||
arr = arr.concat(keyEvent(code, 1));
|
||||
arr = arr.concat(keyEvent(code, 0));
|
||||
}
|
||||
arr = arr.concat(fbUpdateRequest(1));
|
||||
arr = arr.concat(fbUpdateRequests());
|
||||
ws.send(arr);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,150 +12,109 @@
|
||||
|
||||
var UI = {
|
||||
|
||||
rfb_state : 'loaded',
|
||||
settingsOpen : false,
|
||||
connSettingsOpen : false,
|
||||
clipboardOpen: false,
|
||||
keyboardVisible: false,
|
||||
|
||||
// Render default UI and initialize settings menu
|
||||
load: function(target) {
|
||||
load: function() {
|
||||
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
|
||||
html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
|
||||
html += ' <option value="default">default</option>';
|
||||
sheet = WebUtil.selectStylesheet();
|
||||
sheets = WebUtil.getStylesheets();
|
||||
for (i = 0; i < sheets.length; i += 1) {
|
||||
html += '<option value="' + sheets[i].title + '">' + sheets[i].title + '</option>';
|
||||
UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
|
||||
}
|
||||
html += ' </select> Style</li>';
|
||||
|
||||
// Logging selection dropdown
|
||||
html += ' <li><select id="VNC_logging" name="vncLogging">';
|
||||
llevels = ['error', 'warn', 'info', 'debug'];
|
||||
for (i = 0; i < llevels.length; i += 1) {
|
||||
html += '<option value="' + llevels[i] + '">' + llevels[i] + '</option>';
|
||||
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
|
||||
UI.initSetting('logging', 'warn');
|
||||
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'));
|
||||
|
||||
/* Populate the controls if defaults are provided in the URL */
|
||||
UI.initSetting('host', '');
|
||||
UI.initSetting('port', '');
|
||||
UI.initSetting('password', '');
|
||||
UI.initSetting('encrypt', false);
|
||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||
UI.initSetting('true_color', true);
|
||||
UI.initSetting('cursor', false);
|
||||
UI.initSetting('shared', true);
|
||||
UI.initSetting('view_only', false);
|
||||
UI.initSetting('connectTimeout', 2);
|
||||
UI.initSetting('path', 'websockify');
|
||||
|
||||
UI.rfb = RFB({'target': $D('VNC_canvas'),
|
||||
UI.rfb = RFB({'target': $D('noVNC_canvas'),
|
||||
'onUpdateState': UI.updateState,
|
||||
'onClipboard': UI.clipReceive});
|
||||
UI.updateVisualState();
|
||||
|
||||
// Unfocus clipboard when over the VNC area
|
||||
$D('VNC_screen').onmousemove = function () {
|
||||
var keyboard = UI.rfb.get_keyboard();
|
||||
if ((! keyboard) || (! keyboard.get_focused())) {
|
||||
$D('VNC_clipboard_text').blur();
|
||||
}
|
||||
};
|
||||
//$D('VNC_screen').onmousemove = function () {
|
||||
// var keyboard = UI.rfb.get_keyboard();
|
||||
// if ((! keyboard) || (! keyboard.get_focused())) {
|
||||
// $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
|
||||
getSetting: function(name) {
|
||||
var val, ctrl = $D('VNC_' + name);
|
||||
var val, ctrl = $D('noVNC_' + name);
|
||||
val = WebUtil.readCookie(name);
|
||||
if (ctrl.type === 'checkbox') {
|
||||
if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
|
||||
@@ -170,7 +129,8 @@ getSetting: function(name) {
|
||||
// Update cookie and form control setting. If value is not set, then
|
||||
// updates from control to current cookie setting.
|
||||
updateSetting: function(name, value) {
|
||||
var i, ctrl = $D('VNC_' + name);
|
||||
|
||||
var i, ctrl = $D('noVNC_' + name);
|
||||
// Save the cookie for this session
|
||||
if (typeof value !== 'undefined') {
|
||||
WebUtil.createCookie(name, value);
|
||||
@@ -178,8 +138,10 @@ updateSetting: function(name, value) {
|
||||
|
||||
// Update the settings control
|
||||
value = UI.getSetting(name);
|
||||
|
||||
if (ctrl.type === 'checkbox') {
|
||||
ctrl.checked = value;
|
||||
|
||||
} else if (typeof ctrl.options !== 'undefined') {
|
||||
for (i = 0; i < ctrl.options.length; i += 1) {
|
||||
if (ctrl.options[i].value === value) {
|
||||
@@ -188,13 +150,18 @@ updateSetting: function(name, value) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*Weird IE9 error leads to 'null' appearring
|
||||
in textboxes instead of ''.*/
|
||||
if (value === null) {
|
||||
value = "";
|
||||
}
|
||||
ctrl.value = value;
|
||||
}
|
||||
},
|
||||
|
||||
// Save control setting to cookie
|
||||
saveSetting: function(name) {
|
||||
var val, ctrl = $D('VNC_' + name);
|
||||
var val, ctrl = $D('noVNC_' + name);
|
||||
if (ctrl.type === 'checkbox') {
|
||||
val = ctrl.checked;
|
||||
} else if (typeof ctrl.options !== 'undefined') {
|
||||
@@ -217,18 +184,77 @@ initSetting: function(name, defVal) {
|
||||
val = WebUtil.readCookie(name, defVal);
|
||||
}
|
||||
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;
|
||||
},
|
||||
|
||||
|
||||
// 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:
|
||||
// On open, settings are refreshed from saved cookies.
|
||||
// On close, settings are applied
|
||||
clickSettingsMenu: function() {
|
||||
toggleSettingsPanel: function() {
|
||||
// Close the description panel
|
||||
$D('noVNC_description').style.display = "none";
|
||||
if (UI.settingsOpen) {
|
||||
UI.settingsApply();
|
||||
|
||||
UI.closeSettingsMenu();
|
||||
} else {
|
||||
UI.updateSetting('encrypt');
|
||||
@@ -237,10 +263,13 @@ clickSettingsMenu: function() {
|
||||
UI.updateSetting('cursor');
|
||||
} else {
|
||||
UI.updateSetting('cursor', false);
|
||||
$D('VNC_cursor').disabled = true;
|
||||
$D('noVNC_cursor').disabled = true;
|
||||
}
|
||||
UI.updateSetting('clip');
|
||||
UI.updateSetting('shared');
|
||||
UI.updateSetting('view_only');
|
||||
UI.updateSetting('connectTimeout');
|
||||
UI.updateSetting('path');
|
||||
UI.updateSetting('stylesheet');
|
||||
UI.updateSetting('logging');
|
||||
|
||||
@@ -250,32 +279,27 @@ clickSettingsMenu: function() {
|
||||
|
||||
// Open menu
|
||||
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;
|
||||
},
|
||||
|
||||
// Close menu (without applying settings)
|
||||
closeSettingsMenu: function() {
|
||||
$D('VNC_settings_menu').style.display = "none";
|
||||
$D('noVNC_settings').style.display = "none";
|
||||
$D('settingsButton').className = "noVNC_status_button";
|
||||
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
|
||||
settingsApply: function() {
|
||||
//Util.Debug(">> settingsApply");
|
||||
@@ -284,22 +308,31 @@ settingsApply: function() {
|
||||
if (UI.rfb.get_display().get_cursor_uri()) {
|
||||
UI.saveSetting('cursor');
|
||||
}
|
||||
UI.saveSetting('clip');
|
||||
UI.saveSetting('shared');
|
||||
UI.saveSetting('view_only');
|
||||
UI.saveSetting('connectTimeout');
|
||||
UI.saveSetting('path');
|
||||
UI.saveSetting('stylesheet');
|
||||
UI.saveSetting('logging');
|
||||
|
||||
// Settings with immediate (non-connected related) effect
|
||||
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
|
||||
WebUtil.init_logging(UI.getSetting('logging'));
|
||||
|
||||
UI.setViewClip();
|
||||
UI.setViewDrag(UI.rfb.get_viewportDrag());
|
||||
//Util.Debug("<< settingsApply");
|
||||
},
|
||||
|
||||
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
@@ -307,52 +340,63 @@ sendCtrlAltDel: function() {
|
||||
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) {
|
||||
var s, sb, c, cad, klass;
|
||||
s = $D('VNC_status');
|
||||
sb = $D('VNC_status_bar');
|
||||
c = $D('VNC_connect_button');
|
||||
cad = $D('sendCtrlAltDelButton');
|
||||
var s, sb, c, d, cad, vd, klass;
|
||||
UI.rfb_state = state;
|
||||
s = $D('noVNC_status');
|
||||
sb = $D('noVNC_status_bar');
|
||||
switch (state) {
|
||||
case 'failed':
|
||||
case 'fatal':
|
||||
c.disabled = true;
|
||||
cad.disabled = true;
|
||||
UI.settingsDisabled(true, rfb);
|
||||
klass = "VNC_status_error";
|
||||
klass = "noVNC_status_error";
|
||||
break;
|
||||
case 'normal':
|
||||
c.value = "Disconnect";
|
||||
c.onclick = UI.disconnect;
|
||||
c.disabled = false;
|
||||
cad.disabled = false;
|
||||
UI.settingsDisabled(true, rfb);
|
||||
klass = "VNC_status_normal";
|
||||
klass = "noVNC_status_normal";
|
||||
break;
|
||||
case 'disconnected':
|
||||
$D('noVNC_logo').style.display = "block";
|
||||
// Fall through
|
||||
case 'loaded':
|
||||
c.value = "Connect";
|
||||
c.onclick = UI.connect;
|
||||
|
||||
c.disabled = false;
|
||||
cad.disabled = true;
|
||||
UI.settingsDisabled(false, rfb);
|
||||
klass = "VNC_status_normal";
|
||||
klass = "noVNC_status_normal";
|
||||
break;
|
||||
case 'password':
|
||||
c.value = "Send Password";
|
||||
c.onclick = UI.setPassword;
|
||||
UI.toggleConnectPanel();
|
||||
|
||||
c.disabled = false;
|
||||
cad.disabled = true;
|
||||
UI.settingsDisabled(true, rfb);
|
||||
klass = "VNC_status_warn";
|
||||
$D('noVNC_connect_button').value = "Send Password";
|
||||
$D('noVNC_connect_button').onclick = UI.setPassword;
|
||||
$D('noVNC_password').focus();
|
||||
|
||||
klass = "noVNC_status_warn";
|
||||
break;
|
||||
default:
|
||||
c.disabled = true;
|
||||
cad.disabled = true;
|
||||
UI.settingsDisabled(true, rfb);
|
||||
klass = "VNC_status_warn";
|
||||
klass = "noVNC_status_warn";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -362,23 +406,79 @@ updateState: function(rfb, state, oldstate, 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;
|
||||
|
||||
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) {
|
||||
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
|
||||
$D('VNC_clipboard_text').value = text;
|
||||
$D('noVNC_clipboard_text').value = text;
|
||||
Util.Debug("<< UI.clipReceive");
|
||||
},
|
||||
|
||||
|
||||
connect: function() {
|
||||
var host, port, password;
|
||||
var host, port, password, path;
|
||||
|
||||
UI.closeSettingsMenu();
|
||||
UI.toggleConnectPanel();
|
||||
|
||||
host = $D('VNC_host').value;
|
||||
port = $D('VNC_port').value;
|
||||
password = $D('VNC_password').value;
|
||||
host = $D('noVNC_host').value;
|
||||
port = $D('noVNC_port').value;
|
||||
password = $D('noVNC_password').value;
|
||||
path = $D('noVNC_path').value;
|
||||
if ((!host) || (!port)) {
|
||||
throw("Must set host and port");
|
||||
}
|
||||
@@ -387,15 +487,22 @@ connect: function() {
|
||||
UI.rfb.set_true_color(UI.getSetting('true_color'));
|
||||
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
|
||||
UI.rfb.set_shared(UI.getSetting('shared'));
|
||||
UI.rfb.set_view_only(UI.getSetting('view_only'));
|
||||
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
|
||||
|
||||
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() {
|
||||
UI.closeSettingsMenu();
|
||||
|
||||
UI.rfb.disconnect();
|
||||
|
||||
$D('noVNC_logo').style.display = "block";
|
||||
UI.connSettingsOpen = false;
|
||||
UI.toggleConnectPanel();
|
||||
},
|
||||
|
||||
displayBlur: function() {
|
||||
@@ -409,15 +516,138 @@ displayFocus: function() {
|
||||
},
|
||||
|
||||
clipClear: function() {
|
||||
$D('VNC_clipboard_text').value = "";
|
||||
$D('noVNC_clipboard_text').value = "";
|
||||
UI.rfb.clipboardPasteFrom("");
|
||||
},
|
||||
|
||||
clipSend: function() {
|
||||
var text = $D('VNC_clipboard_text').value;
|
||||
var text = $D('noVNC_clipboard_text').value;
|
||||
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
|
||||
UI.rfb.clipboardPasteFrom(text);
|
||||
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';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,30 @@ Array.prototype.push32 = function (num) {
|
||||
(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;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in Util
|
||||
@@ -159,7 +183,7 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
|
||||
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
|
||||
arr[i][2], arr[i][3], arr[i][4]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
@@ -184,6 +208,7 @@ Util.getEventPosition = function (e, obj, scale) {
|
||||
var evt, docX, docY, pos;
|
||||
//if (!e) evt = window.event;
|
||||
evt = (e ? e : window.event);
|
||||
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
|
||||
if (evt.pageX || evt.pageY) {
|
||||
docX = evt.pageX;
|
||||
docY = evt.pageY;
|
||||
@@ -239,8 +264,11 @@ Util.stopEvent = function(e) {
|
||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||
|
||||
Util.Engine = {
|
||||
'presto': (function() {
|
||||
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': (function() { return (!window.opera) ? false : true; }()),
|
||||
|
||||
'trident': (function() {
|
||||
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
||||
'webkit': (function() {
|
||||
|
||||
@@ -30,7 +30,6 @@ function get_INCLUDE_URI() {
|
||||
|
||||
extra += start + "util.js" + end;
|
||||
extra += start + "webutil.js" + end;
|
||||
extra += start + "logo.js" + end;
|
||||
extra += start + "base64.js" + end;
|
||||
extra += start + "websock.js" + end;
|
||||
extra += start + "des.js" + end;
|
||||
|
||||
@@ -14,13 +14,23 @@
|
||||
* read binary data off of the receive queue.
|
||||
*/
|
||||
|
||||
/*jslint browser: true, bitwise: false, plusplus: false */
|
||||
/*global Util, Base64 */
|
||||
|
||||
|
||||
// Load Flash WebSocket emulator if needed
|
||||
|
||||
if (window.WebSocket) {
|
||||
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||
Websock_native = true;
|
||||
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||
Websock_native = true;
|
||||
window.WebSocket = window.MozWebSocket;
|
||||
} else {
|
||||
/* no builtin WebSocket so load web_socket.js */
|
||||
|
||||
// To enable debug:
|
||||
// window.WEB_SOCKET_DEBUG=1;
|
||||
|
||||
Websock_native = false;
|
||||
(function () {
|
||||
function get_INCLUDE_URI() {
|
||||
@@ -31,11 +41,11 @@ if (window.WebSocket) {
|
||||
var start = "<script src='" + get_INCLUDE_URI(),
|
||||
end = "'><\/script>", extra = "";
|
||||
|
||||
WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
||||
window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
||||
"web-socket-js/WebSocketMain.swf";
|
||||
if (Util.Engine.trident) {
|
||||
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/web_socket.js" + end;
|
||||
@@ -80,7 +90,7 @@ function get_rQi() {
|
||||
}
|
||||
function set_rQi(val) {
|
||||
rQi = val;
|
||||
};
|
||||
}
|
||||
|
||||
function rQlen() {
|
||||
return rQ.length - rQi;
|
||||
@@ -112,6 +122,7 @@ function rQshift32() {
|
||||
(rQ[rQi++] );
|
||||
}
|
||||
function rQshiftStr(len) {
|
||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||
var arr = rQ.slice(rQi, rQi + len);
|
||||
rQi += len;
|
||||
return arr.map(function (num) {
|
||||
@@ -119,6 +130,7 @@ function rQshiftStr(len) {
|
||||
|
||||
}
|
||||
function rQshiftBytes(len) {
|
||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||
rQi += len;
|
||||
return rQ.slice(rQi-len, rQi);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
"use strict";
|
||||
/*jslint bitwise: false, white: false */
|
||||
/*global window, document */
|
||||
/*global Util, window, document */
|
||||
|
||||
// Globals defined here
|
||||
var WebUtil = {}, $D;
|
||||
@@ -17,7 +17,7 @@ var WebUtil = {}, $D;
|
||||
* Simple DOM selector by ID
|
||||
*/
|
||||
if (!window.$D) {
|
||||
$D = function (id) {
|
||||
window.$D = function (id) {
|
||||
if (document.getElementById) {
|
||||
return document.getElementById(id);
|
||||
} else if (document.all) {
|
||||
@@ -42,8 +42,8 @@ WebUtil.init_logging = function() {
|
||||
/logging=([A-Za-z0-9\._\-]*)/) ||
|
||||
['', Util._log_level])[1];
|
||||
|
||||
Util.init_logging()
|
||||
}
|
||||
Util.init_logging();
|
||||
};
|
||||
WebUtil.init_logging();
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
<script src="../include/base64.js"></script>
|
||||
<script src="../include/canvas.js"></script>
|
||||
<script src="../include/display.js"></script>
|
||||
<script src="face.png.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<script>
|
||||
var msg_cnt = 0;
|
||||
var start_width = 300, start_height = 100;
|
||||
var display, start_width = 300, start_height = 100;
|
||||
var iterations;
|
||||
|
||||
function message(str) {
|
||||
@@ -48,12 +48,12 @@
|
||||
}
|
||||
|
||||
function test_functions () {
|
||||
var img, x, y, w, h, ctx = canvas.getContext();
|
||||
w = canvas.get_width();
|
||||
h = canvas.get_height();
|
||||
canvas.fillRect(0, 0, w, h, [240,240,240]);
|
||||
var img, x, y, w, h, ctx = display.get_context();
|
||||
w = display.get_width();
|
||||
h = display.get_height();
|
||||
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();
|
||||
himg.onload = function () {
|
||||
@@ -70,14 +70,14 @@
|
||||
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]);
|
||||
canvas.putTile(img);
|
||||
img = display.getTile(5,5,16,16,[0,128,128]);
|
||||
display.putTile(img);
|
||||
|
||||
img = canvas.getTile(90,15,16,16,[0,0,0]);
|
||||
canvas.setSubTile(img, 0,0,16,16,[128,128,0]);
|
||||
canvas.putTile(img);
|
||||
img = display.getTile(90,15,16,16,[0,0,0]);
|
||||
display.setSubTile(img, 0,0,16,16,[128,128,0]);
|
||||
display.putTile(img);
|
||||
}
|
||||
|
||||
function begin () {
|
||||
@@ -90,7 +90,7 @@
|
||||
function start_delayed () {
|
||||
var ret;
|
||||
|
||||
ret = canvas.set_prefer_js(true);
|
||||
ret = display.set_prefer_js(true);
|
||||
if (ret) {
|
||||
message("Running test: prefer Javascript ops");
|
||||
var time1 = run_test();
|
||||
@@ -100,14 +100,14 @@
|
||||
message("Could not run: prefer Javascript ops");
|
||||
}
|
||||
|
||||
canvas.set_prefer_js(false);
|
||||
display.set_prefer_js(false);
|
||||
message("Running test: prefer Canvas ops");
|
||||
var time2 = run_test();
|
||||
message("prefer Canvas ops: " + time2 + "ms total, " +
|
||||
(time2 / iterations) + "ms per frame");
|
||||
|
||||
if (Util.get_logging() !== 'debug') {
|
||||
canvas.resize(start_width, start_height, true);
|
||||
display.resize(start_width, start_height, true);
|
||||
test_functions();
|
||||
}
|
||||
$D('startButton').disabled = false;
|
||||
@@ -118,7 +118,7 @@
|
||||
var width, height;
|
||||
width = $D('width').value;
|
||||
height = $D('height').value;
|
||||
canvas.resize(width, height);
|
||||
display.resize(width, height);
|
||||
var color, start_time = (new Date()).getTime(), w, h;
|
||||
for (var i=0; i < iterations; i++) {
|
||||
color = [128, 128, (255 / iterations) * i, 0];
|
||||
@@ -126,9 +126,9 @@
|
||||
for (var y=0; y < height; y = y + 16) {
|
||||
w = Math.min(16, width - x);
|
||||
h = Math.min(16, height - y);
|
||||
var tile = canvas.getTile(x, y, w, h, color);
|
||||
canvas.setSubTile(tile, 0, 0, w, h, color);
|
||||
canvas.putTile(tile);
|
||||
var tile = display.getTile(x, y, w, h, color);
|
||||
display.setSubTile(tile, 0, 0, w, h, color);
|
||||
display.putTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,8 +139,8 @@
|
||||
window.onload = function() {
|
||||
message("in onload");
|
||||
$D('iterations').value = 10;
|
||||
canvas = new Canvas({'target' : $D('canvas')});
|
||||
canvas.resize(start_width, start_height, true);
|
||||
display = new Display({'target' : $D('canvas')});
|
||||
display.resize(start_width, start_height, true);
|
||||
message("Canvas initialized");
|
||||
test_functions();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
<body>
|
||||
<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"
|
||||
style="border-style: dotted; border-width: 1px;">
|
||||
Canvas not supported.
|
||||
@@ -23,11 +27,11 @@
|
||||
<script src="../include/webutil.js"></script>
|
||||
<script src="../include/base64.js"></script>
|
||||
<script src="../include/input.js"></script>
|
||||
<script src="../include/canvas.js"></script>
|
||||
<script src="../include/display.js"></script>
|
||||
<script>
|
||||
var msg_cnt = 0;
|
||||
var width = 400, height = 200;
|
||||
var iterations;
|
||||
var msg_cnt = 0, iterations,
|
||||
width = 400, height = 200,
|
||||
canvas, keyboard, mouse;
|
||||
|
||||
var newline = "\n";
|
||||
if (Util.Engine.trident) {
|
||||
@@ -61,17 +65,54 @@
|
||||
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() {
|
||||
var canvas = new Canvas({'target' : $D('canvas')});
|
||||
canvas = new Display({'target' : $D('canvas')});
|
||||
keyboard = new Keyboard({'target': document,
|
||||
'keyPress': keyPress});
|
||||
'onKeyPress': keyPress});
|
||||
mouse = new Mouse({'target': $D('canvas'),
|
||||
'mouseButton': mouseButton,
|
||||
'mouseMove': mouseMove});
|
||||
'onMouseButton': mouseButton,
|
||||
'onMouseMove': mouseMove});
|
||||
|
||||
canvas.resize(width, height, true);
|
||||
keyboard.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>
|
||||
</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>
|
||||
<head>
|
||||
<title>VNC Performance Benchmark</title>
|
||||
<link rel="stylesheet" href="../include/plain.css">
|
||||
<link rel="stylesheet" href="../include/base.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</script>
|
||||
<script src="../include/vnc.js"></script>
|
||||
<script src="../include/playback.js"></script>
|
||||
<script src="data/multi.js"></script>
|
||||
<script src="../data/multi.js"></script>
|
||||
|
||||
<script>
|
||||
var start_time, VNC_frame_data, pass, passes, encIdx,
|
||||
@@ -126,7 +126,9 @@
|
||||
pass += 1;
|
||||
if (pass > passes) {
|
||||
// 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').value = "Start";
|
||||
finish_passes();
|
||||
|
||||
@@ -8,7 +8,7 @@ Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||
Supports following protocol versions:
|
||||
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
||||
- 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:
|
||||
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 base64 import b64encode, b64decode
|
||||
|
||||
# Imports that vary by python version
|
||||
|
||||
# python 3.0 differences
|
||||
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')
|
||||
s2b = lambda s: s.encode('latin_1')
|
||||
s2a = lambda s: s
|
||||
else:
|
||||
# python 2.X
|
||||
from cStringIO import StringIO
|
||||
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
from urlparse import urlsplit
|
||||
# No-ops
|
||||
b2s = lambda buf: buf
|
||||
s2b = lambda s: s
|
||||
b2s = lambda buf: buf # No-op
|
||||
s2b = lambda s: s # No-op
|
||||
s2a = lambda s: [ord(c) for c in s]
|
||||
try: from io import StringIO
|
||||
except: from cStringIO import StringIO
|
||||
try: from http.server import SimpleHTTPRequestHandler
|
||||
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
try: from urllib.parse import urlsplit
|
||||
except: from urlparse import urlsplit
|
||||
|
||||
if sys.hexversion >= 0x2060000:
|
||||
# python >= 2.6
|
||||
from multiprocessing import Process
|
||||
from hashlib import md5, sha1
|
||||
else:
|
||||
# python < 2.6
|
||||
Process = None
|
||||
from md5 import md5
|
||||
from sha import sha as sha1
|
||||
# python 2.6 differences
|
||||
try: from hashlib import md5, sha1
|
||||
except: from md5 import md5; from sha import sha as sha1
|
||||
|
||||
# python 2.5 differences
|
||||
try:
|
||||
from struct import pack, unpack_from
|
||||
except:
|
||||
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
|
||||
for mod, sup in [('numpy', 'HyBi protocol'),
|
||||
('ctypes', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
|
||||
for mod, sup in [('numpy', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
|
||||
('multiprocessing', 'Multi-Processing'),
|
||||
('resource', 'daemonizing')]:
|
||||
try:
|
||||
globals()[mod] = __import__(mod)
|
||||
except ImportError:
|
||||
globals()[mod] = None
|
||||
print("WARNING: no '%s' module, %s support disabled" % (
|
||||
print("WARNING: no '%s' module, %s is slower or disabled" % (
|
||||
mod, sup))
|
||||
if multiprocessing and sys.platform == 'win32':
|
||||
# make sockets pickle-able/inheritable
|
||||
import multiprocessing.reduction
|
||||
|
||||
|
||||
class WebSocketServer(object):
|
||||
@@ -89,7 +97,8 @@ Sec-WebSocket-Accept: %s\r
|
||||
|
||||
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
||||
verbose=False, cert='', key='', ssl_only=None,
|
||||
daemon=False, record='', web=''):
|
||||
daemon=False, record='', web='',
|
||||
run_once=False, timeout=0):
|
||||
|
||||
# settings
|
||||
self.verbose = verbose
|
||||
@@ -97,6 +106,11 @@ Sec-WebSocket-Accept: %s\r
|
||||
self.listen_port = listen_port
|
||||
self.ssl_only = ssl_only
|
||||
self.daemon = daemon
|
||||
self.run_once = run_once
|
||||
self.timeout = timeout
|
||||
|
||||
self.launch_time = time.time()
|
||||
self.ws_connection = False
|
||||
self.handler_id = 1
|
||||
|
||||
# Make paths settings absolute
|
||||
@@ -208,6 +222,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.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
|
||||
def encode_hybi(buf, opcode, base64=False):
|
||||
""" Encode a HyBi style WebSocket frame.
|
||||
@@ -225,11 +271,11 @@ Sec-WebSocket-Accept: %s\r
|
||||
b1 = 0x80 | (opcode & 0x0f) # FIN + opcode
|
||||
payload_len = len(buf)
|
||||
if payload_len <= 125:
|
||||
header = struct.pack('>BB', b1, payload_len)
|
||||
elif payload_len > 125 and payload_len <= 65536:
|
||||
header = struct.pack('>BBH', b1, 126, payload_len)
|
||||
header = pack('>BB', b1, payload_len)
|
||||
elif payload_len > 125 and payload_len < 65536:
|
||||
header = pack('>BBH', b1, 126, payload_len)
|
||||
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))
|
||||
|
||||
@@ -266,7 +312,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
if blen < f['hlen']:
|
||||
return f # Incomplete frame header
|
||||
|
||||
b1, b2 = struct.unpack_from(">BB", buf)
|
||||
b1, b2 = unpack_from(">BB", buf)
|
||||
f['opcode'] = b1 & 0x0f
|
||||
f['fin'] = (b1 & 0x80) >> 7
|
||||
has_mask = (b2 & 0x80) >> 7
|
||||
@@ -277,12 +323,12 @@ Sec-WebSocket-Accept: %s\r
|
||||
f['hlen'] = 4
|
||||
if blen < f['hlen']:
|
||||
return f # Incomplete frame header
|
||||
(f['length'],) = struct.unpack_from('>xxH', buf)
|
||||
(f['length'],) = unpack_from('>xxH', buf)
|
||||
elif f['length'] == 127:
|
||||
f['hlen'] = 10
|
||||
if blen < f['hlen']:
|
||||
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']
|
||||
|
||||
@@ -296,24 +342,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
if has_mask:
|
||||
# unmask payload
|
||||
f['mask'] = buf[f['hlen']:f['hlen']+4]
|
||||
b = c = ''
|
||||
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
|
||||
f['payload'] = WebSocketServer.unmask(buf, f)
|
||||
else:
|
||||
print("Unmasked frame: %s" % repr(buf))
|
||||
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
|
||||
@@ -328,7 +357,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
|
||||
if f['opcode'] == 0x08:
|
||||
if f['length'] >= 2:
|
||||
f['close_code'] = struct.unpack_from(">H", f['payload'])
|
||||
f['close_code'] = unpack_from(">H", f['payload'])
|
||||
if f['length'] > 3:
|
||||
f['close_reason'] = f['payload'][2:]
|
||||
|
||||
@@ -358,7 +387,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
|
||||
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())
|
||||
|
||||
#
|
||||
@@ -469,11 +498,11 @@ Sec-WebSocket-Accept: %s\r
|
||||
break
|
||||
|
||||
else:
|
||||
if buf[0:2] == '\xff\x00':
|
||||
if buf[0:2] == s2b('\xff\x00'):
|
||||
closed = "Client sent orderly close frame"
|
||||
break
|
||||
|
||||
elif buf[0:2] == '\x00\xff':
|
||||
elif buf[0:2] == s2b('\x00\xff'):
|
||||
buf = buf[2:]
|
||||
continue # No-op
|
||||
|
||||
@@ -509,7 +538,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
if self.version.startswith("hybi"):
|
||||
msg = s2b('')
|
||||
if code != None:
|
||||
msg = struct.pack(">H%ds" % (len(reason)), code)
|
||||
msg = pack(">H%ds" % (len(reason)), code)
|
||||
|
||||
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
|
||||
self.client.send(buf)
|
||||
@@ -557,7 +586,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
sock.send(s2b(self.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
|
||||
if not ssl:
|
||||
raise self.EClose("SSL connection but no 'ssl' module")
|
||||
@@ -612,11 +641,11 @@ Sec-WebSocket-Accept: %s\r
|
||||
if ver:
|
||||
# HyBi/IETF version of the protocol
|
||||
|
||||
if sys.hexversion < 0x2060000 or not numpy:
|
||||
raise self.EClose("Python >= 2.6 and numpy module is required for HyBi-07 or greater")
|
||||
|
||||
if ver in ['7', '8', '9']:
|
||||
self.version = "hybi-0" + ver
|
||||
# HyBi-07 report version 7
|
||||
# HyBi-08 - HyBi-12 report version 8
|
||||
# HyBi-13 reports version 13
|
||||
if ver in ['7', '8', '13']:
|
||||
self.version = "hybi-%02d" % int(ver)
|
||||
else:
|
||||
raise self.EClose('Unsupported protocol version %s' % ver)
|
||||
|
||||
@@ -633,7 +662,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
# Generate the hash value for the accept header
|
||||
accept = b64encode(sha1(s2b(key + self.GUID)).digest())
|
||||
|
||||
response = self.server_handshake_hybi % accept
|
||||
response = self.server_handshake_hybi % b2s(accept)
|
||||
if self.base64:
|
||||
response += "Sec-WebSocket-Protocol: base64\r\n"
|
||||
else:
|
||||
@@ -667,6 +696,9 @@ Sec-WebSocket-Accept: %s\r
|
||||
self.msg("%s: %s WebSocket connection" % (address[0], stype))
|
||||
self.msg("%s: Version %s, base64: '%s'" % (address[0],
|
||||
self.version, self.base64))
|
||||
if self.path != '/':
|
||||
self.msg("%s: Path: '%s'" % (address[0], self.path))
|
||||
|
||||
|
||||
# Send server WebSockets handshake response
|
||||
#self.msg("sending response [%s]" % response)
|
||||
@@ -725,6 +757,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
self.rec = open(fname, 'w+')
|
||||
self.rec.write("var VNC_frame_data = [\n")
|
||||
|
||||
self.ws_connection = True
|
||||
self.new_client()
|
||||
except self.EClose:
|
||||
_, exc, _ = sys.exc_info()
|
||||
@@ -764,7 +797,7 @@ Sec-WebSocket-Accept: %s\r
|
||||
|
||||
# Allow override of SIGINT
|
||||
signal.signal(signal.SIGINT, self.do_SIGINT)
|
||||
if not Process:
|
||||
if not multiprocessing:
|
||||
# os.fork() (python 2.4) child reaper
|
||||
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
||||
|
||||
@@ -775,6 +808,12 @@ Sec-WebSocket-Accept: %s\r
|
||||
startsock = None
|
||||
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:
|
||||
self.poll()
|
||||
|
||||
@@ -797,9 +836,17 @@ Sec-WebSocket-Accept: %s\r
|
||||
else:
|
||||
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])
|
||||
p = Process(target=self.top_new_client,
|
||||
p = multiprocessing.Process(
|
||||
target=self.top_new_client,
|
||||
args=(startsock, address))
|
||||
p.start()
|
||||
# child will not return
|
||||
|
||||
@@ -226,6 +226,10 @@ if __name__ == '__main__':
|
||||
parser.add_option("--daemon", "-D",
|
||||
dest="daemon", action="store_true",
|
||||
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",
|
||||
help="SSL certificate file")
|
||||
parser.add_option("--key", default=None,
|
||||
@@ -255,7 +259,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Parse host:port and convert ports to numbers
|
||||
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:
|
||||
opts.listen_host, opts.listen_port = '', args[0]
|
||||
|
||||
@@ -267,7 +271,7 @@ if __name__ == '__main__':
|
||||
opts.target_port = None
|
||||
else:
|
||||
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:
|
||||
parser.error("Error parsing target")
|
||||
try: opts.target_port = int(opts.target_port)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
websockify
|
||||
@@ -1,31 +1,198 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.1//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile11.dtd">
|
||||
<html>
|
||||
<!--
|
||||
<head>
|
||||
|
||||
<!--
|
||||
noVNC example: simple example using default UI
|
||||
Copyright (C) 2011 Joel Martin
|
||||
Licensed under LGPL-3 (see LICENSE.txt)
|
||||
-->
|
||||
<head>
|
||||
<title>noVNC</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<link rel="stylesheet" href="include/plain.css">
|
||||
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black">
|
||||
<!--
|
||||
<script type='text/javascript'
|
||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
<script src="include/vnc.js"></script>
|
||||
<script src="include/ui.js"></script>
|
||||
</head>
|
||||
<title>noVNC</title>
|
||||
|
||||
<body>
|
||||
<div id='vnc'>Loading</div>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<script>
|
||||
window.onload = function () {
|
||||
UI.load('vnc');
|
||||
};
|
||||
</script>
|
||||
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
|
||||
Remove this if you use the .htaccess -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
</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>
|
||||
<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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<head>
|
||||
<title>noVNC</title>
|
||||
<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'
|
||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
@@ -20,17 +20,17 @@
|
||||
</head>
|
||||
|
||||
<body style="margin: 0px;">
|
||||
<div id="VNC_screen">
|
||||
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
|
||||
<div id="noVNC_screen">
|
||||
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
|
||||
<table border=0 width="100%"><tr>
|
||||
<td><div id="VNC_status">Loading</div></td>
|
||||
<td width="1%"><div id="VNC_buttons">
|
||||
<td><div id="noVNC_status">Loading</div></td>
|
||||
<td width="1%"><div id="noVNC_buttons">
|
||||
<input type=button value="Send CtrlAltDel"
|
||||
id="sendCtrlAltDelButton">
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<canvas id="VNC_canvas" width="640px" height="20px">
|
||||
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||
Canvas not supported.
|
||||
</canvas>
|
||||
</div>
|
||||
@@ -47,10 +47,10 @@
|
||||
msg = '<form onsubmit="return setPassword();"';
|
||||
msg += ' style="margin-bottom: 0px">';
|
||||
msg += 'Password Required: ';
|
||||
msg += '<input type=password size=10 id="password_input" class="VNC_status">';
|
||||
msg += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
||||
msg += '<\/form>';
|
||||
$D('VNC_status_bar').setAttribute("class", "VNC_status_warn");
|
||||
$D('VNC_status').innerHTML = msg;
|
||||
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
||||
$D('noVNC_status').innerHTML = msg;
|
||||
}
|
||||
function setPassword() {
|
||||
rfb.sendPassword($D('password_input').value);
|
||||
@@ -62,8 +62,8 @@
|
||||
}
|
||||
function updateState(rfb, state, oldstate, msg) {
|
||||
var s, sb, cad, level;
|
||||
s = $D('VNC_status');
|
||||
sb = $D('VNC_status_bar');
|
||||
s = $D('noVNC_status');
|
||||
sb = $D('noVNC_status_bar');
|
||||
cad = $D('sendCtrlAltDelButton');
|
||||
switch (state) {
|
||||
case 'failed': level = "error"; break;
|
||||
@@ -78,34 +78,38 @@
|
||||
else { cad.disabled = true; }
|
||||
|
||||
if (typeof(msg) !== 'undefined') {
|
||||
sb.setAttribute("class", "VNC_status_" + level);
|
||||
sb.setAttribute("class", "noVNC_status_" + level);
|
||||
s.innerHTML = msg;
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
var host, port, password;
|
||||
var host, port, password, path;
|
||||
|
||||
$D('sendCtrlAltDelButton').style.display = "inline";
|
||||
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
||||
|
||||
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
|
||||
host = WebUtil.getQueryVar('host', null);
|
||||
port = WebUtil.getQueryVar('port', null);
|
||||
password = WebUtil.getQueryVar('password', '');
|
||||
path = WebUtil.getQueryVar('path', 'websockify');
|
||||
if ((!host) || (!port)) {
|
||||
updateState('failed',
|
||||
"Must specify host and port in URL");
|
||||
return;
|
||||
}
|
||||
|
||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
||||
'encrypt': WebUtil.getQueryVar('encrypt', false),
|
||||
rfb = new RFB({'target': $D('noVNC_canvas'),
|
||||
'encrypt': WebUtil.getQueryVar('encrypt',
|
||||
(window.location.protocol === "https:")),
|
||||
'true_color': WebUtil.getQueryVar('true_color', true),
|
||||
'local_cursor': WebUtil.getQueryVar('cursor', true),
|
||||
'shared': WebUtil.getQueryVar('shared', true),
|
||||
'view_only': WebUtil.getQueryVar('view_only', false),
|
||||
'updateState': updateState,
|
||||
'onPasswordRequired': passwordRequired});
|
||||
rfb.connect(host, port, password);
|
||||
rfb.connect(host, port, password, path);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||