Browse Source

VNC Server with websockify

Reinhard Russinger 5 years ago
parent
commit
ebe7529963
100 changed files with 21584 additions and 916 deletions
  1. 1 1
      board/GfA/Display001/BUILD
  2. BIN
      board/GfA/Display001/rootfs/usr/sbin/fbvncserver
  3. 3 3
      board/GfA/Display001/rootfs/var/GfA/WebVnc/index.vnc
  4. 0 382
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile
  5. 0 2
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile.am
  6. 0 382
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile.in
  7. 3 3
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/README
  8. 1 1
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/proxy.vnc
  9. 5 5
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/ss_vncviewer
  10. 111 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch
  11. 2600 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch
  12. 5494 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch
  13. 0 35
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/LICENSE.txt
  14. 0 102
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/README.md
  15. 56 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/error-handler.js
  16. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/alt.svg
  17. 106 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/clipboard.svg
  18. 96 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/connect.svg
  19. 96 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/ctrl.svg
  20. 100 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/ctrlaltdel.svg
  21. 94 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/disconnect.svg
  22. 71 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/drag.svg
  23. 81 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/error.svg
  24. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/esc.svg
  25. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/expander.svg
  26. 93 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/fullscreen.svg
  27. 82 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/handle.svg
  28. 172 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/handle_bg.svg
  29. 42 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/Makefile
  30. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-120x120.png
  31. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-144x144.png
  32. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-152x152.png
  33. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-16x16.png
  34. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-192x192.png
  35. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-24x24.png
  36. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-32x32.png
  37. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-48x48.png
  38. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-60x60.png
  39. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-64x64.png
  40. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-72x72.png
  41. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-76x76.png
  42. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-96x96.png
  43. 163 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-icon-sm.svg
  44. 163 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-icon.svg
  45. 81 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/info.svg
  46. 75 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/keyboard.svg
  47. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_left.svg
  48. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_middle.svg
  49. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_none.svg
  50. 92 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_right.svg
  51. 87 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/power.svg
  52. 76 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/settings.svg
  53. 86 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/tab.svg
  54. 90 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/toggleextrakeys.svg
  55. 81 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/warning.svg
  56. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/de.json
  57. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/el.json
  58. 68 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/es.json
  59. 68 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/nl.json
  60. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/pl.json
  61. 68 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/sv.json
  62. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/tr.json
  63. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/zh.json
  64. 170 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/localization.js
  65. 4 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/CREDITS
  66. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/bell.mp3
  67. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/bell.oga
  68. 0 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/Orbitron700.ttf
  69. 0 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/Orbitron700.woff
  70. 902 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/base.css
  71. 63 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/lite.css
  72. 1669 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/ui.js
  73. 230 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/webutil.js
  74. 110 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/base64.js
  75. 271 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/des.js
  76. 698 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/display.js
  77. 40 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/encodings.js
  78. 38 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/inflator.js
  79. 310 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/domkeytable.js
  80. 127 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/fixedkeys.js
  81. 314 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keyboard.js
  82. 614 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keysym.js
  83. 688 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keysymdef.js
  84. 280 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/mouse.js
  85. 167 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/util.js
  86. 116 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/vkeys.js
  87. 171 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/xtscancodes.js
  88. 2540 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/rfb.js
  89. 69 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/browser.js
  90. 138 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/events.js
  91. 40 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/eventtarget.js
  92. 51 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/logging.js
  93. 54 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/polyfill.js
  94. 15 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/strings.js
  95. 316 0
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/websock.js
  96. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/favicon.ico
  97. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/clipboard.png
  98. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/connect.png
  99. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/ctrlaltdel.png
  100. BIN
      board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/disconnect.png

+ 1 - 1
board/GfA/Display001/BUILD

@@ -1 +1 @@
-660
+661

BIN
board/GfA/Display001/rootfs/usr/sbin/fbvncserver


+ 3 - 3
board/GfA/Display001/rootfs/var/GfA/WebVnc/index.vnc

@@ -18,7 +18,7 @@ $USER's $DESKTOP desktop ($DISPLAY)
 <br/>
 <br/>
 <br/>
 <br/>
 
 
-If the above Java applet does not work, you can also try the new JavaScript-only <a href="http://kanaka.github.com/noVNC/">noVNC</a> viewer. You will need a HTML5-capable browser though.
+If the above Java applet does not work, you can also try the new JavaScript-only <a href="https://novnc.com/">noVNC</a> viewer. You will need a HTML5-capable browser though.
 <script language="JavaScript">
 <script language="JavaScript">
     <!--
     <!--
     function start_novnc(){
     function start_novnc(){
@@ -30,7 +30,7 @@ If the above Java applet does not work, you can also try the new JavaScript-only
 	   if(host.charAt(host.length-1) != "]")
 	   if(host.charAt(host.length-1) != "]")
 	      host = host + "]";
 	      host = host + "]";
 	}
 	}
-	open("novnc/vnc_auto.html?host=" + host + "&port=$PORT&true_color=1");
+	open("novnc/vnc.html?autoconnect=true&host=" + host + "&port=$PORT");
     }
     }
     -->
     -->
 </script>
 </script>
@@ -41,5 +41,5 @@ If the above Java applet does not work, you can also try the new JavaScript-only
 <br/>
 <br/>
 <br/>
 <br/>
 <br/>
 <br/>
-<A href="http://libvncserver.sf.net/">LibVNCServer/LibVNCClient Homepage</A>
+<A href="https://libvnc.github.io/">LibVNCServer/LibVNCClient Homepage</A>
 </HTML>
 </HTML>

+ 0 - 382
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile

@@ -1,382 +0,0 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
-# webclients/java-applet/ssl/Makefile.  Generated from Makefile.in by configure.
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
-# Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-
-
-pkgdatadir = $(datadir)/LibVNCServer
-pkgincludedir = $(includedir)/LibVNCServer
-pkglibdir = $(libdir)/LibVNCServer
-pkglibexecdir = $(libexecdir)/LibVNCServer
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = x86_64-unknown-linux-gnu
-host_triplet = arm-buildroot-linux-gnueabihf
-subdir = webclients/java-applet/ssl
-DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
-	$(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-	$(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/rfbconfig.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
-am__v_GEN_0 = @echo "  GEN   " $@;
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
-am__v_at_0 = @
-SOURCES =
-DIST_SOURCES =
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run aclocal-1.11
-AMTAR = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run tar
-AM_DEFAULT_VERBOSITY = 0
-AR = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-ar
-AS = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-as
-AUTOCONF = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run autoconf
-AUTOHEADER = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run autoheader
-AUTOMAKE = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run automake-1.11
-AVAHI_CFLAGS = 
-AVAHI_LIBS = 
-AWK = gawk
-CC = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-gcc
-CCDEPMODE = depmode=none
-CFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -pipe -Os -g2 -Wall -I/home/ru/BUILDROOTTEST/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/include
-CPP = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-cpp
-CPPFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-CRYPT_LIBS = -lcrypt
-CXX = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-g++
-CXXCPP = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-g++ -E
-CXXDEPMODE = depmode=none
-CXXFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -pipe -Os -g2
-CYGPATH_W = echo
-DEFS = -DHAVE_CONFIG_H
-DEPDIR = .deps
-DLLTOOL = dlltool
-ECHO = echo
-ECHO_C = 
-ECHO_N = -n
-ECHO_T = 
-EGREP = /bin/grep -E
-EXEEXT = 
-F77 = 
-FFLAGS = 
-GNUTLS_CFLAGS = 
-GNUTLS_LIBS = 
-GREP = /bin/grep
-GTK_CFLAGS = 
-GTK_LIBS = 
-INSTALL = /usr/bin/install -c
-INSTALL_DATA = ${INSTALL} -m 644
-INSTALL_PROGRAM = ${INSTALL}
-INSTALL_SCRIPT = ${INSTALL}
-INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
-JPEG_LDFLAGS = 
-LDFLAGS = 
-LIBGCRYPT_CFLAGS = -I/home/ru/BUILDROOTTEST/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/include
-LIBGCRYPT_CONFIG = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/bin/libgcrypt-config
-LIBGCRYPT_LIBS = -L/home/ru/BUILDROOTTEST/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/lib -lgcrypt -lgpg-error
-LIBOBJS =  ${LIBOBJDIR}lstat$U.o ${LIBOBJDIR}stat$U.o
-LIBS = -lnsl -lpthread -lz -lpng -ljpeg -lresolv -lssl -lcrypto -L/home/ru/BUILDROOTTEST/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/lib -lgcrypt -lgpg-error
-LIBTOOL = $(SHELL) $(top_builddir)/libtool
-LN_S = ln -s
-LTLIBOBJS =  ${LIBOBJDIR}lstat$U.lo ${LIBOBJDIR}stat$U.lo
-MAKEINFO = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/missing --run makeinfo
-MKDIR_P = /bin/mkdir -p
-OBJDUMP = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-objdump
-OBJEXT = o
-PACKAGE = LibVNCServer
-PACKAGE_BUGREPORT = http://sourceforge.net/projects/libvncserver
-PACKAGE_NAME = LibVNCServer
-PACKAGE_STRING = LibVNCServer 0.9.9
-PACKAGE_TARNAME = libvncserver
-PACKAGE_URL = 
-PACKAGE_VERSION = 0.9.9
-PATH_SEPARATOR = :
-PKG_CONFIG = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/pkg-config
-PKG_CONFIG_LIBDIR = 
-PKG_CONFIG_PATH = 
-RANLIB = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-ranlib
-RPMSOURCEDIR = NOT-FOUND
-SDL_CFLAGS = 
-SDL_LIBS = 
-SET_MAKE = 
-SHELL = /bin/bash
-SSL_LIBS = -lssl -lcrypto
-STRIP = /home/ru/BUILDROOTTEST/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabihf-strip
-SYSTEM_LIBVNCSERVER_CFLAGS = 
-SYSTEM_LIBVNCSERVER_LIBS = 
-VERSION = 0.9.9
-WSOCKLIB = 
-XMKMF = 
-X_CFLAGS = 
-X_EXTRA_LIBS = 
-X_LIBS = 
-X_PRE_LIBS = 
-abs_builddir = /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/webclients/java-applet/ssl
-abs_srcdir = /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/webclients/java-applet/ssl
-abs_top_builddir = /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9
-abs_top_srcdir = /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9
-ac_ct_CC = 
-ac_ct_CXX = 
-ac_ct_F77 = 
-am__include = include
-am__leading_dot = .
-am__quote = 
-am__tar = ${AMTAR} chof - "$$tardir"
-am__untar = ${AMTAR} xf -
-bindir = ${exec_prefix}/bin
-build = x86_64-unknown-linux-gnu
-build_alias = x86_64-unknown-linux-gnu
-build_cpu = x86_64
-build_os = linux-gnu
-build_vendor = unknown
-builddir = .
-datadir = ${datarootdir}
-datarootdir = ${prefix}/share
-docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
-dvidir = ${docdir}
-exec_prefix = /usr
-host = arm-buildroot-linux-gnueabihf
-host_alias = arm-buildroot-linux-gnueabihf
-host_cpu = arm
-host_os = linux-gnueabihf
-host_vendor = buildroot
-htmldir = ${docdir}
-includedir = ${prefix}/include
-infodir = ${datarootdir}/info
-install_sh = ${SHELL} /home/ru/BUILDROOTTEST/buildroot/output/build/libvncserver-0.9.9/install-sh
-libdir = ${exec_prefix}/lib
-libexecdir = ${exec_prefix}/libexec
-localedir = ${datarootdir}/locale
-localstatedir = /var
-mandir = ${datarootdir}/man
-mkdir_p = /bin/mkdir -p
-oldincludedir = /usr/include
-pdfdir = ${docdir}
-prefix = /usr
-program_transform_name = s&^&&
-psdir = ${docdir}
-sbindir = ${exec_prefix}/sbin
-sharedstatedir = ${prefix}/com
-srcdir = .
-sysconfdir = /etc
-target_alias = arm-buildroot-linux-gnueabihf
-top_build_prefix = ../../../
-top_builddir = ../../..
-top_srcdir = ../../..
-with_ffmpeg = 
-EXTRA_DIST = VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc
-all: all-am
-
-.SUFFIXES:
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
-	@for dep in $?; do \
-	  case '$(am__configure_deps)' in \
-	    *$$dep*) \
-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
-	        && { if test -f $@; then exit 0; else break; fi; }; \
-	      exit 1;; \
-	  esac; \
-	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu webclients/java-applet/ssl/Makefile'; \
-	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu webclients/java-applet/ssl/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-	@case '$?' in \
-	  *config.status*) \
-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
-	  *) \
-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
-	esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure:  $(am__configure_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-
-mostlyclean-libtool:
-	-rm -f *.lo
-
-clean-libtool:
-	-rm -rf .libs _libs
-tags: TAGS
-TAGS:
-
-ctags: CTAGS
-CTAGS:
-
-
-distdir: $(DISTFILES)
-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	list='$(DISTFILES)'; \
-	  dist_files=`for file in $$list; do echo $$file; done | \
-	  sed -e "s|^$$srcdirstrip/||;t" \
-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-	case $$dist_files in \
-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-			   sort -u` ;; \
-	esac; \
-	for file in $$dist_files; do \
-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-	  if test -d $$d/$$file; then \
-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-	    if test -d "$(distdir)/$$file"; then \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
-	  else \
-	    test -f "$(distdir)/$$file" \
-	    || cp -p $$d/$$file "$(distdir)/$$file" \
-	    || exit 1; \
-	  fi; \
-	done
-check-am: all-am
-check: check-am
-all-am: Makefile
-installdirs:
-install: install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	  `test -z '$(STRIP)' || \
-	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
-	@echo "This command is intended for maintainers to use"
-	@echo "it deletes files that may require special tools to rebuild."
-clean: clean-am
-
-clean-am: clean-generic clean-libtool mostlyclean-am
-
-distclean: distclean-am
-	-rm -f Makefile
-distclean-am: clean-am distclean-generic
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am:
-
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-	-rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-generic mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am:
-
-.MAKE: install-am install-strip
-
-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
-	distclean distclean-generic distclean-libtool distdir dvi \
-	dvi-am html html-am info info-am install install-am \
-	install-data install-data-am install-dvi install-dvi-am \
-	install-exec install-exec-am install-html install-html-am \
-	install-info install-info-am install-man install-pdf \
-	install-pdf-am install-ps install-ps-am install-strip \
-	installcheck installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
-
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:

+ 0 - 2
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile.am

@@ -1,2 +0,0 @@
-EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc
-

+ 0 - 382
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/Makefile.in

@@ -1,382 +0,0 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
-# Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-subdir = webclients/java-applet/ssl
-DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
-	$(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-	$(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/rfbconfig.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
-am__v_GEN_0 = @echo "  GEN   " $@;
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
-am__v_at_0 = @
-SOURCES =
-DIST_SOURCES =
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = @ACLOCAL@
-AMTAR = @AMTAR@
-AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AR = @AR@
-AS = @AS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AVAHI_CFLAGS = @AVAHI_CFLAGS@
-AVAHI_LIBS = @AVAHI_LIBS@
-AWK = @AWK@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CRYPT_LIBS = @CRYPT_LIBS@
-CXX = @CXX@
-CXXCPP = @CXXCPP@
-CXXDEPMODE = @CXXDEPMODE@
-CXXFLAGS = @CXXFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-DLLTOOL = @DLLTOOL@
-ECHO = @ECHO@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-F77 = @F77@
-FFLAGS = @FFLAGS@
-GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
-GNUTLS_LIBS = @GNUTLS_LIBS@
-GREP = @GREP@
-GTK_CFLAGS = @GTK_CFLAGS@
-GTK_LIBS = @GTK_LIBS@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-JPEG_LDFLAGS = @JPEG_LDFLAGS@
-LDFLAGS = @LDFLAGS@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIBTOOL = @LIBTOOL@
-LN_S = @LN_S@
-LTLIBOBJS = @LTLIBOBJS@
-MAKEINFO = @MAKEINFO@
-MKDIR_P = @MKDIR_P@
-OBJDUMP = @OBJDUMP@
-OBJEXT = @OBJEXT@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_URL = @PACKAGE_URL@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-PKG_CONFIG = @PKG_CONFIG@
-PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
-PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
-RANLIB = @RANLIB@
-RPMSOURCEDIR = @RPMSOURCEDIR@
-SDL_CFLAGS = @SDL_CFLAGS@
-SDL_LIBS = @SDL_LIBS@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SSL_LIBS = @SSL_LIBS@
-STRIP = @STRIP@
-SYSTEM_LIBVNCSERVER_CFLAGS = @SYSTEM_LIBVNCSERVER_CFLAGS@
-SYSTEM_LIBVNCSERVER_LIBS = @SYSTEM_LIBVNCSERVER_LIBS@
-VERSION = @VERSION@
-WSOCKLIB = @WSOCKLIB@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_CXX = @ac_ct_CXX@
-ac_ct_F77 = @ac_ct_F77@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_build_prefix = @top_build_prefix@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-with_ffmpeg = @with_ffmpeg@
-EXTRA_DIST = VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc
-all: all-am
-
-.SUFFIXES:
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
-	@for dep in $?; do \
-	  case '$(am__configure_deps)' in \
-	    *$$dep*) \
-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
-	        && { if test -f $@; then exit 0; else break; fi; }; \
-	      exit 1;; \
-	  esac; \
-	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu webclients/java-applet/ssl/Makefile'; \
-	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu webclients/java-applet/ssl/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-	@case '$?' in \
-	  *config.status*) \
-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
-	  *) \
-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
-	esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure:  $(am__configure_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-
-mostlyclean-libtool:
-	-rm -f *.lo
-
-clean-libtool:
-	-rm -rf .libs _libs
-tags: TAGS
-TAGS:
-
-ctags: CTAGS
-CTAGS:
-
-
-distdir: $(DISTFILES)
-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	list='$(DISTFILES)'; \
-	  dist_files=`for file in $$list; do echo $$file; done | \
-	  sed -e "s|^$$srcdirstrip/||;t" \
-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-	case $$dist_files in \
-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-			   sort -u` ;; \
-	esac; \
-	for file in $$dist_files; do \
-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-	  if test -d $$d/$$file; then \
-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-	    if test -d "$(distdir)/$$file"; then \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
-	  else \
-	    test -f "$(distdir)/$$file" \
-	    || cp -p $$d/$$file "$(distdir)/$$file" \
-	    || exit 1; \
-	  fi; \
-	done
-check-am: all-am
-check: check-am
-all-am: Makefile
-installdirs:
-install: install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	  `test -z '$(STRIP)' || \
-	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
-	@echo "This command is intended for maintainers to use"
-	@echo "it deletes files that may require special tools to rebuild."
-clean: clean-am
-
-clean-am: clean-generic clean-libtool mostlyclean-am
-
-distclean: distclean-am
-	-rm -f Makefile
-distclean-am: clean-am distclean-generic
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am:
-
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-	-rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-generic mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am:
-
-.MAKE: install-am install-strip
-
-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
-	distclean distclean-generic distclean-libtool distdir dvi \
-	dvi-am html html-am info info-am install install-am \
-	install-data install-data-am install-dvi install-dvi-am \
-	install-exec install-exec-am install-html install-html-am \
-	install-info install-info-am install-man install-pdf \
-	install-pdf-am install-ps install-ps-am install-strip \
-	installcheck installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
-
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:

+ 3 - 3
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/README

@@ -42,9 +42,9 @@ Send full Java Console output for failures.
 Tips:
 Tips:
 
 
 When doing single-port proxy connections (e.g. both VNC and HTTPS
 When doing single-port proxy connections (e.g. both VNC and HTTPS
-thru port 5900) it helps to move through the 'do you trust this site'
+through port 5900) it helps to move through the 'do you trust this site'
 dialogs quickly.   x11vnc has to wait to see if the traffic is VNC or
 dialogs quickly.   x11vnc has to wait to see if the traffic is VNC or
-HTTP and this can cause timeouts if you don't move thru them quickly.
+HTTP and this can cause timeouts if you don't move through them quickly.
 
 
 You may have to restart your browser completely if it gets into a
 You may have to restart your browser completely if it gets into a
 weird state.  For one case we saw the JVM requesting VncViewer.class
 weird state.  For one case we saw the JVM requesting VncViewer.class
@@ -172,7 +172,7 @@ Both TightVNC and UltraVNC Java viewers:
 	Set to do a special HTTP GET (/request.https.vnc.connection)
 	Set to do a special HTTP GET (/request.https.vnc.connection)
 	to the vnc server that will cause it to switch to VNC instead.
 	to the vnc server that will cause it to switch to VNC instead.
 	This is to speedup/make more robust, the single port HTTPS and VNC
 	This is to speedup/make more robust, the single port HTTPS and VNC
-	mode of x11vnc (e.g. both services thru port 5900, etc) 
+	mode of x11vnc (e.g. both services through port 5900, etc)
 	
 	
   urlPrefix
   urlPrefix
 	string, default: none
 	string, default: none

+ 1 - 1
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/proxy.vnc

@@ -25,7 +25,7 @@ fails in the proxy environment.
 The applet is not allowed to open a socket connection to the proxy (since
 The applet is not allowed to open a socket connection to the proxy (since
 that would let it connect to just about any host, e.g. CONNECT method).
 that would let it connect to just about any host, e.g. CONNECT method).
 
 
-This is indpendent of SSL but of course fails for that socket connection
+This is independent of SSL but of course fails for that socket connection
 as well.  I.e. this is a problem for non-SSL VNC Viewers as well.
 as well.  I.e. this is a problem for non-SSL VNC Viewers as well.
 
 
 Solution?  Sign the applet and have the user click on "Yes" that they
 Solution?  Sign the applet and have the user click on "Yes" that they

+ 5 - 5
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/ss_vncviewer

@@ -91,7 +91,7 @@
 #
 #
 #             [user1@]host1[:port1],[user2@]host2[:port2]
 #             [user1@]host1[:port1],[user2@]host2[:port2]
 #
 #
-#         in which case a ssh to host1 and thru it via a -L redir a 2nd
+#         in which case a ssh to host1 and through it via a -L redir a 2nd
 #         ssh is established to host2.  
 #         ssh is established to host2.  
 #
 #
 #         Examples:
 #         Examples:
@@ -109,10 +109,10 @@
 #
 #
 # -sshargs "args"  pass "args" to the ssh process, e.g. -L/-R port redirs.
 # -sshargs "args"  pass "args" to the ssh process, e.g. -L/-R port redirs.
 #
 #
-# -sshssl Tunnel the SSL connection thru a SSH connection.  The tunnel as
-#         under -ssh is set up and the SSL connection goes thru it.  Use
+# -sshssl Tunnel the SSL connection through a SSH connection.  The tunnel as
+#         under -ssh is set up and the SSL connection goes through it.  Use
 #         this if you want to have and end-to-end SSL connection but must
 #         this if you want to have and end-to-end SSL connection but must
-#         go thru a SSH gateway host (e.g. not the vnc server).  Or use
+#         go through a SSH gateway host (e.g. not the vnc server).  Or use
 #         this if you need to tunnel additional services via -R and -L 
 #         this if you need to tunnel additional services via -R and -L 
 #         (see -sshargs above).
 #         (see -sshargs above).
 #
 #
@@ -351,7 +351,7 @@ if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then
 	STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG
 	STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG
 fi
 fi
 
 
-# this is the -t ssh option (gives better keyboard response thru SSH tunnel)
+# this is the -t ssh option (gives better keyboard response through SSH tunnel)
 targ="-t"
 targ="-t"
 if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then
 if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then
 	targ=""
 	targ=""

+ 111 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch

@@ -0,0 +1,111 @@
+--- vnc_javasrc.orig/VncCanvas.java	2004-10-10 02:15:54.000000000 -0400
++++ vnc_javasrc/VncCanvas.java	2010-11-30 21:01:15.000000000 -0500
+@@ -28,13 +28,14 @@
+ import java.lang.*;
+ import java.util.zip.*;
+ 
++import java.util.Collections;
+ 
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+ //
+ 
+ class VncCanvas extends Canvas
+-  implements KeyListener, MouseListener, MouseMotionListener {
++  implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+ 
+   VncViewer viewer;
+   RfbProto rfb;
+@@ -81,6 +82,20 @@
+     cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+     cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+ 
++    // kludge to not show any Java cursor in the canvas since we are
++    // showing the soft cursor (should be a user setting...)
++    Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++        Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++        "dot");
++    this.setCursor(dot);
++
++    // while we are at it... get rid of the keyboard traversals that
++    // make it so we can't type a Tab character:
++    this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++        Collections.EMPTY_SET);
++    this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++        Collections.EMPTY_SET);
++
+     colors = new Color[256];
+     for (int i = 0; i < 256; i++)
+       colors[i] = new Color(cm8.getRGB(i));
+@@ -169,6 +184,7 @@
+       inputEnabled = true;
+       addMouseListener(this);
+       addMouseMotionListener(this);
++      addMouseWheelListener(this);
+       if (viewer.showControls) {
+ 	viewer.buttonPanel.enableRemoteAccessControls(true);
+       }
+@@ -177,6 +193,7 @@
+       inputEnabled = false;
+       removeMouseListener(this);
+       removeMouseMotionListener(this);
++      removeMouseWheelListener(this);
+       if (viewer.showControls) {
+ 	viewer.buttonPanel.enableRemoteAccessControls(false);
+       }
+@@ -1190,6 +1207,9 @@
+   public void mouseDragged(MouseEvent evt) {
+     processLocalMouseEvent(evt, true);
+   }
++  public void mouseWheelMoved(MouseWheelEvent evt) {
++    processLocalMouseWheelEvent(evt);
++  }
+ 
+   public void processLocalKeyEvent(KeyEvent evt) {
+     if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1221,6 +1241,19 @@
+     evt.consume();
+   }
+ 
++  public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++    if (viewer.rfb != null && rfb.inNormalProtocol) {
++      synchronized(rfb) {
++	try {
++	  rfb.writeWheelEvent(evt);
++	} catch (Exception e) {
++	  e.printStackTrace();
++	}
++	rfb.notify();
++      }
++    }
++  }
++
+   public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+     if (viewer.rfb != null && rfb.inNormalProtocol) {
+       if (moved) {
+@@ -1387,9 +1420,9 @@
+ 		result = cm8.getRGB(pixBuf[i]);
+ 	      } else {
+ 		result = 0xFF000000 |
+-		  (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+-		  (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+-		  (pixBuf[i * 4 + 3] & 0xFF);
++		  (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++		  (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++		  (pixBuf[i * 4 + 0] & 0xFF);
+ 	      }
+ 	    } else {
+ 	      result = 0;	// Transparent pixel
+@@ -1403,9 +1436,9 @@
+ 	      result = cm8.getRGB(pixBuf[i]);
+ 	    } else {
+ 	      result = 0xFF000000 |
+-		(pixBuf[i * 4 + 1] & 0xFF) << 16 |
+-		(pixBuf[i * 4 + 2] & 0xFF) << 8 |
+-		(pixBuf[i * 4 + 3] & 0xFF);
++		(pixBuf[i * 4 + 2] & 0xFF) << 16 |
++		(pixBuf[i * 4 + 1] & 0xFF) << 8 |
++		(pixBuf[i * 4 + 0] & 0xFF);
+ 	    }
+ 	  } else {
+ 	    result = 0;		// Transparent pixel

+ 2600 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch

@@ -0,0 +1,2600 @@
+diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile
+--- vnc_javasrc.orig/Makefile	2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/Makefile	2010-05-18 20:56:26.000000000 -0400
+@@ -4,6 +4,7 @@
+ 
+ CP = cp
+ JC = javac
++JC_ARGS = -target 1.4 -source 1.4
+ JAR = jar
+ ARCHIVE = VncViewer.jar
+ MANIFEST = MANIFEST.MF
+@@ -15,25 +16,29 @@
+ 	  DesCipher.class CapabilityInfo.class CapsContainer.class \
+ 	  RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \
+ 	  SocketFactory.class HTTPConnectSocketFactory.class \
+-	  HTTPConnectSocket.class ReloginPanel.class
++	  HTTPConnectSocket.class ReloginPanel.class \
++	  SSLSocketToMe.class
++
++SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class
+ 
+ SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
+ 	  OptionsFrame.java ClipboardFrame.java ButtonPanel.java \
+ 	  DesCipher.java CapabilityInfo.java CapsContainer.java \
+ 	  RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \
+ 	  SocketFactory.java HTTPConnectSocketFactory.java \
+-	  HTTPConnectSocket.java ReloginPanel.java
++	  HTTPConnectSocket.java ReloginPanel.java \
++	  SSLSocketToMe.java
+ 
+ all: $(CLASSES) $(ARCHIVE)
+ 
+ $(CLASSES): $(SOURCES)
+-	$(JC) -target 1.1 -O $(SOURCES)
++	$(JC) $(JC_ARGS) -O $(SOURCES)
+ 
+ $(ARCHIVE): $(CLASSES) $(MANIFEST)
+-	$(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES)
++	$(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES)
+ 
+ install: $(CLASSES) $(ARCHIVE)
+-	$(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
++	$(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
+ 
+ export:: $(CLASSES) $(ARCHIVE) $(PAGES)
+ 	@$(ExportJavaClasses)
+diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java
+--- vnc_javasrc.orig/RfbProto.java	2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/RfbProto.java	2010-11-30 22:05:12.000000000 -0500
+@@ -199,7 +199,21 @@
+     host = h;
+     port = p;
+ 
+-    if (viewer.socketFactory == null) {
++    if (! viewer.disableSSL) {
++	System.out.println("new SSLSocketToMe");
++	SSLSocketToMe ssl;
++	try {
++		ssl = new SSLSocketToMe(host, port, v);
++	} catch (Exception e) {
++		throw new IOException(e.getMessage());
++	}
++
++	try {
++		sock = ssl.connectSock();
++	} catch (Exception es) {
++		throw new IOException(es.getMessage());
++	}
++    } else if (viewer.socketFactory == null) {
+       sock = new Socket(host, port);
+     } else {
+       try {
+@@ -255,7 +269,7 @@
+ 	|| (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
+     {
+       throw new Exception("Host " + host + " port " + port +
+-			  " is not an RFB server");
++			  " is not an RFB server: " + b);
+     }
+ 
+     serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
+@@ -892,6 +906,38 @@
+   final static int ALT_MASK   = InputEvent.ALT_MASK;
+ 
+ 
++  void writeWheelEvent(MouseWheelEvent evt) throws IOException {
++
++    eventBufLen = 0;
++
++    int x = evt.getX();
++    int y = evt.getY();
++
++    if (x < 0) x = 0;
++    if (y < 0) y = 0;
++
++    int ptrmask;
++
++    int clicks = evt.getWheelRotation();
++    System.out.println("writeWheelEvent: clicks: " + clicks);
++    if (clicks > 0) {
++    	ptrmask = 16;
++    } else if (clicks < 0) {
++    	ptrmask = 8;
++    } else {
++    	return;
++    }
++
++    eventBuf[eventBufLen++] = (byte) PointerEvent;
++    eventBuf[eventBufLen++] = (byte) ptrmask;
++    eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
++    eventBuf[eventBufLen++] = (byte) (x & 0xff);
++    eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
++    eventBuf[eventBufLen++] = (byte) (y & 0xff);
++
++    os.write(eventBuf, 0, eventBufLen);
++  }
++
+   //
+   // Write a pointer event message.  We may need to send modifier key events
+   // around it to set the correct modifier state.
+@@ -992,6 +1038,19 @@
+     boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
+ 
+     int key;
++	if (viewer.debugKeyboard) {
++		System.out.println("----------------------------------------");
++		System.out.println("evt.getKeyChar:      " + evt.getKeyChar());
++		System.out.println("getKeyText:          " + KeyEvent.getKeyText(evt.getKeyCode()));
++		System.out.println("evt.getKeyCode:      " + evt.getKeyCode());
++		System.out.println("evt.getID:           " + evt.getID());
++		System.out.println("evt.getKeyLocation:  " + evt.getKeyLocation());
++		System.out.println("evt.isActionKey:     " + evt.isActionKey());
++		System.out.println("evt.isControlDown:   " + evt.isControlDown());
++		System.out.println("evt.getModifiers:    " + evt.getModifiers());
++		System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers()));
++		System.out.println("evt.paramString:     " + evt.paramString());
++	}
+     if (evt.isActionKey()) {
+ 
+       //
+@@ -1025,6 +1084,13 @@
+         return;
+       }
+ 
++      if(key == 0xffc2 && viewer.mapF5_to_atsign) {
++	 if (viewer.debugKeyboard) {
++	    System.out.println("Mapping: F5 -> AT ");
++	 }
++      	 key = 0x40;
++      }
++
+     } else {
+ 
+       //
+@@ -1036,6 +1102,7 @@
+ 
+       key = keyChar;
+ 
++
+       if (key < 0x20) {
+         if (evt.isControlDown()) {
+           key += 0x60;
+@@ -1121,6 +1188,16 @@
+   int oldModifiers = 0;
+ 
+   void writeModifierKeyEvents(int newModifiers) {
++    if(viewer.forbid_Ctrl_Alt) {
++	if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) {
++		int orig = newModifiers;
++		newModifiers &= ~ALT_MASK;
++		newModifiers &= ~CTRL_MASK;
++		if (viewer.debugKeyboard) {
++			System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers);
++		}
++	}
++    }
+     if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
+       writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
+ 
+diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java
+--- vnc_javasrc.orig/SSLSocketToMe.java	1969-12-31 19:00:00.000000000 -0500
++++ vnc_javasrc/SSLSocketToMe.java	2010-07-10 19:18:06.000000000 -0400
+@@ -0,0 +1,2067 @@
++/*
++ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
++ *
++ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com>
++ * All rights reserved.
++ *
++ *  This is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License, or
++ *  (at your option) any later version.
++ *
++ *  This software is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this software; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
++ *  USA.
++ *
++ */
++
++import java.net.*;
++import java.io.*;
++import javax.net.ssl.*;
++import java.util.*;
++
++import java.security.*;
++import java.security.cert.*;
++import java.security.spec.*;
++import java.security.cert.Certificate;
++import java.security.cert.CertificateFactory;
++
++import java.awt.*;
++import java.awt.event.*;
++
++public class SSLSocketToMe {
++
++	/* basic member data: */
++	String host;
++	int port;
++	VncViewer viewer;
++
++	boolean debug = true;
++	boolean debug_certs = false;
++
++	/* sockets */
++	SSLSocket socket = null;
++	SSLSocketFactory factory;
++
++	/* fallback for Proxy connection */
++	boolean proxy_in_use = false;
++	boolean proxy_failure = false;
++	public DataInputStream is = null;
++	public OutputStream os = null;
++
++	/* strings from user WRT proxy: */
++	String proxy_auth_string = null;
++	String proxy_dialog_host = null;
++	int proxy_dialog_port = 0;
++
++	Socket proxySock;
++	DataInputStream proxy_is;
++	OutputStream proxy_os;
++
++	/* trust contexts */
++	SSLContext trustloc_ctx;
++	SSLContext trustall_ctx;
++	SSLContext trustsrv_ctx;
++	SSLContext trusturl_ctx;
++	SSLContext trustone_ctx;
++
++	/* corresponding trust managers */
++	TrustManager[] trustAllCerts;
++	TrustManager[] trustSrvCert;
++	TrustManager[] trustUrlCert;
++	TrustManager[] trustOneCert;
++
++	/* client-side SSL auth key (oneTimeKey=...) */
++	KeyManager[] mykey = null;
++
++	boolean user_wants_to_see_cert = true;
++	String cert_fail = null;
++
++	/* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
++	java.security.cert.Certificate[] trustallCerts = null;
++	java.security.cert.Certificate[] trustsrvCerts = null;
++	java.security.cert.Certificate[] trusturlCerts = null;
++
++	/* utility to decode hex oneTimeKey=... and serverCert=... */
++	byte[] hex2bytes(String s) {
++		byte[] bytes = new byte[s.length()/2];
++		for (int i=0; i<s.length()/2; i++) {
++			int j = 2*i;
++			try {
++				int val = Integer.parseInt(s.substring(j, j+2), 16);
++				if (val > 127) {
++					val -= 256;
++				}
++				Integer I = new Integer(val);
++				bytes[i] = Byte.decode(I.toString()).byteValue();
++				
++			} catch (Exception e) {
++				;
++			}
++		}
++		return bytes;
++	}
++
++	SSLSocketToMe(String h, int p, VncViewer v) throws Exception {
++		host = h;
++		port = p;
++		viewer = v;
++
++		debug_certs = v.debugCerts;
++
++		/* we will first try default factory for certification: */
++
++		factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
++
++		dbg("SSL startup: " + host + " " + port);
++
++
++		/* create trust managers to be used if initial handshake fails: */
++
++		trustAllCerts = new TrustManager[] {
++		    /*
++		     * this one accepts everything.  Only used if user
++		     * has disabled checking (trustAllVncCerts=yes)
++		     * or when we grab the cert to show it to them in
++		     * a dialog and ask them to manually verify/accept it.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) {
++				/* empty */
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) {
++				/* empty */
++				dbg("ALL: an untrusted connect to grab cert.");
++			}
++		    }
++		};
++
++		trustUrlCert = new TrustManager[] {
++		    /*
++		     * this one accepts only the retrieved server
++		     * cert by SSLSocket by this applet and stored in
++		     * trusturlCerts.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (URL)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trusturlCerts' */
++				if (trusturlCerts == null) {
++					throw new CertificateException(
++					    "No Trust url Certs array.");
++				}
++				if (trusturlCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust url Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trusturlCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trusturlCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("URL: cert mismatch at i=" + i);
++						dbg("URL: cert mismatch cert" + certs[i]);
++						dbg("URL: cert mismatch  url" + trusturlCerts[i]);
++						if (cert_fail == null) {
++							cert_fail = "cert-mismatch";
++						}
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("URL: cert info at i=" + i);
++						dbg("URL: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("URL: cert info  url" + trusturlCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != URL Cert Chain.");
++				}
++				dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		trustSrvCert = new TrustManager[] {
++		    /*
++		     * this one accepts cert given to us in the serverCert
++		     * Applet Parameter we were started with.  It is
++		     * currently a fatal error if the VNC Server's cert
++		     * doesn't match it.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (SRV)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trustsrvCerts' */
++				if (trustsrvCerts == null) {
++					throw new CertificateException(
++					    "No Trust srv Certs array.");
++				}
++				if (trustsrvCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust srv Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trustsrvCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trustsrvCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("SRV: cert mismatch at i=" + i);
++						dbg("SRV: cert mismatch cert" + certs[i]);
++						dbg("SRV: cert mismatch  srv" + trustsrvCerts[i]);
++						if (cert_fail == null) {
++							cert_fail = "server-cert-mismatch";
++						}
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("SRV: cert info at i=" + i);
++						dbg("SRV: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("SRV: cert info  srv" + trustsrvCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
++				}
++				dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		trustOneCert = new TrustManager[] {
++		    /*
++		     * this one accepts only the retrieved server
++		     * cert by SSLSocket by this applet we stored in
++		     * trustallCerts that user has accepted or applet
++		     * parameter trustAllVncCerts=yes is set.  This is
++		     * for when we reconnect after the user has manually
++		     * accepted the trustall cert in the dialog (or set
++		     * trustAllVncCerts=yes applet param.)
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (ONE)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trustallCerts' */
++				if (trustallCerts == null) {
++					throw new CertificateException(
++					    "No Trust All Server Certs array.");
++				}
++				if (trustallCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust All Server Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trustallCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trustallCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("ONE: cert mismatch at i=" + i);
++						dbg("ONE: cert mismatch cert" + certs[i]);
++						dbg("ONE: cert mismatch  all" + trustallCerts[i]);
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("ONE: cert info at i=" + i);
++						dbg("ONE: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("ONE: cert info  all" + trustallCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != TRUSTALL Cert Chain.");
++				}
++				dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		/* 
++		 * The above TrustManagers are used:
++		 *
++		 * 1) to retrieve the server cert in case of failure to
++		 *    display it to the user in a dialog.
++		 * 2) to subsequently connect to the server if user agrees.
++		 */
++
++		/*
++		 * build oneTimeKey cert+key if supplied in applet parameter:
++		 */
++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
++			ClientCertDialog d = new ClientCertDialog();
++			viewer.oneTimeKey = d.queryUser();
++		}
++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
++			int idx = viewer.oneTimeKey.indexOf(",");
++
++			String onetimekey = viewer.oneTimeKey.substring(0, idx);
++			byte[] key = hex2bytes(onetimekey);
++			String onetimecert = viewer.oneTimeKey.substring(idx+1);
++			byte[] cert = hex2bytes(onetimecert);
++
++			KeyFactory kf = KeyFactory.getInstance("RSA");
++			PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
++			PrivateKey ff = kf.generatePrivate (keysp);
++			if (debug_certs) {
++				dbg("one time key " + ff);
++			}
++
++			CertificateFactory cf = CertificateFactory.getInstance("X.509");
++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++			Certificate[] certs = new Certificate[c.toArray().length];
++			if (c.size() == 1) {
++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++				if (debug_certs) {
++					dbg("one time cert" + tmpcert);
++				}
++				certs[0] = tmpcert;
++			} else {
++				certs = (Certificate[]) c.toArray();
++			}
++
++			KeyStore ks = KeyStore.getInstance("JKS");
++			ks.load(null, null);
++			ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs);
++			String da = KeyManagerFactory.getDefaultAlgorithm();
++			KeyManagerFactory kmf = KeyManagerFactory.getInstance(da);
++			kmf.init(ks, "".toCharArray());
++
++			mykey = kmf.getKeyManagers();
++		}
++
++		/*
++		 * build serverCert cert if supplied in applet parameter:
++		 */
++		if (viewer.serverCert != null) {
++			CertificateFactory cf = CertificateFactory.getInstance("X.509");
++			byte[] cert = hex2bytes(viewer.serverCert);
++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++			trustsrvCerts = new Certificate[c.toArray().length];
++			if (c.size() == 1) {
++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++				trustsrvCerts[0] = tmpcert;
++			} else {
++				trustsrvCerts = (Certificate[]) c.toArray();
++			}
++		}
++
++		/* the trust loc certs context: */
++		try {
++			trustloc_ctx = SSLContext.getInstance("SSL");
++
++			/*
++			 * below is a failed attempt to get jvm's default
++			 * trust manager using null (below) makes it so
++			 * for HttpsURLConnection the server cannot be
++			 * verified (no prompting.)
++			 */
++			if (false) {
++				boolean didit = false;
++				TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
++				tmf.init((KeyStore) null);
++				TrustManager [] tml = tmf.getTrustManagers();
++				for (int i = 0; i < tml.length; i++) {
++					TrustManager tm = tml[i];
++					if (tm instanceof X509TrustManager) {
++						TrustManager tm1[] = new TrustManager[1];
++						tm1[0] = tm;
++						trustloc_ctx.init(mykey, tm1, null);
++						didit = true;
++						break;
++					}
++				}
++				if (!didit) {
++					trustloc_ctx.init(mykey, null, null);
++				}
++			} else {
++				/* we have to set trust manager to null */
++				trustloc_ctx.init(mykey, null, null);
++			}
++
++		} catch (Exception e) {
++			String msg = "SSL trustloc_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust all certs context: */
++		try {
++			trustall_ctx = SSLContext.getInstance("SSL");
++			trustall_ctx.init(mykey, trustAllCerts, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustall_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust url certs context: */
++		try {
++			trusturl_ctx = SSLContext.getInstance("SSL");
++			trusturl_ctx.init(mykey, trustUrlCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trusturl_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust srv certs context: */
++		try {
++			trustsrv_ctx = SSLContext.getInstance("SSL");
++			trustsrv_ctx.init(mykey, trustSrvCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustsrv_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust the one cert from server context: */
++		try {
++			trustone_ctx = SSLContext.getInstance("SSL");
++			trustone_ctx.init(mykey, trustOneCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustone_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++	}
++
++	/*
++	 * we call this early on to 1) check for a proxy, 2) grab
++	 * Browser/JVM accepted HTTPS cert.
++	 */
++	public void check_for_proxy_and_grab_vnc_server_cert() {
++		
++		trusturlCerts = null;
++		proxy_in_use = false;
++
++		if (viewer.ignoreProxy) {
++			/* applet param says skip it. */
++			/* the downside is we do not set trusturlCerts for comparison later... */
++			/* nor do we autodetect x11vnc for GET=1. */
++			return;
++		}
++
++		dbg("------------------------------------------------");
++		dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
++
++		dbg("TRYING HTTPS:");
++		String ustr = "https://" + host + ":";
++		if (viewer.httpsPort != null) {
++			ustr += viewer.httpsPort;
++		} else {
++			ustr += port;
++		}
++		ustr += viewer.urlPrefix + "/check.https.proxy.connection";
++		dbg("ustr is: " + ustr);
++
++		try {
++			/* prepare for an HTTPS URL connection to host:port */
++			URL url = new URL(ustr);
++			HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
++
++			if (mykey != null) {
++				/* with oneTimeKey (mykey) we can't use the default SSL context */
++				if (trustsrvCerts != null) {
++					dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
++					https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());	
++				} else if (trustloc_ctx != null) {
++					dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
++					https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());	
++				}
++			}
++
++			https.setUseCaches(false);
++			https.setRequestMethod("GET");
++			https.setRequestProperty("Pragma", "No-Cache");
++			https.setRequestProperty("Proxy-Connection", "Keep-Alive");
++			https.setDoInput(true);
++
++			dbg("trying https.connect()");
++			https.connect();
++
++			dbg("trying https.getServerCertificates()");
++			trusturlCerts = https.getServerCertificates();
++
++			if (trusturlCerts == null) {
++				dbg("set trusturlCerts to null!");
++			} else {
++				dbg("set trusturlCerts to non-null");
++			}
++
++			if (https.usingProxy()) {
++				proxy_in_use = true;
++				dbg("An HTTPS proxy is in use. There may be connection problems.");
++			}
++
++			dbg("trying https.getContent()");
++			Object output = https.getContent();
++			dbg("trying https.disconnect()");
++			https.disconnect();
++			if (! viewer.GET) {
++				String header = https.getHeaderField("VNC-Server");
++				if (header != null && header.startsWith("x11vnc")) {
++					dbg("detected x11vnc server (1), setting GET=1");
++					viewer.GET = true;
++				}
++			}
++
++		} catch(Exception e) {
++			dbg("HttpsURLConnection: " + e.getMessage());
++		}
++
++		if (proxy_in_use) {
++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++			dbg("------------------------------------------------");
++			return;
++		} else if (trusturlCerts != null && !viewer.forceProxy) {
++			/* Allow user to require HTTP check?  use forceProxy for now. */
++			dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++			dbg("------------------------------------------------");
++			return;
++		}
++
++		/*
++		 * XXX need to remember scenario where this extra check
++		 * gives useful info.  User's Browser proxy settings?
++		 */
++		dbg("TRYING HTTP:");
++		ustr = "http://" + host + ":" + port;
++		ustr += viewer.urlPrefix + "/index.vnc";
++		dbg("ustr is: " + ustr);
++
++		try {
++			/* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
++			URL url = new URL(ustr);
++			HttpURLConnection http = (HttpURLConnection)
++			    url.openConnection();
++
++			http.setUseCaches(false);
++			http.setRequestMethod("GET");
++			http.setRequestProperty("Pragma", "No-Cache");
++			http.setRequestProperty("Proxy-Connection", "Keep-Alive");
++			http.setDoInput(true);
++
++			dbg("trying http.connect()");
++			http.connect();
++
++			if (http.usingProxy()) {
++				proxy_in_use = true;
++				dbg("An HTTP proxy is in use. There may be connection problems.");
++			}
++			dbg("trying http.getContent()");
++			Object output = http.getContent();
++			dbg("trying http.disconnect()");
++			http.disconnect();
++			if (! viewer.GET) {
++				String header = http.getHeaderField("VNC-Server");
++				if (header != null && header.startsWith("x11vnc")) {
++					dbg("detected x11vnc server (2), setting GET=1");
++					viewer.GET = true;
++				}
++			}
++		} catch(Exception e) {
++			dbg("HttpURLConnection:  " + e.getMessage());
++		}
++		dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++		dbg("------------------------------------------------");
++	}
++
++	public Socket connectSock() throws IOException {
++		/*
++		 * first try a https connection to detect a proxy, and
++		 * grab the VNC server cert at the same time:
++		 */
++		check_for_proxy_and_grab_vnc_server_cert();
++
++		boolean srv_cert = false;
++		
++		if (trustsrvCerts != null) {
++			/* applet parameter suppled serverCert */
++			dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
++			factory = trustsrv_ctx.getSocketFactory();
++			srv_cert = true;
++		} else if (viewer.trustAllVncCerts) {
++			/* trust all certs (no checking) */
++			dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
++			factory = trustall_ctx.getSocketFactory();
++		} else if (trusturlCerts != null) {
++			/* trust certs the Browser/JVM accepted in check_for_proxy... */
++			dbg("using trusturl_ctx");
++			factory = trusturl_ctx.getSocketFactory();
++		} else {
++			/* trust the local defaults */
++			dbg("using trustloc_ctx");
++			factory = trustloc_ctx.getSocketFactory();
++		}
++
++		socket = null;
++
++		try {
++			if (proxy_in_use && viewer.forceProxy) {
++				throw new Exception("forcing proxy (forceProxy)");
++			} else if (viewer.CONNECT != null) {
++				throw new Exception("forcing CONNECT");
++			}
++
++			int timeout = 6;
++			if (timeout > 0) {
++				socket = (SSLSocket) factory.createSocket();
++				InetSocketAddress inetaddr = new InetSocketAddress(host, port);
++				dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port);
++				socket.connect(inetaddr, timeout * 1000);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++		} catch (Exception esock) {
++			dbg("socket error: " + esock.getMessage());
++			if (proxy_in_use || viewer.CONNECT != null) {
++				proxy_failure = true;
++				if (proxy_in_use) {
++					dbg("HTTPS proxy in use. Trying to go with it.");
++				} else {
++					dbg("viewer.CONNECT reverse proxy in use. Trying to go with it.");
++				}
++				try {
++					socket = proxy_socket(factory);
++				} catch (Exception e) {
++					dbg("proxy_socket error: " + e.getMessage());
++				}
++			} else {
++				/* n.b. socket is left in error state to cause ex. below. */
++			}
++		}
++
++		try {
++			socket.startHandshake();
++
++			dbg("The Server Connection Verified OK on 1st try.");
++
++			java.security.cert.Certificate[] currentTrustedCerts;
++			BrowserCertsDialog bcd;
++
++			SSLSession sess = socket.getSession();
++			currentTrustedCerts = sess.getPeerCertificates();
++
++			if (viewer.trustAllVncCerts) {
++				dbg("viewer.trustAllVncCerts-1  keeping socket.");
++			} else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
++				try {
++					socket.close();
++				} catch (Exception e) {
++					dbg("socket is grumpy.");
++				}
++				socket = null;
++				throw new SSLHandshakeException("no current certs");
++			}
++
++			String serv = "";
++			try {
++				CertInfo ci = new CertInfo(currentTrustedCerts[0]);
++				serv = ci.get_certinfo("CN");
++			} catch (Exception e) {
++				;
++			}
++
++			if (viewer.trustAllVncCerts) {
++				dbg("viewer.trustAllVncCerts-2  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else if (viewer.serverCert != null && trustsrvCerts != null) {
++				dbg("viewer.serverCert-1  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else if (viewer.trustUrlVncCert) {
++				dbg("viewer.trustUrlVncCert-1  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else {
++				/* have a dialog with the user: */
++				bcd = new BrowserCertsDialog(serv, host + ":" + port);
++				dbg("browser certs dialog begin.");
++				bcd.queryUser();
++				dbg("browser certs dialog finished.");
++
++				if (bcd.showCertDialog) {
++					String msg = "user wants to see cert";
++					dbg(msg);
++					user_wants_to_see_cert = true;
++					if (cert_fail == null) {
++						cert_fail = "user-view";
++					}
++					throw new SSLHandshakeException(msg);
++				} else {
++					user_wants_to_see_cert = false;
++					dbg("browser certs dialog: user said yes, accept it");
++				}
++			}
++
++		} catch (SSLHandshakeException eh)  {
++			dbg("SSLHandshakeException: could not automatically verify Server.");
++			dbg("msg: " + eh.getMessage());
++
++
++			/* send a cleanup string just in case: */
++			String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
++
++			try {
++				OutputStream os = socket.getOutputStream();
++				os.write(getoutstr.getBytes());
++				socket.close();
++			} catch (Exception e) {
++				dbg("socket is grumpy!");
++			}
++
++			/* reload */
++
++			socket = null;
++
++			String reason = null;
++
++			if (srv_cert) {
++				/* for serverCert usage we make this a fatal error. */
++				throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
++				/* see below in TrustDialog were we describe this case to user anyway */
++			}
++
++			/*
++			 * Reconnect, trusting any cert, so we can grab
++			 * the cert to show it to the user in a dialog
++			 * for him to manually accept.  This connection
++			 * is not used for anything else.
++			 */
++			factory = trustall_ctx.getSocketFactory();
++			if (proxy_failure) {
++				socket = proxy_socket(factory);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++			if (debug_certs) {
++				dbg("trusturlCerts: " + trusturlCerts);
++				dbg("trustsrvCerts: " + trustsrvCerts);
++			}
++			if (trusturlCerts == null && cert_fail == null) {
++				cert_fail = "missing-certs";
++			}
++
++			try {
++				socket.startHandshake();
++
++				dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
++
++				/* grab the cert: */
++				try {
++					SSLSession sess = socket.getSession();
++					trustallCerts = sess.getPeerCertificates();
++				} catch (Exception e) {
++					throw new Exception("Could not get " + 
++					    "Peer Certificate");	
++				}
++				if (debug_certs) {
++					dbg("trustallCerts: " + trustallCerts);
++				}
++
++				if (viewer.trustAllVncCerts) {
++					dbg("viewer.trustAllVncCerts-3.  skipping dialog, trusting everything.");
++				} else if (! browser_cert_match()) {
++					/*
++					 * close socket now, we will reopen after
++					 * dialog if user agrees to use the cert.
++					 */
++					try {
++						OutputStream os = socket.getOutputStream();
++						os.write(getoutstr.getBytes());
++						socket.close();
++					} catch (Exception e) {
++						dbg("socket is grumpy!!");
++					}
++					socket = null;
++
++					/* dialog with user to accept cert or not: */
++
++					TrustDialog td= new TrustDialog(host, port,
++					    trustallCerts);
++
++					if (cert_fail == null) {
++						;
++					} else if (cert_fail.equals("user-view")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        You Asked to View the Certificate.";
++					} else if (cert_fail.equals("server-cert-mismatch")) {
++						/* this is now fatal error, see above. */
++						reason = "Reason for this Dialog:\n\n"
++						       + "        The VNC Server's Certificate does not match the Certificate\n"
++						       + "        specified in the supplied 'serverCert' Applet Parameter.";
++					} else if (cert_fail.equals("cert-mismatch")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        The VNC Server's Certificate does not match the Website's\n"
++						       + "        HTTPS Certificate (that you previously accepted; either\n"
++						       + "        manually or automatically via Certificate Authority.)";
++					} else if (cert_fail.equals("missing-certs")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        Not all Certificates could be obtained to check.";
++					}
++
++					if (! td.queryUser(reason)) {
++						String msg = "User decided against it.";
++						dbg(msg);
++						throw new IOException(msg);
++					}
++				}
++
++			} catch (Exception ehand2)  {
++				dbg("** Could not TrustAll Verify Server!");
++
++				throw new IOException(ehand2.getMessage());
++			}
++
++			/* reload again: */
++
++			if (socket != null) {
++				try {
++					socket.close();
++				} catch (Exception e) {
++					dbg("socket is grumpy!!!");
++				}
++				socket = null;
++			}
++
++			/*
++			 * Now connect a 3rd time, using the cert
++			 * retrieved during connection 2 (sadly, that
++			 * the user likely blindly agreed to...)
++			 */
++
++			factory = trustone_ctx.getSocketFactory();
++			if (proxy_failure) {
++				socket = proxy_socket(factory);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++			try {
++				socket.startHandshake();
++				dbg("TrustAll/TrustOne Server Connection Verified #3.");
++
++			} catch (Exception ehand3)  {
++				dbg("** Could not TrustAll/TrustOne Verify Server #3.");
++
++				throw new IOException(ehand3.getMessage());
++			}
++		}
++
++		/* we have socket (possibly null) at this point, so proceed: */
++
++		/* handle x11vnc GET=1, if applicable: */
++		if (socket != null && viewer.GET) {
++			String str = "GET ";
++			str += viewer.urlPrefix;
++			str += "/request.https.vnc.connection";
++			str += " HTTP/1.0\r\n";
++			str += "Pragma: No-Cache\r\n";
++			str += "\r\n";
++
++			System.out.println("sending: " + str);
++    			OutputStream os = socket.getOutputStream();
++			String type = "os";
++
++			if (type == "os") {
++				os.write(str.getBytes());
++				os.flush();
++				System.out.println("used OutputStream");
++			} else if (type == "bs") {
++				BufferedOutputStream bs = new BufferedOutputStream(os);
++				bs.write(str.getBytes());
++				bs.flush();
++				System.out.println("used BufferedOutputStream");
++			} else if (type == "ds") {
++				DataOutputStream ds = new DataOutputStream(os);
++				ds.write(str.getBytes());
++				ds.flush();
++				System.out.println("used DataOutputStream");
++			}
++			if (false) {
++				String rep = "";
++				DataInputStream is = new DataInputStream(
++				    new BufferedInputStream(socket.getInputStream(), 16384));
++				while (true) {
++					rep += readline(is);
++					if (rep.indexOf("\r\n\r\n") >= 0) {
++						break;
++					}
++				}
++				System.out.println("rep: " + rep);
++			}
++		}
++
++		dbg("SSL returning socket to caller.");
++		dbg("");
++
++		/* could be null, let caller handle that. */
++		return (Socket) socket;
++	}
++
++	boolean browser_cert_match() {
++		String msg = "Browser URL accept previously accepted cert";
++
++		if (user_wants_to_see_cert) {
++			return false;
++		}
++
++		if (viewer.serverCert != null || trustsrvCerts != null) {
++			if (cert_fail == null) {
++				cert_fail = "server-cert-mismatch";
++			}
++		}
++		if (trustallCerts != null && trusturlCerts != null) {
++		    if (trustallCerts.length == trusturlCerts.length) {
++			boolean ok = true;
++			/* check toath trustallCerts (socket) equals trusturlCerts (browser) */
++			for (int i = 0; i < trusturlCerts.length; i++)  {
++				if (! trustallCerts[i].equals(trusturlCerts[i])) {
++					dbg("BCM: cert mismatch at i=" + i);
++					dbg("BCM: cert mismatch  url" + trusturlCerts[i]);
++					dbg("BCM: cert mismatch  all" + trustallCerts[i]);
++					ok = false;
++				}
++			}
++			if (ok) {
++				System.out.println(msg);
++				if (cert_fail == null) {
++					cert_fail = "did-not-fail";
++				}
++				return true;
++			} else {
++				if (cert_fail == null) {
++					cert_fail = "cert-mismatch";
++				}
++				return false;
++			}
++		    }
++		}
++		if (cert_fail == null) {
++			cert_fail = "missing-certs";
++		}
++		return false;
++	}
++
++	private void dbg(String s) {
++		if (debug) {
++			System.out.println(s);
++		}
++	}
++
++	private int gint(String s) {
++		int n = -1;
++		try {
++			Integer I = new Integer(s);
++			n = I.intValue();
++		} catch (Exception ex) {
++			return -1;
++		}
++		return n;
++	}
++
++	/* this will do the proxy CONNECT negotiation and hook us up.  */
++
++	private void proxy_helper(String proxyHost, int proxyPort) {
++
++		boolean proxy_auth = false;
++		String proxy_auth_basic_realm = "";
++		String hp = host + ":" + port;
++		dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
++
++		/* we loop here a few times trying for the password case */
++		for (int k=0; k < 2; k++) {
++			dbg("proxy_in_use psocket: " + k);
++
++			if (proxySock != null) {
++				try {
++					proxySock.close();
++				} catch (Exception e) {
++					dbg("proxy socket is grumpy.");
++				}
++			}
++
++			proxySock = psocket(proxyHost, proxyPort);
++			if (proxySock == null) {
++				dbg("1-a sadly, returning a null socket");
++				return;
++			}
++
++			String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
++			    + "Host: " + hp + "\r\n";
++
++			dbg("requesting via proxy: " + req1);
++
++			if (proxy_auth) {
++				if (proxy_auth_string == null) {
++					ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm);
++					pp.queryUser();
++					proxy_auth_string = pp.getAuth();
++				}
++				//dbg("auth1: " + proxy_auth_string);
++
++				String auth2 = Base64Coder.encodeString(proxy_auth_string);
++				//dbg("auth2: " + auth2);
++
++				req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
++				//dbg("req1: " + req1);
++
++				dbg("added Proxy-Authorization: Basic ... to request");
++			}
++			req1 += "\r\n";
++
++			try {
++				proxy_os.write(req1.getBytes());
++				String reply = readline(proxy_is);
++
++				dbg("proxy replied: " + reply.trim());
++
++				if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) {
++					proxy_auth = true;
++					proxySock.close();
++				} else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++					proxySock.close();
++					proxySock = psocket(proxyHost, proxyPort);
++					if (proxySock == null) {
++						dbg("2-a sadly, returning a null socket");
++						return;
++					}
++				}
++			} catch(Exception e) {
++				dbg("some proxy socket problem: " + e.getMessage());
++			}
++
++			/* read the rest of the HTTP headers */
++			while (true) {
++				String line = readline(proxy_is);
++				dbg("proxy line: " + line.trim());
++				if (proxy_auth) {
++					String uc = line.toLowerCase();
++					if (uc.indexOf("proxy-authenticate:") == 0) {
++						if (uc.indexOf(" basic ") >= 0) {
++							int idx = uc.indexOf(" realm");
++							if (idx >= 0) {
++								proxy_auth_basic_realm = uc.substring(idx+1);
++							}
++						}
++					}
++				}
++				if (line.equals("\r\n") || line.equals("\n")) {
++					break;
++				}
++			}
++			if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
++				/* we only try once for the non-password case: */
++				break;
++			}
++		}
++	}
++
++	public SSLSocket proxy_socket(SSLSocketFactory factory) {
++		Properties props = null;
++		String proxyHost = null;
++		int proxyPort = 0;
++		String proxyHost_nossl = null;
++		int proxyPort_nossl = 0;
++		String str;
++
++		/* see if we can guess the proxy info from Properties: */
++		try {
++			props = System.getProperties();
++		} catch (Exception e) {
++			/* sandboxed applet might not be able to read it. */
++			dbg("props failed: " + e.getMessage());
++		}
++		if (viewer.proxyHost != null) {
++			dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters.");
++			proxyHost = viewer.proxyHost;
++			if (viewer.proxyPort != null) {
++				proxyPort = gint(viewer.proxyPort);
++			} else {
++				proxyPort = 8080;
++			}
++			
++		} else if (props != null) {
++			dbg("\n---------------\nAll props:");
++			props.list(System.out);
++			dbg("\n---------------\n\n");
++
++			/* scrape throught properties looking for proxy info: */
++
++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++				String s = (String) e.nextElement();
++				String v = System.getProperty(s);
++				String s2 = s.toLowerCase();
++				String v2 = v.toLowerCase();
++
++				if (s2.indexOf("proxy.https.host") >= 0) {
++					proxyHost = v2;
++					continue;
++				}
++				if (s2.indexOf("proxy.https.port") >= 0) {
++					proxyPort = gint(v2);
++					continue;
++				}
++				if (s2.indexOf("proxy.http.host") >= 0) {
++					proxyHost_nossl = v2;
++					continue;
++				}
++				if (s2.indexOf("proxy.http.port") >= 0) {
++					proxyPort_nossl = gint(v2);
++					continue;
++				}
++			}
++
++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++				String s = (String) e.nextElement();
++				String v = System.getProperty(s);
++				String s2 = s.toLowerCase();
++				String v2 = v.toLowerCase();
++
++				if (proxyHost != null && proxyPort > 0) {
++					break;
++				}
++
++				// look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082
++				if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) {
++					continue;
++				}
++				if (v2.indexOf("http") < 0) {
++					continue;
++				}
++
++				String[] pieces = v.split("[,;]");
++				for (int i = 0; i < pieces.length; i++) {
++					String p = pieces[i];
++					int j = p.indexOf("https");
++					if (j < 0) {
++						j = p.indexOf("http");
++						if (j < 0) {
++							continue;
++						}
++					}
++					j = p.indexOf("=", j);
++					if (j < 0) {
++						continue;
++					}
++					p = p.substring(j+1);
++					String [] hp = p.split(":");
++					if (hp.length != 2) {
++						continue;
++					}
++					if (hp[0].length() > 1 && hp[1].length() > 1) {
++
++						proxyPort = gint(hp[1]);
++						if (proxyPort < 0) {
++							continue;
++						}
++						proxyHost = new String(hp[0]);
++						break;
++					}
++				}
++			}
++		}
++		if (proxyHost != null) {
++			if (proxyHost_nossl != null && proxyPort_nossl > 0) {
++				dbg("Using http proxy info instead of https.");
++				proxyHost = proxyHost_nossl;
++				proxyPort = proxyPort_nossl;
++			}
++		}
++
++		if (proxy_in_use) {
++			if (proxy_dialog_host != null && proxy_dialog_port > 0) {
++				proxyHost = proxy_dialog_host;
++				proxyPort = proxy_dialog_port;
++			}
++			if (proxyHost != null) {
++				dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort);
++			} else {
++				/* ask user to help us: */
++				ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort);
++				pd.queryUser();
++				proxyHost = pd.getHost(); 
++				proxyPort = pd.getPort();
++				proxy_dialog_host = new String(proxyHost);
++				proxy_dialog_port = proxyPort;
++				dbg("User said host: " + pd.getHost() + " port: " + pd.getPort());
++			}
++
++			proxy_helper(proxyHost, proxyPort);
++			if (proxySock == null) {
++				return null;
++			}
++		} else if (viewer.CONNECT != null) {
++			dbg("viewer.CONNECT psocket:");
++			proxySock = psocket(host, port);
++			if (proxySock == null) {
++				dbg("1-b sadly, returning a null socket");
++				return null;
++			}
++		}
++		
++		if (viewer.CONNECT != null) {
++			String hp = viewer.CONNECT;
++			String req2 = "CONNECT " + hp + " HTTP/1.1\r\n"
++			    + "Host: " + hp + "\r\n\r\n";
++
++			dbg("requesting2: " + req2);
++
++			try {
++				proxy_os.write(req2.getBytes());
++				String reply = readline(proxy_is);
++
++				dbg("proxy replied2: " + reply.trim());
++
++				if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++					proxySock.close();
++					proxySock = psocket(proxyHost, proxyPort);
++					if (proxySock == null) {
++						dbg("2-b sadly, returning a null socket");
++						return null;
++					}
++				}
++			} catch(Exception e) {
++				dbg("proxy socket problem-2: " + e.getMessage());
++			}
++
++			while (true) {
++				String line = readline(proxy_is);
++				dbg("proxy line2: " + line.trim());
++				if (line.equals("\r\n") || line.equals("\n")) {
++					break;
++				}
++			}
++		}
++
++		Socket sslsock = null;
++		try {
++			sslsock = factory.createSocket(proxySock, host, port, true);
++		} catch(Exception e) {
++			dbg("sslsock prob: " + e.getMessage());
++			dbg("3 sadly, returning a null socket");
++		}
++
++		return (SSLSocket) sslsock;
++	}
++
++	Socket psocket(String h, int p) {
++		Socket psock = null;
++		try {
++			psock = new Socket(h, p);
++			proxy_is = new DataInputStream(new BufferedInputStream(
++			    psock.getInputStream(), 16384));
++			proxy_os = psock.getOutputStream();
++		} catch(Exception e) {
++			dbg("psocket prob: " + e.getMessage());
++			return null;
++		}
++
++		return psock;
++	}
++
++	String readline(DataInputStream i) {
++		byte[] ba = new byte[1];
++		String s = new String("");
++		ba[0] = 0;
++		try {
++			while (ba[0] != 0xa) {
++				ba[0] = (byte) i.readUnsignedByte();
++				s += new String(ba);
++			}
++		} catch (Exception e) {
++			;
++		}
++		return s;
++	}
++}
++
++class TrustDialog implements ActionListener {
++	String msg, host, text;
++	int port;
++	java.security.cert.Certificate[] trustallCerts = null;
++	boolean viewing_cert = false;
++	boolean trust_this_session = false;
++
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok, cancel, viewcert;
++	TextArea textarea;
++	Checkbox accept, deny;
++	Dialog dialog;
++
++	String s1 = "Accept this certificate temporarily for this session";
++	String s2 = "Do not accept this certificate and do not connect to"
++	    + " this VNC server";
++	String ln = "\n---------------------------------------------------\n\n";
++		
++	TrustDialog (String h, int p, java.security.cert.Certificate[] s) {
++		host = h;
++		port = p;
++		trustallCerts = s;
++
++		msg = "VNC Server " + host + ":" + port + " Not Verified";
++	}
++
++	public boolean queryUser(String reason) {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame(msg);
++
++		dialog = new Dialog(frame, true);
++
++		String infostr = "";
++		if (trustallCerts.length == 1) {
++			CertInfo ci = new CertInfo(trustallCerts[0]);
++			infostr = ci.get_certinfo("all");
++		}
++		if (reason != null) {
++			reason += "\n\n";
++		}
++
++		text = "\n" 
+++ "Unable to verify the identity of\n"
+++ "\n"
+++ "        " + host + ":" + port + "\n" 
+++ "\n"
+++ infostr
+++ "\n"
+++ "as a trusted VNC server.\n"
+++ "\n"
+++ reason
+++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
+++ "is due to one of the following:\n"
+++ "\n"
+++ " - Your requesting to View the Certificate before accepting.\n"
+++ "\n"
+++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
+++ "   Authority not recognized by your Web Browser or Java Plugin runtime.\n"
+++ "\n"
+++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
+++ "   the Apache Web server has a certificate *different* from the VNC server's.\n"
+++ "\n"
+++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
+++ "   obtained by this applet to compare the VNC Server Certificate against.\n"
+++ "\n"
+++ " - The VNC Server's Certificate does not match the one specified in the\n"
+++ "   supplied 'serverCert' Java Applet Parameter.\n"
+++ "\n"
+++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
+++ "   to connect to.  (Wouldn't that be exciting!!)\n"
+++ "\n"
+++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
+++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
+++ "automatically authenticate this VNC Server.\n"
+++ "\n"
+++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
+++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
+++ "certificate (except for the Apache portal case above where they don't match.)\n"
+++ "\n"
+++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
+++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
+++ "and thereby see no dialog from this VNC Viewer applet.\n"
++;
++
++		/* the accept / do-not-accept radio buttons: */
++		CheckboxGroup checkbox = new CheckboxGroup();
++		accept = new Checkbox(s1, true, checkbox);
++		deny   = new Checkbox(s2, false, checkbox);
++
++		/* put the checkboxes in a panel: */
++		Panel check = new Panel();
++		check.setLayout(new GridLayout(2, 1));
++
++		check.add(accept);
++		check.add(deny);
++
++		/* make the 3 buttons: */
++		ok = new Button("OK");
++		cancel = new Button("Cancel");
++		viewcert = new Button("View Certificate");
++
++		ok.addActionListener(this);
++		cancel.addActionListener(this);
++		viewcert.addActionListener(this);
++
++		/* put the buttons in their own panel: */
++		Panel buttonrow = new Panel();
++		buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT));
++		buttonrow.add(viewcert);
++		buttonrow.add(ok);
++		buttonrow.add(cancel);
++
++		/* label at the top: */
++		Label label = new Label(msg, Label.CENTER);
++		label.setFont(new Font("Helvetica", Font.BOLD, 16));
++
++		/* textarea in the middle */
++		textarea = new TextArea(text, 38, 64,
++		    TextArea.SCROLLBARS_VERTICAL_ONLY);
++		textarea.setEditable(false);
++
++		/* put the two panels in their own panel at bottom: */
++		Panel bot = new Panel();
++		bot.setLayout(new GridLayout(2, 1));
++		bot.add(check);
++		bot.add(buttonrow);
++
++		/* now arrange things inside the dialog: */
++		dialog.setLayout(new BorderLayout());
++
++		dialog.add("North", label);
++		dialog.add("South", bot);
++		dialog.add("Center", textarea);
++
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++
++		return trust_this_session;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++
++		if (evt.getSource() == viewcert) {
++			/* View Certificate button clicked */
++			if (viewing_cert) {
++				/* show the original info text: */
++				textarea.setText(text);
++				viewcert.setLabel("View Certificate");
++				viewing_cert = false;
++			} else {
++				int i;
++				/* show all (likely just one) certs: */
++				textarea.setText("");
++				for (i=0; i < trustallCerts.length; i++) {
++					int j = i + 1;
++					textarea.append("Certificate[" +
++					    j + "]\n\n");
++					textarea.append(
++					    trustallCerts[i].toString());
++					textarea.append(ln);
++				}
++				viewcert.setLabel("View Info");
++				viewing_cert = true;
++
++				textarea.setCaretPosition(0);
++			}
++
++		} else if (evt.getSource() == ok) {
++			/* OK button clicked */
++			if (accept.getState()) {
++				trust_this_session = true;
++			} else {
++				trust_this_session = false;
++			}
++			//dialog.dispose();
++			dialog.hide();
++
++		} else if (evt.getSource() == cancel) {
++			/* Cancel button clicked */
++			trust_this_session = false;
++
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++
++	String get_certinfo() {
++		String all = "";
++		String fields[] = {"CN", "OU", "O", "L", "C"};
++		int i;
++		if (trustallCerts.length < 1) {
++			all = "";
++			return all;
++		}
++		String cert = trustallCerts[0].toString();
++
++		/*
++		 * For now we simply scrape the cert string, there must
++		 * be an API for this... perhaps optionValue?
++		 */
++
++		for (i=0; i < fields.length; i++) {
++			int f, t, t1, t2;
++			String sub, mat = fields[i] + "=";
++			
++			f = cert.indexOf(mat, 0);
++			if (f > 0) {
++				t1 = cert.indexOf(", ", f);
++				t2 = cert.indexOf("\n", f);
++				if (t1 < 0 && t2 < 0) {
++					continue;
++				} else if (t1 < 0) {
++					t = t2;
++				} else if (t2 < 0) {
++					t = t1;
++				} else if (t1 < t2) {
++					t = t1;
++				} else {
++					t = t2;
++				}
++				if (t > f) {
++					sub = cert.substring(f, t);
++					all = all + "        " + sub + "\n";
++				}
++			}
++		}
++		return all;
++	}
++}
++
++class ProxyDialog implements ActionListener {
++	String guessedHost = null;
++	String guessedPort = null;
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok;
++	Dialog dialog;
++	TextField entry;
++	String reply = "";
++
++	ProxyDialog (String h, int p) {
++		guessedHost = h;
++		try {
++			guessedPort = Integer.toString(p);
++		} catch (Exception e) {
++			guessedPort = "8080";
++		}
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Need Proxy host:port");
++
++		dialog = new Dialog(frame, true);
++
++
++		Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER);
++		//label.setFont(new Font("Helvetica", Font.BOLD, 16));
++		entry = new TextField(30);
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		String guess = "";
++		if (guessedHost != null) {
++			guess = guessedHost + ":" + guessedPort;
++		}
++		entry.setText(guess);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry);
++		dialog.add("South", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return;
++	}
++
++	public String getHost() {
++		int i = reply.indexOf(":");
++		if (i < 0) {
++			return "unknown";
++		}
++		String h = reply.substring(0, i);
++		return h;
++	}
++
++	public int getPort() {
++		int i = reply.indexOf(":");
++		int p = 8080;
++		if (i < 0) {
++			return p;
++		}
++		i++;
++		String ps = reply.substring(i);
++		try {
++			Integer I = new Integer(ps);
++			p = I.intValue();
++		} catch (Exception e) {
++			;
++		}
++		return p;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply = entry.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class ProxyPasswdDialog implements ActionListener {
++	String guessedHost = null;
++	String guessedPort = null;
++	String guessedUser = null;
++	String guessedPasswd = null;
++	String realm = null;
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok;
++	Dialog dialog;
++	TextField entry1;
++	TextField entry2;
++	String reply1 = "";
++	String reply2 = "";
++
++	ProxyPasswdDialog (String h, int p, String realm) {
++		guessedHost = h;
++		try {
++			guessedPort = Integer.toString(p);
++		} catch (Exception e) {
++			guessedPort = "8080";
++		}
++		this.realm = realm;
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Proxy Requires Username and Password");
++
++		dialog = new Dialog(frame, true);
++
++		//Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER);
++		TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE);
++		entry1 = new TextField(30);
++		entry2 = new TextField(30);
++		entry2.setEchoChar('*');
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry1);
++		dialog.add("South",  entry2);
++		dialog.add("East", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return;
++	}
++
++	public String getAuth() {
++		return reply1 + ":" + reply2;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply1 = entry1.getText();
++			reply2 = entry2.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class ClientCertDialog implements ActionListener {
++
++	Button ok;
++	Dialog dialog;
++	TextField entry;
++	String reply = "";
++
++	ClientCertDialog() {
++		;
++	}
++
++	public String queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Enter SSL Client Cert+Key String");
++
++		dialog = new Dialog(frame, true);
++
++
++		Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER);
++		entry = new TextField(30);
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry);
++		dialog.add("South", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return reply;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply = entry.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class BrowserCertsDialog implements ActionListener {
++	Button yes, no;
++	Dialog dialog;
++	String vncServer;
++	String hostport;
++	public boolean showCertDialog = true;
++
++	BrowserCertsDialog(String serv, String hp) {
++		vncServer = serv;
++		hostport = hp;
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Use Browser/JVM Certs?");
++
++		dialog = new Dialog(frame, true);
++
++		String m = "";
++m += "\n";
++m += "This VNC Viewer applet does not have its own keystore to track\n";
++m += "SSL certificates, and so cannot authenticate the certificate\n";
++m += "of the VNC Server:\n";
++m += "\n";
++m += "        " + hostport + "\n\n        " + vncServer + "\n";
++m += "\n";
++m += "on its own.\n";
++m += "\n";
++m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
++m += "has previously accepted the same certificate.  You may have set\n";
++m += "this up permanently or just for this session, or the server\n";
++m += "certificate was signed by a CA cert that your Web Browser or\n";
++m += "Java VM Plugin has.\n";
++m += "\n";
++m += "If the VNC Server connection times out while you are reading this\n";
++m += "dialog, then restart the connection and try again.\n";
++m += "\n";
++m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
++m += "\n";
++
++		TextArea textarea = new TextArea(m, 22, 64,
++		    TextArea.SCROLLBARS_VERTICAL_ONLY);
++		textarea.setEditable(false);
++		yes = new Button("Yes");
++		yes.addActionListener(this);
++		no = new Button("No, Let Me See the Certificate.");
++		no.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", textarea);
++		dialog.add("Center", yes);
++		dialog.add("South", no);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til Yes or No pressed. */
++		System.out.println("done show()");
++		return;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == yes) {
++			showCertDialog = false;
++			//dialog.dispose();
++			dialog.hide();
++		} else if (evt.getSource() == no) {
++			showCertDialog = true;
++			//dialog.dispose();
++			dialog.hide();
++		}
++		System.out.println("done actionPerformed()");
++	}
++}
++
++class CertInfo {
++	String fields[] = {"CN", "OU", "O", "L", "C"};
++	java.security.cert.Certificate cert;
++	String certString = "";
++
++	CertInfo(java.security.cert.Certificate c) {
++		cert = c;
++		certString = cert.toString();
++	}
++	
++	String get_certinfo(String which) {
++		int i;
++		String cs = new String(certString);
++		String all = "";
++
++		/*
++		 * For now we simply scrape the cert string, there must
++		 * be an API for this... perhaps optionValue?
++		 */
++		for (i=0; i < fields.length; i++) {
++			int f, t, t1, t2;
++			String sub, mat = fields[i] + "=";
++			
++			f = cs.indexOf(mat, 0);
++			if (f > 0) {
++				t1 = cs.indexOf(", ", f);
++				t2 = cs.indexOf("\n", f);
++				if (t1 < 0 && t2 < 0) {
++					continue;
++				} else if (t1 < 0) {
++					t = t2;
++				} else if (t2 < 0) {
++					t = t1;
++				} else if (t1 < t2) {
++					t = t1;
++				} else {
++					t = t2;
++				}
++				if (t > f) {
++					sub = cs.substring(f, t);
++					all = all + "        " + sub + "\n";
++					if (which.equals(fields[i])) {
++						return sub;
++					}
++				}
++			}
++		}
++		if (which.equals("all")) {
++			return all;
++		} else {
++			return "";
++		}
++	}
++}
++
++class Base64Coder {
++
++	// Mapping table from 6-bit nibbles to Base64 characters.
++	private static char[]    map1 = new char[64];
++	   static {
++	      int i=0;
++	      for (char c='A'; c<='Z'; c++) map1[i++] = c;
++	      for (char c='a'; c<='z'; c++) map1[i++] = c;
++	      for (char c='0'; c<='9'; c++) map1[i++] = c;
++	      map1[i++] = '+'; map1[i++] = '/'; }
++
++	// Mapping table from Base64 characters to 6-bit nibbles.
++	private static byte[]    map2 = new byte[128];
++	   static {
++	      for (int i=0; i<map2.length; i++) map2[i] = -1;
++	      for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
++
++	/**
++	* Encodes a string into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param s  a String to be encoded.
++	* @return   A String with the Base64 encoded data.
++	*/
++	public static String encodeString (String s) {
++	   return new String(encode(s.getBytes())); }
++
++	/**
++	* Encodes a byte array into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param in  an array containing the data bytes to be encoded.
++	* @return    A character array with the Base64 encoded data.
++	*/
++	public static char[] encode (byte[] in) {
++	   return encode(in,in.length); }
++
++	/**
++	* Encodes a byte array into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param in   an array containing the data bytes to be encoded.
++	* @param iLen number of bytes to process in <code>in</code>.
++	* @return     A character array with the Base64 encoded data.
++	*/
++	public static char[] encode (byte[] in, int iLen) {
++	   int oDataLen = (iLen*4+2)/3;       // output length without padding
++	   int oLen = ((iLen+2)/3)*4;         // output length including padding
++	   char[] out = new char[oLen];
++	   int ip = 0;
++	   int op = 0;
++	   while (ip < iLen) {
++	      int i0 = in[ip++] & 0xff;
++	      int i1 = ip < iLen ? in[ip++] & 0xff : 0;
++	      int i2 = ip < iLen ? in[ip++] & 0xff : 0;
++	      int o0 = i0 >>> 2;
++	      int o1 = ((i0 &   3) << 4) | (i1 >>> 4);
++	      int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
++	      int o3 = i2 & 0x3F;
++	      out[op++] = map1[o0];
++	      out[op++] = map1[o1];
++	      out[op] = op < oDataLen ? map1[o2] : '='; op++;
++	      out[op] = op < oDataLen ? map1[o3] : '='; op++; }
++	   return out; }
++
++	/**
++	* Decodes a string from Base64 format.
++	* @param s  a Base64 String to be decoded.
++	* @return   A String containing the decoded data.
++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static String decodeString (String s) {
++	   return new String(decode(s)); }
++
++	/**
++	* Decodes a byte array from Base64 format.
++	* @param s  a Base64 String to be decoded.
++	* @return   An array containing the decoded data bytes.
++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static byte[] decode (String s) {
++	   return decode(s.toCharArray()); }
++
++	/**
++	* Decodes a byte array from Base64 format.
++	* No blanks or line breaks are allowed within the Base64 encoded data.
++	* @param in  a character array containing the Base64 encoded data.
++	* @return    An array containing the decoded data bytes.
++	* @throws    IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static byte[] decode (char[] in) {
++	   int iLen = in.length;
++	   if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
++	   while (iLen > 0 && in[iLen-1] == '=') iLen--;
++	   int oLen = (iLen*3) / 4;
++	   byte[] out = new byte[oLen];
++	   int ip = 0;
++	   int op = 0;
++	   while (ip < iLen) {
++	      int i0 = in[ip++];
++	      int i1 = in[ip++];
++	      int i2 = ip < iLen ? in[ip++] : 'A';
++	      int i3 = ip < iLen ? in[ip++] : 'A';
++	      if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++	      int b0 = map2[i0];
++	      int b1 = map2[i1];
++	      int b2 = map2[i2];
++	      int b3 = map2[i3];
++	      if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++	      int o0 = ( b0       <<2) | (b1>>>4);
++	      int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
++	      int o2 = ((b2 &   3)<<6) |  b3;
++	      out[op++] = (byte)o0;
++	      if (op<oLen) out[op++] = (byte)o1;
++	      if (op<oLen) out[op++] = (byte)o2; }
++	   return out; }
++
++	// Dummy constructor.
++	private Base64Coder() {}
++
++}
+diff -Naur vnc_javasrc.orig/VncCanvas.java vnc_javasrc/VncCanvas.java
+--- vnc_javasrc.orig/VncCanvas.java	2004-10-10 02:15:54.000000000 -0400
++++ vnc_javasrc/VncCanvas.java	2010-11-30 21:01:15.000000000 -0500
+@@ -28,13 +28,14 @@
+ import java.lang.*;
+ import java.util.zip.*;
+ 
++import java.util.Collections;
+ 
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+ //
+ 
+ class VncCanvas extends Canvas
+-  implements KeyListener, MouseListener, MouseMotionListener {
++  implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+ 
+   VncViewer viewer;
+   RfbProto rfb;
+@@ -81,6 +82,20 @@
+     cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+     cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+ 
++    // kludge to not show any Java cursor in the canvas since we are
++    // showing the soft cursor (should be a user setting...)
++    Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++        Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++        "dot");
++    this.setCursor(dot);
++
++    // while we are at it... get rid of the keyboard traversals that
++    // make it so we can't type a Tab character:
++    this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++        Collections.EMPTY_SET);
++    this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++        Collections.EMPTY_SET);
++
+     colors = new Color[256];
+     for (int i = 0; i < 256; i++)
+       colors[i] = new Color(cm8.getRGB(i));
+@@ -169,6 +184,7 @@
+       inputEnabled = true;
+       addMouseListener(this);
+       addMouseMotionListener(this);
++      addMouseWheelListener(this);
+       if (viewer.showControls) {
+ 	viewer.buttonPanel.enableRemoteAccessControls(true);
+       }
+@@ -177,6 +193,7 @@
+       inputEnabled = false;
+       removeMouseListener(this);
+       removeMouseMotionListener(this);
++      removeMouseWheelListener(this);
+       if (viewer.showControls) {
+ 	viewer.buttonPanel.enableRemoteAccessControls(false);
+       }
+@@ -1190,6 +1207,9 @@
+   public void mouseDragged(MouseEvent evt) {
+     processLocalMouseEvent(evt, true);
+   }
++  public void mouseWheelMoved(MouseWheelEvent evt) {
++    processLocalMouseWheelEvent(evt);
++  }
+ 
+   public void processLocalKeyEvent(KeyEvent evt) {
+     if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1221,6 +1241,19 @@
+     evt.consume();
+   }
+ 
++  public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++    if (viewer.rfb != null && rfb.inNormalProtocol) {
++      synchronized(rfb) {
++	try {
++	  rfb.writeWheelEvent(evt);
++	} catch (Exception e) {
++	  e.printStackTrace();
++	}
++	rfb.notify();
++      }
++    }
++  }
++
+   public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+     if (viewer.rfb != null && rfb.inNormalProtocol) {
+       if (moved) {
+@@ -1387,9 +1420,9 @@
+ 		result = cm8.getRGB(pixBuf[i]);
+ 	      } else {
+ 		result = 0xFF000000 |
+-		  (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+-		  (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+-		  (pixBuf[i * 4 + 3] & 0xFF);
++		  (pixBuf[i * 4 + 2] & 0xFF) << 16 |
++		  (pixBuf[i * 4 + 1] & 0xFF) << 8 |
++		  (pixBuf[i * 4 + 0] & 0xFF);
+ 	      }
+ 	    } else {
+ 	      result = 0;	// Transparent pixel
+@@ -1403,9 +1436,9 @@
+ 	      result = cm8.getRGB(pixBuf[i]);
+ 	    } else {
+ 	      result = 0xFF000000 |
+-		(pixBuf[i * 4 + 1] & 0xFF) << 16 |
+-		(pixBuf[i * 4 + 2] & 0xFF) << 8 |
+-		(pixBuf[i * 4 + 3] & 0xFF);
++		(pixBuf[i * 4 + 2] & 0xFF) << 16 |
++		(pixBuf[i * 4 + 1] & 0xFF) << 8 |
++		(pixBuf[i * 4 + 0] & 0xFF);
+ 	    }
+ 	  } else {
+ 	    result = 0;		// Transparent pixel
+diff -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncViewer.java
+--- vnc_javasrc.orig/VncViewer.java	2004-03-04 08:34:25.000000000 -0500
++++ vnc_javasrc/VncViewer.java	2010-03-27 17:57:04.000000000 -0400
+@@ -29,6 +29,7 @@
+ import java.awt.event.*;
+ import java.io.*;
+ import java.net.*;
++import java.util.*;
+ 
+ public class VncViewer extends java.applet.Applet
+   implements java.lang.Runnable, WindowListener {
+@@ -80,7 +81,7 @@
+   // Variables read from parameter values.
+   String socketFactory;
+   String host;
+-  int port;
++  int port, vncserverport;
+   boolean showControls;
+   boolean offerRelogin;
+   boolean showOfflineDesktop;
+@@ -88,6 +89,24 @@
+   int deferCursorUpdates;
+   int deferUpdateRequests;
+ 
++  boolean disableSSL;
++  boolean GET;
++  String CONNECT;
++  String urlPrefix;
++  String httpsPort;
++  String oneTimeKey;
++  String serverCert;
++  String proxyHost;
++  String proxyPort;
++  boolean forceProxy;
++  boolean ignoreProxy;
++  boolean trustAllVncCerts;
++  boolean trustUrlVncCert;
++  boolean debugCerts;
++  boolean debugKeyboard;
++  boolean mapF5_to_atsign;
++  boolean forbid_Ctrl_Alt;
++
+   // Reference to this applet for inter-applet communication.
+   public static java.applet.Applet refApplet;
+ 
+@@ -282,10 +301,24 @@
+       validate();
+     }
+ 
+-    while (!tryAuthenticate()) {
+-      authenticator.retry();
+-      authenticatorUnixLogin.retry();
+-    }
++	if (false) {
++		/* a bug on retries: 'Error: bad position: 8' sun.awt.X11.XTextFieldPeer.setCaretPosition(XTextFieldPeer.java:215) */
++		while (!tryAuthenticate()) {
++			authenticator.retry();
++			authenticatorUnixLogin.retry();
++		}
++	} else {
++		/* just try once and not forever... */
++		if (!tryAuthenticate()) {
++    			showConnectionStatus("Authentication Failed.");
++			showMessage("Authentication Failed.");
++			if (!offerRelogin) {
++				fatalError("auth failed.");
++			}
++		} else {
++			//showConnectionStatus("Authentication OK.");
++		}
++	}
+   }
+ 
+ 
+@@ -428,7 +461,10 @@
+     gbc.ipadx = 100;
+     gbc.ipady = 50;
+     gridbag.setConstraints(authPanel, gbc);
++    try {
+     vncContainer.add(authPanel);
++    } catch (Exception e) {
++    }
+ 
+     if (inSeparateFrame) {
+       vncFrame.pack();
+@@ -590,9 +626,28 @@
+ 	fatalError("HOST parameter not specified");
+       }
+     }
++    Date d = new Date();
++    System.out.println("-\nSSL VNC Java Applet starting.  " + d);
+ 
+-    String str = readParameter("PORT", true);
+-    port = Integer.parseInt(str);
++    port = 0;
++    String str = readParameter("PORT", false);
++    if (str != null) {
++	port = Integer.parseInt(str);
++    }
++    // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall).
++    vncserverport = 0;
++    str = readParameter("VNCSERVERPORT", false);
++    if (str != null) {
++	vncserverport = Integer.parseInt(str);
++    }
++    if (port == 0 && vncserverport == 0) {
++	fatalError("Neither PORT nor VNCSERVERPORT parameters specified");
++    }
++    if (port == 0) {
++	// Nevertheless, fall back to vncserverport if we have to.
++	System.out.println("using vncserverport: '" + vncserverport + "' for PORT.");
++	port = vncserverport;
++    }
+ 
+     if (inAnApplet) {
+       str = readParameter("Open New Window", false);
+@@ -626,6 +681,121 @@
+ 
+     // SocketFactory.
+     socketFactory = readParameter("SocketFactory", false);
++
++    // SSL
++    disableSSL = false;
++    str = readParameter("DisableSSL", false);
++    if (str != null && str.equalsIgnoreCase("Yes"))
++      disableSSL = true;
++
++    httpsPort = readParameter("httpsPort", false);
++
++    // Extra GET, CONNECT string:
++    CONNECT = readParameter("CONNECT", false);
++    if (CONNECT != null) {
++	CONNECT = CONNECT.replaceAll(" ", ":");
++    }
++
++    GET = false;
++    str = readParameter("GET", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++      GET = true;
++    }
++    if (str != null && str.equalsIgnoreCase("1")) {
++      GET = true;
++    }
++
++    urlPrefix = readParameter("urlPrefix", false);
++    if (urlPrefix != null) {
++	urlPrefix = urlPrefix.replaceAll("%2F", "/");
++	urlPrefix = urlPrefix.replaceAll("%2f", "/");
++	urlPrefix = urlPrefix.replaceAll("_2F_", "/");
++	if (urlPrefix.indexOf("/") != 0) {
++		urlPrefix = "/" + urlPrefix;
++	}
++    } else {
++    	urlPrefix = "";
++    }
++    System.out.println("urlPrefix: '" + urlPrefix + "'");
++
++    oneTimeKey = readParameter("oneTimeKey", false);
++    if (oneTimeKey != null) {
++    	System.out.println("oneTimeKey is set.");
++    }
++
++    serverCert = readParameter("serverCert", false);
++    if (serverCert != null) {
++    	System.out.println("serverCert is set.");
++    }
++
++    forceProxy = false;
++    proxyHost = null;
++    proxyPort = null;
++    str = readParameter("forceProxy", false);
++    if (str != null) {
++	    if (str.equalsIgnoreCase("Yes")) {
++		forceProxy = true;
++	    } else if (str.equalsIgnoreCase("No")) {
++		forceProxy = false;
++	    } else {
++		forceProxy = true;
++		String[] pieces = str.split(" ");
++		proxyHost = new String(pieces[0]);
++		if (pieces.length >= 2) {
++			proxyPort = new String(pieces[1]);
++		} else {
++			proxyPort = new String("8080");
++		}
++	    }
++    }
++    str = readParameter("proxyHost", false);
++    if (str != null) {
++	proxyHost = new String(str);
++    }
++    str = readParameter("proxyPort", false);
++    if (str != null) {
++	proxyPort = new String(str);
++    }
++    if (proxyHost != null && proxyPort == null) {
++    	proxyPort = new String("8080");
++    }
++
++    ignoreProxy = false;
++    str = readParameter("ignoreProxy", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	ignoreProxy = true;
++    }
++
++    trustAllVncCerts = false;
++    str = readParameter("trustAllVncCerts", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	trustAllVncCerts = true;
++    }
++    trustUrlVncCert = false;
++    str = readParameter("trustUrlVncCert", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	trustUrlVncCert = true;
++    }
++    debugCerts = false;
++    str = readParameter("debugCerts", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	debugCerts = true;
++    }
++    debugKeyboard = false;
++    str = readParameter("debugKeyboard", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	debugKeyboard = true;
++    }
++    mapF5_to_atsign = false;
++    str = readParameter("mapF5_to_atsign", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	mapF5_to_atsign = true;
++    }
++    forbid_Ctrl_Alt = false;
++    str = readParameter("forbid_Ctrl_Alt", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	forbid_Ctrl_Alt = true;
++    }
+   }
+ 
+   public String readParameter(String name, boolean required) {

+ 5494 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch

@@ -0,0 +1,5494 @@
+diff -Naur JavaViewer.orig/ButtonPanel.java JavaViewer/ButtonPanel.java
+--- JavaViewer.orig/ButtonPanel.java	2004-12-12 20:51:02.000000000 -0500
++++ JavaViewer/ButtonPanel.java	2007-05-31 15:40:45.000000000 -0400
+@@ -43,30 +43,36 @@
+     viewer = v;
+ 
+     setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+-    disconnectButton = new Button("Disconnect");
++	if (v.ftpOnly) {
++		disconnectButton = new Button("Quit");
++	} else {
++		disconnectButton = new Button("Close");
++	}
+     disconnectButton.setEnabled(false);
+     add(disconnectButton);
+     disconnectButton.addActionListener(this);
+-    optionsButton = new Button("Options");
+-    add(optionsButton);
+-    optionsButton.addActionListener(this);
+-    clipboardButton = new Button("Clipboard");
+-    clipboardButton.setEnabled(false);
+-    add(clipboardButton);
+-    clipboardButton.addActionListener(this);
+-    if (viewer.rec != null) {
+-      recordButton = new Button("Record");
+-      add(recordButton);
+-      recordButton.addActionListener(this);
+-    }
+-    ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
+-    ctrlAltDelButton.setEnabled(false);
+-    add(ctrlAltDelButton);
+-    ctrlAltDelButton.addActionListener(this);
+-    refreshButton = new Button("Refresh");
+-    refreshButton.setEnabled(false);
+-    add(refreshButton);
+-    refreshButton.addActionListener(this);
++	if (!v.ftpOnly) {
++		optionsButton = new Button("Options");
++		add(optionsButton);
++		optionsButton.addActionListener(this);
++		clipboardButton = new Button("Clipboard");
++		clipboardButton.setEnabled(false);
++		add(clipboardButton);
++		clipboardButton.addActionListener(this);
++		if (viewer.rec != null) {
++			recordButton = new Button("Record");
++			add(recordButton);
++			recordButton.addActionListener(this);
++		}
++		ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
++		ctrlAltDelButton.setEnabled(false);
++		add(ctrlAltDelButton);
++		ctrlAltDelButton.addActionListener(this);
++		refreshButton = new Button("Refresh");
++		refreshButton.setEnabled(false);
++		add(refreshButton);
++		refreshButton.addActionListener(this);
++	}
+     ftpButton = new Button("File Transfer");
+     ftpButton.setEnabled(false);
+     add(ftpButton);
+@@ -79,9 +85,10 @@
+ 
+   public void enableButtons() {
+     disconnectButton.setEnabled(true);
++    ftpButton.setEnabled(true);
++    if (viewer.ftpOnly) {return;}
+     clipboardButton.setEnabled(true);
+     refreshButton.setEnabled(true);
+-    ftpButton.setEnabled(true);
+   }
+ 
+   //
+@@ -89,6 +96,9 @@
+   //
+ 
+   public void disableButtonsOnDisconnect() {
++    ftpButton.setEnabled(false);
++    if (viewer.ftpOnly) {return;}
++
+     remove(disconnectButton);
+     disconnectButton = new Button("Hide desktop");
+     disconnectButton.setEnabled(true);
+@@ -99,7 +109,6 @@
+     clipboardButton.setEnabled(false);
+     ctrlAltDelButton.setEnabled(false);
+     refreshButton.setEnabled(false);
+-    ftpButton.setEnabled(false);
+ 
+     validate();
+   }
+@@ -110,6 +119,7 @@
+   //
+ 
+   public void enableRemoteAccessControls(boolean enable) {
++    if (viewer.ftpOnly) {return;}
+     ctrlAltDelButton.setEnabled(enable);
+   }
+ 
+@@ -163,9 +173,19 @@
+     }
+     else if (evt.getSource() == ftpButton)
+     {
+-		viewer.ftp.setVisible(!viewer.ftp.isVisible());
++// begin runge/x11vnc
++		if (viewer.ftpOnly) {
++			viewer.vncFrame.setVisible(false);
++		}
++		viewer.ftp.setSavedLocations();
++		if (viewer.ftp.isVisible()) {
++			viewer.ftp.doClose();
++		} else {
++			viewer.ftp.doOpen();
++		}
++// end runge/x11vnc
+ 		viewer.rfb.readServerDriveList();
+-	
++
+     }
+   }
+ }
+diff -Naur JavaViewer.orig/FTPFrame.java JavaViewer/FTPFrame.java
+--- JavaViewer.orig/FTPFrame.java	2005-03-15 23:53:14.000000000 -0500
++++ JavaViewer/FTPFrame.java	2009-01-13 09:48:30.000000000 -0500
+@@ -24,8 +24,17 @@
+ import java.io.*;
+ import java.util.ArrayList;
+ import java.util.Vector;
++import java.util.Date;
+ import javax.swing.*;
+ 
++import java.nio.ByteBuffer;
++import java.nio.CharBuffer;
++import java.nio.charset.*;
++
++// begin runge/x11vnc
++import java.util.Arrays;
++// end runge/x11vnc
++
+ 
+ /*
+  * Created on Feb 25, 2004
+@@ -74,12 +83,31 @@
+ 	public javax.swing.JTextField connectionStatus = null;
+ 	public boolean updateDriveList;
+ 	private Vector remoteList = null;
++	private Vector remoteListInfo = null;
+ 	private Vector localList = null;
++	private Vector localListInfo = null;
+ 	private File currentLocalDirectory = null;	// Holds the current local Directory
+ 	private File currentRemoteDirectory = null;	// Holds the current remote Directory
+ 	private File localSelection = null;		// Holds the currently selected local file  
+ 	private String remoteSelection = null;	// Holds the currently selected remote file
+ 	public String selectedTable = null;
++
++// begin runge/x11vnc
++	private javax.swing.JButton viewButton = null;
++	private javax.swing.JButton refreshButton = null;
++	public File saveLocalDirectory = null;
++	public long saveLocalDirectoryTime = 0;
++	public int saveLocalDirectoryCount = 0;
++	public String saveRemoteDirectory = null;
++	public long saveRemoteDirectoryTime = 0;
++	public int saveRemoteDirectoryCount = 0;
++	private boolean localCurrentIsDir = true;
++	private int lastRemoteIndex = -1;
++	private int lastLocalIndex = -1;
++	private boolean doingShortcutDir = false;
++	private boolean gotShortcutDir = false;
++	private boolean ignore_events = false;
++// end   runge/x11vnc
+ 	
+ //	 sf@2004 - Separate directories and files for better lisibility
+ 	private ArrayList DirsList;
+@@ -125,11 +153,61 @@
+ 	 
+ 	 void refreshRemoteLocation()
+ 	 {
++
++//System.out.println("refreshRemoteLocation1");
+ 		remoteList.clear();
++		remoteListInfo.clear();
+ 		remoteFileTable.setListData(remoteList);	
++System.out.println("refreshRemoteLocation '" + remoteLocation.getText() + "'");	// runge/x11vnc
+ 		viewer.rfb.readServerDirectory(remoteLocation.getText());
+ 	 }
+ 	 
++// begin runge/x11vnc
++	 public void setSavedLocations() {
++		saveLocalDirectory = currentLocalDirectory;
++		saveLocalDirectoryTime = System.currentTimeMillis();
++		saveLocalDirectoryCount = 0;
++
++		if (remoteLocation != null) {
++			saveRemoteDirectory = remoteLocation.getText();
++System.out.println("RemoteSave '" + saveRemoteDirectory + "'");
++		}
++		saveRemoteDirectoryTime = System.currentTimeMillis();
++		saveRemoteDirectoryCount = 0;
++	 }
++
++	private File saveLocalHack(File dir) {
++		saveLocalDirectoryCount++;
++//System.out.println("L " + saveLocalDirectoryCount + " dt: " + (System.currentTimeMillis() - saveLocalDirectoryTime) + " - " + saveLocalDirectory);
++		if (System.currentTimeMillis() > saveLocalDirectoryTime + 2000 || saveLocalDirectoryCount > 2) {
++			saveLocalDirectory = null;
++		}
++		if (saveLocalDirectory != null) {
++			currentLocalDirectory = saveLocalDirectory;
++			localLocation.setText(saveLocalDirectory.toString());
++			return saveLocalDirectory;
++		} else {
++			return dir;
++		}
++	}
++
++	private String saveRemoteHack(String indrive) {
++		saveRemoteDirectoryCount++;
++//System.out.println("R " + saveRemoteDirectoryCount + " - " + saveRemoteDirectory);
++		if (saveRemoteDirectory != null && saveRemoteDirectoryCount > 1) {
++			saveRemoteDirectory = null;
++		}
++		if (saveRemoteDirectory != null) {
++			if (! saveRemoteDirectory.equals("")) {
++System.out.println("saveRemoteHack setText + refreshRemoteLocation '" + saveRemoteDirectory + "'");
++				return saveRemoteDirectory;
++			}
++		}
++		return indrive;
++	}
++// end runge/x11vnc
++
++
+ 	/*
+ 	 * Prints the list of drives on the remote directory and returns a String[].  
+ 	 * str takes as string like A:fC:lD:lE:lF:lG:cH:c
+@@ -143,6 +221,9 @@
+ 		int size = str.length();
+ 		String driveType = null;
+ 		String[] drive = new String[str.length() / 3];
++		int idx = 0, C_drive = -1, O_drive = -1;
++
++System.out.println("ComboBox: Str   '" + str + "'");
+ 
+ 		// Loop through the string to create a String[]
+ 		for (int i = 0; i < size; i = i + 3) {
+@@ -150,26 +231,68 @@
+ 			driveType = str.substring(i + 2, i + 3);
+ 			if (driveType.compareTo("f") == 0)
+ 				drive[i / 3] += "\\ Floppy";
+-			if (driveType.compareTo("l") == 0)
++			if (driveType.compareTo("l") == 0) {
+ 				drive[i / 3] += "\\ Local Disk";
++				if (drive[i/3].substring(0,1).toUpperCase().equals("C")) {
++					C_drive = idx;
++				} else if (O_drive < 0) {
++					O_drive = idx;
++				}
++			}
+ 			if (driveType.compareTo("c") == 0)
+ 				drive[i / 3] += "\\ CD-ROM";
+ 			if (driveType.compareTo("n") == 0)
+ 				drive[i / 3] += "\\ Network";
+ 
+ 			remoteDrivesComboBox.addItem(drive[i / 3]);
++System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'");
++			idx++;
++		}
++
++		// runge
++		if (viewer.ftpDropDown != null) {
++			String[] dd = viewer.ftpDropDown.split("\\.");
++			for (int i=0; i < dd.length; i++) {
++				if (!dd[i].equals("")) {
++					String s = dd[i];
++					if (s.startsWith("TOP_")) {
++						s = s.substring(4);
++						remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0);
++					} else {
++						remoteDrivesComboBox.addItem(" [" + s + "]");
++					}
++				}
++			}
++		} else {
++			remoteDrivesComboBox.addItem(" [My Documents]");
++			remoteDrivesComboBox.addItem(" [Desktop]");
++			remoteDrivesComboBox.addItem(" [Home]");
+ 		}
++
+ 		//sf@ - Select Drive C:as default if possible
+ 		boolean bFound = false;
+-		for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++)
+-		{
+-			if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C"))
+-			{
+-				remoteDrivesComboBox.setSelectedIndex(i);
++
++		if (false) {
++			for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) {
++				if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) {
++					remoteDrivesComboBox.setSelectedIndex(i);
++					bFound = true;
++				}
++			}
++		} else {
++			if (C_drive >= 0) {
++				remoteDrivesComboBox.setSelectedIndex(C_drive);
++				bFound = true;
++System.out.println("ComboBox: C_drive index: " + C_drive);
++			} else if (O_drive >= 0) {
++				remoteDrivesComboBox.setSelectedIndex(O_drive);
+ 				bFound = true;
++System.out.println("ComboBox: Other_drive index: " + O_drive);
+ 			}
+ 		}
++
+ 		if (!bFound) remoteDrivesComboBox.setSelectedIndex(0);
++
+ 		updateDriveList = false;
+ 		return drive;
+ 	}
+@@ -185,6 +308,8 @@
+ 		stopButton.setVisible(true);
+ 		stopButton.setEnabled(true);
+ 		receiveButton.setEnabled(false);
++		viewButton.setEnabled(false);	// runge/x11vnc
++		refreshButton.setEnabled(false);
+ 		remoteTopButton.setEnabled(false);
+ 		sendButton.setEnabled(false);
+ 		remoteFileTable.setEnabled(false);
+@@ -207,6 +332,8 @@
+ 		stopButton.setVisible(false);
+ 		stopButton.setEnabled(false);
+ 		receiveButton.setEnabled(true);
++		viewButton.setEnabled(true);	// runge/x11vnc
++		refreshButton.setEnabled(true);
+ 		remoteTopButton.setEnabled(true);
+ 		sendButton.setEnabled(true);
+ 		remoteFileTable.setEnabled(true);
+@@ -221,10 +348,11 @@
+ 	/*
+ 	 * Print Directory prints out all the contents of a directory
+ 	 */
+-	void printDirectory(ArrayList a) {
++	void printDirectory(ArrayList a, ArrayList b) {
+ 
+ 		for (int i = 0; i < a.size(); i++) {
+ 			remoteList.addElement(a.get(i));
++			remoteListInfo.addElement(b.get(i));
+ 		}
+ 		remoteFileTable.setListData(remoteList);
+ 	}
+@@ -235,10 +363,12 @@
+ 	 * @return void
+ 	 */
+ 	private void initialize() {
++		ignore_events = true;
+ 		this.setSize(794, 500);
+ 		this.setContentPane(getJContentPane());
++		ignore_events = false;
+ 		updateDriveList = true;
+-		}
++	}
+ 	/**
+ 	 * This method initializes jContentPane.  This is the main content pane
+ 	 * 
+@@ -253,6 +383,33 @@
+ 			jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST);
+ 			jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST);
+ 			jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER);
++
++			KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
++			AbstractAction escapeAction = new AbstractAction() {
++				public void actionPerformed(ActionEvent actionEvent) {
++					System.out.println("Escape Pressed");
++					if (viewer.ftpOnly) {
++						System.out.println("exiting...");
++						System.exit(0);
++					} else {
++						doClose();
++					}
++				}
++			};
++			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
++			jContentPane.getInputMap().put(stroke, "escapeAction");
++			jContentPane.getActionMap().put("escapeAction", escapeAction);
++
++			stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK);
++			AbstractAction resetAction = new AbstractAction() {
++				public void actionPerformed(ActionEvent actionEvent) {
++					System.out.println("Ctrl-R Pressed");
++					doReset();
++				}
++			};
++			jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction");
++			jContentPane.getInputMap().put(stroke, "resetAction");
++			jContentPane.getActionMap().put("resetAction", resetAction);
+ 		}
+ 		return jContentPane;
+ 	}
+@@ -270,6 +427,7 @@
+ 			topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER);
+ 			topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST);
+ 			topPanelLocal.setBackground(java.awt.Color.lightGray);
++//System.out.println("getTopPanelLocal");
+ 		}
+ 		return topPanelLocal;
+ 	}
+@@ -288,6 +446,7 @@
+ 			topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER);
+ 			topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST);
+ 			topPanelRemote.setBackground(java.awt.Color.lightGray);
++//System.out.println("getTopPanelRemote");
+ 		}
+ 		return topPanelRemote;
+ 	}
+@@ -301,6 +460,7 @@
+ 		if (topPanelCenter == null) {
+ 			topPanelCenter = new javax.swing.JPanel();
+ 			topPanelCenter.add(getDummyButton(), null);
++//System.out.println("getTopPanelCenter");
+ 		}
+ 		return topPanelCenter;
+ 	}
+@@ -328,6 +488,7 @@
+ 			topPanel.add(getRemoteTopButton(), null);
+ 			topPanel.setBackground(java.awt.Color.lightGray);
+ 			*/
++//System.out.println("getTopPanel");
+ 		}
+ 		return topPanel;
+ 	}
+@@ -348,6 +509,7 @@
+ 			statusPanel.add(getJProgressBar(), null);
+ 			statusPanel.add(getConnectionStatus(), null);
+ 			statusPanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getStatusPanel");
+ 			
+ 		}
+ 		return statusPanel;
+@@ -368,6 +530,7 @@
+ 			remotePanel.add(getRemoteScrollPane(), null);
+ 			remotePanel.add(getRemoteStatus(), null);
+ 			remotePanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getRemotePanel");
+ 		}
+ 		return remotePanel;
+ 	}
+@@ -390,6 +553,7 @@
+ 			localPanel.setComponentOrientation(
+ 				java.awt.ComponentOrientation.UNKNOWN);
+ 			localPanel.setName("localPanel");
++//System.out.println("getLocalPanel");
+ 		}
+ 		return localPanel;
+ 	}
+@@ -405,12 +569,15 @@
+ 			buttonPanel = new javax.swing.JPanel();
+ 			buttonPanel.setLayout(null);
+ 			buttonPanel.add(getReceiveButton(), null);
++			buttonPanel.add(getRefreshButton(), null);	// runge/x11vnc
++			buttonPanel.add(getViewButton(), null);	// runge/x11vnc
+ 			buttonPanel.add(getNewFolderButton(), null);
+ 			buttonPanel.add(getCloseButton(), null);
+ 			buttonPanel.add(getDeleteButton(), null);
+ 			buttonPanel.add(getSendButton(), null);
+ 			buttonPanel.add(getStopButton(), null);
+ 			buttonPanel.setBackground(java.awt.Color.lightGray);
++//System.out.println("getButtonPanel");
+ 		}
+ 		return buttonPanel;
+ 	}
+@@ -422,10 +589,11 @@
+ 	private javax.swing.JButton getSendButton() {
+ 		if (sendButton == null) {
+ 			sendButton = new javax.swing.JButton();
+-			sendButton.setBounds(20, 30, 97, 25);
++			sendButton.setBounds(15, 30, 107, 25);	// runge/x11vnc
+ 			sendButton.setText("Send >>");
+ 			sendButton.setName("sendButton");
+ 			sendButton.addActionListener(this);
++//System.out.println("getSendButton");
+ 
+ 		}
+ 		return sendButton;
+@@ -438,7 +606,7 @@
+ 	private javax.swing.JButton getReceiveButton() {
+ 		if (receiveButton == null) {
+ 			receiveButton = new javax.swing.JButton();
+-			receiveButton.setBounds(20, 60, 97, 25);
++			receiveButton.setBounds(15, 60, 107, 25);	// runge/x11vnc
+ 			receiveButton.setText("<< Receive");
+ 			receiveButton.setName("receiveButton");
+ 			receiveButton.addActionListener(this);
+@@ -453,7 +621,7 @@
+ 	private javax.swing.JButton getDeleteButton() {
+ 		if (deleteButton == null) {
+ 			deleteButton = new javax.swing.JButton();
+-			deleteButton.setBounds(20, 110, 97, 25);
++			deleteButton.setBounds(15, 110, 107, 25);	// runge/x11vnc
+ 			deleteButton.setText("Delete File");
+ 			deleteButton.setName("deleteButton");
+ 			deleteButton.addActionListener(this);
+@@ -468,7 +636,7 @@
+ 	private javax.swing.JButton getNewFolderButton() {
+ 		if (newFolderButton == null) {
+ 			newFolderButton = new javax.swing.JButton();
+-			newFolderButton.setBounds(20, 140, 97, 25);
++			newFolderButton.setBounds(15, 140, 107, 25);	// runge/x11vnc
+ 			newFolderButton.setText("New Folder");
+ 			newFolderButton.setName("newFolderButton");
+ 			newFolderButton.addActionListener(this);
+@@ -476,6 +644,39 @@
+ 		return newFolderButton;
+ 	}
+ 	
++// begin runge/x11vnc
++	/**
++	 * This method initializes refreshButton
++	 * 
++	 * @return javax.swing.JButton
++	 */
++	private javax.swing.JButton getRefreshButton() {
++		if (refreshButton == null) {
++			refreshButton = new javax.swing.JButton();
++			refreshButton.setBounds(15, 170, 107, 25);
++			refreshButton.setText("Refresh");
++			refreshButton.setName("refreshButton");
++			refreshButton.addActionListener(this);
++		}
++		return refreshButton;
++	}
++	/**
++	 * This method initializes viewButton
++	 * 
++	 * @return javax.swing.JButton
++	 */
++	private javax.swing.JButton getViewButton() {
++		if (viewButton == null) {
++			viewButton = new javax.swing.JButton();
++			viewButton.setBounds(15, 200, 107, 25);
++			viewButton.setText("View File");
++			viewButton.setName("viewButton");
++			viewButton.addActionListener(this);
++		}
++		return viewButton;
++	}
++// end   runge/x11vnc
++
+ 	/**
+ 	 * This method initializes stopButton
+ 	 * 
+@@ -486,7 +687,7 @@
+ 		if (stopButton == null)
+ 		{
+ 			stopButton = new javax.swing.JButton();
+-			stopButton.setBounds(20, 200, 97, 25);
++			stopButton.setBounds(15, 230, 107, 25);	// runge/x11vnc
+ 			stopButton.setText("Stop");
+ 			stopButton.setName("stopButton");
+ 			stopButton.addActionListener(this);
+@@ -503,8 +704,12 @@
+ 	private javax.swing.JButton getCloseButton() {
+ 		if (closeButton == null) {
+ 			closeButton = new javax.swing.JButton();
+-			closeButton.setBounds(20, 325, 97, 25);
+-			closeButton.setText("Close");
++			closeButton.setBounds(15, 325, 107, 25);	// runge/x11vnc
++			if (viewer.ftpOnly) {
++				closeButton.setText("Quit");
++			} else {
++				closeButton.setText("Close");
++			}
+ 			closeButton.setName("closeButton");
+ 			closeButton.addActionListener(this);
+ 		}
+@@ -551,6 +756,7 @@
+ 			//Select the second entry (e.g. C:\)
+ 			// localDrivesComboBox.setSelectedIndex(1);
+ 			localDrivesComboBox.addActionListener(this);
++//System.out.println("getLocalDrivesComboBox");
+ 		}
+ 		updateDriveList = false;
+ 		return localDrivesComboBox;
+@@ -567,6 +773,7 @@
+ 			remoteDrivesComboBox.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ 			remoteDrivesComboBox.addActionListener(this);
++//System.out.println("getRemoteDrivesComboBox");
+ 
+ 		}
+ 		return remoteDrivesComboBox;
+@@ -587,6 +794,7 @@
+ 			localMachineLabel.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 11));
+ 			localMachineLabel.setEditable(false);
++//System.out.println("getLocalMachineLabel");
+ 		}
+ 		return localMachineLabel;
+ 	}
+@@ -622,6 +830,7 @@
+ 			localTopButton.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+ 			localTopButton.addActionListener(this);
++//System.out.println("getLocalTopButton");
+ 		}
+ 		return localTopButton;
+ 	}
+@@ -638,6 +847,7 @@
+ 			remoteTopButton.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+ 			remoteTopButton.addActionListener(this);
++//System.out.println("getRemoteTopButton");
+ 		}
+ 		return remoteTopButton;
+ 	}
+@@ -650,9 +860,24 @@
+ 	private javax.swing.JList getLocalFileTable() {
+ 		if (localFileTable == null) {
+ 			localList = new Vector(0);
++			localListInfo = new Vector(0);
+ 			localFileTable = new JList(localList);
++			MouseMotionListener mlisten = new MouseMotionAdapter() {
++				public void mouseMoved(MouseEvent e) {
++					int index = localFileTable.locationToIndex(e.getPoint());
++					if (index == lastLocalIndex) {
++						return;
++					} else if (index < 0) {
++						return;
++					}
++					lastLocalIndex = index;
++					connectionStatus.setText((String) localListInfo.get(index));
++				}
++			};
+ 			localFileTable.addMouseListener(this);
++			localFileTable.addMouseMotionListener(mlisten);
+ 			localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
++//System.out.println("getLocalFileTable");
+ 		}
+ 		return localFileTable;
+ 	}
+@@ -669,6 +894,7 @@
+ 			localScrollPane.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ 			localScrollPane.setName("localFileList");
++//System.out.println("getLocalScrollPane");
+ 		}
+ 		return localScrollPane;
+ 	}
+@@ -680,10 +906,25 @@
+ 	private javax.swing.JList getRemoteFileTable() {
+ 		if (remoteFileTable == null) {
+ 			remoteList = new Vector(0);
++			remoteListInfo = new Vector(0);
+ 			remoteFileTable = new JList(remoteList);
++			MouseMotionListener mlisten = new MouseMotionAdapter() {
++				public void mouseMoved(MouseEvent e) {
++					int index = remoteFileTable.locationToIndex(e.getPoint());
++					if (index == lastRemoteIndex) {
++						return;
++					} else if (index < 0) {
++						return;
++					}
++					lastRemoteIndex = index;
++					connectionStatus.setText((String) remoteListInfo.get(index));
++				}
++			};
+ 			remoteFileTable.addMouseListener(this);
++			remoteFileTable.addMouseMotionListener(mlisten);
+ 			remoteFileTable.setSelectedValue("C:\\", false);
+ 			remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
++//System.out.println("getRemoteFileTable");
+ 			
+ 		}
+ 		return remoteFileTable;
+@@ -698,6 +939,7 @@
+ 			remoteScrollPane = new javax.swing.JScrollPane();
+ 			remoteScrollPane.setViewportView(getRemoteFileTable());
+ 			remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418));
++//System.out.println("getRemoteScrollPane");
+ 		}
+ 		return remoteScrollPane;
+ 	}
+@@ -716,6 +958,7 @@
+ 			remoteLocation.setBackground(new Color(255,255,238));
+ 			remoteLocation.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getRemoteLocation");
+ 		}
+ 		return remoteLocation;
+ 	}
+@@ -732,6 +975,7 @@
+ 			localLocation.setBackground( new Color(255,255,238));
+ 			localLocation.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getLocalLocation");
+ 		}
+ 		return localLocation;
+ 	}
+@@ -748,6 +992,7 @@
+ 			localStatus.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ 			localStatus.setEditable(false);
++//System.out.println("getLocalStatus");
+ 		}
+ 		return localStatus;
+ 	}
+@@ -764,6 +1009,7 @@
+ 			remoteStatus.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
+ 			remoteStatus.setEditable(false);
++//System.out.println("getRemoteStatus");
+ 		}
+ 		return remoteStatus;
+ 	}
+@@ -777,9 +1023,10 @@
+ 			historyComboBox = new javax.swing.JComboBox();
+ 			historyComboBox.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.BOLD, 10));
+-			historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0);
++			historyComboBox.insertItemAt(new String("Pulldown to view history;   Press Escape to Close/Quit;   Press Ctrl-R to Reset Panel."),0);
+ 			historyComboBox.setSelectedIndex(0);
+ 			historyComboBox.addActionListener(this);
++//System.out.println("getHistoryComboBox");
+ 		}
+ 		return historyComboBox;
+ 	}
+@@ -791,6 +1038,7 @@
+ 	private javax.swing.JProgressBar getJProgressBar() {
+ 		if (jProgressBar == null) {
+ 			jProgressBar = new javax.swing.JProgressBar();
++//System.out.println("getJProgressBar");
+ 		}
+ 		return jProgressBar;
+ 	}
+@@ -806,6 +1054,7 @@
+ 			connectionStatus.setBackground(java.awt.Color.lightGray);
+ 			connectionStatus.setFont(
+ 				new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10));
++//System.out.println("getConnectionStatus");
+ 		}
+ 			connectionStatus.setEditable(false);
+ 		return connectionStatus;
+@@ -815,7 +1064,12 @@
+ 	 * Implements Action listener.
+ 	 */
+ 	public void actionPerformed(ActionEvent evt) {
+-		System.out.println(evt.getSource());
++//		System.out.println(evt.getSource());
++
++		if (ignore_events) {
++			System.out.println("ignore_events: " + evt.getSource());
++			return;
++		}
+ 
+ 		if (evt.getSource() == closeButton)
+ 		{ // Close Button
+@@ -829,15 +1083,27 @@
+ 		{
+ 			doReceive();
+ 		}
++// begin runge/x11vnc
++		else if (evt.getSource() == viewButton)
++		{
++			doView();
++		}
++// end   runge/x11vnc
+ 		else if (evt.getSource() == localDrivesComboBox)
+ 		{
+ 			changeLocalDrive();
+ 		}
+ 		else if (evt.getSource() == remoteDrivesComboBox)
+ 		{ 
++//System.out.println("remoteDrivesComboBox");	// runge/x11vnc
+ 			changeRemoteDrive();
+-			remoteList.clear();
+-			remoteFileTable.setListData(remoteList);
++
++			// are these really needed? changeRemoteDrive() does them at the end.
++			if (false) {
++				remoteList.clear();
++				remoteListInfo.clear();
++				remoteFileTable.setListData(remoteList);
++			}
+ 		}
+ 		else if (evt.getSource() == localTopButton)
+ 		{
+@@ -845,12 +1111,17 @@
+ 		}
+ 		else if (evt.getSource() == remoteTopButton)
+ 		{
++//System.out.println("remoteTopButton");	// runge/x11vnc
+ 		  	changeRemoteDrive();
+ 		}
+ 		else if(evt.getSource() == deleteButton)
+ 		{
+ 			doDelete();
+ 		}
++		else if(evt.getSource() == refreshButton)
++		{
++			doRefresh();
++		}
+ 		else if(evt.getSource()==newFolderButton)
+ 		{
+ 			doNewFolder();
+@@ -864,7 +1135,7 @@
+ 
+ 	private void doNewFolder()
+ 	{
+-		String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
++		String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE);
+ 		if(selectedTable.equals("remote"))
+ 		{
+ 			name = remoteLocation.getText()+name;
+@@ -880,34 +1151,106 @@
+ 			historyComboBox.setSelectedIndex(0);
+ 		}
+ 	}
+-	private void doClose()
++	public void doClose()
+ 	{
++		if (viewer.ftpOnly) {
++			viewer.disconnect();
++			return;
++		}
+ 		try {
+ 			this.setVisible(false);
+-			viewer.rfb.writeFramebufferUpdateRequest(
+-									0,
+-									0,
+-									viewer.rfb.framebufferWidth,
+-									viewer.rfb.framebufferHeight,
+-									true);
++			viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth,
++			    viewer.rfb.framebufferHeight, true);
++
++			if (false) {
++				this.dispose();
++				jContentPane = null;
++			}
+ 		} catch (IOException e) {
+ 			// TODO Auto-generated catch block
+ 			e.printStackTrace();
+ 		}
+ 	}
++	private void unSwing() {
++		jContentPane = null;
++		topPanel = null;
++		topPanelLocal = null;
++		topPanelRemote = null;
++		topPanelCenter = null;
++		statusPanel = null;
++		remotePanel = null;
++		localPanel = null;
++		buttonPanel = null;
++		sendButton = null;
++		receiveButton = null;
++		deleteButton = null;
++		newFolderButton = null;
++		stopButton = null;
++		closeButton = null;
++		dummyButton = null;
++		localDrivesComboBox = null;
++		remoteDrivesComboBox = null;
++		localMachineLabel = null;
++		remoteMachineLabel = null;
++		localTopButton = null;
++		remoteTopButton = null;
++		localScrollPane = null;
++		localFileTable = null;
++		remoteScrollPane = null;
++		remoteFileTable = null;
++		remoteLocation = null;
++		localLocation = null;
++		localStatus = null;
++		remoteStatus = null;
++		historyComboBox = null;
++		jProgressBar = null;
++		connectionStatus = null;
++		viewButton = null;
++		refreshButton = null;
++	}
++
++	public void doReset()
++	{
++		try {
++			this.setVisible(false);
++			this.dispose();
++			jContentPane = null;
++		        try {Thread.sleep(500);} catch (InterruptedException e) {}
++			viewer.ftp_init();
++		} catch (Exception e) {
++			// TODO Auto-generated catch block
++			e.printStackTrace();
++		}
++	}
+ 
++	public void doOpen()
++	{
++		try {
++			this.setVisible(true);
++			if (false) {
++				this.initialize();
++			}
++		} catch (Exception e) {
++			// TODO Auto-generated catch block
++			e.printStackTrace();
++		}
++	}
+ 	private void doDelete()
+ 	{
+-		System.out.println("Delete Button Pressed");
++//		System.out.println("Delete Button Pressed");
+ 		//Call this method to delete a file at server
+ 		if(selectedTable.equals("remote"))
+ 		{	
+-			String sFileName = ((String) this.remoteFileTable.getSelectedValue());
++			Object selected = this.remoteFileTable.getSelectedValue();
++			if (selected == null) {
++				return;
++			}
++			String sFileName = ((String) selected);
+ 			
+ //			 sf@2004 - Directory can't be deleted
+ 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ 			{
+-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ 				return;
+ 			}			
+ 			
+@@ -916,7 +1259,7 @@
+ 			// sf@2004 - Delete prompt
+ 			if (remoteList.contains(sFileName))
+ 			{
+-				int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++				int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ 				if (r == JOptionPane.NO_OPTION)
+ 					return;
+ 			}
+@@ -926,18 +1269,22 @@
+ 		}
+ 		else
+ 		{
+-			String sFileName = ((String) this.localFileTable.getSelectedValue());
++			Object selected = this.localFileTable.getSelectedValue();
++			if (selected == null) {
++				return;
++			}
++			String sFileName = ((String) selected);
+ 			
+ //			 sf@2004 - Directory can't be deleted
+ 			if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ 			{
+-				JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++				JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ 				return;
+ 			}			
+ 			// sf@2004 - Delete prompt
+ 			if (localList.contains(sFileName))
+ 			{
+-				int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++				int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ 				if (r == JOptionPane.NO_OPTION)
+ 					return;
+ 			}			
+@@ -952,21 +1299,25 @@
+ 
+ 	private void doReceive()
+ 	{
+-		System.out.println("Received Button Pressed");
++//		System.out.println("Received Button Pressed");
+ 
+-		String sFileName = ((String) this.remoteFileTable.getSelectedValue());
++		Object selected = this.remoteFileTable.getSelectedValue();
++		if (selected == null) {
++			return;
++		}
++		String sFileName = ((String) selected);
+ 		
+		// sf@2004 - Directory can't be transferred
+ 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ 		{
+-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ 			return;
+ 		}
+ 		
+ 		// sf@2004 - Overwrite prompt
+ 		if (localList.contains(sFileName))
+ 		{
+-			int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++			int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ 			if (r == JOptionPane.NO_OPTION)
+ 				return;
+ 		}
+@@ -979,23 +1330,101 @@
+ 		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
+ 	}
+ 
++// begin runge/x11vnc
++	private void doRefresh()
++	{
++		System.out.println("Refreshing Local and Remote.");
++		refreshLocalLocation();
++		refreshRemoteLocation();
++	}
++
++	private void doView()
++	{
++//		System.out.println("View Button Pressed");
++
++		if (selectedTable == null) {
++			return;
++		}
++		if (selectedTable.equals("remote")) {
++			viewRemote();
++		} else if (selectedTable.equals("local")) {
++			viewLocal();
++		}
++	}
++
++	private File doReceiveTmp()
++	{
++
++		if (remoteFileTable == null) {
++			return null;
++		}
++		Object selected = this.remoteFileTable.getSelectedValue();
++		if (selected == null) {
++			return null;
++		}
++		String sFileName = ((String) selected);
++		
++		if (sFileName == null) {
++			return null;
++		}
++		
++		// sf@2004 - Directory can't be transferred
++		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
++		{
++			return null;
++		}
++		
++		File tmp = null;
++		try {
++			tmp = File.createTempFile("ULTRAFTP", ".txt");
++		} catch (Exception e) {
++			return null;
++		}
++		
++		//updateHistory("Downloaded " + localSelection.toString());
++		String remoteFileName = this.remoteLocation.getText();
++		remoteFileName+= ((String) this.remoteFileTable.getSelectedValue()).substring(1);
++		System.out.println("remoteFileName: " + remoteFileName);
++if (false) {
++		char[] b = remoteFileName.toCharArray();
++		for (int n = 0; n < b.length; n++) {
++			System.out.print(Integer.toHexString(b[n]) + " ");
++		}
++		System.out.println("");
++		for (int n = 0; n < b.length; n++) {
++			System.out.print(b[n]);
++		}
++		System.out.println("");
++}
++		
++		String localDestinationPath = tmp.getAbsolutePath();
++		viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath);
++		System.out.println("ReceiveTmp: " + localDestinationPath);
++		return tmp;
++	}
++// end   runge/x11vnc
++
+ 	private void doSend()
+ 	{
+-		System.out.println("Send Button Pressed");
++//		System.out.println("Send Button Pressed");
+ 
+-		String sFileName = ((String) this.localFileTable.getSelectedValue());
++		Object selected = this.localFileTable.getSelectedValue();
++		if (selected == null) {
++			return;
++		}
++		String sFileName = ((String) selected);
+ 		
+		// sf@2004 - Directory can't be transferred
+ 		if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]"))
+ 		{
+-			JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
++			JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE);
+ 			return;
+ 		}
+ 		
+ 		// sf@2004 - Overwrite prompt
+ 		if (remoteList.contains(sFileName))
+ 		{
+-			int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
++			int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION);
+ 			if (r == JOptionPane.NO_OPTION)
+ 				return;
+ 		}
+@@ -1013,6 +1442,7 @@
+ 	// 
+ 	private void doStop()
+ 	{
++		System.out.println("** Current Transfer Aborted **");
+ 		viewer.rfb.fAbort = true;
+ 	}
+ 	/**
+@@ -1024,6 +1454,14 @@
+ 		System.out.println("History: " + message);
+ 		historyComboBox.insertItemAt(new String(message), 0);
+ 	}
++
++	public void receivedRemoteDirectoryName(String str) {
++		if (doingShortcutDir) {
++			if (str.length() > 1) {
++				remoteLocation.setText(str);
++			}
++		}
++	}
+ 	
+ 	/**
+ 	 * This method updates the file table to the current selection of the remoteComboBox
+@@ -1034,11 +1472,44 @@
+ 		remoteSelection = null;
+ 	
+ 		if (!updateDriveList) {
+-			String drive =	remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\";
+-			viewer.rfb.readServerDirectory(drive);
+-			remoteLocation.setText(drive);
++//System.out.println("changeRemoteDrive-A " + drive);	// begin runge/x11vnc
++			Object selected = remoteDrivesComboBox.getSelectedItem();
++			if (selected != null)  {
++				String instr =	selected.toString();
++				if (instr != null) {
++System.out.println("changeRemoteDrive: instr='" + instr + "'");
++					String drive =	instr.substring(0,1)+ ":\\";
++					if (instr.startsWith(" [")) {
++						int idx = instr.lastIndexOf(']'); 
++						if (idx > 2) {
++							drive = instr.substring(2, idx);
++						} else {
++							drive = instr.substring(2);
++						}
++						if (drive.equals("Home")) {
++							drive = "";
++						}
++						drive += "\\";
++						doingShortcutDir = true;
++					} else {
++						doingShortcutDir = false;
++						drive = saveRemoteHack(drive);
++					}
++					gotShortcutDir = false;
++					viewer.rfb.readServerDirectory(drive);
++					if (!gotShortcutDir) {
++						remoteLocation.setText(drive);
++					}
++				} else {
++System.out.println("changeRemoteDrive: instr null");
++				}
++			} else {
++System.out.println("changeRemoteDrive: selection null");
++			}
++//System.out.println("changeRemoteDrive-B " + drive);	// end runge/x11vnc
+ 		}
+ 		remoteList.clear();
++		remoteListInfo.clear();
+ 		remoteFileTable.setListData(remoteList);
+ 	}
+ 	/**
+@@ -1048,6 +1519,7 @@
+ 	private void changeLocalDrive()
+ 	{
+ 		File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString());
++System.out.println("changeLocalDrive " + currentDrive.toString());	// runge/x11vnc
+ 		if(currentDrive.canRead())
+ 		{
+ 			localSelection = null;
+@@ -1057,9 +1529,11 @@
+ 		else
+ 		{
+ 			localList.clear();
++			localListInfo.clear();
+ 			localStatus.setText("WARNING: Drive " + localDrivesComboBox.getSelectedItem().toString());
+ 			connectionStatus.setText(" > WARNING - Local Drive unavailable (possibly restricted access or media not present)");
+ 		}
++
+ 	}
+ 	/**
+ 	 * Determines which FileTable was double-clicked and updates the table
+@@ -1098,10 +1572,18 @@
+ 		selectedTable = "remote";
+ 		localFileTable.setBackground(new Color(238, 238, 238));
+ 		remoteFileTable.setBackground(new Color(255, 255, 255));
+-		String name = (remoteFileTable.getSelectedValue().toString()).substring(1);
++		Object selected = remoteFileTable.getSelectedValue();
++		if (selected == null) {
++			return;
++		}
++		String selstr = selected.toString();
++		if (selstr == null) {
++			return;
++		}
++		String name = selstr.substring(1);
+ 		if( !name.substring(0, 2).equals(" ["))	
+ 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
+-		
++
+ 	}
+ 
+ 	/*
+@@ -1115,10 +1597,38 @@
+ 		localFileTable.setBackground(new Color(255, 255, 255));
+ 		File currentSelection = new File(currentLocalDirectory, getTrimmedSelection());
+ 		
+-		if(currentSelection.isFile()) 
++// begin runge/x11vnc
++		// localSelection = currentSelection.getAbsoluteFile();
++		if(currentSelection.isFile()) {
+ 			localSelection = currentSelection.getAbsoluteFile();
++			localCurrentIsDir = false;
++		} else {
++			localCurrentIsDir = true;
++		}
++// end   runge/x11vnc
+ 
+ 	}
++
++// begin runge/x11vnc
++	private void viewRemote() {
++		File tmp = doReceiveTmp();
++		if (tmp == null) {
++			return;
++		}
++		TextViewer tv = new TextViewer("Remote: " + remoteSelection, tmp, true);
++	}
++	private void viewLocal() {
++		if (localSelection == null) {
++			return;
++		}
++		if (localCurrentIsDir) {
++			return;
++		}
++		File loc = new File(localSelection.toString());
++		TextViewer tv = new TextViewer("Local: " + localSelection.toString(), loc, false);
++	}
++// end runge/x11vnc
++
+ 	/**
+ 	 * Updates the Remote File Table based on selection.  Called from mouseClicked handler
+ 	 */
+@@ -1126,20 +1636,29 @@
+ 		String name = null;
+ 		String action = null;
+ 		String drive = null;
+-		name = (remoteFileTable.getSelectedValue().toString()).substring(1);
++		Object selected = remoteFileTable.getSelectedValue();
++		if (selected == null) {
++			return;
++		}
++		String sname = selected.toString();
++		if (sname == null) {
++			return;
++		}
++		name = sname.substring(1);
+ 
+ 		if (name.equals("[..]"))
+ 		{
+ 			action = "up";
+ 			remoteSelection = null;
+ 			drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1);
+-			// JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
++			// JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE);
+ 			int index = drive.lastIndexOf("\\");
+ 			drive = drive.substring(0, index + 1);
+ 
+ 			remoteLocation.setText(drive);
+ 			viewer.rfb.readServerDirectory(drive);
+ 			remoteList.clear();
++			remoteListInfo.clear();
+ 			remoteFileTable.setListData(remoteList);	
+ 		}
+ 		else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]"))
+@@ -1149,6 +1668,7 @@
+ 			remoteSelection = remoteLocation.getText() + name.substring(0, name.length());
+ 			drive = remoteLocation.getText();
+ 			// ??
++			viewRemote();	// runge/x11vnc
+ 		}
+ 		else
+ 		{ 
+@@ -1159,10 +1679,12 @@
+ 			remoteLocation.setText(drive);
+ 			viewer.rfb.readServerDirectory(drive);
+ 			remoteList.clear();
++			remoteListInfo.clear();
+ 			remoteFileTable.setListData(remoteList);	
+ 		}	
+ 		//remoteLocation.setText(drive);	
+ 	}
++
+ 	/**
+ 	 * Updates the Local File Table based on selection. Called from MouseClicked handler
+ 	 */
+@@ -1188,6 +1710,7 @@
+ 		else if (currentSelection.isFile())
+ 		{
+ 			localSelection = currentSelection.getAbsoluteFile();
++			viewLocal();	// runge/x11vnc
+ 		}
+ 		else if (currentSelection.isDirectory())
+ 		{
+@@ -1201,13 +1724,22 @@
+ 	 * 
+ 	 */
+ 	private String getTrimmedSelection(){
+-		String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1);
+-				if(currentSelection.substring(0,1).equals("[") &&
+-				currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
+-				return currentSelection.substring(1,currentSelection.length()-1);
+-				} else {
+-					return currentSelection;
+-				}
++		String currentSelection = "";
++		Object selected = localFileTable.getSelectedValue();
++		if (selected == null) {
++			return currentSelection;
++		}
++		String selstr = selected.toString();
++		if (selstr == null) {
++			return currentSelection;
++		}
++		currentSelection = selstr.substring(1);
++		if(currentSelection.substring(0,1).equals("[") &&
++			currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){
++			return currentSelection.substring(1,currentSelection.length()-1);
++		} else {
++			return currentSelection;
++		}
+ 	}
+ 
+ 	/*
+@@ -1241,36 +1773,148 @@
+ 	 	return null;
+ 	}
+ 	
++	String timeStr(long t) {
++		Date date = new Date(t);
++		return date.toString();
++	}
++	String dotPast(double f, int n) {
++		String fs = "" + f;
++		int i = fs.lastIndexOf(".") + n;
++		if (i >= 0) {
++			int len = fs.length();
++			if (i >= len) {
++				i = len-1;
++			}
++			fs = fs.substring(0, i);
++		}
++		return fs;
++	}
++	String sizeStr(int s) {
++		if (s < 0) {
++			return s + "? B";
++		} else if (s < 1024) {
++			return s + " B";
++		} else if (s < 1024 * 1024) {
++			double k = s / 1024.0;
++			String ks = dotPast(k, 3);
++			
++			return s + " (" + ks + " KB)";
++		} else {
++			double m = s / (1024.0*1024.0);
++			String ms = dotPast(m, 3);
++			return s + " (" + ms + " MB)";
++		}
++	}
++
++	int max_char(String text) {
++		int maxc = 0;
++		char chars[] = text.toCharArray();
++		for (int n = 0; n < chars.length; n++) {
++			if ((int) chars[n] > maxc) {
++				maxc = (int) chars[n];
++			}
++		}
++		return maxc;
++	}
+ 
+ 	/*
+ 	 * Navigates the local file structure up or down one directory
+ 	 */
+ 	public void changeLocalDirectory(File dir)
+ 	{
+-			currentLocalDirectory = dir;	// Updates Global
++			dir = saveLocalHack(dir);	// runge/x11vnc
++
++			if (dir == null) {
++				connectionStatus.setText("Error changing local directory.");
++				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
++				historyComboBox.setSelectedIndex(0);
++				return;
++			}
++
+ 			File allFiles[] = dir.listFiles();	// Reads files
+ 			String[] contents = dir.list();
+ 
++			if (contents == null || allFiles == null) {
++				connectionStatus.setText("Error changing local directory.");
++				historyComboBox.insertItemAt(new String("> Error changing local directory."), 0);
++				historyComboBox.setSelectedIndex(0);
++				return;
++			}
++
++			currentLocalDirectory = dir;	// Updates Global
++// begin runge/x11vnc
++System.out.println("changeLocalDirectory: " + dir.toString());
++			if (contents != null) {
++				java.util.Arrays.sort(contents, String.CASE_INSENSITIVE_ORDER);
++				for (int i = 0; i < contents.length; i++) {
++					allFiles[i] = new File(dir, contents[i]);
++				}
++			} else {
++				return;
++			}
++// end runge/x11vnc
++
+ 			localList.clear();
++			localListInfo.clear();
+ 			localList.addElement(" [..]");
++			localListInfo.addElement(" [..]");
++
++			ArrayList DirInfo = new ArrayList();
++			ArrayList FilInfo = new ArrayList();
++
++			Charset charset = Charset.forName("ISO-8859-1");
++			CharsetDecoder decoder = charset.newDecoder();
++			CharsetEncoder encoder = charset.newEncoder();
+ 			
+ 			// Populate the Lists
+ 			for (int i = 0; i < contents.length; i++)
+ 			{
+-				if (allFiles[i].isDirectory())
++				String f1 = contents[i];
++
++if (false) {
++	
++System.out.println("max_char: " + max_char(f1) + " "  + f1);
++				if (max_char(f1) > 255) {
++					try {
++System.out.println("bbuf1");
++						ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray()));
++System.out.println("bbuf2");
++						CharBuffer cbuf = decoder.decode(bbuf);
++System.out.println("bbuf3");
++						f1 = cbuf.toString(); 
++System.out.println("did bbuf: " + f1);
++					} catch (Exception e) {
++						;
++					}
++				}
++}
++				
++				String f2 = f1;
++				if (f2.length() < 24) {
++					for (int ik = f2.length(); ik < 24; ik++) {
++						f2 = f2 + " ";
++					}
++				}
++				String s = f2 + "    \tLastmod: " + timeStr(allFiles[i].lastModified()) + "    \t\tSize: " + sizeStr((int) allFiles[i].length()); 
++				if (allFiles[i].isDirectory()) {
+ 					// localList.addElement("[" + contents[i] + "]");
+-					DirsList.add(" [" + contents[i] + "]"); // sf@2004
+-				else
+-				{
++					DirsList.add(" [" + f1 + "]"); // sf@2004
++					DirInfo.add(s);
++				} else {
+ 					// localList.addElement(contents[i]);
+-					FilesList.add(" " + contents[i]); // sf@2004
++					FilesList.add(" " + f1); // sf@2004
++					FilInfo.add(s);
+ 				}
+ 			}
+ 			// sf@2004
+-			for (int i = 0; i < DirsList.size(); i++) 
++			for (int i = 0; i < DirsList.size(); i++) {
+ 				localList.addElement(DirsList.get(i));
+-			for (int i = 0; i < FilesList.size(); i++) 
++				localListInfo.addElement(DirInfo.get(i));
++			}
++			for (int i = 0; i < FilesList.size(); i++) {
+ 				localList.addElement(FilesList.get(i));
++				localListInfo.addElement(FilInfo.get(i));
++			}
+ 			
+ 			FilesList.clear();
+ 			DirsList.clear();
+@@ -1296,3 +1940,147 @@
+ 	}
+ 
+ } //  @jve:visual-info  decl-index=0 visual-constraint="10,10"
++
++// begin runge/x11vnc
++class TextViewer extends JFrame implements ActionListener {
++
++	JTextArea textArea = new JTextArea(35, 80);
++	File file = null;
++	JButton refreshButton;
++	JButton dismissButton;
++	Timer tim = null;
++	int rcnt = 0;
++	int tms = 250;
++	boolean delete_it = false;
++	TextViewer me;
++
++	public TextViewer(String s, File f, boolean d) {
++
++		delete_it = d;
++		file = f;
++		me = this;
++
++		JScrollPane scrollPane = new JScrollPane(textArea,
++		    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
++		    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
++
++		textArea.setEditable(false);
++		textArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
++
++		KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK);
++		AbstractAction escapeAction = new AbstractAction() {
++			public void actionPerformed(ActionEvent actionEvent) {
++				cleanse();
++				me.dispose();
++			}
++		};
++		textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction");
++		textArea.getInputMap().put(stroke, "escapeAction");
++		textArea.getActionMap().put("escapeAction", escapeAction);
++
++		refreshButton = new JButton();
++		refreshButton.setText("Reload");
++		refreshButton.setName("refreshButton");
++		refreshButton.addActionListener(this);
++
++		dismissButton = new JButton();
++		dismissButton.setText("Dismiss");
++		dismissButton.setName("dismissButton");
++		dismissButton.addActionListener(this);
++
++		JPanel buttons = new JPanel();
++		buttons.setLayout(new BorderLayout());
++		buttons.add(refreshButton, BorderLayout.WEST);
++		buttons.add(dismissButton, BorderLayout.EAST);
++
++		JPanel content = new JPanel();
++		content.setLayout(new BorderLayout());
++		content.add(scrollPane, BorderLayout.CENTER);
++		content.add(buttons, BorderLayout.SOUTH);
++
++		ActionListener tsk = new ActionListener() {
++			public void actionPerformed(ActionEvent evt) {
++				// System.out.println("tsk");
++				refresh();
++			}
++		};
++		tim = new Timer(tms, tsk);
++		tim.start();
++
++		this.setContentPane(content);
++		this.setTitle("TextViewer - " + s);
++		this.pack();
++		this.setVisible(true);
++	}
++
++	private void refresh() {
++
++		rcnt++;
++		if (rcnt * tms > 3000 && tim != null) {
++			tim.stop();
++			tim = null;
++		}
++		BufferedReader input = null;
++		StringBuffer contents = new StringBuffer();
++		try {
++			if (input == null) {
++				input = new BufferedReader(new FileReader(file));
++			}
++			String line = null;
++			int i = 0;
++			while (( line = input.readLine()) != null) {
++				if (i == 0) {
++					// System.out.println("read");
++				}
++				i++;
++				contents.append(line);
++				contents.append(System.getProperty("line.separator"));
++			}
++		} catch (Exception e) {
++			;
++		} finally {
++			try {
++				if (input != null) {
++					input.close();
++					input = null;
++				}
++			} catch (Exception e) {
++				;
++			}
++		}
++
++		textArea.setText(contents.toString());
++		textArea.setCaretPosition(0);
++	}
++
++	public void actionPerformed(ActionEvent evt) {
++
++		if (evt.getSource() == refreshButton) {
++			refresh();
++		}
++		if (evt.getSource() == dismissButton) {
++			cleanse();
++			this.dispose();
++		}
++	}
++
++	private void cleanse() {
++		if (delete_it && file != null) {
++			try {
++				file.delete();
++				file = null;
++			} catch (Exception e) {
++				;
++			}
++		}
++	}
++
++	protected void finalize() throws Throwable {
++		try {
++			cleanse();
++		} finally {
++			super.finalize();
++		}
++	}
++}
++// end runge/x11vnc
+diff -Naur JavaViewer.orig/Makefile JavaViewer/Makefile
+--- JavaViewer.orig/Makefile	2006-05-29 09:06:32.000000000 -0400
++++ JavaViewer/Makefile	2010-05-18 20:53:32.000000000 -0400
+@@ -4,6 +4,7 @@
+ 
+ CP = cp
+ JC = javac
++JC_ARGS = -target 1.4 -source 1.4
+ JAR = jar
+ ARCHIVE = VncViewer.jar
+ PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc
+@@ -20,7 +21,7 @@
+ all: $(CLASSES) $(ARCHIVE)
+ 
+ $(CLASSES): $(SOURCES)
+-	$(JC) -O $(SOURCES)
++	$(JC) $(JC_ARGS) -O $(SOURCES)
+ 
+ $(ARCHIVE): $(CLASSES)
+ 	$(JAR) cf $(ARCHIVE) $(CLASSES)
+diff -Naur JavaViewer.orig/OptionsFrame.java JavaViewer/OptionsFrame.java
+--- JavaViewer.orig/OptionsFrame.java	2005-11-21 18:50:16.000000000 -0500
++++ JavaViewer/OptionsFrame.java	2007-05-13 22:18:30.000000000 -0400
+@@ -144,7 +144,10 @@
+     choices[jpegQualityIndex].select("6");
+     choices[cursorUpdatesIndex].select("Enable");
+     choices[useCopyRectIndex].select("Yes");
+-    choices[eightBitColorsIndex].select("64");
++// begin runge/x11vnc
++//    choices[eightBitColorsIndex].select("64");
++    choices[eightBitColorsIndex].select("Full");
++// end runge/x11vnc
+     choices[mouseButtonIndex].select("Normal");
+     choices[viewOnlyIndex].select("No");
+     choices[shareDesktopIndex].select("Yes");
+diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java
+--- JavaViewer.orig/RfbProto.java	2006-05-24 15:14:40.000000000 -0400
++++ JavaViewer/RfbProto.java	2010-11-30 22:13:58.000000000 -0500
+@@ -31,6 +31,7 @@
+ import java.net.Socket;
+ import java.util.*;
+ import java.util.zip.*;
++import java.text.DateFormat;
+ 
+ 
+ class RfbProto {
+@@ -86,8 +87,11 @@
+ 
+ 	// sf@2004 - FileTransfer part
+ 	ArrayList remoteDirsList;
++	ArrayList remoteDirsListInfo;
+ 	ArrayList remoteFilesList;
++	ArrayList remoteFilesListInfo;
+ 	ArrayList a;
++	ArrayList b;
+ 	boolean fFTInit = true; // sf@2004
+ 	boolean fFTAllowed = true;
+ 	boolean fAbort = false;
+@@ -199,6 +203,10 @@
+ 	// playback.
+ 	int numUpdatesInSession;
+ 
++// begin runge/x11vnc
++	int readServerDriveListCnt = -1;
++	long readServerDriveListTime = 0;
++// end   runge/x11vnc
+ 	//
+ 	// Constructor. Make TCP connection to RFB server.
+ 	//
+@@ -207,7 +215,27 @@
+ 		viewer = v;
+ 		host = h;
+ 		port = p;
+-		sock = new Socket(host, port);
++// begin runge/x11vnc
++//		sock = new Socket(host, port);
++    if (! viewer.disableSSL) {
++       System.out.println("new SSLSocketToMe");
++       SSLSocketToMe ssl;
++       try {
++               ssl = new SSLSocketToMe(host, port, v);
++       } catch (Exception e) {
++               throw new IOException(e.getMessage());
++       }
++
++       try {
++               sock = ssl.connectSock();
++       } catch (Exception es) {
++               throw new IOException(es.getMessage());
++       }
++    } else {
++       sock = new Socket(host, port);
++    }
++// end runge/x11vnc
++
+ 		is =
+ 			new DataInputStream(
+ 				new BufferedInputStream(sock.getInputStream(), 16384));
+@@ -215,9 +243,12 @@
+ 		osw = new OutputStreamWriter(sock.getOutputStream());
+ 		inDirectory2 = false;
+ 		a = new ArrayList();
++		b = new ArrayList();
+ 		// sf@2004
+ 		remoteDirsList = new ArrayList();
++		remoteDirsListInfo = new ArrayList();
+ 		remoteFilesList = new ArrayList();
++		remoteFilesListInfo = new ArrayList();
+ 	
+ 		sendFileSource = "";
+ 	}
+@@ -420,7 +451,13 @@
+ 	//
+ 
+ 	int readServerMessageType() throws IOException {
+-		int msgType = is.readUnsignedByte();
++		int msgType;
++		try {
++			msgType = is.readUnsignedByte();
++		} catch (Exception e) {
++			viewer.disconnect();
++			return -1;
++		}
+ 
+ 		// If the session is being recorded:
+ 		if (rec != null) {
+@@ -600,6 +637,7 @@
+ 		contentParamT = is.readUnsignedByte();
+ 		contentParamT = contentParamT << 8;
+ 		contentParam = contentParam | contentParamT;
++//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam);
+ 		if (contentType == rfbRDrivesList || contentType == rfbDirPacket)
+ 		{
+ 			readDriveOrDirectory(contentParam);
+@@ -610,7 +648,7 @@
+ 		}
+ 		else if (contentType == rfbFilePacket)
+ 		{
+-				receiveFileChunk();
++			receiveFileChunk();
+ 		}
+ 		else if (contentType == rfbEndOfFile)
+ 		{
+@@ -618,6 +656,10 @@
+ 		}
+ 		else if (contentType == rfbAbortFileTransfer)
+ 		{
++			System.out.println("rfbAbortFileTransfer: fFileReceptionRunning="
++			    + fFileReceptionRunning + " fAbort="
++			    + fAbort + " fFileReceptionError="
++			    + fFileReceptionError);
+ 			if (fFileReceptionRunning)
+ 			{
+ 				endOfReceiveFile(false); // Error
+@@ -626,6 +668,11 @@
+ 			{
+ 				// sf@2004 - Todo: Add TestPermission 
+ 				// System.out.println("File Transfer Aborted!");
++
++				// runge: seems like we must at least read the remaining
++				// 8 bytes of the header, right?
++				int size = is.readInt();
++				int length = is.readInt();
+ 			}
+ 			
+ 		}
+@@ -645,6 +692,7 @@
+ 		{
+ 			System.out.println("ContentType: " + contentType);
+ 		}
++//System.out.println("FTM: done");
+ 	}
+ 
+ 	//Refactored from readRfbFileTransferMsg()
+@@ -662,6 +710,7 @@
+ 
+ 	//Refactored from readRfbFileTransferMsg()
+ 	public void readDriveOrDirectory(int contentParam) throws IOException {
++//System.out.println("RDOD: " + contentParam + " " + inDirectory2);
+ 		if (contentParam == rfbADrivesList)
+ 		{
+ 			readFTPMsgDriveList();
+@@ -688,13 +737,21 @@
+ 
+ 	// Internally used. Write an Rfb message to the server
+ 	void writeRfbFileTransferMsg(
+-								int contentType,
+-								int contentParam,
+-								long size, // 0 : compression not supported - 1 : compression supported
+-								long length,
+-								String text) throws IOException
++		int contentType,
++		int contentParam,
++		long size, // 0 : compression not supported - 1 : compression supported
++		long length,
++		String text) throws IOException
+ 	{
+ 		byte b[] = new byte[12];
++		byte byteArray[];
++
++		if (viewer.dsmActive) {
++			// need to send the rfbFileTransfer msg type twice for the plugin...
++			byte b2[] = new byte[1];
++			b2[0] = (byte) rfbFileTransfer;
++			os.write(b2);
++		}
+ 
+ 		b[0] = (byte) rfbFileTransfer;
+ 		b[1] = (byte) contentType;
+@@ -702,7 +759,7 @@
+ 
+ 		byte by = 0;
+ 		long c = 0;
+-		length++;
++
+ 		c = size & 0xFF000000;
+ 		by = (byte) (c >>> 24);
+ 		b[4] = by;
+@@ -716,6 +773,32 @@
+ 		by = (byte) c;
+ 		b[7] = by;
+ 
++		if (text != null) {
++			byte byteArray0[] = text.getBytes();
++			int maxc = max_char(text);
++			if (maxc > 255) {
++				System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")");
++				byteArray0 = text.getBytes("UTF-8");
++			} else if (maxc > 127) {
++				System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")");
++				byteArray0 = text.getBytes("ISO-8859-1");
++			}
++			byteArray = new byte[byteArray0.length + 1];
++			for (int i = 0; i < byteArray0.length; i++) {
++				byteArray[i] = byteArray0[i];
++			}
++			byteArray[byteArray.length - 1] = 0;
++System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length);
++
++			// will equal length for ascii, ISO-8859-1, more for UTF-8
++			length = byteArray.length;
++
++			//length++;	// used to not include null byte at end.
++		} else {
++			String moo = "moo";
++			byteArray = moo.getBytes();
++		}
++
+ 		c = length & 0xFF000000;
+ 		by = (byte) (c >>> 24);
+ 		b[8] = by;
+@@ -729,29 +812,91 @@
+ 		by = (byte) c;
+ 		b[11] = by;
+ 		os.write(b);
++
++//System.out.println("size: " + size + " length: " + length + " text: " + text);
+ 		
+ 
+ 		if (text != null)
+ 		{
+-			byte byteArray[] = text.getBytes();
+-			byte byteArray2[] = new byte[byteArray.length + 1];
+-			for (int i = 0; i < byteArray.length; i++) {
+-				byteArray2[i] = byteArray[i];
++			os.write(byteArray);
++		}
++	}
++
++	int max_char(String text) {
++		int maxc = 0;
++		char chars[] = text.toCharArray();
++		for (int n = 0; n < chars.length; n++) {
++			if ((int) chars[n] > maxc) {
++				maxc = (int) chars[n];
+ 			}
+-			byteArray2[byteArray2.length - 1] = 0;
+-			os.write(byteArray2);
+ 		}
+-		
++		return maxc;
+ 	}
+ 
++	String guess_encoding(char[] chars) {
++		boolean saw_high_char = false;
++
++		for (int i = 0; i < chars.length; i++) {
++			if (chars[i] == '\0') {
++				break;
++			}
++			if (chars[i] >= 128) {
++				saw_high_char = true;
++				break;
++			}
++		}
++		if (!saw_high_char) {
++			return "ASCII";
++		}
++		char prev = 1;
++		boolean valid_utf8 = true;
++		int n = 0;
++		for (int i = 0; i < chars.length; i++) {
++			if (chars[i] == '\0') {
++				break;
++			}
++			char c = chars[i];
++			if (prev < 128 && c >= 128) {
++				if (c >> 5 == 0x6) {
++					n = 1;
++				} else if (c >> 4 == 0xe) {
++					n = 2;
++				} else if (c >> 3 == 0x1e) {
++					n = 3;
++				} else if (c >> 2 == 0x3e) {
++					n = 4;
++				} else {
++					valid_utf8 = false;
++					break;
++				}
++			} else {
++				if (n > 0) {
++					if (c < 128) {
++						valid_utf8 = false;
++						break;
++					}
++					n--;
++				}
++			}
++
++			prev = c;
++		}
++		if (valid_utf8) {
++			return "UTF-8";
++		} else {
++			return "ISO-8859-1";
++		}
++	}
++
++
+ 	//Internally used. Write an rfb message to the server for sending files ONLY 
+ 	int writeRfbFileTransferMsgForSendFile(
+-											int contentType,
+-											int contentParam,
+-											long size,
+-											long length,
+-											String source
+-											) throws IOException
++		int contentType,
++		int contentParam,
++		long size,
++		long length,
++		String source
++		) throws IOException
+ 	{
+ 		File f = new File(source);
+ 		fis = new FileInputStream(f);
+@@ -768,50 +913,47 @@
+ 	
+ 		while (bytesRead!=-1)
+ 		{
+-				counter += bytesRead;
+-				myDeflater.setInput(byteBuffer, 0, bytesRead);
+-				myDeflater.finish();
+-				compressedSize = myDeflater.deflate(CompressionBuffer);
+-				myDeflater.reset();
+-				// If the compressed data is larger than the original one, we're dealing with
+-				// already compressed data
+-				if (compressedSize > bytesRead)
+-					fCompress = false;
+-				this.writeRfbFileTransferMsg(
+-											contentType,
+-											contentParam,
+-											(fCompress ? 1 : 0), 
+-											(fCompress ? compressedSize-1 : bytesRead-1),
+-											null
+-											);
+-				// Todo: Test write error !
+-				os.write(
+-						fCompress ? CompressionBuffer : byteBuffer,
+-						0,
+-						fCompress ? compressedSize : bytesRead
+-						);
+-				
+-				// Todo: test read error !
+-				bytesRead = fis.read(byteBuffer);
+-				
+-				// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
+-				viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
+-				viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
+-				
+-				if (fAbort == true)
+-				{
+-					fAbort = false;
+-					fError = true;
+-					break;
+-				}
+-				try
+-				{
+-			        Thread.sleep(5);
+-			    }
+-				catch(InterruptedException e)
+-				{
+-			        System.err.println("Interrupted");
+-			    }				
++			counter += bytesRead;
++			myDeflater.setInput(byteBuffer, 0, bytesRead);
++			myDeflater.finish();
++			compressedSize = myDeflater.deflate(CompressionBuffer);
++			myDeflater.reset();
++			// If the compressed data is larger than the original one, we're dealing with
++			// already compressed data
++			if (compressedSize > bytesRead)
++				fCompress = false;
++			this.writeRfbFileTransferMsg(
++				contentType,
++				contentParam,
++				(fCompress ? 1 : 0), 
++// RUNGE				(fCompress ? compressedSize-1 : bytesRead-1),
++				(fCompress ? compressedSize : bytesRead),
++				null
++			);
++			// Todo: Test write error !
++			os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead);
++			
++			// Todo: test read error !
++			bytesRead = fis.read(byteBuffer);
++			
++			// viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes");
++			viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length()));
++			viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%");
++			
++			if (fAbort == true)
++			{
++				fAbort = false;
++				fError = true;
++				break;
++			}
++			try
++			{
++		        Thread.sleep(5);
++		    }
++			catch(InterruptedException e)
++			{
++		        System.err.println("Interrupted");
++		    }				
+ 		}
+ 		
+ 		writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null);
+@@ -831,24 +973,30 @@
+ 			{
+ 				System.out.print((char) is.readUnsignedByte());
+ 			}
++			System.out.println("");
++
++			if (size == rfbRErrorCmd || size == -1) {
++				viewer.ftp.enableButtons();
++				viewer.ftp.connectionStatus.setText("Remote file not available for writing.");
++				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0);
++				viewer.ftp.historyComboBox.setSelectedIndex(0);
++				return;
++			}
+ 			
+-			int ret = writeRfbFileTransferMsgForSendFile(
+-															rfbFilePacket,
+-															0,
+-															0,
+-															0,
+-															sendFileSource);
++			int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource);
+ 	
+ 			viewer.ftp.refreshRemoteLocation();
+ 			if (ret != 1)
+ 			{
+ 				viewer.ftp.connectionStatus.setText(" > Error - File NOT sent");
+-				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0);
++				viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource)
++				    + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0);
+ 			}
+ 			else
+ 			{
+ 				viewer.ftp.connectionStatus.setText(" > File sent");
+-				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0);
++				viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource)
++				    + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0);
+ 			}
+ 			viewer.ftp.historyComboBox.setSelectedIndex(0);
+ 			viewer.ftp.enableButtons();
+@@ -907,7 +1055,7 @@
+ 	//Handles acknowledgement that the file has been deleted on the server
+ 	void deleteRemoteFileFeedback() throws IOException
+ 	{
+-		is.readInt();
++		int ret = is.readInt();
+ 		int length = is.readInt();
+ 		String f = "";
+ 		for (int i = 0; i < length; i++)
+@@ -916,7 +1064,11 @@
+ 		}
+ 		
+ 		viewer.ftp.refreshRemoteLocation();	
+-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
++		if (ret == -1) {
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0);
++		} else {
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0);
++		}
+ 		viewer.ftp.historyComboBox.setSelectedIndex(0);
+ 	}
+ 
+@@ -926,12 +1078,7 @@
+ 		try
+ 		{
+ 			String temp = text;
+-			writeRfbFileTransferMsg(
+-									rfbCommand,
+-									rfbCFileDelete,
+-									0,
+-									temp.length(),
+-									temp);
++			writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp);
+ 		}
+ 		catch (IOException e)
+ 		{
+@@ -943,7 +1090,7 @@
+ 	// Handles acknowledgement that the directory has been created on the server
+ 	void createRemoteDirectoryFeedback() throws IOException
+ 	{
+-		is.readInt();
++		int ret = is.readInt();
+ 		int length = is.readInt();
+ 		String f="";
+ 		for (int i = 0; i < length; i++)
+@@ -951,7 +1098,11 @@
+ 			f += (char)is.readUnsignedByte();
+ 		}
+ 		viewer.ftp.refreshRemoteLocation();	
+-		viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
++		if (ret == -1) {
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0);
++		} else {
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0);
++		}
+ 		viewer.ftp.historyComboBox.setSelectedIndex(0);
+ 	}
+ 
+@@ -961,12 +1112,7 @@
+ 		try
+ 		{
+ 			String temp = text;
+-			writeRfbFileTransferMsg(
+-				rfbCommand,
+-				rfbCDirCreate,
+-				0,
+-				temp.length(),
+-				temp);
++			writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp);
+ 		}
+ 		catch (IOException e)
+ 		{
+@@ -979,15 +1125,13 @@
+ 	{
+ 		try
+ 		{
++//System.out.println("requestRemoteFile text: " + text);
++//System.out.println("requestRemoteFile leng: " + text.length());
+ 			String temp = text;
+ 			receivePath = localPath;
+ 					
+-			writeRfbFileTransferMsg(
+-									rfbFileTransferRequest,
+-									0,
+-									1, // 0 : compression not supported - 1 : compression supported
+-									temp.length(),
+-									temp);
++			// 0 : compression not supported - 1 : compression supported
++			writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp);
+ 		}
+ 		catch (IOException e)
+ 		{
+@@ -1004,6 +1148,9 @@
+ 		viewer.ftp.disableButtons();
+ 		int size = is.readInt();
+ 		int length = is.readInt();
++
++//System.out.println("receiveFileHeader size: " + size);
++//System.out.println("receiveFileHeader leng: " + length);
+ 		
+ 		String tempName = "";
+ 		for (int i = 0; i < length; i++)
+@@ -1011,6 +1158,15 @@
+ 			tempName += (char) is.readUnsignedByte();
+ 		}
+ 
++		if (size == rfbRErrorCmd || size == -1) {
++			fFileReceptionRunning = false;
++			viewer.ftp.enableButtons();
++			viewer.ftp.connectionStatus.setText("Remote file not available for reading.");
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0);
++			viewer.ftp.historyComboBox.setSelectedIndex(0);
++			return;
++		}
++
+ 		// sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for 
+ 		// backward compatibility reasons...)
+ 		int sizeH = is.readInt();
+@@ -1021,7 +1177,16 @@
+ 		fileSize=0;
+ 		fileChunkCounter = 0;
+ 		String fileName = receivePath;
+-		fos = new FileOutputStream(fileName);
++		try {
++			fos = new FileOutputStream(fileName);
++		} catch (Exception e) {
++			fFileReceptionRunning = false;
++			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0);
++			viewer.ftp.historyComboBox.setSelectedIndex(0);
++			viewer.ftp.enableButtons();
++			return;
++		}
+ 		writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null);
+ 	}
+ 
+@@ -1085,7 +1250,13 @@
+ 			fAbort = false;
+ 			fFileReceptionError = true;
+ 			writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null);
+-			
++
++			//runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent.
++		        try {Thread.sleep(500);} catch (InterruptedException e) {}
++			viewer.ftp.enableButtons();
++			viewer.ftp.refreshLocalLocation();
++			viewer.ftp.connectionStatus.setText(" > Error - File NOT received");
++			viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0);
+ 		}
+ 		// sf@2004 - For old FT protocole only
+ 		/*
+@@ -1104,7 +1275,7 @@
+ 		int length = is.readInt();
+ 		fileSize=0;
+ 		fos.close();
+-		
++
+ 		viewer.ftp.refreshLocalLocation();
+ 		if (fReceptionOk && !fFileReceptionError)
+ 		{
+@@ -1132,12 +1303,7 @@
+ 		try
+ 		{
+ 			String temp = text;
+-			writeRfbFileTransferMsg(
+-									rfbDirContentRequest,
+-									rfbRDirContent,
+-									0,
+-									temp.length(),
+-									temp);
++			writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp);
+ 		}
+ 		catch (IOException e)
+ 		{
+@@ -1197,11 +1363,80 @@
+ 					str += temp;
+ 				}
+ 			}
++			// runge
++			viewer.ftp.receivedRemoteDirectoryName(str);
+ 			// viewer.ftp.changeRemoteDirectory(str);
+ 			
+ 		}
+ 	}
+ 
++	int zogswap(int n) {
++		long l = n;
++		if (l < 0) {
++			l += 0x100000000L;
++		}
++		l = l & 0xFFFFFFFF;
++		l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24);
++		return (int) l;
++	}
++
++	int windozeToUnix(int L, int H) {
++		long L2 = zogswap(L);
++		long H2 = zogswap(H);
++		long unix = (H2 << 32) + L2;
++		unix -= 11644473600L * 10000000L;
++		unix /= 10000000L;
++		//System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2);
++		return (int) unix;
++	}
++	
++	String timeStr(int t, int h) {
++		if (h == 0) {
++			// x11vnc/libvncserver unix
++			t = zogswap(t);
++		} else {
++			// ultra (except if h==0 by chance)
++			t = windozeToUnix(t, h);
++		}
++		long tl = (long) t;
++		Date date = new Date(tl * 1000);
++		if (true) {
++			return date.toString();
++		} else {
++			return DateFormat.getDateTimeInstance().format(date);
++		}
++	}
++
++	String dotPast(double f, int n) {
++		String fs = "" + f;
++		int i = fs.lastIndexOf(".") + n;
++		if (i >= 0) {
++			int len = fs.length();
++			if (i >= len) {
++				i = len-1;
++			}
++			fs = fs.substring(0, i);
++		}
++		return fs;
++	}
++	String sizeStr(int s) {
++		s = zogswap(s);
++		if (s < 0) {
++			return s + "? B";
++		} else if (s < 1024) {
++			return s + " B";
++		} else if (s < 1024 * 1024) {
++			double k = s / 1024.0;
++			String ks = dotPast(k, 3);
++			
++			return s + " (" + ks + " KB)";
++		} else {
++			double m = s / (1024.0*1024.0);
++			String ms = dotPast(m, 3);
++			return s + " (" + ms + " MB)";
++		}
++	}
++
+ 	//Internally used to receive directory content from server
+ 	//Here, the server sends one file/directory with it's attributes
+ 	void readFTPMsgDirectoryListContent() throws IOException
+@@ -1217,17 +1452,32 @@
+ 			dwReserved0,
+ 			dwReserved1;
+ 		long ftCreationTime, ftLastAccessTime, ftLastWriteTime;
++		int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL;
++		int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH;
+ 		char cFileName, cAlternateFileName;
+ 		int length = 0;
+ 		is.readInt();
+ 		length = is.readInt();
++
++		char[] chars = new char[4*length];
++		int char_cnt = 0;
++		for (int i = 0; i < chars.length; i++) {
++			chars[i] = '\0';
++		}
++		
+ 		dwFileAttributes = is.readInt();
+ 		length -= 4;
+-		ftCreationTime = is.readLong();
++		//ftCreationTime = is.readLong();
++		ftCreationTimeL = is.readInt();
++		ftCreationTimeH = is.readInt();
+ 		length -= 8;
+-		ftLastAccessTime = is.readLong();
++		//ftLastAccessTime = is.readLong();
++		ftLastAccessTimeL = is.readInt();
++		ftLastAccessTimeH = is.readInt();
+ 		length -= 8;
+-		ftLastWriteTime = is.readLong();
++		//ftLastWriteTime = is.readLong();
++		ftLastWriteTimeL = is.readInt();
++		ftLastWriteTimeH = is.readInt();
+ 		length -= 8;
+ 		nFileSizeHigh = is.readInt();
+ 		length -= 4;
+@@ -1239,10 +1489,12 @@
+ 		length -= 4;
+ 		cFileName = (char) is.readUnsignedByte();
+ 		length--;
++		chars[char_cnt++] = cFileName;
+ 		while (cFileName != '\0')
+ 		{
+ 			fileName += cFileName;
+ 			cFileName = (char) is.readUnsignedByte();
++			chars[char_cnt++] = cFileName;
+ 			length--;
+ 		}
+ 		cAlternateFileName = (char) is.readByte();
+@@ -1253,7 +1505,28 @@
+ 			cAlternateFileName = (char) is.readUnsignedByte();
+ 			length--;
+ 		}
+-		if (dwFileAttributes == 268435456
++		String guessed = guess_encoding(chars);
++		if (!guessed.equals("ASCII")) {
++			System.out.println("guess: " + guessed + "\t" + fileName);
++		}
++		if (guessed.equals("UTF-8")) {
++			try {
++				byte[] bytes = new byte[char_cnt-1];
++				for (int i=0; i < char_cnt-1; i++) {
++					bytes[i] = (byte) chars[i];
++				}
++				String newstr = new String(bytes, "UTF-8");
++				fileName = newstr;
++			} catch (Exception e) {
++				System.out.println("failed to convert bytes to UTF-8 based string");
++			}
++		}
++		for (int i = 0; i < char_cnt; i++) {
++			//System.out.println("char[" + i + "]\t" + (int) chars[i]);
++		}
++		if (fileName.length() <= 0) {
++			;
++		} else if (dwFileAttributes == 268435456
+ 			|| dwFileAttributes == 369098752
+ 			|| dwFileAttributes == 285212672 
+ 			|| dwFileAttributes == 271056896
+@@ -1263,11 +1536,74 @@
+ 			|| dwFileAttributes == 369623040)
+ 		{
+ 			fileName = " [" + fileName + "]";
+-			remoteDirsList.add(fileName); // sf@2004
+-		}
+-		else
+-		{
+-			remoteFilesList.add(" " + fileName); // sf@2004
++// begin runge/x11vnc
++//			remoteDirsList.add(fileName); // sf@2004
++			int i = -1;
++			String t1 = fileName.toLowerCase();
++			for (int j = 0; j < remoteDirsList.size(); j++) {
++				String t = (String) remoteDirsList.get(j);
++				String t2 = t.toLowerCase();
++				if (t1.compareTo(t2) < 0) {
++					i = j;
++					break;
++				}
++			}
++			//String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    " + fileName; 
++			String f2 = fileName;
++			if (f2.length() < 24) {
++				for (int ik = f2.length(); ik < 24; ik++) {
++					f2 = f2 + " ";
++				}
++			}
++			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow); 
++			//s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL); 
++			if (i >= 0) {
++				remoteDirsList.add(i, fileName);
++				remoteDirsListInfo.add(i, s);
++			} else {
++				remoteDirsList.add(fileName);
++				remoteDirsListInfo.add(s);
++			}
++// end runge/x11vnc
++		} else {
++// begin runge/x11vnc
++//			remoteFilesList.add(" " + fileName); // sf@2004
++	
++			fileName = " " + fileName;
++			int i = -1;
++			String t1 = fileName.toLowerCase();
++			for (int j = 0; j < remoteFilesList.size(); j++) {
++				String t = (String) remoteFilesList.get(j);
++				String t2 = t.toLowerCase();
++				if (t1.compareTo(t2) < 0) {
++					i = j;
++					break;
++				}
++			}
++			String f2 = fileName;
++			if (f2.length() < 24) {
++				for (int ik = f2.length(); ik < 24; ik++) {
++					f2 = f2 + " ";
++				}
++			}
++
++if (false) {
++System.out.println("fileName:         " + f2);
++System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL);
++System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH);
++System.out.println("nFileSizeLow:     " + nFileSizeLow);
++}
++
++			String s = f2 + "    \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + "    \t\tSize: " + sizeStr(nFileSizeLow); 
++			//s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + "  Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow); 
++			if (i >= 0) {
++				remoteFilesList.add(i, fileName);
++				remoteFilesListInfo.add(i, s);
++			} else {
++				remoteFilesList.add(fileName);
++				remoteFilesListInfo.add(s);
++			}
++// end runge/x11vnc
+ 		}
+ 	
+ 		// a.add(fileName);
+@@ -1282,14 +1618,32 @@
+ 
+ 		// sf@2004
+ 		a.clear();
+-		for (int i = 0; i < remoteDirsList.size(); i++) 
++		b.clear();
++		for (int i = 0; i < remoteDirsList.size(); i++) {
+ 			a.add(remoteDirsList.get(i));
+-		for (int i = 0; i < remoteFilesList.size(); i++) 
++			b.add(remoteDirsListInfo.get(i));
++		}
++		for (int i = 0; i < remoteFilesList.size(); i++) {
+ 			a.add(remoteFilesList.get(i));
++
++			b.add(remoteFilesListInfo.get(i));
++		}
+ 		remoteDirsList.clear();
++		remoteDirsListInfo.clear();
+ 		remoteFilesList.clear();
++		remoteFilesListInfo.clear();
+ 		
+-		viewer.ftp.printDirectory(a);
++// begin runge/x11vnc
++		// Hack for double listing at startup... probably libvncserver bug..
++		readServerDriveListCnt++;
++		if (readServerDriveListCnt == 2) {
++			if (System.currentTimeMillis() - readServerDriveListTime < 2000)  {
++//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt);
++				return;
++			}
++		}
++// end runge/x11vnc
++		viewer.ftp.printDirectory(a, b);
+ 	}
+ 
+ 	//Internally used to signify the drive requested is not ready
+@@ -1299,6 +1653,8 @@
+ 		System.out.println("Remote Drive unavailable");
+ 		viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)");
+ 		viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable");
++		viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0);
++		viewer.ftp.historyComboBox.setSelectedIndex(0);
+ 	}
+ 
+ 	//Call this method to request the list of drives on the server.
+@@ -1306,12 +1662,11 @@
+ 	{
+ 		try
+ 		{
+-			viewer.rfb.writeRfbFileTransferMsg(
+-												RfbProto.rfbDirContentRequest,
+-												RfbProto.rfbRDrivesList,
+-												0,
+-												0,
+-												null);
++			viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null);
++// begin runge/x11vnc
++			readServerDriveListCnt = 0;
++			readServerDriveListTime = System.currentTimeMillis();
++// end   runge/x11vnc
+ 		}
+ 		catch (IOException e)
+ 		{
+@@ -1355,21 +1710,21 @@
+ 		int h,
+ 		boolean incremental)
+ 		throws IOException {
+-			if (!viewer.ftp.isVisible()) {
+-		byte[] b = new byte[10];
++		if (!viewer.ftp.isVisible()) {
++			byte[] b = new byte[10];
+ 
+-		b[0] = (byte) FramebufferUpdateRequest;
+-		b[1] = (byte) (incremental ? 1 : 0);
+-		b[2] = (byte) ((x >> 8) & 0xff);
+-		b[3] = (byte) (x & 0xff);
+-		b[4] = (byte) ((y >> 8) & 0xff);
+-		b[5] = (byte) (y & 0xff);
+-		b[6] = (byte) ((w >> 8) & 0xff);
+-		b[7] = (byte) (w & 0xff);
+-		b[8] = (byte) ((h >> 8) & 0xff);
+-		b[9] = (byte) (h & 0xff);
++			b[0] = (byte) FramebufferUpdateRequest;
++			b[1] = (byte) (incremental ? 1 : 0);
++			b[2] = (byte) ((x >> 8) & 0xff);
++			b[3] = (byte) (x & 0xff);
++			b[4] = (byte) ((y >> 8) & 0xff);
++			b[5] = (byte) (y & 0xff);
++			b[6] = (byte) ((w >> 8) & 0xff);
++			b[7] = (byte) (w & 0xff);
++			b[8] = (byte) ((h >> 8) & 0xff);
++			b[9] = (byte) (h & 0xff);
+ 
+-		os.write(b);
++			os.write(b);
+ 		}
+ 	}
+ 
+@@ -1482,7 +1837,13 @@
+ 		b[6] = (byte) ((text.length() >> 8) & 0xff);
+ 		b[7] = (byte) (text.length() & 0xff);
+ 
+-		System.arraycopy(text.getBytes(), 0, b, 8, text.length());
++		if (false && max_char(text) > 255) {
++			System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length());
++		} else if (max_char(text) > 127) {
++			System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length());
++		} else {
++			System.arraycopy(text.getBytes(), 0, b, 8, text.length());
++		}
+ 
+ 		os.write(b);
+ 	//	}
+@@ -1506,6 +1867,37 @@
+ 	final static int META_MASK = InputEvent.META_MASK;
+ 	final static int ALT_MASK = InputEvent.ALT_MASK;
+ 
++	void writeWheelEvent(MouseWheelEvent evt) throws IOException {
++		eventBufLen = 0;
++
++		int x = evt.getX();
++		int y = evt.getY();
++
++		if (x < 0) x = 0;
++		if (y < 0) y = 0;
++
++		int ptrmask;
++
++		int clicks = evt.getWheelRotation();
++		System.out.println("writeWheelEvent: clicks: " + clicks);
++		if (clicks > 0) {
++			ptrmask = 16;
++		} else if (clicks < 0) {
++			ptrmask = 8;
++		} else {
++			return;
++		}
++
++		eventBuf[eventBufLen++] = (byte) PointerEvent;
++		eventBuf[eventBufLen++] = (byte) ptrmask;
++		eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
++		eventBuf[eventBufLen++] = (byte) (x & 0xff);
++		eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
++		eventBuf[eventBufLen++] = (byte) (y & 0xff);
++
++		os.write(eventBuf, 0, eventBufLen);
++	}
++
+ 	//
+ 	// Write a pointer event message.  We may need to send modifier key events
+ 	// around it to set the correct modifier state.
+@@ -1610,6 +2002,21 @@
+ 
+ 		boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
+ 
++		if (viewer.debugKeyboard) {
++			System.out.println("----------------------------------------");
++			System.out.println("evt.getKeyChar:      " + evt.getKeyChar());
++			System.out.println("getKeyText:          " + KeyEvent.getKeyText(evt.getKeyCode()));
++			System.out.println("evt.getKeyCode:      " + evt.getKeyCode());
++			System.out.println("evt.getID:           " + evt.getID());
++			System.out.println("evt.getKeyLocation:  " + evt.getKeyLocation());
++			System.out.println("evt.isActionKey:     " + evt.isActionKey());
++			System.out.println("evt.isControlDown:   " + evt.isControlDown());
++			System.out.println("evt.getModifiers:    " + evt.getModifiers());
++			System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers()));
++			System.out.println("evt.paramString:     " + evt.paramString());
++		}
++
++
+ 		int key;
+ 		if (evt.isActionKey()) {
+ 
+@@ -1685,6 +2092,9 @@
+ 				default :
+ 					return;
+ 			}
++			if (key == 0xffc2 && viewer.mapF5_to_atsign) {
++				key = 0x40;
++			}
+ 
+ 		} else {
+ 
+@@ -1794,6 +2204,16 @@
+ 	int oldModifiers = 0;
+ 
+ 	void writeModifierKeyEvents(int newModifiers) {
++		if(viewer.forbid_Ctrl_Alt) {
++			if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) {
++				int orig = newModifiers;
++				newModifiers &= ~ALT_MASK;
++				newModifiers &= ~CTRL_MASK;
++				if (viewer.debugKeyboard) {
++					System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers);
++				}
++			}
++		}
+ 		if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
+ 			writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
+ 
+diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+--- JavaViewer.orig/SSLSocketToMe.java	1969-12-31 19:00:00.000000000 -0500
++++ JavaViewer/SSLSocketToMe.java	2010-07-10 19:18:06.000000000 -0400
+@@ -0,0 +1,2067 @@
++/*
++ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
++ *
++ * Copyright (c) 2006 Karl J. Runge <runge@karlrunge.com>
++ * All rights reserved.
++ *
++ *  This is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License, or
++ *  (at your option) any later version.
++ *
++ *  This software is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this software; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
++ *  USA.
++ *
++ */
++
++import java.net.*;
++import java.io.*;
++import javax.net.ssl.*;
++import java.util.*;
++
++import java.security.*;
++import java.security.cert.*;
++import java.security.spec.*;
++import java.security.cert.Certificate;
++import java.security.cert.CertificateFactory;
++
++import java.awt.*;
++import java.awt.event.*;
++
++public class SSLSocketToMe {
++
++	/* basic member data: */
++	String host;
++	int port;
++	VncViewer viewer;
++
++	boolean debug = true;
++	boolean debug_certs = false;
++
++	/* sockets */
++	SSLSocket socket = null;
++	SSLSocketFactory factory;
++
++	/* fallback for Proxy connection */
++	boolean proxy_in_use = false;
++	boolean proxy_failure = false;
++	public DataInputStream is = null;
++	public OutputStream os = null;
++
++	/* strings from user WRT proxy: */
++	String proxy_auth_string = null;
++	String proxy_dialog_host = null;
++	int proxy_dialog_port = 0;
++
++	Socket proxySock;
++	DataInputStream proxy_is;
++	OutputStream proxy_os;
++
++	/* trust contexts */
++	SSLContext trustloc_ctx;
++	SSLContext trustall_ctx;
++	SSLContext trustsrv_ctx;
++	SSLContext trusturl_ctx;
++	SSLContext trustone_ctx;
++
++	/* corresponding trust managers */
++	TrustManager[] trustAllCerts;
++	TrustManager[] trustSrvCert;
++	TrustManager[] trustUrlCert;
++	TrustManager[] trustOneCert;
++
++	/* client-side SSL auth key (oneTimeKey=...) */
++	KeyManager[] mykey = null;
++
++	boolean user_wants_to_see_cert = true;
++	String cert_fail = null;
++
++	/* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
++	java.security.cert.Certificate[] trustallCerts = null;
++	java.security.cert.Certificate[] trustsrvCerts = null;
++	java.security.cert.Certificate[] trusturlCerts = null;
++
++	/* utility to decode hex oneTimeKey=... and serverCert=... */
++	byte[] hex2bytes(String s) {
++		byte[] bytes = new byte[s.length()/2];
++		for (int i=0; i<s.length()/2; i++) {
++			int j = 2*i;
++			try {
++				int val = Integer.parseInt(s.substring(j, j+2), 16);
++				if (val > 127) {
++					val -= 256;
++				}
++				Integer I = new Integer(val);
++				bytes[i] = Byte.decode(I.toString()).byteValue();
++				
++			} catch (Exception e) {
++				;
++			}
++		}
++		return bytes;
++	}
++
++	SSLSocketToMe(String h, int p, VncViewer v) throws Exception {
++		host = h;
++		port = p;
++		viewer = v;
++
++		debug_certs = v.debugCerts;
++
++		/* we will first try default factory for certification: */
++
++		factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
++
++		dbg("SSL startup: " + host + " " + port);
++
++
++		/* create trust managers to be used if initial handshake fails: */
++
++		trustAllCerts = new TrustManager[] {
++		    /*
++		     * this one accepts everything.  Only used if user
++		     * has disabled checking (trustAllVncCerts=yes)
++		     * or when we grab the cert to show it to them in
++		     * a dialog and ask them to manually verify/accept it.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) {
++				/* empty */
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) {
++				/* empty */
++				dbg("ALL: an untrusted connect to grab cert.");
++			}
++		    }
++		};
++
++		trustUrlCert = new TrustManager[] {
++		    /*
++		     * this one accepts only the retrieved server
++		     * cert by SSLSocket by this applet and stored in
++		     * trusturlCerts.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (URL)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trusturlCerts' */
++				if (trusturlCerts == null) {
++					throw new CertificateException(
++					    "No Trust url Certs array.");
++				}
++				if (trusturlCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust url Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trusturlCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trusturlCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("URL: cert mismatch at i=" + i);
++						dbg("URL: cert mismatch cert" + certs[i]);
++						dbg("URL: cert mismatch  url" + trusturlCerts[i]);
++						if (cert_fail == null) {
++							cert_fail = "cert-mismatch";
++						}
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("URL: cert info at i=" + i);
++						dbg("URL: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("URL: cert info  url" + trusturlCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != URL Cert Chain.");
++				}
++				dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		trustSrvCert = new TrustManager[] {
++		    /*
++		     * this one accepts cert given to us in the serverCert
++		     * Applet Parameter we were started with.  It is
++		     * currently a fatal error if the VNC Server's cert
++		     * doesn't match it.
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (SRV)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trustsrvCerts' */
++				if (trustsrvCerts == null) {
++					throw new CertificateException(
++					    "No Trust srv Certs array.");
++				}
++				if (trustsrvCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust srv Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trustsrvCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trustsrvCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("SRV: cert mismatch at i=" + i);
++						dbg("SRV: cert mismatch cert" + certs[i]);
++						dbg("SRV: cert mismatch  srv" + trustsrvCerts[i]);
++						if (cert_fail == null) {
++							cert_fail = "server-cert-mismatch";
++						}
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("SRV: cert info at i=" + i);
++						dbg("SRV: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("SRV: cert info  srv" + trustsrvCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
++				}
++				dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		trustOneCert = new TrustManager[] {
++		    /*
++		     * this one accepts only the retrieved server
++		     * cert by SSLSocket by this applet we stored in
++		     * trustallCerts that user has accepted or applet
++		     * parameter trustAllVncCerts=yes is set.  This is
++		     * for when we reconnect after the user has manually
++		     * accepted the trustall cert in the dialog (or set
++		     * trustAllVncCerts=yes applet param.)
++		     */
++		    new X509TrustManager() {
++			public java.security.cert.X509Certificate[]
++			    getAcceptedIssuers() {
++				return null;
++			}
++			public void checkClientTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				throw new CertificateException("No Clients (ONE)");
++			}
++			public void checkServerTrusted(
++			    java.security.cert.X509Certificate[] certs,
++			    String authType) throws CertificateException {
++				/* we want to check 'certs' against 'trustallCerts' */
++				if (trustallCerts == null) {
++					throw new CertificateException(
++					    "No Trust All Server Certs array.");
++				}
++				if (trustallCerts.length < 1) {
++					throw new CertificateException(
++					    "No Trust All Server Certs.");
++				}
++				if (certs == null) {
++					throw new CertificateException(
++					    "No this-certs array.");
++				}
++				if (certs.length < 1) {
++					throw new CertificateException(
++					    "No this-certs Certs.");
++				}
++				if (certs.length != trustallCerts.length) {
++					throw new CertificateException(
++					    "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length);
++				}
++				boolean ok = true;
++				for (int i = 0; i < certs.length; i++)  {
++					if (! trustallCerts[i].equals(certs[i])) {
++						ok = false;
++						dbg("ONE: cert mismatch at i=" + i);
++						dbg("ONE: cert mismatch cert" + certs[i]);
++						dbg("ONE: cert mismatch  all" + trustallCerts[i]);
++					}
++					if (debug_certs) {
++						dbg("\n***********************************************");
++						dbg("ONE: cert info at i=" + i);
++						dbg("ONE: cert info cert" + certs[i]);
++						dbg("===============================================");
++						dbg("ONE: cert info  all" + trustallCerts[i]);
++						dbg("***********************************************");
++					}
++				}
++				if (!ok) {
++					throw new CertificateException(
++					    "Server Cert Chain != TRUSTALL Cert Chain.");
++				}
++				dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1));
++			}
++		    }
++		};
++
++		/* 
++		 * The above TrustManagers are used:
++		 *
++		 * 1) to retrieve the server cert in case of failure to
++		 *    display it to the user in a dialog.
++		 * 2) to subsequently connect to the server if user agrees.
++		 */
++
++		/*
++		 * build oneTimeKey cert+key if supplied in applet parameter:
++		 */
++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
++			ClientCertDialog d = new ClientCertDialog();
++			viewer.oneTimeKey = d.queryUser();
++		}
++		if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
++			int idx = viewer.oneTimeKey.indexOf(",");
++
++			String onetimekey = viewer.oneTimeKey.substring(0, idx);
++			byte[] key = hex2bytes(onetimekey);
++			String onetimecert = viewer.oneTimeKey.substring(idx+1);
++			byte[] cert = hex2bytes(onetimecert);
++
++			KeyFactory kf = KeyFactory.getInstance("RSA");
++			PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
++			PrivateKey ff = kf.generatePrivate (keysp);
++			if (debug_certs) {
++				dbg("one time key " + ff);
++			}
++
++			CertificateFactory cf = CertificateFactory.getInstance("X.509");
++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++			Certificate[] certs = new Certificate[c.toArray().length];
++			if (c.size() == 1) {
++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++				if (debug_certs) {
++					dbg("one time cert" + tmpcert);
++				}
++				certs[0] = tmpcert;
++			} else {
++				certs = (Certificate[]) c.toArray();
++			}
++
++			KeyStore ks = KeyStore.getInstance("JKS");
++			ks.load(null, null);
++			ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs);
++			String da = KeyManagerFactory.getDefaultAlgorithm();
++			KeyManagerFactory kmf = KeyManagerFactory.getInstance(da);
++			kmf.init(ks, "".toCharArray());
++
++			mykey = kmf.getKeyManagers();
++		}
++
++		/*
++		 * build serverCert cert if supplied in applet parameter:
++		 */
++		if (viewer.serverCert != null) {
++			CertificateFactory cf = CertificateFactory.getInstance("X.509");
++			byte[] cert = hex2bytes(viewer.serverCert);
++			Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
++			trustsrvCerts = new Certificate[c.toArray().length];
++			if (c.size() == 1) {
++				Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
++				trustsrvCerts[0] = tmpcert;
++			} else {
++				trustsrvCerts = (Certificate[]) c.toArray();
++			}
++		}
++
++		/* the trust loc certs context: */
++		try {
++			trustloc_ctx = SSLContext.getInstance("SSL");
++
++			/*
++			 * below is a failed attempt to get jvm's default
++			 * trust manager using null (below) makes it so
++			 * for HttpsURLConnection the server cannot be
++			 * verified (no prompting.)
++			 */
++			if (false) {
++				boolean didit = false;
++				TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
++				tmf.init((KeyStore) null);
++				TrustManager [] tml = tmf.getTrustManagers();
++				for (int i = 0; i < tml.length; i++) {
++					TrustManager tm = tml[i];
++					if (tm instanceof X509TrustManager) {
++						TrustManager tm1[] = new TrustManager[1];
++						tm1[0] = tm;
++						trustloc_ctx.init(mykey, tm1, null);
++						didit = true;
++						break;
++					}
++				}
++				if (!didit) {
++					trustloc_ctx.init(mykey, null, null);
++				}
++			} else {
++				/* we have to set trust manager to null */
++				trustloc_ctx.init(mykey, null, null);
++			}
++
++		} catch (Exception e) {
++			String msg = "SSL trustloc_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust all certs context: */
++		try {
++			trustall_ctx = SSLContext.getInstance("SSL");
++			trustall_ctx.init(mykey, trustAllCerts, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustall_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust url certs context: */
++		try {
++			trusturl_ctx = SSLContext.getInstance("SSL");
++			trusturl_ctx.init(mykey, trustUrlCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trusturl_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust srv certs context: */
++		try {
++			trustsrv_ctx = SSLContext.getInstance("SSL");
++			trustsrv_ctx.init(mykey, trustSrvCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustsrv_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++
++		/* the trust the one cert from server context: */
++		try {
++			trustone_ctx = SSLContext.getInstance("SSL");
++			trustone_ctx.init(mykey, trustOneCert, new
++			    java.security.SecureRandom());
++
++		} catch (Exception e) {
++			String msg = "SSL trustone_ctx FAILED.";
++			dbg(msg);
++			throw new Exception(msg);
++		}
++	}
++
++	/*
++	 * we call this early on to 1) check for a proxy, 2) grab
++	 * Browser/JVM accepted HTTPS cert.
++	 */
++	public void check_for_proxy_and_grab_vnc_server_cert() {
++		
++		trusturlCerts = null;
++		proxy_in_use = false;
++
++		if (viewer.ignoreProxy) {
++			/* applet param says skip it. */
++			/* the downside is we do not set trusturlCerts for comparison later... */
++			/* nor do we autodetect x11vnc for GET=1. */
++			return;
++		}
++
++		dbg("------------------------------------------------");
++		dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
++
++		dbg("TRYING HTTPS:");
++		String ustr = "https://" + host + ":";
++		if (viewer.httpsPort != null) {
++			ustr += viewer.httpsPort;
++		} else {
++			ustr += port;
++		}
++		ustr += viewer.urlPrefix + "/check.https.proxy.connection";
++		dbg("ustr is: " + ustr);
++
++		try {
++			/* prepare for an HTTPS URL connection to host:port */
++			URL url = new URL(ustr);
++			HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
++
++			if (mykey != null) {
++				/* with oneTimeKey (mykey) we can't use the default SSL context */
++				if (trustsrvCerts != null) {
++					dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
++					https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());	
++				} else if (trustloc_ctx != null) {
++					dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
++					https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());	
++				}
++			}
++
++			https.setUseCaches(false);
++			https.setRequestMethod("GET");
++			https.setRequestProperty("Pragma", "No-Cache");
++			https.setRequestProperty("Proxy-Connection", "Keep-Alive");
++			https.setDoInput(true);
++
++			dbg("trying https.connect()");
++			https.connect();
++
++			dbg("trying https.getServerCertificates()");
++			trusturlCerts = https.getServerCertificates();
++
++			if (trusturlCerts == null) {
++				dbg("set trusturlCerts to null!");
++			} else {
++				dbg("set trusturlCerts to non-null");
++			}
++
++			if (https.usingProxy()) {
++				proxy_in_use = true;
++				dbg("An HTTPS proxy is in use. There may be connection problems.");
++			}
++
++			dbg("trying https.getContent()");
++			Object output = https.getContent();
++			dbg("trying https.disconnect()");
++			https.disconnect();
++			if (! viewer.GET) {
++				String header = https.getHeaderField("VNC-Server");
++				if (header != null && header.startsWith("x11vnc")) {
++					dbg("detected x11vnc server (1), setting GET=1");
++					viewer.GET = true;
++				}
++			}
++
++		} catch(Exception e) {
++			dbg("HttpsURLConnection: " + e.getMessage());
++		}
++
++		if (proxy_in_use) {
++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++			dbg("------------------------------------------------");
++			return;
++		} else if (trusturlCerts != null && !viewer.forceProxy) {
++			/* Allow user to require HTTP check?  use forceProxy for now. */
++			dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
++			dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++			dbg("------------------------------------------------");
++			return;
++		}
++
++		/*
++		 * XXX need to remember scenario where this extra check
++		 * gives useful info.  User's Browser proxy settings?
++		 */
++		dbg("TRYING HTTP:");
++		ustr = "http://" + host + ":" + port;
++		ustr += viewer.urlPrefix + "/index.vnc";
++		dbg("ustr is: " + ustr);
++
++		try {
++			/* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
++			URL url = new URL(ustr);
++			HttpURLConnection http = (HttpURLConnection)
++			    url.openConnection();
++
++			http.setUseCaches(false);
++			http.setRequestMethod("GET");
++			http.setRequestProperty("Pragma", "No-Cache");
++			http.setRequestProperty("Proxy-Connection", "Keep-Alive");
++			http.setDoInput(true);
++
++			dbg("trying http.connect()");
++			http.connect();
++
++			if (http.usingProxy()) {
++				proxy_in_use = true;
++				dbg("An HTTP proxy is in use. There may be connection problems.");
++			}
++			dbg("trying http.getContent()");
++			Object output = http.getContent();
++			dbg("trying http.disconnect()");
++			http.disconnect();
++			if (! viewer.GET) {
++				String header = http.getHeaderField("VNC-Server");
++				if (header != null && header.startsWith("x11vnc")) {
++					dbg("detected x11vnc server (2), setting GET=1");
++					viewer.GET = true;
++				}
++			}
++		} catch(Exception e) {
++			dbg("HttpURLConnection:  " + e.getMessage());
++		}
++		dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
++		dbg("------------------------------------------------");
++	}
++
++	public Socket connectSock() throws IOException {
++		/*
++		 * first try a https connection to detect a proxy, and
++		 * grab the VNC server cert at the same time:
++		 */
++		check_for_proxy_and_grab_vnc_server_cert();
++
++		boolean srv_cert = false;
++		
++		if (trustsrvCerts != null) {
++			/* applet parameter suppled serverCert */
++			dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
++			factory = trustsrv_ctx.getSocketFactory();
++			srv_cert = true;
++		} else if (viewer.trustAllVncCerts) {
++			/* trust all certs (no checking) */
++			dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
++			factory = trustall_ctx.getSocketFactory();
++		} else if (trusturlCerts != null) {
++			/* trust certs the Browser/JVM accepted in check_for_proxy... */
++			dbg("using trusturl_ctx");
++			factory = trusturl_ctx.getSocketFactory();
++		} else {
++			/* trust the local defaults */
++			dbg("using trustloc_ctx");
++			factory = trustloc_ctx.getSocketFactory();
++		}
++
++		socket = null;
++
++		try {
++			if (proxy_in_use && viewer.forceProxy) {
++				throw new Exception("forcing proxy (forceProxy)");
++			} else if (viewer.CONNECT != null) {
++				throw new Exception("forcing CONNECT");
++			}
++
++			int timeout = 6;
++			if (timeout > 0) {
++				socket = (SSLSocket) factory.createSocket();
++				InetSocketAddress inetaddr = new InetSocketAddress(host, port);
++				dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port);
++				socket.connect(inetaddr, timeout * 1000);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++		} catch (Exception esock) {
++			dbg("socket error: " + esock.getMessage());
++			if (proxy_in_use || viewer.CONNECT != null) {
++				proxy_failure = true;
++				if (proxy_in_use) {
++					dbg("HTTPS proxy in use. Trying to go with it.");
++				} else {
++					dbg("viewer.CONNECT reverse proxy in use. Trying to go with it.");
++				}
++				try {
++					socket = proxy_socket(factory);
++				} catch (Exception e) {
++					dbg("proxy_socket error: " + e.getMessage());
++				}
++			} else {
++				/* n.b. socket is left in error state to cause ex. below. */
++			}
++		}
++
++		try {
++			socket.startHandshake();
++
++			dbg("The Server Connection Verified OK on 1st try.");
++
++			java.security.cert.Certificate[] currentTrustedCerts;
++			BrowserCertsDialog bcd;
++
++			SSLSession sess = socket.getSession();
++			currentTrustedCerts = sess.getPeerCertificates();
++
++			if (viewer.trustAllVncCerts) {
++				dbg("viewer.trustAllVncCerts-1  keeping socket.");
++			} else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
++				try {
++					socket.close();
++				} catch (Exception e) {
++					dbg("socket is grumpy.");
++				}
++				socket = null;
++				throw new SSLHandshakeException("no current certs");
++			}
++
++			String serv = "";
++			try {
++				CertInfo ci = new CertInfo(currentTrustedCerts[0]);
++				serv = ci.get_certinfo("CN");
++			} catch (Exception e) {
++				;
++			}
++
++			if (viewer.trustAllVncCerts) {
++				dbg("viewer.trustAllVncCerts-2  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else if (viewer.serverCert != null && trustsrvCerts != null) {
++				dbg("viewer.serverCert-1  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else if (viewer.trustUrlVncCert) {
++				dbg("viewer.trustUrlVncCert-1  skipping browser certs dialog");
++				user_wants_to_see_cert = false;
++			} else {
++				/* have a dialog with the user: */
++				bcd = new BrowserCertsDialog(serv, host + ":" + port);
++				dbg("browser certs dialog begin.");
++				bcd.queryUser();
++				dbg("browser certs dialog finished.");
++
++				if (bcd.showCertDialog) {
++					String msg = "user wants to see cert";
++					dbg(msg);
++					user_wants_to_see_cert = true;
++					if (cert_fail == null) {
++						cert_fail = "user-view";
++					}
++					throw new SSLHandshakeException(msg);
++				} else {
++					user_wants_to_see_cert = false;
++					dbg("browser certs dialog: user said yes, accept it");
++				}
++			}
++
++		} catch (SSLHandshakeException eh)  {
++			dbg("SSLHandshakeException: could not automatically verify Server.");
++			dbg("msg: " + eh.getMessage());
++
++
++			/* send a cleanup string just in case: */
++			String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
++
++			try {
++				OutputStream os = socket.getOutputStream();
++				os.write(getoutstr.getBytes());
++				socket.close();
++			} catch (Exception e) {
++				dbg("socket is grumpy!");
++			}
++
++			/* reload */
++
++			socket = null;
++
++			String reason = null;
++
++			if (srv_cert) {
++				/* for serverCert usage we make this a fatal error. */
++				throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
++				/* see below in TrustDialog were we describe this case to user anyway */
++			}
++
++			/*
++			 * Reconnect, trusting any cert, so we can grab
++			 * the cert to show it to the user in a dialog
++			 * for him to manually accept.  This connection
++			 * is not used for anything else.
++			 */
++			factory = trustall_ctx.getSocketFactory();
++			if (proxy_failure) {
++				socket = proxy_socket(factory);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++			if (debug_certs) {
++				dbg("trusturlCerts: " + trusturlCerts);
++				dbg("trustsrvCerts: " + trustsrvCerts);
++			}
++			if (trusturlCerts == null && cert_fail == null) {
++				cert_fail = "missing-certs";
++			}
++
++			try {
++				socket.startHandshake();
++
++				dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
++
++				/* grab the cert: */
++				try {
++					SSLSession sess = socket.getSession();
++					trustallCerts = sess.getPeerCertificates();
++				} catch (Exception e) {
++					throw new Exception("Could not get " + 
++					    "Peer Certificate");	
++				}
++				if (debug_certs) {
++					dbg("trustallCerts: " + trustallCerts);
++				}
++
++				if (viewer.trustAllVncCerts) {
++					dbg("viewer.trustAllVncCerts-3.  skipping dialog, trusting everything.");
++				} else if (! browser_cert_match()) {
++					/*
++					 * close socket now, we will reopen after
++					 * dialog if user agrees to use the cert.
++					 */
++					try {
++						OutputStream os = socket.getOutputStream();
++						os.write(getoutstr.getBytes());
++						socket.close();
++					} catch (Exception e) {
++						dbg("socket is grumpy!!");
++					}
++					socket = null;
++
++					/* dialog with user to accept cert or not: */
++
++					TrustDialog td= new TrustDialog(host, port,
++					    trustallCerts);
++
++					if (cert_fail == null) {
++						;
++					} else if (cert_fail.equals("user-view")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        You Asked to View the Certificate.";
++					} else if (cert_fail.equals("server-cert-mismatch")) {
++						/* this is now fatal error, see above. */
++						reason = "Reason for this Dialog:\n\n"
++						       + "        The VNC Server's Certificate does not match the Certificate\n"
++						       + "        specified in the supplied 'serverCert' Applet Parameter.";
++					} else if (cert_fail.equals("cert-mismatch")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        The VNC Server's Certificate does not match the Website's\n"
++						       + "        HTTPS Certificate (that you previously accepted; either\n"
++						       + "        manually or automatically via Certificate Authority.)";
++					} else if (cert_fail.equals("missing-certs")) {
++						reason = "Reason for this Dialog:\n\n"
++						       + "        Not all Certificates could be obtained to check.";
++					}
++
++					if (! td.queryUser(reason)) {
++						String msg = "User decided against it.";
++						dbg(msg);
++						throw new IOException(msg);
++					}
++				}
++
++			} catch (Exception ehand2)  {
++				dbg("** Could not TrustAll Verify Server!");
++
++				throw new IOException(ehand2.getMessage());
++			}
++
++			/* reload again: */
++
++			if (socket != null) {
++				try {
++					socket.close();
++				} catch (Exception e) {
++					dbg("socket is grumpy!!!");
++				}
++				socket = null;
++			}
++
++			/*
++			 * Now connect a 3rd time, using the cert
++			 * retrieved during connection 2 (sadly, that
++			 * the user likely blindly agreed to...)
++			 */
++
++			factory = trustone_ctx.getSocketFactory();
++			if (proxy_failure) {
++				socket = proxy_socket(factory);
++			} else {
++				socket = (SSLSocket) factory.createSocket(host, port);
++			}
++
++			try {
++				socket.startHandshake();
++				dbg("TrustAll/TrustOne Server Connection Verified #3.");
++
++			} catch (Exception ehand3)  {
++				dbg("** Could not TrustAll/TrustOne Verify Server #3.");
++
++				throw new IOException(ehand3.getMessage());
++			}
++		}
++
++		/* we have socket (possibly null) at this point, so proceed: */
++
++		/* handle x11vnc GET=1, if applicable: */
++		if (socket != null && viewer.GET) {
++			String str = "GET ";
++			str += viewer.urlPrefix;
++			str += "/request.https.vnc.connection";
++			str += " HTTP/1.0\r\n";
++			str += "Pragma: No-Cache\r\n";
++			str += "\r\n";
++
++			System.out.println("sending: " + str);
++    			OutputStream os = socket.getOutputStream();
++			String type = "os";
++
++			if (type == "os") {
++				os.write(str.getBytes());
++				os.flush();
++				System.out.println("used OutputStream");
++			} else if (type == "bs") {
++				BufferedOutputStream bs = new BufferedOutputStream(os);
++				bs.write(str.getBytes());
++				bs.flush();
++				System.out.println("used BufferedOutputStream");
++			} else if (type == "ds") {
++				DataOutputStream ds = new DataOutputStream(os);
++				ds.write(str.getBytes());
++				ds.flush();
++				System.out.println("used DataOutputStream");
++			}
++			if (false) {
++				String rep = "";
++				DataInputStream is = new DataInputStream(
++				    new BufferedInputStream(socket.getInputStream(), 16384));
++				while (true) {
++					rep += readline(is);
++					if (rep.indexOf("\r\n\r\n") >= 0) {
++						break;
++					}
++				}
++				System.out.println("rep: " + rep);
++			}
++		}
++
++		dbg("SSL returning socket to caller.");
++		dbg("");
++
++		/* could be null, let caller handle that. */
++		return (Socket) socket;
++	}
++
++	boolean browser_cert_match() {
++		String msg = "Browser URL accept previously accepted cert";
++
++		if (user_wants_to_see_cert) {
++			return false;
++		}
++
++		if (viewer.serverCert != null || trustsrvCerts != null) {
++			if (cert_fail == null) {
++				cert_fail = "server-cert-mismatch";
++			}
++		}
++		if (trustallCerts != null && trusturlCerts != null) {
++		    if (trustallCerts.length == trusturlCerts.length) {
++			boolean ok = true;
++			/* check toath trustallCerts (socket) equals trusturlCerts (browser) */
++			for (int i = 0; i < trusturlCerts.length; i++)  {
++				if (! trustallCerts[i].equals(trusturlCerts[i])) {
++					dbg("BCM: cert mismatch at i=" + i);
++					dbg("BCM: cert mismatch  url" + trusturlCerts[i]);
++					dbg("BCM: cert mismatch  all" + trustallCerts[i]);
++					ok = false;
++				}
++			}
++			if (ok) {
++				System.out.println(msg);
++				if (cert_fail == null) {
++					cert_fail = "did-not-fail";
++				}
++				return true;
++			} else {
++				if (cert_fail == null) {
++					cert_fail = "cert-mismatch";
++				}
++				return false;
++			}
++		    }
++		}
++		if (cert_fail == null) {
++			cert_fail = "missing-certs";
++		}
++		return false;
++	}
++
++	private void dbg(String s) {
++		if (debug) {
++			System.out.println(s);
++		}
++	}
++
++	private int gint(String s) {
++		int n = -1;
++		try {
++			Integer I = new Integer(s);
++			n = I.intValue();
++		} catch (Exception ex) {
++			return -1;
++		}
++		return n;
++	}
++
++	/* this will do the proxy CONNECT negotiation and hook us up.  */
++
++	private void proxy_helper(String proxyHost, int proxyPort) {
++
++		boolean proxy_auth = false;
++		String proxy_auth_basic_realm = "";
++		String hp = host + ":" + port;
++		dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
++
++		/* we loop here a few times trying for the password case */
++		for (int k=0; k < 2; k++) {
++			dbg("proxy_in_use psocket: " + k);
++
++			if (proxySock != null) {
++				try {
++					proxySock.close();
++				} catch (Exception e) {
++					dbg("proxy socket is grumpy.");
++				}
++			}
++
++			proxySock = psocket(proxyHost, proxyPort);
++			if (proxySock == null) {
++				dbg("1-a sadly, returning a null socket");
++				return;
++			}
++
++			String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
++			    + "Host: " + hp + "\r\n";
++
++			dbg("requesting via proxy: " + req1);
++
++			if (proxy_auth) {
++				if (proxy_auth_string == null) {
++					ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm);
++					pp.queryUser();
++					proxy_auth_string = pp.getAuth();
++				}
++				//dbg("auth1: " + proxy_auth_string);
++
++				String auth2 = Base64Coder.encodeString(proxy_auth_string);
++				//dbg("auth2: " + auth2);
++
++				req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
++				//dbg("req1: " + req1);
++
++				dbg("added Proxy-Authorization: Basic ... to request");
++			}
++			req1 += "\r\n";
++
++			try {
++				proxy_os.write(req1.getBytes());
++				String reply = readline(proxy_is);
++
++				dbg("proxy replied: " + reply.trim());
++
++				if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) {
++					proxy_auth = true;
++					proxySock.close();
++				} else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++					proxySock.close();
++					proxySock = psocket(proxyHost, proxyPort);
++					if (proxySock == null) {
++						dbg("2-a sadly, returning a null socket");
++						return;
++					}
++				}
++			} catch(Exception e) {
++				dbg("some proxy socket problem: " + e.getMessage());
++			}
++
++			/* read the rest of the HTTP headers */
++			while (true) {
++				String line = readline(proxy_is);
++				dbg("proxy line: " + line.trim());
++				if (proxy_auth) {
++					String uc = line.toLowerCase();
++					if (uc.indexOf("proxy-authenticate:") == 0) {
++						if (uc.indexOf(" basic ") >= 0) {
++							int idx = uc.indexOf(" realm");
++							if (idx >= 0) {
++								proxy_auth_basic_realm = uc.substring(idx+1);
++							}
++						}
++					}
++				}
++				if (line.equals("\r\n") || line.equals("\n")) {
++					break;
++				}
++			}
++			if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
++				/* we only try once for the non-password case: */
++				break;
++			}
++		}
++	}
++
++	public SSLSocket proxy_socket(SSLSocketFactory factory) {
++		Properties props = null;
++		String proxyHost = null;
++		int proxyPort = 0;
++		String proxyHost_nossl = null;
++		int proxyPort_nossl = 0;
++		String str;
++
++		/* see if we can guess the proxy info from Properties: */
++		try {
++			props = System.getProperties();
++		} catch (Exception e) {
++			/* sandboxed applet might not be able to read it. */
++			dbg("props failed: " + e.getMessage());
++		}
++		if (viewer.proxyHost != null) {
++			dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters.");
++			proxyHost = viewer.proxyHost;
++			if (viewer.proxyPort != null) {
++				proxyPort = gint(viewer.proxyPort);
++			} else {
++				proxyPort = 8080;
++			}
++			
++		} else if (props != null) {
++			dbg("\n---------------\nAll props:");
++			props.list(System.out);
++			dbg("\n---------------\n\n");
++
++			/* scrape throught properties looking for proxy info: */
++
++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++				String s = (String) e.nextElement();
++				String v = System.getProperty(s);
++				String s2 = s.toLowerCase();
++				String v2 = v.toLowerCase();
++
++				if (s2.indexOf("proxy.https.host") >= 0) {
++					proxyHost = v2;
++					continue;
++				}
++				if (s2.indexOf("proxy.https.port") >= 0) {
++					proxyPort = gint(v2);
++					continue;
++				}
++				if (s2.indexOf("proxy.http.host") >= 0) {
++					proxyHost_nossl = v2;
++					continue;
++				}
++				if (s2.indexOf("proxy.http.port") >= 0) {
++					proxyPort_nossl = gint(v2);
++					continue;
++				}
++			}
++
++			for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
++				String s = (String) e.nextElement();
++				String v = System.getProperty(s);
++				String s2 = s.toLowerCase();
++				String v2 = v.toLowerCase();
++
++				if (proxyHost != null && proxyPort > 0) {
++					break;
++				}
++
++				// look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082
++				if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) {
++					continue;
++				}
++				if (v2.indexOf("http") < 0) {
++					continue;
++				}
++
++				String[] pieces = v.split("[,;]");
++				for (int i = 0; i < pieces.length; i++) {
++					String p = pieces[i];
++					int j = p.indexOf("https");
++					if (j < 0) {
++						j = p.indexOf("http");
++						if (j < 0) {
++							continue;
++						}
++					}
++					j = p.indexOf("=", j);
++					if (j < 0) {
++						continue;
++					}
++					p = p.substring(j+1);
++					String [] hp = p.split(":");
++					if (hp.length != 2) {
++						continue;
++					}
++					if (hp[0].length() > 1 && hp[1].length() > 1) {
++
++						proxyPort = gint(hp[1]);
++						if (proxyPort < 0) {
++							continue;
++						}
++						proxyHost = new String(hp[0]);
++						break;
++					}
++				}
++			}
++		}
++		if (proxyHost != null) {
++			if (proxyHost_nossl != null && proxyPort_nossl > 0) {
++				dbg("Using http proxy info instead of https.");
++				proxyHost = proxyHost_nossl;
++				proxyPort = proxyPort_nossl;
++			}
++		}
++
++		if (proxy_in_use) {
++			if (proxy_dialog_host != null && proxy_dialog_port > 0) {
++				proxyHost = proxy_dialog_host;
++				proxyPort = proxy_dialog_port;
++			}
++			if (proxyHost != null) {
++				dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort);
++			} else {
++				/* ask user to help us: */
++				ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort);
++				pd.queryUser();
++				proxyHost = pd.getHost(); 
++				proxyPort = pd.getPort();
++				proxy_dialog_host = new String(proxyHost);
++				proxy_dialog_port = proxyPort;
++				dbg("User said host: " + pd.getHost() + " port: " + pd.getPort());
++			}
++
++			proxy_helper(proxyHost, proxyPort);
++			if (proxySock == null) {
++				return null;
++			}
++		} else if (viewer.CONNECT != null) {
++			dbg("viewer.CONNECT psocket:");
++			proxySock = psocket(host, port);
++			if (proxySock == null) {
++				dbg("1-b sadly, returning a null socket");
++				return null;
++			}
++		}
++		
++		if (viewer.CONNECT != null) {
++			String hp = viewer.CONNECT;
++			String req2 = "CONNECT " + hp + " HTTP/1.1\r\n"
++			    + "Host: " + hp + "\r\n\r\n";
++
++			dbg("requesting2: " + req2);
++
++			try {
++				proxy_os.write(req2.getBytes());
++				String reply = readline(proxy_is);
++
++				dbg("proxy replied2: " + reply.trim());
++
++				if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) {
++					proxySock.close();
++					proxySock = psocket(proxyHost, proxyPort);
++					if (proxySock == null) {
++						dbg("2-b sadly, returning a null socket");
++						return null;
++					}
++				}
++			} catch(Exception e) {
++				dbg("proxy socket problem-2: " + e.getMessage());
++			}
++
++			while (true) {
++				String line = readline(proxy_is);
++				dbg("proxy line2: " + line.trim());
++				if (line.equals("\r\n") || line.equals("\n")) {
++					break;
++				}
++			}
++		}
++
++		Socket sslsock = null;
++		try {
++			sslsock = factory.createSocket(proxySock, host, port, true);
++		} catch(Exception e) {
++			dbg("sslsock prob: " + e.getMessage());
++			dbg("3 sadly, returning a null socket");
++		}
++
++		return (SSLSocket) sslsock;
++	}
++
++	Socket psocket(String h, int p) {
++		Socket psock = null;
++		try {
++			psock = new Socket(h, p);
++			proxy_is = new DataInputStream(new BufferedInputStream(
++			    psock.getInputStream(), 16384));
++			proxy_os = psock.getOutputStream();
++		} catch(Exception e) {
++			dbg("psocket prob: " + e.getMessage());
++			return null;
++		}
++
++		return psock;
++	}
++
++	String readline(DataInputStream i) {
++		byte[] ba = new byte[1];
++		String s = new String("");
++		ba[0] = 0;
++		try {
++			while (ba[0] != 0xa) {
++				ba[0] = (byte) i.readUnsignedByte();
++				s += new String(ba);
++			}
++		} catch (Exception e) {
++			;
++		}
++		return s;
++	}
++}
++
++class TrustDialog implements ActionListener {
++	String msg, host, text;
++	int port;
++	java.security.cert.Certificate[] trustallCerts = null;
++	boolean viewing_cert = false;
++	boolean trust_this_session = false;
++
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok, cancel, viewcert;
++	TextArea textarea;
++	Checkbox accept, deny;
++	Dialog dialog;
++
++	String s1 = "Accept this certificate temporarily for this session";
++	String s2 = "Do not accept this certificate and do not connect to"
++	    + " this VNC server";
++	String ln = "\n---------------------------------------------------\n\n";
++		
++	TrustDialog (String h, int p, java.security.cert.Certificate[] s) {
++		host = h;
++		port = p;
++		trustallCerts = s;
++
++		msg = "VNC Server " + host + ":" + port + " Not Verified";
++	}
++
++	public boolean queryUser(String reason) {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame(msg);
++
++		dialog = new Dialog(frame, true);
++
++		String infostr = "";
++		if (trustallCerts.length == 1) {
++			CertInfo ci = new CertInfo(trustallCerts[0]);
++			infostr = ci.get_certinfo("all");
++		}
++		if (reason != null) {
++			reason += "\n\n";
++		}
++
++		text = "\n" 
+++ "Unable to verify the identity of\n"
+++ "\n"
+++ "        " + host + ":" + port + "\n" 
+++ "\n"
+++ infostr
+++ "\n"
+++ "as a trusted VNC server.\n"
+++ "\n"
+++ reason
+++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
+++ "is due to one of the following:\n"
+++ "\n"
+++ " - Your requesting to View the Certificate before accepting.\n"
+++ "\n"
+++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
+++ "   Authority not recognized by your Web Browser or Java Plugin runtime.\n"
+++ "\n"
+++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
+++ "   the Apache Web server has a certificate *different* from the VNC server's.\n"
+++ "\n"
+++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
+++ "   obtained by this applet to compare the VNC Server Certificate against.\n"
+++ "\n"
+++ " - The VNC Server's Certificate does not match the one specified in the\n"
+++ "   supplied 'serverCert' Java Applet Parameter.\n"
+++ "\n"
+++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
+++ "   to connect to.  (Wouldn't that be exciting!!)\n"
+++ "\n"
+++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
+++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
+++ "automatically authenticate this VNC Server.\n"
+++ "\n"
+++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
+++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
+++ "certificate (except for the Apache portal case above where they don't match.)\n"
+++ "\n"
+++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
+++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
+++ "and thereby see no dialog from this VNC Viewer applet.\n"
++;
++
++		/* the accept / do-not-accept radio buttons: */
++		CheckboxGroup checkbox = new CheckboxGroup();
++		accept = new Checkbox(s1, true, checkbox);
++		deny   = new Checkbox(s2, false, checkbox);
++
++		/* put the checkboxes in a panel: */
++		Panel check = new Panel();
++		check.setLayout(new GridLayout(2, 1));
++
++		check.add(accept);
++		check.add(deny);
++
++		/* make the 3 buttons: */
++		ok = new Button("OK");
++		cancel = new Button("Cancel");
++		viewcert = new Button("View Certificate");
++
++		ok.addActionListener(this);
++		cancel.addActionListener(this);
++		viewcert.addActionListener(this);
++
++		/* put the buttons in their own panel: */
++		Panel buttonrow = new Panel();
++		buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT));
++		buttonrow.add(viewcert);
++		buttonrow.add(ok);
++		buttonrow.add(cancel);
++
++		/* label at the top: */
++		Label label = new Label(msg, Label.CENTER);
++		label.setFont(new Font("Helvetica", Font.BOLD, 16));
++
++		/* textarea in the middle */
++		textarea = new TextArea(text, 38, 64,
++		    TextArea.SCROLLBARS_VERTICAL_ONLY);
++		textarea.setEditable(false);
++
++		/* put the two panels in their own panel at bottom: */
++		Panel bot = new Panel();
++		bot.setLayout(new GridLayout(2, 1));
++		bot.add(check);
++		bot.add(buttonrow);
++
++		/* now arrange things inside the dialog: */
++		dialog.setLayout(new BorderLayout());
++
++		dialog.add("North", label);
++		dialog.add("South", bot);
++		dialog.add("Center", textarea);
++
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++
++		return trust_this_session;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++
++		if (evt.getSource() == viewcert) {
++			/* View Certificate button clicked */
++			if (viewing_cert) {
++				/* show the original info text: */
++				textarea.setText(text);
++				viewcert.setLabel("View Certificate");
++				viewing_cert = false;
++			} else {
++				int i;
++				/* show all (likely just one) certs: */
++				textarea.setText("");
++				for (i=0; i < trustallCerts.length; i++) {
++					int j = i + 1;
++					textarea.append("Certificate[" +
++					    j + "]\n\n");
++					textarea.append(
++					    trustallCerts[i].toString());
++					textarea.append(ln);
++				}
++				viewcert.setLabel("View Info");
++				viewing_cert = true;
++
++				textarea.setCaretPosition(0);
++			}
++
++		} else if (evt.getSource() == ok) {
++			/* OK button clicked */
++			if (accept.getState()) {
++				trust_this_session = true;
++			} else {
++				trust_this_session = false;
++			}
++			//dialog.dispose();
++			dialog.hide();
++
++		} else if (evt.getSource() == cancel) {
++			/* Cancel button clicked */
++			trust_this_session = false;
++
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++
++	String get_certinfo() {
++		String all = "";
++		String fields[] = {"CN", "OU", "O", "L", "C"};
++		int i;
++		if (trustallCerts.length < 1) {
++			all = "";
++			return all;
++		}
++		String cert = trustallCerts[0].toString();
++
++		/*
++		 * For now we simply scrape the cert string, there must
++		 * be an API for this... perhaps optionValue?
++		 */
++
++		for (i=0; i < fields.length; i++) {
++			int f, t, t1, t2;
++			String sub, mat = fields[i] + "=";
++			
++			f = cert.indexOf(mat, 0);
++			if (f > 0) {
++				t1 = cert.indexOf(", ", f);
++				t2 = cert.indexOf("\n", f);
++				if (t1 < 0 && t2 < 0) {
++					continue;
++				} else if (t1 < 0) {
++					t = t2;
++				} else if (t2 < 0) {
++					t = t1;
++				} else if (t1 < t2) {
++					t = t1;
++				} else {
++					t = t2;
++				}
++				if (t > f) {
++					sub = cert.substring(f, t);
++					all = all + "        " + sub + "\n";
++				}
++			}
++		}
++		return all;
++	}
++}
++
++class ProxyDialog implements ActionListener {
++	String guessedHost = null;
++	String guessedPort = null;
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok;
++	Dialog dialog;
++	TextField entry;
++	String reply = "";
++
++	ProxyDialog (String h, int p) {
++		guessedHost = h;
++		try {
++			guessedPort = Integer.toString(p);
++		} catch (Exception e) {
++			guessedPort = "8080";
++		}
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Need Proxy host:port");
++
++		dialog = new Dialog(frame, true);
++
++
++		Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER);
++		//label.setFont(new Font("Helvetica", Font.BOLD, 16));
++		entry = new TextField(30);
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		String guess = "";
++		if (guessedHost != null) {
++			guess = guessedHost + ":" + guessedPort;
++		}
++		entry.setText(guess);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry);
++		dialog.add("South", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return;
++	}
++
++	public String getHost() {
++		int i = reply.indexOf(":");
++		if (i < 0) {
++			return "unknown";
++		}
++		String h = reply.substring(0, i);
++		return h;
++	}
++
++	public int getPort() {
++		int i = reply.indexOf(":");
++		int p = 8080;
++		if (i < 0) {
++			return p;
++		}
++		i++;
++		String ps = reply.substring(i);
++		try {
++			Integer I = new Integer(ps);
++			p = I.intValue();
++		} catch (Exception e) {
++			;
++		}
++		return p;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply = entry.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class ProxyPasswdDialog implements ActionListener {
++	String guessedHost = null;
++	String guessedPort = null;
++	String guessedUser = null;
++	String guessedPasswd = null;
++	String realm = null;
++	/*
++	 * this is the gui to show the user the cert and info and ask
++	 * them if they want to continue using this cert.
++	 */
++
++	Button ok;
++	Dialog dialog;
++	TextField entry1;
++	TextField entry2;
++	String reply1 = "";
++	String reply2 = "";
++
++	ProxyPasswdDialog (String h, int p, String realm) {
++		guessedHost = h;
++		try {
++			guessedPort = Integer.toString(p);
++		} catch (Exception e) {
++			guessedPort = "8080";
++		}
++		this.realm = realm;
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Proxy Requires Username and Password");
++
++		dialog = new Dialog(frame, true);
++
++		//Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER);
++		TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE);
++		entry1 = new TextField(30);
++		entry2 = new TextField(30);
++		entry2.setEchoChar('*');
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry1);
++		dialog.add("South",  entry2);
++		dialog.add("East", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return;
++	}
++
++	public String getAuth() {
++		return reply1 + ":" + reply2;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply1 = entry1.getText();
++			reply2 = entry2.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class ClientCertDialog implements ActionListener {
++
++	Button ok;
++	Dialog dialog;
++	TextField entry;
++	String reply = "";
++
++	ClientCertDialog() {
++		;
++	}
++
++	public String queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Enter SSL Client Cert+Key String");
++
++		dialog = new Dialog(frame, true);
++
++
++		Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER);
++		entry = new TextField(30);
++		ok = new Button("OK");
++		ok.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", label);
++		dialog.add("Center", entry);
++		dialog.add("South", ok);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til OK or Cancel pressed. */
++		return reply;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == ok) {
++			reply = entry.getText();
++			//dialog.dispose();
++			dialog.hide();
++		}
++	}
++}
++
++class BrowserCertsDialog implements ActionListener {
++	Button yes, no;
++	Dialog dialog;
++	String vncServer;
++	String hostport;
++	public boolean showCertDialog = true;
++
++	BrowserCertsDialog(String serv, String hp) {
++		vncServer = serv;
++		hostport = hp;
++	}
++
++	public void queryUser() {
++
++		/* create and display the dialog for unverified cert. */
++
++		Frame frame = new Frame("Use Browser/JVM Certs?");
++
++		dialog = new Dialog(frame, true);
++
++		String m = "";
++m += "\n";
++m += "This VNC Viewer applet does not have its own keystore to track\n";
++m += "SSL certificates, and so cannot authenticate the certificate\n";
++m += "of the VNC Server:\n";
++m += "\n";
++m += "        " + hostport + "\n\n        " + vncServer + "\n";
++m += "\n";
++m += "on its own.\n";
++m += "\n";
++m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
++m += "has previously accepted the same certificate.  You may have set\n";
++m += "this up permanently or just for this session, or the server\n";
++m += "certificate was signed by a CA cert that your Web Browser or\n";
++m += "Java VM Plugin has.\n";
++m += "\n";
++m += "If the VNC Server connection times out while you are reading this\n";
++m += "dialog, then restart the connection and try again.\n";
++m += "\n";
++m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
++m += "\n";
++
++		TextArea textarea = new TextArea(m, 22, 64,
++		    TextArea.SCROLLBARS_VERTICAL_ONLY);
++		textarea.setEditable(false);
++		yes = new Button("Yes");
++		yes.addActionListener(this);
++		no = new Button("No, Let Me See the Certificate.");
++		no.addActionListener(this);
++
++		dialog.setLayout(new BorderLayout());
++		dialog.add("North", textarea);
++		dialog.add("Center", yes);
++		dialog.add("South", no);
++		dialog.pack();
++		dialog.resize(dialog.preferredSize());
++
++		dialog.show();	/* block here til Yes or No pressed. */
++		System.out.println("done show()");
++		return;
++	}
++
++	public synchronized void actionPerformed(ActionEvent evt) {
++		System.out.println(evt.getActionCommand());
++		if (evt.getSource() == yes) {
++			showCertDialog = false;
++			//dialog.dispose();
++			dialog.hide();
++		} else if (evt.getSource() == no) {
++			showCertDialog = true;
++			//dialog.dispose();
++			dialog.hide();
++		}
++		System.out.println("done actionPerformed()");
++	}
++}
++
++class CertInfo {
++	String fields[] = {"CN", "OU", "O", "L", "C"};
++	java.security.cert.Certificate cert;
++	String certString = "";
++
++	CertInfo(java.security.cert.Certificate c) {
++		cert = c;
++		certString = cert.toString();
++	}
++	
++	String get_certinfo(String which) {
++		int i;
++		String cs = new String(certString);
++		String all = "";
++
++		/*
++		 * For now we simply scrape the cert string, there must
++		 * be an API for this... perhaps optionValue?
++		 */
++		for (i=0; i < fields.length; i++) {
++			int f, t, t1, t2;
++			String sub, mat = fields[i] + "=";
++			
++			f = cs.indexOf(mat, 0);
++			if (f > 0) {
++				t1 = cs.indexOf(", ", f);
++				t2 = cs.indexOf("\n", f);
++				if (t1 < 0 && t2 < 0) {
++					continue;
++				} else if (t1 < 0) {
++					t = t2;
++				} else if (t2 < 0) {
++					t = t1;
++				} else if (t1 < t2) {
++					t = t1;
++				} else {
++					t = t2;
++				}
++				if (t > f) {
++					sub = cs.substring(f, t);
++					all = all + "        " + sub + "\n";
++					if (which.equals(fields[i])) {
++						return sub;
++					}
++				}
++			}
++		}
++		if (which.equals("all")) {
++			return all;
++		} else {
++			return "";
++		}
++	}
++}
++
++class Base64Coder {
++
++	// Mapping table from 6-bit nibbles to Base64 characters.
++	private static char[]    map1 = new char[64];
++	   static {
++	      int i=0;
++	      for (char c='A'; c<='Z'; c++) map1[i++] = c;
++	      for (char c='a'; c<='z'; c++) map1[i++] = c;
++	      for (char c='0'; c<='9'; c++) map1[i++] = c;
++	      map1[i++] = '+'; map1[i++] = '/'; }
++
++	// Mapping table from Base64 characters to 6-bit nibbles.
++	private static byte[]    map2 = new byte[128];
++	   static {
++	      for (int i=0; i<map2.length; i++) map2[i] = -1;
++	      for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
++
++	/**
++	* Encodes a string into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param s  a String to be encoded.
++	* @return   A String with the Base64 encoded data.
++	*/
++	public static String encodeString (String s) {
++	   return new String(encode(s.getBytes())); }
++
++	/**
++	* Encodes a byte array into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param in  an array containing the data bytes to be encoded.
++	* @return    A character array with the Base64 encoded data.
++	*/
++	public static char[] encode (byte[] in) {
++	   return encode(in,in.length); }
++
++	/**
++	* Encodes a byte array into Base64 format.
++	* No blanks or line breaks are inserted.
++	* @param in   an array containing the data bytes to be encoded.
++	* @param iLen number of bytes to process in <code>in</code>.
++	* @return     A character array with the Base64 encoded data.
++	*/
++	public static char[] encode (byte[] in, int iLen) {
++	   int oDataLen = (iLen*4+2)/3;       // output length without padding
++	   int oLen = ((iLen+2)/3)*4;         // output length including padding
++	   char[] out = new char[oLen];
++	   int ip = 0;
++	   int op = 0;
++	   while (ip < iLen) {
++	      int i0 = in[ip++] & 0xff;
++	      int i1 = ip < iLen ? in[ip++] & 0xff : 0;
++	      int i2 = ip < iLen ? in[ip++] & 0xff : 0;
++	      int o0 = i0 >>> 2;
++	      int o1 = ((i0 &   3) << 4) | (i1 >>> 4);
++	      int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
++	      int o3 = i2 & 0x3F;
++	      out[op++] = map1[o0];
++	      out[op++] = map1[o1];
++	      out[op] = op < oDataLen ? map1[o2] : '='; op++;
++	      out[op] = op < oDataLen ? map1[o3] : '='; op++; }
++	   return out; }
++
++	/**
++	* Decodes a string from Base64 format.
++	* @param s  a Base64 String to be decoded.
++	* @return   A String containing the decoded data.
++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static String decodeString (String s) {
++	   return new String(decode(s)); }
++
++	/**
++	* Decodes a byte array from Base64 format.
++	* @param s  a Base64 String to be decoded.
++	* @return   An array containing the decoded data bytes.
++	* @throws   IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static byte[] decode (String s) {
++	   return decode(s.toCharArray()); }
++
++	/**
++	* Decodes a byte array from Base64 format.
++	* No blanks or line breaks are allowed within the Base64 encoded data.
++	* @param in  a character array containing the Base64 encoded data.
++	* @return    An array containing the decoded data bytes.
++	* @throws    IllegalArgumentException if the input is not valid Base64 encoded data.
++	*/
++	public static byte[] decode (char[] in) {
++	   int iLen = in.length;
++	   if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
++	   while (iLen > 0 && in[iLen-1] == '=') iLen--;
++	   int oLen = (iLen*3) / 4;
++	   byte[] out = new byte[oLen];
++	   int ip = 0;
++	   int op = 0;
++	   while (ip < iLen) {
++	      int i0 = in[ip++];
++	      int i1 = in[ip++];
++	      int i2 = ip < iLen ? in[ip++] : 'A';
++	      int i3 = ip < iLen ? in[ip++] : 'A';
++	      if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++	      int b0 = map2[i0];
++	      int b1 = map2[i1];
++	      int b2 = map2[i2];
++	      int b3 = map2[i3];
++	      if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
++		 throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
++	      int o0 = ( b0       <<2) | (b1>>>4);
++	      int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
++	      int o2 = ((b2 &   3)<<6) |  b3;
++	      out[op++] = (byte)o0;
++	      if (op<oLen) out[op++] = (byte)o1;
++	      if (op<oLen) out[op++] = (byte)o2; }
++	   return out; }
++
++	// Dummy constructor.
++	private Base64Coder() {}
++
++}
+diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java
+--- JavaViewer.orig/VncCanvas.java	2005-11-21 18:50:18.000000000 -0500
++++ JavaViewer/VncCanvas.java	2010-11-30 22:57:50.000000000 -0500
+@@ -27,6 +27,13 @@
+ import java.lang.*;
+ import java.util.zip.*;
+ 
++// begin runge/x11vnc
++import java.util.Collections;
++// end runge/x11vnc
++
++// begin runge/x11vnc
++// all the MouseWheel stuff below.
++// end runge/x11vnc
+ 
+ //
+ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+@@ -34,7 +41,7 @@
+ 
+ class VncCanvas
+ 	extends Canvas
+-	implements KeyListener, MouseListener, MouseMotionListener {
++	implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+ 
+ 	VncViewer viewer;
+ 	RfbProto rfb;
+@@ -85,6 +92,22 @@
+ 		
+ 		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+ 
++// begin runge/x11vnc
++// kludge to not show any Java cursor in the canvas since we are
++// showing the soft cursor (should be a user setting...)
++Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor(
++    Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0),
++    "dot");
++this.setCursor(dot);
++
++// while we are at it... get rid of the keyboard traversals that
++// make it so we can't type a Tab character:
++this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
++    Collections.EMPTY_SET);
++this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
++    Collections.EMPTY_SET);
++// end runge/x11vnc
++
+ 		colors = new Color[256];
+ 		// sf@2005 - Now Default
+ 		for (int i = 0; i < 256; i++)
+@@ -186,6 +209,7 @@
+ 			inputEnabled = true;
+ 			addMouseListener(this);
+ 			addMouseMotionListener(this);
++			addMouseWheelListener(this);
+ 			if (viewer.showControls) {
+ 				viewer.buttonPanel.enableRemoteAccessControls(true);
+ 			}
+@@ -193,6 +217,7 @@
+ 			inputEnabled = false;
+ 			removeMouseListener(this);
+ 			removeMouseMotionListener(this);
++			removeMouseWheelListener(this);
+ 			if (viewer.showControls) {
+ 				viewer.buttonPanel.enableRemoteAccessControls(false);
+ 			}
+@@ -202,6 +227,9 @@
+ 	
+ 	public void setPixelFormat() throws IOException {
+ 		// sf@2005 - Adding more color modes
++		if (viewer.graftFtp) {
++			return;
++		}
+ 		if (viewer.options.eightBitColors > 0)
+ 		{
+ 			viewer.options.oldEightBitColors = viewer.options.eightBitColors;
+@@ -237,6 +265,9 @@
+ 		}
+ 		else 
+ 		{
++// begin runge/x11vnc
++			viewer.options.oldEightBitColors = viewer.options.eightBitColors;
++// end runge/x11vnc
+ 			rfb.writeSetPixelFormat(
+ 				32,
+ 				24,
+@@ -376,12 +407,14 @@
+ 		// Start/stop session recording if necessary.
+ 		viewer.checkRecordingStatus();
+ 
+-		rfb.writeFramebufferUpdateRequest(
+-			0,
+-			0,
+-			rfb.framebufferWidth,
+-			rfb.framebufferHeight,
+-			false);
++		if (!viewer.graftFtp) {
++			rfb.writeFramebufferUpdateRequest(
++				0,
++				0,
++				rfb.framebufferWidth,
++				rfb.framebufferHeight,
++				false);
++		}
+ 
+ 		//
+ 		// main dispatch loop
+@@ -390,6 +423,9 @@
+ 		while (true) {
+ 			// Read message type from the server.
+ 			int msgType = rfb.readServerMessageType();
++			if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) {
++				System.out.println("msgType:" + msgType);
++			}
+ 
+ 			// Process the message depending on its type.
+ 			switch (msgType) {
+@@ -1332,6 +1368,9 @@
+ 	public void mouseDragged(MouseEvent evt) {
+ 		processLocalMouseEvent(evt, true);
+ 	}
++	public void mouseWheelMoved(MouseWheelEvent evt) {
++		processLocalMouseWheelEvent(evt);
++	}
+ 
+ 	public void processLocalKeyEvent(KeyEvent evt) {
+ 		if (viewer.rfb != null && rfb.inNormalProtocol) {
+@@ -1367,6 +1406,19 @@
+ 		evt.consume();
+ 	}
+ 
++	public void processLocalMouseWheelEvent(MouseWheelEvent evt) {
++		if (viewer.rfb != null && rfb.inNormalProtocol) {
++			synchronized(rfb) {
++			try {
++				rfb.writeWheelEvent(evt);
++			} catch (Exception e) {
++				e.printStackTrace();
++			}
++				rfb.notify();
++			}
++		}
++	}
++
+ 	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+ 		if (viewer.rfb != null && rfb.inNormalProtocol) {
+ 			if (moved) {
+@@ -1532,9 +1584,14 @@
+ 							else
+ 							{
+ 								result =
+-									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
+-										<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
+-										<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
++// begin runge/x11vnc
++//									0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
++//										<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
++//										<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
++									0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
++										<< 16 | (pixBuf[i * 4 + 1] & 0xFF)
++										<< 8 | (pixBuf[i * 4 + 0] & 0xFF);
++// end runge/x11vnc
+ 							}
+ 						} else {
+ 							result = 0; // Transparent pixel
+@@ -1565,9 +1622,14 @@
+ 						else
+ 						{
+ 							result =
+-								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
+-									<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
+-									<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
++// begin runge/x11vnc
++//								0xFF000000 | (pixBuf[i * 4 + 1] & 0xFF)
++//									<< 16 | (pixBuf[i * 4 + 2] & 0xFF)
++//									<< 8 | (pixBuf[i * 4 + 3] & 0xFF);
++								0xFF000000 | (pixBuf[i * 4 + 2] & 0xFF)
++									<< 16 | (pixBuf[i * 4 + 1] & 0xFF)
++									<< 8 | (pixBuf[i * 4 + 0] & 0xFF);
++// end runge/x11vnc
+ 						}
+ 					} else {
+ 						result = 0; // Transparent pixel
+diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
+--- JavaViewer.orig/VncViewer.java	2006-05-24 15:14:40.000000000 -0400
++++ JavaViewer/VncViewer.java	2010-03-27 18:00:28.000000000 -0400
+@@ -41,6 +41,7 @@
+ import java.io.*;
+ import java.net.*;
+ import javax.swing.*;
++import java.util.Date;
+ 
+ public class VncViewer extends java.applet.Applet
+   implements java.lang.Runnable, WindowListener {
+@@ -80,11 +81,11 @@
+   GridBagLayout gridbag;
+   ButtonPanel buttonPanel;
+   AuthPanel authenticator;
+-  VncCanvas vc;
++  VncCanvas vc = null;
+   OptionsFrame options;
+   ClipboardFrame clipboard;
+   RecordingFrame rec;
+-  FTPFrame ftp; // KMC: FTP Frame declaration
++  FTPFrame ftp = null; // KMC: FTP Frame declaration
+ 
+   // Control session recording.
+   Object recordingSync;
+@@ -96,7 +97,7 @@
+ 
+   // Variables read from parameter values.
+   String host;
+-  int port;
++  int port, vncserverport;
+   String passwordParam;
+   String encPasswordParam;
+   boolean showControls;
+@@ -115,28 +116,75 @@
+   int i;
+   // mslogon support 2 end
+ 
++// begin runge/x11vnc
++boolean disableSSL;
++boolean GET;
++String CONNECT;
++String urlPrefix;
++String httpsPort;
++String oneTimeKey;
++String serverCert;
++String ftpDropDown;
++String proxyHost;
++String proxyPort;
++boolean forceProxy;
++boolean ignoreProxy;
++boolean trustAllVncCerts;
++boolean trustUrlVncCert;
++boolean debugCerts;
++boolean debugKeyboard;
++boolean mapF5_to_atsign;
++boolean forbid_Ctrl_Alt;
++
++boolean ignoreMSLogonCheck;
++boolean delayAuthPanel;
++boolean ftpOnly;
++boolean graftFtp;
++boolean dsmActive;
++
++boolean gotAuth;
++int authGot;
++// end runge/x11vnc
++
++
+   //
+   // init()
+   //
+ 
++public void ftp_init() {
++	boolean show = false;
++	if (ftp != null) {
++		show = true;
++	}
++	ftp = null;
++
++	ftp = new FTPFrame(this); // KMC: FTPFrame creation
++
++	if (show) {
++		ftp.doOpen();
++		rfb.readServerDriveList();
++	}
++}
++
+   public void init() {
+ 
+     readParameters();
+ 
+     if (inSeparateFrame) {
+-      vncFrame = new Frame("Ultr@VNC");
+-      if (!inAnApplet) {
+-	vncFrame.add("Center", this);
+-      }
+-      vncContainer = vncFrame;
++	vncFrame = new Frame("Ultr@VNC");
++	if (!inAnApplet) {
++		vncFrame.add("Center", this);
++	}
++	vncContainer = vncFrame;
+     } else {
+-      vncContainer = this;
++	vncContainer = this;
+     }
+ 
+     recordingSync = new Object();
+ 
+     options = new OptionsFrame(this);
+     clipboard = new ClipboardFrame(this);
++
+     // authenticator = new AuthPanel(false);   // mslogon support : go to connectAndAuthenticate()
+     if (RecordingFrame.checkSecurity())
+       rec = new RecordingFrame(this);
+@@ -147,10 +195,11 @@
+     cursorUpdatesDef = null;
+     eightBitColorsDef = null;
+ 
+-    if (inSeparateFrame)
++    if (inSeparateFrame && vncFrame != null)
+       vncFrame.addWindowListener(this);
+ 
+-    ftp = new FTPFrame(this); // KMC: FTPFrame creation
++    ftp_init();
++
+     rfbThread = new Thread(this);
+     rfbThread.start();
+   }
+@@ -186,6 +235,30 @@
+       gbc.weightx = 1.0;
+       gbc.weighty = 1.0;
+ 
++	if (ftpOnly) {
++		if (showControls) {
++			buttonPanel.enableButtons();
++		}
++		ActionListener taskPerformer = new ActionListener() {
++			public void actionPerformed(ActionEvent evt) {
++				vncFrame.setVisible(false);
++				ftp.setSavedLocations();
++				if (ftp.isVisible()) {
++					ftp.doClose();
++				} else {
++					ftp.doOpen();
++				}
++				rfb.readServerDriveList();
++			}
++		};
++		Timer t = new Timer(300, taskPerformer);
++		t.setRepeats(false);
++		t.start();
++		
++		vc.processNormalProtocol();
++		return;
++	}
++
+ 	// Add ScrollPanel to applet mode
+ 
+ 	// Create a panel which itself is resizeable and can hold
+@@ -286,6 +359,24 @@
+ 
+   void connectAndAuthenticate() throws Exception {
+ 
++	if (graftFtp) {
++		rfb = new RfbProto(host, port, this);
++		rfb.desktopName = "ftponly";
++		rfb.framebufferWidth  = 12;
++		rfb.framebufferHeight = 12;
++		rfb.bitsPerPixel = 32;
++		rfb.depth = 24;
++		rfb.trueColour = true;
++		rfb.redMax   = 255;
++		rfb.greenMax = 255;
++		rfb.blueMax  = 255;
++		rfb.redShift   = 16;
++		rfb.greenShift = 8;
++		rfb.blueShift  = 0;
++		rfb.inNormalProtocol = true;
++		return;
++	}
++
+     // If "ENCPASSWORD" parameter is set, decrypt the password into
+     // the passwordParam string.
+ 
+@@ -336,7 +427,22 @@
+     //
+ 
+     
+-    prologueDetectAuthProtocol() ;
++// begin runge/x11vnc
++	gotAuth = false;
++	if (delayAuthPanel) {
++      		if (tryAuthenticate(null, null)) {
++			if (inSeparateFrame) {
++				vncFrame.pack();
++				vncFrame.show();
++			}
++      			return;
++      		}
++	}
++//  prologueDetectAuthProtocol() ;
++    if (ignoreMSLogonCheck == false) {
++        prologueDetectAuthProtocol() ;
++    }
++// end runge/x11vnc
+ 
+     authenticator = new AuthPanel(mslogon);
+     
+@@ -371,6 +477,7 @@
+      //mslogon support end
+     }
+ 
++    int tries = 0;
+     while (true) {
+       // Wait for user entering a password, or a username and a password
+       synchronized(authenticator) {
+@@ -390,6 +497,13 @@
+ 	break;
+       //mslogon support end
+ 
++// begin runge/x11vnc
++      gotAuth = false;
++      if (++tries > 2) {
++         throw new Exception("Incorrect password entered " + tries + " times.");
++      }
++// end runge/x11vnc
++
+       // Retry on authentication failure.
+       authenticator.retry();
+     }
+@@ -405,9 +519,11 @@
+ 
+   void prologueDetectAuthProtocol() throws Exception {
+ 
+-    rfb = new RfbProto(host, port, this);
++	if (!gotAuth) {
++		rfb = new RfbProto(host, port, this);
+ 
+-    rfb.readVersionMsg();
++		rfb.readVersionMsg();
++	}
+ 
+     System.out.println("RFB server supports protocol version " +
+ 		       rfb.serverMajor + "." + rfb.serverMinor);
+@@ -431,16 +547,36 @@
+ 
+   boolean tryAuthenticate(String us, String pw) throws Exception {
+     
+-    rfb = new RfbProto(host, port, this);
++	int authScheme;
+ 
+-    rfb.readVersionMsg();
++	if (!gotAuth) {
++		rfb = new RfbProto(host, port, this);
+ 
+-    System.out.println("RFB server supports protocol version " +
+-		       rfb.serverMajor + "." + rfb.serverMinor);
++		rfb.readVersionMsg();
+ 
+-    rfb.writeVersionMsg();
++		System.out.println("RFB server supports protocol version: " +
++			       rfb.serverMajor + "." + rfb.serverMinor);
+ 
+-    int authScheme = rfb.readAuthScheme();
++		rfb.writeVersionMsg();
++
++		authScheme = rfb.readAuthScheme();
++
++		gotAuth = true;
++		authGot = authScheme;
++	} else {
++		authScheme = authGot;
++	}
++// begin runge/x11vnc
++	if (delayAuthPanel && pw == null) {
++		if (authScheme == RfbProto.NoAuth) {
++			System.out.println("No authentication needed");
++			return true;
++		} else {
++			return false;
++		}
++	}
++System.out.println("as: " + authScheme);
++// end runge/x11vnc
+ 
+     switch (authScheme) {
+ 
+@@ -629,6 +765,10 @@
+ 
+   void doProtocolInitialisation() throws IOException {
+ 
++	if (graftFtp) {
++		return;
++	}
++
+     rfb.writeClientInit();
+ 
+     rfb.readServerInit();
+@@ -774,9 +914,28 @@
+ 	fatalError("HOST parameter not specified");
+       }
+     }
++    Date d = new Date();
++    System.out.println("-\nSSL VNC Java Applet starting.  " + d);
+ 
+-    String str = readParameter("PORT", true);
+-    port = Integer.parseInt(str);
++    port = 0;
++    String str = readParameter("PORT", false);
++    if (str != null) {
++	port = Integer.parseInt(str);
++    }
++    // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall).
++    vncserverport = 0;
++    str = readParameter("VNCSERVERPORT", false);
++    if (str != null) {
++	vncserverport = Integer.parseInt(str);
++    }
++    if (port == 0 && vncserverport == 0) {
++	fatalError("Neither PORT nor VNCSERVERPORT parameters specified");
++    }
++    if (port == 0) {
++	// Nevertheless, fall back to vncserverport if we have to.
++	System.out.println("using vncserverport: '" + vncserverport + "' for PORT.");
++	port = vncserverport;
++    }
+ 
+     if (inAnApplet) {
+       str = readParameter("Open New Window", false);
+@@ -804,6 +963,158 @@
+     deferScreenUpdates = readIntParameter("Defer screen updates", 20);
+     deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
+     deferUpdateRequests = readIntParameter("Defer update requests", 50);
++
++// begin runge/x11vnc
++    // SSL
++    disableSSL = false;
++    str = readParameter("DisableSSL", false);
++    if (str != null && str.equalsIgnoreCase("Yes"))
++      disableSSL = true;
++
++    httpsPort = readParameter("httpsPort", false);
++
++    // Extra GET, CONNECT string:
++    CONNECT = readParameter("CONNECT", false);
++    if (CONNECT != null) {
++	CONNECT = CONNECT.replaceAll(" ", ":");
++    }
++
++    GET = false;
++    str = readParameter("GET", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++      GET = true;
++    }
++    if (str != null && str.equalsIgnoreCase("1")) {
++      GET = true;
++    }
++
++    urlPrefix = readParameter("urlPrefix", false);
++    if (urlPrefix != null) {
++	urlPrefix = urlPrefix.replaceAll("%2F", "/");
++	urlPrefix = urlPrefix.replaceAll("%2f", "/");
++	urlPrefix = urlPrefix.replaceAll("_2F_", "/");
++	if (urlPrefix.indexOf("/") != 0) {
++		urlPrefix = "/" + urlPrefix;
++	}
++    } else {
++    	urlPrefix = "";
++    }
++    System.out.println("urlPrefix: '" + urlPrefix + "'");
++
++    ftpDropDown = readParameter("ftpDropDown", false);
++    if (ftpDropDown != null) {
++	ftpDropDown = ftpDropDown.replaceAll("%2F", "/");
++	ftpDropDown = ftpDropDown.replaceAll("%2f", "/");
++	ftpDropDown = ftpDropDown.replaceAll("_2F_", "/");
++	ftpDropDown = ftpDropDown.replaceAll("%20", " ");
++	System.out.println("ftpDropDown: '" + ftpDropDown + "'");
++    }
++
++
++    oneTimeKey = readParameter("oneTimeKey", false);
++    if (oneTimeKey != null) {
++    	System.out.println("oneTimeKey is set.");
++    }
++
++    serverCert = readParameter("serverCert", false);
++    if (serverCert != null) {
++    	System.out.println("serverCert is set.");
++    }
++
++    forceProxy = false;
++    proxyHost = null;
++    proxyPort = null;
++    str = readParameter("forceProxy", false);
++    if (str != null) {
++	    if (str.equalsIgnoreCase("Yes")) {
++		forceProxy = true;
++	    } else if (str.equalsIgnoreCase("No")) {
++		forceProxy = false;
++	    } else {
++		forceProxy = true;
++		String[] pieces = str.split(" ");
++		proxyHost = new String(pieces[0]);
++		if (pieces.length >= 2) {
++			proxyPort = new String(pieces[1]);
++		} else {
++			proxyPort = new String("8080");
++		}
++	    }
++    }
++    str = readParameter("proxyHost", false);
++    if (str != null) {
++	proxyHost = new String(str);
++    }
++    str = readParameter("proxyPort", false);
++    if (str != null) {
++	proxyPort = new String(str);
++    }
++    if (proxyHost != null && proxyPort == null) {
++    	proxyPort = new String("8080");
++    }
++
++    ignoreProxy = false;
++    str = readParameter("ignoreProxy", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	ignoreProxy = true;
++    }
++
++    trustAllVncCerts = false;
++    str = readParameter("trustAllVncCerts", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	trustAllVncCerts = true;
++    }
++    trustUrlVncCert = false;
++    str = readParameter("trustUrlVncCert", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	trustUrlVncCert = true;
++    }
++    debugCerts = false;
++    str = readParameter("debugCerts", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	debugCerts = true;
++    }
++    debugKeyboard = false;
++    str = readParameter("debugKeyboard", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	debugKeyboard = true;
++    }
++    mapF5_to_atsign = false;
++    str = readParameter("mapF5_to_atsign", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	mapF5_to_atsign = true;
++    }
++    forbid_Ctrl_Alt = false;
++    str = readParameter("forbid_Ctrl_Alt", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	forbid_Ctrl_Alt = true;
++    }
++    ignoreMSLogonCheck = false;
++    str = readParameter("ignoreMSLogonCheck", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	ignoreMSLogonCheck = true;
++    }
++    ftpOnly = false;
++    str = readParameter("ftpOnly", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	ftpOnly = true;
++    }
++    graftFtp = false;
++    str = readParameter("graftFtp", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	graftFtp = true;
++    }
++    dsmActive = false;
++    str = readParameter("dsmActive", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	dsmActive = true;
++    }
++    delayAuthPanel = false;
++    str = readParameter("delayAuthPanel", false);
++    if (str != null && str.equalsIgnoreCase("Yes")) {
++	delayAuthPanel = true;
++    }
++// end runge/x11vnc
+   }
+ 
+   public String readParameter(String name, boolean required) {

+ 0 - 35
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/LICENSE.txt

@@ -1,35 +0,0 @@
-noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
-
-Some portions of noVNC are copyright to their individual authors.
-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 (all LGPL-3 compatible):
-
-    include/input.js           : LGPL-2 or any later version
-   
-    include/base64.js          : Dual GPL-2 or LGPL-2.1
-   
-    include/des.js             : Various BSD style licenses
-
-    include/jsunzip.js         : zlib/libpng license
-
-    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/
-
-The license texts are included at:
-    docs/LICENSE.LGPL-3 and
-    docs/LICENSE.GPL-3
-    docs/LICENSE.OFL-1.1
-
-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

+ 0 - 102
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/README.md

@@ -1,102 +0,0 @@
-## noVNC: HTML5 VNC Client
-
-
-### Description
-
-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/), [Foreman](http://theforeman.org) 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, tight, tightPNG
-* WebSocket SSL/TLS encryption (i.e. "wss://") support
-* 24-bit true color and 8 bit colour mapped
-* Supports desktop resize notification/pseudo-encoding
-* Local or remote cursor
-* Clipboard copy/paste
-* Clipping or scolling modes for large remote screens
-* Easy site integration and theming (3 example themes included)
-* Licensed under the [LGPLv3](http://www.gnu.org/licenses/lgpl.html)
-
-### Screenshots
-
-Running in Chrome before and after connecting:
-
-<img src="http://kanaka.github.com/noVNC/img/noVNC-5.png" width=400>&nbsp;<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 (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. iOS 4.2+ has built-in
-  WebSocket support.
-
-* 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 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 (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
-
-* Use the launch script to start a mini-webserver and the WebSockets
-  proxy (websockify). The `--vnc` option is used to specify the location of
-  a running VNC server:
-
-    `./utils/launch.sh --vnc localhost:5901`
-
-* Point your browser to the cut-and-paste URL that is output by the
-  launch script. Enter a password if the VNC server has one
-  configured. Hit the Connect button and enjoy!
-
-
-### Other Pages
-
-* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL
-  certificate, starting a VNC server, advanced websockify usage, etc.
-
-* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects.
-
-* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems.
-
-
-### Authors/Contributors
-
-* noVNC : Joel Martin (github.com/kanaka)
-    * New UI and Icons : Chris Gordon
-    * Original Logo : Michael Sersen
-    * tight encoding : Michael Tinglof (Mercuri.ca)
-
-* Included libraries:
-    * web-socket-js : Hiroshi Ichikawa (github.com/gimite/web-socket-js)
-    * as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
-    * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
-    * jsunzip : Erik Moller (github.com/operasoftware/jsunzip),
-    * tinflate : Joergen Ibsen (ibsensoftware.com)
-    * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
-
-

+ 56 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/error-handler.js

@@ -0,0 +1,56 @@
+// NB: this should *not* be included as a module until we have
+// native support in the browsers, so that our error handler
+// can catch script-loading errors.
+
+
+(function(){
+    "use strict";
+
+    // Fallback for all uncought errors
+    function handleError (event, err) {
+        try {
+            var msg = document.getElementById('noVNC_fallback_errormsg');
+
+            // Only show the initial error
+            if (msg.hasChildNodes()) {
+                return false;
+            }
+
+            var div = document.createElement("div");
+            div.classList.add('noVNC_message');
+            div.appendChild(document.createTextNode(event.message));
+            msg.appendChild(div);
+
+            if (event.filename) {
+                div = document.createElement("div");
+                div.className = 'noVNC_location';
+                var text = event.filename;
+                if (event.lineno !== undefined) {
+                    text += ":" + event.lineno;
+                    if (event.colno !== undefined) {
+                        text += ":" + event.colno;
+                    }
+                }
+                div.appendChild(document.createTextNode(text));
+                msg.appendChild(div);
+            }
+
+            if (err && (err.stack !== undefined)) {
+                div = document.createElement("div");
+                div.className = 'noVNC_stack';
+                div.appendChild(document.createTextNode(err.stack));
+                msg.appendChild(div);
+            }
+
+            document.getElementById('noVNC_fallback_error')
+                .classList.add("noVNC_open");
+        } catch (exc) {
+            document.write("noVNC encountered an error.");
+        }
+        // Don't return true since this would prevent the error
+        // from being printed to the browser console.
+        return false;
+    }
+    window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
+    window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
+})();

+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/alt.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="alt.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="18.205425"
+     inkscape:cy="17.531398"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5290">
+      <path
+         d="m 9.9560547,1042.3329 -2.9394531,0 -0.4638672,1.3281 -1.8896485,0 2.7001953,-7.29 2.241211,0 2.7001958,7.29 -1.889649,0 -0.4589843,-1.3281 z m -2.4707031,-1.3526 1.9970703,0 -0.9960938,-2.9003 -1.0009765,2.9003 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5340" />
+      <path
+         d="m 13.188477,1036.0634 1.748046,0 0,7.5976 -1.748046,0 0,-7.5976 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5342" />
+      <path
+         d="m 18.535156,1036.6395 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151367,0.5176 0.151368,0.1318 0.600586,0.1318 l 0.898438,0 0,1.25 -1.499024,0 q -1.035156,0 -1.469726,-0.4297 -0.429688,-0.4345 -0.429688,-1.4697 l 0,-2.3193 -0.86914,0 0,-1.25 0.86914,0 0,-1.5528 1.748047,0 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5344" />
+    </g>
+  </g>
+</svg>

+ 106 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/clipboard.svg

@@ -0,0 +1,106 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="clipboard.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="15.366606"
+     inkscape:cy="16.42981"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 9,6 6,6 C 5.4459889,6 5,6.4459889 5,7 l 0,13 c 0,0.554011 0.4459889,1 1,1 l 13,0 c 0.554011,0 1,-0.445989 1,-1 L 20,7 C 20,6.4459889 19.554011,6 19,6 l -3,0"
+       transform="translate(0,1027.3622)"
+       id="rect6083"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cssssssssc" />
+    <rect
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect6085"
+       width="7"
+       height="4"
+       x="9"
+       y="1031.3622"
+       ry="1.00002" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
+       d="m 8.5071212,1038.8622 7.9999998,0"
+       id="path6087"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
+       d="m 8.5071212,1041.8622 3.9999998,0"
+       id="path6089"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
+       d="m 8.5071212,1044.8622 5.9999998,0"
+       id="path6091"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 96 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/connect.svg

@@ -0,0 +1,96 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="connect.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="37.14834"
+     inkscape:cy="1.9525926"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <g
+       id="g5103"
+       transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-729.15757,315.8823)">
+      <path
+         sodipodi:nodetypes="cssssc"
+         inkscape:connector-curvature="0"
+         id="rect5096"
+         d="m 11,1040.3622 -5,0 c -1.108,0 -2,-0.892 -2,-2 l 0,-4 c 0,-1.108 0.892,-2 2,-2 l 5,0"
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="m 14,1032.3622 5,0 c 1.108,0 2,0.892 2,2 l 0,4 c 0,1.108 -0.892,2 -2,2 l -5,0"
+         id="path5099"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cssssc" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path5101"
+         d="m 9,1036.3622 7,0"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>

+ 96 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/ctrl.svg

@@ -0,0 +1,96 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="ctrl.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="18.205425"
+     inkscape:cy="17.531398"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5290">
+      <path
+         d="m 9.1210938,1043.1898 q -0.5175782,0.2686 -1.0791016,0.4053 -0.5615235,0.1367 -1.171875,0.1367 -1.8212891,0 -2.8857422,-1.0156 -1.0644531,-1.0205 -1.0644531,-2.7637 0,-1.748 1.0644531,-2.7637 1.0644531,-1.0205 2.8857422,-1.0205 0.6103515,0 1.171875,0.1368 0.5615234,0.1367 1.0791016,0.4052 l 0,1.5088 q -0.522461,-0.3564 -1.0302735,-0.5224 -0.5078125,-0.1661 -1.0693359,-0.1661 -1.0058594,0 -1.5820313,0.6446 -0.5761719,0.6445 -0.5761719,1.7773 0,1.1279 0.5761719,1.7725 0.5761719,0.6445 1.5820313,0.6445 0.5615234,0 1.0693359,-0.166 0.5078125,-0.166 1.0302735,-0.5225 l 0,1.5088 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5370" />
+      <path
+         d="m 12.514648,1036.5687 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151368,0.5176 0.151367,0.1318 0.600586,0.1318 l 0.898437,0 0,1.25 -1.499023,0 q -1.035157,0 -1.469727,-0.4297 -0.429687,-0.4345 -0.429687,-1.4697 l 0,-2.3193 -0.8691411,0 0,-1.25 0.8691411,0 0,-1.5528 1.748046,0 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5372" />
+      <path
+         d="m 19.453125,1039.6107 q -0.229492,-0.1074 -0.458984,-0.1562 -0.22461,-0.054 -0.454102,-0.054 -0.673828,0 -1.040039,0.4345 -0.361328,0.4297 -0.361328,1.2354 l 0,2.5195 -1.748047,0 0,-5.4687 1.748047,0 0,0.8984 q 0.336914,-0.5371 0.771484,-0.7813 0.439453,-0.249 1.049805,-0.249 0.08789,0 0.19043,0.01 0.102539,0 0.297851,0.029 l 0.0049,1.582 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5374" />
+      <path
+         d="m 20.332031,1035.9926 1.748047,0 0,7.5976 -1.748047,0 0,-7.5976 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5376" />
+    </g>
+  </g>
+</svg>

+ 100 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/ctrlaltdel.svg

@@ -0,0 +1,100 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="ctrlaltdel.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="11.135667"
+     inkscape:cy="16.407428"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <rect
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5253"
+       width="5"
+       height="5.0000172"
+       x="16"
+       y="1031.3622"
+       ry="1.0000174" />
+    <rect
+       y="1043.3622"
+       x="4"
+       height="5.0000172"
+       width="5"
+       id="rect5255"
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       ry="1.0000174" />
+    <rect
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5257"
+       width="5"
+       height="5.0000172"
+       x="13"
+       y="1043.3622"
+       ry="1.0000174" />
+  </g>
+</svg>

+ 94 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/disconnect.svg

@@ -0,0 +1,94 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="disconnect.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="25.05707"
+     inkscape:cy="11.594858"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <g
+       id="g5171"
+       transform="translate(-24.062499,-6.15775e-4)">
+      <path
+         id="path5110"
+         transform="translate(0,1027.3622)"
+         d="m 39.744141,3.4960938 c -0.769923,0 -1.539607,0.2915468 -2.121094,0.8730468 l -2.566406,2.5664063 1.414062,1.4140625 2.566406,-2.5664063 c 0.403974,-0.404 1.010089,-0.404 1.414063,0 l 2.828125,2.828125 c 0.40398,0.4039 0.403907,1.0101621 0,1.4140629 l -2.566406,2.566406 1.414062,1.414062 2.566406,-2.566406 c 1.163041,-1.1629 1.162968,-3.0791874 0,-4.2421874 L 41.865234,4.3691406 C 41.283747,3.7876406 40.514063,3.4960937 39.744141,3.4960938 Z M 39.017578,9.015625 a 1.0001,1.0001 0 0 0 -0.6875,0.3027344 l -0.445312,0.4453125 1.414062,1.4140621 0.445313,-0.445312 A 1.0001,1.0001 0 0 0 39.017578,9.015625 Z m -6.363281,0.7070312 a 1.0001,1.0001 0 0 0 -0.6875,0.3027348 L 28.431641,13.5625 c -1.163042,1.163 -1.16297,3.079187 0,4.242188 l 2.828125,2.828124 c 1.162974,1.163101 3.079213,1.163101 4.242187,0 l 3.535156,-3.535156 a 1.0001,1.0001 0 1 0 -1.414062,-1.414062 l -3.535156,3.535156 c -0.403974,0.404 -1.010089,0.404 -1.414063,0 l -2.828125,-2.828125 c -0.403981,-0.404 -0.403908,-1.010162 0,-1.414063 l 3.535156,-3.537109 A 1.0001,1.0001 0 0 0 32.654297,9.7226562 Z m 3.109375,2.1621098 -2.382813,2.384765 a 1.0001,1.0001 0 1 0 1.414063,1.414063 l 2.382812,-2.384766 -1.414062,-1.414062 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+      <rect
+         transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"
+         y="752.29541"
+         x="-712.31262"
+         height="18.000017"
+         width="3"
+         id="rect5116"
+         style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>

File diff suppressed because it is too large
+ 71 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/drag.svg


+ 81 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/error.svg

@@ -0,0 +1,81 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="error.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="14.00357"
+     inkscape:cy="12.443398"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 7 3 C 4.7839905 3 3 4.7839905 3 7 L 3 18 C 3 20.21601 4.7839905 22 7 22 L 18 22 C 20.21601 22 22 20.21601 22 18 L 22 7 C 22 4.7839905 20.21601 3 18 3 L 7 3 z M 7.6992188 6 A 1.6916875 1.6924297 0 0 1 8.9121094 6.5117188 L 12.5 10.101562 L 16.087891 6.5117188 A 1.6916875 1.6924297 0 0 1 17.251953 6 A 1.6916875 1.6924297 0 0 1 18.480469 8.90625 L 14.892578 12.496094 L 18.480469 16.085938 A 1.6916875 1.6924297 0 1 1 16.087891 18.478516 L 12.5 14.888672 L 8.9121094 18.478516 A 1.6916875 1.6924297 0 1 1 6.5214844 16.085938 L 10.109375 12.496094 L 6.5214844 8.90625 A 1.6916875 1.6924297 0 0 1 7.6992188 6 z "
+       transform="translate(0,1027.3622)"
+       id="rect4135" />
+  </g>
+</svg>

+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/esc.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="esc.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="18.205425"
+     inkscape:cy="17.531398"
+     inkscape:document-units="px"
+     inkscape:current-layer="text5290"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5290">
+      <path
+         d="m 3.9331055,1036.1464 5.0732422,0 0,1.4209 -3.1933594,0 0,1.3574 3.0029297,0 0,1.4209 -3.0029297,0 0,1.6699 3.3007812,0 0,1.4209 -5.180664,0 0,-7.29 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5314" />
+      <path
+         d="m 14.963379,1038.1385 0,1.3282 q -0.561524,-0.2344 -1.083984,-0.3516 -0.522461,-0.1172 -0.986329,-0.1172 -0.498046,0 -0.742187,0.127 -0.239258,0.122 -0.239258,0.3808 0,0.21 0.180664,0.3223 0.185547,0.1123 0.65918,0.166 l 0.307617,0.044 q 1.342773,0.1709 1.806641,0.5615 0.463867,0.3906 0.463867,1.2256 0,0.874 -0.644531,1.3134 -0.644532,0.4395 -1.923829,0.4395 -0.541992,0 -1.123046,-0.088 -0.576172,-0.083 -1.186524,-0.2539 l 0,-1.3281 q 0.522461,0.2539 1.069336,0.3808 0.551758,0.127 1.118164,0.127 0.512695,0 0.771485,-0.1416 0.258789,-0.1416 0.258789,-0.4199 0,-0.2344 -0.180664,-0.3467 -0.175782,-0.1172 -0.708008,-0.1807 l -0.307617,-0.039 q -1.166993,-0.1465 -1.635743,-0.542 -0.46875,-0.3955 -0.46875,-1.2012 0,-0.8691 0.595703,-1.2891 0.595704,-0.4199 1.826172,-0.4199 0.483399,0 1.015625,0.073 0.532227,0.073 1.157227,0.2294 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5316" />
+      <path
+         d="m 21.066895,1038.1385 0,1.4258 q -0.356446,-0.2441 -0.717774,-0.3613 -0.356445,-0.1172 -0.742187,-0.1172 -0.732422,0 -1.142579,0.4297 -0.405273,0.4248 -0.405273,1.1914 0,0.7666 0.405273,1.1963 0.410157,0.4248 1.142579,0.4248 0.410156,0 0.776367,-0.1221 0.371094,-0.122 0.683594,-0.3613 l 0,1.4307 q -0.410157,0.1513 -0.834961,0.2246 -0.419922,0.078 -0.844727,0.078 -1.479492,0 -2.314453,-0.7568 -0.834961,-0.7618 -0.834961,-2.1143 0,-1.3525 0.834961,-2.1094 0.834961,-0.7617 2.314453,-0.7617 0.429688,0 0.844727,0.078 0.419921,0.073 0.834961,0.2246 z"
+         style="font-size:10px;fill:#ffffff;fill-opacity:1"
+         id="path5318" />
+    </g>
+  </g>
+</svg>

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/expander.svg

@@ -0,0 +1,69 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="9"
+   height="10"
+   viewBox="0 0 9 10"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="expander.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="45.254834"
+     inkscape:cx="9.8737281"
+     inkscape:cy="6.4583132"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:snap-object-midpoints="false"
+     inkscape:object-nodes="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1042.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="M 2.0800781,1042.3633 A 2.0002,2.0002 0 0 0 0,1044.3613 l 0,6 a 2.0002,2.0002 0 0 0 3.0292969,1.7168 l 5,-3 a 2.0002,2.0002 0 0 0 0,-3.4316 l -5,-3 a 2.0002,2.0002 0 0 0 -0.9492188,-0.2832 z"
+       id="path4138"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 93 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/fullscreen.svg

@@ -0,0 +1,93 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="fullscreen.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="16.400723"
+     inkscape:cy="15.083758"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <rect
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5006"
+       width="17"
+       height="17.000017"
+       x="4"
+       y="1031.3622"
+       ry="3.0000174" />
+    <path
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+       d="m 7.5,1044.8622 4,0 -1.5,-1.5 1.5,-1.5 -1,-1 -1.5,1.5 -1.5,-1.5 0,4 z"
+       id="path5017"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path5025"
+       d="m 17.5,1034.8622 -4,0 1.5,1.5 -1.5,1.5 1,1 1.5,-1.5 1.5,1.5 0,-4 z"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+  </g>
+</svg>

+ 82 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/handle.svg

@@ -0,0 +1,82 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="5"
+   height="6"
+   viewBox="0 0 5 6"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="handle.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="32"
+     inkscape:cx="1.3551778"
+     inkscape:cy="8.7800329"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1046.3622)">
+    <path
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 4.0000803,1049.3622 -3,-2 0,4 z"
+       id="path4247"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>

+ 172 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/handle_bg.svg

@@ -0,0 +1,172 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="15"
+   height="50"
+   viewBox="0 0 15 50"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="handle_bg.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="-10.001409"
+     inkscape:cy="24.512566"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1002.3622)">
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4249"
+       width="1"
+       height="1.0000174"
+       x="9.5"
+       y="1008.8622"
+       ry="1.7382812e-05" />
+    <rect
+       ry="1.7382812e-05"
+       y="1013.8622"
+       x="9.5"
+       height="1.0000174"
+       width="1"
+       id="rect4255"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       ry="1.7382812e-05"
+       y="1008.8622"
+       x="4.5"
+       height="1.0000174"
+       width="1"
+       id="rect4261"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4263"
+       width="1"
+       height="1.0000174"
+       x="4.5"
+       y="1013.8622"
+       ry="1.7382812e-05" />
+    <rect
+       ry="1.7382812e-05"
+       y="1039.8622"
+       x="9.5"
+       height="1.0000174"
+       width="1"
+       id="rect4265"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4267"
+       width="1"
+       height="1.0000174"
+       x="9.5"
+       y="1044.8622"
+       ry="1.7382812e-05" />
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4269"
+       width="1"
+       height="1.0000174"
+       x="4.5"
+       y="1039.8622"
+       ry="1.7382812e-05" />
+    <rect
+       ry="1.7382812e-05"
+       y="1044.8622"
+       x="4.5"
+       height="1.0000174"
+       width="1"
+       id="rect4271"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4273"
+       width="1"
+       height="1.0000174"
+       x="9.5"
+       y="1018.8622"
+       ry="1.7382812e-05" />
+    <rect
+       ry="1.7382812e-05"
+       y="1018.8622"
+       x="4.5"
+       height="1.0000174"
+       width="1"
+       id="rect4275"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4277"
+       width="1"
+       height="1.0000174"
+       x="9.5"
+       y="1034.8622"
+       ry="1.7382812e-05" />
+    <rect
+       ry="1.7382812e-05"
+       y="1034.8622"
+       x="4.5"
+       height="1.0000174"
+       width="1"
+       id="rect4279"
+       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+  </g>
+</svg>

+ 42 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/Makefile

@@ -0,0 +1,42 @@
+ICONS := \
+	novnc-16x16.png \
+	novnc-24x24.png \
+	novnc-32x32.png \
+	novnc-48x48.png \
+	novnc-64x64.png
+
+ANDROID_LAUNCHER := \
+	novnc-48x48.png \
+	novnc-72x72.png \
+	novnc-96x96.png \
+	novnc-144x144.png \
+	novnc-192x192.png
+
+IPHONE_LAUNCHER := \
+	novnc-60x60.png \
+	novnc-120x120.png
+
+IPAD_LAUNCHER := \
+	novnc-76x76.png \
+	novnc-152x152.png
+
+ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER)
+
+all: $(ALL_ICONS)
+
+novnc-16x16.png: novnc-icon-sm.svg
+	convert -density 90 \
+		-background transparent "$<" "$@"
+novnc-24x24.png: novnc-icon-sm.svg
+	convert -density 135 \
+		-background transparent "$<" "$@"
+novnc-32x32.png: novnc-icon-sm.svg
+	convert -density 180 \
+		-background transparent "$<" "$@"
+
+novnc-%.png: novnc-icon.svg
+	convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \
+		-background transparent "$<" "$@"
+
+clean:
+	rm -f *.png

BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-120x120.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-144x144.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-152x152.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-16x16.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-192x192.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-24x24.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-32x32.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-48x48.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-60x60.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-64x64.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-72x72.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-76x76.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-96x96.png


+ 163 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-icon-sm.svg

@@ -0,0 +1,163 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   viewBox="0 0 16 16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="novnc-icon-sm.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="45.254834"
+     inkscape:cx="9.722703"
+     inkscape:cy="5.5311896"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:object-nodes="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-midpoints="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4169" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3621)">
+    <rect
+       style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4167"
+       width="16"
+       height="15.999992"
+       x="0"
+       y="1036.3622"
+       ry="2.6666584" />
+    <path
+       style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 2.6666667,1036.3621 C 1.1893373,1036.3621 0,1037.5515 0,1039.0288 l 0,10.6666 c 0,1.4774 1.1893373,2.6667 2.6666667,2.6667 l 4,0 C 11.837333,1052.3621 16,1046.7128 16,1039.6955 l 0,-0.6667 c 0,-1.4773 -1.189337,-2.6667 -2.666667,-2.6667 l -10.6666663,0 z"
+       id="rect4173"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g4381">
+      <g
+         transform="translate(0.25,0.25)"
+         style="fill:#000000;fill-opacity:1"
+         id="g4365">
+        <g
+           style="fill:#000000;fill-opacity:1"
+           id="g4367">
+          <path
+             inkscape:connector-curvature="0"
+             id="path4369"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 4.3289754,1039.3621 c 0.1846149,0 0.3419956,0.071 0.4716623,0.2121 C 4.933546,1039.7121 5,1039.8793 5,1040.0759 l 0,3.2862 -1,0 0,-2.964 c 0,-0.024 -0.011592,-0.036 -0.034038,-0.036 l -1.931924,0 C 2.011349,1040.3621 2,1040.3741 2,1040.3981 l 0,2.964 -1,0 0,-4 z"
+             sodipodi:nodetypes="scsccsssscccs" />
+          <path
+             inkscape:connector-curvature="0"
+             id="path4371"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 6.6710244,1039.3621 2.6579513,0 c 0.184775,0 0.3419957,0.071 0.471662,0.2121 C 9.933546,1039.7121 10,1039.8793 10,1040.0759 l 0,2.5724 c 0,0.1966 -0.066454,0.3655 -0.1993623,0.5069 -0.1296663,0.1379 -0.286887,0.2069 -0.471662,0.2069 l -2.6579513,0 c -0.184775,0 -0.3436164,-0.069 -0.4765247,-0.2069 C 6.0648334,1043.0138 6,1042.8449 6,1042.6483 l 0,-2.5724 c 0,-0.1966 0.064833,-0.3638 0.1944997,-0.5017 0.1329083,-0.1414 0.2917497,-0.2121 0.4765247,-0.2121 z m 2.2949386,1 -1.931926,0 C 7.011344,1040.3621 7,1040.3741 7,1040.3981 l 0,1.928 c 0,0.024 0.011347,0.036 0.034037,0.036 l 1.931926,0 c 0.02269,0 0.034037,-0.012 0.034037,-0.036 l 0,-1.928 c 0,-0.024 -0.011347,-0.036 -0.034037,-0.036 z"
+             sodipodi:nodetypes="sscsscsscsscssssssssss" />
+        </g>
+        <g
+           style="fill:#000000;fill-opacity:1"
+           id="g4373">
+          <path
+             inkscape:connector-curvature="0"
+             id="path4375"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 3,1047.1121 1,-2.75 1,0 -1.5,4 -1,0 -1.5,-4 1,0 z"
+             sodipodi:nodetypes="cccccccc" />
+          <path
+             inkscape:connector-curvature="0"
+             id="path4377"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 9,1046.8621 0,-2.5 1,0 0,4 -1,0 -2,-2.5 0,2.5 -1,0 0,-4 1,0 z"
+             sodipodi:nodetypes="ccccccccccc" />
+          <path
+             inkscape:connector-curvature="0"
+             id="path4379"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 15,1045.3621 -2.96596,0 c -0.02269,0 -0.03404,0.012 -0.03404,0.036 l 0,1.928 c 0,0.024 0.01135,0.036 0.03404,0.036 l 2.96596,0 0,1 -3.324113,0 c -0.188017,0 -0.348479,-0.068 -0.481388,-0.2037 C 11.064833,1048.0192 11,1047.8511 11,1047.6542 l 0,-2.5842 c 0,-0.1969 0.06483,-0.3633 0.194499,-0.4991 0.132909,-0.1392 0.293371,-0.2088 0.481388,-0.2088 l 3.324113,0 z"
+             sodipodi:nodetypes="cssssccscsscscc" />
+        </g>
+      </g>
+      <g
+         id="g4356">
+        <g
+           id="g4347">
+          <path
+             sodipodi:nodetypes="scsccsssscccs"
+             d="m 4.3289754,1039.3621 c 0.1846149,0 0.3419956,0.071 0.4716623,0.2121 C 4.933546,1039.7121 5,1039.8793 5,1040.0759 l 0,3.2862 -1,0 0,-2.964 c 0,-0.024 -0.011592,-0.036 -0.034038,-0.036 l -1.931924,0 c -0.022689,0 -0.034038,0.012 -0.034038,0.036 l 0,2.964 -1,0 0,-4 z"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             id="path4143"
+             inkscape:connector-curvature="0" />
+          <path
+             sodipodi:nodetypes="sscsscsscsscssssssssss"
+             d="m 6.6710244,1039.3621 2.6579513,0 c 0.184775,0 0.3419957,0.071 0.471662,0.2121 C 9.933546,1039.7121 10,1039.8793 10,1040.0759 l 0,2.5724 c 0,0.1966 -0.066454,0.3655 -0.1993623,0.5069 -0.1296663,0.1379 -0.286887,0.2069 -0.471662,0.2069 l -2.6579513,0 c -0.184775,0 -0.3436164,-0.069 -0.4765247,-0.2069 C 6.0648334,1043.0138 6,1042.8449 6,1042.6483 l 0,-2.5724 c 0,-0.1966 0.064833,-0.3638 0.1944997,-0.5017 0.1329083,-0.1414 0.2917497,-0.2121 0.4765247,-0.2121 z m 2.2949386,1 -1.931926,0 C 7.011344,1040.3621 7,1040.3741 7,1040.3981 l 0,1.928 c 0,0.024 0.011347,0.036 0.034037,0.036 l 1.931926,0 c 0.02269,0 0.034037,-0.012 0.034037,-0.036 l 0,-1.928 c 0,-0.024 -0.011347,-0.036 -0.034037,-0.036 z"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             id="path4145"
+             inkscape:connector-curvature="0" />
+        </g>
+        <g
+           id="g4351">
+          <path
+             sodipodi:nodetypes="cccccccc"
+             d="m 3,1047.1121 1,-2.75 1,0 -1.5,4 -1,0 -1.5,-4 1,0 z"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             id="path4147"
+             inkscape:connector-curvature="0" />
+          <path
+             sodipodi:nodetypes="ccccccccccc"
+             d="m 9,1046.8621 0,-2.5 1,0 0,4 -1,0 -2,-2.5 0,2.5 -1,0 0,-4 1,0 z"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             id="path4149"
+             inkscape:connector-curvature="0" />
+          <path
+             sodipodi:nodetypes="cssssccscsscscc"
+             d="m 15,1045.3621 -2.96596,0 c -0.02269,0 -0.03404,0.012 -0.03404,0.036 l 0,1.928 c 0,0.024 0.01135,0.036 0.03404,0.036 l 2.96596,0 0,1 -3.324113,0 c -0.188017,0 -0.348479,-0.068 -0.481388,-0.2037 C 11.064833,1048.0192 11,1047.8511 11,1047.6542 l 0,-2.5842 c 0,-0.1969 0.06483,-0.3633 0.194499,-0.4991 0.132909,-0.1392 0.293371,-0.2088 0.481388,-0.2088 l 3.324113,0 z"
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             id="path4151"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>

+ 163 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/icons/novnc-icon.svg

@@ -0,0 +1,163 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48"
+   height="48"
+   viewBox="0 0 48 48.000001"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="novnc-icon.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="27.187245"
+     inkscape:cy="17.700974"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:object-nodes="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-midpoints="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4169" />
+  </sodipodi:namedview>
+  <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:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1004.3621)">
+    <rect
+       style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4167"
+       width="48"
+       height="48"
+       x="0"
+       y="1004.3621"
+       ry="7.9999785" />
+    <path
+       style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="m 8,1004.3621 c -4.4319881,0 -8,3.568 -8,8 l 0,32 c 0,4.432 3.5680119,8 8,8 l 12,0 c 15.512,0 28,-16.948 28,-38 l 0,-2 c 0,-4.432 -3.568012,-8 -8,-8 l -32,0 z"
+       id="rect4173"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g4300"
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       transform="translate(0.5,0.5)">
+      <g
+         id="g4302"
+         style="fill:#000000;fill-opacity:1;stroke:none">
+        <path
+           sodipodi:nodetypes="scsccsssscccs"
+           d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           id="path4304"
+           inkscape:connector-curvature="0" />
+        <path
+           sodipodi:nodetypes="sscsscsscsscssssssssss"
+           d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           id="path4306"
+           inkscape:connector-curvature="0" />
+      </g>
+      <g
+         id="g4308"
+         style="fill:#000000;fill-opacity:1;stroke:none">
+        <path
+           sodipodi:nodetypes="cccccccc"
+           d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           id="path4310"
+           inkscape:connector-curvature="0" />
+        <path
+           sodipodi:nodetypes="ccccccccccc"
+           d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           id="path4312"
+           inkscape:connector-curvature="0" />
+        <path
+           sodipodi:nodetypes="cssssccscsscscc"
+           d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           id="path4314"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+    <g
+       id="g4291"
+       style="stroke:none">
+      <g
+         id="g4282"
+         style="stroke:none">
+        <path
+           inkscape:connector-curvature="0"
+           id="path4143"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z"
+           sodipodi:nodetypes="scsccsssscccs" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path4145"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z"
+           sodipodi:nodetypes="sscsscsscsscssssssssss" />
+      </g>
+      <g
+         id="g4286"
+         style="stroke:none">
+        <path
+           inkscape:connector-curvature="0"
+           id="path4147"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z"
+           sodipodi:nodetypes="cccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path4149"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z"
+           sodipodi:nodetypes="ccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path4151"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z"
+           sodipodi:nodetypes="cssssccscsscscc" />
+      </g>
+    </g>
+  </g>
+</svg>

+ 81 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/info.svg

@@ -0,0 +1,81 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="info.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="15.720838"
+     inkscape:cy="8.9111233"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 12.5 3 A 9.5 9.4999914 0 0 0 3 12.5 A 9.5 9.4999914 0 0 0 12.5 22 A 9.5 9.4999914 0 0 0 22 12.5 A 9.5 9.4999914 0 0 0 12.5 3 z M 12.5 5 A 1.5 1.5000087 0 0 1 14 6.5 A 1.5 1.5000087 0 0 1 12.5 8 A 1.5 1.5000087 0 0 1 11 6.5 A 1.5 1.5000087 0 0 1 12.5 5 z M 10.521484 8.9785156 L 12.521484 8.9785156 A 1.50015 1.50015 0 0 1 14.021484 10.478516 L 14.021484 15.972656 A 1.50015 1.50015 0 0 1 14.498047 18.894531 C 14.498047 18.894531 13.74301 19.228309 12.789062 18.912109 C 12.312092 18.754109 11.776235 18.366625 11.458984 17.828125 C 11.141734 17.289525 11.021484 16.668469 11.021484 15.980469 L 11.021484 11.980469 L 10.521484 11.980469 A 1.50015 1.50015 0 1 1 10.521484 8.9804688 L 10.521484 8.9785156 z "
+       transform="translate(0,1027.3622)"
+       id="path4136" />
+  </g>
+</svg>

File diff suppressed because it is too large
+ 75 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/keyboard.svg


+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_left.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="mouse_left.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="15.551515"
+     inkscape:cy="12.205592"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
+       id="path6219" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
+       id="path6217" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
+       id="path6215" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
+       id="rect6178" />
+  </g>
+</svg>

+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_middle.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="mouse_middle.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="15.551515"
+     inkscape:cy="12.205592"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
+       id="path6219" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
+       id="path6217" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
+       id="path6215" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
+       id="rect6178" />
+  </g>
+</svg>

+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_none.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="mouse_none.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="23.160825"
+     inkscape:cy="13.208262"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
+       id="path6219" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
+       id="path6217" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
+       id="path6215" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
+       id="rect6178" />
+  </g>
+</svg>

+ 92 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/mouse_right.svg

@@ -0,0 +1,92 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="mouse_right.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="15.551515"
+     inkscape:cy="12.205592"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
+       id="path6219" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
+       id="path6217" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
+       id="path6215" />
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
+       id="rect6178" />
+  </g>
+</svg>

+ 87 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/power.svg

@@ -0,0 +1,87 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="power.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="9.3159849"
+     inkscape:cy="13.436208"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="M 9 6.8183594 C 6.3418164 8.1213032 4.5 10.849161 4.5 14 C 4.5 18.4065 8.0935666 22 12.5 22 C 16.906433 22 20.5 18.4065 20.5 14 C 20.5 10.849161 18.658184 8.1213032 16 6.8183594 L 16 9.125 C 17.514327 10.211757 18.5 11.984508 18.5 14 C 18.5 17.3256 15.825553 20 12.5 20 C 9.1744469 20 6.5 17.3256 6.5 14 C 6.5 11.984508 7.4856727 10.211757 9 9.125 L 9 6.8183594 z "
+       transform="translate(0,1027.3622)"
+       id="path6140" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 12.5,1031.8836 0,6.4786"
+       id="path6142"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+  </g>
+</svg>

+ 76 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/settings.svg

@@ -0,0 +1,76 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="settings.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="14.69683"
+     inkscape:cy="8.8039511"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 11 3 L 11 5.1601562 A 7.5 7.5 0 0 0 8.3671875 6.2460938 L 6.84375 4.7226562 L 4.7226562 6.84375 L 6.2480469 8.3691406 A 7.5 7.5 0 0 0 5.1523438 11 L 3 11 L 3 14 L 5.1601562 14 A 7.5 7.5 0 0 0 6.2460938 16.632812 L 4.7226562 18.15625 L 6.84375 20.277344 L 8.3691406 18.751953 A 7.5 7.5 0 0 0 11 19.847656 L 11 22 L 14 22 L 14 19.839844 A 7.5 7.5 0 0 0 16.632812 18.753906 L 18.15625 20.277344 L 20.277344 18.15625 L 18.751953 16.630859 A 7.5 7.5 0 0 0 19.847656 14 L 22 14 L 22 11 L 19.839844 11 A 7.5 7.5 0 0 0 18.753906 8.3671875 L 20.277344 6.84375 L 18.15625 4.7226562 L 16.630859 6.2480469 A 7.5 7.5 0 0 0 14 5.1523438 L 14 3 L 11 3 z M 12.5 10 A 2.5 2.5 0 0 1 15 12.5 A 2.5 2.5 0 0 1 12.5 15 A 2.5 2.5 0 0 1 10 12.5 A 2.5 2.5 0 0 1 12.5 10 z "
+       transform="translate(0,1027.3622)"
+       id="rect4967" />
+  </g>
+</svg>

+ 86 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/tab.svg

@@ -0,0 +1,86 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="tab.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="11.67335"
+     inkscape:cy="17.881696"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="true"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="m 3,1031.3622 0,8 2,0 0,-4 0,-4 -2,0 z m 2,4 4,4 0,-3 13,0 0,-2 -13,0 0,-3 -4,4 z"
+       id="rect5194"
+       inkscape:connector-curvature="0" />
+    <path
+       id="path5211"
+       d="m 22,1048.3622 0,-8 -2,0 0,4 0,4 2,0 z m -2,-4 -4,-4 0,3 -13,0 0,2 13,0 0,3 4,-4 z"
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 90 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/toggleextrakeys.svg

@@ -0,0 +1,90 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="extrakeys.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="15.234555"
+     inkscape:cy="9.9710826"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 8,1031.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,8.9996 c 0,2.1987 1.8012876,4 4,4 l 9,0 c 2.198712,0 4,-1.8013 4,-4 l 0,-8.9996 c 0,-2.1987 -1.801288,-4 -4,-4 z m 0,2 9,0 c 1.125307,0 2,0.8747 2,2 l 0,7.0005 c 0,1.1253 -0.874693,2 -2,2 l -9,0 c -1.1253069,0 -2,-0.8747 -2,-2 l 0,-7.0005 c 0,-1.1253 0.8746931,-2 2,-2 z"
+       id="rect5006"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ssssssssssssssssss" />
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text4167"
+       transform="matrix(0.96021948,0,0,0.96021948,0.18921715,41.80659)">
+      <path
+         d="m 14.292969,1040.6791 -2.939453,0 -0.463868,1.3281 -1.889648,0 2.700195,-7.29 2.241211,0 2.700196,7.29 -1.889649,0 -0.458984,-1.3281 z m -2.470703,-1.3526 1.99707,0 -0.996094,-2.9004 -1.000976,2.9004 z"
+         id="path4172"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>

+ 81 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/images/warning.svg

@@ -0,0 +1,81 @@
+<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="25"
+   height="25"
+   viewBox="0 0 25 25"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="warning.svg"
+   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#959595"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="16.457343"
+     inkscape:cy="12.179552"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:object-paths="true"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     inkscape:window-x="1920"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-nodes="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4136" />
+  </sodipodi:namedview>
+  <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1027.3622)">
+    <path
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="M 12.513672 3.0019531 C 11.751609 2.9919531 11.052563 3.4242687 10.710938 4.1054688 L 3.2109375 19.105469 C 2.5461937 20.435369 3.5132277 21.9999 5 22 L 20 22 C 21.486772 21.9999 22.453806 20.435369 21.789062 19.105469 L 14.289062 4.1054688 C 13.951849 3.4330688 13.265888 3.0066531 12.513672 3.0019531 z M 12.478516 6.9804688 A 1.50015 1.50015 0 0 1 14 8.5 L 14 14.5 A 1.50015 1.50015 0 1 1 11 14.5 L 11 8.5 A 1.50015 1.50015 0 0 1 12.478516 6.9804688 z M 12.5 17 A 1.5 1.5 0 0 1 14 18.5 A 1.5 1.5 0 0 1 12.5 20 A 1.5 1.5 0 0 1 11 18.5 A 1.5 1.5 0 0 1 12.5 17 z "
+       transform="translate(0,1027.3622)"
+       id="path4208" />
+  </g>
+</svg>

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/de.json

@@ -0,0 +1,69 @@
+{
+    "Connecting...": "Verbinden...",
+    "Disconnecting...": "Verbindung trennen...",
+    "Reconnecting...": "Verbindung wiederherstellen...",
+    "Internal error": "Interner Fehler",
+    "Must set host": "Richten Sie den Server ein",
+    "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
+    "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
+    "Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt",
+    "Disconnected": "Verbindung zum Server getrennt",
+    "New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ",
+    "New connection has been rejected": "Verbindung wurde abgelehnt",
+    "Password is required": "Passwort ist erforderlich",
+    "noVNC encountered an error:": "Ein Fehler ist aufgetreten:",
+    "Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen",
+    "Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen",
+    "viewport drag": "Ansichtsfenster ziehen",
+    "Active Mouse Button": "Aktive Maustaste",
+    "No mousebutton": "Keine Maustaste",
+    "Left mousebutton": "Linke Maustaste",
+    "Middle mousebutton": "Mittlere Maustaste",
+    "Right mousebutton": "Rechte Maustaste",
+    "Keyboard": "Tastatur",
+    "Show Keyboard": "Tastatur anzeigen",
+    "Extra keys": "Zusatztasten",
+    "Show Extra Keys": "Zusatztasten anzeigen",
+    "Ctrl": "Strg",
+    "Toggle Ctrl": "Strg umschalten",
+    "Alt": "Alt",
+    "Toggle Alt": "Alt umschalten",
+    "Send Tab": "Tab senden",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Escape senden",
+    "Ctrl+Alt+Del": "Strg+Alt+Entf",
+    "Send Ctrl-Alt-Del": "Strg+Alt+Entf senden",
+    "Shutdown/Reboot": "Herunterfahren/Neustarten",
+    "Shutdown/Reboot...": "Herunterfahren/Neustarten...",
+    "Power": "Energie",
+    "Shutdown": "Herunterfahren",
+    "Reboot": "Neustarten",
+    "Reset": "Zurücksetzen",
+    "Clipboard": "Zwischenablage",
+    "Clear": "Löschen",
+    "Fullscreen": "Vollbild",
+    "Settings": "Einstellungen",
+    "Shared Mode": "Geteilter Modus",
+    "View Only": "Nur betrachten",
+    "Clip to Window": "Auf Fenster begrenzen",
+    "Scaling Mode:": "Skalierungsmodus:",
+    "None": "Keiner",
+    "Local Scaling": "Lokales skalieren",
+    "Remote Resizing": "Serverseitiges skalieren",
+    "Advanced": "Erweitert",
+    "Repeater ID:": "Repeater ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Verschlüsselt",
+    "Host:": "Server:",
+    "Port:": "Port:",
+    "Path:": "Pfad:",
+    "Automatic Reconnect": "Automatisch wiederverbinden",
+    "Reconnect Delay (ms):": "Wiederverbindungsverzögerung (ms):",
+    "Logging:": "Protokollierung:",
+    "Disconnect": "Verbindung trennen",
+    "Connect": "Verbinden",
+    "Password:": "Passwort:",
+    "Cancel": "Abbrechen",
+    "Canvas not supported.": "Canvas nicht unterstützt."
+}

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/el.json

@@ -0,0 +1,69 @@
+{
+    "Connecting...": "Συνδέεται...",
+    "Disconnecting...": "Aποσυνδέεται...",
+    "Reconnecting...": "Επανασυνδέεται...",
+    "Internal error": "Εσωτερικό σφάλμα",
+    "Must set host": "Πρέπει να οριστεί ο διακομιστής",
+    "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
+    "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
+    "Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε",
+    "Disconnected": "Αποσυνδέθηκε",
+    "New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ",
+    "New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ",
+    "Password is required": "Απαιτείται ο κωδικός πρόσβασης",
+    "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
+    "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
+    "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
+    "viewport drag": "σύρσιμο θεατού πεδίου",
+    "Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού",
+    "No mousebutton": "Χωρίς Πλήκτρο Ποντικιού",
+    "Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού",
+    "Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού",
+    "Right mousebutton": "Δεξί Πλήκτρο Ποντικιού",
+    "Keyboard": "Πληκτρολόγιο",
+    "Show Keyboard": "Εμφάνιση Πληκτρολογίου",
+    "Extra keys": "Επιπλέον πλήκτρα",
+    "Show Extra Keys": "Εμφάνιση Επιπλέον Πλήκτρων",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Εναλλαγή Ctrl",
+    "Alt": "Alt",
+    "Toggle Alt": "Εναλλαγή Alt",
+    "Send Tab": "Αποστολή Tab",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Αποστολή Escape",
+    "Ctrl+Alt+Del": "Ctrl+Alt+Del",
+    "Send Ctrl-Alt-Del": "Αποστολή Ctrl-Alt-Del",
+    "Shutdown/Reboot": "Κλείσιμο/Επανεκκίνηση",
+    "Shutdown/Reboot...": "Κλείσιμο/Επανεκκίνηση...",
+    "Power": "Απενεργοποίηση",
+    "Shutdown": "Κλείσιμο",
+    "Reboot": "Επανεκκίνηση",
+    "Reset": "Επαναφορά",
+    "Clipboard": "Πρόχειρο",
+    "Clear": "Καθάρισμα",
+    "Fullscreen": "Πλήρης Οθόνη",
+    "Settings": "Ρυθμίσεις",
+    "Shared Mode": "Κοινόχρηστη Λειτουργία",
+    "View Only": "Μόνο Θέαση",
+    "Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
+    "Scaling Mode:": "Λειτουργία Κλιμάκωσης:",
+    "None": "Καμία",
+    "Local Scaling": "Τοπική Κλιμάκωση",
+    "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
+    "Advanced": "Για προχωρημένους",
+    "Repeater ID:": "Repeater ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Κρυπτογράφηση",
+    "Host:": "Όνομα διακομιστή:",
+    "Port:": "Πόρτα διακομιστή:",
+    "Path:": "Διαδρομή:",
+    "Automatic Reconnect": "Αυτόματη επανασύνδεση",
+    "Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):",
+    "Logging:": "Καταγραφή:",
+    "Disconnect": "Αποσύνδεση",
+    "Connect": "Σύνδεση",
+    "Password:": "Κωδικός Πρόσβασης:",
+    "Cancel": "Ακύρωση",
+    "Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas"
+}

+ 68 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/es.json

@@ -0,0 +1,68 @@
+{
+    "Connecting...": "Conectando...",
+    "Connected (encrypted) to ": "Conectado (con encriptación) a",
+    "Connected (unencrypted) to ": "Conectado (sin encriptación) a",
+    "Disconnecting...": "Desconectando...",
+    "Disconnected": "Desconectado",
+    "Must set host": "Debes configurar el host",
+    "Reconnecting...": "Reconectando...",
+    "Password is required": "Contraseña es obligatoria",
+    "Disconnect timeout": "Tiempo de desconexión agotado",
+    "noVNC encountered an error:": "noVNC ha encontrado un error:",
+    "Hide/Show the control bar": "Ocultar/Mostrar la barra de control",
+    "Move/Drag Viewport": "Mover/Arrastrar la ventana",
+    "viewport drag": "Arrastrar la ventana",
+    "Active Mouse Button": "Botón activo del ratón",
+    "No mousebutton": "Ningún botón del ratón",
+    "Left mousebutton": "Botón izquierdo del ratón",
+    "Middle mousebutton": "Botón central del ratón",
+    "Right mousebutton": "Botón derecho del ratón",
+    "Keyboard": "Teclado",
+    "Show Keyboard": "Mostrar teclado",
+    "Extra keys": "Teclas adicionales",
+    "Show Extra Keys": "Mostrar Teclas Adicionales",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Pulsar/Soltar Ctrl",
+    "Alt": "Alt",
+    "Toggle Alt": "Pulsar/Soltar Alt",
+    "Send Tab": "Enviar Tabulación",
+    "Tab": "Tabulación",
+    "Esc": "Esc",
+    "Send Escape": "Enviar Escape",
+    "Ctrl+Alt+Del": "Ctrl+Alt+Del",
+    "Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del",
+    "Shutdown/Reboot": "Apagar/Reiniciar",
+    "Shutdown/Reboot...": "Apagar/Reiniciar...",
+    "Power": "Encender",
+    "Shutdown": "Apagar",
+    "Reboot": "Reiniciar",
+    "Reset": "Restablecer",
+    "Clipboard": "Portapapeles",
+    "Clear": "Vaciar",
+    "Fullscreen": "Pantalla Completa",
+    "Settings": "Configuraciones",
+    "Shared Mode": "Modo Compartido",
+    "View Only": "Solo visualización",
+    "Clip to Window": "Recortar al tamaño de la ventana",
+    "Scaling Mode:": "Modo de escalado:",
+    "None": "Ninguno",
+    "Local Scaling": "Escalado Local",
+    "Local Downscaling": "Reducción de escala local",
+    "Remote Resizing": "Cambio de tamaño remoto",
+    "Advanced": "Avanzado",
+    "Local Cursor": "Cursor Local",
+    "Repeater ID:": "ID del Repetidor",
+    "WebSocket": "WebSocket",
+    "Encrypt": "",
+    "Host:": "Host",
+    "Port:": "Puesto",
+    "Path:": "Ruta",
+    "Automatic Reconnect": "Reconexión automática",
+    "Reconnect Delay (ms):": "Retraso en la reconexión (ms)",
+    "Logging:": "Logging",
+    "Disconnect": "Desconectar",
+    "Connect": "Conectar",
+    "Password:": "Contraseña",
+    "Cancel": "Cancelar",
+    "Canvas not supported.": "Canvas no está soportado"
+}

+ 68 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/nl.json

@@ -0,0 +1,68 @@
+{
+    "Connecting...": "Verbinden...",
+    "Connected (encrypted) to ": "Verbonden (versleuteld) met ",
+    "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
+    "Disconnecting...": "Verbinding verbreken...",
+    "Disconnected": "Verbinding verbroken",
+    "Must set host": "Host moeten worden ingesteld",
+    "Reconnecting...": "Opnieuw verbinding maken...",
+    "Password is required": "Wachtwoord is vereist",
+    "Disconnect timeout": "Timeout tijdens verbreken van verbinding",
+    "noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
+    "Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
+    "Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
+    "viewport drag": "kijkvenster slepen",
+    "Active Mouse Button": "Actieve Muisknop",
+    "No mousebutton": "Geen muisknop",
+    "Left mousebutton": "Linker muisknop",
+    "Middle mousebutton": "Middelste muisknop",
+    "Right mousebutton": "Rechter muisknop",
+    "Keyboard": "Toetsenbord",
+    "Show Keyboard": "Toon Toetsenbord",
+    "Extra keys": "Extra toetsen",
+    "Show Extra Keys": "Toon Extra Toetsen",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Ctrl aan/uitzetten",
+    "Alt": "Alt",
+    "Toggle Alt": "Alt aan/uitzetten",
+    "Send Tab": "Tab Sturen",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Escape Sturen",
+    "Ctrl+Alt+Del": "Ctrl-Alt-Del",
+    "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Sturen",
+    "Shutdown/Reboot": "Uitschakelen/Herstarten",
+    "Shutdown/Reboot...": "Uitschakelen/Herstarten...",
+    "Power": "Systeem",
+    "Shutdown": "Uitschakelen",
+    "Reboot": "Herstarten",
+    "Reset": "Resetten",
+    "Clipboard": "Klembord",
+    "Clear": "Wissen",
+    "Fullscreen": "Volledig Scherm",
+    "Settings": "Instellingen",
+    "Shared Mode": "Gedeelde Modus",
+    "View Only": "Alleen Kijken",
+    "Clip to Window": "Randen buiten venster afsnijden",
+    "Scaling Mode:": "Schaalmodus:",
+    "None": "Geen",
+    "Local Scaling": "Lokaal Schalen",
+    "Local Downscaling": "Lokaal Neerschalen",
+    "Remote Resizing": "Op Afstand Formaat Wijzigen",
+    "Advanced": "Geavanceerd",
+    "Local Cursor": "Lokale Cursor",
+    "Repeater ID:": "Repeater ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Versleutelen",
+    "Host:": "Host:",
+    "Port:": "Poort:",
+    "Path:": "Pad:",
+    "Automatic Reconnect": "Automatisch Opnieuw Verbinden",
+    "Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
+    "Logging:": "Logmeldingen:",
+    "Disconnect": "Verbinding verbreken",
+    "Connect": "Verbinden",
+    "Password:": "Wachtwoord:",
+    "Cancel": "Annuleren",
+    "Canvas not supported.": "Canvas wordt niet ondersteund."
+}

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/pl.json

@@ -0,0 +1,69 @@
+{
+    "Connecting...": "Łączenie...",
+    "Disconnecting...": "Rozłączanie...",
+    "Reconnecting...": "Łączenie...",
+    "Internal error": "Błąd wewnętrzny",
+    "Must set host": "Host i port są wymagane",
+    "Connected (encrypted) to ": "Połączenie (szyfrowane) z ",
+    "Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ",
+    "Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte",
+    "Disconnected": "Rozłączony",
+    "New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ",
+    "New connection has been rejected": "Nowe połączenie zostało odrzucone",
+    "Password is required": "Hasło jest wymagane",
+    "noVNC encountered an error:": "noVNC napotkało błąd:",
+    "Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień",
+    "Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport",
+    "viewport drag": "przeciągnij viewport",
+    "Active Mouse Button": "Aktywny Przycisk Myszy",
+    "No mousebutton": "Brak przycisku myszy",
+    "Left mousebutton": "Lewy przycisk myszy",
+    "Middle mousebutton": "Środkowy przycisk myszy",
+    "Right mousebutton": "Prawy przycisk myszy",
+    "Keyboard": "Klawiatura",
+    "Show Keyboard": "Pokaż klawiaturę",
+    "Extra keys": "Przyciski dodatkowe",
+    "Show Extra Keys": "Pokaż przyciski dodatkowe",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Przełącz Ctrl",
+    "Alt": "Alt",
+    "Toggle Alt": "Przełącz Alt",
+    "Send Tab": "Wyślij Tab",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Wyślij Escape",
+    "Ctrl+Alt+Del": "Ctrl+Alt+Del",
+    "Send Ctrl-Alt-Del": "Wyślij Ctrl-Alt-Del",
+    "Shutdown/Reboot": "Wyłącz/Uruchom ponownie",
+    "Shutdown/Reboot...": "Wyłącz/Uruchom ponownie...",
+    "Power": "Włączony",
+    "Shutdown": "Wyłącz",
+    "Reboot": "Uruchom ponownie",
+    "Reset": "Resetuj",
+    "Clipboard": "Schowek",
+    "Clear": "Wyczyść",
+    "Fullscreen": "Pełny ekran",
+    "Settings": "Ustawienia",
+    "Shared Mode": "Tryb Współdzielenia",
+    "View Only": "Tylko Podgląd",
+    "Clip to Window": "Przytnij do Okna",
+    "Scaling Mode:": "Tryb Skalowania:",
+    "None": "Brak",
+    "Local Scaling": "Skalowanie lokalne",
+    "Remote Resizing": "Skalowanie zdalne",
+    "Advanced": "Zaawansowane",
+    "Repeater ID:": "ID Repeatera:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Szyfrowanie",
+    "Host:": "Host:",
+    "Port:": "Port:",
+    "Path:": "Ścieżka:",
+    "Automatic Reconnect": "Automatycznie wznawiaj połączenie",
+    "Reconnect Delay (ms):": "Opóźnienie wznawiania (ms):",
+    "Logging:": "Poziom logowania:",
+    "Disconnect": "Rozłącz",
+    "Connect": "Połącz",
+    "Password:": "Hasło:",
+    "Cancel": "Anuluj",
+    "Canvas not supported.": "Element Canvas nie jest wspierany."
+}

+ 68 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/sv.json

@@ -0,0 +1,68 @@
+{
+    "Connecting...": "Ansluter...",
+    "Connected (encrypted) to ": "Ansluten (krypterat) till ",
+    "Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
+    "Disconnecting...": "Kopplar ner...",
+    "Disconnected": "Frånkopplad",
+    "Must set host": "Du måste specifiera en värd",
+    "Reconnecting...": "Återansluter...",
+    "Password is required": "Lösenord krävs",
+    "Disconnect timeout": "Det tog för lång tid att koppla ner",
+    "noVNC encountered an error:": "noVNC stötte på ett problem:",
+    "Hide/Show the control bar": "Göm/Visa kontrollbaren",
+    "Move/Drag Viewport": "Flytta/Dra Vyn",
+    "viewport drag": "dra vy",
+    "Active Mouse Button": "Aktiv musknapp",
+    "No mousebutton": "Ingen musknapp",
+    "Left mousebutton": "Vänster musknapp",
+    "Middle mousebutton": "Mitten-musknapp",
+    "Right mousebutton": "Höger musknapp",
+    "Keyboard": "Tangentbord",
+    "Show Keyboard": "Visa Tangentbord",
+    "Extra keys": "Extraknappar",
+    "Show Extra Keys": "Visa Extraknappar",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Växla Ctrl",
+    "Alt": "Alt",
+    "Toggle Alt": "Växla Alt",
+    "Send Tab": "Skicka Tab",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Skicka Escape",
+    "Ctrl+Alt+Del": "Ctrl+Alt+Del",
+    "Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del",
+    "Shutdown/Reboot": "Stäng av/Boota om",
+    "Shutdown/Reboot...": "Stäng av/Boota om...",
+    "Power": "Ström",
+    "Shutdown": "Stäng av",
+    "Reboot": "Boota om",
+    "Reset": "Återställ",
+    "Clipboard": "Urklipp",
+    "Clear": "Rensa",
+    "Fullscreen": "Fullskärm",
+    "Settings": "Inställningar",
+    "Shared Mode": "Delat Läge",
+    "View Only": "Endast Visning",
+    "Clip to Window": "Begränsa till Fönster",
+    "Scaling Mode:": "Skalningsläge:",
+    "None": "Ingen",
+    "Local Scaling": "Lokal Skalning",
+    "Local Downscaling": "Lokal Nedskalning",
+    "Remote Resizing": "Ändra Storlek",
+    "Advanced": "Avancerat",
+    "Local Cursor": "Lokal Muspekare",
+    "Repeater ID:": "Repeater-ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Kryptera",
+    "Host:": "Värd:",
+    "Port:": "Port:",
+    "Path:": "Sökväg:",
+    "Automatic Reconnect": "Automatisk Återanslutning",
+    "Reconnect Delay (ms):": "Fördröjning (ms):",
+    "Logging:": "Loggning:",
+    "Disconnect": "Koppla från",
+    "Connect": "Anslut",
+    "Password:": "Lösenord:",
+    "Cancel": "Avbryt",
+    "Canvas not supported.": "Canvas stöds ej"
+}

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/tr.json

@@ -0,0 +1,69 @@
+{
+    "Connecting...": "Bağlanıyor...",
+    "Disconnecting...": "Bağlantı kesiliyor...",
+    "Reconnecting...": "Yeniden bağlantı kuruluyor...",
+    "Internal error": "İç hata",
+    "Must set host": "Sunucuyu kur",
+    "Connected (encrypted) to ": "Bağlı (şifrelenmiş)",
+    "Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)",
+    "Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi",
+    "Disconnected": "Bağlantı kesildi",
+    "New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ",
+    "New connection has been rejected": "Bağlantı reddedildi",
+    "Password is required": "Şifre gerekli",
+    "noVNC encountered an error:": "Bir hata oluştu:",
+    "Hide/Show the control bar": "Denetim masasını Gizle/Göster",
+    "Move/Drag Viewport": "Görünümü Taşı/Sürükle",
+    "viewport drag": "Görüntü penceresini sürükle",
+    "Active Mouse Button": "Aktif Fare Düğmesi",
+    "No mousebutton": "Fare düğmesi yok",
+    "Left mousebutton": "Farenin sol düğmesi",
+    "Middle mousebutton": "Farenin orta düğmesi",
+    "Right mousebutton": "Farenin sağ düğmesi",
+    "Keyboard": "Klavye",
+    "Show Keyboard": "Klavye Düzenini Göster",
+    "Extra keys": "Ekstra tuşlar",
+    "Show Extra Keys": "Ekstra tuşları göster",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Ctrl Değiştir ",
+    "Alt": "Alt",
+    "Toggle Alt": "Alt Değiştir",
+    "Send Tab": "Sekme Gönder",
+    "Tab": "Sekme",
+    "Esc": "Esc",
+    "Send Escape": "Boşluk Gönder",
+    "Ctrl+Alt+Del": "Ctrl + Alt + Del",
+    "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder",
+    "Shutdown/Reboot": "Kapat/Yeniden Başlat",
+    "Shutdown/Reboot...": "Kapat/Yeniden Başlat...",
+    "Power": "Güç",
+    "Shutdown": "Kapat",
+    "Reboot": "Yeniden Başlat",
+    "Reset": "Sıfırla",
+    "Clipboard": "Pano",
+    "Clear": "Temizle",
+    "Fullscreen": "Tam Ekran",
+    "Settings": "Ayarlar",
+    "Shared Mode": "Paylaşım Modu",
+    "View Only": "Sadece Görüntüle",
+    "Clip to Window": "Pencereye Tıkla",
+    "Scaling Mode:": "Ölçekleme Modu:",
+    "None": "Bilinmeyen",
+    "Local Scaling": "Yerel Ölçeklendirme",
+    "Remote Resizing": "Uzaktan Yeniden Boyutlandırma",
+    "Advanced": "Gelişmiş",
+    "Repeater ID:": "Tekralayıcı ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "Şifrele",
+    "Host:": "Ana makine:",
+    "Port:": "Port:",
+    "Path:": "Yol:",
+    "Automatic Reconnect": "Otomatik Yeniden Bağlan",
+    "Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):",
+    "Logging:": "Giriş yapılıyor:",
+    "Disconnect": "Bağlantıyı Kes",
+    "Connect": "Bağlan",
+    "Password:": "Parola:",
+    "Cancel": "Vazgeç",
+    "Canvas not supported.": "Tuval desteklenmiyor."
+}

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/locale/zh.json

@@ -0,0 +1,69 @@
+{
+    "Connecting...": "連線中...",
+    "Disconnecting...": "正在中斷連線...",
+    "Reconnecting...": "重新連線中...",
+    "Internal error": "內部錯誤",
+    "Must set host": "請提供主機資訊",
+    "Connected (encrypted) to ": "已加密連線到",
+    "Connected (unencrypted) to ": "未加密連線到",
+    "Something went wrong, connection is closed": "發生錯誤,連線已關閉",
+    "Failed to connect to server": "無法連線到伺服器",
+    "Disconnected": "連線已中斷",
+    "New connection has been rejected with reason: ": "連線被拒絕,原因:",
+    "New connection has been rejected": "連線被拒絕",
+    "Password is required": "請提供密碼",
+    "noVNC encountered an error:": "noVNC 遇到一個錯誤:",
+    "Hide/Show the control bar": "顯示/隱藏控制列",
+    "Move/Drag Viewport": "拖放顯示範圍",
+    "viewport drag": "顯示範圍拖放",
+    "Active Mouse Button": "啟用滑鼠按鍵",
+    "No mousebutton": "無滑鼠按鍵",
+    "Left mousebutton": "滑鼠左鍵",
+    "Middle mousebutton": "滑鼠中鍵",
+    "Right mousebutton": "滑鼠右鍵",
+    "Keyboard": "鍵盤",
+    "Show Keyboard": "顯示鍵盤",
+    "Extra keys": "額外按鍵",
+    "Show Extra Keys": "顯示額外按鍵",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "切換 Ctrl",
+    "Alt": "Alt",
+    "Toggle Alt": "切換 Alt",
+    "Send Tab": "送出 Tab 鍵",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "送出 Escape 鍵",
+    "Ctrl+Alt+Del": "Ctrl-Alt-Del",
+    "Send Ctrl-Alt-Del": "送出 Ctrl-Alt-Del 快捷鍵",
+    "Shutdown/Reboot": "關機/重新啟動",
+    "Shutdown/Reboot...": "關機/重新啟動...",
+    "Power": "電源",
+    "Shutdown": "關機",
+    "Reboot": "重新啟動",
+    "Reset": "重設",
+    "Clipboard": "剪貼簿",
+    "Clear": "清除",
+    "Fullscreen": "全螢幕",
+    "Settings": "設定",
+    "Shared Mode": "分享模式",
+    "View Only": "僅檢視",
+    "Clip to Window": "限制/裁切視窗大小",
+    "Scaling Mode:": "縮放模式:",
+    "None": "無",
+    "Local Scaling": "本機縮放",
+    "Remote Resizing": "遠端調整大小",
+    "Advanced": "進階",
+    "Repeater ID:": "中繼站 ID",
+    "WebSocket": "WebSocket",
+    "Encrypt": "加密",
+    "Host:": "主機:",
+    "Port:": "連接埠:",
+    "Path:": "路徑:",
+    "Automatic Reconnect": "自動重新連線",
+    "Reconnect Delay (ms):": "重新連線間隔 (ms):",
+    "Logging:": "日誌級別:",
+    "Disconnect": "中斷連線",
+    "Connect": "連線",
+    "Password:": "密碼:",
+    "Cancel": "取消"
+}

+ 170 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/localization.js

@@ -0,0 +1,170 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+/*
+ * Localization Utilities
+ */
+
+export function Localizer() {
+    // Currently configured language
+    this.language = 'en';
+
+    // Current dictionary of translations
+    this.dictionary = undefined;
+}
+
+Localizer.prototype = {
+    // Configure suitable language based on user preferences
+    setup: function (supportedLanguages) {
+        var userLanguages;
+
+        this.language = 'en'; // Default: US English
+
+        /*
+         * Navigator.languages only available in Chrome (32+) and FireFox (32+)
+         * Fall back to navigator.language for other browsers
+         */
+        if (typeof window.navigator.languages == 'object') {
+            userLanguages = window.navigator.languages;
+        } else {
+            userLanguages = [navigator.language || navigator.userLanguage];
+        }
+
+        for (var i = 0;i < userLanguages.length;i++) {
+            var userLang = userLanguages[i];
+            userLang = userLang.toLowerCase();
+            userLang = userLang.replace("_", "-");
+            userLang = userLang.split("-");
+
+            // Built-in default?
+            if ((userLang[0] === 'en') &&
+                ((userLang[1] === undefined) || (userLang[1] === 'us'))) {
+                return;
+            }
+
+            // First pass: perfect match
+            for (var j = 0;j < supportedLanguages.length;j++) {
+                var supLang = supportedLanguages[j];
+                supLang = supLang.toLowerCase();
+                supLang = supLang.replace("_", "-");
+                supLang = supLang.split("-");
+
+                if (userLang[0] !== supLang[0])
+                    continue;
+                if (userLang[1] !== supLang[1])
+                    continue;
+
+                this.language = supportedLanguages[j];
+                return;
+            }
+
+            // Second pass: fallback
+            for (var j = 0;j < supportedLanguages.length;j++) {
+                supLang = supportedLanguages[j];
+                supLang = supLang.toLowerCase();
+                supLang = supLang.replace("_", "-");
+                supLang = supLang.split("-");
+
+                if (userLang[0] !== supLang[0])
+                    continue;
+                if (supLang[1] !== undefined)
+                    continue;
+
+                this.language = supportedLanguages[j];
+                return;
+            }
+        }
+    },
+
+    // Retrieve localised text
+    get: function (id) {
+        if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
+            return this.dictionary[id];
+        } else {
+            return id;
+        }
+    },
+
+    // Traverses the DOM and translates relevant fields
+    // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
+    translateDOM: function () {
+        var self = this;
+        function process(elem, enabled) {
+            function isAnyOf(searchElement, items) {
+                return items.indexOf(searchElement) !== -1;
+            }
+
+            function translateAttribute(elem, attr) {
+                var str = elem.getAttribute(attr);
+                str = self.get(str);
+                elem.setAttribute(attr, str);
+            }
+
+            function translateTextNode(node) {
+                var str = node.data.trim();
+                str = self.get(str);
+                node.data = str;
+            }
+
+            if (elem.hasAttribute("translate")) {
+                if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
+                    enabled = true;
+                } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
+                    enabled = false;
+                }
+            }
+
+            if (enabled) {
+                if (elem.hasAttribute("abbr") &&
+                    elem.tagName === "TH") {
+                    translateAttribute(elem, "abbr");
+                }
+                if (elem.hasAttribute("alt") &&
+                    isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
+                    translateAttribute(elem, "alt");
+                }
+                if (elem.hasAttribute("download") &&
+                    isAnyOf(elem.tagName, ["A", "AREA"])) {
+                    translateAttribute(elem, "download");
+                }
+                if (elem.hasAttribute("label") &&
+                    isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
+                                   "OPTION", "TRACK"])) {
+                    translateAttribute(elem, "label");
+                }
+                // FIXME: Should update "lang"
+                if (elem.hasAttribute("placeholder") &&
+                    isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
+                    translateAttribute(elem, "placeholder");
+                }
+                if (elem.hasAttribute("title")) {
+                    translateAttribute(elem, "title");
+                }
+                if (elem.hasAttribute("value") &&
+                    elem.tagName === "INPUT" &&
+                    isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) {
+                    translateAttribute(elem, "value");
+                }
+            }
+
+            for (var i = 0;i < elem.childNodes.length;i++) {
+                var node = elem.childNodes[i];
+                if (node.nodeType === node.ELEMENT_NODE) {
+                    process(node, enabled);
+                } else if (node.nodeType === node.TEXT_NODE && enabled) {
+                    translateTextNode(node);
+                }
+            }
+        }
+
+        process(document.body, true);
+    },
+};
+
+export var l10n = new Localizer();
+export default l10n.get.bind(l10n);

+ 4 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/CREDITS

@@ -0,0 +1,4 @@
+bell
+        Copyright: Dr. Richard Boulanger et al
+        URL: http://www.archive.org/details/Berklee44v12
+        License: CC-BY Attribution 3.0 Unported

BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/bell.mp3


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/sounds/bell.oga


+ 0 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/include/Orbitron700.ttf → board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/Orbitron700.ttf


+ 0 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/include/Orbitron700.woff → board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/Orbitron700.woff


+ 902 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/base.css

@@ -0,0 +1,902 @@
+/*
+ * noVNC base CSS
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2016 Samuel Mannehed for Cendio AB
+ * Copyright (C) 2016 Pierre Ossman for Cendio AB
+ * noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
+ * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
+ */
+
+/*
+ * Z index layers:
+ *
+ * 0: Main screen
+ * 10: Control bar
+ * 50: Transition blocker
+ * 60: Connection popups
+ * 100: Status bar
+ * ...
+ * 1000: Javascript crash
+ * ...
+ * 10000: Max (used for polyfills)
+ */
+
+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%;
+  touch-action: none;
+}
+
+html {
+  height:100%;
+}
+
+.noVNC_only_touch.noVNC_hidden {
+  display: none;
+}
+
+.noVNC_disabled {
+  color: rgb(128, 128, 128);
+}
+
+/* ----------------------------------------
+ * Spinner
+ * ----------------------------------------
+ */
+
+.noVNC_spinner {
+  position: relative;
+}
+.noVNC_spinner, .noVNC_spinner::before, .noVNC_spinner::after {
+  width: 10px;
+  height: 10px;
+  border-radius: 2px;
+  box-shadow: -60px 10px 0 rgba(255, 255, 255, 0);
+  animation: noVNC_spinner 1.0s linear infinite;
+}
+.noVNC_spinner::before {
+  content: "";
+  position: absolute;
+  left: 0px;
+  top: 0px;
+  animation-delay: -0.1s;
+}
+.noVNC_spinner::after {
+  content: "";
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  animation-delay: 0.1s;
+}
+@keyframes noVNC_spinner {
+  0% { box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); width: 20px; }
+  25% { box-shadow: 20px 10px 0 rgba(255, 255, 255, 1); width: 10px; }
+  50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; }
+}
+
+/* ----------------------------------------
+ * Input Elements
+ * ----------------------------------------
+ */
+
+input[type=input], input[type=password], input[type=number],
+input:not([type]), textarea {
+  /* Disable default rendering */
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  background: none;
+
+  margin: 2px;
+  padding: 2px;
+  border: 1px solid rgb(192, 192, 192);
+  border-radius: 5px;
+  color: black;
+  background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
+}
+
+input[type=button], input[type=submit], select {
+  /* Disable default rendering */
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  background: none;
+
+  margin: 2px;
+  padding: 2px;
+  border: 1px solid rgb(192, 192, 192);
+  border-bottom-width: 2px;
+  border-radius: 5px;
+  color: black;
+  background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240));
+
+  /* This avoids it jumping around when :active */
+  vertical-align: middle;
+}
+
+input[type=button], input[type=submit] {
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+option {
+  color: black;
+  background: white;
+}
+
+input[type=input]:focus, input[type=password]:focus,
+input:not([type]):focus, input[type=button]:focus,
+input[type=submit]:focus,
+textarea:focus, select:focus {
+  box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
+  border-color: rgb(74, 144, 217);
+  outline: none;
+}
+
+input[type=button]::-moz-focus-inner,
+input[type=submit]::-moz-focus-inner {
+  border: none;
+}
+
+input[type=input]:disabled, input[type=password]:disabled,
+input:not([type]):disabled, input[type=button]:disabled,
+input[type=submit]:disabled, input[type=number]:disabled,
+textarea:disabled, select:disabled {
+  color: rgb(128, 128, 128);
+  background: rgb(240, 240, 240);
+}
+
+input[type=button]:active, input[type=submit]:active,
+select:active {
+  border-bottom-width: 1px;
+  margin-top: 3px;
+}
+
+:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
+:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
+:root:not(.noVNC_touch) select:hover:not(:disabled) {
+  background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
+}
+
+/* ----------------------------------------
+ * WebKit centering hacks
+ * ----------------------------------------
+ */
+
+.noVNC_center {
+  /*
+   * This is a workaround because webkit misrenders transforms and
+   * uses non-integer coordinates, resulting in blurry content.
+   * Ideally we'd use "top: 50%; transform: translateY(-50%);" on
+   * the objects instead.
+   */
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+}
+.noVNC_center > * {
+  pointer-events: auto;
+}
+.noVNC_vcenter {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  position: fixed;
+  top: 0;
+  left: 0;
+  height: 100%;
+  pointer-events: none;
+}
+.noVNC_vcenter > * {
+  pointer-events: auto;
+}
+
+/* ----------------------------------------
+ * Layering
+ * ----------------------------------------
+ */
+
+.noVNC_connect_layer {
+  z-index: 60;
+}
+
+/* ----------------------------------------
+ * Fallback error
+ * ----------------------------------------
+ */
+
+#noVNC_fallback_error {
+  z-index: 1000;
+  visibility: hidden;
+}
+#noVNC_fallback_error.noVNC_open {
+  visibility: visible;
+}
+
+#noVNC_fallback_error > div {
+  max-width: 90%;
+  padding: 15px;
+
+  transition: 0.5s ease-in-out;
+
+  transform: translateY(-50px);
+  opacity: 0;
+
+  text-align: center;
+  font-weight: bold;
+  color: #fff;
+
+  border-radius: 10px;
+  box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+  background: rgba(200,55,55,0.8);
+}
+#noVNC_fallback_error.noVNC_open > div {
+  transform: translateY(0);
+  opacity: 1;
+}
+
+#noVNC_fallback_errormsg {
+  font-weight: normal;
+}
+
+#noVNC_fallback_errormsg .noVNC_message {
+  display: inline-block;
+  text-align: left;
+  font-family: monospace;
+  white-space: pre-wrap;
+}
+
+#noVNC_fallback_error .noVNC_location {
+  font-style: italic;
+  font-size: 0.8em;
+  color: rgba(255, 255, 255, 0.8);
+}
+
+#noVNC_fallback_error .noVNC_stack {
+  max-height: 50vh;
+  padding: 10px;
+  margin: 10px;
+  font-size: 0.8em;
+  text-align: left;
+  font-family: monospace;
+  white-space: pre;
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  background: rgba(0, 0, 0, 0.2);
+  overflow: auto;
+}
+
+/* ----------------------------------------
+ * Control Bar
+ * ----------------------------------------
+ */
+
+#noVNC_control_bar_anchor {
+  /* The anchor is needed to get z-stacking to work */
+  position: fixed;
+  z-index: 10;
+
+  transition: 0.5s ease-in-out;
+
+  /* Edge misrenders animations wihthout this */
+  transform: translateX(0);
+}
+:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle {
+  opacity: 0.8;
+}
+#noVNC_control_bar_anchor.noVNC_right {
+  left: auto;
+  right: 0;
+}
+
+#noVNC_control_bar {
+  position: relative;
+  left: -100%;
+
+  transition: 0.5s ease-in-out;
+
+  background-color: rgb(110, 132, 163);
+  border-radius: 0 10px 10px 0;
+
+}
+#noVNC_control_bar.noVNC_open {
+  box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+  left: 0;
+}
+#noVNC_control_bar::before {
+  /* This extra element is to get a proper shadow */
+  content: "";
+  position: absolute;
+  z-index: -1;
+  height: 100%;
+  width: 30px;
+  left: -30px;
+  transition: box-shadow 0.5s ease-in-out;
+}
+#noVNC_control_bar.noVNC_open::before {
+  box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+}
+.noVNC_right #noVNC_control_bar {
+  left: 100%;
+  border-radius: 10px 0 0 10px;
+}
+.noVNC_right #noVNC_control_bar.noVNC_open {
+  left: 0;
+}
+.noVNC_right #noVNC_control_bar::before {
+  visibility: hidden;
+}
+
+#noVNC_control_bar_handle {
+  position: absolute;
+  left: -15px;
+  top: 0;
+  transform: translateY(35px);
+  width: calc(100% + 30px);
+  height: 50px;
+  z-index: -1;
+  cursor: pointer;
+  border-radius: 5px;
+  background-color: rgb(83, 99, 122);
+  background-image: url("../images/handle_bg.svg");
+  background-repeat: no-repeat;
+  background-position: right;
+  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5);
+}
+#noVNC_control_bar_handle:after {
+  content: "";
+  transition: transform 0.5s ease-in-out;
+  background: url("../images/handle.svg");
+  position: absolute;
+  top: 22px; /* (50px-6px)/2 */
+  right: 5px;
+  width: 5px;
+  height: 6px;
+}
+#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
+  transform: translateX(1px) rotate(180deg);
+}
+:root:not(.noVNC_connected) #noVNC_control_bar_handle {
+  display: none;
+}
+.noVNC_right #noVNC_control_bar_handle {
+  background-position: left;
+}
+.noVNC_right #noVNC_control_bar_handle:after {
+  left: 5px;
+  right: 0;
+  transform: translateX(1px) rotate(180deg);
+}
+.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
+  transform: none;
+}
+#noVNC_control_bar_handle div {
+  position: absolute;
+  right: -35px;
+  top: 0;
+  width: 50px;
+  height: 50px;
+}
+:root:not(.noVNC_touch) #noVNC_control_bar_handle div {
+  display: none;
+}
+.noVNC_right #noVNC_control_bar_handle div {
+  left: -35px;
+  right: auto;
+}
+
+#noVNC_control_bar .noVNC_scroll {
+  max-height: 100vh; /* Chrome is buggy with 100% */
+  overflow-x: hidden;
+  overflow-y: auto;
+  padding: 0 10px 0 5px;
+}
+.noVNC_right #noVNC_control_bar .noVNC_scroll {
+  padding: 0 5px 0 10px;
+}
+
+/* Control bar hint */
+#noVNC_control_bar_hint {
+  position: fixed;
+  left: calc(100vw - 50px);
+  right: auto;
+  top: 50%;
+  transform: translateY(-50%) scale(0);
+  width: 100px;
+  height: 50%;
+  max-height: 600px;
+
+  visibility: hidden;
+  opacity: 0;
+  transition: 0.2s ease-in-out;
+  background: transparent;
+  box-shadow: 0 0 10px black, inset 0 0 10px 10px rgba(110, 132, 163, 0.8);
+  border-radius: 10px;
+  transition-delay: 0s;
+}
+#noVNC_control_bar_anchor.noVNC_right #noVNC_control_bar_hint{
+  left: auto;
+  right: calc(100vw - 50px);
+}
+#noVNC_control_bar_hint.noVNC_active {
+  visibility: visible;
+  opacity: 1;
+  transition-delay: 0.2s;
+  transform: translateY(-50%) scale(1);
+}
+
+/* General button style */
+.noVNC_button {
+  display: block;
+  padding: 4px 4px;
+  margin: 10px 0;
+  vertical-align: middle;
+  border:1px solid rgba(255, 255, 255, 0.2);
+  border-radius: 6px;
+}
+.noVNC_button.noVNC_selected {
+  border-color: rgba(0, 0, 0, 0.8);
+  background: rgba(0, 0, 0, 0.5);
+}
+.noVNC_button:disabled {
+  opacity: 0.4;
+}
+.noVNC_button:focus {
+  outline: none;
+}
+.noVNC_button:active {
+  padding-top: 5px;
+  padding-bottom: 3px;
+}
+/* Android browsers don't properly update hover state if touch events
+ * are intercepted, but focus should be safe to display */
+:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover,
+.noVNC_button.noVNC_selected:focus {
+  border-color: rgba(0, 0, 0, 0.4);
+  background: rgba(0, 0, 0, 0.2);
+}
+:root:not(.noVNC_touch) .noVNC_button:hover,
+.noVNC_button:focus {
+  background: rgba(255, 255, 255, 0.2);
+}
+.noVNC_button.noVNC_hidden {
+  display: none;
+}
+
+/* Panels */
+.noVNC_panel {
+  transform: translateX(25px);
+
+  transition: 0.5s ease-in-out;
+
+  max-height: 100vh; /* Chrome is buggy with 100% */
+  overflow-x: hidden;
+  overflow-y: auto;
+
+  visibility: hidden;
+  opacity: 0;
+
+  padding: 15px;
+
+  background: #fff;
+  border-radius: 10px;
+  color: #000;
+  border: 2px solid #E0E0E0;
+  box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+}
+.noVNC_panel.noVNC_open {
+  visibility: visible;
+  opacity: 1;
+  transform: translateX(75px);
+}
+.noVNC_right .noVNC_vcenter {
+  left: auto;
+  right: 0;
+}
+.noVNC_right .noVNC_panel {
+  transform: translateX(-25px);
+}
+.noVNC_right .noVNC_panel.noVNC_open {
+  transform: translateX(-75px);
+}
+
+.noVNC_panel hr {
+  border: none;
+  border-top: 1px solid rgb(192, 192, 192);
+}
+
+.noVNC_panel label {
+  display: block;
+  white-space: nowrap;
+}
+
+.noVNC_panel .noVNC_heading {
+  background-color: rgb(110, 132, 163);
+  border-radius: 5px;
+  padding: 5px;
+  /* Compensate for padding in image */
+  padding-right: 8px;
+  color: white;
+  font-size: 20px;
+  margin-bottom: 10px;
+  white-space: nowrap;
+}
+.noVNC_panel .noVNC_heading img {
+  vertical-align: bottom;
+}
+
+.noVNC_submit {
+  float: right;
+}
+
+/* Expanders */
+.noVNC_expander {
+  cursor: pointer;
+}
+.noVNC_expander::before {
+  content: url("../images/expander.svg");
+  display: inline-block;
+  margin-right: 5px;
+  transition: 0.2s ease-in-out;
+}
+.noVNC_expander.noVNC_open::before {
+  transform: rotateZ(90deg);
+}
+.noVNC_expander ~ * {
+  margin: 5px;
+  margin-left: 10px;
+  padding: 5px;
+  background: rgba(0, 0, 0, 0.05);
+  border-radius: 5px;
+}
+.noVNC_expander:not(.noVNC_open) ~ * {
+  display: none;
+}
+
+/* Control bar content */
+
+#noVNC_control_bar .noVNC_logo {
+  font-size: 13px;
+}
+
+:root:not(.noVNC_connected) #noVNC_view_drag_button {
+  display: none;
+}
+
+/* noVNC Touch Device only buttons */
+:root:not(.noVNC_connected) #noVNC_mobile_buttons {
+  display: none;
+}
+:root:not(.noVNC_touch) #noVNC_mobile_buttons {
+  display: none;
+}
+
+/* Extra manual keys */
+:root:not(.noVNC_connected) #noVNC_extra_keys {
+  display: none;
+}
+
+#noVNC_modifiers {
+  background-color: rgb(92, 92, 92);
+  border: none;
+  padding: 0 10px;
+}
+
+/* Shutdown/Reboot */
+:root:not(.noVNC_connected) #noVNC_power_button {
+  display: none;
+}
+#noVNC_power {
+}
+#noVNC_power_buttons {
+  display: none;
+}
+
+#noVNC_power input[type=button] {
+  width: 100%;
+}
+
+/* Clipboard */
+:root:not(.noVNC_connected) #noVNC_clipboard_button {
+  display: none;
+}
+#noVNC_clipboard {
+  /* Full screen, minus padding and left and right margins */
+  max-width: calc(100vw - 2*15px - 75px - 25px);
+}
+#noVNC_clipboard_text {
+  width: 500px;
+  max-width: 100%;
+}
+
+/* Settings */
+#noVNC_settings {
+}
+#noVNC_settings ul {
+  list-style: none;
+  margin: 0px;
+  padding: 0px;
+}
+#noVNC_setting_port {
+  width: 80px;
+}
+#noVNC_setting_path {
+  width: 100px;
+}
+
+/* Connection Controls */
+:root:not(.noVNC_connected) #noVNC_disconnect_button {
+  display: none;
+}
+
+/* ----------------------------------------
+ * Status Dialog
+ * ----------------------------------------
+ */
+
+#noVNC_status {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  z-index: 100;
+  transform: translateY(-100%);
+
+  cursor: pointer;
+
+  transition: 0.5s ease-in-out;
+
+  visibility: hidden;
+  opacity: 0;
+
+  padding: 5px;
+
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-content: center;
+
+  line-height: 25px;
+  word-wrap: break-word;
+  color: #fff;
+
+  border-bottom: 1px solid rgba(0, 0, 0, 0.9);
+}
+#noVNC_status.noVNC_open {
+  transform: translateY(0);
+  visibility: visible;
+  opacity: 1;
+}
+
+#noVNC_status::before {
+  content: "";
+  display: inline-block;
+  width: 25px;
+  height: 25px;
+  margin-right: 5px;
+}
+
+#noVNC_status.noVNC_status_normal {
+  background: rgba(128,128,128,0.9);
+}
+#noVNC_status.noVNC_status_normal::before {
+  content: url("../images/info.svg") " ";
+}
+#noVNC_status.noVNC_status_error {
+  background: rgba(200,55,55,0.9);
+}
+#noVNC_status.noVNC_status_error::before {
+  content: url("../images/error.svg") " ";
+}
+#noVNC_status.noVNC_status_warn {
+  background: rgba(180,180,30,0.9);
+}
+#noVNC_status.noVNC_status_warn::before {
+  content: url("../images/warning.svg") " ";
+}
+
+/* ----------------------------------------
+ * Connect Dialog
+ * ----------------------------------------
+ */
+
+#noVNC_connect_dlg {
+  transition: 0.5s ease-in-out;
+
+  transform: scale(0, 0);
+  visibility: hidden;
+  opacity: 0;
+}
+#noVNC_connect_dlg.noVNC_open {
+  transform: scale(1, 1);
+  visibility: visible;
+  opacity: 1;
+}
+#noVNC_connect_dlg .noVNC_logo {
+  transition: 0.5s ease-in-out;
+  padding: 10px;
+  margin-bottom: 10px;
+
+  font-size: 80px;
+  text-align: center;
+
+  border-radius: 5px;
+}
+@media (max-width: 440px) {
+  #noVNC_connect_dlg {
+    max-width: calc(100vw - 100px);
+  }
+  #noVNC_connect_dlg .noVNC_logo {
+    font-size: calc(25vw - 30px);
+  }
+}
+#noVNC_connect_button {
+  cursor: pointer;
+
+  padding: 10px;
+
+  color: white;
+  background-color: rgb(110, 132, 163);
+  border-radius: 12px;
+
+  text-align: center;
+  font-size: 20px;
+
+  box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+}
+#noVNC_connect_button div {
+  margin: 2px;
+  padding: 5px 30px;
+  border: 1px solid rgb(83, 99, 122);
+  border-bottom-width: 2px;
+  border-radius: 5px;
+  background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147));
+
+  /* This avoids it jumping around when :active */
+  vertical-align: middle;
+}
+#noVNC_connect_button div:active {
+  border-bottom-width: 1px;
+  margin-top: 3px;
+}
+:root:not(.noVNC_touch) #noVNC_connect_button div:hover {
+  background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155));
+}
+
+#noVNC_connect_button img {
+  vertical-align: bottom;
+  height: 1.3em;
+}
+
+/* ----------------------------------------
+ * Password Dialog
+ * ----------------------------------------
+ */
+
+#noVNC_password_dlg {
+  position: relative;
+
+  transform: translateY(-50px);
+}
+#noVNC_password_dlg.noVNC_open {
+  transform: translateY(0);
+}
+#noVNC_password_dlg ul {
+  list-style: none;
+  margin: 0px;
+  padding: 0px;
+}
+
+/* ----------------------------------------
+ * Main Area
+ * ----------------------------------------
+ */
+
+/* Transition screen */
+#noVNC_transition {
+  display: none;
+
+  position: fixed;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+
+  color: white;
+  background: rgba(0, 0, 0, 0.5);
+  z-index: 50;
+
+  /*display: flex;*/
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+}
+:root.noVNC_loading #noVNC_transition,
+:root.noVNC_connecting #noVNC_transition,
+:root.noVNC_disconnecting #noVNC_transition,
+:root.noVNC_reconnecting #noVNC_transition {
+  display: flex;
+}
+:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button {
+  display: none;
+}
+#noVNC_transition_text {
+  font-size: 1.5em;
+}
+
+/* Main container */
+#noVNC_container {
+  width: 100%;
+  height: 100%;
+  background-color: #313131;
+  border-bottom-right-radius: 800px 600px;
+  /*border-top-left-radius: 800px 600px;*/
+}
+
+#noVNC_keyboardinput {
+  width: 1px;
+  height: 1px;
+  background-color: #fff;
+  color: #fff;
+  border: 0;
+  position: absolute;
+  left: -40px;
+  z-index: -1;
+  ime-mode: disabled;
+}
+
+/*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 {
+  color:yellow;
+  font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
+  line-height:90%;
+  text-shadow: 0.1em 0.1em 0 black;
+}
+.noVNC_logo span{
+  color:green;
+}
+
+#noVNC_bell {
+  display: none;
+}
+
+/* ----------------------------------------
+ * Media sizing
+ * ----------------------------------------
+ */
+
+@media screen and (max-width: 640px){
+  #noVNC_logo {
+    font-size: 150px;
+  }
+}
+
+@media screen and (min-width: 321px) and (max-width: 480px) {
+  #noVNC_logo {
+    font-size: 110px;
+  }
+}
+
+@media screen and (max-width: 320px) {
+  #noVNC_logo {
+    font-size: 90px;
+  }
+}

+ 63 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/styles/lite.css

@@ -0,0 +1,63 @@
+/*
+ * noVNC auto CSS
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2017 Samuel Mannehed for Cendio AB
+ * noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
+ * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
+ */
+
+body {
+  margin:0;
+  background-color:#313131;
+  border-bottom-right-radius: 800px 600px;
+  height:100%;
+  display: flex;
+  flex-direction: column;
+}
+
+html {
+  background-color:#494949;
+  height:100%;
+}
+
+#noVNC_status_bar {
+  width: 100%;
+  display:flex;
+  justify-content: space-between;
+}
+
+#noVNC_status {
+  color: #fff;
+  font: bold 12px Helvetica;
+  margin: auto;
+}
+
+.noVNC_status_normal {
+  background: linear-gradient(#b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
+}
+
+.noVNC_status_error {
+  background: linear-gradient(#c83737 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
+}
+
+.noVNC_status_warn {
+  background: linear-gradient(#b4b41e 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
+}
+
+.noNVC_shown {
+  display: inline;
+}
+.noVNC_hidden {
+  display: none;
+}
+
+#noVNC_left_dummy_elem {
+  flex: 1;
+}
+
+#noVNC_buttons {
+  padding: 1px;
+  flex: 1;
+  display: flex;
+  justify-content: flex-end;
+}

+ 1669 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/ui.js

@@ -0,0 +1,1669 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2016 Samuel Mannehed for Cendio AB
+ * Copyright (C) 2016 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+import * as Log from '../core/util/logging.js';
+import _, { l10n } from './localization.js';
+import { isTouchDevice } from '../core/util/browser.js';
+import { setCapture, getPointerEvent } from '../core/util/events.js';
+import KeyTable from "../core/input/keysym.js";
+import keysyms from "../core/input/keysymdef.js";
+import Keyboard from "../core/input/keyboard.js";
+import RFB from "../core/rfb.js";
+import Display from "../core/display.js";
+import * as WebUtil from "./webutil.js";
+
+var UI = {
+
+    connected: false,
+    desktopName: "",
+
+    statusTimeout: null,
+    hideKeyboardTimeout: null,
+    idleControlbarTimeout: null,
+    closeControlbarTimeout: null,
+
+    controlbarGrabbed: false,
+    controlbarDrag: false,
+    controlbarMouseDownClientY: 0,
+    controlbarMouseDownOffsetY: 0,
+
+    isSafari: false,
+    lastKeyboardinput: null,
+    defaultKeyboardinputLen: 100,
+
+    inhibit_reconnect: true,
+    reconnect_callback: null,
+    reconnect_password: null,
+
+    prime: function(callback) {
+        if (document.readyState === "interactive" || document.readyState === "complete") {
+            UI.load(callback);
+        } else {
+            document.addEventListener('DOMContentLoaded', UI.load.bind(UI, callback));
+        }
+    },
+
+    // Setup rfb object, load settings from browser storage, then call
+    // UI.init to setup the UI/menus
+    load: function(callback) {
+        WebUtil.initSettings(UI.start, callback);
+    },
+
+    // Render default UI and initialize settings menu
+    start: function(callback) {
+
+        // Setup global variables first
+        UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 &&
+                       navigator.userAgent.indexOf('Chrome') === -1);
+
+        UI.initSettings();
+
+        // Translate the DOM
+        l10n.translateDOM();
+
+        // Adapt the interface for touch screen devices
+        if (isTouchDevice) {
+            document.documentElement.classList.add("noVNC_touch");
+            // Remove the address bar
+            setTimeout(function() { window.scrollTo(0, 1); }, 100);
+        }
+
+        // Restore control bar position
+        if (WebUtil.readSetting('controlbar_pos') === 'right') {
+            UI.toggleControlbarSide();
+        }
+
+        UI.initFullscreen();
+
+        // Setup event handlers
+        UI.addControlbarHandlers();
+        UI.addTouchSpecificHandlers();
+        UI.addExtraKeysHandlers();
+        UI.addMachineHandlers();
+        UI.addConnectionControlHandlers();
+        UI.addClipboardHandlers();
+        UI.addSettingsHandlers();
+        document.getElementById("noVNC_status")
+            .addEventListener('click', UI.hideStatus);
+
+        // Bootstrap fallback input handler
+        UI.keyboardinputReset();
+
+        UI.openControlbar();
+
+        UI.updateVisualState('init');
+
+        document.documentElement.classList.remove("noVNC_loading");
+
+        var autoconnect = WebUtil.getConfigVar('autoconnect', false);
+        if (autoconnect === 'true' || autoconnect == '1') {
+            autoconnect = true;
+            UI.connect();
+        } else {
+            autoconnect = false;
+            // Show the connect panel on first load unless autoconnecting
+            UI.openConnectPanel();
+        }
+
+        if (typeof callback === "function") {
+            callback(UI.rfb);
+        }
+    },
+
+    initFullscreen: function() {
+        // Only show the button if fullscreen is properly supported
+        // * Safari doesn't support alphanumerical input while in fullscreen
+        if (!UI.isSafari &&
+            (document.documentElement.requestFullscreen ||
+             document.documentElement.mozRequestFullScreen ||
+             document.documentElement.webkitRequestFullscreen ||
+             document.body.msRequestFullscreen)) {
+            document.getElementById('noVNC_fullscreen_button')
+                .classList.remove("noVNC_hidden");
+            UI.addFullscreenHandlers();
+        }
+    },
+
+    initSettings: function() {
+        var i;
+
+        // Logging selection dropdown
+        var llevels = ['error', 'warn', 'info', 'debug'];
+        for (i = 0; i < llevels.length; i += 1) {
+            UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]);
+        }
+
+        // Settings with immediate effects
+        UI.initSetting('logging', 'warn');
+        UI.updateLogging();
+
+        // if port == 80 (or 443) then it won't be present and should be
+        // set manually
+        var port = window.location.port;
+        if (!port) {
+            if (window.location.protocol.substring(0,5) == 'https') {
+                port = 443;
+            }
+            else if (window.location.protocol.substring(0,4) == 'http') {
+                port = 80;
+            }
+        }
+
+        /* Populate the controls if defaults are provided in the URL */
+        UI.initSetting('host', window.location.hostname);
+        UI.initSetting('port', port);
+        UI.initSetting('encrypt', (window.location.protocol === "https:"));
+        UI.initSetting('view_clip', false);
+        UI.initSetting('resize', 'off');
+        UI.initSetting('shared', true);
+        UI.initSetting('view_only', false);
+        UI.initSetting('path', 'websockify');
+        UI.initSetting('repeaterID', '');
+        UI.initSetting('reconnect', false);
+        UI.initSetting('reconnect_delay', 5000);
+
+        UI.setupSettingLabels();
+    },
+    // Adds a link to the label elements on the corresponding input elements
+    setupSettingLabels: function() {
+        var labels = document.getElementsByTagName('LABEL');
+        for (var i = 0; i < labels.length; i++) {
+            var htmlFor = labels[i].htmlFor;
+            if (htmlFor != '') {
+                var elem = document.getElementById(htmlFor);
+                if (elem) elem.label = labels[i];
+            } else {
+                // If 'for' isn't set, use the first input element child
+                var children = labels[i].children;
+                for (var j = 0; j < children.length; j++) {
+                    if (children[j].form !== undefined) {
+                        children[j].label = labels[i];
+                        break;
+                    }
+                }
+            }
+        }
+    },
+
+/* ------^-------
+*     /INIT
+* ==============
+* EVENT HANDLERS
+* ------v------*/
+
+    addControlbarHandlers: function() {
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('mousemove', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('mouseup', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('mousedown', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('keydown', UI.activateControlbar);
+
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('mousedown', UI.keepControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('keydown', UI.keepControlbar);
+
+        document.getElementById("noVNC_view_drag_button")
+            .addEventListener('click', UI.toggleViewDrag);
+
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('mousedown', UI.controlbarHandleMouseDown);
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('mouseup', UI.controlbarHandleMouseUp);
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('mousemove', UI.dragControlbarHandle);
+        // resize events aren't available for elements
+        window.addEventListener('resize', UI.updateControlbarHandle);
+
+        var exps = document.getElementsByClassName("noVNC_expander");
+        for (var i = 0;i < exps.length;i++) {
+            exps[i].addEventListener('click', UI.toggleExpander);
+        }
+    },
+
+    addTouchSpecificHandlers: function() {
+        document.getElementById("noVNC_mouse_button0")
+            .addEventListener('click', function () { UI.setMouseButton(1); });
+        document.getElementById("noVNC_mouse_button1")
+            .addEventListener('click', function () { UI.setMouseButton(2); });
+        document.getElementById("noVNC_mouse_button2")
+            .addEventListener('click', function () { UI.setMouseButton(4); });
+        document.getElementById("noVNC_mouse_button4")
+            .addEventListener('click', function () { UI.setMouseButton(0); });
+        document.getElementById("noVNC_keyboard_button")
+            .addEventListener('click', UI.toggleVirtualKeyboard);
+
+        UI.touchKeyboard = new Keyboard(document.getElementById('noVNC_keyboardinput'));
+        UI.touchKeyboard.onkeyevent = UI.keyEvent;
+        UI.touchKeyboard.grab();
+        document.getElementById("noVNC_keyboardinput")
+            .addEventListener('input', UI.keyInput);
+        document.getElementById("noVNC_keyboardinput")
+            .addEventListener('focus', UI.onfocusVirtualKeyboard);
+        document.getElementById("noVNC_keyboardinput")
+            .addEventListener('blur', UI.onblurVirtualKeyboard);
+        document.getElementById("noVNC_keyboardinput")
+            .addEventListener('submit', function () { return false; });
+
+        document.documentElement
+            .addEventListener('mousedown', UI.keepVirtualKeyboard, true);
+
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('touchstart', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('touchmove', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('touchend', UI.activateControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('input', UI.activateControlbar);
+
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('touchstart', UI.keepControlbar);
+        document.getElementById("noVNC_control_bar")
+            .addEventListener('input', UI.keepControlbar);
+
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('touchstart', UI.controlbarHandleMouseDown);
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('touchend', UI.controlbarHandleMouseUp);
+        document.getElementById("noVNC_control_bar_handle")
+            .addEventListener('touchmove', UI.dragControlbarHandle);
+    },
+
+    addExtraKeysHandlers: function() {
+        document.getElementById("noVNC_toggle_extra_keys_button")
+            .addEventListener('click', UI.toggleExtraKeys);
+        document.getElementById("noVNC_toggle_ctrl_button")
+            .addEventListener('click', UI.toggleCtrl);
+        document.getElementById("noVNC_toggle_alt_button")
+            .addEventListener('click', UI.toggleAlt);
+        document.getElementById("noVNC_send_tab_button")
+            .addEventListener('click', UI.sendTab);
+        document.getElementById("noVNC_send_esc_button")
+            .addEventListener('click', UI.sendEsc);
+        document.getElementById("noVNC_send_ctrl_alt_del_button")
+            .addEventListener('click', UI.sendCtrlAltDel);
+    },
+
+    addMachineHandlers: function() {
+        document.getElementById("noVNC_shutdown_button")
+            .addEventListener('click', function() { UI.rfb.machineShutdown(); });
+        document.getElementById("noVNC_reboot_button")
+            .addEventListener('click', function() { UI.rfb.machineReboot(); });
+        document.getElementById("noVNC_reset_button")
+            .addEventListener('click', function() { UI.rfb.machineReset(); });
+        document.getElementById("noVNC_power_button")
+            .addEventListener('click', UI.togglePowerPanel);
+    },
+
+    addConnectionControlHandlers: function() {
+        document.getElementById("noVNC_disconnect_button")
+            .addEventListener('click', UI.disconnect);
+        document.getElementById("noVNC_connect_button")
+            .addEventListener('click', UI.connect);
+        document.getElementById("noVNC_cancel_reconnect_button")
+            .addEventListener('click', UI.cancelReconnect);
+
+        document.getElementById("noVNC_password_button")
+            .addEventListener('click', UI.setPassword);
+    },
+
+    addClipboardHandlers: function() {
+        document.getElementById("noVNC_clipboard_button")
+            .addEventListener('click', UI.toggleClipboardPanel);
+        document.getElementById("noVNC_clipboard_text")
+            .addEventListener('change', UI.clipboardSend);
+        document.getElementById("noVNC_clipboard_clear_button")
+            .addEventListener('click', UI.clipboardClear);
+    },
+
+    // Add a call to save settings when the element changes,
+    // unless the optional parameter changeFunc is used instead.
+    addSettingChangeHandler: function(name, changeFunc) {
+        var settingElem = document.getElementById("noVNC_setting_" + name);
+        if (changeFunc === undefined) {
+            changeFunc = function () { UI.saveSetting(name); };
+        }
+        settingElem.addEventListener('change', changeFunc);
+    },
+
+    addSettingsHandlers: function() {
+        document.getElementById("noVNC_settings_button")
+            .addEventListener('click', UI.toggleSettingsPanel);
+
+        UI.addSettingChangeHandler('encrypt');
+        UI.addSettingChangeHandler('resize');
+        UI.addSettingChangeHandler('resize', UI.enableDisableViewClip);
+        UI.addSettingChangeHandler('resize', UI.applyResizeMode);
+        UI.addSettingChangeHandler('view_clip');
+        UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
+        UI.addSettingChangeHandler('shared');
+        UI.addSettingChangeHandler('view_only');
+        UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
+        UI.addSettingChangeHandler('host');
+        UI.addSettingChangeHandler('port');
+        UI.addSettingChangeHandler('path');
+        UI.addSettingChangeHandler('repeaterID');
+        UI.addSettingChangeHandler('logging');
+        UI.addSettingChangeHandler('logging', UI.updateLogging);
+        UI.addSettingChangeHandler('reconnect');
+        UI.addSettingChangeHandler('reconnect_delay');
+    },
+
+    addFullscreenHandlers: function() {
+        document.getElementById("noVNC_fullscreen_button")
+            .addEventListener('click', UI.toggleFullscreen);
+
+        window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
+        window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
+        window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton);
+        window.addEventListener('msfullscreenchange', UI.updateFullscreenButton);
+    },
+
+/* ------^-------
+ * /EVENT HANDLERS
+ * ==============
+ *     VISUAL
+ * ------v------*/
+
+    // Disable/enable controls depending on connection state
+    updateVisualState: function(state) {
+
+        document.documentElement.classList.remove("noVNC_connecting");
+        document.documentElement.classList.remove("noVNC_connected");
+        document.documentElement.classList.remove("noVNC_disconnecting");
+        document.documentElement.classList.remove("noVNC_reconnecting");
+
+        let transition_elem = document.getElementById("noVNC_transition_text");
+        switch (state) {
+            case 'init':
+                break;
+            case 'connecting':
+                transition_elem.textContent = _("Connecting...");
+                document.documentElement.classList.add("noVNC_connecting");
+                break;
+            case 'connected':
+                document.documentElement.classList.add("noVNC_connected");
+                break;
+            case 'disconnecting':
+                transition_elem.textContent = _("Disconnecting...");
+                document.documentElement.classList.add("noVNC_disconnecting");
+                break;
+            case 'disconnected':
+                break;
+            case 'reconnecting':
+                transition_elem.textContent = _("Reconnecting...");
+                document.documentElement.classList.add("noVNC_reconnecting");
+                break;
+            default:
+                Log.Error("Invalid visual state: " + state);
+                UI.showStatus(_("Internal error"), 'error');
+                return;
+        }
+
+        UI.enableDisableViewClip();
+
+        if (UI.connected) {
+            UI.disableSetting('encrypt');
+            UI.disableSetting('shared');
+            UI.disableSetting('host');
+            UI.disableSetting('port');
+            UI.disableSetting('path');
+            UI.disableSetting('repeaterID');
+            UI.setMouseButton(1);
+
+            // Hide the controlbar after 2 seconds
+            UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
+        } else {
+            UI.enableSetting('encrypt');
+            UI.enableSetting('shared');
+            UI.enableSetting('host');
+            UI.enableSetting('port');
+            UI.enableSetting('path');
+            UI.enableSetting('repeaterID');
+            UI.updatePowerButton();
+            UI.keepControlbar();
+        }
+
+        // State change disables viewport dragging.
+        // It is enabled (toggled) by direct click on the button
+        UI.setViewDrag(false);
+
+        // State change also closes the password dialog
+        document.getElementById('noVNC_password_dlg')
+            .classList.remove('noVNC_open');
+    },
+
+    showStatus: function(text, status_type, time) {
+        var statusElem = document.getElementById('noVNC_status');
+
+        clearTimeout(UI.statusTimeout);
+
+        if (typeof status_type === 'undefined') {
+            status_type = 'normal';
+        }
+
+        // Don't overwrite more severe visible statuses and never
+        // errors. Only shows the first error.
+        let visible_status_type = 'none';
+        if (statusElem.classList.contains("noVNC_open")) {
+            if (statusElem.classList.contains("noVNC_status_error")) {
+                visible_status_type = 'error';
+            } else if (statusElem.classList.contains("noVNC_status_warn")) {
+                visible_status_type = 'warn';
+            } else {
+                visible_status_type = 'normal';
+            }
+        }
+        if (visible_status_type === 'error' ||
+            (visible_status_type === 'warn' && status_type === 'normal')) {
+            return;
+        }
+
+        switch (status_type) {
+            case 'error':
+                statusElem.classList.remove("noVNC_status_warn");
+                statusElem.classList.remove("noVNC_status_normal");
+                statusElem.classList.add("noVNC_status_error");
+                break;
+            case 'warning':
+            case 'warn':
+                statusElem.classList.remove("noVNC_status_error");
+                statusElem.classList.remove("noVNC_status_normal");
+                statusElem.classList.add("noVNC_status_warn");
+                break;
+            case 'normal':
+            case 'info':
+            default:
+                statusElem.classList.remove("noVNC_status_error");
+                statusElem.classList.remove("noVNC_status_warn");
+                statusElem.classList.add("noVNC_status_normal");
+                break;
+        }
+
+        statusElem.textContent = text;
+        statusElem.classList.add("noVNC_open");
+
+        // If no time was specified, show the status for 1.5 seconds
+        if (typeof time === 'undefined') {
+            time = 1500;
+        }
+
+        // Error messages do not timeout
+        if (status_type !== 'error') {
+            UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
+        }
+    },
+
+    hideStatus: function() {
+        clearTimeout(UI.statusTimeout);
+        document.getElementById('noVNC_status').classList.remove("noVNC_open");
+    },
+
+    activateControlbar: function(event) {
+        clearTimeout(UI.idleControlbarTimeout);
+        // We manipulate the anchor instead of the actual control
+        // bar in order to avoid creating new a stacking group
+        document.getElementById('noVNC_control_bar_anchor')
+            .classList.remove("noVNC_idle");
+        UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000);
+    },
+
+    idleControlbar: function() {
+        document.getElementById('noVNC_control_bar_anchor')
+            .classList.add("noVNC_idle");
+    },
+
+    keepControlbar: function() {
+        clearTimeout(UI.closeControlbarTimeout);
+    },
+
+    openControlbar: function() {
+        document.getElementById('noVNC_control_bar')
+            .classList.add("noVNC_open");
+    },
+
+    closeControlbar: function() {
+        UI.closeAllPanels();
+        document.getElementById('noVNC_control_bar')
+            .classList.remove("noVNC_open");
+    },
+
+    toggleControlbar: function() {
+        if (document.getElementById('noVNC_control_bar')
+            .classList.contains("noVNC_open")) {
+            UI.closeControlbar();
+        } else {
+            UI.openControlbar();
+        }
+    },
+
+    toggleControlbarSide: function () {
+        // Temporarily disable animation to avoid weird movement
+        var bar = document.getElementById('noVNC_control_bar');
+        bar.style.transitionDuration = '0s';
+        bar.addEventListener('transitionend', function () { this.style.transitionDuration = ""; });
+
+        var anchor = document.getElementById('noVNC_control_bar_anchor');
+        if (anchor.classList.contains("noVNC_right")) {
+            WebUtil.writeSetting('controlbar_pos', 'left');
+            anchor.classList.remove("noVNC_right");
+        } else {
+            WebUtil.writeSetting('controlbar_pos', 'right');
+            anchor.classList.add("noVNC_right");
+        }
+
+        // Consider this a movement of the handle
+        UI.controlbarDrag = true;
+    },
+
+    showControlbarHint: function (show) {
+        var hint = document.getElementById('noVNC_control_bar_hint');
+        if (show) {
+            hint.classList.add("noVNC_active");
+        } else {
+            hint.classList.remove("noVNC_active");
+        }
+    },
+
+    dragControlbarHandle: function (e) {
+        if (!UI.controlbarGrabbed) return;
+
+        var ptr = getPointerEvent(e);
+
+        var anchor = document.getElementById('noVNC_control_bar_anchor');
+        if (ptr.clientX < (window.innerWidth * 0.1)) {
+            if (anchor.classList.contains("noVNC_right")) {
+                UI.toggleControlbarSide();
+            }
+        } else if (ptr.clientX > (window.innerWidth * 0.9)) {
+            if (!anchor.classList.contains("noVNC_right")) {
+                UI.toggleControlbarSide();
+            }
+        }
+
+        if (!UI.controlbarDrag) {
+            // The goal is to trigger on a certain physical width, the
+            // devicePixelRatio brings us a bit closer but is not optimal.
+            var dragThreshold = 10 * (window.devicePixelRatio || 1);
+            var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
+
+            if (dragDistance < dragThreshold) return;
+
+            UI.controlbarDrag = true;
+        }
+
+        var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
+
+        UI.moveControlbarHandle(eventY);
+
+        e.preventDefault();
+        e.stopPropagation();
+        UI.keepControlbar();
+        UI.activateControlbar();
+    },
+
+    // Move the handle but don't allow any position outside the bounds
+    moveControlbarHandle: function (viewportRelativeY) {
+        var handle = document.getElementById("noVNC_control_bar_handle");
+        var handleHeight = handle.getBoundingClientRect().height;
+        var controlbarBounds = document.getElementById("noVNC_control_bar")
+            .getBoundingClientRect();
+        var margin = 10;
+
+        // These heights need to be non-zero for the below logic to work
+        if (handleHeight === 0 || controlbarBounds.height === 0) {
+            return;
+        }
+
+        var newY = viewportRelativeY;
+
+        // Check if the coordinates are outside the control bar
+        if (newY < controlbarBounds.top + margin) {
+            // Force coordinates to be below the top of the control bar
+            newY = controlbarBounds.top + margin;
+
+        } else if (newY > controlbarBounds.top +
+                   controlbarBounds.height - handleHeight - margin) {
+            // Force coordinates to be above the bottom of the control bar
+            newY = controlbarBounds.top +
+                controlbarBounds.height - handleHeight - margin;
+        }
+
+        // Corner case: control bar too small for stable position
+        if (controlbarBounds.height < (handleHeight + margin * 2)) {
+            newY = controlbarBounds.top +
+                (controlbarBounds.height - handleHeight) / 2;
+        }
+
+        // The transform needs coordinates that are relative to the parent
+        var parentRelativeY = newY - controlbarBounds.top;
+        handle.style.transform = "translateY(" + parentRelativeY + "px)";
+    },
+
+    updateControlbarHandle: function () {
+        // Since the control bar is fixed on the viewport and not the page,
+        // the move function expects coordinates relative the the viewport.
+        var handle = document.getElementById("noVNC_control_bar_handle");
+        var handleBounds = handle.getBoundingClientRect();
+        UI.moveControlbarHandle(handleBounds.top);
+    },
+
+    controlbarHandleMouseUp: function(e) {
+        if ((e.type == "mouseup") && (e.button != 0)) return;
+
+        // mouseup and mousedown on the same place toggles the controlbar
+        if (UI.controlbarGrabbed && !UI.controlbarDrag) {
+            UI.toggleControlbar();
+            e.preventDefault();
+            e.stopPropagation();
+            UI.keepControlbar();
+            UI.activateControlbar();
+        }
+        UI.controlbarGrabbed = false;
+        UI.showControlbarHint(false);
+    },
+
+    controlbarHandleMouseDown: function(e) {
+        if ((e.type == "mousedown") && (e.button != 0)) return;
+
+        var ptr = getPointerEvent(e);
+
+        var handle = document.getElementById("noVNC_control_bar_handle");
+        var bounds = handle.getBoundingClientRect();
+
+        // Touch events have implicit capture
+        if (e.type === "mousedown") {
+            setCapture(handle);
+        }
+
+        UI.controlbarGrabbed = true;
+        UI.controlbarDrag = false;
+
+        UI.showControlbarHint(true);
+
+        UI.controlbarMouseDownClientY = ptr.clientY;
+        UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
+        e.preventDefault();
+        e.stopPropagation();
+        UI.keepControlbar();
+        UI.activateControlbar();
+    },
+
+    toggleExpander: function(e) {
+        if (this.classList.contains("noVNC_open")) {
+            this.classList.remove("noVNC_open");
+        } else {
+            this.classList.add("noVNC_open");
+        }
+    },
+
+/* ------^-------
+ *    /VISUAL
+ * ==============
+ *    SETTINGS
+ * ------v------*/
+
+    // Initial page load read/initialization of settings
+    initSetting: function(name, defVal) {
+        // Check Query string followed by cookie
+        var val = WebUtil.getConfigVar(name);
+        if (val === null) {
+            val = WebUtil.readSetting(name, defVal);
+        }
+        UI.updateSetting(name, val);
+        return val;
+    },
+
+    // Update cookie and form control setting. If value is not set, then
+    // updates from control to current cookie setting.
+    updateSetting: function(name, value) {
+
+        // Save the cookie for this session
+        if (typeof value !== 'undefined') {
+            WebUtil.writeSetting(name, value);
+        }
+
+        // Update the settings control
+        value = UI.getSetting(name);
+
+        var ctrl = document.getElementById('noVNC_setting_' + name);
+        if (ctrl.type === 'checkbox') {
+            ctrl.checked = value;
+
+        } else if (typeof ctrl.options !== 'undefined') {
+            for (var i = 0; i < ctrl.options.length; i += 1) {
+                if (ctrl.options[i].value === value) {
+                    ctrl.selectedIndex = i;
+                    break;
+                }
+            }
+        } 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 = document.getElementById('noVNC_setting_' + name);
+        if (ctrl.type === 'checkbox') {
+            val = ctrl.checked;
+        } else if (typeof ctrl.options !== 'undefined') {
+            val = ctrl.options[ctrl.selectedIndex].value;
+        } else {
+            val = ctrl.value;
+        }
+        WebUtil.writeSetting(name, val);
+        //Log.Debug("Setting saved '" + name + "=" + val + "'");
+        return val;
+    },
+
+    // Read form control compatible setting from cookie
+    getSetting: function(name) {
+        var ctrl = document.getElementById('noVNC_setting_' + name);
+        var val = WebUtil.readSetting(name);
+        if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
+            if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
+                val = false;
+            } else {
+                val = true;
+            }
+        }
+        return val;
+    },
+
+    // These helpers compensate for the lack of parent-selectors and
+    // previous-sibling-selectors in CSS which are needed when we want to
+    // disable the labels that belong to disabled input elements.
+    disableSetting: function(name) {
+        var ctrl = document.getElementById('noVNC_setting_' + name);
+        ctrl.disabled = true;
+        ctrl.label.classList.add('noVNC_disabled');
+    },
+
+    enableSetting: function(name) {
+        var ctrl = document.getElementById('noVNC_setting_' + name);
+        ctrl.disabled = false;
+        ctrl.label.classList.remove('noVNC_disabled');
+    },
+
+/* ------^-------
+ *   /SETTINGS
+ * ==============
+ *    PANELS
+ * ------v------*/
+
+    closeAllPanels: function() {
+        UI.closeSettingsPanel();
+        UI.closePowerPanel();
+        UI.closeClipboardPanel();
+        UI.closeExtraKeys();
+    },
+
+/* ------^-------
+ *   /PANELS
+ * ==============
+ * SETTINGS (panel)
+ * ------v------*/
+
+    openSettingsPanel: function() {
+        UI.closeAllPanels();
+        UI.openControlbar();
+
+        // Refresh UI elements from saved cookies
+        UI.updateSetting('encrypt');
+        UI.updateSetting('view_clip');
+        UI.updateSetting('resize');
+        UI.updateSetting('shared');
+        UI.updateSetting('view_only');
+        UI.updateSetting('path');
+        UI.updateSetting('repeaterID');
+        UI.updateSetting('logging');
+        UI.updateSetting('reconnect');
+        UI.updateSetting('reconnect_delay');
+
+        document.getElementById('noVNC_settings')
+            .classList.add("noVNC_open");
+        document.getElementById('noVNC_settings_button')
+            .classList.add("noVNC_selected");
+    },
+
+    closeSettingsPanel: function() {
+        document.getElementById('noVNC_settings')
+            .classList.remove("noVNC_open");
+        document.getElementById('noVNC_settings_button')
+            .classList.remove("noVNC_selected");
+    },
+
+    toggleSettingsPanel: function() {
+        if (document.getElementById('noVNC_settings')
+            .classList.contains("noVNC_open")) {
+            UI.closeSettingsPanel();
+        } else {
+            UI.openSettingsPanel();
+        }
+    },
+
+/* ------^-------
+ *   /SETTINGS
+ * ==============
+ *     POWER
+ * ------v------*/
+
+    openPowerPanel: function() {
+        UI.closeAllPanels();
+        UI.openControlbar();
+
+        document.getElementById('noVNC_power')
+            .classList.add("noVNC_open");
+        document.getElementById('noVNC_power_button')
+            .classList.add("noVNC_selected");
+    },
+
+    closePowerPanel: function() {
+        document.getElementById('noVNC_power')
+            .classList.remove("noVNC_open");
+        document.getElementById('noVNC_power_button')
+            .classList.remove("noVNC_selected");
+    },
+
+    togglePowerPanel: function() {
+        if (document.getElementById('noVNC_power')
+            .classList.contains("noVNC_open")) {
+            UI.closePowerPanel();
+        } else {
+            UI.openPowerPanel();
+        }
+    },
+
+    // Disable/enable power button
+    updatePowerButton: function() {
+        if (UI.connected &&
+            UI.rfb.capabilities.power &&
+            !UI.rfb.viewOnly) {
+            document.getElementById('noVNC_power_button')
+                .classList.remove("noVNC_hidden");
+        } else {
+            document.getElementById('noVNC_power_button')
+                .classList.add("noVNC_hidden");
+            // Close power panel if open
+            UI.closePowerPanel();
+        }
+    },
+
+/* ------^-------
+ *    /POWER
+ * ==============
+ *   CLIPBOARD
+ * ------v------*/
+
+    openClipboardPanel: function() {
+        UI.closeAllPanels();
+        UI.openControlbar();
+
+        document.getElementById('noVNC_clipboard')
+            .classList.add("noVNC_open");
+        document.getElementById('noVNC_clipboard_button')
+            .classList.add("noVNC_selected");
+    },
+
+    closeClipboardPanel: function() {
+        document.getElementById('noVNC_clipboard')
+            .classList.remove("noVNC_open");
+        document.getElementById('noVNC_clipboard_button')
+            .classList.remove("noVNC_selected");
+    },
+
+    toggleClipboardPanel: function() {
+        if (document.getElementById('noVNC_clipboard')
+            .classList.contains("noVNC_open")) {
+            UI.closeClipboardPanel();
+        } else {
+            UI.openClipboardPanel();
+        }
+    },
+
+    clipboardReceive: function(e) {
+        Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0,40) + "...");
+        document.getElementById('noVNC_clipboard_text').value = e.detail.text;
+        Log.Debug("<< UI.clipboardReceive");
+    },
+
+    clipboardClear: function() {
+        document.getElementById('noVNC_clipboard_text').value = "";
+        UI.rfb.clipboardPasteFrom("");
+    },
+
+    clipboardSend: function() {
+        var text = document.getElementById('noVNC_clipboard_text').value;
+        Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
+        UI.rfb.clipboardPasteFrom(text);
+        Log.Debug("<< UI.clipboardSend");
+    },
+
+/* ------^-------
+ *  /CLIPBOARD
+ * ==============
+ *  CONNECTION
+ * ------v------*/
+
+    openConnectPanel: function() {
+        document.getElementById('noVNC_connect_dlg')
+            .classList.add("noVNC_open");
+    },
+
+    closeConnectPanel: function() {
+        document.getElementById('noVNC_connect_dlg')
+            .classList.remove("noVNC_open");
+    },
+
+    connect: function(event, password) {
+
+        // Ignore when rfb already exists
+        if (typeof UI.rfb !== 'undefined') {
+            return;
+        }
+
+        var host = UI.getSetting('host');
+        var port = UI.getSetting('port');
+        var path = UI.getSetting('path');
+
+        if (typeof password === 'undefined') {
+            password = WebUtil.getConfigVar('password');
+            UI.reconnect_password = password;
+        }
+
+        if (password === null) {
+            password = undefined;
+        }
+
+        UI.hideStatus();
+
+        if (!host) {
+            Log.Error("Can't connect when host is: " + host);
+            UI.showStatus(_("Must set host"), 'error');
+            return;
+        }
+
+        UI.closeAllPanels();
+        UI.closeConnectPanel();
+
+        UI.updateVisualState('connecting');
+
+        var url;
+
+        url = UI.getSetting('encrypt') ? 'wss' : 'ws';
+
+        url += '://' + host;
+        if(port) {
+            url += ':' + port;
+        }
+        url += '/' + path;
+
+        UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
+                         { shared: UI.getSetting('shared'),
+                           repeaterID: UI.getSetting('repeaterID'),
+                           credentials: { password: password } });
+        UI.rfb.addEventListener("connect", UI.connectFinished);
+        UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
+        UI.rfb.addEventListener("credentialsrequired", UI.credentials);
+        UI.rfb.addEventListener("securityfailure", UI.securityFailed);
+        UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); });
+        UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
+        UI.rfb.addEventListener("bell", UI.bell);
+        UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
+        UI.rfb.clipViewport = UI.getSetting('view_clip');
+        UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+        UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+
+        UI.updateViewOnly(); // requires UI.rfb
+    },
+
+    disconnect: function() {
+        UI.closeAllPanels();
+        UI.rfb.disconnect();
+
+        UI.connected = false;
+
+        // Disable automatic reconnecting
+        UI.inhibit_reconnect = true;
+
+        UI.updateVisualState('disconnecting');
+
+        // Don't display the connection settings until we're actually disconnected
+    },
+
+    reconnect: function() {
+        UI.reconnect_callback = null;
+
+        // if reconnect has been disabled in the meantime, do nothing.
+        if (UI.inhibit_reconnect) {
+            return;
+        }
+
+        UI.connect(null, UI.reconnect_password);
+    },
+
+    cancelReconnect: function() {
+        if (UI.reconnect_callback !== null) {
+            clearTimeout(UI.reconnect_callback);
+            UI.reconnect_callback = null;
+        }
+
+        UI.updateVisualState('disconnected');
+
+        UI.openControlbar();
+        UI.openConnectPanel();
+    },
+
+    connectFinished: function (e) {
+        UI.connected = true;
+        UI.inhibit_reconnect = false;
+
+        let msg;
+        if (UI.getSetting('encrypt')) {
+            msg = _("Connected (encrypted) to ") + UI.desktopName;
+        } else {
+            msg = _("Connected (unencrypted) to ") + UI.desktopName;
+        }
+        UI.showStatus(msg);
+        UI.updateVisualState('connected');
+
+        // Do this last because it can only be used on rendered elements
+        UI.rfb.focus();
+    },
+
+    disconnectFinished: function (e) {
+        let wasConnected = UI.connected;
+
+        // This variable is ideally set when disconnection starts, but
+        // when the disconnection isn't clean or if it is initiated by
+        // the server, we need to do it here as well since
+        // UI.disconnect() won't be used in those cases.
+        UI.connected = false;
+
+        UI.rfb = undefined;
+
+        if (!e.detail.clean) {
+            UI.updateVisualState('disconnected');
+            if (wasConnected) {
+                UI.showStatus(_("Something went wrong, connection is closed"),
+                              'error');
+            } else {
+                UI.showStatus(_("Failed to connect to server"), 'error');
+            }
+        } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
+            UI.updateVisualState('reconnecting');
+
+            var delay = parseInt(UI.getSetting('reconnect_delay'));
+            UI.reconnect_callback = setTimeout(UI.reconnect, delay);
+            return;
+        } else {
+            UI.updateVisualState('disconnected');
+            UI.showStatus(_("Disconnected"), 'normal');
+        }
+
+        UI.openControlbar();
+        UI.openConnectPanel();
+    },
+
+    securityFailed: function (e) {
+        let msg = "";
+        // On security failures we might get a string with a reason
+        // directly from the server. Note that we can't control if
+        // this string is translated or not.
+        if ('reason' in e.detail) {
+            msg = _("New connection has been rejected with reason: ") +
+                e.detail.reason;
+        } else {
+            msg = _("New connection has been rejected");
+        }
+        UI.showStatus(msg, 'error');
+    },
+
+/* ------^-------
+ *  /CONNECTION
+ * ==============
+ *   PASSWORD
+ * ------v------*/
+
+    credentials: function(e) {
+        // FIXME: handle more types
+        document.getElementById('noVNC_password_dlg')
+            .classList.add('noVNC_open');
+
+        setTimeout(function () {
+                document.getElementById('noVNC_password_input').focus();
+            }, 100);
+
+        Log.Warn("Server asked for a password");
+        UI.showStatus(_("Password is required"), "warning");
+    },
+
+    setPassword: function(e) {
+        // Prevent actually submitting the form
+        e.preventDefault();
+
+        var inputElem = document.getElementById('noVNC_password_input');
+        var password = inputElem.value;
+        // Clear the input after reading the password
+        inputElem.value = "";
+        UI.rfb.sendCredentials({ password: password });
+        UI.reconnect_password = password;
+        document.getElementById('noVNC_password_dlg')
+            .classList.remove('noVNC_open');
+    },
+
+/* ------^-------
+ *  /PASSWORD
+ * ==============
+ *   FULLSCREEN
+ * ------v------*/
+
+    toggleFullscreen: function() {
+        if (document.fullscreenElement || // alternative standard method
+            document.mozFullScreenElement || // currently working methods
+            document.webkitFullscreenElement ||
+            document.msFullscreenElement) {
+            if (document.exitFullscreen) {
+                document.exitFullscreen();
+            } else if (document.mozCancelFullScreen) {
+                document.mozCancelFullScreen();
+            } else if (document.webkitExitFullscreen) {
+                document.webkitExitFullscreen();
+            } else if (document.msExitFullscreen) {
+                document.msExitFullscreen();
+            }
+        } else {
+            if (document.documentElement.requestFullscreen) {
+                document.documentElement.requestFullscreen();
+            } else if (document.documentElement.mozRequestFullScreen) {
+                document.documentElement.mozRequestFullScreen();
+            } else if (document.documentElement.webkitRequestFullscreen) {
+                document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+            } else if (document.body.msRequestFullscreen) {
+                document.body.msRequestFullscreen();
+            }
+        }
+        UI.enableDisableViewClip();
+        UI.updateFullscreenButton();
+    },
+
+    updateFullscreenButton: function() {
+        if (document.fullscreenElement || // alternative standard method
+            document.mozFullScreenElement || // currently working methods
+            document.webkitFullscreenElement ||
+            document.msFullscreenElement ) {
+            document.getElementById('noVNC_fullscreen_button')
+                .classList.add("noVNC_selected");
+        } else {
+            document.getElementById('noVNC_fullscreen_button')
+                .classList.remove("noVNC_selected");
+        }
+    },
+
+/* ------^-------
+ *  /FULLSCREEN
+ * ==============
+ *     RESIZE
+ * ------v------*/
+
+    // Apply remote resizing or local scaling
+    applyResizeMode: function() {
+        if (!UI.rfb) return;
+
+        UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+        UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+    },
+
+/* ------^-------
+ *    /RESIZE
+ * ==============
+ * VIEW CLIPPING
+ * ------v------*/
+
+    // Update parameters that depend on the viewport clip setting
+    updateViewClip: function() {
+        if (!UI.rfb) return;
+
+        var cur_clip = UI.rfb.clipViewport;
+        var new_clip = UI.getSetting('view_clip');
+
+        if (isTouchDevice) {
+            // Touch devices usually have shit scrollbars
+            new_clip = true;
+        }
+
+        if (cur_clip !== new_clip) {
+            UI.rfb.clipViewport = new_clip;
+        }
+
+        // Changing the viewport may change the state of
+        // the dragging button
+        UI.updateViewDrag();
+    },
+
+    // Handle special cases where viewport clipping is forced on/off or locked
+    enableDisableViewClip: function() {
+        var resizeSetting = UI.getSetting('resize');
+        // Disable clipping if we are scaling, connected or on touch
+        if (resizeSetting === 'scale' ||
+            isTouchDevice) {
+            UI.disableSetting('view_clip');
+        } else {
+            UI.enableSetting('view_clip');
+        }
+    },
+
+/* ------^-------
+ * /VIEW CLIPPING
+ * ==============
+ *    VIEWDRAG
+ * ------v------*/
+
+    toggleViewDrag: function() {
+        if (!UI.rfb) return;
+
+        var drag = UI.rfb.dragViewport;
+        UI.setViewDrag(!drag);
+     },
+
+    // Set the view drag mode which moves the viewport on mouse drags
+    setViewDrag: function(drag) {
+        if (!UI.rfb) return;
+
+        UI.rfb.dragViewport = drag;
+
+        UI.updateViewDrag();
+    },
+
+    updateViewDrag: function() {
+        if (!UI.connected) return;
+
+        var viewDragButton = document.getElementById('noVNC_view_drag_button');
+
+        if (!UI.rfb.clipViewport && UI.rfb.dragViewport) {
+            // We are no longer clipping the viewport. Make sure
+            // viewport drag isn't active when it can't be used.
+            UI.rfb.dragViewport = false;
+        }
+
+        if (UI.rfb.dragViewport) {
+            viewDragButton.classList.add("noVNC_selected");
+        } else {
+            viewDragButton.classList.remove("noVNC_selected");
+        }
+
+        // Different behaviour for touch vs non-touch
+        // The button is disabled instead of hidden on touch devices
+        if (isTouchDevice) {
+            viewDragButton.classList.remove("noVNC_hidden");
+
+            if (UI.rfb.clipViewport) {
+                viewDragButton.disabled = false;
+            } else {
+                viewDragButton.disabled = true;
+            }
+        } else {
+            viewDragButton.disabled = false;
+
+            if (UI.rfb.clipViewport) {
+                viewDragButton.classList.remove("noVNC_hidden");
+            } else {
+                viewDragButton.classList.add("noVNC_hidden");
+            }
+        }
+    },
+
+/* ------^-------
+ *   /VIEWDRAG
+ * ==============
+ *    KEYBOARD
+ * ------v------*/
+
+    showVirtualKeyboard: function() {
+        if (!isTouchDevice) return;
+
+        var input = document.getElementById('noVNC_keyboardinput');
+
+        if (document.activeElement == input) return;
+
+        input.focus();
+
+        try {
+            var l = input.value.length;
+            // Move the caret to the end
+            input.setSelectionRange(l, l);
+        } catch (err) {} // setSelectionRange is undefined in Google Chrome
+    },
+
+    hideVirtualKeyboard: function() {
+        if (!isTouchDevice) return;
+
+        var input = document.getElementById('noVNC_keyboardinput');
+
+        if (document.activeElement != input) return;
+
+        input.blur();
+    },
+
+    toggleVirtualKeyboard: function () {
+        if (document.getElementById('noVNC_keyboard_button')
+            .classList.contains("noVNC_selected")) {
+            UI.hideVirtualKeyboard();
+        } else {
+            UI.showVirtualKeyboard();
+        }
+    },
+
+    onfocusVirtualKeyboard: function(event) {
+        document.getElementById('noVNC_keyboard_button')
+            .classList.add("noVNC_selected");
+        if (UI.rfb) {
+            UI.rfb.focusOnClick = false;
+        }
+    },
+
+    onblurVirtualKeyboard: function(event) {
+        document.getElementById('noVNC_keyboard_button')
+            .classList.remove("noVNC_selected");
+        if (UI.rfb) {
+            UI.rfb.focusOnClick = true;
+        }
+    },
+
+    keepVirtualKeyboard: function(event) {
+        var input = document.getElementById('noVNC_keyboardinput');
+
+        // Only prevent focus change if the virtual keyboard is active
+        if (document.activeElement != input) {
+            return;
+        }
+
+        // Only allow focus to move to other elements that need
+        // focus to function properly
+        if (event.target.form !== undefined) {
+            switch (event.target.type) {
+                case 'text':
+                case 'email':
+                case 'search':
+                case 'password':
+                case 'tel':
+                case 'url':
+                case 'textarea':
+                case 'select-one':
+                case 'select-multiple':
+                    return;
+            }
+        }
+
+        event.preventDefault();
+    },
+
+    keyboardinputReset: function() {
+        var kbi = document.getElementById('noVNC_keyboardinput');
+        kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
+        UI.lastKeyboardinput = kbi.value;
+    },
+
+    keyEvent: function (keysym, code, down) {
+        if (!UI.rfb) return;
+
+        UI.rfb.sendKey(keysym, code, down);
+    },
+
+    // When normal keyboard events are left uncought, use the input events from
+    // the keyboardinput element instead and generate the corresponding key events.
+    // This code is required since some browsers on Android are inconsistent in
+    // sending keyCodes in the normal keyboard events when using on screen keyboards.
+    keyInput: function(event) {
+
+        if (!UI.rfb) return;
+
+        var newValue = event.target.value;
+
+        if (!UI.lastKeyboardinput) {
+            UI.keyboardinputReset();
+        }
+        var oldValue = UI.lastKeyboardinput;
+
+        var newLen;
+        try {
+            // Try to check caret position since whitespace at the end
+            // will not be considered by value.length in some browsers
+            newLen = Math.max(event.target.selectionStart, newValue.length);
+        } catch (err) {
+            // selectionStart is undefined in Google Chrome
+            newLen = newValue.length;
+        }
+        var oldLen = oldValue.length;
+
+        var backspaces;
+        var inputs = newLen - oldLen;
+        if (inputs < 0) {
+            backspaces = -inputs;
+        } else {
+            backspaces = 0;
+        }
+
+        // Compare the old string with the new to account for
+        // text-corrections or other input that modify existing text
+        var i;
+        for (i = 0; i < Math.min(oldLen, newLen); i++) {
+            if (newValue.charAt(i) != oldValue.charAt(i)) {
+                inputs = newLen - i;
+                backspaces = oldLen - i;
+                break;
+            }
+        }
+
+        // Send the key events
+        for (i = 0; i < backspaces; i++) {
+            UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace");
+        }
+        for (i = newLen - inputs; i < newLen; i++) {
+            UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i)));
+        }
+
+        // Control the text content length in the keyboardinput element
+        if (newLen > 2 * UI.defaultKeyboardinputLen) {
+            UI.keyboardinputReset();
+        } else if (newLen < 1) {
+            // There always have to be some text in the keyboardinput
+            // element with which backspace can interact.
+            UI.keyboardinputReset();
+            // This sometimes causes the keyboard to disappear for a second
+            // but it is required for the android keyboard to recognize that
+            // text has been added to the field
+            event.target.blur();
+            // This has to be ran outside of the input handler in order to work
+            setTimeout(event.target.focus.bind(event.target), 0);
+        } else {
+            UI.lastKeyboardinput = newValue;
+        }
+    },
+
+/* ------^-------
+ *   /KEYBOARD
+ * ==============
+ *   EXTRA KEYS
+ * ------v------*/
+
+    openExtraKeys: function() {
+        UI.closeAllPanels();
+        UI.openControlbar();
+
+        document.getElementById('noVNC_modifiers')
+            .classList.add("noVNC_open");
+        document.getElementById('noVNC_toggle_extra_keys_button')
+            .classList.add("noVNC_selected");
+    },
+
+    closeExtraKeys: function() {
+        document.getElementById('noVNC_modifiers')
+            .classList.remove("noVNC_open");
+        document.getElementById('noVNC_toggle_extra_keys_button')
+            .classList.remove("noVNC_selected");
+    },
+
+    toggleExtraKeys: function() {
+        if(document.getElementById('noVNC_modifiers')
+            .classList.contains("noVNC_open")) {
+            UI.closeExtraKeys();
+        } else  {
+            UI.openExtraKeys();
+        }
+    },
+
+    sendEsc: function() {
+        UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
+    },
+
+    sendTab: function() {
+        UI.rfb.sendKey(KeyTable.XK_Tab);
+    },
+
+    toggleCtrl: function() {
+        var btn = document.getElementById('noVNC_toggle_ctrl_button');
+        if (btn.classList.contains("noVNC_selected")) {
+            UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
+            btn.classList.remove("noVNC_selected");
+        } else {
+            UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
+            btn.classList.add("noVNC_selected");
+        }
+    },
+
+    toggleAlt: function() {
+        var btn = document.getElementById('noVNC_toggle_alt_button');
+        if (btn.classList.contains("noVNC_selected")) {
+            UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
+            btn.classList.remove("noVNC_selected");
+        } else {
+            UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
+            btn.classList.add("noVNC_selected");
+        }
+    },
+
+    sendCtrlAltDel: function() {
+        UI.rfb.sendCtrlAltDel();
+    },
+
+/* ------^-------
+ *   /EXTRA KEYS
+ * ==============
+ *     MISC
+ * ------v------*/
+
+    setMouseButton: function(num) {
+        var view_only = UI.rfb.viewOnly;
+        if (UI.rfb && !view_only) {
+            UI.rfb.touchButton = num;
+        }
+
+        var blist = [0, 1,2,4];
+        for (var b = 0; b < blist.length; b++) {
+            var button = document.getElementById('noVNC_mouse_button' +
+                                                 blist[b]);
+            if (blist[b] === num && !view_only) {
+                button.classList.remove("noVNC_hidden");
+            } else {
+                button.classList.add("noVNC_hidden");
+            }
+        }
+    },
+
+    updateViewOnly: function() {
+        if (!UI.rfb) return;
+        UI.rfb.viewOnly = UI.getSetting('view_only');
+
+        // Hide input related buttons in view only mode
+        if (UI.rfb.viewOnly) {
+            document.getElementById('noVNC_keyboard_button')
+                .classList.add('noVNC_hidden');
+            document.getElementById('noVNC_toggle_extra_keys_button')
+                .classList.add('noVNC_hidden');
+        } else {
+            document.getElementById('noVNC_keyboard_button')
+                .classList.remove('noVNC_hidden');
+            document.getElementById('noVNC_toggle_extra_keys_button')
+                .classList.remove('noVNC_hidden');
+        }
+        UI.setMouseButton(1); //has it's own logic for hiding/showing
+    },
+
+    updateLogging: function() {
+        WebUtil.init_logging(UI.getSetting('logging'));
+    },
+
+    updateDesktopName: function(e) {
+        UI.desktopName = e.detail.name;
+        // Display the desktop name in the document title
+        document.title = e.detail.name + " - noVNC";
+    },
+
+    bell: function(e) {
+        if (WebUtil.getConfigVar('bell', 'on') === 'on') {
+            var promise = document.getElementById('noVNC_bell').play();
+            // The standards disagree on the return value here
+            if (promise) {
+                promise.catch(function(e) {
+                    if (e.name === "NotAllowedError") {
+                        // Ignore when the browser doesn't let us play audio.
+                        // It is common that the browsers require audio to be
+                        // initiated from a user action.
+                    } else {
+                        Log.Error("Unable to play bell: " + e);
+                    }
+                });
+            }
+        }
+    },
+
+    //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);
+    },
+
+/* ------^-------
+ *    /MISC
+ * ==============
+ */
+};
+
+// Set up translations
+var LINGUAS = ["de", "el", "es", "nl", "pl", "sv", "tr", "zh"];
+l10n.setup(LINGUAS);
+if (l10n.language !== "en" && l10n.dictionary === undefined) {
+    WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
+        l10n.dictionary = translations;
+
+        // wait for translations to load before loading the UI
+        UI.prime();
+    }, function (err) {
+        Log.Error("Failed to load translations: " + err);
+        UI.prime();
+    });
+} else {
+    UI.prime();
+}
+
+export default UI;

+ 230 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/app/webutil.js

@@ -0,0 +1,230 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2013 NTT corp.
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+import { init_logging as main_init_logging } from '../core/util/logging.js';
+
+// init log level reading the logging HTTP param
+export function init_logging (level) {
+    "use strict";
+    if (typeof level !== "undefined") {
+        main_init_logging(level);
+    } else {
+        var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
+        main_init_logging(param || undefined);
+    }
+};
+
+// Read a query string variable
+export function getQueryVar (name, defVal) {
+    "use strict";
+    var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
+        match = document.location.href.match(re);
+    if (typeof defVal === 'undefined') { defVal = null; }
+    if (match) {
+        return decodeURIComponent(match[1]);
+    } else {
+        return defVal;
+    }
+};
+
+// Read a hash fragment variable
+export function getHashVar (name, defVal) {
+    "use strict";
+    var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
+        match = document.location.hash.match(re);
+    if (typeof defVal === 'undefined') { defVal = null; }
+    if (match) {
+        return decodeURIComponent(match[1]);
+    } else {
+        return defVal;
+    }
+};
+
+// Read a variable from the fragment or the query string
+// Fragment takes precedence
+export function getConfigVar (name, defVal) {
+    "use strict";
+    var val = getHashVar(name);
+    if (val === null) {
+        val = getQueryVar(name, defVal);
+    }
+    return val;
+};
+
+/*
+ * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
+ */
+
+// No days means only for this browser session
+export function createCookie (name, value, days) {
+    "use strict";
+    var date, expires;
+    if (days) {
+        date = new Date();
+        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+        expires = "; expires=" + date.toGMTString();
+    } else {
+        expires = "";
+    }
+
+    var secure;
+    if (document.location.protocol === "https:") {
+        secure = "; secure";
+    } else {
+        secure = "";
+    }
+    document.cookie = name + "=" + value + expires + "; path=/" + secure;
+};
+
+export function readCookie (name, defaultValue) {
+    "use strict";
+    var nameEQ = name + "=",
+        ca = document.cookie.split(';');
+
+    for (var i = 0; i < ca.length; i += 1) {
+        var c = ca[i];
+        while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
+        if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
+    }
+    return (typeof defaultValue !== 'undefined') ? defaultValue : null;
+};
+
+export function eraseCookie (name) {
+    "use strict";
+    createCookie(name, "", -1);
+};
+
+/*
+ * Setting handling.
+ */
+
+var settings = {};
+
+export function initSettings (callback /*, ...callbackArgs */) {
+    "use strict";
+    var callbackArgs = Array.prototype.slice.call(arguments, 1);
+    if (window.chrome && window.chrome.storage) {
+        window.chrome.storage.sync.get(function (cfg) {
+            settings = cfg;
+            if (callback) {
+                callback.apply(this, callbackArgs);
+            }
+        });
+    } else {
+        // No-op
+        if (callback) {
+            callback.apply(this, callbackArgs);
+        }
+    }
+};
+
+// No days means only for this browser session
+export function writeSetting (name, value) {
+    "use strict";
+    if (window.chrome && window.chrome.storage) {
+        if (settings[name] !== value) {
+            settings[name] = value;
+            window.chrome.storage.sync.set(settings);
+        }
+    } else {
+        localStorage.setItem(name, value);
+    }
+};
+
+export function readSetting (name, defaultValue) {
+    "use strict";
+    var value;
+    if (window.chrome && window.chrome.storage) {
+        value = settings[name];
+    } else {
+        value = localStorage.getItem(name);
+    }
+    if (typeof value === "undefined") {
+        value = null;
+    }
+    if (value === null && typeof defaultValue !== "undefined") {
+        return defaultValue;
+    } else {
+        return value;
+    }
+};
+
+export function eraseSetting (name) {
+    "use strict";
+    if (window.chrome && window.chrome.storage) {
+        window.chrome.storage.sync.remove(name);
+        delete settings[name];
+    } else {
+        localStorage.removeItem(name);
+    }
+};
+
+export function injectParamIfMissing (path, param, value) {
+    // force pretend that we're dealing with a relative path
+    // (assume that we wanted an extra if we pass one in)
+    path = "/" + path;
+
+    var elem = document.createElement('a');
+    elem.href = path;
+
+    var param_eq = encodeURIComponent(param) + "=";
+    var query;
+    if (elem.search) {
+        query = elem.search.slice(1).split('&');
+    } else {
+        query = [];
+    }
+
+    if (!query.some(function (v) { return v.startsWith(param_eq); })) {
+        query.push(param_eq + encodeURIComponent(value));
+        elem.search = "?" + query.join("&");
+    }
+
+    // some browsers (e.g. IE11) may occasionally omit the leading slash
+    // in the elem.pathname string. Handle that case gracefully.
+    if (elem.pathname.charAt(0) == "/") {
+        return elem.pathname.slice(1) + elem.search + elem.hash;
+    } else {
+        return elem.pathname + elem.search + elem.hash;
+    }
+};
+
+// sadly, we can't use the Fetch API until we decide to drop
+// IE11 support or polyfill promises and fetch in IE11.
+// resolve will receive an object on success, while reject
+// will receive either an event or an error on failure.
+export function fetchJSON(path, resolve, reject) {
+    // NB: IE11 doesn't support JSON as a responseType
+    var req = new XMLHttpRequest();
+    req.open('GET', path);
+
+    req.onload = function () {
+        if (req.status === 200) {
+            try {
+                var resObj = JSON.parse(req.responseText);
+            } catch (err) {
+                reject(err);
+                return;
+            }
+            resolve(resObj);
+        } else {
+            reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
+        }
+    };
+
+    req.onerror = function (evt) {
+        reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
+    };
+
+    req.ontimeout = function (evt) {
+        reject(new Error("XHR timed out while trying to load '" + path + "'"));
+    };
+
+    req.send();
+}

+ 110 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/base64.js

@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
+
+import * as Log from './util/logging.js';
+
+export default {
+    /* Convert data (an array of integers) to a Base64 string. */
+    toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
+    base64Pad     : '=',
+
+    encode: function (data) {
+        "use strict";
+        var result = '';
+        var toBase64Table = this.toBase64Table;
+        var length = data.length;
+        var lengthpad = (length % 3);
+        // Convert every three bytes to 4 ascii characters.
+
+        for (var i = 0; i < (length - 2); i += 3) {
+            result += toBase64Table[data[i] >> 2];
+            result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
+            result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
+            result += toBase64Table[data[i + 2] & 0x3f];
+        }
+
+        // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
+        var j = 0;
+        if (lengthpad === 2) {
+            j = length - lengthpad;
+            result += toBase64Table[data[j] >> 2];
+            result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
+            result += toBase64Table[(data[j + 1] & 0x0f) << 2];
+            result += toBase64Table[64];
+        } else if (lengthpad === 1) {
+            j = length - lengthpad;
+            result += toBase64Table[data[j] >> 2];
+            result += toBase64Table[(data[j] & 0x03) << 4];
+            result += toBase64Table[64];
+            result += toBase64Table[64];
+        }
+
+        return result;
+    },
+
+    /* Convert Base64 data to a string */
+    toBinaryTable : [
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+        52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
+        -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+        15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+        -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+        41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+    ],
+
+    decode: function (data, offset) {
+        "use strict";
+        offset = typeof(offset) !== 'undefined' ? offset : 0;
+        var toBinaryTable = this.toBinaryTable;
+        var base64Pad = this.base64Pad;
+        var result, result_length;
+        var leftbits = 0; // number of bits decoded, but yet to be appended
+        var leftdata = 0; // bits decoded, but yet to be appended
+        var data_length = data.indexOf('=') - offset;
+
+        if (data_length < 0) { data_length = data.length - offset; }
+
+        /* Every four characters is 3 resulting numbers */
+        result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
+        result = new Array(result_length);
+
+        // Convert one by one.
+        for (var idx = 0, i = offset; i < data.length; i++) {
+            var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
+            var padding = (data.charAt(i) === base64Pad);
+            // Skip illegal characters and whitespace
+            if (c === -1) {
+                Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
+                continue;
+            }
+
+            // Collect data into leftdata, update bitcount
+            leftdata = (leftdata << 6) | c;
+            leftbits += 6;
+
+            // If we have 8 or more bits, append 8 bits to the result
+            if (leftbits >= 8) {
+                leftbits -= 8;
+                // Append if not padding.
+                if (!padding) {
+                    result[idx++] = (leftdata >> leftbits) & 0xff;
+                }
+                leftdata &= (1 << leftbits) - 1;
+            }
+        }
+
+        // If there are any bits left, the base64 string was corrupted
+        if (leftbits) {
+            err = new Error('Corrupted base64 string');
+            err.name = 'Base64-Error';
+            throw err;
+        }
+
+        return result;
+    }
+}; /* End of Base64 namespace */

+ 271 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/des.js

@@ -0,0 +1,271 @@
+/*
+ * Ported from Flashlight VNC ActionScript implementation:
+ *     http://www.wizhelp.com/flashlight-vnc/
+ *
+ * Full attribution follows:
+ *
+ * -------------------------------------------------------------------------
+ *
+ * This DES class has been extracted from package Acme.Crypto for use in VNC.
+ * The unnecessary odd parity code has been removed.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+
+ * DesCipher - the DES encryption method
+ *
+ * The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
+ *
+ * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
+ * without fee is hereby granted, provided that this copyright notice is kept
+ * intact.
+ *
+ * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
+ * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
+ * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
+ * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
+ * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
+ * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
+ * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  WIDGET WORKSHOP
+ * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
+ * HIGH RISK ACTIVITIES.
+ *
+ *
+ * The rest is:
+ *
+ * Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Visit the ACME Labs Java page for up-to-date versions of this and other
+ * fine Java utilities: http://www.acme.com/java/
+ */
+
+export default function DES(passwd) {
+    "use strict";
+
+    // Tables, permutations, S-boxes, etc.
+    var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
+               25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
+               50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
+        totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
+        z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
+        keys = [];
+
+    a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
+    SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
+           z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
+           a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
+           c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
+    a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
+    SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
+           a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
+           z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
+           z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
+    a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
+    SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
+           b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
+           c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
+           b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
+    a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
+    SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
+           z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
+           b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
+           c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
+    a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
+    SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
+           a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
+           z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
+           c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
+    a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
+    SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
+           z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
+           b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
+           a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
+    a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
+    SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
+           b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
+           b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
+           z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
+    a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
+    SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
+           c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
+           a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
+           z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
+
+    // Set the key.
+    function setKeys(keyBlock) {
+        var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
+            raw0, raw1, rawi, KnLi;
+
+        for (j = 0, l = 56; j < 56; ++j, l -= 8) {
+            l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
+            m = l & 0x7;
+            pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
+        }
+
+        for (i = 0; i < 16; ++i) {
+            m = i << 1;
+            n = m + 1;
+            kn[m] = kn[n] = 0;
+            for (o = 28; o < 59; o += 28) {
+                for (j = o - 28; j < o; ++j) {
+                    l = j + totrot[i];
+                    if (l < o) {
+                        pcr[j] = pc1m[l];
+                    } else {
+                        pcr[j] = pc1m[l - 28];
+                    }
+                }
+            }
+            for (j = 0; j < 24; ++j) {
+                if (pcr[PC2[j]] !== 0) {
+                    kn[m] |= 1 << (23 - j);
+                }
+                if (pcr[PC2[j + 24]] !== 0) {
+                    kn[n] |= 1 << (23 - j);
+                }
+            }
+        }
+
+        // cookey
+        for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
+            raw0 = kn[rawi++];
+            raw1 = kn[rawi++];
+            keys[KnLi] = (raw0 & 0x00fc0000) << 6;
+            keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
+            keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
+            keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
+            ++KnLi;
+            keys[KnLi] = (raw0 & 0x0003f000) << 12;
+            keys[KnLi] |= (raw0 & 0x0000003f) << 16;
+            keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
+            keys[KnLi] |= (raw1 & 0x0000003f);
+            ++KnLi;
+        }
+    }
+
+    // Encrypt 8 bytes of text
+    function enc8(text) {
+        var i = 0, b = text.slice(), fval, keysi = 0,
+            l, r, x; // left, right, accumulator
+
+        // Squash 8 bytes to 2 ints
+        l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
+        r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
+
+        x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
+        r ^= x;
+        l ^= (x << 4);
+        x = ((l >>> 16) ^ r) & 0x0000ffff;
+        r ^= x;
+        l ^= (x << 16);
+        x = ((r >>> 2) ^ l) & 0x33333333;
+        l ^= x;
+        r ^= (x << 2);
+        x = ((r >>> 8) ^ l) & 0x00ff00ff;
+        l ^= x;
+        r ^= (x << 8);
+        r = (r << 1) | ((r >>> 31) & 1);
+        x = (l ^ r) & 0xaaaaaaaa;
+        l ^= x;
+        r ^= x;
+        l = (l << 1) | ((l >>> 31) & 1);
+
+        for (i = 0; i < 8; ++i) {
+            x = (r << 28) | (r >>> 4);
+            x ^= keys[keysi++];
+            fval =  SP7[x & 0x3f];
+            fval |= SP5[(x >>> 8) & 0x3f];
+            fval |= SP3[(x >>> 16) & 0x3f];
+            fval |= SP1[(x >>> 24) & 0x3f];
+            x = r ^ keys[keysi++];
+            fval |= SP8[x & 0x3f];
+            fval |= SP6[(x >>> 8) & 0x3f];
+            fval |= SP4[(x >>> 16) & 0x3f];
+            fval |= SP2[(x >>> 24) & 0x3f];
+            l ^= fval;
+            x = (l << 28) | (l >>> 4);
+            x ^= keys[keysi++];
+            fval =  SP7[x & 0x3f];
+            fval |= SP5[(x >>> 8) & 0x3f];
+            fval |= SP3[(x >>> 16) & 0x3f];
+            fval |= SP1[(x >>> 24) & 0x3f];
+            x = l ^ keys[keysi++];
+            fval |= SP8[x & 0x0000003f];
+            fval |= SP6[(x >>> 8) & 0x3f];
+            fval |= SP4[(x >>> 16) & 0x3f];
+            fval |= SP2[(x >>> 24) & 0x3f];
+            r ^= fval;
+        }
+
+        r = (r << 31) | (r >>> 1);
+        x = (l ^ r) & 0xaaaaaaaa;
+        l ^= x;
+        r ^= x;
+        l = (l << 31) | (l >>> 1);
+        x = ((l >>> 8) ^ r) & 0x00ff00ff;
+        r ^= x;
+        l ^= (x << 8);
+        x = ((l >>> 2) ^ r) & 0x33333333;
+        r ^= x;
+        l ^= (x << 2);
+        x = ((r >>> 16) ^ l) & 0x0000ffff;
+        l ^= x;
+        r ^= (x << 16);
+        x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
+        l ^= x;
+        r ^= (x << 4);
+
+        // Spread ints to bytes
+        x = [r, l];
+        for (i = 0; i < 8; i++) {
+            b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
+            if (b[i] < 0) { b[i] += 256; } // unsigned
+        }
+        return b;
+    }
+
+    // Encrypt 16 bytes of text using passwd as key
+    function encrypt(t) {
+        return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
+    }
+
+    setKeys(passwd);             // Setup keys
+    return {'encrypt': encrypt}; // Public interface
+
+}; // function DES

+ 698 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/display.js

@@ -0,0 +1,698 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2015 Samuel Mannehed for Cendio AB
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+import * as Log from './util/logging.js';
+import Base64 from "./base64.js";
+
+export default function Display(target) {
+    this._drawCtx = null;
+    this._c_forceCanvas = false;
+
+    this._renderQ = [];  // queue drawing actions for in-oder rendering
+    this._flushing = false;
+
+    // the full frame buffer (logical canvas) size
+    this._fb_width = 0;
+    this._fb_height = 0;
+
+    this._prevDrawStyle = "";
+    this._tile = null;
+    this._tile16x16 = null;
+    this._tile_x = 0;
+    this._tile_y = 0;
+
+    Log.Debug(">> Display.constructor");
+
+    // The visible canvas
+    this._target = target;
+
+    if (!this._target) {
+        throw new Error("Target must be set");
+    }
+
+    if (typeof this._target === 'string') {
+        throw new Error('target must be a DOM element');
+    }
+
+    if (!this._target.getContext) {
+        throw new Error("no getContext method");
+    }
+
+    this._targetCtx = this._target.getContext('2d');
+
+    // the visible canvas viewport (i.e. what actually gets seen)
+    this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
+
+    // The hidden canvas, where we do the actual rendering
+    this._backbuffer = document.createElement('canvas');
+    this._drawCtx = this._backbuffer.getContext('2d');
+
+    this._damageBounds = { left:0, top:0,
+                           right: this._backbuffer.width,
+                           bottom: this._backbuffer.height };
+
+    Log.Debug("User Agent: " + navigator.userAgent);
+
+    this.clear();
+
+    // Check canvas features
+    if (!('createImageData' in this._drawCtx)) {
+        throw new Error("Canvas does not support createImageData");
+    }
+
+    this._tile16x16 = this._drawCtx.createImageData(16, 16);
+    Log.Debug("<< Display.constructor");
+};
+
+var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
+try {
+    new ImageData(new Uint8ClampedArray(4), 1, 1);
+    SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
+} catch (ex) {
+    // ignore failure
+}
+
+Display.prototype = {
+    // ===== PROPERTIES =====
+
+    _scale: 1.0,
+    get scale() { return this._scale; },
+    set scale(scale) {
+        this._rescale(scale);
+    },
+
+    _clipViewport: false,
+    get clipViewport() { return this._clipViewport; },
+    set clipViewport(viewport) {
+        this._clipViewport = viewport;
+        // May need to readjust the viewport dimensions
+        var vp = this._viewportLoc;
+        this.viewportChangeSize(vp.w, vp.h);
+        this.viewportChangePos(0, 0);
+    },
+
+    get width() {
+        return this._fb_width;
+    },
+    get height() {
+        return this._fb_height;
+    },
+
+    logo: null,
+
+    // ===== EVENT HANDLERS =====
+
+    onflush: function () {},        // A flush request has finished
+
+    // ===== PUBLIC METHODS =====
+
+    viewportChangePos: function (deltaX, deltaY) {
+        var vp = this._viewportLoc;
+        deltaX = Math.floor(deltaX);
+        deltaY = Math.floor(deltaY);
+
+        if (!this._clipViewport) {
+            deltaX = -vp.w;  // clamped later of out of bounds
+            deltaY = -vp.h;
+        }
+
+        var vx2 = vp.x + vp.w - 1;
+        var vy2 = vp.y + vp.h - 1;
+
+        // Position change
+
+        if (deltaX < 0 && vp.x + deltaX < 0) {
+            deltaX = -vp.x;
+        }
+        if (vx2 + deltaX >= this._fb_width) {
+            deltaX -= vx2 + deltaX - this._fb_width + 1;
+        }
+
+        if (vp.y + deltaY < 0) {
+            deltaY = -vp.y;
+        }
+        if (vy2 + deltaY >= this._fb_height) {
+            deltaY -= (vy2 + deltaY - this._fb_height + 1);
+        }
+
+        if (deltaX === 0 && deltaY === 0) {
+            return;
+        }
+        Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
+
+        vp.x += deltaX;
+        vp.y += deltaY;
+
+        this._damage(vp.x, vp.y, vp.w, vp.h);
+
+        this.flip();
+    },
+
+    viewportChangeSize: function(width, height) {
+
+        if (!this._clipViewport ||
+            typeof(width) === "undefined" ||
+            typeof(height) === "undefined") {
+
+            Log.Debug("Setting viewport to full display region");
+            width = this._fb_width;
+            height = this._fb_height;
+        }
+
+        if (width > this._fb_width) {
+            width = this._fb_width;
+        }
+        if (height > this._fb_height) {
+            height = this._fb_height;
+        }
+
+        var vp = this._viewportLoc;
+        if (vp.w !== width || vp.h !== height) {
+            vp.w = width;
+            vp.h = height;
+
+            var canvas = this._target;
+            canvas.width = width;
+            canvas.height = height;
+
+            // The position might need to be updated if we've grown
+            this.viewportChangePos(0, 0);
+
+            this._damage(vp.x, vp.y, vp.w, vp.h);
+            this.flip();
+
+            // Update the visible size of the target canvas
+            this._rescale(this._scale);
+        }
+    },
+
+    absX: function (x) {
+        return x / this._scale + this._viewportLoc.x;
+    },
+
+    absY: function (y) {
+        return y / this._scale + this._viewportLoc.y;
+    },
+
+    resize: function (width, height) {
+        this._prevDrawStyle = "";
+
+        this._fb_width = width;
+        this._fb_height = height;
+
+        var canvas = this._backbuffer;
+        if (canvas.width !== width || canvas.height !== height) {
+
+            // We have to save the canvas data since changing the size will clear it
+            var saveImg = null;
+            if (canvas.width > 0 && canvas.height > 0) {
+                saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
+            }
+
+            if (canvas.width !== width) {
+                canvas.width = width;
+            }
+            if (canvas.height !== height) {
+                canvas.height = height;
+            }
+
+            if (saveImg) {
+                this._drawCtx.putImageData(saveImg, 0, 0);
+            }
+        }
+
+        // Readjust the viewport as it may be incorrectly sized
+        // and positioned
+        var vp = this._viewportLoc;
+        this.viewportChangeSize(vp.w, vp.h);
+        this.viewportChangePos(0, 0);
+    },
+
+    // Track what parts of the visible canvas that need updating
+    _damage: function(x, y, w, h) {
+        if (x < this._damageBounds.left) {
+            this._damageBounds.left = x;
+        }
+        if (y < this._damageBounds.top) {
+            this._damageBounds.top = y;
+        }
+        if ((x + w) > this._damageBounds.right) {
+            this._damageBounds.right = x + w;
+        }
+        if ((y + h) > this._damageBounds.bottom) {
+            this._damageBounds.bottom = y + h;
+        }
+    },
+
+    // Update the visible canvas with the contents of the
+    // rendering canvas
+    flip: function(from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            this._renderQ_push({
+                'type': 'flip'
+            });
+        } else {
+            var x, y, vx, vy, w, h;
+
+            x = this._damageBounds.left;
+            y = this._damageBounds.top;
+            w = this._damageBounds.right - x;
+            h = this._damageBounds.bottom - y;
+
+            vx = x - this._viewportLoc.x;
+            vy = y - this._viewportLoc.y;
+
+            if (vx < 0) {
+                w += vx;
+                x -= vx;
+                vx = 0;
+            }
+            if (vy < 0) {
+                h += vy;
+                y -= vy;
+                vy = 0;
+            }
+
+            if ((vx + w) > this._viewportLoc.w) {
+                w = this._viewportLoc.w - vx;
+            }
+            if ((vy + h) > this._viewportLoc.h) {
+                h = this._viewportLoc.h - vy;
+            }
+
+            if ((w > 0) && (h > 0)) {
+                // FIXME: We may need to disable image smoothing here
+                //        as well (see copyImage()), but we haven't
+                //        noticed any problem yet.
+                this._targetCtx.drawImage(this._backbuffer,
+                                          x, y, w, h,
+                                          vx, vy, w, h);
+            }
+
+            this._damageBounds.left = this._damageBounds.top = 65535;
+            this._damageBounds.right = this._damageBounds.bottom = 0;
+        }
+    },
+
+    clear: function () {
+        if (this._logo) {
+            this.resize(this._logo.width, this._logo.height);
+            this.imageRect(0, 0, this._logo.type, this._logo.data);
+        } else {
+            this.resize(240, 20);
+            this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
+        }
+        this.flip();
+    },
+
+    pending: function() {
+        return this._renderQ.length > 0;
+    },
+
+    flush: function() {
+        if (this._renderQ.length === 0) {
+            this.onflush();
+        } else {
+            this._flushing = true;
+        }
+    },
+
+    fillRect: function (x, y, width, height, color, from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            this._renderQ_push({
+                'type': 'fill',
+                'x': x,
+                'y': y,
+                'width': width,
+                'height': height,
+                'color': color
+            });
+        } else {
+            this._setFillColor(color);
+            this._drawCtx.fillRect(x, y, width, height);
+            this._damage(x, y, width, height);
+        }
+    },
+
+    copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            this._renderQ_push({
+                'type': 'copy',
+                'old_x': old_x,
+                'old_y': old_y,
+                'x': new_x,
+                'y': new_y,
+                'width': w,
+                'height': h,
+            });
+        } else {
+            // Due to this bug among others [1] we need to disable the image-smoothing to
+            // avoid getting a blur effect when copying data.
+            //
+            // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719
+            //
+            // We need to set these every time since all properties are reset
+            // when the the size is changed
+            this._drawCtx.mozImageSmoothingEnabled = false;
+            this._drawCtx.webkitImageSmoothingEnabled = false;
+            this._drawCtx.msImageSmoothingEnabled = false;
+            this._drawCtx.imageSmoothingEnabled = false;
+
+            this._drawCtx.drawImage(this._backbuffer,
+                                    old_x, old_y, w, h,
+                                    new_x, new_y, w, h);
+            this._damage(new_x, new_y, w, h);
+        }
+    },
+
+    imageRect: function(x, y, mime, arr) {
+        var img = new Image();
+        img.src = "data: " + mime + ";base64," + Base64.encode(arr);
+        this._renderQ_push({
+            'type': 'img',
+            'img': img,
+            'x': x,
+            'y': y
+        });
+    },
+
+    // start updating a tile
+    startTile: function (x, y, width, height, color) {
+        this._tile_x = x;
+        this._tile_y = y;
+        if (width === 16 && height === 16) {
+            this._tile = this._tile16x16;
+        } else {
+            this._tile = this._drawCtx.createImageData(width, height);
+        }
+
+        var red = color[2];
+        var green = color[1];
+        var blue = color[0];
+
+        var data = this._tile.data;
+        for (var i = 0; i < width * height * 4; i += 4) {
+            data[i] = red;
+            data[i + 1] = green;
+            data[i + 2] = blue;
+            data[i + 3] = 255;
+        }
+    },
+
+    // update sub-rectangle of the current tile
+    subTile: function (x, y, w, h, color) {
+        var red = color[2];
+        var green = color[1];
+        var blue = color[0];
+        var xend = x + w;
+        var yend = y + h;
+
+        var data = this._tile.data;
+        var width = this._tile.width;
+        for (var j = y; j < yend; j++) {
+            for (var i = x; i < xend; i++) {
+                var p = (i + (j * width)) * 4;
+                data[p] = red;
+                data[p + 1] = green;
+                data[p + 2] = blue;
+                data[p + 3] = 255;
+            }
+        }
+    },
+
+    // draw the current tile to the screen
+    finishTile: function () {
+        this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
+        this._damage(this._tile_x, this._tile_y,
+                     this._tile.width, this._tile.height);
+    },
+
+    blitImage: function (x, y, width, height, arr, offset, from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            // NB(directxman12): it's technically more performant here to use preallocated arrays,
+            // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
+            // this probably isn't getting called *nearly* as much
+            var new_arr = new Uint8Array(width * height * 4);
+            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
+            this._renderQ_push({
+                'type': 'blit',
+                'data': new_arr,
+                'x': x,
+                'y': y,
+                'width': width,
+                'height': height,
+            });
+        } else {
+            this._bgrxImageData(x, y, width, height, arr, offset);
+        }
+    },
+
+    blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            // NB(directxman12): it's technically more performant here to use preallocated arrays,
+            // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
+            // this probably isn't getting called *nearly* as much
+            var new_arr = new Uint8Array(width * height * 3);
+            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
+            this._renderQ_push({
+                'type': 'blitRgb',
+                'data': new_arr,
+                'x': x,
+                'y': y,
+                'width': width,
+                'height': height,
+            });
+        } else {
+            this._rgbImageData(x, y, width, height, arr, offset);
+        }
+    },
+
+    blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
+        if (this._renderQ.length !== 0 && !from_queue) {
+            // NB(directxman12): it's technically more performant here to use preallocated arrays,
+            // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
+            // this probably isn't getting called *nearly* as much
+            var new_arr = new Uint8Array(width * height * 4);
+            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
+            this._renderQ_push({
+                'type': 'blitRgbx',
+                'data': new_arr,
+                'x': x,
+                'y': y,
+                'width': width,
+                'height': height,
+            });
+        } else {
+            this._rgbxImageData(x, y, width, height, arr, offset);
+        }
+    },
+
+    drawImage: function (img, x, y) {
+        this._drawCtx.drawImage(img, x, y);
+        this._damage(x, y, img.width, img.height);
+    },
+
+    changeCursor: function (pixels, mask, hotx, hoty, w, h) {
+        Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
+    },
+
+    defaultCursor: function () {
+        this._target.style.cursor = "default";
+    },
+
+    disableLocalCursor: function () {
+        this._target.style.cursor = "none";
+    },
+
+    autoscale: function (containerWidth, containerHeight) {
+        var vp = this._viewportLoc;
+        var targetAspectRatio = containerWidth / containerHeight;
+        var fbAspectRatio = vp.w / vp.h;
+
+        var scaleRatio;
+        if (fbAspectRatio >= targetAspectRatio) {
+            scaleRatio = containerWidth / vp.w;
+        } else {
+            scaleRatio = containerHeight / vp.h;
+        }
+
+        this._rescale(scaleRatio);
+    },
+
+    // ===== PRIVATE METHODS =====
+
+    _rescale: function (factor) {
+        this._scale = factor;
+        var vp = this._viewportLoc;
+
+        // NB(directxman12): If you set the width directly, or set the
+        //                   style width to a number, the canvas is cleared.
+        //                   However, if you set the style width to a string
+        //                   ('NNNpx'), the canvas is scaled without clearing.
+        var width = Math.round(factor * vp.w) + 'px';
+        var height = Math.round(factor * vp.h) + 'px';
+
+        if ((this._target.style.width !== width) ||
+            (this._target.style.height !== height)) {
+            this._target.style.width = width;
+            this._target.style.height = height;
+        }
+    },
+
+    _setFillColor: function (color) {
+        var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
+        if (newStyle !== this._prevDrawStyle) {
+            this._drawCtx.fillStyle = newStyle;
+            this._prevDrawStyle = newStyle;
+        }
+    },
+
+    _rgbImageData: function (x, y, width, height, arr, offset) {
+        var img = this._drawCtx.createImageData(width, height);
+        var data = img.data;
+        for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
+            data[i]     = arr[j];
+            data[i + 1] = arr[j + 1];
+            data[i + 2] = arr[j + 2];
+            data[i + 3] = 255;  // Alpha
+        }
+        this._drawCtx.putImageData(img, x, y);
+        this._damage(x, y, img.width, img.height);
+    },
+
+    _bgrxImageData: function (x, y, width, height, arr, offset) {
+        var img = this._drawCtx.createImageData(width, height);
+        var data = img.data;
+        for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
+            data[i]     = arr[j + 2];
+            data[i + 1] = arr[j + 1];
+            data[i + 2] = arr[j];
+            data[i + 3] = 255;  // Alpha
+        }
+        this._drawCtx.putImageData(img, x, y);
+        this._damage(x, y, img.width, img.height);
+    },
+
+    _rgbxImageData: function (x, y, width, height, arr, offset) {
+        // NB(directxman12): arr must be an Type Array view
+        var img;
+        if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
+            img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
+        } else {
+            img = this._drawCtx.createImageData(width, height);
+            img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
+        }
+        this._drawCtx.putImageData(img, x, y);
+        this._damage(x, y, img.width, img.height);
+    },
+
+    _renderQ_push: function (action) {
+        this._renderQ.push(action);
+        if (this._renderQ.length === 1) {
+            // If this can be rendered immediately it will be, otherwise
+            // the scanner will wait for the relevant event
+            this._scan_renderQ();
+        }
+    },
+
+    _resume_renderQ: function() {
+        // "this" is the object that is ready, not the
+        // display object
+        this.removeEventListener('load', this._noVNC_display._resume_renderQ);
+        this._noVNC_display._scan_renderQ();
+    },
+
+    _scan_renderQ: function () {
+        var ready = true;
+        while (ready && this._renderQ.length > 0) {
+            var a = this._renderQ[0];
+            switch (a.type) {
+                case 'flip':
+                    this.flip(true);
+                    break;
+                case 'copy':
+                    this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
+                    break;
+                case 'fill':
+                    this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
+                    break;
+                case 'blit':
+                    this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
+                    break;
+                case 'blitRgb':
+                    this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
+                    break;
+                case 'blitRgbx':
+                    this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
+                    break;
+                case 'img':
+                    if (a.img.complete) {
+                        this.drawImage(a.img, a.x, a.y);
+                    } else {
+                        a.img._noVNC_display = this;
+                        a.img.addEventListener('load', this._resume_renderQ);
+                        // We need to wait for this image to 'load'
+                        // to keep things in-order
+                        ready = false;
+                    }
+                    break;
+            }
+
+            if (ready) {
+                this._renderQ.shift();
+            }
+        }
+
+        if (this._renderQ.length === 0 && this._flushing) {
+            this._flushing = false;
+            this.onflush();
+        }
+    },
+};
+
+// Class Methods
+Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
+    if ((w === 0) || (h === 0)) {
+        target.style.cursor = 'none';
+        return;
+    }
+
+    var cur = []
+    var y, x;
+    for (y = 0; y < h; y++) {
+        for (x = 0; x < w; x++) {
+            var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
+            var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
+            idx = ((w * y) + x) * 4;
+            cur.push(pixels[idx + 2]); // red
+            cur.push(pixels[idx + 1]); // green
+            cur.push(pixels[idx]);     // blue
+            cur.push(alpha);           // alpha
+        }
+    }
+
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d');
+
+    canvas.width = w;
+    canvas.height = h;
+
+    var img;
+    if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
+        img = new ImageData(new Uint8ClampedArray(cur), w, h);
+    } else {
+        img = ctx.createImageData(w, h);
+        img.data.set(new Uint8ClampedArray(cur));
+    }
+    ctx.clearRect(0, 0, w, h);
+    ctx.putImageData(img, 0, 0);
+
+    var url = canvas.toDataURL();
+    target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
+};

+ 40 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/encodings.js

@@ -0,0 +1,40 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+export var encodings = {
+    encodingRaw: 0,
+    encodingCopyRect: 1,
+    encodingRRE: 2,
+    encodingHextile: 5,
+    encodingTight: 7,
+
+    pseudoEncodingQualityLevel9: -23,
+    pseudoEncodingQualityLevel0: -32,
+    pseudoEncodingDesktopSize: -223,
+    pseudoEncodingLastRect: -224,
+    pseudoEncodingCursor: -239,
+    pseudoEncodingQEMUExtendedKeyEvent: -258,
+    pseudoEncodingTightPNG: -260,
+    pseudoEncodingExtendedDesktopSize: -308,
+    pseudoEncodingXvp: -309,
+    pseudoEncodingFence: -312,
+    pseudoEncodingContinuousUpdates: -313,
+    pseudoEncodingCompressLevel9: -247,
+    pseudoEncodingCompressLevel0: -256,
+};
+
+export function encodingName(num) {
+    switch (num) {
+        case encodings.encodingRaw:      return "Raw";
+        case encodings.encodingCopyRect: return "CopyRect";
+        case encodings.encodingRRE:      return "RRE";
+        case encodings.encodingHextile:  return "Hextile";
+        case encodings.encodingTight:    return "Tight";
+        default:                         return "[unknown encoding " + num + "]";
+    }
+}

+ 38 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/inflator.js

@@ -0,0 +1,38 @@
+import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
+import ZStream from "../vendor/pako/lib/zlib/zstream.js";
+
+Inflate.prototype = {
+    inflate: function (data, flush, expected) {
+        this.strm.input = data;
+        this.strm.avail_in = this.strm.input.length;
+        this.strm.next_in = 0;
+        this.strm.next_out = 0;
+
+        // resize our output buffer if it's too small
+        // (we could just use multiple chunks, but that would cause an extra
+        // allocation each time to flatten the chunks)
+        if (expected > this.chunkSize) {
+            this.chunkSize = expected;
+            this.strm.output = new Uint8Array(this.chunkSize);
+        }
+
+        this.strm.avail_out = this.chunkSize;
+
+        inflate(this.strm, flush);
+
+        return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
+    },
+
+    reset: function () {
+        inflateReset(this.strm);
+    }
+};
+
+export default function Inflate() {
+    this.strm = new ZStream();
+    this.chunkSize = 1024 * 10 * 10;
+    this.strm.output = new Uint8Array(this.chunkSize);
+    this.windowBits = 5;
+
+    inflateInit(this.strm, this.windowBits);
+};

+ 310 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/domkeytable.js

@@ -0,0 +1,310 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+import KeyTable from "./keysym.js";
+
+/*
+ * Mapping between HTML key values and VNC/X11 keysyms for "special"
+ * keys that cannot be handled via their Unicode codepoint.
+ *
+ * See https://www.w3.org/TR/uievents-key/ for possible values.
+ */
+
+var DOMKeyTable = {};
+
+function addStandard(key, standard)
+{
+    if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [standard, standard, standard, standard];
+}
+
+function addLeftRight(key, left, right)
+{
+    if (left === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (right === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [left, left, right, left];
+}
+
+function addNumpad(key, standard, numpad)
+{
+    if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\"";
+    if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
+    DOMKeyTable[key] = [standard, standard, standard, numpad];
+}
+
+// 2.2. Modifier Keys
+
+addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);
+addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift);
+addStandard("CapsLock", KeyTable.XK_Caps_Lock);
+addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
+// - Fn
+// - FnLock
+addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+addStandard("NumLock", KeyTable.XK_Num_Lock);
+addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
+addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
+addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
+// - Symbol
+// - SymbolLock
+
+// 2.3. Whitespace Keys
+
+addNumpad("Enter", KeyTable.XK_Return, KeyTable.XK_KP_Enter);
+addStandard("Tab", KeyTable.XK_Tab);
+addNumpad(" ", KeyTable.XK_space, KeyTable.XK_KP_Space);
+
+// 2.4. Navigation Keys
+
+addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down);
+addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
+addNumpad("ArrowLeft", KeyTable.XK_Left, KeyTable.XK_KP_Left);
+addNumpad("ArrowRight", KeyTable.XK_Right, KeyTable.XK_KP_Right);
+addNumpad("End", KeyTable.XK_End, KeyTable.XK_KP_End);
+addNumpad("Home", KeyTable.XK_Home, KeyTable.XK_KP_Home);
+addNumpad("PageDown", KeyTable.XK_Next, KeyTable.XK_KP_Next);
+addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
+
+// 2.5. Editing Keys
+
+addStandard("Backspace", KeyTable.XK_BackSpace);
+addStandard("Clear", KeyTable.XK_Clear);
+addStandard("Copy", KeyTable.XF86XK_Copy);
+// - CrSel
+addStandard("Cut", KeyTable.XF86XK_Cut);
+addNumpad("Delete", KeyTable.XK_Delete, KeyTable.XK_KP_Delete);
+// - EraseEof
+// - ExSel
+addNumpad("Insert", KeyTable.XK_Insert, KeyTable.XK_KP_Insert);
+addStandard("Paste", KeyTable.XF86XK_Paste);
+addStandard("Redo", KeyTable.XK_Redo);
+addStandard("Undo", KeyTable.XK_Undo);
+
+// 2.6. UI Keys
+
+// - Accept
+// - Again (could just be XK_Redo)
+// - Attn
+addStandard("Cancel", KeyTable.XK_Cancel);
+addStandard("ContextMenu", KeyTable.XK_Menu);
+addStandard("Escape", KeyTable.XK_Escape);
+addStandard("Execute", KeyTable.XK_Execute);
+addStandard("Find", KeyTable.XK_Find);
+addStandard("Help", KeyTable.XK_Help);
+addStandard("Pause", KeyTable.XK_Pause);
+// - Play
+// - Props
+addStandard("Select", KeyTable.XK_Select);
+addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn);
+addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut);
+
+// 2.7. Device Keys
+
+addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown);
+addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp);
+addStandard("Eject", KeyTable.XF86XK_Eject);
+addStandard("LogOff", KeyTable.XF86XK_LogOff);
+addStandard("Power", KeyTable.XF86XK_PowerOff);
+addStandard("PowerOff", KeyTable.XF86XK_PowerDown);
+addStandard("PrintScreen", KeyTable.XK_Print);
+addStandard("Hibernate", KeyTable.XF86XK_Hibernate);
+addStandard("Standby", KeyTable.XF86XK_Standby);
+addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
+
+// 2.8. IME and Composition Keys
+
+addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
+addStandard("Alphanumeric", KeyTable.XK_Eisu_Shift); // could also be _Eisu_Toggle
+addStandard("CodeInput", KeyTable.XK_Codeinput);
+addStandard("Compose", KeyTable.XK_Multi_key);
+addStandard("Convert", KeyTable.XK_Henkan);
+// - Dead
+// - FinalMode
+addStandard("GroupFirst", KeyTable.XK_ISO_First_Group);
+addStandard("GroupLast", KeyTable.XK_ISO_Last_Group);
+addStandard("GroupNext", KeyTable.XK_ISO_Next_Group);
+addStandard("GroupPrevious", KeyTable.XK_ISO_Prev_Group);
+// - ModeChange (XK_Mode_switch is often used for AltGr)
+// - NextCandidate
+addStandard("NonConvert", KeyTable.XK_Muhenkan);
+addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate);
+// - Process
+addStandard("SingleCandidate", KeyTable.XK_SingleCandidate);
+addStandard("HangulMode", KeyTable.XK_Hangul);
+addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja);
+addStandard("JunjuaMode", KeyTable.XK_Hangul_Jeonja);
+addStandard("Eisu", KeyTable.XK_Eisu_toggle);
+addStandard("Hankaku", KeyTable.XK_Hankaku);
+addStandard("Hiragana", KeyTable.XK_Hiragana);
+addStandard("HiraganaKatakana", KeyTable.XK_Hiragana_Katakana);
+addStandard("KanaMode", KeyTable.XK_Kana_Shift); // could also be _Kana_Lock
+addStandard("KanjiMode", KeyTable.XK_Kanji);
+addStandard("Katakana", KeyTable.XK_Katakana);
+addStandard("Romaji", KeyTable.XK_Romaji);
+addStandard("Zenkaku", KeyTable.XK_Zenkaku);
+addStandard("ZenkakuHanaku", KeyTable.XK_Zenkaku_Hankaku);
+
+// 2.9. General-Purpose Function Keys
+
+addStandard("F1", KeyTable.XK_F1);
+addStandard("F2", KeyTable.XK_F2);
+addStandard("F3", KeyTable.XK_F3);
+addStandard("F4", KeyTable.XK_F4);
+addStandard("F5", KeyTable.XK_F5);
+addStandard("F6", KeyTable.XK_F6);
+addStandard("F7", KeyTable.XK_F7);
+addStandard("F8", KeyTable.XK_F8);
+addStandard("F9", KeyTable.XK_F9);
+addStandard("F10", KeyTable.XK_F10);
+addStandard("F11", KeyTable.XK_F11);
+addStandard("F12", KeyTable.XK_F12);
+addStandard("F13", KeyTable.XK_F13);
+addStandard("F14", KeyTable.XK_F14);
+addStandard("F15", KeyTable.XK_F15);
+addStandard("F16", KeyTable.XK_F16);
+addStandard("F17", KeyTable.XK_F17);
+addStandard("F18", KeyTable.XK_F18);
+addStandard("F19", KeyTable.XK_F19);
+addStandard("F20", KeyTable.XK_F20);
+addStandard("F21", KeyTable.XK_F21);
+addStandard("F22", KeyTable.XK_F22);
+addStandard("F23", KeyTable.XK_F23);
+addStandard("F24", KeyTable.XK_F24);
+addStandard("F25", KeyTable.XK_F25);
+addStandard("F26", KeyTable.XK_F26);
+addStandard("F27", KeyTable.XK_F27);
+addStandard("F28", KeyTable.XK_F28);
+addStandard("F29", KeyTable.XK_F29);
+addStandard("F30", KeyTable.XK_F30);
+addStandard("F31", KeyTable.XK_F31);
+addStandard("F32", KeyTable.XK_F32);
+addStandard("F33", KeyTable.XK_F33);
+addStandard("F34", KeyTable.XK_F34);
+addStandard("F35", KeyTable.XK_F35);
+// - Soft1...
+
+// 2.10. Multimedia Keys
+
+// - ChannelDown
+// - ChannelUp
+addStandard("Close", KeyTable.XF86XK_Close);
+addStandard("MailForward", KeyTable.XF86XK_MailForward);
+addStandard("MailReply", KeyTable.XF86XK_Reply);
+addStandard("MainSend", KeyTable.XF86XK_Send);
+addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
+addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
+addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
+addStandard("MediaRecord", KeyTable.XF86XK_AudioRecord);
+addStandard("MediaRewind", KeyTable.XF86XK_AudioRewind);
+addStandard("MediaStop", KeyTable.XF86XK_AudioStop);
+addStandard("MediaTrackNext", KeyTable.XF86XK_AudioNext);
+addStandard("MediaTrackPrevious", KeyTable.XF86XK_AudioPrev);
+addStandard("New", KeyTable.XF86XK_New);
+addStandard("Open", KeyTable.XF86XK_Open);
+addStandard("Print", KeyTable.XK_Print);
+addStandard("Save", KeyTable.XF86XK_Save);
+addStandard("SpellCheck", KeyTable.XF86XK_Spell);
+
+// 2.11. Multimedia Numpad Keys
+
+// - Key11
+// - Key12
+
+// 2.12. Audio Keys
+
+// - AudioBalanceLeft
+// - AudioBalanceRight
+// - AudioBassDown
+// - AudioBassBoostDown
+// - AudioBassBoostToggle
+// - AudioBassBoostUp
+// - AudioBassUp
+// - AudioFaderFront
+// - AudioFaderRear
+// - AudioSurroundModeNext
+// - AudioTrebleDown
+// - AudioTrebleUp
+addStandard("AudioVolumeDown", KeyTable.XF86XK_AudioLowerVolume);
+addStandard("AudioVolumeUp", KeyTable.XF86XK_AudioRaiseVolume);
+addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute);
+// - MicrophoneToggle
+// - MicrophoneVolumeDown
+// - MicrophoneVolumeUp
+addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
+
+// 2.13. Speech Keys
+
+// - SpeechCorrectionList
+// - SpeechInputToggle
+
+// 2.14. Application Keys
+
+addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
+addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
+addStandard("LaunchMail", KeyTable.XF86XK_Mail);
+addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
+addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
+addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
+addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
+addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
+addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
+addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW);
+addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam);
+addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word);
+
+// 2.15. Browser Keys
+
+addStandard("BrowserBack", KeyTable.XF86XK_Back);
+addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites);
+addStandard("BrowserForward", KeyTable.XF86XK_Forward);
+addStandard("BrowserHome", KeyTable.XF86XK_HomePage);
+addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh);
+addStandard("BrowserSearch", KeyTable.XF86XK_Search);
+addStandard("BrowserStop", KeyTable.XF86XK_Stop);
+
+// 2.16. Mobile Phone Keys
+
+// - A whole bunch...
+
+// 2.17. TV Keys
+
+// - A whole bunch...
+
+// 2.18. Media Controller Keys
+
+// - A whole bunch...
+addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust);
+addStandard("MediaAudioTrack", KeyTable.XF86XK_AudioCycleTrack);
+addStandard("RandomToggle", KeyTable.XF86XK_AudioRandomPlay);
+addStandard("SplitScreenToggle", KeyTable.XF86XK_SplitScreen);
+addStandard("Subtitle", KeyTable.XF86XK_Subtitle);
+addStandard("VideoModeNext", KeyTable.XF86XK_Next_VMode);
+
+// Extra: Numpad
+
+addNumpad("=", KeyTable.XK_equal, KeyTable.XK_KP_Equal);
+addNumpad("+", KeyTable.XK_plus, KeyTable.XK_KP_Add);
+addNumpad("-", KeyTable.XK_minus, KeyTable.XK_KP_Subtract);
+addNumpad("*", KeyTable.XK_asterisk, KeyTable.XK_KP_Multiply);
+addNumpad("/", KeyTable.XK_slash, KeyTable.XK_KP_Divide);
+addNumpad(".", KeyTable.XK_period, KeyTable.XK_KP_Decimal);
+addNumpad(",", KeyTable.XK_comma, KeyTable.XK_KP_Separator);
+addNumpad("0", KeyTable.XK_0, KeyTable.XK_KP_0);
+addNumpad("1", KeyTable.XK_1, KeyTable.XK_KP_1);
+addNumpad("2", KeyTable.XK_2, KeyTable.XK_KP_2);
+addNumpad("3", KeyTable.XK_3, KeyTable.XK_KP_3);
+addNumpad("4", KeyTable.XK_4, KeyTable.XK_KP_4);
+addNumpad("5", KeyTable.XK_5, KeyTable.XK_KP_5);
+addNumpad("6", KeyTable.XK_6, KeyTable.XK_KP_6);
+addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7);
+addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8);
+addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9);
+
+export default DOMKeyTable;

+ 127 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/fixedkeys.js

@@ -0,0 +1,127 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+/*
+ * Fallback mapping between HTML key codes (physical keys) and
+ * HTML key values. This only works for keys that don't vary
+ * between layouts. We also omit those who manage fine by mapping the
+ * Unicode representation.
+ *
+ * See https://www.w3.org/TR/uievents-code/ for possible codes.
+ * See https://www.w3.org/TR/uievents-key/ for possible values.
+ */
+
+export default {
+
+// 3.1.1.1. Writing System Keys
+
+    'Backspace':        'Backspace',
+
+// 3.1.1.2. Functional Keys
+
+    'AltLeft':          'Alt',
+    'AltRight':         'Alt', // This could also be 'AltGraph'
+    'CapsLock':         'CapsLock',
+    'ContextMenu':      'ContextMenu',
+    'ControlLeft':      'Control',
+    'ControlRight':     'Control',
+    'Enter':            'Enter',
+    'MetaLeft':         'Meta',
+    'MetaRight':        'Meta',
+    'ShiftLeft':        'Shift',
+    'ShiftRight':       'Shift',
+    'Tab':              'Tab',
+    // FIXME: Japanese/Korean keys
+
+// 3.1.2. Control Pad Section
+
+    'Delete':           'Delete',
+    'End':              'End',
+    'Help':             'Help',
+    'Home':             'Home',
+    'Insert':           'Insert',
+    'PageDown':         'PageDown',
+    'PageUp':           'PageUp',
+
+// 3.1.3. Arrow Pad Section
+
+    'ArrowDown':        'ArrowDown',
+    'ArrowLeft':        'ArrowLeft',
+    'ArrowRight':       'ArrowRight',
+    'ArrowUp':          'ArrowUp',
+
+// 3.1.4. Numpad Section
+
+    'NumLock':          'NumLock',
+    'NumpadBackspace':  'Backspace',
+    'NumpadClear':      'Clear',
+
+// 3.1.5. Function Section
+
+    'Escape':           'Escape',
+    'F1':               'F1',
+    'F2':               'F2',
+    'F3':               'F3',
+    'F4':               'F4',
+    'F5':               'F5',
+    'F6':               'F6',
+    'F7':               'F7',
+    'F8':               'F8',
+    'F9':               'F9',
+    'F10':              'F10',
+    'F11':              'F11',
+    'F12':              'F12',
+    'F13':              'F13',
+    'F14':              'F14',
+    'F15':              'F15',
+    'F16':              'F16',
+    'F17':              'F17',
+    'F18':              'F18',
+    'F19':              'F19',
+    'F20':              'F20',
+    'F21':              'F21',
+    'F22':              'F22',
+    'F23':              'F23',
+    'F24':              'F24',
+    'F25':              'F25',
+    'F26':              'F26',
+    'F27':              'F27',
+    'F28':              'F28',
+    'F29':              'F29',
+    'F30':              'F30',
+    'F31':              'F31',
+    'F32':              'F32',
+    'F33':              'F33',
+    'F34':              'F34',
+    'F35':              'F35',
+    'PrintScreen':      'PrintScreen',
+    'ScrollLock':       'ScrollLock',
+    'Pause':            'Pause',
+
+// 3.1.6. Media Keys
+
+    'BrowserBack':      'BrowserBack',
+    'BrowserFavorites': 'BrowserFavorites',
+    'BrowserForward':   'BrowserForward',
+    'BrowserHome':      'BrowserHome',
+    'BrowserRefresh':   'BrowserRefresh',
+    'BrowserSearch':    'BrowserSearch',
+    'BrowserStop':      'BrowserStop',
+    'Eject':            'Eject',
+    'LaunchApp1':       'LaunchMyComputer',
+    'LaunchApp2':       'LaunchCalendar',
+    'LaunchMail':       'LaunchMail',
+    'MediaPlayPause':   'MediaPlay',
+    'MediaStop':        'MediaStop',
+    'MediaTrackNext':   'MediaTrackNext',
+    'MediaTrackPrevious': 'MediaTrackPrevious',
+    'Power':            'Power',
+    'Sleep':            'Sleep',
+    'AudioVolumeDown':  'AudioVolumeDown',
+    'AudioVolumeMute':  'AudioVolumeMute',
+    'AudioVolumeUp':    'AudioVolumeUp',
+    'WakeUp':           'WakeUp',
+};

+ 314 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keyboard.js

@@ -0,0 +1,314 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2013 Samuel Mannehed for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+import * as Log from '../util/logging.js';
+import { stopEvent } from '../util/events.js';
+import * as KeyboardUtil from "./util.js";
+import KeyTable from "./keysym.js";
+import * as browser from "../util/browser.js";
+
+//
+// Keyboard event handler
+//
+
+export default function Keyboard(target) {
+    this._target = target || null;
+
+    this._keyDownList = {};         // List of depressed keys
+                                    // (even if they are happy)
+    this._pendingKey = null;        // Key waiting for keypress
+
+    // keep these here so we can refer to them later
+    this._eventHandlers = {
+        'keyup': this._handleKeyUp.bind(this),
+        'keydown': this._handleKeyDown.bind(this),
+        'keypress': this._handleKeyPress.bind(this),
+        'blur': this._allKeysUp.bind(this)
+    };
+};
+
+Keyboard.prototype = {
+    // ===== EVENT HANDLERS =====
+
+    onkeyevent: function () {},     // Handler for key press/release
+
+    // ===== PRIVATE METHODS =====
+
+    _sendKeyEvent: function (keysym, code, down) {
+        Log.Debug("onkeyevent " + (down ? "down" : "up") +
+                  ", keysym: " + keysym, ", code: " + code);
+
+        // Windows sends CtrlLeft+AltRight when you press
+        // AltGraph, which tends to confuse the hell out of
+        // remote systems. Fake a release of these keys until
+        // there is a way to detect AltGraph properly.
+        var fakeAltGraph = false;
+        if (down && browser.isWindows()) {
+            if ((code !== 'ControlLeft') &&
+                (code !== 'AltRight') &&
+                ('ControlLeft' in this._keyDownList) &&
+                ('AltRight' in this._keyDownList)) {
+                fakeAltGraph = true;
+                this.onkeyevent(this._keyDownList['AltRight'],
+                                 'AltRight', false);
+                this.onkeyevent(this._keyDownList['ControlLeft'],
+                                 'ControlLeft', false);
+            }
+        }
+
+        this.onkeyevent(keysym, code, down);
+
+        if (fakeAltGraph) {
+            this.onkeyevent(this._keyDownList['ControlLeft'],
+                             'ControlLeft', true);
+            this.onkeyevent(this._keyDownList['AltRight'],
+                             'AltRight', true);
+        }
+    },
+
+    _getKeyCode: function (e) {
+        var code = KeyboardUtil.getKeycode(e);
+        if (code !== 'Unidentified') {
+            return code;
+        }
+
+        // Unstable, but we don't have anything else to go on
+        // (don't use it for 'keypress' events thought since
+        // WebKit sets it to the same as charCode)
+        if (e.keyCode && (e.type !== 'keypress')) {
+            // 229 is used for composition events
+            if (e.keyCode !== 229) {
+                return 'Platform' + e.keyCode;
+            }
+        }
+
+        // A precursor to the final DOM3 standard. Unfortunately it
+        // is not layout independent, so it is as bad as using keyCode
+        if (e.keyIdentifier) {
+            // Non-character key?
+            if (e.keyIdentifier.substr(0, 2) !== 'U+') {
+                return e.keyIdentifier;
+            }
+
+            var codepoint = parseInt(e.keyIdentifier.substr(2), 16);
+            var char = String.fromCharCode(codepoint);
+            // Some implementations fail to uppercase the symbols
+            char = char.toUpperCase();
+
+            return 'Platform' + char.charCodeAt();
+        }
+
+        return 'Unidentified';
+    },
+
+    _handleKeyDown: function (e) {
+        var code = this._getKeyCode(e);
+        var keysym = KeyboardUtil.getKeysym(e);
+
+        // We cannot handle keys we cannot track, but we also need
+        // to deal with virtual keyboards which omit key info
+        // (iOS omits tracking info on keyup events, which forces us to
+        // special treat that platform here)
+        if ((code === 'Unidentified') || browser.isIOS()) {
+            if (keysym) {
+                // If it's a virtual keyboard then it should be
+                // sufficient to just send press and release right
+                // after each other
+                this._sendKeyEvent(keysym, code, true);
+                this._sendKeyEvent(keysym, code, false);
+            }
+
+            stopEvent(e);
+            return;
+        }
+
+        // Alt behaves more like AltGraph on macOS, so shuffle the
+        // keys around a bit to make things more sane for the remote
+        // server. This method is used by RealVNC and TigerVNC (and
+        // possibly others).
+        if (browser.isMac()) {
+            switch (keysym) {
+            case KeyTable.XK_Super_L:
+                keysym = KeyTable.XK_Alt_L;
+                break;
+            case KeyTable.XK_Super_R:
+                keysym = KeyTable.XK_Super_L;
+                break;
+            case KeyTable.XK_Alt_L:
+                keysym = KeyTable.XK_Mode_switch;
+                break;
+            case KeyTable.XK_Alt_R:
+                keysym = KeyTable.XK_ISO_Level3_Shift;
+                break;
+            }
+        }
+
+        // Is this key already pressed? If so, then we must use the
+        // same keysym or we'll confuse the server
+        if (code in this._keyDownList) {
+            keysym = this._keyDownList[code];
+        }
+
+        // macOS doesn't send proper key events for modifiers, only
+        // state change events. That gets extra confusing for CapsLock
+        // which toggles on each press, but not on release. So pretend
+        // it was a quick press and release of the button.
+        if (browser.isMac() && (code === 'CapsLock')) {
+            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
+            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
+            stopEvent(e);
+            return;
+        }
+
+        // If this is a legacy browser then we'll need to wait for
+        // a keypress event as well
+        // (IE and Edge has a broken KeyboardEvent.key, so we can't
+        // just check for the presence of that field)
+        if (!keysym && (!e.key || browser.isIE() || browser.isEdge())) {
+            this._pendingKey = code;
+            // However we might not get a keypress event if the key
+            // is non-printable, which needs some special fallback
+            // handling
+            setTimeout(this._handleKeyPressTimeout.bind(this), 10, e);
+            return;
+        }
+
+        this._pendingKey = null;
+        stopEvent(e);
+
+        this._keyDownList[code] = keysym;
+
+        this._sendKeyEvent(keysym, code, true);
+    },
+
+    // Legacy event for browsers without code/key
+    _handleKeyPress: function (e) {
+        stopEvent(e);
+
+        // Are we expecting a keypress?
+        if (this._pendingKey === null) {
+            return;
+        }
+
+        var code = this._getKeyCode(e);
+        var keysym = KeyboardUtil.getKeysym(e);
+
+        // The key we were waiting for?
+        if ((code !== 'Unidentified') && (code != this._pendingKey)) {
+            return;
+        }
+
+        code = this._pendingKey;
+        this._pendingKey = null;
+
+        if (!keysym) {
+            Log.Info('keypress with no keysym:', e);
+            return;
+        }
+
+        this._keyDownList[code] = keysym;
+
+        this._sendKeyEvent(keysym, code, true);
+    },
+    _handleKeyPressTimeout: function (e) {
+        // Did someone manage to sort out the key already?
+        if (this._pendingKey === null) {
+            return;
+        }
+
+        var code, keysym;
+
+        code = this._pendingKey;
+        this._pendingKey = null;
+
+        // We have no way of knowing the proper keysym with the
+        // information given, but the following are true for most
+        // layouts
+        if ((e.keyCode >= 0x30) && (e.keyCode <= 0x39)) {
+            // Digit
+            keysym = e.keyCode;
+        } else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
+            // Character (A-Z)
+            var char = String.fromCharCode(e.keyCode);
+            // A feeble attempt at the correct case
+            if (e.shiftKey)
+                char = char.toUpperCase();
+            else
+                char = char.toLowerCase();
+            keysym = char.charCodeAt();
+        } else {
+            // Unknown, give up
+            keysym = 0;
+        }
+
+        this._keyDownList[code] = keysym;
+
+        this._sendKeyEvent(keysym, code, true);
+    },
+
+    _handleKeyUp: function (e) {
+        stopEvent(e);
+
+        var code = this._getKeyCode(e);
+
+        // See comment in _handleKeyDown()
+        if (browser.isMac() && (code === 'CapsLock')) {
+            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
+            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
+            return;
+        }
+
+        // Do we really think this key is down?
+        if (!(code in this._keyDownList)) {
+            return;
+        }
+
+        this._sendKeyEvent(this._keyDownList[code], code, false);
+
+        delete this._keyDownList[code];
+    },
+
+    _allKeysUp: function () {
+        Log.Debug(">> Keyboard.allKeysUp");
+        for (var code in this._keyDownList) {
+            this._sendKeyEvent(this._keyDownList[code], code, false);
+        };
+        this._keyDownList = {};
+        Log.Debug("<< Keyboard.allKeysUp");
+    },
+
+    // ===== PUBLIC METHODS =====
+
+    grab: function () {
+        //Log.Debug(">> Keyboard.grab");
+        var c = this._target;
+
+        c.addEventListener('keydown', this._eventHandlers.keydown);
+        c.addEventListener('keyup', this._eventHandlers.keyup);
+        c.addEventListener('keypress', this._eventHandlers.keypress);
+
+        // Release (key up) if window loses focus
+        window.addEventListener('blur', this._eventHandlers.blur);
+
+        //Log.Debug("<< Keyboard.grab");
+    },
+
+    ungrab: function () {
+        //Log.Debug(">> Keyboard.ungrab");
+        var c = this._target;
+
+        c.removeEventListener('keydown', this._eventHandlers.keydown);
+        c.removeEventListener('keyup', this._eventHandlers.keyup);
+        c.removeEventListener('keypress', this._eventHandlers.keypress);
+        window.removeEventListener('blur', this._eventHandlers.blur);
+
+        // Release (key up) all keys that are in a down state
+        this._allKeysUp();
+
+        //Log.Debug(">> Keyboard.ungrab");
+    },
+};

+ 614 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keysym.js

@@ -0,0 +1,614 @@
+export default {
+    XK_VoidSymbol:                  0xffffff, /* Void symbol */
+
+    XK_BackSpace:                   0xff08, /* Back space, back char */
+    XK_Tab:                         0xff09,
+    XK_Linefeed:                    0xff0a, /* Linefeed, LF */
+    XK_Clear:                       0xff0b,
+    XK_Return:                      0xff0d, /* Return, enter */
+    XK_Pause:                       0xff13, /* Pause, hold */
+    XK_Scroll_Lock:                 0xff14,
+    XK_Sys_Req:                     0xff15,
+    XK_Escape:                      0xff1b,
+    XK_Delete:                      0xffff, /* Delete, rubout */
+
+    /* International & multi-key character composition */
+
+    XK_Multi_key:                   0xff20, /* Multi-key character compose */
+    XK_Codeinput:                   0xff37,
+    XK_SingleCandidate:             0xff3c,
+    XK_MultipleCandidate:           0xff3d,
+    XK_PreviousCandidate:           0xff3e,
+
+    /* Japanese keyboard support */
+
+    XK_Kanji:                       0xff21, /* Kanji, Kanji convert */
+    XK_Muhenkan:                    0xff22, /* Cancel Conversion */
+    XK_Henkan_Mode:                 0xff23, /* Start/Stop Conversion */
+    XK_Henkan:                      0xff23, /* Alias for Henkan_Mode */
+    XK_Romaji:                      0xff24, /* to Romaji */
+    XK_Hiragana:                    0xff25, /* to Hiragana */
+    XK_Katakana:                    0xff26, /* to Katakana */
+    XK_Hiragana_Katakana:           0xff27, /* Hiragana/Katakana toggle */
+    XK_Zenkaku:                     0xff28, /* to Zenkaku */
+    XK_Hankaku:                     0xff29, /* to Hankaku */
+    XK_Zenkaku_Hankaku:             0xff2a, /* Zenkaku/Hankaku toggle */
+    XK_Touroku:                     0xff2b, /* Add to Dictionary */
+    XK_Massyo:                      0xff2c, /* Delete from Dictionary */
+    XK_Kana_Lock:                   0xff2d, /* Kana Lock */
+    XK_Kana_Shift:                  0xff2e, /* Kana Shift */
+    XK_Eisu_Shift:                  0xff2f, /* Alphanumeric Shift */
+    XK_Eisu_toggle:                 0xff30, /* Alphanumeric toggle */
+    XK_Kanji_Bangou:                0xff37, /* Codeinput */
+    XK_Zen_Koho:                    0xff3d, /* Multiple/All Candidate(s) */
+    XK_Mae_Koho:                    0xff3e, /* Previous Candidate */
+
+    /* Cursor control & motion */
+
+    XK_Home:                        0xff50,
+    XK_Left:                        0xff51, /* Move left, left arrow */
+    XK_Up:                          0xff52, /* Move up, up arrow */
+    XK_Right:                       0xff53, /* Move right, right arrow */
+    XK_Down:                        0xff54, /* Move down, down arrow */
+    XK_Prior:                       0xff55, /* Prior, previous */
+    XK_Page_Up:                     0xff55,
+    XK_Next:                        0xff56, /* Next */
+    XK_Page_Down:                   0xff56,
+    XK_End:                         0xff57, /* EOL */
+    XK_Begin:                       0xff58, /* BOL */
+
+
+    /* Misc functions */
+
+    XK_Select:                      0xff60, /* Select, mark */
+    XK_Print:                       0xff61,
+    XK_Execute:                     0xff62, /* Execute, run, do */
+    XK_Insert:                      0xff63, /* Insert, insert here */
+    XK_Undo:                        0xff65,
+    XK_Redo:                        0xff66, /* Redo, again */
+    XK_Menu:                        0xff67,
+    XK_Find:                        0xff68, /* Find, search */
+    XK_Cancel:                      0xff69, /* Cancel, stop, abort, exit */
+    XK_Help:                        0xff6a, /* Help */
+    XK_Break:                       0xff6b,
+    XK_Mode_switch:                 0xff7e, /* Character set switch */
+    XK_script_switch:               0xff7e, /* Alias for mode_switch */
+    XK_Num_Lock:                    0xff7f,
+
+    /* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
+
+    XK_KP_Space:                    0xff80, /* Space */
+    XK_KP_Tab:                      0xff89,
+    XK_KP_Enter:                    0xff8d, /* Enter */
+    XK_KP_F1:                       0xff91, /* PF1, KP_A, ... */
+    XK_KP_F2:                       0xff92,
+    XK_KP_F3:                       0xff93,
+    XK_KP_F4:                       0xff94,
+    XK_KP_Home:                     0xff95,
+    XK_KP_Left:                     0xff96,
+    XK_KP_Up:                       0xff97,
+    XK_KP_Right:                    0xff98,
+    XK_KP_Down:                     0xff99,
+    XK_KP_Prior:                    0xff9a,
+    XK_KP_Page_Up:                  0xff9a,
+    XK_KP_Next:                     0xff9b,
+    XK_KP_Page_Down:                0xff9b,
+    XK_KP_End:                      0xff9c,
+    XK_KP_Begin:                    0xff9d,
+    XK_KP_Insert:                   0xff9e,
+    XK_KP_Delete:                   0xff9f,
+    XK_KP_Equal:                    0xffbd, /* Equals */
+    XK_KP_Multiply:                 0xffaa,
+    XK_KP_Add:                      0xffab,
+    XK_KP_Separator:                0xffac, /* Separator, often comma */
+    XK_KP_Subtract:                 0xffad,
+    XK_KP_Decimal:                  0xffae,
+    XK_KP_Divide:                   0xffaf,
+
+    XK_KP_0:                        0xffb0,
+    XK_KP_1:                        0xffb1,
+    XK_KP_2:                        0xffb2,
+    XK_KP_3:                        0xffb3,
+    XK_KP_4:                        0xffb4,
+    XK_KP_5:                        0xffb5,
+    XK_KP_6:                        0xffb6,
+    XK_KP_7:                        0xffb7,
+    XK_KP_8:                        0xffb8,
+    XK_KP_9:                        0xffb9,
+
+    /*
+     * Auxiliary functions; note the duplicate definitions for left and right
+     * function keys;  Sun keyboards and a few other manufacturers have such
+     * function key groups on the left and/or right sides of the keyboard.
+     * We've not found a keyboard with more than 35 function keys total.
+     */
+
+    XK_F1:                          0xffbe,
+    XK_F2:                          0xffbf,
+    XK_F3:                          0xffc0,
+    XK_F4:                          0xffc1,
+    XK_F5:                          0xffc2,
+    XK_F6:                          0xffc3,
+    XK_F7:                          0xffc4,
+    XK_F8:                          0xffc5,
+    XK_F9:                          0xffc6,
+    XK_F10:                         0xffc7,
+    XK_F11:                         0xffc8,
+    XK_L1:                          0xffc8,
+    XK_F12:                         0xffc9,
+    XK_L2:                          0xffc9,
+    XK_F13:                         0xffca,
+    XK_L3:                          0xffca,
+    XK_F14:                         0xffcb,
+    XK_L4:                          0xffcb,
+    XK_F15:                         0xffcc,
+    XK_L5:                          0xffcc,
+    XK_F16:                         0xffcd,
+    XK_L6:                          0xffcd,
+    XK_F17:                         0xffce,
+    XK_L7:                          0xffce,
+    XK_F18:                         0xffcf,
+    XK_L8:                          0xffcf,
+    XK_F19:                         0xffd0,
+    XK_L9:                          0xffd0,
+    XK_F20:                         0xffd1,
+    XK_L10:                         0xffd1,
+    XK_F21:                         0xffd2,
+    XK_R1:                          0xffd2,
+    XK_F22:                         0xffd3,
+    XK_R2:                          0xffd3,
+    XK_F23:                         0xffd4,
+    XK_R3:                          0xffd4,
+    XK_F24:                         0xffd5,
+    XK_R4:                          0xffd5,
+    XK_F25:                         0xffd6,
+    XK_R5:                          0xffd6,
+    XK_F26:                         0xffd7,
+    XK_R6:                          0xffd7,
+    XK_F27:                         0xffd8,
+    XK_R7:                          0xffd8,
+    XK_F28:                         0xffd9,
+    XK_R8:                          0xffd9,
+    XK_F29:                         0xffda,
+    XK_R9:                          0xffda,
+    XK_F30:                         0xffdb,
+    XK_R10:                         0xffdb,
+    XK_F31:                         0xffdc,
+    XK_R11:                         0xffdc,
+    XK_F32:                         0xffdd,
+    XK_R12:                         0xffdd,
+    XK_F33:                         0xffde,
+    XK_R13:                         0xffde,
+    XK_F34:                         0xffdf,
+    XK_R14:                         0xffdf,
+    XK_F35:                         0xffe0,
+    XK_R15:                         0xffe0,
+
+    /* Modifiers */
+
+    XK_Shift_L:                     0xffe1, /* Left shift */
+    XK_Shift_R:                     0xffe2, /* Right shift */
+    XK_Control_L:                   0xffe3, /* Left control */
+    XK_Control_R:                   0xffe4, /* Right control */
+    XK_Caps_Lock:                   0xffe5, /* Caps lock */
+    XK_Shift_Lock:                  0xffe6, /* Shift lock */
+
+    XK_Meta_L:                      0xffe7, /* Left meta */
+    XK_Meta_R:                      0xffe8, /* Right meta */
+    XK_Alt_L:                       0xffe9, /* Left alt */
+    XK_Alt_R:                       0xffea, /* Right alt */
+    XK_Super_L:                     0xffeb, /* Left super */
+    XK_Super_R:                     0xffec, /* Right super */
+    XK_Hyper_L:                     0xffed, /* Left hyper */
+    XK_Hyper_R:                     0xffee, /* Right hyper */
+
+    /*
+     * Keyboard (XKB) Extension function and modifier keys
+     * (from Appendix C of "The X Keyboard Extension: Protocol Specification")
+     * Byte 3 = 0xfe
+     */
+
+    XK_ISO_Level3_Shift:            0xfe03, /* AltGr */
+    XK_ISO_Next_Group:              0xfe08,
+    XK_ISO_Prev_Group:              0xfe0a,
+    XK_ISO_First_Group:             0xfe0c,
+    XK_ISO_Last_Group:              0xfe0e,
+
+    /*
+     * Latin 1
+     * (ISO/IEC 8859-1: Unicode U+0020..U+00FF)
+     * Byte 3: 0
+     */
+
+    XK_space:                       0x0020, /* U+0020 SPACE */
+    XK_exclam:                      0x0021, /* U+0021 EXCLAMATION MARK */
+    XK_quotedbl:                    0x0022, /* U+0022 QUOTATION MARK */
+    XK_numbersign:                  0x0023, /* U+0023 NUMBER SIGN */
+    XK_dollar:                      0x0024, /* U+0024 DOLLAR SIGN */
+    XK_percent:                     0x0025, /* U+0025 PERCENT SIGN */
+    XK_ampersand:                   0x0026, /* U+0026 AMPERSAND */
+    XK_apostrophe:                  0x0027, /* U+0027 APOSTROPHE */
+    XK_quoteright:                  0x0027, /* deprecated */
+    XK_parenleft:                   0x0028, /* U+0028 LEFT PARENTHESIS */
+    XK_parenright:                  0x0029, /* U+0029 RIGHT PARENTHESIS */
+    XK_asterisk:                    0x002a, /* U+002A ASTERISK */
+    XK_plus:                        0x002b, /* U+002B PLUS SIGN */
+    XK_comma:                       0x002c, /* U+002C COMMA */
+    XK_minus:                       0x002d, /* U+002D HYPHEN-MINUS */
+    XK_period:                      0x002e, /* U+002E FULL STOP */
+    XK_slash:                       0x002f, /* U+002F SOLIDUS */
+    XK_0:                           0x0030, /* U+0030 DIGIT ZERO */
+    XK_1:                           0x0031, /* U+0031 DIGIT ONE */
+    XK_2:                           0x0032, /* U+0032 DIGIT TWO */
+    XK_3:                           0x0033, /* U+0033 DIGIT THREE */
+    XK_4:                           0x0034, /* U+0034 DIGIT FOUR */
+    XK_5:                           0x0035, /* U+0035 DIGIT FIVE */
+    XK_6:                           0x0036, /* U+0036 DIGIT SIX */
+    XK_7:                           0x0037, /* U+0037 DIGIT SEVEN */
+    XK_8:                           0x0038, /* U+0038 DIGIT EIGHT */
+    XK_9:                           0x0039, /* U+0039 DIGIT NINE */
+    XK_colon:                       0x003a, /* U+003A COLON */
+    XK_semicolon:                   0x003b, /* U+003B SEMICOLON */
+    XK_less:                        0x003c, /* U+003C LESS-THAN SIGN */
+    XK_equal:                       0x003d, /* U+003D EQUALS SIGN */
+    XK_greater:                     0x003e, /* U+003E GREATER-THAN SIGN */
+    XK_question:                    0x003f, /* U+003F QUESTION MARK */
+    XK_at:                          0x0040, /* U+0040 COMMERCIAL AT */
+    XK_A:                           0x0041, /* U+0041 LATIN CAPITAL LETTER A */
+    XK_B:                           0x0042, /* U+0042 LATIN CAPITAL LETTER B */
+    XK_C:                           0x0043, /* U+0043 LATIN CAPITAL LETTER C */
+    XK_D:                           0x0044, /* U+0044 LATIN CAPITAL LETTER D */
+    XK_E:                           0x0045, /* U+0045 LATIN CAPITAL LETTER E */
+    XK_F:                           0x0046, /* U+0046 LATIN CAPITAL LETTER F */
+    XK_G:                           0x0047, /* U+0047 LATIN CAPITAL LETTER G */
+    XK_H:                           0x0048, /* U+0048 LATIN CAPITAL LETTER H */
+    XK_I:                           0x0049, /* U+0049 LATIN CAPITAL LETTER I */
+    XK_J:                           0x004a, /* U+004A LATIN CAPITAL LETTER J */
+    XK_K:                           0x004b, /* U+004B LATIN CAPITAL LETTER K */
+    XK_L:                           0x004c, /* U+004C LATIN CAPITAL LETTER L */
+    XK_M:                           0x004d, /* U+004D LATIN CAPITAL LETTER M */
+    XK_N:                           0x004e, /* U+004E LATIN CAPITAL LETTER N */
+    XK_O:                           0x004f, /* U+004F LATIN CAPITAL LETTER O */
+    XK_P:                           0x0050, /* U+0050 LATIN CAPITAL LETTER P */
+    XK_Q:                           0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
+    XK_R:                           0x0052, /* U+0052 LATIN CAPITAL LETTER R */
+    XK_S:                           0x0053, /* U+0053 LATIN CAPITAL LETTER S */
+    XK_T:                           0x0054, /* U+0054 LATIN CAPITAL LETTER T */
+    XK_U:                           0x0055, /* U+0055 LATIN CAPITAL LETTER U */
+    XK_V:                           0x0056, /* U+0056 LATIN CAPITAL LETTER V */
+    XK_W:                           0x0057, /* U+0057 LATIN CAPITAL LETTER W */
+    XK_X:                           0x0058, /* U+0058 LATIN CAPITAL LETTER X */
+    XK_Y:                           0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
+    XK_Z:                           0x005a, /* U+005A LATIN CAPITAL LETTER Z */
+    XK_bracketleft:                 0x005b, /* U+005B LEFT SQUARE BRACKET */
+    XK_backslash:                   0x005c, /* U+005C REVERSE SOLIDUS */
+    XK_bracketright:                0x005d, /* U+005D RIGHT SQUARE BRACKET */
+    XK_asciicircum:                 0x005e, /* U+005E CIRCUMFLEX ACCENT */
+    XK_underscore:                  0x005f, /* U+005F LOW LINE */
+    XK_grave:                       0x0060, /* U+0060 GRAVE ACCENT */
+    XK_quoteleft:                   0x0060, /* deprecated */
+    XK_a:                           0x0061, /* U+0061 LATIN SMALL LETTER A */
+    XK_b:                           0x0062, /* U+0062 LATIN SMALL LETTER B */
+    XK_c:                           0x0063, /* U+0063 LATIN SMALL LETTER C */
+    XK_d:                           0x0064, /* U+0064 LATIN SMALL LETTER D */
+    XK_e:                           0x0065, /* U+0065 LATIN SMALL LETTER E */
+    XK_f:                           0x0066, /* U+0066 LATIN SMALL LETTER F */
+    XK_g:                           0x0067, /* U+0067 LATIN SMALL LETTER G */
+    XK_h:                           0x0068, /* U+0068 LATIN SMALL LETTER H */
+    XK_i:                           0x0069, /* U+0069 LATIN SMALL LETTER I */
+    XK_j:                           0x006a, /* U+006A LATIN SMALL LETTER J */
+    XK_k:                           0x006b, /* U+006B LATIN SMALL LETTER K */
+    XK_l:                           0x006c, /* U+006C LATIN SMALL LETTER L */
+    XK_m:                           0x006d, /* U+006D LATIN SMALL LETTER M */
+    XK_n:                           0x006e, /* U+006E LATIN SMALL LETTER N */
+    XK_o:                           0x006f, /* U+006F LATIN SMALL LETTER O */
+    XK_p:                           0x0070, /* U+0070 LATIN SMALL LETTER P */
+    XK_q:                           0x0071, /* U+0071 LATIN SMALL LETTER Q */
+    XK_r:                           0x0072, /* U+0072 LATIN SMALL LETTER R */
+    XK_s:                           0x0073, /* U+0073 LATIN SMALL LETTER S */
+    XK_t:                           0x0074, /* U+0074 LATIN SMALL LETTER T */
+    XK_u:                           0x0075, /* U+0075 LATIN SMALL LETTER U */
+    XK_v:                           0x0076, /* U+0076 LATIN SMALL LETTER V */
+    XK_w:                           0x0077, /* U+0077 LATIN SMALL LETTER W */
+    XK_x:                           0x0078, /* U+0078 LATIN SMALL LETTER X */
+    XK_y:                           0x0079, /* U+0079 LATIN SMALL LETTER Y */
+    XK_z:                           0x007a, /* U+007A LATIN SMALL LETTER Z */
+    XK_braceleft:                   0x007b, /* U+007B LEFT CURLY BRACKET */
+    XK_bar:                         0x007c, /* U+007C VERTICAL LINE */
+    XK_braceright:                  0x007d, /* U+007D RIGHT CURLY BRACKET */
+    XK_asciitilde:                  0x007e, /* U+007E TILDE */
+
+    XK_nobreakspace:                0x00a0, /* U+00A0 NO-BREAK SPACE */
+    XK_exclamdown:                  0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
+    XK_cent:                        0x00a2, /* U+00A2 CENT SIGN */
+    XK_sterling:                    0x00a3, /* U+00A3 POUND SIGN */
+    XK_currency:                    0x00a4, /* U+00A4 CURRENCY SIGN */
+    XK_yen:                         0x00a5, /* U+00A5 YEN SIGN */
+    XK_brokenbar:                   0x00a6, /* U+00A6 BROKEN BAR */
+    XK_section:                     0x00a7, /* U+00A7 SECTION SIGN */
+    XK_diaeresis:                   0x00a8, /* U+00A8 DIAERESIS */
+    XK_copyright:                   0x00a9, /* U+00A9 COPYRIGHT SIGN */
+    XK_ordfeminine:                 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
+    XK_guillemotleft:               0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
+    XK_notsign:                     0x00ac, /* U+00AC NOT SIGN */
+    XK_hyphen:                      0x00ad, /* U+00AD SOFT HYPHEN */
+    XK_registered:                  0x00ae, /* U+00AE REGISTERED SIGN */
+    XK_macron:                      0x00af, /* U+00AF MACRON */
+    XK_degree:                      0x00b0, /* U+00B0 DEGREE SIGN */
+    XK_plusminus:                   0x00b1, /* U+00B1 PLUS-MINUS SIGN */
+    XK_twosuperior:                 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
+    XK_threesuperior:               0x00b3, /* U+00B3 SUPERSCRIPT THREE */
+    XK_acute:                       0x00b4, /* U+00B4 ACUTE ACCENT */
+    XK_mu:                          0x00b5, /* U+00B5 MICRO SIGN */
+    XK_paragraph:                   0x00b6, /* U+00B6 PILCROW SIGN */
+    XK_periodcentered:              0x00b7, /* U+00B7 MIDDLE DOT */
+    XK_cedilla:                     0x00b8, /* U+00B8 CEDILLA */
+    XK_onesuperior:                 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
+    XK_masculine:                   0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
+    XK_guillemotright:              0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
+    XK_onequarter:                  0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
+    XK_onehalf:                     0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
+    XK_threequarters:               0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
+    XK_questiondown:                0x00bf, /* U+00BF INVERTED QUESTION MARK */
+    XK_Agrave:                      0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
+    XK_Aacute:                      0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
+    XK_Acircumflex:                 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+    XK_Atilde:                      0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
+    XK_Adiaeresis:                  0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
+    XK_Aring:                       0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
+    XK_AE:                          0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
+    XK_Ccedilla:                    0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
+    XK_Egrave:                      0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
+    XK_Eacute:                      0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
+    XK_Ecircumflex:                 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
+    XK_Ediaeresis:                  0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
+    XK_Igrave:                      0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
+    XK_Iacute:                      0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
+    XK_Icircumflex:                 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+    XK_Idiaeresis:                  0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
+    XK_ETH:                         0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
+    XK_Eth:                         0x00d0, /* deprecated */
+    XK_Ntilde:                      0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
+    XK_Ograve:                      0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
+    XK_Oacute:                      0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
+    XK_Ocircumflex:                 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+    XK_Otilde:                      0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
+    XK_Odiaeresis:                  0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
+    XK_multiply:                    0x00d7, /* U+00D7 MULTIPLICATION SIGN */
+    XK_Oslash:                      0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
+    XK_Ooblique:                    0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
+    XK_Ugrave:                      0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */
+    XK_Uacute:                      0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
+    XK_Ucircumflex:                 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
+    XK_Udiaeresis:                  0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
+    XK_Yacute:                      0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
+    XK_THORN:                       0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
+    XK_Thorn:                       0x00de, /* deprecated */
+    XK_ssharp:                      0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
+    XK_agrave:                      0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
+    XK_aacute:                      0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
+    XK_acircumflex:                 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
+    XK_atilde:                      0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
+    XK_adiaeresis:                  0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
+    XK_aring:                       0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
+    XK_ae:                          0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
+    XK_ccedilla:                    0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
+    XK_egrave:                      0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
+    XK_eacute:                      0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
+    XK_ecircumflex:                 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
+    XK_ediaeresis:                  0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
+    XK_igrave:                      0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
+    XK_iacute:                      0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
+    XK_icircumflex:                 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
+    XK_idiaeresis:                  0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
+    XK_eth:                         0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
+    XK_ntilde:                      0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
+    XK_ograve:                      0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
+    XK_oacute:                      0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
+    XK_ocircumflex:                 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
+    XK_otilde:                      0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
+    XK_odiaeresis:                  0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
+    XK_division:                    0x00f7, /* U+00F7 DIVISION SIGN */
+    XK_oslash:                      0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
+    XK_ooblique:                    0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
+    XK_ugrave:                      0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */
+    XK_uacute:                      0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
+    XK_ucircumflex:                 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
+    XK_udiaeresis:                  0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
+    XK_yacute:                      0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
+    XK_thorn:                       0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
+    XK_ydiaeresis:                  0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
+
+    /*
+     * Korean
+     * Byte 3 = 0x0e
+     */
+
+    XK_Hangul:                      0xff31, /* Hangul start/stop(toggle) */
+    XK_Hangul_Hanja:                0xff34, /* Start Hangul->Hanja Conversion */
+    XK_Hangul_Jeonja:               0xff38, /* Jeonja mode */
+
+    /*
+     * XFree86 vendor specific keysyms.
+     *
+     * The XFree86 keysym range is 0x10080001 - 0x1008FFFF.
+     */
+
+    XF86XK_ModeLock:                0x1008FF01,
+    XF86XK_MonBrightnessUp:         0x1008FF02,
+    XF86XK_MonBrightnessDown:       0x1008FF03,
+    XF86XK_KbdLightOnOff:           0x1008FF04,
+    XF86XK_KbdBrightnessUp:         0x1008FF05,
+    XF86XK_KbdBrightnessDown:       0x1008FF06,
+    XF86XK_Standby:                 0x1008FF10,
+    XF86XK_AudioLowerVolume:        0x1008FF11,
+    XF86XK_AudioMute:               0x1008FF12,
+    XF86XK_AudioRaiseVolume:        0x1008FF13,
+    XF86XK_AudioPlay:               0x1008FF14,
+    XF86XK_AudioStop:               0x1008FF15,
+    XF86XK_AudioPrev:               0x1008FF16,
+    XF86XK_AudioNext:               0x1008FF17,
+    XF86XK_HomePage:                0x1008FF18,
+    XF86XK_Mail:                    0x1008FF19,
+    XF86XK_Start:                   0x1008FF1A,
+    XF86XK_Search:                  0x1008FF1B,
+    XF86XK_AudioRecord:             0x1008FF1C,
+    XF86XK_Calculator:              0x1008FF1D,
+    XF86XK_Memo:                    0x1008FF1E,
+    XF86XK_ToDoList:                0x1008FF1F,
+    XF86XK_Calendar:                0x1008FF20,
+    XF86XK_PowerDown:               0x1008FF21,
+    XF86XK_ContrastAdjust:          0x1008FF22,
+    XF86XK_RockerUp:                0x1008FF23,
+    XF86XK_RockerDown:              0x1008FF24,
+    XF86XK_RockerEnter:             0x1008FF25,
+    XF86XK_Back:                    0x1008FF26,
+    XF86XK_Forward:                 0x1008FF27,
+    XF86XK_Stop:                    0x1008FF28,
+    XF86XK_Refresh:                 0x1008FF29,
+    XF86XK_PowerOff:                0x1008FF2A,
+    XF86XK_WakeUp:                  0x1008FF2B,
+    XF86XK_Eject:                   0x1008FF2C,
+    XF86XK_ScreenSaver:             0x1008FF2D,
+    XF86XK_WWW:                     0x1008FF2E,
+    XF86XK_Sleep:                   0x1008FF2F,
+    XF86XK_Favorites:               0x1008FF30,
+    XF86XK_AudioPause:              0x1008FF31,
+    XF86XK_AudioMedia:              0x1008FF32,
+    XF86XK_MyComputer:              0x1008FF33,
+    XF86XK_VendorHome:              0x1008FF34,
+    XF86XK_LightBulb:               0x1008FF35,
+    XF86XK_Shop:                    0x1008FF36,
+    XF86XK_History:                 0x1008FF37,
+    XF86XK_OpenURL:                 0x1008FF38,
+    XF86XK_AddFavorite:             0x1008FF39,
+    XF86XK_HotLinks:                0x1008FF3A,
+    XF86XK_BrightnessAdjust:        0x1008FF3B,
+    XF86XK_Finance:                 0x1008FF3C,
+    XF86XK_Community:               0x1008FF3D,
+    XF86XK_AudioRewind:             0x1008FF3E,
+    XF86XK_BackForward:             0x1008FF3F,
+    XF86XK_Launch0:                 0x1008FF40,
+    XF86XK_Launch1:                 0x1008FF41,
+    XF86XK_Launch2:                 0x1008FF42,
+    XF86XK_Launch3:                 0x1008FF43,
+    XF86XK_Launch4:                 0x1008FF44,
+    XF86XK_Launch5:                 0x1008FF45,
+    XF86XK_Launch6:                 0x1008FF46,
+    XF86XK_Launch7:                 0x1008FF47,
+    XF86XK_Launch8:                 0x1008FF48,
+    XF86XK_Launch9:                 0x1008FF49,
+    XF86XK_LaunchA:                 0x1008FF4A,
+    XF86XK_LaunchB:                 0x1008FF4B,
+    XF86XK_LaunchC:                 0x1008FF4C,
+    XF86XK_LaunchD:                 0x1008FF4D,
+    XF86XK_LaunchE:                 0x1008FF4E,
+    XF86XK_LaunchF:                 0x1008FF4F,
+    XF86XK_ApplicationLeft:         0x1008FF50,
+    XF86XK_ApplicationRight:        0x1008FF51,
+    XF86XK_Book:                    0x1008FF52,
+    XF86XK_CD:                      0x1008FF53,
+    XF86XK_Calculater:              0x1008FF54,
+    XF86XK_Clear:                   0x1008FF55,
+    XF86XK_Close:                   0x1008FF56,
+    XF86XK_Copy:                    0x1008FF57,
+    XF86XK_Cut:                     0x1008FF58,
+    XF86XK_Display:                 0x1008FF59,
+    XF86XK_DOS:                     0x1008FF5A,
+    XF86XK_Documents:               0x1008FF5B,
+    XF86XK_Excel:                   0x1008FF5C,
+    XF86XK_Explorer:                0x1008FF5D,
+    XF86XK_Game:                    0x1008FF5E,
+    XF86XK_Go:                      0x1008FF5F,
+    XF86XK_iTouch:                  0x1008FF60,
+    XF86XK_LogOff:                  0x1008FF61,
+    XF86XK_Market:                  0x1008FF62,
+    XF86XK_Meeting:                 0x1008FF63,
+    XF86XK_MenuKB:                  0x1008FF65,
+    XF86XK_MenuPB:                  0x1008FF66,
+    XF86XK_MySites:                 0x1008FF67,
+    XF86XK_New:                     0x1008FF68,
+    XF86XK_News:                    0x1008FF69,
+    XF86XK_OfficeHome:              0x1008FF6A,
+    XF86XK_Open:                    0x1008FF6B,
+    XF86XK_Option:                  0x1008FF6C,
+    XF86XK_Paste:                   0x1008FF6D,
+    XF86XK_Phone:                   0x1008FF6E,
+    XF86XK_Q:                       0x1008FF70,
+    XF86XK_Reply:                   0x1008FF72,
+    XF86XK_Reload:                  0x1008FF73,
+    XF86XK_RotateWindows:           0x1008FF74,
+    XF86XK_RotationPB:              0x1008FF75,
+    XF86XK_RotationKB:              0x1008FF76,
+    XF86XK_Save:                    0x1008FF77,
+    XF86XK_ScrollUp:                0x1008FF78,
+    XF86XK_ScrollDown:              0x1008FF79,
+    XF86XK_ScrollClick:             0x1008FF7A,
+    XF86XK_Send:                    0x1008FF7B,
+    XF86XK_Spell:                   0x1008FF7C,
+    XF86XK_SplitScreen:             0x1008FF7D,
+    XF86XK_Support:                 0x1008FF7E,
+    XF86XK_TaskPane:                0x1008FF7F,
+    XF86XK_Terminal:                0x1008FF80,
+    XF86XK_Tools:                   0x1008FF81,
+    XF86XK_Travel:                  0x1008FF82,
+    XF86XK_UserPB:                  0x1008FF84,
+    XF86XK_User1KB:                 0x1008FF85,
+    XF86XK_User2KB:                 0x1008FF86,
+    XF86XK_Video:                   0x1008FF87,
+    XF86XK_WheelButton:             0x1008FF88,
+    XF86XK_Word:                    0x1008FF89,
+    XF86XK_Xfer:                    0x1008FF8A,
+    XF86XK_ZoomIn:                  0x1008FF8B,
+    XF86XK_ZoomOut:                 0x1008FF8C,
+    XF86XK_Away:                    0x1008FF8D,
+    XF86XK_Messenger:               0x1008FF8E,
+    XF86XK_WebCam:                  0x1008FF8F,
+    XF86XK_MailForward:             0x1008FF90,
+    XF86XK_Pictures:                0x1008FF91,
+    XF86XK_Music:                   0x1008FF92,
+    XF86XK_Battery:                 0x1008FF93,
+    XF86XK_Bluetooth:               0x1008FF94,
+    XF86XK_WLAN:                    0x1008FF95,
+    XF86XK_UWB:                     0x1008FF96,
+    XF86XK_AudioForward:            0x1008FF97,
+    XF86XK_AudioRepeat:             0x1008FF98,
+    XF86XK_AudioRandomPlay:         0x1008FF99,
+    XF86XK_Subtitle:                0x1008FF9A,
+    XF86XK_AudioCycleTrack:         0x1008FF9B,
+    XF86XK_CycleAngle:              0x1008FF9C,
+    XF86XK_FrameBack:               0x1008FF9D,
+    XF86XK_FrameForward:            0x1008FF9E,
+    XF86XK_Time:                    0x1008FF9F,
+    XF86XK_Select:                  0x1008FFA0,
+    XF86XK_View:                    0x1008FFA1,
+    XF86XK_TopMenu:                 0x1008FFA2,
+    XF86XK_Red:                     0x1008FFA3,
+    XF86XK_Green:                   0x1008FFA4,
+    XF86XK_Yellow:                  0x1008FFA5,
+    XF86XK_Blue:                    0x1008FFA6,
+    XF86XK_Suspend:                 0x1008FFA7,
+    XF86XK_Hibernate:               0x1008FFA8,
+    XF86XK_TouchpadToggle:          0x1008FFA9,
+    XF86XK_TouchpadOn:              0x1008FFB0,
+    XF86XK_TouchpadOff:             0x1008FFB1,
+    XF86XK_AudioMicMute:            0x1008FFB2,
+    XF86XK_Switch_VT_1:             0x1008FE01,
+    XF86XK_Switch_VT_2:             0x1008FE02,
+    XF86XK_Switch_VT_3:             0x1008FE03,
+    XF86XK_Switch_VT_4:             0x1008FE04,
+    XF86XK_Switch_VT_5:             0x1008FE05,
+    XF86XK_Switch_VT_6:             0x1008FE06,
+    XF86XK_Switch_VT_7:             0x1008FE07,
+    XF86XK_Switch_VT_8:             0x1008FE08,
+    XF86XK_Switch_VT_9:             0x1008FE09,
+    XF86XK_Switch_VT_10:            0x1008FE0A,
+    XF86XK_Switch_VT_11:            0x1008FE0B,
+    XF86XK_Switch_VT_12:            0x1008FE0C,
+    XF86XK_Ungrab:                  0x1008FE20,
+    XF86XK_ClearGrab:               0x1008FE21,
+    XF86XK_Next_VMode:              0x1008FE22,
+    XF86XK_Prev_VMode:              0x1008FE23,
+    XF86XK_LogWindowTree:           0x1008FE24,
+    XF86XK_LogGrabInfo:             0x1008FE25,
+};

+ 688 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/keysymdef.js

@@ -0,0 +1,688 @@
+/*
+ * Mapping from Unicode codepoints to X11/RFB keysyms
+ *
+ * This file was automatically generated from keysymdef.h
+ * DO NOT EDIT!
+ */
+
+/* Functions at the bottom */
+
+var codepoints = {
+    0x0100: 0x03c0, // XK_Amacron
+    0x0101: 0x03e0, // XK_amacron
+    0x0102: 0x01c3, // XK_Abreve
+    0x0103: 0x01e3, // XK_abreve
+    0x0104: 0x01a1, // XK_Aogonek
+    0x0105: 0x01b1, // XK_aogonek
+    0x0106: 0x01c6, // XK_Cacute
+    0x0107: 0x01e6, // XK_cacute
+    0x0108: 0x02c6, // XK_Ccircumflex
+    0x0109: 0x02e6, // XK_ccircumflex
+    0x010a: 0x02c5, // XK_Cabovedot
+    0x010b: 0x02e5, // XK_cabovedot
+    0x010c: 0x01c8, // XK_Ccaron
+    0x010d: 0x01e8, // XK_ccaron
+    0x010e: 0x01cf, // XK_Dcaron
+    0x010f: 0x01ef, // XK_dcaron
+    0x0110: 0x01d0, // XK_Dstroke
+    0x0111: 0x01f0, // XK_dstroke
+    0x0112: 0x03aa, // XK_Emacron
+    0x0113: 0x03ba, // XK_emacron
+    0x0116: 0x03cc, // XK_Eabovedot
+    0x0117: 0x03ec, // XK_eabovedot
+    0x0118: 0x01ca, // XK_Eogonek
+    0x0119: 0x01ea, // XK_eogonek
+    0x011a: 0x01cc, // XK_Ecaron
+    0x011b: 0x01ec, // XK_ecaron
+    0x011c: 0x02d8, // XK_Gcircumflex
+    0x011d: 0x02f8, // XK_gcircumflex
+    0x011e: 0x02ab, // XK_Gbreve
+    0x011f: 0x02bb, // XK_gbreve
+    0x0120: 0x02d5, // XK_Gabovedot
+    0x0121: 0x02f5, // XK_gabovedot
+    0x0122: 0x03ab, // XK_Gcedilla
+    0x0123: 0x03bb, // XK_gcedilla
+    0x0124: 0x02a6, // XK_Hcircumflex
+    0x0125: 0x02b6, // XK_hcircumflex
+    0x0126: 0x02a1, // XK_Hstroke
+    0x0127: 0x02b1, // XK_hstroke
+    0x0128: 0x03a5, // XK_Itilde
+    0x0129: 0x03b5, // XK_itilde
+    0x012a: 0x03cf, // XK_Imacron
+    0x012b: 0x03ef, // XK_imacron
+    0x012e: 0x03c7, // XK_Iogonek
+    0x012f: 0x03e7, // XK_iogonek
+    0x0130: 0x02a9, // XK_Iabovedot
+    0x0131: 0x02b9, // XK_idotless
+    0x0134: 0x02ac, // XK_Jcircumflex
+    0x0135: 0x02bc, // XK_jcircumflex
+    0x0136: 0x03d3, // XK_Kcedilla
+    0x0137: 0x03f3, // XK_kcedilla
+    0x0138: 0x03a2, // XK_kra
+    0x0139: 0x01c5, // XK_Lacute
+    0x013a: 0x01e5, // XK_lacute
+    0x013b: 0x03a6, // XK_Lcedilla
+    0x013c: 0x03b6, // XK_lcedilla
+    0x013d: 0x01a5, // XK_Lcaron
+    0x013e: 0x01b5, // XK_lcaron
+    0x0141: 0x01a3, // XK_Lstroke
+    0x0142: 0x01b3, // XK_lstroke
+    0x0143: 0x01d1, // XK_Nacute
+    0x0144: 0x01f1, // XK_nacute
+    0x0145: 0x03d1, // XK_Ncedilla
+    0x0146: 0x03f1, // XK_ncedilla
+    0x0147: 0x01d2, // XK_Ncaron
+    0x0148: 0x01f2, // XK_ncaron
+    0x014a: 0x03bd, // XK_ENG
+    0x014b: 0x03bf, // XK_eng
+    0x014c: 0x03d2, // XK_Omacron
+    0x014d: 0x03f2, // XK_omacron
+    0x0150: 0x01d5, // XK_Odoubleacute
+    0x0151: 0x01f5, // XK_odoubleacute
+    0x0152: 0x13bc, // XK_OE
+    0x0153: 0x13bd, // XK_oe
+    0x0154: 0x01c0, // XK_Racute
+    0x0155: 0x01e0, // XK_racute
+    0x0156: 0x03a3, // XK_Rcedilla
+    0x0157: 0x03b3, // XK_rcedilla
+    0x0158: 0x01d8, // XK_Rcaron
+    0x0159: 0x01f8, // XK_rcaron
+    0x015a: 0x01a6, // XK_Sacute
+    0x015b: 0x01b6, // XK_sacute
+    0x015c: 0x02de, // XK_Scircumflex
+    0x015d: 0x02fe, // XK_scircumflex
+    0x015e: 0x01aa, // XK_Scedilla
+    0x015f: 0x01ba, // XK_scedilla
+    0x0160: 0x01a9, // XK_Scaron
+    0x0161: 0x01b9, // XK_scaron
+    0x0162: 0x01de, // XK_Tcedilla
+    0x0163: 0x01fe, // XK_tcedilla
+    0x0164: 0x01ab, // XK_Tcaron
+    0x0165: 0x01bb, // XK_tcaron
+    0x0166: 0x03ac, // XK_Tslash
+    0x0167: 0x03bc, // XK_tslash
+    0x0168: 0x03dd, // XK_Utilde
+    0x0169: 0x03fd, // XK_utilde
+    0x016a: 0x03de, // XK_Umacron
+    0x016b: 0x03fe, // XK_umacron
+    0x016c: 0x02dd, // XK_Ubreve
+    0x016d: 0x02fd, // XK_ubreve
+    0x016e: 0x01d9, // XK_Uring
+    0x016f: 0x01f9, // XK_uring
+    0x0170: 0x01db, // XK_Udoubleacute
+    0x0171: 0x01fb, // XK_udoubleacute
+    0x0172: 0x03d9, // XK_Uogonek
+    0x0173: 0x03f9, // XK_uogonek
+    0x0178: 0x13be, // XK_Ydiaeresis
+    0x0179: 0x01ac, // XK_Zacute
+    0x017a: 0x01bc, // XK_zacute
+    0x017b: 0x01af, // XK_Zabovedot
+    0x017c: 0x01bf, // XK_zabovedot
+    0x017d: 0x01ae, // XK_Zcaron
+    0x017e: 0x01be, // XK_zcaron
+    0x0192: 0x08f6, // XK_function
+    0x01d2: 0x10001d1, // XK_Ocaron
+    0x02c7: 0x01b7, // XK_caron
+    0x02d8: 0x01a2, // XK_breve
+    0x02d9: 0x01ff, // XK_abovedot
+    0x02db: 0x01b2, // XK_ogonek
+    0x02dd: 0x01bd, // XK_doubleacute
+    0x0385: 0x07ae, // XK_Greek_accentdieresis
+    0x0386: 0x07a1, // XK_Greek_ALPHAaccent
+    0x0388: 0x07a2, // XK_Greek_EPSILONaccent
+    0x0389: 0x07a3, // XK_Greek_ETAaccent
+    0x038a: 0x07a4, // XK_Greek_IOTAaccent
+    0x038c: 0x07a7, // XK_Greek_OMICRONaccent
+    0x038e: 0x07a8, // XK_Greek_UPSILONaccent
+    0x038f: 0x07ab, // XK_Greek_OMEGAaccent
+    0x0390: 0x07b6, // XK_Greek_iotaaccentdieresis
+    0x0391: 0x07c1, // XK_Greek_ALPHA
+    0x0392: 0x07c2, // XK_Greek_BETA
+    0x0393: 0x07c3, // XK_Greek_GAMMA
+    0x0394: 0x07c4, // XK_Greek_DELTA
+    0x0395: 0x07c5, // XK_Greek_EPSILON
+    0x0396: 0x07c6, // XK_Greek_ZETA
+    0x0397: 0x07c7, // XK_Greek_ETA
+    0x0398: 0x07c8, // XK_Greek_THETA
+    0x0399: 0x07c9, // XK_Greek_IOTA
+    0x039a: 0x07ca, // XK_Greek_KAPPA
+    0x039b: 0x07cb, // XK_Greek_LAMDA
+    0x039c: 0x07cc, // XK_Greek_MU
+    0x039d: 0x07cd, // XK_Greek_NU
+    0x039e: 0x07ce, // XK_Greek_XI
+    0x039f: 0x07cf, // XK_Greek_OMICRON
+    0x03a0: 0x07d0, // XK_Greek_PI
+    0x03a1: 0x07d1, // XK_Greek_RHO
+    0x03a3: 0x07d2, // XK_Greek_SIGMA
+    0x03a4: 0x07d4, // XK_Greek_TAU
+    0x03a5: 0x07d5, // XK_Greek_UPSILON
+    0x03a6: 0x07d6, // XK_Greek_PHI
+    0x03a7: 0x07d7, // XK_Greek_CHI
+    0x03a8: 0x07d8, // XK_Greek_PSI
+    0x03a9: 0x07d9, // XK_Greek_OMEGA
+    0x03aa: 0x07a5, // XK_Greek_IOTAdieresis
+    0x03ab: 0x07a9, // XK_Greek_UPSILONdieresis
+    0x03ac: 0x07b1, // XK_Greek_alphaaccent
+    0x03ad: 0x07b2, // XK_Greek_epsilonaccent
+    0x03ae: 0x07b3, // XK_Greek_etaaccent
+    0x03af: 0x07b4, // XK_Greek_iotaaccent
+    0x03b0: 0x07ba, // XK_Greek_upsilonaccentdieresis
+    0x03b1: 0x07e1, // XK_Greek_alpha
+    0x03b2: 0x07e2, // XK_Greek_beta
+    0x03b3: 0x07e3, // XK_Greek_gamma
+    0x03b4: 0x07e4, // XK_Greek_delta
+    0x03b5: 0x07e5, // XK_Greek_epsilon
+    0x03b6: 0x07e6, // XK_Greek_zeta
+    0x03b7: 0x07e7, // XK_Greek_eta
+    0x03b8: 0x07e8, // XK_Greek_theta
+    0x03b9: 0x07e9, // XK_Greek_iota
+    0x03ba: 0x07ea, // XK_Greek_kappa
+    0x03bb: 0x07eb, // XK_Greek_lamda
+    0x03bc: 0x07ec, // XK_Greek_mu
+    0x03bd: 0x07ed, // XK_Greek_nu
+    0x03be: 0x07ee, // XK_Greek_xi
+    0x03bf: 0x07ef, // XK_Greek_omicron
+    0x03c0: 0x07f0, // XK_Greek_pi
+    0x03c1: 0x07f1, // XK_Greek_rho
+    0x03c2: 0x07f3, // XK_Greek_finalsmallsigma
+    0x03c3: 0x07f2, // XK_Greek_sigma
+    0x03c4: 0x07f4, // XK_Greek_tau
+    0x03c5: 0x07f5, // XK_Greek_upsilon
+    0x03c6: 0x07f6, // XK_Greek_phi
+    0x03c7: 0x07f7, // XK_Greek_chi
+    0x03c8: 0x07f8, // XK_Greek_psi
+    0x03c9: 0x07f9, // XK_Greek_omega
+    0x03ca: 0x07b5, // XK_Greek_iotadieresis
+    0x03cb: 0x07b9, // XK_Greek_upsilondieresis
+    0x03cc: 0x07b7, // XK_Greek_omicronaccent
+    0x03cd: 0x07b8, // XK_Greek_upsilonaccent
+    0x03ce: 0x07bb, // XK_Greek_omegaaccent
+    0x0401: 0x06b3, // XK_Cyrillic_IO
+    0x0402: 0x06b1, // XK_Serbian_DJE
+    0x0403: 0x06b2, // XK_Macedonia_GJE
+    0x0404: 0x06b4, // XK_Ukrainian_IE
+    0x0405: 0x06b5, // XK_Macedonia_DSE
+    0x0406: 0x06b6, // XK_Ukrainian_I
+    0x0407: 0x06b7, // XK_Ukrainian_YI
+    0x0408: 0x06b8, // XK_Cyrillic_JE
+    0x0409: 0x06b9, // XK_Cyrillic_LJE
+    0x040a: 0x06ba, // XK_Cyrillic_NJE
+    0x040b: 0x06bb, // XK_Serbian_TSHE
+    0x040c: 0x06bc, // XK_Macedonia_KJE
+    0x040e: 0x06be, // XK_Byelorussian_SHORTU
+    0x040f: 0x06bf, // XK_Cyrillic_DZHE
+    0x0410: 0x06e1, // XK_Cyrillic_A
+    0x0411: 0x06e2, // XK_Cyrillic_BE
+    0x0412: 0x06f7, // XK_Cyrillic_VE
+    0x0413: 0x06e7, // XK_Cyrillic_GHE
+    0x0414: 0x06e4, // XK_Cyrillic_DE
+    0x0415: 0x06e5, // XK_Cyrillic_IE
+    0x0416: 0x06f6, // XK_Cyrillic_ZHE
+    0x0417: 0x06fa, // XK_Cyrillic_ZE
+    0x0418: 0x06e9, // XK_Cyrillic_I
+    0x0419: 0x06ea, // XK_Cyrillic_SHORTI
+    0x041a: 0x06eb, // XK_Cyrillic_KA
+    0x041b: 0x06ec, // XK_Cyrillic_EL
+    0x041c: 0x06ed, // XK_Cyrillic_EM
+    0x041d: 0x06ee, // XK_Cyrillic_EN
+    0x041e: 0x06ef, // XK_Cyrillic_O
+    0x041f: 0x06f0, // XK_Cyrillic_PE
+    0x0420: 0x06f2, // XK_Cyrillic_ER
+    0x0421: 0x06f3, // XK_Cyrillic_ES
+    0x0422: 0x06f4, // XK_Cyrillic_TE
+    0x0423: 0x06f5, // XK_Cyrillic_U
+    0x0424: 0x06e6, // XK_Cyrillic_EF
+    0x0425: 0x06e8, // XK_Cyrillic_HA
+    0x0426: 0x06e3, // XK_Cyrillic_TSE
+    0x0427: 0x06fe, // XK_Cyrillic_CHE
+    0x0428: 0x06fb, // XK_Cyrillic_SHA
+    0x0429: 0x06fd, // XK_Cyrillic_SHCHA
+    0x042a: 0x06ff, // XK_Cyrillic_HARDSIGN
+    0x042b: 0x06f9, // XK_Cyrillic_YERU
+    0x042c: 0x06f8, // XK_Cyrillic_SOFTSIGN
+    0x042d: 0x06fc, // XK_Cyrillic_E
+    0x042e: 0x06e0, // XK_Cyrillic_YU
+    0x042f: 0x06f1, // XK_Cyrillic_YA
+    0x0430: 0x06c1, // XK_Cyrillic_a
+    0x0431: 0x06c2, // XK_Cyrillic_be
+    0x0432: 0x06d7, // XK_Cyrillic_ve
+    0x0433: 0x06c7, // XK_Cyrillic_ghe
+    0x0434: 0x06c4, // XK_Cyrillic_de
+    0x0435: 0x06c5, // XK_Cyrillic_ie
+    0x0436: 0x06d6, // XK_Cyrillic_zhe
+    0x0437: 0x06da, // XK_Cyrillic_ze
+    0x0438: 0x06c9, // XK_Cyrillic_i
+    0x0439: 0x06ca, // XK_Cyrillic_shorti
+    0x043a: 0x06cb, // XK_Cyrillic_ka
+    0x043b: 0x06cc, // XK_Cyrillic_el
+    0x043c: 0x06cd, // XK_Cyrillic_em
+    0x043d: 0x06ce, // XK_Cyrillic_en
+    0x043e: 0x06cf, // XK_Cyrillic_o
+    0x043f: 0x06d0, // XK_Cyrillic_pe
+    0x0440: 0x06d2, // XK_Cyrillic_er
+    0x0441: 0x06d3, // XK_Cyrillic_es
+    0x0442: 0x06d4, // XK_Cyrillic_te
+    0x0443: 0x06d5, // XK_Cyrillic_u
+    0x0444: 0x06c6, // XK_Cyrillic_ef
+    0x0445: 0x06c8, // XK_Cyrillic_ha
+    0x0446: 0x06c3, // XK_Cyrillic_tse
+    0x0447: 0x06de, // XK_Cyrillic_che
+    0x0448: 0x06db, // XK_Cyrillic_sha
+    0x0449: 0x06dd, // XK_Cyrillic_shcha
+    0x044a: 0x06df, // XK_Cyrillic_hardsign
+    0x044b: 0x06d9, // XK_Cyrillic_yeru
+    0x044c: 0x06d8, // XK_Cyrillic_softsign
+    0x044d: 0x06dc, // XK_Cyrillic_e
+    0x044e: 0x06c0, // XK_Cyrillic_yu
+    0x044f: 0x06d1, // XK_Cyrillic_ya
+    0x0451: 0x06a3, // XK_Cyrillic_io
+    0x0452: 0x06a1, // XK_Serbian_dje
+    0x0453: 0x06a2, // XK_Macedonia_gje
+    0x0454: 0x06a4, // XK_Ukrainian_ie
+    0x0455: 0x06a5, // XK_Macedonia_dse
+    0x0456: 0x06a6, // XK_Ukrainian_i
+    0x0457: 0x06a7, // XK_Ukrainian_yi
+    0x0458: 0x06a8, // XK_Cyrillic_je
+    0x0459: 0x06a9, // XK_Cyrillic_lje
+    0x045a: 0x06aa, // XK_Cyrillic_nje
+    0x045b: 0x06ab, // XK_Serbian_tshe
+    0x045c: 0x06ac, // XK_Macedonia_kje
+    0x045e: 0x06ae, // XK_Byelorussian_shortu
+    0x045f: 0x06af, // XK_Cyrillic_dzhe
+    0x0490: 0x06bd, // XK_Ukrainian_GHE_WITH_UPTURN
+    0x0491: 0x06ad, // XK_Ukrainian_ghe_with_upturn
+    0x05d0: 0x0ce0, // XK_hebrew_aleph
+    0x05d1: 0x0ce1, // XK_hebrew_bet
+    0x05d2: 0x0ce2, // XK_hebrew_gimel
+    0x05d3: 0x0ce3, // XK_hebrew_dalet
+    0x05d4: 0x0ce4, // XK_hebrew_he
+    0x05d5: 0x0ce5, // XK_hebrew_waw
+    0x05d6: 0x0ce6, // XK_hebrew_zain
+    0x05d7: 0x0ce7, // XK_hebrew_chet
+    0x05d8: 0x0ce8, // XK_hebrew_tet
+    0x05d9: 0x0ce9, // XK_hebrew_yod
+    0x05da: 0x0cea, // XK_hebrew_finalkaph
+    0x05db: 0x0ceb, // XK_hebrew_kaph
+    0x05dc: 0x0cec, // XK_hebrew_lamed
+    0x05dd: 0x0ced, // XK_hebrew_finalmem
+    0x05de: 0x0cee, // XK_hebrew_mem
+    0x05df: 0x0cef, // XK_hebrew_finalnun
+    0x05e0: 0x0cf0, // XK_hebrew_nun
+    0x05e1: 0x0cf1, // XK_hebrew_samech
+    0x05e2: 0x0cf2, // XK_hebrew_ayin
+    0x05e3: 0x0cf3, // XK_hebrew_finalpe
+    0x05e4: 0x0cf4, // XK_hebrew_pe
+    0x05e5: 0x0cf5, // XK_hebrew_finalzade
+    0x05e6: 0x0cf6, // XK_hebrew_zade
+    0x05e7: 0x0cf7, // XK_hebrew_qoph
+    0x05e8: 0x0cf8, // XK_hebrew_resh
+    0x05e9: 0x0cf9, // XK_hebrew_shin
+    0x05ea: 0x0cfa, // XK_hebrew_taw
+    0x060c: 0x05ac, // XK_Arabic_comma
+    0x061b: 0x05bb, // XK_Arabic_semicolon
+    0x061f: 0x05bf, // XK_Arabic_question_mark
+    0x0621: 0x05c1, // XK_Arabic_hamza
+    0x0622: 0x05c2, // XK_Arabic_maddaonalef
+    0x0623: 0x05c3, // XK_Arabic_hamzaonalef
+    0x0624: 0x05c4, // XK_Arabic_hamzaonwaw
+    0x0625: 0x05c5, // XK_Arabic_hamzaunderalef
+    0x0626: 0x05c6, // XK_Arabic_hamzaonyeh
+    0x0627: 0x05c7, // XK_Arabic_alef
+    0x0628: 0x05c8, // XK_Arabic_beh
+    0x0629: 0x05c9, // XK_Arabic_tehmarbuta
+    0x062a: 0x05ca, // XK_Arabic_teh
+    0x062b: 0x05cb, // XK_Arabic_theh
+    0x062c: 0x05cc, // XK_Arabic_jeem
+    0x062d: 0x05cd, // XK_Arabic_hah
+    0x062e: 0x05ce, // XK_Arabic_khah
+    0x062f: 0x05cf, // XK_Arabic_dal
+    0x0630: 0x05d0, // XK_Arabic_thal
+    0x0631: 0x05d1, // XK_Arabic_ra
+    0x0632: 0x05d2, // XK_Arabic_zain
+    0x0633: 0x05d3, // XK_Arabic_seen
+    0x0634: 0x05d4, // XK_Arabic_sheen
+    0x0635: 0x05d5, // XK_Arabic_sad
+    0x0636: 0x05d6, // XK_Arabic_dad
+    0x0637: 0x05d7, // XK_Arabic_tah
+    0x0638: 0x05d8, // XK_Arabic_zah
+    0x0639: 0x05d9, // XK_Arabic_ain
+    0x063a: 0x05da, // XK_Arabic_ghain
+    0x0640: 0x05e0, // XK_Arabic_tatweel
+    0x0641: 0x05e1, // XK_Arabic_feh
+    0x0642: 0x05e2, // XK_Arabic_qaf
+    0x0643: 0x05e3, // XK_Arabic_kaf
+    0x0644: 0x05e4, // XK_Arabic_lam
+    0x0645: 0x05e5, // XK_Arabic_meem
+    0x0646: 0x05e6, // XK_Arabic_noon
+    0x0647: 0x05e7, // XK_Arabic_ha
+    0x0648: 0x05e8, // XK_Arabic_waw
+    0x0649: 0x05e9, // XK_Arabic_alefmaksura
+    0x064a: 0x05ea, // XK_Arabic_yeh
+    0x064b: 0x05eb, // XK_Arabic_fathatan
+    0x064c: 0x05ec, // XK_Arabic_dammatan
+    0x064d: 0x05ed, // XK_Arabic_kasratan
+    0x064e: 0x05ee, // XK_Arabic_fatha
+    0x064f: 0x05ef, // XK_Arabic_damma
+    0x0650: 0x05f0, // XK_Arabic_kasra
+    0x0651: 0x05f1, // XK_Arabic_shadda
+    0x0652: 0x05f2, // XK_Arabic_sukun
+    0x0e01: 0x0da1, // XK_Thai_kokai
+    0x0e02: 0x0da2, // XK_Thai_khokhai
+    0x0e03: 0x0da3, // XK_Thai_khokhuat
+    0x0e04: 0x0da4, // XK_Thai_khokhwai
+    0x0e05: 0x0da5, // XK_Thai_khokhon
+    0x0e06: 0x0da6, // XK_Thai_khorakhang
+    0x0e07: 0x0da7, // XK_Thai_ngongu
+    0x0e08: 0x0da8, // XK_Thai_chochan
+    0x0e09: 0x0da9, // XK_Thai_choching
+    0x0e0a: 0x0daa, // XK_Thai_chochang
+    0x0e0b: 0x0dab, // XK_Thai_soso
+    0x0e0c: 0x0dac, // XK_Thai_chochoe
+    0x0e0d: 0x0dad, // XK_Thai_yoying
+    0x0e0e: 0x0dae, // XK_Thai_dochada
+    0x0e0f: 0x0daf, // XK_Thai_topatak
+    0x0e10: 0x0db0, // XK_Thai_thothan
+    0x0e11: 0x0db1, // XK_Thai_thonangmontho
+    0x0e12: 0x0db2, // XK_Thai_thophuthao
+    0x0e13: 0x0db3, // XK_Thai_nonen
+    0x0e14: 0x0db4, // XK_Thai_dodek
+    0x0e15: 0x0db5, // XK_Thai_totao
+    0x0e16: 0x0db6, // XK_Thai_thothung
+    0x0e17: 0x0db7, // XK_Thai_thothahan
+    0x0e18: 0x0db8, // XK_Thai_thothong
+    0x0e19: 0x0db9, // XK_Thai_nonu
+    0x0e1a: 0x0dba, // XK_Thai_bobaimai
+    0x0e1b: 0x0dbb, // XK_Thai_popla
+    0x0e1c: 0x0dbc, // XK_Thai_phophung
+    0x0e1d: 0x0dbd, // XK_Thai_fofa
+    0x0e1e: 0x0dbe, // XK_Thai_phophan
+    0x0e1f: 0x0dbf, // XK_Thai_fofan
+    0x0e20: 0x0dc0, // XK_Thai_phosamphao
+    0x0e21: 0x0dc1, // XK_Thai_moma
+    0x0e22: 0x0dc2, // XK_Thai_yoyak
+    0x0e23: 0x0dc3, // XK_Thai_rorua
+    0x0e24: 0x0dc4, // XK_Thai_ru
+    0x0e25: 0x0dc5, // XK_Thai_loling
+    0x0e26: 0x0dc6, // XK_Thai_lu
+    0x0e27: 0x0dc7, // XK_Thai_wowaen
+    0x0e28: 0x0dc8, // XK_Thai_sosala
+    0x0e29: 0x0dc9, // XK_Thai_sorusi
+    0x0e2a: 0x0dca, // XK_Thai_sosua
+    0x0e2b: 0x0dcb, // XK_Thai_hohip
+    0x0e2c: 0x0dcc, // XK_Thai_lochula
+    0x0e2d: 0x0dcd, // XK_Thai_oang
+    0x0e2e: 0x0dce, // XK_Thai_honokhuk
+    0x0e2f: 0x0dcf, // XK_Thai_paiyannoi
+    0x0e30: 0x0dd0, // XK_Thai_saraa
+    0x0e31: 0x0dd1, // XK_Thai_maihanakat
+    0x0e32: 0x0dd2, // XK_Thai_saraaa
+    0x0e33: 0x0dd3, // XK_Thai_saraam
+    0x0e34: 0x0dd4, // XK_Thai_sarai
+    0x0e35: 0x0dd5, // XK_Thai_saraii
+    0x0e36: 0x0dd6, // XK_Thai_saraue
+    0x0e37: 0x0dd7, // XK_Thai_sarauee
+    0x0e38: 0x0dd8, // XK_Thai_sarau
+    0x0e39: 0x0dd9, // XK_Thai_sarauu
+    0x0e3a: 0x0dda, // XK_Thai_phinthu
+    0x0e3f: 0x0ddf, // XK_Thai_baht
+    0x0e40: 0x0de0, // XK_Thai_sarae
+    0x0e41: 0x0de1, // XK_Thai_saraae
+    0x0e42: 0x0de2, // XK_Thai_sarao
+    0x0e43: 0x0de3, // XK_Thai_saraaimaimuan
+    0x0e44: 0x0de4, // XK_Thai_saraaimaimalai
+    0x0e45: 0x0de5, // XK_Thai_lakkhangyao
+    0x0e46: 0x0de6, // XK_Thai_maiyamok
+    0x0e47: 0x0de7, // XK_Thai_maitaikhu
+    0x0e48: 0x0de8, // XK_Thai_maiek
+    0x0e49: 0x0de9, // XK_Thai_maitho
+    0x0e4a: 0x0dea, // XK_Thai_maitri
+    0x0e4b: 0x0deb, // XK_Thai_maichattawa
+    0x0e4c: 0x0dec, // XK_Thai_thanthakhat
+    0x0e4d: 0x0ded, // XK_Thai_nikhahit
+    0x0e50: 0x0df0, // XK_Thai_leksun
+    0x0e51: 0x0df1, // XK_Thai_leknung
+    0x0e52: 0x0df2, // XK_Thai_leksong
+    0x0e53: 0x0df3, // XK_Thai_leksam
+    0x0e54: 0x0df4, // XK_Thai_leksi
+    0x0e55: 0x0df5, // XK_Thai_lekha
+    0x0e56: 0x0df6, // XK_Thai_lekhok
+    0x0e57: 0x0df7, // XK_Thai_lekchet
+    0x0e58: 0x0df8, // XK_Thai_lekpaet
+    0x0e59: 0x0df9, // XK_Thai_lekkao
+    0x2002: 0x0aa2, // XK_enspace
+    0x2003: 0x0aa1, // XK_emspace
+    0x2004: 0x0aa3, // XK_em3space
+    0x2005: 0x0aa4, // XK_em4space
+    0x2007: 0x0aa5, // XK_digitspace
+    0x2008: 0x0aa6, // XK_punctspace
+    0x2009: 0x0aa7, // XK_thinspace
+    0x200a: 0x0aa8, // XK_hairspace
+    0x2012: 0x0abb, // XK_figdash
+    0x2013: 0x0aaa, // XK_endash
+    0x2014: 0x0aa9, // XK_emdash
+    0x2015: 0x07af, // XK_Greek_horizbar
+    0x2017: 0x0cdf, // XK_hebrew_doublelowline
+    0x2018: 0x0ad0, // XK_leftsinglequotemark
+    0x2019: 0x0ad1, // XK_rightsinglequotemark
+    0x201a: 0x0afd, // XK_singlelowquotemark
+    0x201c: 0x0ad2, // XK_leftdoublequotemark
+    0x201d: 0x0ad3, // XK_rightdoublequotemark
+    0x201e: 0x0afe, // XK_doublelowquotemark
+    0x2020: 0x0af1, // XK_dagger
+    0x2021: 0x0af2, // XK_doubledagger
+    0x2022: 0x0ae6, // XK_enfilledcircbullet
+    0x2025: 0x0aaf, // XK_doubbaselinedot
+    0x2026: 0x0aae, // XK_ellipsis
+    0x2030: 0x0ad5, // XK_permille
+    0x2032: 0x0ad6, // XK_minutes
+    0x2033: 0x0ad7, // XK_seconds
+    0x2038: 0x0afc, // XK_caret
+    0x203e: 0x047e, // XK_overline
+    0x20a9: 0x0eff, // XK_Korean_Won
+    0x20ac: 0x20ac, // XK_EuroSign
+    0x2105: 0x0ab8, // XK_careof
+    0x2116: 0x06b0, // XK_numerosign
+    0x2117: 0x0afb, // XK_phonographcopyright
+    0x211e: 0x0ad4, // XK_prescription
+    0x2122: 0x0ac9, // XK_trademark
+    0x2153: 0x0ab0, // XK_onethird
+    0x2154: 0x0ab1, // XK_twothirds
+    0x2155: 0x0ab2, // XK_onefifth
+    0x2156: 0x0ab3, // XK_twofifths
+    0x2157: 0x0ab4, // XK_threefifths
+    0x2158: 0x0ab5, // XK_fourfifths
+    0x2159: 0x0ab6, // XK_onesixth
+    0x215a: 0x0ab7, // XK_fivesixths
+    0x215b: 0x0ac3, // XK_oneeighth
+    0x215c: 0x0ac4, // XK_threeeighths
+    0x215d: 0x0ac5, // XK_fiveeighths
+    0x215e: 0x0ac6, // XK_seveneighths
+    0x2190: 0x08fb, // XK_leftarrow
+    0x2191: 0x08fc, // XK_uparrow
+    0x2192: 0x08fd, // XK_rightarrow
+    0x2193: 0x08fe, // XK_downarrow
+    0x21d2: 0x08ce, // XK_implies
+    0x21d4: 0x08cd, // XK_ifonlyif
+    0x2202: 0x08ef, // XK_partialderivative
+    0x2207: 0x08c5, // XK_nabla
+    0x2218: 0x0bca, // XK_jot
+    0x221a: 0x08d6, // XK_radical
+    0x221d: 0x08c1, // XK_variation
+    0x221e: 0x08c2, // XK_infinity
+    0x2227: 0x08de, // XK_logicaland
+    0x2228: 0x08df, // XK_logicalor
+    0x2229: 0x08dc, // XK_intersection
+    0x222a: 0x08dd, // XK_union
+    0x222b: 0x08bf, // XK_integral
+    0x2234: 0x08c0, // XK_therefore
+    0x223c: 0x08c8, // XK_approximate
+    0x2243: 0x08c9, // XK_similarequal
+    0x2245: 0x1002248, // XK_approxeq
+    0x2260: 0x08bd, // XK_notequal
+    0x2261: 0x08cf, // XK_identical
+    0x2264: 0x08bc, // XK_lessthanequal
+    0x2265: 0x08be, // XK_greaterthanequal
+    0x2282: 0x08da, // XK_includedin
+    0x2283: 0x08db, // XK_includes
+    0x22a2: 0x0bfc, // XK_righttack
+    0x22a3: 0x0bdc, // XK_lefttack
+    0x22a4: 0x0bc2, // XK_downtack
+    0x22a5: 0x0bce, // XK_uptack
+    0x2308: 0x0bd3, // XK_upstile
+    0x230a: 0x0bc4, // XK_downstile
+    0x2315: 0x0afa, // XK_telephonerecorder
+    0x2320: 0x08a4, // XK_topintegral
+    0x2321: 0x08a5, // XK_botintegral
+    0x2395: 0x0bcc, // XK_quad
+    0x239b: 0x08ab, // XK_topleftparens
+    0x239d: 0x08ac, // XK_botleftparens
+    0x239e: 0x08ad, // XK_toprightparens
+    0x23a0: 0x08ae, // XK_botrightparens
+    0x23a1: 0x08a7, // XK_topleftsqbracket
+    0x23a3: 0x08a8, // XK_botleftsqbracket
+    0x23a4: 0x08a9, // XK_toprightsqbracket
+    0x23a6: 0x08aa, // XK_botrightsqbracket
+    0x23a8: 0x08af, // XK_leftmiddlecurlybrace
+    0x23ac: 0x08b0, // XK_rightmiddlecurlybrace
+    0x23b7: 0x08a1, // XK_leftradical
+    0x23ba: 0x09ef, // XK_horizlinescan1
+    0x23bb: 0x09f0, // XK_horizlinescan3
+    0x23bc: 0x09f2, // XK_horizlinescan7
+    0x23bd: 0x09f3, // XK_horizlinescan9
+    0x2409: 0x09e2, // XK_ht
+    0x240a: 0x09e5, // XK_lf
+    0x240b: 0x09e9, // XK_vt
+    0x240c: 0x09e3, // XK_ff
+    0x240d: 0x09e4, // XK_cr
+    0x2423: 0x0aac, // XK_signifblank
+    0x2424: 0x09e8, // XK_nl
+    0x2500: 0x08a3, // XK_horizconnector
+    0x2502: 0x08a6, // XK_vertconnector
+    0x250c: 0x08a2, // XK_topleftradical
+    0x2510: 0x09eb, // XK_uprightcorner
+    0x2514: 0x09ed, // XK_lowleftcorner
+    0x2518: 0x09ea, // XK_lowrightcorner
+    0x251c: 0x09f4, // XK_leftt
+    0x2524: 0x09f5, // XK_rightt
+    0x252c: 0x09f7, // XK_topt
+    0x2534: 0x09f6, // XK_bott
+    0x253c: 0x09ee, // XK_crossinglines
+    0x2592: 0x09e1, // XK_checkerboard
+    0x25aa: 0x0ae7, // XK_enfilledsqbullet
+    0x25ab: 0x0ae1, // XK_enopensquarebullet
+    0x25ac: 0x0adb, // XK_filledrectbullet
+    0x25ad: 0x0ae2, // XK_openrectbullet
+    0x25ae: 0x0adf, // XK_emfilledrect
+    0x25af: 0x0acf, // XK_emopenrectangle
+    0x25b2: 0x0ae8, // XK_filledtribulletup
+    0x25b3: 0x0ae3, // XK_opentribulletup
+    0x25b6: 0x0add, // XK_filledrighttribullet
+    0x25b7: 0x0acd, // XK_rightopentriangle
+    0x25bc: 0x0ae9, // XK_filledtribulletdown
+    0x25bd: 0x0ae4, // XK_opentribulletdown
+    0x25c0: 0x0adc, // XK_filledlefttribullet
+    0x25c1: 0x0acc, // XK_leftopentriangle
+    0x25c6: 0x09e0, // XK_soliddiamond
+    0x25cb: 0x0ace, // XK_emopencircle
+    0x25cf: 0x0ade, // XK_emfilledcircle
+    0x25e6: 0x0ae0, // XK_enopencircbullet
+    0x2606: 0x0ae5, // XK_openstar
+    0x260e: 0x0af9, // XK_telephone
+    0x2613: 0x0aca, // XK_signaturemark
+    0x261c: 0x0aea, // XK_leftpointer
+    0x261e: 0x0aeb, // XK_rightpointer
+    0x2640: 0x0af8, // XK_femalesymbol
+    0x2642: 0x0af7, // XK_malesymbol
+    0x2663: 0x0aec, // XK_club
+    0x2665: 0x0aee, // XK_heart
+    0x2666: 0x0aed, // XK_diamond
+    0x266d: 0x0af6, // XK_musicalflat
+    0x266f: 0x0af5, // XK_musicalsharp
+    0x2713: 0x0af3, // XK_checkmark
+    0x2717: 0x0af4, // XK_ballotcross
+    0x271d: 0x0ad9, // XK_latincross
+    0x2720: 0x0af0, // XK_maltesecross
+    0x27e8: 0x0abc, // XK_leftanglebracket
+    0x27e9: 0x0abe, // XK_rightanglebracket
+    0x3001: 0x04a4, // XK_kana_comma
+    0x3002: 0x04a1, // XK_kana_fullstop
+    0x300c: 0x04a2, // XK_kana_openingbracket
+    0x300d: 0x04a3, // XK_kana_closingbracket
+    0x309b: 0x04de, // XK_voicedsound
+    0x309c: 0x04df, // XK_semivoicedsound
+    0x30a1: 0x04a7, // XK_kana_a
+    0x30a2: 0x04b1, // XK_kana_A
+    0x30a3: 0x04a8, // XK_kana_i
+    0x30a4: 0x04b2, // XK_kana_I
+    0x30a5: 0x04a9, // XK_kana_u
+    0x30a6: 0x04b3, // XK_kana_U
+    0x30a7: 0x04aa, // XK_kana_e
+    0x30a8: 0x04b4, // XK_kana_E
+    0x30a9: 0x04ab, // XK_kana_o
+    0x30aa: 0x04b5, // XK_kana_O
+    0x30ab: 0x04b6, // XK_kana_KA
+    0x30ad: 0x04b7, // XK_kana_KI
+    0x30af: 0x04b8, // XK_kana_KU
+    0x30b1: 0x04b9, // XK_kana_KE
+    0x30b3: 0x04ba, // XK_kana_KO
+    0x30b5: 0x04bb, // XK_kana_SA
+    0x30b7: 0x04bc, // XK_kana_SHI
+    0x30b9: 0x04bd, // XK_kana_SU
+    0x30bb: 0x04be, // XK_kana_SE
+    0x30bd: 0x04bf, // XK_kana_SO
+    0x30bf: 0x04c0, // XK_kana_TA
+    0x30c1: 0x04c1, // XK_kana_CHI
+    0x30c3: 0x04af, // XK_kana_tsu
+    0x30c4: 0x04c2, // XK_kana_TSU
+    0x30c6: 0x04c3, // XK_kana_TE
+    0x30c8: 0x04c4, // XK_kana_TO
+    0x30ca: 0x04c5, // XK_kana_NA
+    0x30cb: 0x04c6, // XK_kana_NI
+    0x30cc: 0x04c7, // XK_kana_NU
+    0x30cd: 0x04c8, // XK_kana_NE
+    0x30ce: 0x04c9, // XK_kana_NO
+    0x30cf: 0x04ca, // XK_kana_HA
+    0x30d2: 0x04cb, // XK_kana_HI
+    0x30d5: 0x04cc, // XK_kana_FU
+    0x30d8: 0x04cd, // XK_kana_HE
+    0x30db: 0x04ce, // XK_kana_HO
+    0x30de: 0x04cf, // XK_kana_MA
+    0x30df: 0x04d0, // XK_kana_MI
+    0x30e0: 0x04d1, // XK_kana_MU
+    0x30e1: 0x04d2, // XK_kana_ME
+    0x30e2: 0x04d3, // XK_kana_MO
+    0x30e3: 0x04ac, // XK_kana_ya
+    0x30e4: 0x04d4, // XK_kana_YA
+    0x30e5: 0x04ad, // XK_kana_yu
+    0x30e6: 0x04d5, // XK_kana_YU
+    0x30e7: 0x04ae, // XK_kana_yo
+    0x30e8: 0x04d6, // XK_kana_YO
+    0x30e9: 0x04d7, // XK_kana_RA
+    0x30ea: 0x04d8, // XK_kana_RI
+    0x30eb: 0x04d9, // XK_kana_RU
+    0x30ec: 0x04da, // XK_kana_RE
+    0x30ed: 0x04db, // XK_kana_RO
+    0x30ef: 0x04dc, // XK_kana_WA
+    0x30f2: 0x04a6, // XK_kana_WO
+    0x30f3: 0x04dd, // XK_kana_N
+    0x30fb: 0x04a5, // XK_kana_conjunctive
+    0x30fc: 0x04b0, // XK_prolongedsound
+};
+
+export default {
+    lookup : function(u) {
+        // Latin-1 is one-to-one mapping
+        if ((u >= 0x20) && (u <= 0xff)) {
+            return u;
+        }
+
+        // Lookup table (fairly random)
+        var keysym = codepoints[u];
+        if (keysym !== undefined) {
+            return keysym;
+        }
+
+        // General mapping as final fallback
+        return 0x01000000 | u;
+    },
+};

+ 280 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/mouse.js

@@ -0,0 +1,280 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2013 Samuel Mannehed for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+import * as Log from '../util/logging.js';
+import { isTouchDevice } from '../util/browser.js';
+import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
+
+var WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
+var WHEEL_STEP_TIMEOUT = 50; // ms
+var WHEEL_LINE_HEIGHT = 19;
+
+export default function Mouse(target) {
+    this._target = target || document;
+
+    this._doubleClickTimer = null;
+    this._lastTouchPos = null;
+
+    this._pos = null;
+    this._wheelStepXTimer = null;
+    this._wheelStepYTimer = null;
+    this._accumulatedWheelDeltaX = 0;
+    this._accumulatedWheelDeltaY = 0;
+
+    this._eventHandlers = {
+        'mousedown': this._handleMouseDown.bind(this),
+        'mouseup': this._handleMouseUp.bind(this),
+        'mousemove': this._handleMouseMove.bind(this),
+        'mousewheel': this._handleMouseWheel.bind(this),
+        'mousedisable': this._handleMouseDisable.bind(this)
+    };
+};
+
+Mouse.prototype = {
+    // ===== PROPERTIES =====
+
+    touchButton: 1,                 // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
+
+    // ===== EVENT HANDLERS =====
+
+    onmousebutton: function () {},  // Handler for mouse button click/release
+    onmousemove: function () {},    // Handler for mouse movement
+
+    // ===== PRIVATE METHODS =====
+
+    _resetDoubleClickTimer: function () {
+        this._doubleClickTimer = null;
+    },
+
+    _handleMouseButton: function (e, down) {
+        this._updateMousePosition(e);
+        var pos = this._pos;
+
+        var bmask;
+        if (e.touches || e.changedTouches) {
+            // Touch device
+
+            // When two touches occur within 500 ms of each other and are
+            // close enough together a double click is triggered.
+            if (down == 1) {
+                if (this._doubleClickTimer === null) {
+                    this._lastTouchPos = pos;
+                } else {
+                    clearTimeout(this._doubleClickTimer);
+
+                    // When the distance between the two touches is small enough
+                    // force the position of the latter touch to the position of
+                    // the first.
+
+                    var xs = this._lastTouchPos.x - pos.x;
+                    var ys = this._lastTouchPos.y - pos.y;
+                    var d = Math.sqrt((xs * xs) + (ys * ys));
+
+                    // The goal is to trigger on a certain physical width, the
+                    // devicePixelRatio brings us a bit closer but is not optimal.
+                    var threshold = 20 * (window.devicePixelRatio || 1);
+                    if (d < threshold) {
+                        pos = this._lastTouchPos;
+                    }
+                }
+                this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
+            }
+            bmask = this.touchButton;
+            // If bmask is set
+        } else if (e.which) {
+            /* everything except IE */
+            bmask = 1 << e.button;
+        } else {
+            /* IE including 9 */
+            bmask = (e.button & 0x1) +      // Left
+                    (e.button & 0x2) * 2 +  // Right
+                    (e.button & 0x4) / 2;   // Middle
+        }
+
+        Log.Debug("onmousebutton " + (down ? "down" : "up") +
+                  ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
+        this.onmousebutton(pos.x, pos.y, down, bmask);
+
+        stopEvent(e);
+    },
+
+    _handleMouseDown: function (e) {
+        // Touch events have implicit capture
+        if (e.type === "mousedown") {
+            setCapture(this._target);
+        }
+
+        this._handleMouseButton(e, 1);
+    },
+
+    _handleMouseUp: function (e) {
+        this._handleMouseButton(e, 0);
+    },
+
+    // Mouse wheel events are sent in steps over VNC. This means that the VNC
+    // protocol can't handle a wheel event with specific distance or speed.
+    // Therefor, if we get a lot of small mouse wheel events we combine them.
+    _generateWheelStepX: function () {
+
+        if (this._accumulatedWheelDeltaX < 0) {
+            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
+            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 5);
+        } else if (this._accumulatedWheelDeltaX > 0) {
+            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 6);
+            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 6);
+        }
+
+        this._accumulatedWheelDeltaX = 0;
+    },
+
+    _generateWheelStepY: function () {
+
+        if (this._accumulatedWheelDeltaY < 0) {
+            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
+            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 3);
+        } else if (this._accumulatedWheelDeltaY > 0) {
+            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 4);
+            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 4);
+        }
+
+        this._accumulatedWheelDeltaY = 0;
+    },
+
+    _resetWheelStepTimers: function () {
+        window.clearTimeout(this._wheelStepXTimer);
+        window.clearTimeout(this._wheelStepYTimer);
+        this._wheelStepXTimer = null;
+        this._wheelStepYTimer = null;
+    },
+
+    _handleMouseWheel: function (e) {
+        this._resetWheelStepTimers();
+
+        this._updateMousePosition(e);
+
+        var dX = e.deltaX;
+        var dY = e.deltaY;
+
+        // Pixel units unless it's non-zero.
+        // Note that if deltamode is line or page won't matter since we aren't
+        // sending the mouse wheel delta to the server anyway.
+        // The difference between pixel and line can be important however since
+        // we have a threshold that can be smaller than the line height.
+        if (e.deltaMode !== 0) {
+            dX *= WHEEL_LINE_HEIGHT;
+            dY *= WHEEL_LINE_HEIGHT;
+        }
+
+        this._accumulatedWheelDeltaX += dX;
+        this._accumulatedWheelDeltaY += dY;
+
+        // Generate a mouse wheel step event when the accumulated delta
+        // for one of the axes is large enough.
+        // Small delta events that do not pass the threshold get sent
+        // after a timeout.
+        if (Math.abs(this._accumulatedWheelDeltaX) > WHEEL_STEP) {
+            this._generateWheelStepX();
+        } else {
+            this._wheelStepXTimer =
+                window.setTimeout(this._generateWheelStepX.bind(this),
+                                  WHEEL_STEP_TIMEOUT);
+        }
+        if (Math.abs(this._accumulatedWheelDeltaY) > WHEEL_STEP) {
+            this._generateWheelStepY();
+        } else {
+            this._wheelStepYTimer =
+                window.setTimeout(this._generateWheelStepY.bind(this),
+                                  WHEEL_STEP_TIMEOUT);
+        }
+
+        stopEvent(e);
+    },
+
+    _handleMouseMove: function (e) {
+        this._updateMousePosition(e);
+        this.onmousemove(this._pos.x, this._pos.y);
+        stopEvent(e);
+    },
+
+    _handleMouseDisable: function (e) {
+        /*
+         * Stop propagation if inside canvas area
+         * Note: This is only needed for the 'click' event as it fails
+         *       to fire properly for the target element so we have
+         *       to listen on the document element instead.
+         */
+        if (e.target == this._target) {
+            stopEvent(e);
+        }
+    },
+
+    // Update coordinates relative to target
+    _updateMousePosition: function(e) {
+        e = getPointerEvent(e);
+        var bounds = this._target.getBoundingClientRect();
+        var x, y;
+        // Clip to target bounds
+        if (e.clientX < bounds.left) {
+            x = 0;
+        } else if (e.clientX >= bounds.right) {
+            x = bounds.width - 1;
+        } else {
+            x = e.clientX - bounds.left;
+        }
+        if (e.clientY < bounds.top) {
+            y = 0;
+        } else if (e.clientY >= bounds.bottom) {
+            y = bounds.height - 1;
+        } else {
+            y = e.clientY - bounds.top;
+        }
+        this._pos = {x:x, y:y};
+    },
+
+    // ===== PUBLIC METHODS =====
+
+    grab: function () {
+        var c = this._target;
+
+        if (isTouchDevice) {
+            c.addEventListener('touchstart', this._eventHandlers.mousedown);
+            c.addEventListener('touchend', this._eventHandlers.mouseup);
+            c.addEventListener('touchmove', this._eventHandlers.mousemove);
+        }
+        c.addEventListener('mousedown', this._eventHandlers.mousedown);
+        c.addEventListener('mouseup', this._eventHandlers.mouseup);
+        c.addEventListener('mousemove', this._eventHandlers.mousemove);
+        c.addEventListener('wheel', this._eventHandlers.mousewheel);
+
+        /* Prevent middle-click pasting (see above for why we bind to document) */
+        document.addEventListener('click', this._eventHandlers.mousedisable);
+
+        /* preventDefault() on mousedown doesn't stop this event for some
+           reason so we have to explicitly block it */
+        c.addEventListener('contextmenu', this._eventHandlers.mousedisable);
+    },
+
+    ungrab: function () {
+        var c = this._target;
+
+        this._resetWheelStepTimers();
+
+        if (isTouchDevice) {
+            c.removeEventListener('touchstart', this._eventHandlers.mousedown);
+            c.removeEventListener('touchend', this._eventHandlers.mouseup);
+            c.removeEventListener('touchmove', this._eventHandlers.mousemove);
+        }
+        c.removeEventListener('mousedown', this._eventHandlers.mousedown);
+        c.removeEventListener('mouseup', this._eventHandlers.mouseup);
+        c.removeEventListener('mousemove', this._eventHandlers.mousemove);
+        c.removeEventListener('wheel', this._eventHandlers.mousewheel);
+
+        document.removeEventListener('click', this._eventHandlers.mousedisable);
+
+        c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
+    }
+};

+ 167 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/util.js

@@ -0,0 +1,167 @@
+import KeyTable from "./keysym.js";
+import keysyms from "./keysymdef.js";
+import vkeys from "./vkeys.js";
+import fixedkeys from "./fixedkeys.js";
+import DOMKeyTable from "./domkeytable.js";
+import * as browser from "../util/browser.js";
+
+// Get 'KeyboardEvent.code', handling legacy browsers
+export function getKeycode(evt){
+    // Are we getting proper key identifiers?
+    // (unfortunately Firefox and Chrome are crappy here and gives
+    // us an empty string on some platforms, rather than leaving it
+    // undefined)
+    if (evt.code) {
+        // Mozilla isn't fully in sync with the spec yet
+        switch (evt.code) {
+            case 'OSLeft': return 'MetaLeft';
+            case 'OSRight': return 'MetaRight';
+        }
+
+        return evt.code;
+    }
+
+    // The de-facto standard is to use Windows Virtual-Key codes
+    // in the 'keyCode' field for non-printable characters. However
+    // Webkit sets it to the same as charCode in 'keypress' events.
+    if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
+        var code = vkeys[evt.keyCode];
+
+        // macOS has messed up this code for some reason
+        if (browser.isMac() && (code === 'ContextMenu')) {
+            code = 'MetaRight';
+        }
+
+        // The keyCode doesn't distinguish between left and right
+        // for the standard modifiers
+        if (evt.location === 2) {
+            switch (code) {
+                case 'ShiftLeft': return 'ShiftRight';
+                case 'ControlLeft': return 'ControlRight';
+                case 'AltLeft': return 'AltRight';
+            }
+        }
+
+        // Nor a bunch of the numpad keys
+        if (evt.location === 3) {
+            switch (code) {
+                case 'Delete': return 'NumpadDecimal';
+                case 'Insert': return 'Numpad0';
+                case 'End': return 'Numpad1';
+                case 'ArrowDown': return 'Numpad2';
+                case 'PageDown': return 'Numpad3';
+                case 'ArrowLeft': return 'Numpad4';
+                case 'ArrowRight': return 'Numpad6';
+                case 'Home': return 'Numpad7';
+                case 'ArrowUp': return 'Numpad8';
+                case 'PageUp': return 'Numpad9';
+                case 'Enter': return 'NumpadEnter';
+            }
+        }
+
+        return code;
+    }
+
+    return 'Unidentified';
+}
+
+// Get 'KeyboardEvent.key', handling legacy browsers
+export function getKey(evt) {
+    // Are we getting a proper key value?
+    if (evt.key !== undefined) {
+        // IE and Edge use some ancient version of the spec
+        // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
+        switch (evt.key) {
+            case 'Spacebar': return ' ';
+            case 'Esc': return 'Escape';
+            case 'Scroll': return 'ScrollLock';
+            case 'Win': return 'Meta';
+            case 'Apps': return 'ContextMenu';
+            case 'Up': return 'ArrowUp';
+            case 'Left': return 'ArrowLeft';
+            case 'Right': return 'ArrowRight';
+            case 'Down': return 'ArrowDown';
+            case 'Del': return 'Delete';
+            case 'Divide': return '/';
+            case 'Multiply': return '*';
+            case 'Subtract': return '-';
+            case 'Add': return '+';
+            case 'Decimal': return evt.char;
+        }
+
+        // Mozilla isn't fully in sync with the spec yet
+        switch (evt.key) {
+            case 'OS': return 'Meta';
+        }
+
+        // iOS leaks some OS names
+        switch (evt.key) {
+            case 'UIKeyInputUpArrow': return 'ArrowUp';
+            case 'UIKeyInputDownArrow': return 'ArrowDown';
+            case 'UIKeyInputLeftArrow': return 'ArrowLeft';
+            case 'UIKeyInputRightArrow': return 'ArrowRight';
+            case 'UIKeyInputEscape': return 'Escape';
+        }
+
+        // IE and Edge have broken handling of AltGraph so we cannot
+        // trust them for printable characters
+        if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
+            return evt.key;
+        }
+    }
+
+    // Try to deduce it based on the physical key
+    var code = getKeycode(evt);
+    if (code in fixedkeys) {
+        return fixedkeys[code];
+    }
+
+    // If that failed, then see if we have a printable character
+    if (evt.charCode) {
+        return String.fromCharCode(evt.charCode);
+    }
+
+    // At this point we have nothing left to go on
+    return 'Unidentified';
+}
+
+// Get the most reliable keysym value we can get from a key event
+export function getKeysym(evt){
+    var key = getKey(evt);
+
+    if (key === 'Unidentified') {
+        return null;
+    }
+
+    // First look up special keys
+    if (key in DOMKeyTable) {
+        var location = evt.location;
+
+        // Safari screws up location for the right cmd key
+        if ((key === 'Meta') && (location === 0)) {
+            location = 2;
+        }
+
+        if ((location === undefined) || (location > 3)) {
+            location = 0;
+        }
+
+        return DOMKeyTable[key][location];
+    }
+
+    // Now we need to look at the Unicode symbol instead
+
+    var codepoint;
+
+    // Special key? (FIXME: Should have been caught earlier)
+    if (key.length !== 1) {
+        return null;
+    }
+
+    codepoint = key.charCodeAt();
+    if (codepoint) {
+        return keysyms.lookup(codepoint);
+    }
+
+    return null;
+}

+ 116 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/vkeys.js

@@ -0,0 +1,116 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+/*
+ * Mapping between Microsoft® Windows® Virtual-Key codes and
+ * HTML key codes.
+ */
+
+export default {
+    0x08: 'Backspace',
+    0x09: 'Tab',
+    0x0a: 'NumpadClear',
+    0x0d: 'Enter',
+    0x10: 'ShiftLeft',
+    0x11: 'ControlLeft',
+    0x12: 'AltLeft',
+    0x13: 'Pause',
+    0x14: 'CapsLock',
+    0x15: 'Lang1',
+    0x19: 'Lang2',
+    0x1b: 'Escape',
+    0x1c: 'Convert',
+    0x1d: 'NonConvert',
+    0x20: 'Space',
+    0x21: 'PageUp',
+    0x22: 'PageDown',
+    0x23: 'End',
+    0x24: 'Home',
+    0x25: 'ArrowLeft',
+    0x26: 'ArrowUp',
+    0x27: 'ArrowRight',
+    0x28: 'ArrowDown',
+    0x29: 'Select',
+    0x2c: 'PrintScreen',
+    0x2d: 'Insert',
+    0x2e: 'Delete',
+    0x2f: 'Help',
+    0x30: 'Digit0',
+    0x31: 'Digit1',
+    0x32: 'Digit2',
+    0x33: 'Digit3',
+    0x34: 'Digit4',
+    0x35: 'Digit5',
+    0x36: 'Digit6',
+    0x37: 'Digit7',
+    0x38: 'Digit8',
+    0x39: 'Digit9',
+    0x5b: 'MetaLeft',
+    0x5c: 'MetaRight',
+    0x5d: 'ContextMenu',
+    0x5f: 'Sleep',
+    0x60: 'Numpad0',
+    0x61: 'Numpad1',
+    0x62: 'Numpad2',
+    0x63: 'Numpad3',
+    0x64: 'Numpad4',
+    0x65: 'Numpad5',
+    0x66: 'Numpad6',
+    0x67: 'Numpad7',
+    0x68: 'Numpad8',
+    0x69: 'Numpad9',
+    0x6a: 'NumpadMultiply',
+    0x6b: 'NumpadAdd',
+    0x6c: 'NumpadDecimal',
+    0x6d: 'NumpadSubtract',
+    0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows
+    0x6f: 'NumpadDivide',
+    0x70: 'F1',
+    0x71: 'F2',
+    0x72: 'F3',
+    0x73: 'F4',
+    0x74: 'F5',
+    0x75: 'F6',
+    0x76: 'F7',
+    0x77: 'F8',
+    0x78: 'F9',
+    0x79: 'F10',
+    0x7a: 'F11',
+    0x7b: 'F12',
+    0x7c: 'F13',
+    0x7d: 'F14',
+    0x7e: 'F15',
+    0x7f: 'F16',
+    0x80: 'F17',
+    0x81: 'F18',
+    0x82: 'F19',
+    0x83: 'F20',
+    0x84: 'F21',
+    0x85: 'F22',
+    0x86: 'F23',
+    0x87: 'F24',
+    0x90: 'NumLock',
+    0x91: 'ScrollLock',
+    0xa6: 'BrowserBack',
+    0xa7: 'BrowserForward',
+    0xa8: 'BrowserRefresh',
+    0xa9: 'BrowserStop',
+    0xaa: 'BrowserSearch',
+    0xab: 'BrowserFavorites',
+    0xac: 'BrowserHome',
+    0xad: 'AudioVolumeMute',
+    0xae: 'AudioVolumeDown',
+    0xaf: 'AudioVolumeUp',
+    0xb0: 'MediaTrackNext',
+    0xb1: 'MediaTrackPrevious',
+    0xb2: 'MediaStop',
+    0xb3: 'MediaPlayPause',
+    0xb4: 'LaunchMail',
+    0xb5: 'MediaSelect',
+    0xb6: 'LaunchApp1',
+    0xb7: 'LaunchApp2',
+    0xe1: 'AltRight', // Only when it is AltGraph
+};

+ 171 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/input/xtscancodes.js

@@ -0,0 +1,171 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-05-31 16:20
+ * Database checksum sha256(92fd165507f2a3b8c5b3fa56e425d45788dbcb98cf067a307527d91ce22cab94)
+ * To re-generate, run:
+ *   keymap-gen --lang=js code-map keymaps.csv html atset1
+*/
+export default {
+  "Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */
+  "AltLeft": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */
+  "AltRight": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */
+  "ArrowDown": 0xe050, /* html:ArrowDown (ArrowDown) -> linux:108 (KEY_DOWN) -> atset1:57424 */
+  "ArrowLeft": 0xe04b, /* html:ArrowLeft (ArrowLeft) -> linux:105 (KEY_LEFT) -> atset1:57419 */
+  "ArrowRight": 0xe04d, /* html:ArrowRight (ArrowRight) -> linux:106 (KEY_RIGHT) -> atset1:57421 */
+  "ArrowUp": 0xe048, /* html:ArrowUp (ArrowUp) -> linux:103 (KEY_UP) -> atset1:57416 */
+  "AudioVolumeDown": 0xe02e, /* html:AudioVolumeDown (AudioVolumeDown) -> linux:114 (KEY_VOLUMEDOWN) -> atset1:57390 */
+  "AudioVolumeMute": 0xe020, /* html:AudioVolumeMute (AudioVolumeMute) -> linux:113 (KEY_MUTE) -> atset1:57376 */
+  "AudioVolumeUp": 0xe030, /* html:AudioVolumeUp (AudioVolumeUp) -> linux:115 (KEY_VOLUMEUP) -> atset1:57392 */
+  "Backquote": 0x29, /* html:Backquote (Backquote) -> linux:41 (KEY_GRAVE) -> atset1:41 */
+  "Backslash": 0x2b, /* html:Backslash (Backslash) -> linux:43 (KEY_BACKSLASH) -> atset1:43 */
+  "Backspace": 0xe, /* html:Backspace (Backspace) -> linux:14 (KEY_BACKSPACE) -> atset1:14 */
+  "BracketLeft": 0x1a, /* html:BracketLeft (BracketLeft) -> linux:26 (KEY_LEFTBRACE) -> atset1:26 */
+  "BracketRight": 0x1b, /* html:BracketRight (BracketRight) -> linux:27 (KEY_RIGHTBRACE) -> atset1:27 */
+  "BrowserBack": 0xe06a, /* html:BrowserBack (BrowserBack) -> linux:158 (KEY_BACK) -> atset1:57450 */
+  "BrowserFavorites": 0xe066, /* html:BrowserFavorites (BrowserFavorites) -> linux:156 (KEY_BOOKMARKS) -> atset1:57446 */
+  "BrowserForward": 0xe069, /* html:BrowserForward (BrowserForward) -> linux:159 (KEY_FORWARD) -> atset1:57449 */
+  "BrowserHome": 0xe032, /* html:BrowserHome (BrowserHome) -> linux:172 (KEY_HOMEPAGE) -> atset1:57394 */
+  "BrowserRefresh": 0xe067, /* html:BrowserRefresh (BrowserRefresh) -> linux:173 (KEY_REFRESH) -> atset1:57447 */
+  "BrowserSearch": 0xe065, /* html:BrowserSearch (BrowserSearch) -> linux:217 (KEY_SEARCH) -> atset1:57445 */
+  "BrowserStop": 0xe068, /* html:BrowserStop (BrowserStop) -> linux:128 (KEY_STOP) -> atset1:57448 */
+  "CapsLock": 0x3a, /* html:CapsLock (CapsLock) -> linux:58 (KEY_CAPSLOCK) -> atset1:58 */
+  "Comma": 0x33, /* html:Comma (Comma) -> linux:51 (KEY_COMMA) -> atset1:51 */
+  "ContextMenu": 0xe05d, /* html:ContextMenu (ContextMenu) -> linux:127 (KEY_COMPOSE) -> atset1:57437 */
+  "ControlLeft": 0x1d, /* html:ControlLeft (ControlLeft) -> linux:29 (KEY_LEFTCTRL) -> atset1:29 */
+  "ControlRight": 0xe01d, /* html:ControlRight (ControlRight) -> linux:97 (KEY_RIGHTCTRL) -> atset1:57373 */
+  "Convert": 0x79, /* html:Convert (Convert) -> linux:92 (KEY_HENKAN) -> atset1:121 */
+  "Copy": 0xe078, /* html:Copy (Copy) -> linux:133 (KEY_COPY) -> atset1:57464 */
+  "Cut": 0xe03c, /* html:Cut (Cut) -> linux:137 (KEY_CUT) -> atset1:57404 */
+  "Delete": 0xe053, /* html:Delete (Delete) -> linux:111 (KEY_DELETE) -> atset1:57427 */
+  "Digit0": 0xb, /* html:Digit0 (Digit0) -> linux:11 (KEY_0) -> atset1:11 */
+  "Digit1": 0x2, /* html:Digit1 (Digit1) -> linux:2 (KEY_1) -> atset1:2 */
+  "Digit2": 0x3, /* html:Digit2 (Digit2) -> linux:3 (KEY_2) -> atset1:3 */
+  "Digit3": 0x4, /* html:Digit3 (Digit3) -> linux:4 (KEY_3) -> atset1:4 */
+  "Digit4": 0x5, /* html:Digit4 (Digit4) -> linux:5 (KEY_4) -> atset1:5 */
+  "Digit5": 0x6, /* html:Digit5 (Digit5) -> linux:6 (KEY_5) -> atset1:6 */
+  "Digit6": 0x7, /* html:Digit6 (Digit6) -> linux:7 (KEY_6) -> atset1:7 */
+  "Digit7": 0x8, /* html:Digit7 (Digit7) -> linux:8 (KEY_7) -> atset1:8 */
+  "Digit8": 0x9, /* html:Digit8 (Digit8) -> linux:9 (KEY_8) -> atset1:9 */
+  "Digit9": 0xa, /* html:Digit9 (Digit9) -> linux:10 (KEY_9) -> atset1:10 */
+  "Eject": 0xe07d, /* html:Eject (Eject) -> linux:162 (KEY_EJECTCLOSECD) -> atset1:57469 */
+  "End": 0xe04f, /* html:End (End) -> linux:107 (KEY_END) -> atset1:57423 */
+  "Enter": 0x1c, /* html:Enter (Enter) -> linux:28 (KEY_ENTER) -> atset1:28 */
+  "Equal": 0xd, /* html:Equal (Equal) -> linux:13 (KEY_EQUAL) -> atset1:13 */
+  "Escape": 0x1, /* html:Escape (Escape) -> linux:1 (KEY_ESC) -> atset1:1 */
+  "F1": 0x3b, /* html:F1 (F1) -> linux:59 (KEY_F1) -> atset1:59 */
+  "F10": 0x44, /* html:F10 (F10) -> linux:68 (KEY_F10) -> atset1:68 */
+  "F11": 0x57, /* html:F11 (F11) -> linux:87 (KEY_F11) -> atset1:87 */
+  "F12": 0x58, /* html:F12 (F12) -> linux:88 (KEY_F12) -> atset1:88 */
+  "F13": 0x5d, /* html:F13 (F13) -> linux:183 (KEY_F13) -> atset1:93 */
+  "F14": 0x5e, /* html:F14 (F14) -> linux:184 (KEY_F14) -> atset1:94 */
+  "F15": 0x5f, /* html:F15 (F15) -> linux:185 (KEY_F15) -> atset1:95 */
+  "F16": 0x55, /* html:F16 (F16) -> linux:186 (KEY_F16) -> atset1:85 */
+  "F17": 0xe003, /* html:F17 (F17) -> linux:187 (KEY_F17) -> atset1:57347 */
+  "F18": 0xe077, /* html:F18 (F18) -> linux:188 (KEY_F18) -> atset1:57463 */
+  "F19": 0xe004, /* html:F19 (F19) -> linux:189 (KEY_F19) -> atset1:57348 */
+  "F2": 0x3c, /* html:F2 (F2) -> linux:60 (KEY_F2) -> atset1:60 */
+  "F20": 0x5a, /* html:F20 (F20) -> linux:190 (KEY_F20) -> atset1:90 */
+  "F21": 0x74, /* html:F21 (F21) -> linux:191 (KEY_F21) -> atset1:116 */
+  "F22": 0xe079, /* html:F22 (F22) -> linux:192 (KEY_F22) -> atset1:57465 */
+  "F23": 0x6d, /* html:F23 (F23) -> linux:193 (KEY_F23) -> atset1:109 */
+  "F24": 0x6f, /* html:F24 (F24) -> linux:194 (KEY_F24) -> atset1:111 */
+  "F3": 0x3d, /* html:F3 (F3) -> linux:61 (KEY_F3) -> atset1:61 */
+  "F4": 0x3e, /* html:F4 (F4) -> linux:62 (KEY_F4) -> atset1:62 */
+  "F5": 0x3f, /* html:F5 (F5) -> linux:63 (KEY_F5) -> atset1:63 */
+  "F6": 0x40, /* html:F6 (F6) -> linux:64 (KEY_F6) -> atset1:64 */
+  "F7": 0x41, /* html:F7 (F7) -> linux:65 (KEY_F7) -> atset1:65 */
+  "F8": 0x42, /* html:F8 (F8) -> linux:66 (KEY_F8) -> atset1:66 */
+  "F9": 0x43, /* html:F9 (F9) -> linux:67 (KEY_F9) -> atset1:67 */
+  "Find": 0xe041, /* html:Find (Find) -> linux:136 (KEY_FIND) -> atset1:57409 */
+  "Help": 0xe075, /* html:Help (Help) -> linux:138 (KEY_HELP) -> atset1:57461 */
+  "Hiragana": 0x77, /* html:Hiragana (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */
+  "Home": 0xe047, /* html:Home (Home) -> linux:102 (KEY_HOME) -> atset1:57415 */
+  "Insert": 0xe052, /* html:Insert (Insert) -> linux:110 (KEY_INSERT) -> atset1:57426 */
+  "IntlBackslash": 0x56, /* html:IntlBackslash (IntlBackslash) -> linux:86 (KEY_102ND) -> atset1:86 */
+  "IntlRo": 0x73, /* html:IntlRo (IntlRo) -> linux:89 (KEY_RO) -> atset1:115 */
+  "IntlYen": 0x7d, /* html:IntlYen (IntlYen) -> linux:124 (KEY_YEN) -> atset1:125 */
+  "KanaMode": 0x70, /* html:KanaMode (KanaMode) -> linux:93 (KEY_KATAKANAHIRAGANA) -> atset1:112 */
+  "Katakana": 0x78, /* html:Katakana (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */
+  "KeyA": 0x1e, /* html:KeyA (KeyA) -> linux:30 (KEY_A) -> atset1:30 */
+  "KeyB": 0x30, /* html:KeyB (KeyB) -> linux:48 (KEY_B) -> atset1:48 */
+  "KeyC": 0x2e, /* html:KeyC (KeyC) -> linux:46 (KEY_C) -> atset1:46 */
+  "KeyD": 0x20, /* html:KeyD (KeyD) -> linux:32 (KEY_D) -> atset1:32 */
+  "KeyE": 0x12, /* html:KeyE (KeyE) -> linux:18 (KEY_E) -> atset1:18 */
+  "KeyF": 0x21, /* html:KeyF (KeyF) -> linux:33 (KEY_F) -> atset1:33 */
+  "KeyG": 0x22, /* html:KeyG (KeyG) -> linux:34 (KEY_G) -> atset1:34 */
+  "KeyH": 0x23, /* html:KeyH (KeyH) -> linux:35 (KEY_H) -> atset1:35 */
+  "KeyI": 0x17, /* html:KeyI (KeyI) -> linux:23 (KEY_I) -> atset1:23 */
+  "KeyJ": 0x24, /* html:KeyJ (KeyJ) -> linux:36 (KEY_J) -> atset1:36 */
+  "KeyK": 0x25, /* html:KeyK (KeyK) -> linux:37 (KEY_K) -> atset1:37 */
+  "KeyL": 0x26, /* html:KeyL (KeyL) -> linux:38 (KEY_L) -> atset1:38 */
+  "KeyM": 0x32, /* html:KeyM (KeyM) -> linux:50 (KEY_M) -> atset1:50 */
+  "KeyN": 0x31, /* html:KeyN (KeyN) -> linux:49 (KEY_N) -> atset1:49 */
+  "KeyO": 0x18, /* html:KeyO (KeyO) -> linux:24 (KEY_O) -> atset1:24 */
+  "KeyP": 0x19, /* html:KeyP (KeyP) -> linux:25 (KEY_P) -> atset1:25 */
+  "KeyQ": 0x10, /* html:KeyQ (KeyQ) -> linux:16 (KEY_Q) -> atset1:16 */
+  "KeyR": 0x13, /* html:KeyR (KeyR) -> linux:19 (KEY_R) -> atset1:19 */
+  "KeyS": 0x1f, /* html:KeyS (KeyS) -> linux:31 (KEY_S) -> atset1:31 */
+  "KeyT": 0x14, /* html:KeyT (KeyT) -> linux:20 (KEY_T) -> atset1:20 */
+  "KeyU": 0x16, /* html:KeyU (KeyU) -> linux:22 (KEY_U) -> atset1:22 */
+  "KeyV": 0x2f, /* html:KeyV (KeyV) -> linux:47 (KEY_V) -> atset1:47 */
+  "KeyW": 0x11, /* html:KeyW (KeyW) -> linux:17 (KEY_W) -> atset1:17 */
+  "KeyX": 0x2d, /* html:KeyX (KeyX) -> linux:45 (KEY_X) -> atset1:45 */
+  "KeyY": 0x15, /* html:KeyY (KeyY) -> linux:21 (KEY_Y) -> atset1:21 */
+  "KeyZ": 0x2c, /* html:KeyZ (KeyZ) -> linux:44 (KEY_Z) -> atset1:44 */
+  "Lang3": 0x78, /* html:Lang3 (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */
+  "Lang4": 0x77, /* html:Lang4 (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */
+  "Lang5": 0x76, /* html:Lang5 (Lang5) -> linux:85 (KEY_ZENKAKUHANKAKU) -> atset1:118 */
+  "LaunchApp1": 0xe06b, /* html:LaunchApp1 (LaunchApp1) -> linux:157 (KEY_COMPUTER) -> atset1:57451 */
+  "LaunchApp2": 0xe021, /* html:LaunchApp2 (LaunchApp2) -> linux:140 (KEY_CALC) -> atset1:57377 */
+  "LaunchMail": 0xe06c, /* html:LaunchMail (LaunchMail) -> linux:155 (KEY_MAIL) -> atset1:57452 */
+  "MediaPlayPause": 0xe022, /* html:MediaPlayPause (MediaPlayPause) -> linux:164 (KEY_PLAYPAUSE) -> atset1:57378 */
+  "MediaSelect": 0xe06d, /* html:MediaSelect (MediaSelect) -> linux:226 (KEY_MEDIA) -> atset1:57453 */
+  "MediaStop": 0xe024, /* html:MediaStop (MediaStop) -> linux:166 (KEY_STOPCD) -> atset1:57380 */
+  "MediaTrackNext": 0xe019, /* html:MediaTrackNext (MediaTrackNext) -> linux:163 (KEY_NEXTSONG) -> atset1:57369 */
+  "MediaTrackPrevious": 0xe010, /* html:MediaTrackPrevious (MediaTrackPrevious) -> linux:165 (KEY_PREVIOUSSONG) -> atset1:57360 */
+  "MetaLeft": 0xe05b, /* html:MetaLeft (MetaLeft) -> linux:125 (KEY_LEFTMETA) -> atset1:57435 */
+  "MetaRight": 0xe05c, /* html:MetaRight (MetaRight) -> linux:126 (KEY_RIGHTMETA) -> atset1:57436 */
+  "Minus": 0xc, /* html:Minus (Minus) -> linux:12 (KEY_MINUS) -> atset1:12 */
+  "NonConvert": 0x7b, /* html:NonConvert (NonConvert) -> linux:94 (KEY_MUHENKAN) -> atset1:123 */
+  "NumLock": 0x45, /* html:NumLock (NumLock) -> linux:69 (KEY_NUMLOCK) -> atset1:69 */
+  "Numpad0": 0x52, /* html:Numpad0 (Numpad0) -> linux:82 (KEY_KP0) -> atset1:82 */
+  "Numpad1": 0x4f, /* html:Numpad1 (Numpad1) -> linux:79 (KEY_KP1) -> atset1:79 */
+  "Numpad2": 0x50, /* html:Numpad2 (Numpad2) -> linux:80 (KEY_KP2) -> atset1:80 */
+  "Numpad3": 0x51, /* html:Numpad3 (Numpad3) -> linux:81 (KEY_KP3) -> atset1:81 */
+  "Numpad4": 0x4b, /* html:Numpad4 (Numpad4) -> linux:75 (KEY_KP4) -> atset1:75 */
+  "Numpad5": 0x4c, /* html:Numpad5 (Numpad5) -> linux:76 (KEY_KP5) -> atset1:76 */
+  "Numpad6": 0x4d, /* html:Numpad6 (Numpad6) -> linux:77 (KEY_KP6) -> atset1:77 */
+  "Numpad7": 0x47, /* html:Numpad7 (Numpad7) -> linux:71 (KEY_KP7) -> atset1:71 */
+  "Numpad8": 0x48, /* html:Numpad8 (Numpad8) -> linux:72 (KEY_KP8) -> atset1:72 */
+  "Numpad9": 0x49, /* html:Numpad9 (Numpad9) -> linux:73 (KEY_KP9) -> atset1:73 */
+  "NumpadAdd": 0x4e, /* html:NumpadAdd (NumpadAdd) -> linux:78 (KEY_KPPLUS) -> atset1:78 */
+  "NumpadComma": 0x7e, /* html:NumpadComma (NumpadComma) -> linux:121 (KEY_KPCOMMA) -> atset1:126 */
+  "NumpadDecimal": 0x53, /* html:NumpadDecimal (NumpadDecimal) -> linux:83 (KEY_KPDOT) -> atset1:83 */
+  "NumpadDivide": 0xe035, /* html:NumpadDivide (NumpadDivide) -> linux:98 (KEY_KPSLASH) -> atset1:57397 */
+  "NumpadEnter": 0xe01c, /* html:NumpadEnter (NumpadEnter) -> linux:96 (KEY_KPENTER) -> atset1:57372 */
+  "NumpadEqual": 0x59, /* html:NumpadEqual (NumpadEqual) -> linux:117 (KEY_KPEQUAL) -> atset1:89 */
+  "NumpadMultiply": 0x37, /* html:NumpadMultiply (NumpadMultiply) -> linux:55 (KEY_KPASTERISK) -> atset1:55 */
+  "NumpadParenLeft": 0xe076, /* html:NumpadParenLeft (NumpadParenLeft) -> linux:179 (KEY_KPLEFTPAREN) -> atset1:57462 */
+  "NumpadParenRight": 0xe07b, /* html:NumpadParenRight (NumpadParenRight) -> linux:180 (KEY_KPRIGHTPAREN) -> atset1:57467 */
+  "NumpadSubtract": 0x4a, /* html:NumpadSubtract (NumpadSubtract) -> linux:74 (KEY_KPMINUS) -> atset1:74 */
+  "Open": 0x64, /* html:Open (Open) -> linux:134 (KEY_OPEN) -> atset1:100 */
+  "PageDown": 0xe051, /* html:PageDown (PageDown) -> linux:109 (KEY_PAGEDOWN) -> atset1:57425 */
+  "PageUp": 0xe049, /* html:PageUp (PageUp) -> linux:104 (KEY_PAGEUP) -> atset1:57417 */
+  "Paste": 0x65, /* html:Paste (Paste) -> linux:135 (KEY_PASTE) -> atset1:101 */
+  "Pause": 0xe046, /* html:Pause (Pause) -> linux:119 (KEY_PAUSE) -> atset1:57414 */
+  "Period": 0x34, /* html:Period (Period) -> linux:52 (KEY_DOT) -> atset1:52 */
+  "Power": 0xe05e, /* html:Power (Power) -> linux:116 (KEY_POWER) -> atset1:57438 */
+  "PrintScreen": 0x54, /* html:PrintScreen (PrintScreen) -> linux:99 (KEY_SYSRQ) -> atset1:84 */
+  "Props": 0xe006, /* html:Props (Props) -> linux:130 (KEY_PROPS) -> atset1:57350 */
+  "Quote": 0x28, /* html:Quote (Quote) -> linux:40 (KEY_APOSTROPHE) -> atset1:40 */
+  "ScrollLock": 0x46, /* html:ScrollLock (ScrollLock) -> linux:70 (KEY_SCROLLLOCK) -> atset1:70 */
+  "Semicolon": 0x27, /* html:Semicolon (Semicolon) -> linux:39 (KEY_SEMICOLON) -> atset1:39 */
+  "ShiftLeft": 0x2a, /* html:ShiftLeft (ShiftLeft) -> linux:42 (KEY_LEFTSHIFT) -> atset1:42 */
+  "ShiftRight": 0x36, /* html:ShiftRight (ShiftRight) -> linux:54 (KEY_RIGHTSHIFT) -> atset1:54 */
+  "Slash": 0x35, /* html:Slash (Slash) -> linux:53 (KEY_SLASH) -> atset1:53 */
+  "Sleep": 0xe05f, /* html:Sleep (Sleep) -> linux:142 (KEY_SLEEP) -> atset1:57439 */
+  "Space": 0x39, /* html:Space (Space) -> linux:57 (KEY_SPACE) -> atset1:57 */
+  "Suspend": 0xe025, /* html:Suspend (Suspend) -> linux:205 (KEY_SUSPEND) -> atset1:57381 */
+  "Tab": 0xf, /* html:Tab (Tab) -> linux:15 (KEY_TAB) -> atset1:15 */
+  "Undo": 0xe007, /* html:Undo (Undo) -> linux:131 (KEY_UNDO) -> atset1:57351 */
+  "WakeUp": 0xe063, /* html:WakeUp (WakeUp) -> linux:143 (KEY_WAKEUP) -> atset1:57443 */
+};

+ 2540 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/rfb.js

@@ -0,0 +1,2540 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2017 Samuel Mannehed for Cendio AB
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ *
+ * TIGHT decoder portion:
+ * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
+ */
+
+import * as Log from './util/logging.js';
+import { decodeUTF8 } from './util/strings.js';
+import { supportsCursorURIs, isTouchDevice } from './util/browser.js';
+import EventTargetMixin from './util/eventtarget.js';
+import Display from "./display.js";
+import Keyboard from "./input/keyboard.js";
+import Mouse from "./input/mouse.js";
+import Websock from "./websock.js";
+import DES from "./des.js";
+import KeyTable from "./input/keysym.js";
+import XtScancode from "./input/xtscancodes.js";
+import Inflator from "./inflator.js";
+import { encodings, encodingName } from "./encodings.js";
+import "./util/polyfill.js";
+
+/*jslint white: false, browser: true */
+/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */
+
+// How many seconds to wait for a disconnect to finish
+var DISCONNECT_TIMEOUT = 3;
+
+export default function RFB(target, url, options) {
+    if (!target) {
+        throw Error("Must specify target");
+    }
+    if (!url) {
+        throw Error("Must specify URL");
+    }
+
+    this._target = target;
+    this._url = url;
+
+    // Connection details
+    options = options || {};
+    this._rfb_credentials = options.credentials || {};
+    this._shared = 'shared' in options ? !!options.shared : true;
+    this._repeaterID = options.repeaterID || '';
+
+    // Internal state
+    this._rfb_connection_state = '';
+    this._rfb_init_state = '';
+    this._rfb_auth_scheme = '';
+    this._rfb_clean_disconnect = true;
+
+    // Server capabilities
+    this._rfb_version = 0;
+    this._rfb_max_version = 3.8;
+    this._rfb_tightvnc = false;
+    this._rfb_xvp_ver = 0;
+
+    this._fb_width = 0;
+    this._fb_height = 0;
+
+    this._fb_name = "";
+
+    this._capabilities = { power: false };
+
+    this._supportsFence = false;
+
+    this._supportsContinuousUpdates = false;
+    this._enabledContinuousUpdates = false;
+
+    this._supportsSetDesktopSize = false;
+    this._screen_id = 0;
+    this._screen_flags = 0;
+
+    this._qemuExtKeyEventSupported = false;
+
+    // Internal objects
+    this._sock = null;              // Websock object
+    this._display = null;           // Display object
+    this._flushing = false;         // Display flushing state
+    this._keyboard = null;          // Keyboard input handler object
+    this._mouse = null;             // Mouse input handler object
+
+    // Timers
+    this._disconnTimer = null;      // disconnection timer
+    this._resizeTimeout = null;     // resize rate limiting
+
+    // Decoder states and stats
+    this._encHandlers = {};
+    this._encStats = {};
+
+    this._FBU = {
+        rects: 0,
+        subrects: 0,            // RRE and HEXTILE
+        lines: 0,               // RAW
+        tiles: 0,               // HEXTILE
+        bytes: 0,
+        x: 0,
+        y: 0,
+        width: 0,
+        height: 0,
+        encoding: 0,
+        subencoding: -1,
+        background: null,
+        zlibs: []               // TIGHT zlib streams
+    };
+    for (var i = 0; i < 4; i++) {
+        this._FBU.zlibs[i] = new Inflator();
+    }
+
+    this._destBuff = null;
+    this._paletteBuff = new Uint8Array(1024);  // 256 * 4 (max palette size * max bytes-per-pixel)
+
+    this._rre_chunk_sz = 100;
+
+    this._timing = {
+        last_fbu: 0,
+        fbu_total: 0,
+        fbu_total_cnt: 0,
+        full_fbu_total: 0,
+        full_fbu_cnt: 0,
+
+        fbu_rt_start: 0,
+        fbu_rt_total: 0,
+        fbu_rt_cnt: 0,
+        pixels: 0
+    };
+
+    // Mouse state
+    this._mouse_buttonMask = 0;
+    this._mouse_arr = [];
+    this._viewportDragging = false;
+    this._viewportDragPos = {};
+    this._viewportHasMoved = false;
+
+    // Bound event handlers
+    this._eventHandlers = {
+        focusCanvas: this._focusCanvas.bind(this),
+        windowResize: this._windowResize.bind(this),
+    };
+
+    // main setup
+    Log.Debug(">> RFB.constructor");
+
+    // Create DOM elements
+    this._screen = document.createElement('div');
+    this._screen.style.display = 'flex';
+    this._screen.style.width = '100%';
+    this._screen.style.height = '100%';
+    this._screen.style.overflow = 'auto';
+    this._screen.style.backgroundColor = 'rgb(40, 40, 40)';
+    this._canvas = document.createElement('canvas');
+    this._canvas.style.margin = 'auto';
+    // Some browsers add an outline on focus
+    this._canvas.style.outline = 'none';
+    // IE miscalculates width without this :(
+    this._canvas.style.flexShrink = '0';
+    this._canvas.width = 0;
+    this._canvas.height = 0;
+    this._canvas.tabIndex = -1;
+    this._screen.appendChild(this._canvas);
+
+    // populate encHandlers with bound versions
+    this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this);
+    this._encHandlers[encodings.encodingCopyRect] = RFB.encodingHandlers.COPYRECT.bind(this);
+    this._encHandlers[encodings.encodingRRE] = RFB.encodingHandlers.RRE.bind(this);
+    this._encHandlers[encodings.encodingHextile] = RFB.encodingHandlers.HEXTILE.bind(this);
+    this._encHandlers[encodings.encodingTight] = RFB.encodingHandlers.TIGHT.bind(this);
+
+    this._encHandlers[encodings.pseudoEncodingDesktopSize] = RFB.encodingHandlers.DesktopSize.bind(this);
+    this._encHandlers[encodings.pseudoEncodingLastRect] = RFB.encodingHandlers.last_rect.bind(this);
+    this._encHandlers[encodings.pseudoEncodingCursor] = RFB.encodingHandlers.Cursor.bind(this);
+    this._encHandlers[encodings.pseudoEncodingQEMUExtendedKeyEvent] = RFB.encodingHandlers.QEMUExtendedKeyEvent.bind(this);
+    this._encHandlers[encodings.pseudoEncodingExtendedDesktopSize] = RFB.encodingHandlers.ExtendedDesktopSize.bind(this);
+
+    // NB: nothing that needs explicit teardown should be done
+    // before this point, since this can throw an exception
+    try {
+        this._display = new Display(this._canvas);
+    } catch (exc) {
+        Log.Error("Display exception: " + exc);
+        throw exc;
+    }
+    this._display.onflush = this._onFlush.bind(this);
+    this._display.clear();
+
+    this._keyboard = new Keyboard(this._canvas);
+    this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
+
+    this._mouse = new Mouse(this._canvas);
+    this._mouse.onmousebutton = this._handleMouseButton.bind(this);
+    this._mouse.onmousemove = this._handleMouseMove.bind(this);
+
+    this._sock = new Websock();
+    this._sock.on('message', this._handle_message.bind(this));
+    this._sock.on('open', function () {
+        if ((this._rfb_connection_state === 'connecting') &&
+            (this._rfb_init_state === '')) {
+            this._rfb_init_state = 'ProtocolVersion';
+            Log.Debug("Starting VNC handshake");
+        } else {
+            this._fail("Unexpected server connection while " +
+                       this._rfb_connection_state);
+        }
+    }.bind(this));
+    this._sock.on('close', function (e) {
+        Log.Debug("WebSocket on-close event");
+        var msg = "";
+        if (e.code) {
+            msg = "(code: " + e.code;
+            if (e.reason) {
+                msg += ", reason: " + e.reason;
+            }
+            msg += ")";
+        }
+        switch (this._rfb_connection_state) {
+            case 'connecting':
+                this._fail("Connection closed " + msg);
+                break;
+            case 'connected':
+                // Handle disconnects that were initiated server-side
+                this._updateConnectionState('disconnecting');
+                this._updateConnectionState('disconnected');
+                break;
+            case 'disconnecting':
+                // Normal disconnection path
+                this._updateConnectionState('disconnected');
+                break;
+            case 'disconnected':
+                this._fail("Unexpected server disconnect " +
+                           "when already disconnected " + msg);
+                break;
+            default:
+                this._fail("Unexpected server disconnect before connecting " +
+                           msg);
+                break;
+        }
+        this._sock.off('close');
+    }.bind(this));
+    this._sock.on('error', function (e) {
+        Log.Warn("WebSocket on-error event");
+    });
+
+    // Slight delay of the actual connection so that the caller has
+    // time to set up callbacks
+    setTimeout(this._updateConnectionState.bind(this, 'connecting'));
+
+    Log.Debug("<< RFB.constructor");
+};
+
+RFB.prototype = {
+    // ===== PROPERTIES =====
+
+    dragViewport: false,
+    focusOnClick: true,
+
+    _viewOnly: false,
+    get viewOnly() { return this._viewOnly; },
+    set viewOnly(viewOnly) {
+        this._viewOnly = viewOnly;
+
+        if (this._rfb_connection_state === "connecting" ||
+            this._rfb_connection_state === "connected") {
+            if (viewOnly) {
+                this._keyboard.ungrab();
+                this._mouse.ungrab();
+            } else {
+                this._keyboard.grab();
+                this._mouse.grab();
+            }
+        }
+    },
+
+    get capabilities() { return this._capabilities; },
+
+    get touchButton() { return this._mouse.touchButton; },
+    set touchButton(button) { this._mouse.touchButton = button; },
+
+    _clipViewport: false,
+    get clipViewport() { return this._clipViewport; },
+    set clipViewport(viewport) {
+        this._clipViewport = viewport;
+        this._updateClip();
+    },
+
+    _scaleViewport: false,
+    get scaleViewport() { return this._scaleViewport; },
+    set scaleViewport(scale) {
+        this._scaleViewport = scale;
+        // Scaling trumps clipping, so we may need to adjust
+        // clipping when enabling or disabling scaling
+        if (scale && this._clipViewport) {
+            this._updateClip();
+        }
+        this._updateScale();
+        if (!scale && this._clipViewport) {
+            this._updateClip();
+        }
+    },
+
+    _resizeSession: false,
+    get resizeSession() { return this._resizeSession; },
+    set resizeSession(resize) {
+        this._resizeSession = resize;
+        if (resize) {
+            this._requestRemoteResize();
+        }
+    },
+
+    // ===== PUBLIC METHODS =====
+
+    disconnect: function () {
+        this._updateConnectionState('disconnecting');
+        this._sock.off('error');
+        this._sock.off('message');
+        this._sock.off('open');
+    },
+
+    sendCredentials: function (creds) {
+        this._rfb_credentials = creds;
+        setTimeout(this._init_msg.bind(this), 0);
+    },
+
+    sendCtrlAltDel: function () {
+        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
+        Log.Info("Sending Ctrl-Alt-Del");
+
+        this.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
+        this.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
+        this.sendKey(KeyTable.XK_Delete, "Delete", true);
+        this.sendKey(KeyTable.XK_Delete, "Delete", false);
+        this.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
+        this.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
+    },
+
+    machineShutdown: function () {
+        this._xvpOp(1, 2);
+    },
+
+    machineReboot: function () {
+        this._xvpOp(1, 3);
+    },
+
+    machineReset: function () {
+        this._xvpOp(1, 4);
+    },
+
+    // Send a key press. If 'down' is not specified then send a down key
+    // followed by an up key.
+    sendKey: function (keysym, code, down) {
+        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
+
+        if (down === undefined) {
+            this.sendKey(keysym, code, true);
+            this.sendKey(keysym, code, false);
+            return;
+        }
+
+        var scancode = XtScancode[code];
+
+        if (this._qemuExtKeyEventSupported && scancode) {
+            // 0 is NoSymbol
+            keysym = keysym || 0;
+
+            Log.Info("Sending key (" + (down ? "down" : "up") + "): keysym " + keysym + ", scancode " + scancode);
+
+            RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
+        } else {
+            if (!keysym) {
+                return;
+            }
+            Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
+            RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
+        }
+    },
+
+    focus: function () {
+        this._canvas.focus();
+    },
+
+    blur: function () {
+        this._canvas.blur();
+    },
+
+    clipboardPasteFrom: function (text) {
+        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
+        RFB.messages.clientCutText(this._sock, text);
+    },
+
+    // ===== PRIVATE METHODS =====
+
+    _connect: function () {
+        Log.Debug(">> RFB.connect");
+
+        Log.Info("connecting to " + this._url);
+
+        try {
+            // WebSocket.onopen transitions to the RFB init states
+            this._sock.open(this._url, ['binary']);
+        } catch (e) {
+            if (e.name === 'SyntaxError') {
+                this._fail("Invalid host or port (" + e + ")");
+            } else {
+                this._fail("Error when opening socket (" + e + ")");
+            }
+        }
+
+        // Make our elements part of the page
+        this._target.appendChild(this._screen);
+
+        // Monitor size changes of the screen
+        // FIXME: Use ResizeObserver, or hidden overflow
+        window.addEventListener('resize', this._eventHandlers.windowResize);
+
+        // Always grab focus on some kind of click event
+        this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
+        this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
+
+        Log.Debug("<< RFB.connect");
+    },
+
+    _disconnect: function () {
+        Log.Debug(">> RFB.disconnect");
+        this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
+        this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
+        window.removeEventListener('resize', this._eventHandlers.windowResize);
+        this._keyboard.ungrab();
+        this._mouse.ungrab();
+        this._sock.close();
+        this._print_stats();
+        try {
+            this._target.removeChild(this._screen);
+        } catch (e) {
+            if (e.name === 'NotFoundError') {
+                // Some cases where the initial connection fails
+                // can disconnect before the _screen is created
+            } else {
+                throw e;
+            }
+        }
+        clearTimeout(this._resizeTimeout);
+        Log.Debug("<< RFB.disconnect");
+    },
+
+    _print_stats: function () {
+        var stats = this._encStats;
+
+        Log.Info("Encoding stats for this connection:");
+        Object.keys(stats).forEach(function (key) {
+            var s = stats[key];
+            if (s[0] + s[1] > 0) {
+                Log.Info("    " + encodingName(key) + ": " + s[0] + " rects");
+            }
+        });
+
+        Log.Info("Encoding stats since page load:");
+        Object.keys(stats).forEach(function (key) {
+            var s = stats[key];
+            Log.Info("    " + encodingName(key) + ": " + s[1] + " rects");
+        });
+    },
+
+    _focusCanvas: function(event) {
+        // Respect earlier handlers' request to not do side-effects
+        if (event.defaultPrevented) {
+            return;
+        }
+
+        if (!this.focusOnClick) {
+            return;
+        }
+
+        this.focus();
+    },
+
+    _windowResize: function (event) {
+        // If the window resized then our screen element might have
+        // as well. Update the viewport dimensions.
+        window.requestAnimationFrame(function () {
+            this._updateClip();
+            this._updateScale();
+        }.bind(this));
+
+        if (this._resizeSession) {
+            // Request changing the resolution of the remote display to
+            // the size of the local browser viewport.
+
+            // In order to not send multiple requests before the browser-resize
+            // is finished we wait 0.5 seconds before sending the request.
+            clearTimeout(this._resizeTimeout);
+            this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
+        }
+    },
+
+    // Update state of clipping in Display object, and make sure the
+    // configured viewport matches the current screen size
+    _updateClip: function () {
+        var cur_clip = this._display.clipViewport;
+        var new_clip = this._clipViewport;
+
+        if (this._scaleViewport) {
+            // Disable viewport clipping if we are scaling
+            new_clip = false;
+        }
+
+        if (cur_clip !== new_clip) {
+            this._display.clipViewport = new_clip;
+        }
+
+        if (new_clip) {
+            // When clipping is enabled, the screen is limited to
+            // the size of the container.
+            let size = this._screenSize();
+            this._display.viewportChangeSize(size.w, size.h);
+            this._fixScrollbars();
+        }
+    },
+
+    _updateScale: function () {
+        if (!this._scaleViewport) {
+            this._display.scale = 1.0;
+        } else {
+            let size = this._screenSize();
+            this._display.autoscale(size.w, size.h);
+        }
+        this._fixScrollbars();
+    },
+
+    // Requests a change of remote desktop size. This message is an extension
+    // and may only be sent if we have received an ExtendedDesktopSize message
+    _requestRemoteResize: function () {
+        clearTimeout(this._resizeTimeout);
+        this._resizeTimeout = null;
+
+        if (!this._resizeSession || this._viewOnly ||
+            !this._supportsSetDesktopSize) {
+            return;
+        }
+
+        let size = this._screenSize();
+        RFB.messages.setDesktopSize(this._sock, size.w, size.h,
+                                    this._screen_id, this._screen_flags);
+
+        Log.Debug('Requested new desktop size: ' +
+                   size.w + 'x' + size.h);
+    },
+
+    // Gets the the size of the available screen
+    _screenSize: function () {
+        return { w: this._screen.offsetWidth,
+                 h: this._screen.offsetHeight };
+    },
+
+    _fixScrollbars: function () {
+        // This is a hack because Chrome screws up the calculation
+        // for when scrollbars are needed. So to fix it we temporarily
+        // toggle them off and on.
+        var orig = this._screen.style.overflow;
+        this._screen.style.overflow = 'hidden';
+        // Force Chrome to recalculate the layout by asking for
+        // an element's dimensions
+        this._screen.getBoundingClientRect();
+        this._screen.style.overflow = orig;
+    },
+
+    /*
+     * Connection states:
+     *   connecting
+     *   connected
+     *   disconnecting
+     *   disconnected - permanent state
+     */
+    _updateConnectionState: function (state) {
+        var oldstate = this._rfb_connection_state;
+
+        if (state === oldstate) {
+            Log.Debug("Already in state '" + state + "', ignoring");
+            return;
+        }
+
+        // The 'disconnected' state is permanent for each RFB object
+        if (oldstate === 'disconnected') {
+            Log.Error("Tried changing state of a disconnected RFB object");
+            return;
+        }
+
+        // Ensure proper transitions before doing anything
+        switch (state) {
+            case 'connected':
+                if (oldstate !== 'connecting') {
+                    Log.Error("Bad transition to connected state, " +
+                               "previous connection state: " + oldstate);
+                    return;
+                }
+                break;
+
+            case 'disconnected':
+                if (oldstate !== 'disconnecting') {
+                    Log.Error("Bad transition to disconnected state, " +
+                               "previous connection state: " + oldstate);
+                    return;
+                }
+                break;
+
+            case 'connecting':
+                if (oldstate !== '') {
+                    Log.Error("Bad transition to connecting state, " +
+                               "previous connection state: " + oldstate);
+                    return;
+                }
+                break;
+
+            case 'disconnecting':
+                if (oldstate !== 'connected' && oldstate !== 'connecting') {
+                    Log.Error("Bad transition to disconnecting state, " +
+                               "previous connection state: " + oldstate);
+                    return;
+                }
+                break;
+
+            default:
+                Log.Error("Unknown connection state: " + state);
+                return;
+        }
+
+        // State change actions
+
+        this._rfb_connection_state = state;
+
+        var smsg = "New state '" + state + "', was '" + oldstate + "'.";
+        Log.Debug(smsg);
+
+        if (this._disconnTimer && state !== 'disconnecting') {
+            Log.Debug("Clearing disconnect timer");
+            clearTimeout(this._disconnTimer);
+            this._disconnTimer = null;
+
+            // make sure we don't get a double event
+            this._sock.off('close');
+        }
+
+        switch (state) {
+            case 'connecting':
+                this._connect();
+                break;
+
+            case 'connected':
+                var event = new CustomEvent("connect", { detail: {} });
+                this.dispatchEvent(event);
+                break;
+
+            case 'disconnecting':
+                this._disconnect();
+
+                this._disconnTimer = setTimeout(function () {
+                    Log.Error("Disconnection timed out.");
+                    this._updateConnectionState('disconnected');
+                }.bind(this), DISCONNECT_TIMEOUT * 1000);
+                break;
+
+            case 'disconnected':
+                event = new CustomEvent(
+                    "disconnect", { detail:
+                                    { clean: this._rfb_clean_disconnect } });
+                this.dispatchEvent(event);
+                break;
+        }
+    },
+
+    /* Print errors and disconnect
+     *
+     * The parameter 'details' is used for information that
+     * should be logged but not sent to the user interface.
+     */
+    _fail: function (details) {
+        switch (this._rfb_connection_state) {
+            case 'disconnecting':
+                Log.Error("Failed when disconnecting: " + details);
+                break;
+            case 'connected':
+                Log.Error("Failed while connected: " + details);
+                break;
+            case 'connecting':
+                Log.Error("Failed when connecting: " + details);
+                break;
+            default:
+                Log.Error("RFB failure: " + details);
+                break;
+        }
+        this._rfb_clean_disconnect = false; //This is sent to the UI
+
+        // Transition to disconnected without waiting for socket to close
+        this._updateConnectionState('disconnecting');
+        this._updateConnectionState('disconnected');
+
+        return false;
+    },
+
+    _setCapability: function (cap, val) {
+        this._capabilities[cap] = val;
+        var event = new CustomEvent("capabilities",
+                                    { detail: { capabilities: this._capabilities } });
+        this.dispatchEvent(event);
+    },
+
+    _handle_message: function () {
+        if (this._sock.rQlen() === 0) {
+            Log.Warn("handle_message called on an empty receive queue");
+            return;
+        }
+
+        switch (this._rfb_connection_state) {
+            case 'disconnected':
+                Log.Error("Got data while disconnected");
+                break;
+            case 'connected':
+                while (true) {
+                    if (this._flushing) {
+                        break;
+                    }
+                    if (!this._normal_msg()) {
+                        break;
+                    }
+                    if (this._sock.rQlen() === 0) {
+                        break;
+                    }
+                }
+                break;
+            default:
+                this._init_msg();
+                break;
+        }
+    },
+
+    _handleKeyEvent: function (keysym, code, down) {
+        this.sendKey(keysym, code, down);
+    },
+
+    _handleMouseButton: function (x, y, down, bmask) {
+        if (down) {
+            this._mouse_buttonMask |= bmask;
+        } else {
+            this._mouse_buttonMask &= ~bmask;
+        }
+
+        if (this.dragViewport) {
+            if (down && !this._viewportDragging) {
+                this._viewportDragging = true;
+                this._viewportDragPos = {'x': x, 'y': y};
+                this._viewportHasMoved = false;
+
+                // Skip sending mouse events
+                return;
+            } else {
+                this._viewportDragging = false;
+
+                // If we actually performed a drag then we are done
+                // here and should not send any mouse events
+                if (this._viewportHasMoved) {
+                    return;
+                }
+
+                // Otherwise we treat this as a mouse click event.
+                // Send the button down event here, as the button up
+                // event is sent at the end of this function.
+                RFB.messages.pointerEvent(this._sock,
+                                          this._display.absX(x),
+                                          this._display.absY(y),
+                                          bmask);
+            }
+        }
+
+        if (this._viewOnly) { return; } // View only, skip mouse events
+
+        if (this._rfb_connection_state !== 'connected') { return; }
+        RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
+    },
+
+    _handleMouseMove: function (x, y) {
+        if (this._viewportDragging) {
+            var deltaX = this._viewportDragPos.x - x;
+            var deltaY = this._viewportDragPos.y - y;
+
+            // The goal is to trigger on a certain physical width, the
+            // devicePixelRatio brings us a bit closer but is not optimal.
+            var dragThreshold = 10 * (window.devicePixelRatio || 1);
+
+            if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
+                                           Math.abs(deltaY) > dragThreshold)) {
+                this._viewportHasMoved = true;
+
+                this._viewportDragPos = {'x': x, 'y': y};
+                this._display.viewportChangePos(deltaX, deltaY);
+            }
+
+            // Skip sending mouse events
+            return;
+        }
+
+        if (this._viewOnly) { return; } // View only, skip mouse events
+
+        if (this._rfb_connection_state !== 'connected') { return; }
+        RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
+    },
+
+    // Message Handlers
+
+    _negotiate_protocol_version: function () {
+        if (this._sock.rQlen() < 12) {
+            return this._fail("Received incomplete protocol version.");
+        }
+
+        var sversion = this._sock.rQshiftStr(12).substr(4, 7);
+        Log.Info("Server ProtocolVersion: " + sversion);
+        var is_repeater = 0;
+        switch (sversion) {
+            case "000.000":  // UltraVNC repeater
+                is_repeater = 1;
+                break;
+            case "003.003":
+            case "003.006":  // UltraVNC
+            case "003.889":  // Apple Remote Desktop
+                this._rfb_version = 3.3;
+                break;
+            case "003.007":
+                this._rfb_version = 3.7;
+                break;
+            case "003.008":
+            case "004.000":  // Intel AMT KVM
+            case "004.001":  // RealVNC 4.6
+            case "005.000":  // RealVNC 5.3
+                this._rfb_version = 3.8;
+                break;
+            default:
+                return this._fail("Invalid server version " + sversion);
+        }
+
+        if (is_repeater) {
+            var repeaterID = "ID:" + this._repeaterID;
+            while (repeaterID.length < 250) {
+                repeaterID += "\0";
+            }
+            this._sock.send_string(repeaterID);
+            return true;
+        }
+
+        if (this._rfb_version > this._rfb_max_version) {
+            this._rfb_version = this._rfb_max_version;
+        }
+
+        var cversion = "00" + parseInt(this._rfb_version, 10) +
+                       ".00" + ((this._rfb_version * 10) % 10);
+        this._sock.send_string("RFB " + cversion + "\n");
+        Log.Debug('Sent ProtocolVersion: ' + cversion);
+
+        this._rfb_init_state = 'Security';
+    },
+
+    _negotiate_security: function () {
+        // Polyfill since IE and PhantomJS doesn't have
+        // TypedArray.includes()
+        function includes(item, array) {
+            for (var i = 0; i < array.length; i++) {
+                if (array[i] === item) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        if (this._rfb_version >= 3.7) {
+            // Server sends supported list, client decides
+            var num_types = this._sock.rQshift8();
+            if (this._sock.rQwait("security type", num_types, 1)) { return false; }
+
+            if (num_types === 0) {
+                return this._handle_security_failure("no security types");
+            }
+
+            var types = this._sock.rQshiftBytes(num_types);
+            Log.Debug("Server security types: " + types);
+
+            // Look for each auth in preferred order
+            this._rfb_auth_scheme = 0;
+            if (includes(1, types)) {
+                this._rfb_auth_scheme = 1; // None
+            } else if (includes(22, types)) {
+                this._rfb_auth_scheme = 22; // XVP
+            } else if (includes(16, types)) {
+                this._rfb_auth_scheme = 16; // Tight
+            } else if (includes(2, types)) {
+                this._rfb_auth_scheme = 2; // VNC Auth
+            } else {
+                return this._fail("Unsupported security types (types: " + types + ")");
+            }
+
+            this._sock.send([this._rfb_auth_scheme]);
+        } else {
+            // Server decides
+            if (this._sock.rQwait("security scheme", 4)) { return false; }
+            this._rfb_auth_scheme = this._sock.rQshift32();
+        }
+
+        this._rfb_init_state = 'Authentication';
+        Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
+
+        return this._init_msg(); // jump to authentication
+    },
+
+    /*
+     * Get the security failure reason if sent from the server and
+     * send the 'securityfailure' event.
+     *
+     * - The optional parameter context can be used to add some extra
+     *   context to the log output.
+     *
+     * - The optional parameter security_result_status can be used to
+     *   add a custom status code to the event.
+     */
+    _handle_security_failure: function (context, security_result_status) {
+
+        if (typeof context === 'undefined') {
+            context = "";
+        } else {
+            context = " on " + context;
+        }
+
+        if (typeof security_result_status === 'undefined') {
+            security_result_status = 1; // fail
+        }
+
+        if (this._sock.rQwait("reason length", 4)) {
+            return false;
+        }
+        let strlen = this._sock.rQshift32();
+        let reason = "";
+
+        if (strlen > 0) {
+            if (this._sock.rQwait("reason", strlen, 8)) { return false; }
+            reason = this._sock.rQshiftStr(strlen);
+        }
+
+        if (reason !== "") {
+
+            let event = new CustomEvent(
+                "securityfailure",
+                { detail: { status: security_result_status, reason: reason } });
+            this.dispatchEvent(event);
+
+            return this._fail("Security negotiation failed" + context +
+                              " (reason: " + reason + ")");
+        } else {
+
+            let event = new CustomEvent(
+                "securityfailure",
+                { detail: { status: security_result_status } });
+            this.dispatchEvent(event);
+
+            return this._fail("Security negotiation failed" + context);
+        }
+    },
+
+    // authentication
+    _negotiate_xvp_auth: function () {
+        if (!this._rfb_credentials.username ||
+            !this._rfb_credentials.password ||
+            !this._rfb_credentials.target) {
+            var event = new CustomEvent("credentialsrequired",
+                                        { detail: { types: ["username", "password", "target"] } });
+            this.dispatchEvent(event);
+            return false;
+        }
+
+        var xvp_auth_str = String.fromCharCode(this._rfb_credentials.username.length) +
+                           String.fromCharCode(this._rfb_credentials.target.length) +
+                           this._rfb_credentials.username +
+                           this._rfb_credentials.target;
+        this._sock.send_string(xvp_auth_str);
+        this._rfb_auth_scheme = 2;
+        return this._negotiate_authentication();
+    },
+
+    _negotiate_std_vnc_auth: function () {
+        if (this._sock.rQwait("auth challenge", 16)) { return false; }
+
+        if (!this._rfb_credentials.password) {
+            var event = new CustomEvent("credentialsrequired",
+                                        { detail: { types: ["password"] } });
+            this.dispatchEvent(event);
+            return false;
+        }
+
+        // TODO(directxman12): make genDES not require an Array
+        var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
+        var response = RFB.genDES(this._rfb_credentials.password, challenge);
+        this._sock.send(response);
+        this._rfb_init_state = "SecurityResult";
+        return true;
+    },
+
+    _negotiate_tight_tunnels: function (numTunnels) {
+        var clientSupportedTunnelTypes = {
+            0: { vendor: 'TGHT', signature: 'NOTUNNEL' }
+        };
+        var serverSupportedTunnelTypes = {};
+        // receive tunnel capabilities
+        for (var i = 0; i < numTunnels; i++) {
+            var cap_code = this._sock.rQshift32();
+            var cap_vendor = this._sock.rQshiftStr(4);
+            var cap_signature = this._sock.rQshiftStr(8);
+            serverSupportedTunnelTypes[cap_code] = { vendor: cap_vendor, signature: cap_signature };
+        }
+
+        // choose the notunnel type
+        if (serverSupportedTunnelTypes[0]) {
+            if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor ||
+                serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) {
+                return this._fail("Client's tunnel type had the incorrect " +
+                                  "vendor or signature");
+            }
+            this._sock.send([0, 0, 0, 0]);  // use NOTUNNEL
+            return false; // wait until we receive the sub auth count to continue
+        } else {
+            return this._fail("Server wanted tunnels, but doesn't support " +
+                              "the notunnel type");
+        }
+    },
+
+    _negotiate_tight_auth: function () {
+        if (!this._rfb_tightvnc) {  // first pass, do the tunnel negotiation
+            if (this._sock.rQwait("num tunnels", 4)) { return false; }
+            var numTunnels = this._sock.rQshift32();
+            if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; }
+
+            this._rfb_tightvnc = true;
+
+            if (numTunnels > 0) {
+                this._negotiate_tight_tunnels(numTunnels);
+                return false;  // wait until we receive the sub auth to continue
+            }
+        }
+
+        // second pass, do the sub-auth negotiation
+        if (this._sock.rQwait("sub auth count", 4)) { return false; }
+        var subAuthCount = this._sock.rQshift32();
+        if (subAuthCount === 0) {  // empty sub-auth list received means 'no auth' subtype selected
+            this._rfb_init_state = 'SecurityResult';
+            return true;
+        }
+
+        if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; }
+
+        var clientSupportedTypes = {
+            'STDVNOAUTH__': 1,
+            'STDVVNCAUTH_': 2
+        };
+
+        var serverSupportedTypes = [];
+
+        for (var i = 0; i < subAuthCount; i++) {
+            var capNum = this._sock.rQshift32();
+            var capabilities = this._sock.rQshiftStr(12);
+            serverSupportedTypes.push(capabilities);
+        }
+
+        for (var authType in clientSupportedTypes) {
+            if (serverSupportedTypes.indexOf(authType) != -1) {
+                this._sock.send([0, 0, 0, clientSupportedTypes[authType]]);
+
+                switch (authType) {
+                    case 'STDVNOAUTH__':  // no auth
+                        this._rfb_init_state = 'SecurityResult';
+                        return true;
+                    case 'STDVVNCAUTH_': // VNC auth
+                        this._rfb_auth_scheme = 2;
+                        return this._init_msg();
+                    default:
+                        return this._fail("Unsupported tiny auth scheme " +
+                                          "(scheme: " + authType + ")");
+                }
+            }
+        }
+
+        return this._fail("No supported sub-auth types!");
+    },
+
+    _negotiate_authentication: function () {
+        switch (this._rfb_auth_scheme) {
+            case 0:  // connection failed
+                return this._handle_security_failure("authentication scheme");
+
+            case 1:  // no auth
+                if (this._rfb_version >= 3.8) {
+                    this._rfb_init_state = 'SecurityResult';
+                    return true;
+                }
+                this._rfb_init_state = 'ClientInitialisation';
+                return this._init_msg();
+
+            case 22:  // XVP auth
+                return this._negotiate_xvp_auth();
+
+            case 2:  // VNC authentication
+                return this._negotiate_std_vnc_auth();
+
+            case 16:  // TightVNC Security Type
+                return this._negotiate_tight_auth();
+
+            default:
+                return this._fail("Unsupported auth scheme (scheme: " +
+                                  this._rfb_auth_scheme + ")");
+        }
+    },
+
+    _handle_security_result: function () {
+        if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
+
+        let status = this._sock.rQshift32();
+
+        if (status === 0) { // OK
+            this._rfb_init_state = 'ClientInitialisation';
+            Log.Debug('Authentication OK');
+            return this._init_msg();
+        } else {
+            if (this._rfb_version >= 3.8) {
+                return this._handle_security_failure("security result", status);
+            } else {
+                let event = new CustomEvent("securityfailure",
+                                            { detail: { status: status } });
+                this.dispatchEvent(event);
+
+                return this._fail("Security handshake failed");
+            }
+        }
+    },
+
+    _negotiate_server_init: function () {
+        if (this._sock.rQwait("server initialization", 24)) { return false; }
+
+        /* Screen size */
+        var width = this._sock.rQshift16();
+        var height = this._sock.rQshift16();
+
+        /* PIXEL_FORMAT */
+        var bpp         = this._sock.rQshift8();
+        var depth       = this._sock.rQshift8();
+        var big_endian  = this._sock.rQshift8();
+        var true_color  = this._sock.rQshift8();
+
+        var red_max     = this._sock.rQshift16();
+        var green_max   = this._sock.rQshift16();
+        var blue_max    = this._sock.rQshift16();
+        var red_shift   = this._sock.rQshift8();
+        var green_shift = this._sock.rQshift8();
+        var blue_shift  = this._sock.rQshift8();
+        this._sock.rQskipBytes(3);  // padding
+
+        // NB(directxman12): we don't want to call any callbacks or print messages until
+        //                   *after* we're past the point where we could backtrack
+
+        /* Connection name/title */
+        var name_length = this._sock.rQshift32();
+        if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
+        this._fb_name = decodeUTF8(this._sock.rQshiftStr(name_length));
+
+        if (this._rfb_tightvnc) {
+            if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
+            // In TightVNC mode, ServerInit message is extended
+            var numServerMessages = this._sock.rQshift16();
+            var numClientMessages = this._sock.rQshift16();
+            var numEncodings = this._sock.rQshift16();
+            this._sock.rQskipBytes(2);  // padding
+
+            var totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;
+            if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + name_length)) { return false; }
+
+            // we don't actually do anything with the capability information that TIGHT sends,
+            // so we just skip the all of this.
+
+            // TIGHT server message capabilities
+            this._sock.rQskipBytes(16 * numServerMessages);
+
+            // TIGHT client message capabilities
+            this._sock.rQskipBytes(16 * numClientMessages);
+
+            // TIGHT encoding capabilities
+            this._sock.rQskipBytes(16 * numEncodings);
+        }
+
+        // NB(directxman12): these are down here so that we don't run them multiple times
+        //                   if we backtrack
+        Log.Info("Screen: " + width + "x" + height +
+                  ", bpp: " + bpp + ", depth: " + depth +
+                  ", big_endian: " + big_endian +
+                  ", true_color: " + true_color +
+                  ", red_max: " + red_max +
+                  ", green_max: " + green_max +
+                  ", blue_max: " + blue_max +
+                  ", red_shift: " + red_shift +
+                  ", green_shift: " + green_shift +
+                  ", blue_shift: " + blue_shift);
+
+        if (big_endian !== 0) {
+            Log.Warn("Server native endian is not little endian");
+        }
+
+        if (red_shift !== 16) {
+            Log.Warn("Server native red-shift is not 16");
+        }
+
+        if (blue_shift !== 0) {
+            Log.Warn("Server native blue-shift is not 0");
+        }
+
+        // we're past the point where we could backtrack, so it's safe to call this
+        var event = new CustomEvent("desktopname",
+                                    { detail: { name: this._fb_name } });
+        this.dispatchEvent(event);
+
+        this._resize(width, height);
+
+        if (!this._viewOnly) { this._keyboard.grab(); }
+        if (!this._viewOnly) { this._mouse.grab(); }
+
+        this._fb_depth = 24;
+
+        if (this._fb_name === "Intel(r) AMT KVM") {
+            Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
+            this._fb_depth = 8;
+        }
+
+        RFB.messages.pixelFormat(this._sock, this._fb_depth, true);
+        this._sendEncodings();
+        RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height);
+
+        this._timing.fbu_rt_start = (new Date()).getTime();
+        this._timing.pixels = 0;
+
+        // Cursor will be server side until the server decides to honor
+        // our request and send over the cursor image
+        this._display.disableLocalCursor();
+
+        this._updateConnectionState('connected');
+        return true;
+    },
+
+    _sendEncodings: function () {
+        var encs = [];
+
+        // In preference order
+        encs.push(encodings.encodingCopyRect);
+        // Only supported with full depth support
+        if (this._fb_depth == 24) {
+            encs.push(encodings.encodingTight);
+            encs.push(encodings.encodingHextile);
+            encs.push(encodings.encodingRRE);
+        }
+        encs.push(encodings.encodingRaw);
+
+        // Psuedo-encoding settings
+        encs.push(encodings.pseudoEncodingTightPNG);
+        encs.push(encodings.pseudoEncodingQualityLevel0 + 6);
+        encs.push(encodings.pseudoEncodingCompressLevel0 + 2);
+
+        encs.push(encodings.pseudoEncodingDesktopSize);
+        encs.push(encodings.pseudoEncodingLastRect);
+        encs.push(encodings.pseudoEncodingQEMUExtendedKeyEvent);
+        encs.push(encodings.pseudoEncodingExtendedDesktopSize);
+        encs.push(encodings.pseudoEncodingXvp);
+        encs.push(encodings.pseudoEncodingFence);
+        encs.push(encodings.pseudoEncodingContinuousUpdates);
+
+        if (supportsCursorURIs() &&
+            !isTouchDevice && this._fb_depth == 24) {
+            encs.push(encodings.pseudoEncodingCursor);
+        }
+
+        RFB.messages.clientEncodings(this._sock, encs);
+    },
+
+    /* RFB protocol initialization states:
+     *   ProtocolVersion
+     *   Security
+     *   Authentication
+     *   SecurityResult
+     *   ClientInitialization - not triggered by server message
+     *   ServerInitialization
+     */
+    _init_msg: function () {
+        switch (this._rfb_init_state) {
+            case 'ProtocolVersion':
+                return this._negotiate_protocol_version();
+
+            case 'Security':
+                return this._negotiate_security();
+
+            case 'Authentication':
+                return this._negotiate_authentication();
+
+            case 'SecurityResult':
+                return this._handle_security_result();
+
+            case 'ClientInitialisation':
+                this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation
+                this._rfb_init_state = 'ServerInitialisation';
+                return true;
+
+            case 'ServerInitialisation':
+                return this._negotiate_server_init();
+
+            default:
+                return this._fail("Unknown init state (state: " +
+                                  this._rfb_init_state + ")");
+        }
+    },
+
+    _handle_set_colour_map_msg: function () {
+        Log.Debug("SetColorMapEntries");
+
+        return this._fail("Unexpected SetColorMapEntries message");
+    },
+
+    _handle_server_cut_text: function () {
+        Log.Debug("ServerCutText");
+
+        if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
+        this._sock.rQskipBytes(3);  // Padding
+        var length = this._sock.rQshift32();
+        if (this._sock.rQwait("ServerCutText", length, 8)) { return false; }
+
+        var text = this._sock.rQshiftStr(length);
+
+        if (this._viewOnly) { return true; }
+
+        var event = new CustomEvent("clipboard",
+                                    { detail: { text: text } });
+        this.dispatchEvent(event);
+
+        return true;
+    },
+
+    _handle_server_fence_msg: function() {
+        if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; }
+        this._sock.rQskipBytes(3); // Padding
+        var flags = this._sock.rQshift32();
+        var length = this._sock.rQshift8();
+
+        if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
+
+        if (length > 64) {
+            Log.Warn("Bad payload length (" + length + ") in fence response");
+            length = 64;
+        }
+
+        var payload = this._sock.rQshiftStr(length);
+
+        this._supportsFence = true;
+
+        /*
+         * Fence flags
+         *
+         *  (1<<0)  - BlockBefore
+         *  (1<<1)  - BlockAfter
+         *  (1<<2)  - SyncNext
+         *  (1<<31) - Request
+         */
+
+        if (!(flags & (1<<31))) {
+            return this._fail("Unexpected fence response");
+        }
+
+        // Filter out unsupported flags
+        // FIXME: support syncNext
+        flags &= (1<<0) | (1<<1);
+
+        // BlockBefore and BlockAfter are automatically handled by
+        // the fact that we process each incoming message
+        // synchronuosly.
+        RFB.messages.clientFence(this._sock, flags, payload);
+
+        return true;
+    },
+
+    _handle_xvp_msg: function () {
+        if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; }
+        this._sock.rQskip8();  // Padding
+        var xvp_ver = this._sock.rQshift8();
+        var xvp_msg = this._sock.rQshift8();
+
+        switch (xvp_msg) {
+            case 0:  // XVP_FAIL
+                Log.Error("XVP Operation Failed");
+                break;
+            case 1:  // XVP_INIT
+                this._rfb_xvp_ver = xvp_ver;
+                Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
+                this._setCapability("power", true);
+                break;
+            default:
+                this._fail("Illegal server XVP message (msg: " + xvp_msg + ")");
+                break;
+        }
+
+        return true;
+    },
+
+    _normal_msg: function () {
+        var msg_type;
+
+        if (this._FBU.rects > 0) {
+            msg_type = 0;
+        } else {
+            msg_type = this._sock.rQshift8();
+        }
+
+        switch (msg_type) {
+            case 0:  // FramebufferUpdate
+                var ret = this._framebufferUpdate();
+                if (ret && !this._enabledContinuousUpdates) {
+                    RFB.messages.fbUpdateRequest(this._sock, true, 0, 0,
+                                                 this._fb_width, this._fb_height);
+                }
+                return ret;
+
+            case 1:  // SetColorMapEntries
+                return this._handle_set_colour_map_msg();
+
+            case 2:  // Bell
+                Log.Debug("Bell");
+                var event = new CustomEvent("bell", { detail: {} });
+                this.dispatchEvent(event);
+                return true;
+
+            case 3:  // ServerCutText
+                return this._handle_server_cut_text();
+
+            case 150: // EndOfContinuousUpdates
+                var first = !(this._supportsContinuousUpdates);
+                this._supportsContinuousUpdates = true;
+                this._enabledContinuousUpdates = false;
+                if (first) {
+                    this._enabledContinuousUpdates = true;
+                    this._updateContinuousUpdates();
+                    Log.Info("Enabling continuous updates.");
+                } else {
+                    // FIXME: We need to send a framebufferupdaterequest here
+                    // if we add support for turning off continuous updates
+                }
+                return true;
+
+            case 248: // ServerFence
+                return this._handle_server_fence_msg();
+
+            case 250:  // XVP
+                return this._handle_xvp_msg();
+
+            default:
+                this._fail("Unexpected server message (type " + msg_type + ")");
+                Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
+                return true;
+        }
+    },
+
+    _onFlush: function() {
+        this._flushing = false;
+        // Resume processing
+        if (this._sock.rQlen() > 0) {
+            this._handle_message();
+        }
+    },
+
+    _framebufferUpdate: function () {
+        var ret = true;
+        var now;
+
+        if (this._FBU.rects === 0) {
+            if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
+            this._sock.rQskip8();  // Padding
+            this._FBU.rects = this._sock.rQshift16();
+            this._FBU.bytes = 0;
+            this._timing.cur_fbu = 0;
+            if (this._timing.fbu_rt_start > 0) {
+                now = (new Date()).getTime();
+                Log.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
+            }
+
+            // Make sure the previous frame is fully rendered first
+            // to avoid building up an excessive queue
+            if (this._display.pending()) {
+                this._flushing = true;
+                this._display.flush();
+                return false;
+            }
+        }
+
+        while (this._FBU.rects > 0) {
+            if (this._rfb_connection_state !== 'connected') { return false; }
+
+            if (this._sock.rQwait("FBU", this._FBU.bytes)) { return false; }
+            if (this._FBU.bytes === 0) {
+                if (this._sock.rQwait("rect header", 12)) { return false; }
+                /* New FramebufferUpdate */
+
+                var hdr = this._sock.rQshiftBytes(12);
+                this._FBU.x        = (hdr[0] << 8) + hdr[1];
+                this._FBU.y        = (hdr[2] << 8) + hdr[3];
+                this._FBU.width    = (hdr[4] << 8) + hdr[5];
+                this._FBU.height   = (hdr[6] << 8) + hdr[7];
+                this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
+                                              (hdr[10] << 8) + hdr[11], 10);
+
+                if (!this._encHandlers[this._FBU.encoding]) {
+                    this._fail("Unsupported encoding (encoding: " +
+                               this._FBU.encoding + ")");
+                    return false;
+                }
+            }
+
+            this._timing.last_fbu = (new Date()).getTime();
+
+            ret = this._encHandlers[this._FBU.encoding]();
+
+            now = (new Date()).getTime();
+            this._timing.cur_fbu += (now - this._timing.last_fbu);
+
+            if (ret) {
+                if (!(this._FBU.encoding in this._encStats)) {
+                    this._encStats[this._FBU.encoding] = [0, 0];
+                }
+                this._encStats[this._FBU.encoding][0]++;
+                this._encStats[this._FBU.encoding][1]++;
+                this._timing.pixels += this._FBU.width * this._FBU.height;
+            }
+
+            if (this._timing.pixels >= (this._fb_width * this._fb_height)) {
+                if ((this._FBU.width === this._fb_width && this._FBU.height === this._fb_height) ||
+                    this._timing.fbu_rt_start > 0) {
+                    this._timing.full_fbu_total += this._timing.cur_fbu;
+                    this._timing.full_fbu_cnt++;
+                    Log.Info("Timing of full FBU, curr: " +
+                              this._timing.cur_fbu + ", total: " +
+                              this._timing.full_fbu_total + ", cnt: " +
+                              this._timing.full_fbu_cnt + ", avg: " +
+                              (this._timing.full_fbu_total / this._timing.full_fbu_cnt));
+                }
+
+                if (this._timing.fbu_rt_start > 0) {
+                    var fbu_rt_diff = now - this._timing.fbu_rt_start;
+                    this._timing.fbu_rt_total += fbu_rt_diff;
+                    this._timing.fbu_rt_cnt++;
+                    Log.Info("full FBU round-trip, cur: " +
+                              fbu_rt_diff + ", total: " +
+                              this._timing.fbu_rt_total + ", cnt: " +
+                              this._timing.fbu_rt_cnt + ", avg: " +
+                              (this._timing.fbu_rt_total / this._timing.fbu_rt_cnt));
+                    this._timing.fbu_rt_start = 0;
+                }
+            }
+
+            if (!ret) { return ret; }  // need more data
+        }
+
+        this._display.flip();
+
+        return true;  // We finished this FBU
+    },
+
+    _updateContinuousUpdates: function() {
+        if (!this._enabledContinuousUpdates) { return; }
+
+        RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
+                                             this._fb_width, this._fb_height);
+    },
+
+    _resize: function(width, height) {
+        this._fb_width = width;
+        this._fb_height = height;
+
+        this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4);
+
+        this._display.resize(this._fb_width, this._fb_height);
+
+        // Adjust the visible viewport based on the new dimensions
+        this._updateClip();
+        this._updateScale();
+
+        this._timing.fbu_rt_start = (new Date()).getTime();
+        this._updateContinuousUpdates();
+    },
+
+    _xvpOp: function (ver, op) {
+        if (this._rfb_xvp_ver < ver) { return; }
+        Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
+        RFB.messages.xvpOp(this._sock, ver, op);
+    },
+};
+
+Object.assign(RFB.prototype, EventTargetMixin);
+
+// Class Methods
+RFB.messages = {
+    keyEvent: function (sock, keysym, down) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 4;  // msg-type
+        buff[offset + 1] = down;
+
+        buff[offset + 2] = 0;
+        buff[offset + 3] = 0;
+
+        buff[offset + 4] = (keysym >> 24);
+        buff[offset + 5] = (keysym >> 16);
+        buff[offset + 6] = (keysym >> 8);
+        buff[offset + 7] = keysym;
+
+        sock._sQlen += 8;
+        sock.flush();
+    },
+
+    QEMUExtendedKeyEvent: function (sock, keysym, down, keycode) {
+        function getRFBkeycode(xt_scancode) {
+            var upperByte = (keycode >> 8);
+            var lowerByte = (keycode & 0x00ff);
+            if (upperByte === 0xe0 && lowerByte < 0x7f) {
+                lowerByte = lowerByte | 0x80;
+                return lowerByte;
+            }
+            return xt_scancode;
+        }
+
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 255; // msg-type
+        buff[offset + 1] = 0; // sub msg-type
+
+        buff[offset + 2] = (down >> 8);
+        buff[offset + 3] = down;
+
+        buff[offset + 4] = (keysym >> 24);
+        buff[offset + 5] = (keysym >> 16);
+        buff[offset + 6] = (keysym >> 8);
+        buff[offset + 7] = keysym;
+
+        var RFBkeycode = getRFBkeycode(keycode);
+
+        buff[offset + 8] = (RFBkeycode >> 24);
+        buff[offset + 9] = (RFBkeycode >> 16);
+        buff[offset + 10] = (RFBkeycode >> 8);
+        buff[offset + 11] = RFBkeycode;
+
+        sock._sQlen += 12;
+        sock.flush();
+    },
+
+    pointerEvent: function (sock, x, y, mask) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 5; // msg-type
+
+        buff[offset + 1] = mask;
+
+        buff[offset + 2] = x >> 8;
+        buff[offset + 3] = x;
+
+        buff[offset + 4] = y >> 8;
+        buff[offset + 5] = y;
+
+        sock._sQlen += 6;
+        sock.flush();
+    },
+
+    // TODO(directxman12): make this unicode compatible?
+    clientCutText: function (sock, text) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 6; // msg-type
+
+        buff[offset + 1] = 0; // padding
+        buff[offset + 2] = 0; // padding
+        buff[offset + 3] = 0; // padding
+
+        var n = text.length;
+
+        buff[offset + 4] = n >> 24;
+        buff[offset + 5] = n >> 16;
+        buff[offset + 6] = n >> 8;
+        buff[offset + 7] = n;
+
+        for (var i = 0; i < n; i++) {
+            buff[offset + 8 + i] =  text.charCodeAt(i);
+        }
+
+        sock._sQlen += 8 + n;
+        sock.flush();
+    },
+
+    setDesktopSize: function (sock, width, height, id, flags) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 251;              // msg-type
+        buff[offset + 1] = 0;            // padding
+        buff[offset + 2] = width >> 8;   // width
+        buff[offset + 3] = width;
+        buff[offset + 4] = height >> 8;  // height
+        buff[offset + 5] = height;
+
+        buff[offset + 6] = 1;            // number-of-screens
+        buff[offset + 7] = 0;            // padding
+
+        // screen array
+        buff[offset + 8] = id >> 24;     // id
+        buff[offset + 9] = id >> 16;
+        buff[offset + 10] = id >> 8;
+        buff[offset + 11] = id;
+        buff[offset + 12] = 0;           // x-position
+        buff[offset + 13] = 0;
+        buff[offset + 14] = 0;           // y-position
+        buff[offset + 15] = 0;
+        buff[offset + 16] = width >> 8;  // width
+        buff[offset + 17] = width;
+        buff[offset + 18] = height >> 8; // height
+        buff[offset + 19] = height;
+        buff[offset + 20] = flags >> 24; // flags
+        buff[offset + 21] = flags >> 16;
+        buff[offset + 22] = flags >> 8;
+        buff[offset + 23] = flags;
+
+        sock._sQlen += 24;
+        sock.flush();
+    },
+
+    clientFence: function (sock, flags, payload) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 248; // msg-type
+
+        buff[offset + 1] = 0; // padding
+        buff[offset + 2] = 0; // padding
+        buff[offset + 3] = 0; // padding
+
+        buff[offset + 4] = flags >> 24; // flags
+        buff[offset + 5] = flags >> 16;
+        buff[offset + 6] = flags >> 8;
+        buff[offset + 7] = flags;
+
+        var n = payload.length;
+
+        buff[offset + 8] = n; // length
+
+        for (var i = 0; i < n; i++) {
+            buff[offset + 9 + i] = payload.charCodeAt(i);
+        }
+
+        sock._sQlen += 9 + n;
+        sock.flush();
+    },
+
+    enableContinuousUpdates: function (sock, enable, x, y, width, height) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 150;             // msg-type
+        buff[offset + 1] = enable;      // enable-flag
+
+        buff[offset + 2] = x >> 8;      // x
+        buff[offset + 3] = x;
+        buff[offset + 4] = y >> 8;      // y
+        buff[offset + 5] = y;
+        buff[offset + 6] = width >> 8;  // width
+        buff[offset + 7] = width;
+        buff[offset + 8] = height >> 8; // height
+        buff[offset + 9] = height;
+
+        sock._sQlen += 10;
+        sock.flush();
+    },
+
+    pixelFormat: function (sock, depth, true_color) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        var bpp, bits;
+
+        if (depth > 16) {
+            bpp = 32;
+        } else if (depth > 8) {
+            bpp = 16;
+        } else {
+            bpp = 8;
+        }
+
+        bits = Math.floor(depth/3);
+
+        buff[offset] = 0;  // msg-type
+
+        buff[offset + 1] = 0; // padding
+        buff[offset + 2] = 0; // padding
+        buff[offset + 3] = 0; // padding
+
+        buff[offset + 4] = bpp;                 // bits-per-pixel
+        buff[offset + 5] = depth;               // depth
+        buff[offset + 6] = 0;                   // little-endian
+        buff[offset + 7] = true_color ? 1 : 0;  // true-color
+
+        buff[offset + 8] = 0;    // red-max
+        buff[offset + 9] = (1 << bits) - 1;  // red-max
+
+        buff[offset + 10] = 0;   // green-max
+        buff[offset + 11] = (1 << bits) - 1; // green-max
+
+        buff[offset + 12] = 0;   // blue-max
+        buff[offset + 13] = (1 << bits) - 1; // blue-max
+
+        buff[offset + 14] = bits * 2; // red-shift
+        buff[offset + 15] = bits * 1; // green-shift
+        buff[offset + 16] = bits * 0; // blue-shift
+
+        buff[offset + 17] = 0;   // padding
+        buff[offset + 18] = 0;   // padding
+        buff[offset + 19] = 0;   // padding
+
+        sock._sQlen += 20;
+        sock.flush();
+    },
+
+    clientEncodings: function (sock, encodings) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 2; // msg-type
+        buff[offset + 1] = 0; // padding
+
+        buff[offset + 2] = encodings.length >> 8;
+        buff[offset + 3] = encodings.length;
+
+        var i, j = offset + 4;
+        for (i = 0; i < encodings.length; i++) {
+            var enc = encodings[i];
+            buff[j] = enc >> 24;
+            buff[j + 1] = enc >> 16;
+            buff[j + 2] = enc >> 8;
+            buff[j + 3] = enc;
+
+            j += 4;
+        }
+
+        sock._sQlen += j - offset;
+        sock.flush();
+    },
+
+    fbUpdateRequest: function (sock, incremental, x, y, w, h) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        if (typeof(x) === "undefined") { x = 0; }
+        if (typeof(y) === "undefined") { y = 0; }
+
+        buff[offset] = 3;  // msg-type
+        buff[offset + 1] = incremental ? 1 : 0;
+
+        buff[offset + 2] = (x >> 8) & 0xFF;
+        buff[offset + 3] = x & 0xFF;
+
+        buff[offset + 4] = (y >> 8) & 0xFF;
+        buff[offset + 5] = y & 0xFF;
+
+        buff[offset + 6] = (w >> 8) & 0xFF;
+        buff[offset + 7] = w & 0xFF;
+
+        buff[offset + 8] = (h >> 8) & 0xFF;
+        buff[offset + 9] = h & 0xFF;
+
+        sock._sQlen += 10;
+        sock.flush();
+    },
+
+    xvpOp: function (sock, ver, op) {
+        var buff = sock._sQ;
+        var offset = sock._sQlen;
+
+        buff[offset] = 250; // msg-type
+        buff[offset + 1] = 0; // padding
+
+        buff[offset + 2] = ver;
+        buff[offset + 3] = op;
+
+        sock._sQlen += 4;
+        sock.flush();
+    },
+};
+
+RFB.genDES = function (password, challenge) {
+    var passwd = [];
+    for (var i = 0; i < password.length; i++) {
+        passwd.push(password.charCodeAt(i));
+    }
+    return (new DES(passwd)).encrypt(challenge);
+};
+
+RFB.encodingHandlers = {
+    RAW: function () {
+        if (this._FBU.lines === 0) {
+            this._FBU.lines = this._FBU.height;
+        }
+
+        var pixelSize = this._fb_depth == 8 ? 1 : 4;
+        this._FBU.bytes = this._FBU.width * pixelSize;  // at least a line
+        if (this._sock.rQwait("RAW", this._FBU.bytes)) { return false; }
+        var cur_y = this._FBU.y + (this._FBU.height - this._FBU.lines);
+        var curr_height = Math.min(this._FBU.lines,
+                                   Math.floor(this._sock.rQlen() / (this._FBU.width * pixelSize)));
+        var data = this._sock.get_rQ();
+        var index = this._sock.get_rQi();
+        if (this._fb_depth == 8) {
+            var pixels = this._FBU.width * curr_height
+            var newdata = new Uint8Array(pixels * 4);
+            var i;
+            for (i = 0;i < pixels;i++) {
+                newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
+                newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
+                newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
+                newdata[i * 4 + 4] = 0;
+            }
+            data = newdata;
+            index = 0;
+        }
+        this._display.blitImage(this._FBU.x, cur_y, this._FBU.width,
+                                curr_height, data, index);
+        this._sock.rQskipBytes(this._FBU.width * curr_height * pixelSize);
+        this._FBU.lines -= curr_height;
+
+        if (this._FBU.lines > 0) {
+            this._FBU.bytes = this._FBU.width * pixelSize;  // At least another line
+        } else {
+            this._FBU.rects--;
+            this._FBU.bytes = 0;
+        }
+
+        return true;
+    },
+
+    COPYRECT: function () {
+        this._FBU.bytes = 4;
+        if (this._sock.rQwait("COPYRECT", 4)) { return false; }
+        this._display.copyImage(this._sock.rQshift16(), this._sock.rQshift16(),
+                                this._FBU.x, this._FBU.y, this._FBU.width,
+                                this._FBU.height);
+
+        this._FBU.rects--;
+        this._FBU.bytes = 0;
+        return true;
+    },
+
+    RRE: function () {
+        var color;
+        if (this._FBU.subrects === 0) {
+            this._FBU.bytes = 4 + 4;
+            if (this._sock.rQwait("RRE", 4 + 4)) { return false; }
+            this._FBU.subrects = this._sock.rQshift32();
+            color = this._sock.rQshiftBytes(4);  // Background
+            this._display.fillRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, color);
+        }
+
+        while (this._FBU.subrects > 0 && this._sock.rQlen() >= (4 + 8)) {
+            color = this._sock.rQshiftBytes(4);
+            var x = this._sock.rQshift16();
+            var y = this._sock.rQshift16();
+            var width = this._sock.rQshift16();
+            var height = this._sock.rQshift16();
+            this._display.fillRect(this._FBU.x + x, this._FBU.y + y, width, height, color);
+            this._FBU.subrects--;
+        }
+
+        if (this._FBU.subrects > 0) {
+            var chunk = Math.min(this._rre_chunk_sz, this._FBU.subrects);
+            this._FBU.bytes = (4 + 8) * chunk;
+        } else {
+            this._FBU.rects--;
+            this._FBU.bytes = 0;
+        }
+
+        return true;
+    },
+
+    HEXTILE: function () {
+        var rQ = this._sock.get_rQ();
+        var rQi = this._sock.get_rQi();
+
+        if (this._FBU.tiles === 0) {
+            this._FBU.tiles_x = Math.ceil(this._FBU.width / 16);
+            this._FBU.tiles_y = Math.ceil(this._FBU.height / 16);
+            this._FBU.total_tiles = this._FBU.tiles_x * this._FBU.tiles_y;
+            this._FBU.tiles = this._FBU.total_tiles;
+        }
+
+        while (this._FBU.tiles > 0) {
+            this._FBU.bytes = 1;
+            if (this._sock.rQwait("HEXTILE subencoding", this._FBU.bytes)) { return false; }
+            var subencoding = rQ[rQi];  // Peek
+            if (subencoding > 30) {  // Raw
+                this._fail("Illegal hextile subencoding (subencoding: " +
+                           subencoding + ")");
+                return false;
+            }
+
+            var subrects = 0;
+            var curr_tile = this._FBU.total_tiles - this._FBU.tiles;
+            var tile_x = curr_tile % this._FBU.tiles_x;
+            var tile_y = Math.floor(curr_tile / this._FBU.tiles_x);
+            var x = this._FBU.x + tile_x * 16;
+            var y = this._FBU.y + tile_y * 16;
+            var w = Math.min(16, (this._FBU.x + this._FBU.width) - x);
+            var h = Math.min(16, (this._FBU.y + this._FBU.height) - y);
+
+            // Figure out how much we are expecting
+            if (subencoding & 0x01) {  // Raw
+                this._FBU.bytes += w * h * 4;
+            } else {
+                if (subencoding & 0x02) {  // Background
+                    this._FBU.bytes += 4;
+                }
+                if (subencoding & 0x04) {  // Foreground
+                    this._FBU.bytes += 4;
+                }
+                if (subencoding & 0x08) {  // AnySubrects
+                    this._FBU.bytes++;  // Since we aren't shifting it off
+                    if (this._sock.rQwait("hextile subrects header", this._FBU.bytes)) { return false; }
+                    subrects = rQ[rQi + this._FBU.bytes - 1];  // Peek
+                    if (subencoding & 0x10) {  // SubrectsColoured
+                        this._FBU.bytes += subrects * (4 + 2);
+                    } else {
+                        this._FBU.bytes += subrects * 2;
+                    }
+                }
+            }
+
+            if (this._sock.rQwait("hextile", this._FBU.bytes)) { return false; }
+
+            // We know the encoding and have a whole tile
+            this._FBU.subencoding = rQ[rQi];
+            rQi++;
+            if (this._FBU.subencoding === 0) {
+                if (this._FBU.lastsubencoding & 0x01) {
+                    // Weird: ignore blanks are RAW
+                    Log.Debug("     Ignoring blank after RAW");
+                } else {
+                    this._display.fillRect(x, y, w, h, this._FBU.background);
+                }
+            } else if (this._FBU.subencoding & 0x01) {  // Raw
+                this._display.blitImage(x, y, w, h, rQ, rQi);
+                rQi += this._FBU.bytes - 1;
+            } else {
+                if (this._FBU.subencoding & 0x02) {  // Background
+                    this._FBU.background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+                    rQi += 4;
+                }
+                if (this._FBU.subencoding & 0x04) {  // Foreground
+                    this._FBU.foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+                    rQi += 4;
+                }
+
+                this._display.startTile(x, y, w, h, this._FBU.background);
+                if (this._FBU.subencoding & 0x08) {  // AnySubrects
+                    subrects = rQ[rQi];
+                    rQi++;
+
+                    for (var s = 0; s < subrects; s++) {
+                        var color;
+                        if (this._FBU.subencoding & 0x10) {  // SubrectsColoured
+                            color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+                            rQi += 4;
+                        } else {
+                            color = this._FBU.foreground;
+                        }
+                        var xy = rQ[rQi];
+                        rQi++;
+                        var sx = (xy >> 4);
+                        var sy = (xy & 0x0f);
+
+                        var wh = rQ[rQi];
+                        rQi++;
+                        var sw = (wh >> 4) + 1;
+                        var sh = (wh & 0x0f) + 1;
+
+                        this._display.subTile(sx, sy, sw, sh, color);
+                    }
+                }
+                this._display.finishTile();
+            }
+            this._sock.set_rQi(rQi);
+            this._FBU.lastsubencoding = this._FBU.subencoding;
+            this._FBU.bytes = 0;
+            this._FBU.tiles--;
+        }
+
+        if (this._FBU.tiles === 0) {
+            this._FBU.rects--;
+        }
+
+        return true;
+    },
+
+    TIGHT: function () {
+        this._FBU.bytes = 1;  // compression-control byte
+        if (this._sock.rQwait("TIGHT compression-control", this._FBU.bytes)) { return false; }
+
+        var checksum = function (data) {
+            var sum = 0;
+            for (var i = 0; i < data.length; i++) {
+                sum += data[i];
+                if (sum > 65536) sum -= 65536;
+            }
+            return sum;
+        };
+
+        var resetStreams = 0;
+        var streamId = -1;
+        var decompress = function (data, expected) {
+            for (var i = 0; i < 4; i++) {
+                if ((resetStreams >> i) & 1) {
+                    this._FBU.zlibs[i].reset();
+                    Log.Info("Reset zlib stream " + i);
+                }
+            }
+
+            //var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
+            var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
+            /*if (uncompressed.status !== 0) {
+                Log.Error("Invalid data in zlib stream");
+            }*/
+
+            //return uncompressed.data;
+            return uncompressed;
+        }.bind(this);
+
+        var indexedToRGBX2Color = function (data, palette, width, height) {
+            // Convert indexed (palette based) image data to RGB
+            // TODO: reduce number of calculations inside loop
+            var dest = this._destBuff;
+            var w = Math.floor((width + 7) / 8);
+            var w1 = Math.floor(width / 8);
+
+            /*for (var y = 0; y < height; y++) {
+                var b, x, dp, sp;
+                var yoffset = y * width;
+                var ybitoffset = y * w;
+                var xoffset, targetbyte;
+                for (x = 0; x < w1; x++) {
+                    xoffset = yoffset + x * 8;
+                    targetbyte = data[ybitoffset + x];
+                    for (b = 7; b >= 0; b--) {
+                        dp = (xoffset + 7 - b) * 3;
+                        sp = (targetbyte >> b & 1) * 3;
+                        dest[dp] = palette[sp];
+                        dest[dp + 1] = palette[sp + 1];
+                        dest[dp + 2] = palette[sp + 2];
+                    }
+                }
+
+                xoffset = yoffset + x * 8;
+                targetbyte = data[ybitoffset + x];
+                for (b = 7; b >= 8 - width % 8; b--) {
+                    dp = (xoffset + 7 - b) * 3;
+                    sp = (targetbyte >> b & 1) * 3;
+                    dest[dp] = palette[sp];
+                    dest[dp + 1] = palette[sp + 1];
+                    dest[dp + 2] = palette[sp + 2];
+                }
+            }*/
+
+            for (var y = 0; y < height; y++) {
+                var b, x, dp, sp;
+                for (x = 0; x < w1; x++) {
+                    for (b = 7; b >= 0; b--) {
+                        dp = (y * width + x * 8 + 7 - b) * 4;
+                        sp = (data[y * w + x] >> b & 1) * 3;
+                        dest[dp] = palette[sp];
+                        dest[dp + 1] = palette[sp + 1];
+                        dest[dp + 2] = palette[sp + 2];
+                        dest[dp + 3] = 255;
+                    }
+                }
+
+                for (b = 7; b >= 8 - width % 8; b--) {
+                    dp = (y * width + x * 8 + 7 - b) * 4;
+                    sp = (data[y * w + x] >> b & 1) * 3;
+                    dest[dp] = palette[sp];
+                    dest[dp + 1] = palette[sp + 1];
+                    dest[dp + 2] = palette[sp + 2];
+                    dest[dp + 3] = 255;
+                }
+            }
+
+            return dest;
+        }.bind(this);
+
+        var indexedToRGBX = function (data, palette, width, height) {
+            // Convert indexed (palette based) image data to RGB
+            var dest = this._destBuff;
+            var total = width * height * 4;
+            for (var i = 0, j = 0; i < total; i += 4, j++) {
+                var sp = data[j] * 3;
+                dest[i] = palette[sp];
+                dest[i + 1] = palette[sp + 1];
+                dest[i + 2] = palette[sp + 2];
+                dest[i + 3] = 255;
+            }
+
+            return dest;
+        }.bind(this);
+
+        var rQi = this._sock.get_rQi();
+        var rQ = this._sock.rQwhole();
+        var cmode, data;
+        var cl_header, cl_data;
+
+        var handlePalette = function () {
+            var numColors = rQ[rQi + 2] + 1;
+            var paletteSize = numColors * 3;
+            this._FBU.bytes += paletteSize;
+            if (this._sock.rQwait("TIGHT palette " + cmode, this._FBU.bytes)) { return false; }
+
+            var bpp = (numColors <= 2) ? 1 : 8;
+            var rowSize = Math.floor((this._FBU.width * bpp + 7) / 8);
+            var raw = false;
+            if (rowSize * this._FBU.height < 12) {
+                raw = true;
+                cl_header = 0;
+                cl_data = rowSize * this._FBU.height;
+                //clength = [0, rowSize * this._FBU.height];
+            } else {
+                // begin inline getTightCLength (returning two-item arrays is bad for performance with GC)
+                var cl_offset = rQi + 3 + paletteSize;
+                cl_header = 1;
+                cl_data = 0;
+                cl_data += rQ[cl_offset] & 0x7f;
+                if (rQ[cl_offset] & 0x80) {
+                    cl_header++;
+                    cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+                    if (rQ[cl_offset + 1] & 0x80) {
+                        cl_header++;
+                        cl_data += rQ[cl_offset + 2] << 14;
+                    }
+                }
+                // end inline getTightCLength
+            }
+
+            this._FBU.bytes += cl_header + cl_data;
+            if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
+
+            // Shift ctl, filter id, num colors, palette entries, and clength off
+            this._sock.rQskipBytes(3);
+            //var palette = this._sock.rQshiftBytes(paletteSize);
+            this._sock.rQshiftTo(this._paletteBuff, paletteSize);
+            this._sock.rQskipBytes(cl_header);
+
+            if (raw) {
+                data = this._sock.rQshiftBytes(cl_data);
+            } else {
+                data = decompress(this._sock.rQshiftBytes(cl_data), rowSize * this._FBU.height);
+            }
+
+            // Convert indexed (palette based) image data to RGB
+            var rgbx;
+            if (numColors == 2) {
+                rgbx = indexedToRGBX2Color(data, this._paletteBuff, this._FBU.width, this._FBU.height);
+                this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false);
+            } else {
+                rgbx = indexedToRGBX(data, this._paletteBuff, this._FBU.width, this._FBU.height);
+                this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false);
+            }
+
+
+            return true;
+        }.bind(this);
+
+        var handleCopy = function () {
+            var raw = false;
+            var uncompressedSize = this._FBU.width * this._FBU.height * 3;
+            if (uncompressedSize < 12) {
+                raw = true;
+                cl_header = 0;
+                cl_data = uncompressedSize;
+            } else {
+                // begin inline getTightCLength (returning two-item arrays is for peformance with GC)
+                var cl_offset = rQi + 1;
+                cl_header = 1;
+                cl_data = 0;
+                cl_data += rQ[cl_offset] & 0x7f;
+                if (rQ[cl_offset] & 0x80) {
+                    cl_header++;
+                    cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+                    if (rQ[cl_offset + 1] & 0x80) {
+                        cl_header++;
+                        cl_data += rQ[cl_offset + 2] << 14;
+                    }
+                }
+                // end inline getTightCLength
+            }
+            this._FBU.bytes = 1 + cl_header + cl_data;
+            if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
+
+            // Shift ctl, clength off
+            this._sock.rQshiftBytes(1 + cl_header);
+
+            if (raw) {
+                data = this._sock.rQshiftBytes(cl_data);
+            } else {
+                data = decompress(this._sock.rQshiftBytes(cl_data), uncompressedSize);
+            }
+
+            this._display.blitRgbImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, data, 0, false);
+
+            return true;
+        }.bind(this);
+
+        var ctl = this._sock.rQpeek8();
+
+        // Keep tight reset bits
+        resetStreams = ctl & 0xF;
+
+        // Figure out filter
+        ctl = ctl >> 4;
+        streamId = ctl & 0x3;
+
+        if (ctl === 0x08)       cmode = "fill";
+        else if (ctl === 0x09)  cmode = "jpeg";
+        else if (ctl === 0x0A)  cmode = "png";
+        else if (ctl & 0x04)    cmode = "filter";
+        else if (ctl < 0x04)    cmode = "copy";
+        else return this._fail("Illegal tight compression received (ctl: " +
+                               ctl + ")");
+
+        switch (cmode) {
+            // fill use depth because TPIXELs drop the padding byte
+            case "fill":  // TPIXEL
+                this._FBU.bytes += 3;
+                break;
+            case "jpeg":  // max clength
+                this._FBU.bytes += 3;
+                break;
+            case "png":  // max clength
+                this._FBU.bytes += 3;
+                break;
+            case "filter":  // filter id + num colors if palette
+                this._FBU.bytes += 2;
+                break;
+            case "copy":
+                break;
+        }
+
+        if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
+
+        // Determine FBU.bytes
+        switch (cmode) {
+            case "fill":
+                // skip ctl byte
+                this._display.fillRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, [rQ[rQi + 3], rQ[rQi + 2], rQ[rQi + 1]], false);
+                this._sock.rQskipBytes(4);
+                break;
+            case "png":
+            case "jpeg":
+                // begin inline getTightCLength (returning two-item arrays is for peformance with GC)
+                var cl_offset = rQi + 1;
+                cl_header = 1;
+                cl_data = 0;
+                cl_data += rQ[cl_offset] & 0x7f;
+                if (rQ[cl_offset] & 0x80) {
+                    cl_header++;
+                    cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+                    if (rQ[cl_offset + 1] & 0x80) {
+                        cl_header++;
+                        cl_data += rQ[cl_offset + 2] << 14;
+                    }
+                }
+                // end inline getTightCLength
+                this._FBU.bytes = 1 + cl_header + cl_data;  // ctl + clength size + jpeg-data
+                if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
+
+                // We have everything, render it
+                this._sock.rQskipBytes(1 + cl_header);  // shift off clt + compact length
+                data = this._sock.rQshiftBytes(cl_data);
+                this._display.imageRect(this._FBU.x, this._FBU.y, "image/" + cmode, data);
+                break;
+            case "filter":
+                var filterId = rQ[rQi + 1];
+                if (filterId === 1) {
+                    if (!handlePalette()) { return false; }
+                } else {
+                    // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter
+                    // Filter 2, Gradient is valid but not use if jpeg is enabled
+                    this._fail("Unsupported tight subencoding received " +
+                               "(filter: " + filterId + ")");
+                }
+                break;
+            case "copy":
+                if (!handleCopy()) { return false; }
+                break;
+        }
+
+
+        this._FBU.bytes = 0;
+        this._FBU.rects--;
+
+        return true;
+    },
+
+    last_rect: function () {
+        this._FBU.rects = 0;
+        return true;
+    },
+
+    ExtendedDesktopSize: function () {
+        this._FBU.bytes = 1;
+        if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
+
+        var firstUpdate = !this._supportsSetDesktopSize;
+        this._supportsSetDesktopSize = true;
+
+        // Normally we only apply the current resize mode after a
+        // window resize event. However there is no such trigger on the
+        // initial connect. And we don't know if the server supports
+        // resizing until we've gotten here.
+        if (firstUpdate) {
+            this._requestRemoteResize();
+        }
+
+        var number_of_screens = this._sock.rQpeek8();
+
+        this._FBU.bytes = 4 + (number_of_screens * 16);
+        if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
+
+        this._sock.rQskipBytes(1);  // number-of-screens
+        this._sock.rQskipBytes(3);  // padding
+
+        for (var i = 0; i < number_of_screens; i += 1) {
+            // Save the id and flags of the first screen
+            if (i === 0) {
+                this._screen_id = this._sock.rQshiftBytes(4);    // id
+                this._sock.rQskipBytes(2);                       // x-position
+                this._sock.rQskipBytes(2);                       // y-position
+                this._sock.rQskipBytes(2);                       // width
+                this._sock.rQskipBytes(2);                       // height
+                this._screen_flags = this._sock.rQshiftBytes(4); // flags
+            } else {
+                this._sock.rQskipBytes(16);
+            }
+        }
+
+        /*
+         * The x-position indicates the reason for the change:
+         *
+         *  0 - server resized on its own
+         *  1 - this client requested the resize
+         *  2 - another client requested the resize
+         */
+
+        // We need to handle errors when we requested the resize.
+        if (this._FBU.x === 1 && this._FBU.y !== 0) {
+            var msg = "";
+            // The y-position indicates the status code from the server
+            switch (this._FBU.y) {
+            case 1:
+                msg = "Resize is administratively prohibited";
+                break;
+            case 2:
+                msg = "Out of resources";
+                break;
+            case 3:
+                msg = "Invalid screen layout";
+                break;
+            default:
+                msg = "Unknown reason";
+                break;
+            }
+            Log.Warn("Server did not accept the resize request: "
+                     + msg);
+        } else {
+            this._resize(this._FBU.width, this._FBU.height);
+        }
+
+        this._FBU.bytes = 0;
+        this._FBU.rects -= 1;
+        return true;
+    },
+
+    DesktopSize: function () {
+        this._resize(this._FBU.width, this._FBU.height);
+        this._FBU.bytes = 0;
+        this._FBU.rects -= 1;
+        return true;
+    },
+
+    Cursor: function () {
+        Log.Debug(">> set_cursor");
+        var x = this._FBU.x;  // hotspot-x
+        var y = this._FBU.y;  // hotspot-y
+        var w = this._FBU.width;
+        var h = this._FBU.height;
+
+        var pixelslength = w * h * 4;
+        var masklength = Math.floor((w + 7) / 8) * h;
+
+        this._FBU.bytes = pixelslength + masklength;
+        if (this._sock.rQwait("cursor encoding", this._FBU.bytes)) { return false; }
+
+        this._display.changeCursor(this._sock.rQshiftBytes(pixelslength),
+                                   this._sock.rQshiftBytes(masklength),
+                                   x, y, w, h);
+
+        this._FBU.bytes = 0;
+        this._FBU.rects--;
+
+        Log.Debug("<< set_cursor");
+        return true;
+    },
+
+    QEMUExtendedKeyEvent: function () {
+        this._FBU.rects--;
+
+        // Old Safari doesn't support creating keyboard events
+        try {
+            var keyboardEvent = document.createEvent("keyboardEvent");
+            if (keyboardEvent.code !== undefined) {
+                this._qemuExtKeyEventSupported = true;
+            }
+        } catch (err) {
+        }
+    },
+};

+ 69 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/browser.js

@@ -0,0 +1,69 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+import * as Log from './logging.js';
+
+// Touch detection
+export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
+                                 // requried for Chrome debugger
+                                 (document.ontouchstart !== undefined) ||
+                                 // required for MS Surface
+                                 (navigator.maxTouchPoints > 0) ||
+                                 (navigator.msMaxTouchPoints > 0);
+window.addEventListener('touchstart', function onFirstTouch() {
+    isTouchDevice = true;
+    window.removeEventListener('touchstart', onFirstTouch, false);
+}, false);
+
+var _cursor_uris_supported = null;
+
+export function supportsCursorURIs () {
+    if (_cursor_uris_supported === null) {
+        try {
+            var target = document.createElement('canvas');
+            target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
+
+            if (target.style.cursor) {
+                Log.Info("Data URI scheme cursor supported");
+                _cursor_uris_supported = true;
+            } else {
+                Log.Warn("Data URI scheme cursor not supported");
+                _cursor_uris_supported = false;
+            }
+        } catch (exc) {
+            Log.Error("Data URI scheme cursor test exception: " + exc);
+            _cursor_uris_supported = false;
+        }
+    }
+
+    return _cursor_uris_supported;
+};
+
+export function isMac() {
+    return navigator && !!(/mac/i).exec(navigator.platform);
+}
+
+export function isIE() {
+    return navigator && !!(/trident/i).exec(navigator.userAgent);
+}
+
+export function isEdge() {
+    return navigator && !!(/edge/i).exec(navigator.userAgent);
+}
+
+export function isWindows() {
+    return navigator && !!(/win/i).exec(navigator.platform);
+}
+
+export function isIOS() {
+    return navigator &&
+           (!!(/ipad/i).exec(navigator.platform) ||
+            !!(/iphone/i).exec(navigator.platform) ||
+            !!(/ipod/i).exec(navigator.platform));
+}
+

+ 138 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/events.js

@@ -0,0 +1,138 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+/*
+ * Cross-browser event and position routines
+ */
+
+export function getPointerEvent (e) {
+    return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
+};
+
+export function stopEvent (e) {
+    e.stopPropagation();
+    e.preventDefault();
+};
+
+// Emulate Element.setCapture() when not supported
+var _captureRecursion = false;
+var _captureElem = null;
+function _captureProxy(e) {
+    // Recursion protection as we'll see our own event
+    if (_captureRecursion) return;
+
+    // Clone the event as we cannot dispatch an already dispatched event
+    var newEv = new e.constructor(e.type, e);
+
+    _captureRecursion = true;
+    _captureElem.dispatchEvent(newEv);
+    _captureRecursion = false;
+
+    // Avoid double events
+    e.stopPropagation();
+
+    // Respect the wishes of the redirected event handlers
+    if (newEv.defaultPrevented) {
+        e.preventDefault();
+    }
+
+    // Implicitly release the capture on button release
+    if (e.type === "mouseup") {
+        releaseCapture();
+    }
+};
+
+// Follow cursor style of target element
+function _captureElemChanged() {
+    var captureElem = document.getElementById("noVNC_mouse_capture_elem");
+    captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
+};
+var _captureObserver = new MutationObserver(_captureElemChanged);
+
+var _captureIndex = 0;
+
+export function setCapture (elem) {
+    if (elem.setCapture) {
+
+        elem.setCapture();
+
+        // IE releases capture on 'click' events which might not trigger
+        elem.addEventListener('mouseup', releaseCapture);
+
+    } else {
+        // Release any existing capture in case this method is
+        // called multiple times without coordination
+        releaseCapture();
+
+        var captureElem = document.getElementById("noVNC_mouse_capture_elem");
+
+        if (captureElem === null) {
+            captureElem = document.createElement("div");
+            captureElem.id = "noVNC_mouse_capture_elem";
+            captureElem.style.position = "fixed";
+            captureElem.style.top = "0px";
+            captureElem.style.left = "0px";
+            captureElem.style.width = "100%";
+            captureElem.style.height = "100%";
+            captureElem.style.zIndex = 10000;
+            captureElem.style.display = "none";
+            document.body.appendChild(captureElem);
+
+            // This is to make sure callers don't get confused by having
+            // our blocking element as the target
+            captureElem.addEventListener('contextmenu', _captureProxy);
+
+            captureElem.addEventListener('mousemove', _captureProxy);
+            captureElem.addEventListener('mouseup', _captureProxy);
+        }
+
+        _captureElem = elem;
+        _captureIndex++;
+
+        // Track cursor and get initial cursor
+        _captureObserver.observe(elem, {attributes:true});
+        _captureElemChanged();
+
+        captureElem.style.display = "";
+
+        // We listen to events on window in order to keep tracking if it
+        // happens to leave the viewport
+        window.addEventListener('mousemove', _captureProxy);
+        window.addEventListener('mouseup', _captureProxy);
+    }
+};
+
+export function releaseCapture () {
+    if (document.releaseCapture) {
+
+        document.releaseCapture();
+
+    } else {
+        if (!_captureElem) {
+            return;
+        }
+
+        // There might be events already queued, so we need to wait for
+        // them to flush. E.g. contextmenu in Microsoft Edge
+        window.setTimeout(function(expected) {
+            // Only clear it if it's the expected grab (i.e. no one
+            // else has initiated a new grab)
+            if (_captureIndex === expected) {
+                _captureElem = null;
+            }
+        }, 0, _captureIndex);
+
+        _captureObserver.disconnect();
+
+        var captureElem = document.getElementById("noVNC_mouse_capture_elem");
+        captureElem.style.display = "none";
+
+        window.removeEventListener('mousemove', _captureProxy);
+        window.removeEventListener('mouseup', _captureProxy);
+    }
+};

+ 40 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/eventtarget.js

@@ -0,0 +1,40 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright 2017 Pierre Ossman for Cendio AB
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+var EventTargetMixin = {
+    _listeners: null,
+
+   addEventListener: function(type, callback) {
+      if (!this._listeners) {
+         this._listeners = new Map();
+      }
+      if (!this._listeners.has(type)) {
+         this._listeners.set(type, new Set());
+      }
+      this._listeners.get(type).add(callback);
+   },
+
+   removeEventListener: function(type, callback) {
+      if (!this._listeners || !this._listeners.has(type)) {
+         return;
+      }
+      this._listeners.get(type).delete(callback);
+   },
+
+   dispatchEvent: function(event) {
+      if (!this._listeners || !this._listeners.has(event.type)) {
+         return true;
+      }
+      this._listeners.get(event.type).forEach(function (callback) {
+         callback.call(this, event);
+      }, this);
+      return !event.defaultPrevented;
+   },
+};
+
+export default EventTargetMixin;

+ 51 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/logging.js

@@ -0,0 +1,51 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+/*
+ * Logging/debug routines
+ */
+
+var _log_level = 'warn';
+
+var Debug = function (msg) {};
+var Info = function (msg) {};
+var Warn = function (msg) {};
+var Error = function (msg) {};
+
+export function init_logging (level) {
+    if (typeof level === 'undefined') {
+        level = _log_level;
+    } else {
+        _log_level = level;
+    }
+
+    Debug = Info = Warn = Error = function (msg) {};
+    if (typeof window.console !== "undefined") {
+        switch (level) {
+            case 'debug':
+                Debug = console.debug.bind(window.console);
+            case 'info':
+                Info  = console.info.bind(window.console);
+            case 'warn':
+                Warn  = console.warn.bind(window.console);
+            case 'error':
+                Error = console.error.bind(window.console);
+            case 'none':
+                break;
+            default:
+                throw new Error("invalid logging type '" + level + "'");
+        }
+    }
+};
+export function get_logging () {
+    return _log_level;
+};
+export { Debug, Info, Warn, Error };
+
+// Initialize logging level
+init_logging();

+ 54 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/polyfill.js

@@ -0,0 +1,54 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright 2017 Pierre Ossman for noVNC
+ * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
+ */
+
+/* Polyfills to provide new APIs in old browsers */
+
+/* Object.assign() (taken from MDN) */
+if (typeof Object.assign != 'function') {
+    // Must be writable: true, enumerable: false, configurable: true
+    Object.defineProperty(Object, "assign", {
+        value: function assign(target, varArgs) { // .length of function is 2
+            'use strict';
+            if (target == null) { // TypeError if undefined or null
+                throw new TypeError('Cannot convert undefined or null to object');
+            }
+
+            var to = Object(target);
+
+            for (var index = 1; index < arguments.length; index++) {
+                var nextSource = arguments[index];
+
+                if (nextSource != null) { // Skip over if undefined or null
+                    for (var nextKey in nextSource) {
+                        // Avoid bugs when hasOwnProperty is shadowed
+                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
+                            to[nextKey] = nextSource[nextKey];
+                        }
+                    }
+                }
+            }
+            return to;
+        },
+        writable: true,
+        configurable: true
+    });
+}
+
+/* CustomEvent constructor (taken from MDN) */
+(function () {
+    function CustomEvent ( event, params ) {
+        params = params || { bubbles: false, cancelable: false, detail: undefined };
+        var evt = document.createEvent( 'CustomEvent' );
+        evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
+        return evt;
+    }
+
+    CustomEvent.prototype = window.Event.prototype;
+
+    if (typeof window.CustomEvent !== "function") {
+        window.CustomEvent = CustomEvent;
+    }
+})();

+ 15 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/util/strings.js

@@ -0,0 +1,15 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+/*
+ * Decode from UTF-8
+ */
+export function decodeUTF8 (utf8string) {
+    "use strict";
+    return decodeURIComponent(escape(utf8string));
+};

+ 316 - 0
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/core/websock.js

@@ -0,0 +1,316 @@
+/*
+ * Websock: high-performance binary WebSockets
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * Websock is similar to the standard WebSocket object but with extra
+ * buffer handling.
+ *
+ * Websock has built-in receive queue buffering; the message event
+ * does not contain actual data but is simply a notification that
+ * there is new data available. Several rQ* methods are available to
+ * read binary data off of the receive queue.
+ */
+
+import * as Log from './util/logging.js';
+
+export default function Websock() {
+    "use strict";
+
+    this._websocket = null;  // WebSocket object
+
+    this._rQi = 0;           // Receive queue index
+    this._rQlen = 0;         // Next write position in the receive queue
+    this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
+    this._rQmax = this._rQbufferSize / 8;
+    // called in init: this._rQ = new Uint8Array(this._rQbufferSize);
+    this._rQ = null; // Receive queue
+
+    this._sQbufferSize = 1024 * 10;  // 10 KiB
+    // called in init: this._sQ = new Uint8Array(this._sQbufferSize);
+    this._sQlen = 0;
+    this._sQ = null;  // Send queue
+
+    this._eventHandlers = {
+        'message': function () {},
+        'open': function () {},
+        'close': function () {},
+        'error': function () {}
+    };
+};
+
+// this has performance issues in some versions Chromium, and
+// doesn't gain a tremendous amount of performance increase in Firefox
+// at the moment.  It may be valuable to turn it on in the future.
+var ENABLE_COPYWITHIN = false;
+
+var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024;  // 40 MiB
+
+var typedArrayToString = (function () {
+    // This is only for PhantomJS, which doesn't like apply-ing
+    // with Typed Arrays
+    try {
+        var arr = new Uint8Array([1, 2, 3]);
+        String.fromCharCode.apply(null, arr);
+        return function (a) { return String.fromCharCode.apply(null, a); };
+    } catch (ex) {
+        return function (a) {
+            return String.fromCharCode.apply(
+                null, Array.prototype.slice.call(a));
+        };
+    }
+})();
+
+Websock.prototype = {
+    // Getters and Setters
+    get_sQ: function () {
+        return this._sQ;
+    },
+
+    get_rQ: function () {
+        return this._rQ;
+    },
+
+    get_rQi: function () {
+        return this._rQi;
+    },
+
+    set_rQi: function (val) {
+        this._rQi = val;
+    },
+
+    // Receive Queue
+    rQlen: function () {
+        return this._rQlen - this._rQi;
+    },
+
+    rQpeek8: function () {
+        return this._rQ[this._rQi];
+    },
+
+    rQshift8: function () {
+        return this._rQ[this._rQi++];
+    },
+
+    rQskip8: function () {
+        this._rQi++;
+    },
+
+    rQskipBytes: function (num) {
+        this._rQi += num;
+    },
+
+    // TODO(directxman12): test performance with these vs a DataView
+    rQshift16: function () {
+        return (this._rQ[this._rQi++] << 8) +
+               this._rQ[this._rQi++];
+    },
+
+    rQshift32: function () {
+        return (this._rQ[this._rQi++] << 24) +
+               (this._rQ[this._rQi++] << 16) +
+               (this._rQ[this._rQi++] << 8) +
+               this._rQ[this._rQi++];
+    },
+
+    rQshiftStr: function (len) {
+        if (typeof(len) === 'undefined') { len = this.rQlen(); }
+        var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
+        this._rQi += len;
+        return typedArrayToString(arr);
+    },
+
+    rQshiftBytes: function (len) {
+        if (typeof(len) === 'undefined') { len = this.rQlen(); }
+        this._rQi += len;
+        return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
+    },
+
+    rQshiftTo: function (target, len) {
+        if (len === undefined) { len = this.rQlen(); }
+        // TODO: make this just use set with views when using a ArrayBuffer to store the rQ
+        target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
+        this._rQi += len;
+    },
+
+    rQwhole: function () {
+        return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
+    },
+
+    rQslice: function (start, end) {
+        if (end) {
+            return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
+        } else {
+            return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
+        }
+    },
+
+    // Check to see if we must wait for 'num' bytes (default to FBU.bytes)
+    // to be available in the receive queue. Return true if we need to
+    // wait (and possibly print a debug message), otherwise false.
+    rQwait: function (msg, num, goback) {
+        var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
+        if (rQlen < num) {
+            if (goback) {
+                if (this._rQi < goback) {
+                    throw new Error("rQwait cannot backup " + goback + " bytes");
+                }
+                this._rQi -= goback;
+            }
+            return true; // true means need more data
+        }
+        return false;
+    },
+
+    // Send Queue
+
+    flush: function () {
+        if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
+            this._websocket.send(this._encode_message());
+            this._sQlen = 0;
+        }
+    },
+
+    send: function (arr) {
+        this._sQ.set(arr, this._sQlen);
+        this._sQlen += arr.length;
+        this.flush();
+    },
+
+    send_string: function (str) {
+        this.send(str.split('').map(function (chr) {
+            return chr.charCodeAt(0);
+        }));
+    },
+
+    // Event Handlers
+    off: function (evt) {
+        this._eventHandlers[evt] = function () {};
+    },
+
+    on: function (evt, handler) {
+        this._eventHandlers[evt] = handler;
+    },
+
+    _allocate_buffers: function () {
+        this._rQ = new Uint8Array(this._rQbufferSize);
+        this._sQ = new Uint8Array(this._sQbufferSize);
+    },
+
+    init: function () {
+        this._allocate_buffers();
+        this._rQi = 0;
+        this._websocket = null;
+    },
+
+    open: function (uri, protocols) {
+        var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
+        this.init();
+
+        this._websocket = new WebSocket(uri, protocols);
+        this._websocket.binaryType = 'arraybuffer';
+
+        this._websocket.onmessage = this._recv_message.bind(this);
+        this._websocket.onopen = (function () {
+            Log.Debug('>> WebSock.onopen');
+            if (this._websocket.protocol) {
+                Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
+            }
+
+            this._eventHandlers.open();
+            Log.Debug("<< WebSock.onopen");
+        }).bind(this);
+        this._websocket.onclose = (function (e) {
+            Log.Debug(">> WebSock.onclose");
+            this._eventHandlers.close(e);
+            Log.Debug("<< WebSock.onclose");
+        }).bind(this);
+        this._websocket.onerror = (function (e) {
+            Log.Debug(">> WebSock.onerror: " + e);
+            this._eventHandlers.error(e);
+            Log.Debug("<< WebSock.onerror: " + e);
+        }).bind(this);
+    },
+
+    close: function () {
+        if (this._websocket) {
+            if ((this._websocket.readyState === WebSocket.OPEN) ||
+                    (this._websocket.readyState === WebSocket.CONNECTING)) {
+                Log.Info("Closing WebSocket connection");
+                this._websocket.close();
+            }
+
+            this._websocket.onmessage = function (e) { return; };
+        }
+    },
+
+    // private methods
+    _encode_message: function () {
+        // Put in a binary arraybuffer
+        // according to the spec, you can send ArrayBufferViews with the send method
+        return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
+    },
+
+    _expand_compact_rQ: function (min_fit) {
+        var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
+        if (resizeNeeded) {
+            if (!min_fit) {
+                // just double the size if we need to do compaction
+                this._rQbufferSize *= 2;
+            } else {
+                // otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
+                this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
+            }
+        }
+
+        // we don't want to grow unboundedly
+        if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
+            this._rQbufferSize = MAX_RQ_GROW_SIZE;
+            if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
+                throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
+            }
+        }
+
+        if (resizeNeeded) {
+            var old_rQbuffer = this._rQ.buffer;
+            this._rQmax = this._rQbufferSize / 8;
+            this._rQ = new Uint8Array(this._rQbufferSize);
+            this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
+        } else {
+            if (ENABLE_COPYWITHIN) {
+                this._rQ.copyWithin(0, this._rQi);
+            } else {
+                this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
+            }
+        }
+
+        this._rQlen = this._rQlen - this._rQi;
+        this._rQi = 0;
+    },
+
+    _decode_message: function (data) {
+        // push arraybuffer values onto the end
+        var u8 = new Uint8Array(data);
+        if (u8.length > this._rQbufferSize - this._rQlen) {
+            this._expand_compact_rQ(u8.length);
+        }
+        this._rQ.set(u8, this._rQlen);
+        this._rQlen += u8.length;
+    },
+
+    _recv_message: function (e) {
+        this._decode_message(e.data);
+        if (this.rQlen() > 0) {
+            this._eventHandlers.message();
+            // Compact the receive queue
+            if (this._rQlen == this._rQi) {
+                this._rQlen = 0;
+                this._rQi = 0;
+            } else if (this._rQlen > this._rQmax) {
+                this._expand_compact_rQ();
+            }
+        } else {
+            Log.Debug("Ignoring empty message");
+        }
+    }
+};

BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/favicon.ico


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/clipboard.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/connect.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/ctrlaltdel.png


BIN
board/GfA/Display001/rootfs/var/GfA/WebVnc/novnc/images/disconnect.png


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