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/>
 
-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">
     <!--
     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) != "]")
 	      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>
@@ -41,5 +41,5 @@ If the above Java applet does not work, you can also try the new JavaScript-only
 <br/>
 <br/>
 <br/>
-<A href="http://libvncserver.sf.net/">LibVNCServer/LibVNCClient Homepage</A>
+<A href="https://libvnc.github.io/">LibVNCServer/LibVNCClient Homepage</A>
 </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:
 
 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
-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
 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)
 	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
-	mode of x11vnc (e.g. both services thru port 5900, etc) 
+	mode of x11vnc (e.g. both services through port 5900, etc)
 	
   urlPrefix
 	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
 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.
 
 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]
 #
-#         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.  
 #
 #         Examples:
@@ -109,10 +109,10 @@
 #
 # -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
-#         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 
 #         (see -sshargs above).
 #
@@ -351,7 +351,7 @@ if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then
 	STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG
 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"
 if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then
 	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