Initial revision


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/LICENCE.TXT b/LICENCE.TXT
new file mode 100644
index 0000000..ae3b531
--- /dev/null
+++ b/LICENCE.TXT
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+	  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+    USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..9341f4a
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,5 @@
+
+SUBDIRS = @ZLIB_DIR@ rdr network Xregion rfb tx x0vncserver vncviewer \
+          vncconfig vncpasswd
+
+# followed by boilerplate.mk
diff --git a/README b/README
new file mode 100644
index 0000000..4c0e5fb
--- /dev/null
+++ b/README
@@ -0,0 +1,284 @@
+
+VNC 4.0 Source Distribution for Unix platforms
+==============================================
+
+Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+
+This software is distributed under the GNU General Public Licence as published
+by the Free Software Foundation.  See the file LICENCE.TXT for the conditions
+under which this software is made available.  VNC also contains code from other
+sources.  See the Acknowledgements section below, and the individual files for
+details of the conditions under which they are made available.
+
+
+There are six programs here:
+
+  vncviewer - this is the VNC viewer, or client, program for X.
+
+  Xvnc - this is the X VNC server - it is both a VNC server and an X server
+         with a "virtual" framebuffer.  You normally use the vncserver script
+         to start Xvnc.
+
+  vncserver - this is a wrapper script which makes starting an X VNC server
+              (i.e. desktop) more convenient.  It is written in Perl, so to use
+              the script you need that.
+
+  vncpasswd - this program allows you to change the password used to access
+              your X VNC desktops.  The vncserver script uses this program when
+              you first start a VNC server.
+
+  vncconfig - this program is used to configure and control a running instance
+              of Xvnc.
+
+  x0vncserver - this is an inefficient VNC server which continuously polls any
+                X display, allowing it to be controlled via VNC.  It is
+                intended mainly as a demonstration of a simple VNC server.
+
+In addition to these standalone programs, this distribution can also be used to
+turn the native X server for a platform into a VNC server.  For XFree86 version
+4 servers, this is done using a module loaded at run-time.  For other X servers
+it requires replacing the native X server binary.
+
+To build this distribution you need a C++ compiler as well as a C compiler.
+You also need a reasonably recent version of the X window system installed.
+This comes as standard with most unix machines.  If you don't have it
+installed, see http://www.xfree86.org or http://www.x.org
+
+To build everything but Xvnc, do:
+
+  % ./configure
+  % make
+
+This should build first some libraries - zlib, rdr, network, Xregion, rfb and
+tx - then vncviewer, vncconfig and vncpasswd.  If you already have zlib
+installed on your system you can run "./configure --with-installed-zlib" if you
+prefer (this is strongly advised on FreeBSD, since we've been told there are
+problems otherwise).
+
+Building Xvnc
+=============
+
+Building Xvnc and the VNC support for native X servers is much more complex.
+If you don't need to build it, skip to the section below on installing.
+
+Xvnc differs from the other programs in that it is built inside the X source
+tree.  Unlike previous versions of Xvnc, we do not provide an X source tree
+with this distribution.  We have designed the distribution to be as independent
+as possible of the X tree used.
+
+We have successfully used XFree86 version 4.3.0, 4.2.0 and 3.3.6 (available
+from http://www.xfree86.org).  You could also try the original X.org tree
+available from http://www.x.org but this does not build as easily because of
+lack of support for C++, no support for building server only, and other issues.
+Note that the X tree is enormous and notoriously difficult to deal with -
+building it is not for the faint-hearted!
+
+Once you have a copy of the X source tree, make sure it is unpacked at the top
+level of this distribution, so that the xc directory of the X source tree
+matches the xc of this distribution, for example:
+
+  % tar xzf X420src-1.tgz
+
+Then you must apply a patch to some files in the X source tree:
+
+  % patch -Np0 <xc.patch
+
+If this works, you should be able to build the entire X tree, including Xvnc:
+
+  % cd xc
+  % make World
+
+This will take a long time, and will quite probably fail for one reason or
+another!  If you are having trouble, we suggest you try to build the X tree in
+isolation first before attempting it with the VNC additions.
+
+If successful, in the xc/programs/Xserver directory you should find an Xvnc
+binary, plus the native X server binary(ies) for your platform with VNC support
+compiled in.  If you are building from an XFree86 version 4 tree on a supported
+platform, you should also find a vnc.so module in
+xc/programs/Xserver/vnc/modules.
+
+Exactly which X extensions and features are built into Xvnc and the native X
+server binary is determined by the settings in xc/config/cf.  The file vnc.def
+contains the settings we use to build our binary distributions.  You may need
+to edit this and the other files as appropriate.
+
+Installing
+==========
+
+Different unix platforms have different conventions for where software should
+be installed.  To copy the programs to some directory which is in your PATH
+environment variable, such as /usr/local/bin, there is a script called
+vncinstall which you can use:
+
+  % cd ..
+  % ./vncinstall /usr/local/bin
+
+This will also attempt to install the manual pages in an appropriate directory.
+You can specify an alternative directory as a second argument to vncinstall:
+
+  % ./vncinstall /usr/local/bin /usr/local/man
+
+It will also try to install the vnc.so XFree86 version 4 module if appropriate.
+This will be copied to the /usr/X11R6/lib/modules/extensions directory and can
+be enabled like any other module by adding a Load "vnc" line to the Module
+section of XF86Config.  The parameters listed in the Xvnc manual page can be
+set as options in XF86Config e.g. Option "passwordFile" "/root/.vnc/passwd".
+Note that for some reason options cannot be set in the Module section of
+XF86Config - try the Screen section.
+
+If you want to use the Java VNC viewer, you should copy the files from
+the java directory to some suitable installation directory such as
+/usr/local/vnc/classes:
+
+  % mkdir -p /usr/local/vnc/classes
+  % cp java/* /usr/local/vnc/classes
+
+We recommend that you use the vncserver script to run Xvnc for you.  You can
+edit the script as appropriate for your site.  Things you may need to change
+include:
+
+ * The location of Perl - if Perl is not installed in /usr/bin you'll need
+   to edit the "#!/usr/bin/perl" first line of vncserver.
+
+ * Xvnc's font path and color database.  If you have an installation of
+   X which is not in the standard place you may need to add arguments to the
+   Xvnc command line to set these.  These should be appended to the $cmd
+   variable at the comment "# Add font path and color database...".
+
+ * $vncJavaFiles - this specifies the location of the files for
+   the VNC viewer Java applet.  The default is /usr/local/vnc/classes.
+
+
+ACKNOWLEDGEMENTS
+================
+
+This distribution contains public domain DES software by Richard Outerbridge.
+This is:
+
+    Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+    (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+
+
+This distribution contains software from the X Window System.  This is:
+
+ Copyright 1987, 1988, 1998  The Open Group
+ 
+ Permission to use, copy, modify, distribute, and sell this software and its
+ documentation for any purpose is hereby granted without fee, provided that
+ the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation.
+ 
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ 
+ Except as contained in this notice, the name of The Open Group shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from The Open Group.
+ 
+ 
+ Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+ 
+                         All Rights Reserved
+ 
+ Permission to use, copy, modify, and distribute this software and its 
+ documentation for any purpose and without fee is hereby granted, 
+ provided that the above copyright notice appear in all copies and that
+ both that copyright notice and this permission notice appear in 
+ supporting documentation, and that the name of Digital not be
+ used in advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.  
+ 
+ DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ SOFTWARE.
+
+
+This distribution contains zlib compression software.  This is:
+
+  Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+This distribution contains Java DES software by Dave Zimmerman
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>.  This 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.
+
+    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/
diff --git a/README.hpux b/README.hpux
new file mode 100644
index 0000000..ecbe966
--- /dev/null
+++ b/README.hpux
@@ -0,0 +1,39 @@
+I have managed to build Xvnc on HPUX but only with some ugly hacking of
+the X tree.  The X tree I used as the basis for the build is the XFree86 4.2.0
+tree.  The XFree86 4.3.0 tree is unsuitable as it seems to have had some HPUX
+stuff removed from it.  I built using the aCC C++ compiler.
+
+Set the following environment variables:
+
+  % CXX=/opt/aCC/bin/aCC
+  % CFLAGS="+DAportable"
+  % CXXFLAGS="+DAportable -AA +W749 +W740"
+  % BOOTSTRAPCFLAGS=-Dhpux
+  % export CXX CFLAGS CXXFLAGS BOOTSTRAPCFLAGS
+
+Build the main part of the VNC distribution as normal:
+
+  % ./configure
+  % make
+
+Unpack the X tree and apply the patches in xc.patch:
+
+  % gunzip -c X420src-1.tgz | tar xf -
+  % patch -Np0 <xc.patch
+
+Then additionally apply the patches in hpux.patch:
+
+  % patch -Np0 <hpux.patch
+
+Finally try building the X tree:
+
+  % cd xc
+  % make World
+
+If it all goes to plan you will be left with Xvnc in xc/programs/Xserver.  You
+will probably have to modify the vncserver script to set up a sensible font
+path, since many of the font directories on HPUX are different from the
+defaults compiled into Xvnc.
+
+If anyone can find a neater way of building a VNC-compatible X tree on HPUX
+please let us know (see http://www.realvnc.com for contact details).
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..0811395
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,126 @@
+
+VNC 4 Source Distribution for Windows platforms
+=============================================
+
+VNC 4 is Copyright RealVNC Ltd. 2002-2004.  This software is
+distributed under the GNU General Public Licence as published by the
+Free Software Foundation.  See the accompanying licence.txt file for
+the conditions under which the software is made available.
+
+VNC 4 also contains code from other sources.  See the Acknowledgements
+section below, and the individual files for details of the conditions
+under which they are made available.
+
+The source tree contains a number of directories, and is most easily
+built by loading the VNC workspace file (vnc.dsw) into Microsoft
+Visual Studio 6/7.  This will preserve the required dependencies
+between the sub-projects.
+
+There are three main executable projects:
+
+	vncviewer - The VNC Viewer for Win32.
+
+	winvnc - The VNC Server for Win32 (command-line operation
+		 only).
+
+	vncconfig - The configuration applet and GUI front-end for VNC
+		    Server.
+
+These projects are designed to be built using Microsoft Visual C++
+6.0, and should also compile with Microsoft Visual Studio .NET
+(version 7).  Other compilers have not been tested but the code base
+is extremely portable.
+
+
+ACKNOWLEDGEMENTS
+================
+
+This distribution contains zlib software by Jean-loup Gailly and Mark Adler.
+This is:
+
+    Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler.
+
+	This software is provided 'as-is', without any express or implied
+	warranty.  In no event will the authors be held liable for any damages
+	arising from the use of this software.
+
+	Permission is granted to anyone to use this software for any purpose,
+	including commercial applications, and to alter it and redistribute it
+	freely, subject to the following restrictions:
+
+	1. The origin of this software must not be misrepresented; you must not
+	   claim that you wrote the original software. If you use this software
+	   in a product, an acknowledgment in the product documentation would be
+       appreciated but is not required.
+	2. Altered source versions must be plainly marked as such, and must not be
+       misrepresented as being the original software.
+	3. This notice may not be removed or altered from any source distribution.
+
+	Jean-loup Gailly        Mark Adler
+	jloup@gzip.org          madler@alumni.caltech.edu
+
+
+	The data format used by the zlib library is described by RFCs (Request for
+	Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+	(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+
+
+This distribution contains public domain DES software by Richard Outerbridge.
+This is:
+
+	Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+	(GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+
+
+This distribution contains Java DES software by Dave Zimmerman
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>.  This 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.
+
+    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/
diff --git a/README_BINARY.txt b/README_BINARY.txt
new file mode 100644
index 0000000..8f89fe9
--- /dev/null
+++ b/README_BINARY.txt
@@ -0,0 +1,120 @@
+
+VNC 4 Binary Distribution for Windows platforms
+=============================================
+
+VNC 4 is Copyright RealVNC Ltd. 2002-2004.  This software is
+distributed under the GNU General Public Licence as published by the
+Free Software Foundation. VNC also contains code from other sources,
+as outlined in the Acknowledgements section below.
+
+The installer package contains two VNC components:
+
+	VNC Viewer - this is the VNC Viewer, or client, program for
+		     Win32.
+			[Win9x, WinME, NT4, Win2000, WinXP,
+			 Windows 2003 Server]
+
+	VNC Server - this is the VNC Server for Win32. It allows a
+		     Windows desktop to be accessed remotely using a
+		     VNC Viewer.
+			[Win9x, WinME, NT4, Win2000, WinXP(*),
+			 Windows 2003 Server]
+
+(*)  May not work if the in-built Fast User Switching or Remote
+     Administration features are in use.
+
+Both components were built using Microsoft Visual C++ 6.0, and are
+designed to operate upon the Win32 platforms listed above.
+
+ACKNOWLEDGEMENTS
+================
+
+This distribution contains zlib software by Jean-loup Gailly and Mark Adler.
+This is:
+
+    Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler.
+
+	This software is provided 'as-is', without any express or implied
+	warranty.  In no event will the authors be held liable for any damages
+	arising from the use of this software.
+
+	Permission is granted to anyone to use this software for any purpose,
+	including commercial applications, and to alter it and redistribute it
+	freely, subject to the following restrictions:
+
+	1. The origin of this software must not be misrepresented; you must not
+	   claim that you wrote the original software. If you use this software
+	   in a product, an acknowledgment in the product documentation would be
+       appreciated but is not required.
+	2. Altered source versions must be plainly marked as such, and must not be
+       misrepresented as being the original software.
+	3. This notice may not be removed or altered from any source distribution.
+
+	Jean-loup Gailly        Mark Adler
+	jloup@gzip.org          madler@alumni.caltech.edu
+
+
+	The data format used by the zlib library is described by RFCs (Request for
+	Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+	(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+
+
+This distribution contains public domain DES software by Richard Outerbridge.
+This is:
+
+	Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+	(GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+
+
+This distribution contains Java DES software by Dave Zimmerman
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>.  This 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.
+
+    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/
diff --git a/Xregion/Makefile.in b/Xregion/Makefile.in
new file mode 100644
index 0000000..878a29b
--- /dev/null
+++ b/Xregion/Makefile.in
@@ -0,0 +1,15 @@
+
+SRCS = Region.c
+
+OBJS = $(SRCS:.c=.o)
+
+library = libXregion.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/Xregion/Region.c b/Xregion/Region.c
new file mode 100644
index 0000000..bf2d123
--- /dev/null
+++ b/Xregion/Region.c
@@ -0,0 +1,1687 @@
+/* $Xorg: Region.c,v 1.6 2001/02/09 02:03:35 xorgcvs Exp $ */
+/************************************************************************
+
+Copyright 1987, 1988, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/Region.c,v 1.8 2001/12/14 19:54:05 dawes Exp $ */
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+
+#include "Xregion.h"
+//#include "Xlibint.h"
+//#include "Xutil.h"
+#include "region.h"
+//#include "poly.h"
+
+#ifdef DEBUG
+#include <stdio.h>
+#define assert(expr) {if (!(expr)) fprintf(stderr,\
+"Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); }
+#else
+#define assert(expr)
+#endif
+
+typedef void (*voidProcp)();
+
+static void miRegionOp();
+/*	Create a new empty region	*/
+Region
+XCreateRegion()
+{
+    Region temp;
+
+    if (! (temp = ( Region )Xmalloc( (unsigned) sizeof( REGION ))))
+	return (Region) NULL;
+    if (! (temp->rects = ( BOX * )Xmalloc( (unsigned) sizeof( BOX )))) {
+	Xfree((char *) temp);
+	return (Region) NULL;
+    }
+    temp->numRects = 0;
+    temp->extents.x1 = 0;
+    temp->extents.y1 = 0;
+    temp->extents.x2 = 0;
+    temp->extents.y2 = 0;
+    temp->size = 1;
+    return( temp );
+}
+
+int
+XClipBox( r, rect )
+    Region r;
+    XRectangle *rect;
+{
+    rect->x = r->extents.x1;
+    rect->y = r->extents.y1;
+    rect->width = r->extents.x2 - r->extents.x1;
+    rect->height = r->extents.y2 - r->extents.y1;
+    return 1;
+}
+
+int
+XUnionRectWithRegion(rect, source, dest)
+    register XRectangle *rect;
+    Region source, dest;
+{
+    REGION region;
+
+    if (!rect->width || !rect->height)
+	return 0;
+    region.rects = &region.extents;
+    region.numRects = 1;
+    region.extents.x1 = rect->x;
+    region.extents.y1 = rect->y;
+    region.extents.x2 = rect->x + rect->width;
+    region.extents.y2 = rect->y + rect->height;
+    region.size = 1;
+
+    return XUnionRegion(&region, source, dest);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ *	Reset the extents of a region to what they should be. Called by
+ *	miSubtract and miIntersect b/c they can't figure it out along the
+ *	way or do so easily, as miUnion can.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The region's 'extents' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+miSetExtents (pReg)
+    Region	  	pReg;
+{
+    register BoxPtr	pBox,
+			pBoxEnd,
+			pExtents;
+
+    if (pReg->numRects == 0)
+    {
+	pReg->extents.x1 = 0;
+	pReg->extents.y1 = 0;
+	pReg->extents.x2 = 0;
+	pReg->extents.y2 = 0;
+	return;
+    }
+
+    pExtents = &pReg->extents;
+    pBox = pReg->rects;
+    pBoxEnd = &pBox[pReg->numRects - 1];
+
+    /*
+     * Since pBox is the first rectangle in the region, it must have the
+     * smallest y1 and since pBoxEnd is the last rectangle in the region,
+     * it must have the largest y2, because of banding. Initialize x1 and
+     * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
+     * to...
+     */
+    pExtents->x1 = pBox->x1;
+    pExtents->y1 = pBox->y1;
+    pExtents->x2 = pBoxEnd->x2;
+    pExtents->y2 = pBoxEnd->y2;
+
+    assert(pExtents->y1 < pExtents->y2);
+    while (pBox <= pBoxEnd)
+    {
+	if (pBox->x1 < pExtents->x1)
+	{
+	    pExtents->x1 = pBox->x1;
+	}
+	if (pBox->x2 > pExtents->x2)
+	{
+	    pExtents->x2 = pBox->x2;
+	}
+	pBox++;
+    }
+    assert(pExtents->x1 < pExtents->x2);
+}
+
+extern void _XSetClipRectangles();
+
+#if 0
+int
+XSetRegion( dpy, gc, r )
+    Display *dpy;
+    GC gc;
+    register Region r;
+{
+    register int i;
+    register XRectangle *xr, *pr;
+    register BOX *pb;
+    unsigned long total;
+
+    LockDisplay (dpy);
+    total = r->numRects * sizeof (XRectangle);
+    if ((xr = (XRectangle *) _XAllocTemp(dpy, total))) {
+	for (pr = xr, pb = r->rects, i = r->numRects; --i >= 0; pr++, pb++) {
+	    pr->x = pb->x1;
+	    pr->y = pb->y1;
+	    pr->width = pb->x2 - pb->x1;
+	    pr->height = pb->y2 - pb->y1;
+	}
+    }
+    if (xr || !r->numRects)
+	_XSetClipRectangles(dpy, gc, 0, 0, xr, r->numRects, YXBanded);
+    if (xr)
+	_XFreeTemp(dpy, (char *)xr, total);
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return 1;
+}
+#endif
+
+int
+XDestroyRegion( r )
+    Region r;
+{
+    Xfree( (char *) r->rects );
+    Xfree( (char *) r );
+    return 1;
+}
+
+
+/* TranslateRegion(pRegion, x, y)
+   translates in place
+   added by raymond
+*/
+
+int
+XOffsetRegion(pRegion, x, y)
+    register Region pRegion;
+    register int x;
+    register int y;
+{
+    register int nbox;
+    register BOX *pbox;
+
+    pbox = pRegion->rects;
+    nbox = pRegion->numRects;
+
+    while(nbox--)
+    {
+	pbox->x1 += x;
+	pbox->x2 += x;
+	pbox->y1 += y;
+	pbox->y2 += y;
+	pbox++;
+    }
+    pRegion->extents.x1 += x;
+    pRegion->extents.x2 += x;
+    pRegion->extents.y1 += y;
+    pRegion->extents.y2 += y;
+    return 1;
+}
+
+/* 
+   Utility procedure Compress:
+   Replace r by the region r', where 
+     p in r' iff (Quantifer m <= dx) (p + m in r), and
+     Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
+     (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
+
+   Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
+   of all points p such that p and the next dx points on the same
+   horizontal scan line are all in r.  We do this using by noting
+   that p is the head of a run of length 2^i + k iff p is the head
+   of a run of length 2^i and p+2^i is the head of a run of length
+   k. Thus, the loop invariant: s contains the region corresponding
+   to the runs of length shift.  r contains the region corresponding
+   to the runs of length 1 + dxo & (shift-1), where dxo is the original
+   value of dx.  dx = dxo & ~(shift-1).  As parameters, s and t are
+   scratch regions, so that we don't have to allocate them on every
+   call.
+*/
+
+#define ZOpRegion(a,b,c) if (grow) XUnionRegion(a,b,c); \
+			 else XIntersectRegion(a,b,c)
+#define ZShiftRegion(a,b) if (xdir) XOffsetRegion(a,b,0); \
+			  else XOffsetRegion(a,0,b)
+#define ZCopyRegion(a,b) XUnionRegion(a,a,b)
+
+static void
+Compress(r, s, t, dx, xdir, grow)
+    Region r, s, t;
+    register unsigned dx;
+    register int xdir, grow;
+{
+    register unsigned shift = 1;
+
+    ZCopyRegion(r, s);
+    while (dx) {
+        if (dx & shift) {
+            ZShiftRegion(r, -(int)shift);
+            ZOpRegion(r, s, r);
+            dx -= shift;
+            if (!dx) break;
+        }
+        ZCopyRegion(s, t);
+        ZShiftRegion(s, -(int)shift);
+        ZOpRegion(s, t, s);
+        shift <<= 1;
+    }
+}
+
+#undef ZOpRegion
+#undef ZShiftRegion
+#undef ZCopyRegion
+
+int
+XShrinkRegion(r, dx, dy)
+    Region r;
+    int dx, dy;
+{
+    Region s, t;
+    int grow;
+
+    if (!dx && !dy) return 0;
+    if ((! (s = XCreateRegion()))  || (! (t = XCreateRegion()))) return 0;
+    if ((grow = (dx < 0))) dx = -dx;
+    if (dx) Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
+    if ((grow = (dy < 0))) dy = -dy;
+    if (dy) Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
+    XOffsetRegion(r, dx, dy);
+    XDestroyRegion(s);
+    XDestroyRegion(t);
+    return 0;
+}
+
+#ifdef notdef
+/***********************************************************
+ *     Bop down the array of rects until we have passed
+ *     scanline y.  numRects is the size of the array.
+ ***********************************************************/
+
+static BOX 
+*IndexRects(rects, numRects, y)
+    register BOX *rects;
+    register int numRects;
+    register int y;
+{
+     while ((numRects--) && (rects->y2 <= y))
+        rects++;
+     return(rects);
+}
+#endif
+
+/*======================================================================
+ *	    Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ *	Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miIntersectO (pReg, r1, r1End, r2, r2End, y1, y2)
+    register Region	pReg;
+    register BoxPtr	r1;
+    BoxPtr  	  	r1End;
+    register BoxPtr	r2;
+    BoxPtr  	  	r2End;
+    short    	  	y1;
+    short    	  	y2;
+{
+    register short  	x1;
+    register short  	x2;
+    register BoxPtr	pNextRect;
+
+    pNextRect = &pReg->rects[pReg->numRects];
+
+    while ((r1 != r1End) && (r2 != r2End))
+    {
+	x1 = max(r1->x1,r2->x1);
+	x2 = min(r1->x2,r2->x2);
+
+	/*
+	 * If there's any overlap between the two rectangles, add that
+	 * overlap to the new region.
+	 * There's no need to check for subsumption because the only way
+	 * such a need could arise is if some region has two rectangles
+	 * right next to each other. Since that should never happen...
+	 */
+	if (x1 < x2)
+	{
+	    assert(y1<y2);
+
+	    MEMCHECK(pReg, pNextRect, pReg->rects);
+	    pNextRect->x1 = x1;
+	    pNextRect->y1 = y1;
+	    pNextRect->x2 = x2;
+	    pNextRect->y2 = y2;
+	    pReg->numRects += 1;
+	    pNextRect++;
+	    assert(pReg->numRects <= pReg->size);
+	}
+
+	/*
+	 * Need to advance the pointers. Shift the one that extends
+	 * to the right the least, since the other still has a chance to
+	 * overlap with that region's next rectangle, if you see what I mean.
+	 */
+	if (r1->x2 < r2->x2)
+	{
+	    r1++;
+	}
+	else if (r2->x2 < r1->x2)
+	{
+	    r2++;
+	}
+	else
+	{
+	    r1++;
+	    r2++;
+	}
+    }
+    return 0;	/* lint */
+}
+
+int
+XIntersectRegion(reg1, reg2, newReg)
+    Region 	  	reg1;
+    Region	  	reg2;          /* source regions     */
+    register Region 	newReg;               /* destination Region */
+{
+   /* check for trivial reject */
+    if ( (!(reg1->numRects)) || (!(reg2->numRects))  ||
+	(!EXTENTCHECK(&reg1->extents, &reg2->extents)))
+        newReg->numRects = 0;
+    else
+	miRegionOp (newReg, reg1, reg2, 
+    		(voidProcp) miIntersectO, (voidProcp) NULL, (voidProcp) NULL);
+    
+    /*
+     * Can't alter newReg's extents before we call miRegionOp because
+     * it might be one of the source regions and miRegionOp depends
+     * on the extents of those regions being the same. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    miSetExtents(newReg);
+    return 1;
+}
+
+static void
+miRegionCopy(dstrgn, rgn)
+    register Region dstrgn;
+    register Region rgn;
+
+{
+    if (dstrgn != rgn) /*  don't want to copy to itself */
+    {  
+        if (dstrgn->size < rgn->numRects)
+        {
+            if (dstrgn->rects)
+            {
+		BOX *prevRects = dstrgn->rects;
+		
+                if (! (dstrgn->rects = (BOX *)
+		       Xrealloc((char *) dstrgn->rects,
+				(unsigned) rgn->numRects * (sizeof(BOX))))) {
+		    Xfree(prevRects);
+		    return;
+		}
+            }
+            dstrgn->size = rgn->numRects;
+	}
+        dstrgn->numRects = rgn->numRects;
+        dstrgn->extents.x1 = rgn->extents.x1;
+        dstrgn->extents.y1 = rgn->extents.y1;
+        dstrgn->extents.x2 = rgn->extents.x2;
+        dstrgn->extents.y2 = rgn->extents.y2;
+
+	memcpy((char *) dstrgn->rects, (char *) rgn->rects,
+	       (int) (rgn->numRects * sizeof(BOX)));
+    }
+}
+
+#ifdef notdef
+
+/*
+ *  combinRegs(newReg, reg1, reg2)
+ *    if one region is above or below the other.
+*/ 
+
+static void
+combineRegs(newReg, reg1, reg2)
+    register Region newReg;
+    Region reg1;
+    Region reg2;
+{
+    register Region tempReg;
+    register BOX *rects;
+    register BOX *rects1;
+    register BOX *rects2;
+    register int total;
+
+    rects1 = reg1->rects;
+    rects2 = reg2->rects;
+
+    total = reg1->numRects + reg2->numRects;
+    if (! (tempReg = XCreateRegion()))
+	return;
+    tempReg->size = total;
+    /*  region 1 is below region 2  */
+    if (reg1->extents.y1 > reg2->extents.y1)
+    {
+        miRegionCopy(tempReg, reg2);
+        rects = &tempReg->rects[tempReg->numRects];
+        total -= tempReg->numRects;
+        while (total--)
+            *rects++ = *rects1++;
+    }
+    else
+    {
+        miRegionCopy(tempReg, reg1);
+        rects = &tempReg->rects[tempReg->numRects];
+        total -= tempReg->numRects;
+        while (total--)
+            *rects++ = *rects2++;
+    }
+    tempReg->extents = reg1->extents;
+    tempReg->numRects = reg1->numRects + reg2->numRects;
+    EXTENTS(&reg2->extents, tempReg);  
+    miRegionCopy(newReg, tempReg);
+    Xfree((char *)tempReg);
+}
+
+/*
+ *  QuickCheck checks to see if it does not have to go through all the
+ *  the ugly code for the region call.  It returns 1 if it did all
+ *  the work for Union, otherwise 0 - still work to be done.
+*/ 
+
+static int
+QuickCheck(newReg, reg1, reg2)
+    Region newReg, reg1, reg2;
+{
+
+    /*  if unioning with itself or no rects to union with  */
+    if ( (reg1 == reg2) || (!(reg1->numRects)) )
+    {
+        miRegionCopy(newReg, reg2);
+        return TRUE;
+    }
+
+    /*   if nothing to union   */
+    if (!(reg2->numRects))
+    {
+        miRegionCopy(newReg, reg1);
+        return TRUE;
+    }
+
+    /*   could put an extent check to see if add above or below */
+
+    if ((reg1->extents.y1 >= reg2->extents.y2) ||
+        (reg2->extents.y1 >= reg1->extents.y2) )
+    {
+        combineRegs(newReg, reg1, reg2);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/*   TopRects(rects, reg1, reg2)
+ * N.B. We now assume that reg1 and reg2 intersect.  Therefore we are
+ * NOT checking in the two while loops for stepping off the end of the
+ * region.  
+ */ 
+
+static int
+TopRects(newReg, rects, reg1, reg2, FirstRect)
+    register Region newReg;
+    register BOX *rects;
+    register Region reg1;
+    register Region reg2; 
+    BOX *FirstRect;
+{
+    register BOX *tempRects;
+
+    /*  need to add some rects from region 1 */
+    if (reg1->extents.y1 < reg2->extents.y1)
+    {
+        tempRects = reg1->rects;
+        while(tempRects->y1 < reg2->extents.y1)
+	{
+	    MEMCHECK(newReg, rects, FirstRect);
+            ADDRECTNOX(newReg,rects, tempRects->x1, tempRects->y1, 
+		       tempRects->x2, MIN(tempRects->y2, reg2->extents.y1));
+            tempRects++;
+	}
+    }
+    /*  need to add some rects from region 2 */
+    if (reg2->extents.y1 < reg1->extents.y1)
+    {
+        tempRects = reg2->rects;
+        while (tempRects->y1 < reg1->extents.y1)
+        {
+            MEMCHECK(newReg, rects, FirstRect);
+            ADDRECTNOX(newReg, rects, tempRects->x1,tempRects->y1, 
+		       tempRects->x2, MIN(tempRects->y2, reg1->extents.y1));
+            tempRects++;
+	}
+    }
+    return 1;
+}
+#endif
+
+/*======================================================================
+ *	    Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ *	Attempt to merge the boxes in the current band with those in the
+ *	previous one. Used only by miRegionOp.
+ *
+ * Results:
+ *	The new index for the previous band.
+ *
+ * Side Effects:
+ *	If coalescing takes place:
+ *	    - rectangles in the previous band will have their y2 fields
+ *	      altered.
+ *	    - pReg->numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static int*/
+static int
+miCoalesce (pReg, prevStart, curStart)
+    register Region	pReg;	    	/* Region to coalesce */
+    int	    	  	prevStart;  	/* Index of start of previous band */
+    int	    	  	curStart;   	/* Index of start of current band */
+{
+    register BoxPtr	pPrevBox;   	/* Current box in previous band */
+    register BoxPtr	pCurBox;    	/* Current box in current band */
+    register BoxPtr	pRegEnd;    	/* End of region */
+    int	    	  	curNumRects;	/* Number of rectangles in current
+					 * band */
+    int	    	  	prevNumRects;	/* Number of rectangles in previous
+					 * band */
+    int	    	  	bandY1;	    	/* Y1 coordinate for current band */
+
+    pRegEnd = &pReg->rects[pReg->numRects];
+
+    pPrevBox = &pReg->rects[prevStart];
+    prevNumRects = curStart - prevStart;
+
+    /*
+     * Figure out how many rectangles are in the current band. Have to do
+     * this because multiple bands could have been added in miRegionOp
+     * at the end when one region has been exhausted.
+     */
+    pCurBox = &pReg->rects[curStart];
+    bandY1 = pCurBox->y1;
+    for (curNumRects = 0;
+	 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
+	 curNumRects++)
+    {
+	pCurBox++;
+    }
+    
+    if (pCurBox != pRegEnd)
+    {
+	/*
+	 * If more than one band was added, we have to find the start
+	 * of the last band added so the next coalescing job can start
+	 * at the right place... (given when multiple bands are added,
+	 * this may be pointless -- see above).
+	 */
+	pRegEnd--;
+	while (pRegEnd[-1].y1 == pRegEnd->y1)
+	{
+	    pRegEnd--;
+	}
+	curStart = pRegEnd - pReg->rects;
+	pRegEnd = pReg->rects + pReg->numRects;
+    }
+	
+    if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
+	pCurBox -= curNumRects;
+	/*
+	 * The bands may only be coalesced if the bottom of the previous
+	 * matches the top scanline of the current.
+	 */
+	if (pPrevBox->y2 == pCurBox->y1)
+	{
+	    /*
+	     * Make sure the bands have boxes in the same places. This
+	     * assumes that boxes have been added in such a way that they
+	     * cover the most area possible. I.e. two boxes in a band must
+	     * have some horizontal space between them.
+	     */
+	    do
+	    {
+		if ((pPrevBox->x1 != pCurBox->x1) ||
+		    (pPrevBox->x2 != pCurBox->x2))
+		{
+		    /*
+		     * The bands don't line up so they can't be coalesced.
+		     */
+		    return (curStart);
+		}
+		pPrevBox++;
+		pCurBox++;
+		prevNumRects -= 1;
+	    } while (prevNumRects != 0);
+
+	    pReg->numRects -= curNumRects;
+	    pCurBox -= curNumRects;
+	    pPrevBox -= curNumRects;
+
+	    /*
+	     * The bands may be merged, so set the bottom y of each box
+	     * in the previous band to that of the corresponding box in
+	     * the current band.
+	     */
+	    do
+	    {
+		pPrevBox->y2 = pCurBox->y2;
+		pPrevBox++;
+		pCurBox++;
+		curNumRects -= 1;
+	    } while (curNumRects != 0);
+
+	    /*
+	     * If only one band was added to the region, we have to backup
+	     * curStart to the start of the previous band.
+	     *
+	     * If more than one band was added to the region, copy the
+	     * other bands down. The assumption here is that the other bands
+	     * came from the same region as the current one and no further
+	     * coalescing can be done on them since it's all been done
+	     * already... curStart is already in the right place.
+	     */
+	    if (pCurBox == pRegEnd)
+	    {
+		curStart = prevStart;
+	    }
+	    else
+	    {
+		do
+		{
+		    *pPrevBox++ = *pCurBox++;
+		} while (pCurBox != pRegEnd);
+	    }
+	    
+	}
+    }
+    return (curStart);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ *	Apply an operation to two regions. Called by miUnion, miInverse,
+ *	miSubtract, miIntersect...
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The new region is overwritten.
+ *
+ * Notes:
+ *	The idea behind this function is to view the two regions as sets.
+ *	Together they cover a rectangle of area that this function divides
+ *	into horizontal bands where points are covered only by one region
+ *	or by both. For the first case, the nonOverlapFunc is called with
+ *	each the band and the band's upper and lower extents. For the
+ *	second, the overlapFunc is called to process the entire band. It
+ *	is responsible for clipping the rectangles in the band, though
+ *	this function provides the boundaries.
+ *	At the end of each band, the new region is coalesced, if possible,
+ *	to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static void
+miRegionOp(newReg, reg1, reg2, overlapFunc,  nonOverlap1Func, nonOverlap2Func)
+    register Region 	newReg;	    	    	/* Place to store result */
+    Region	  	reg1;	    	    	/* First region in operation */
+    Region	  	reg2;	    	    	/* 2d region in operation */
+    void    	  	(*overlapFunc)();   	/* Function to call for over-
+						 * lapping bands */
+    void    	  	(*nonOverlap1Func)();	/* Function to call for non-
+						 * overlapping bands in region
+						 * 1 */
+    void    	  	(*nonOverlap2Func)();	/* Function to call for non-
+						 * overlapping bands in region
+						 * 2 */
+{
+    register BoxPtr	r1; 	    	    	/* Pointer into first region */
+    register BoxPtr	r2; 	    	    	/* Pointer into 2d region */
+    BoxPtr  	  	r1End;	    	    	/* End of 1st region */
+    BoxPtr  	  	r2End;	    	    	/* End of 2d region */
+    register short  	ybot;	    	    	/* Bottom of intersection */
+    register short  	ytop;	    	    	/* Top of intersection */
+    BoxPtr  	  	oldRects;   	    	/* Old rects for newReg */
+    int	    	  	prevBand;   	    	/* Index of start of
+						 * previous band in newReg */
+    int	    	  	curBand;    	    	/* Index of start of current
+						 * band in newReg */
+    register BoxPtr 	r1BandEnd;  	    	/* End of current band in r1 */
+    register BoxPtr 	r2BandEnd;  	    	/* End of current band in r2 */
+    short     	  	top;	    	    	/* Top of non-overlapping
+						 * band */
+    short     	  	bot;	    	    	/* Bottom of non-overlapping
+						 * band */
+    
+    /*
+     * Initialization:
+     *	set r1, r2, r1End and r2End appropriately, preserve the important
+     * parts of the destination region until the end in case it's one of
+     * the two source regions, then mark the "new" region empty, allocating
+     * another array of rectangles for it to use.
+     */
+    r1 = reg1->rects;
+    r2 = reg2->rects;
+    r1End = r1 + reg1->numRects;
+    r2End = r2 + reg2->numRects;
+    
+    oldRects = newReg->rects;
+    
+    EMPTY_REGION(newReg);
+
+    /*
+     * Allocate a reasonable number of rectangles for the new region. The idea
+     * is to allocate enough so the individual functions don't need to
+     * reallocate and copy the array, which is time consuming, yet we don't
+     * have to worry about using too much memory. I hope to be able to
+     * nuke the Xrealloc() at the end of this function eventually.
+     */
+    newReg->size = max(reg1->numRects,reg2->numRects) * 2;
+
+    if (! (newReg->rects = (BoxPtr)
+	   Xmalloc ((unsigned) (sizeof(BoxRec) * newReg->size)))) {
+	newReg->size = 0;
+	return;
+    }
+    
+    /*
+     * Initialize ybot and ytop.
+     * In the upcoming loop, ybot and ytop serve different functions depending
+     * on whether the band being handled is an overlapping or non-overlapping
+     * band.
+     * 	In the case of a non-overlapping band (only one of the regions
+     * has points in the band), ybot is the bottom of the most recent
+     * intersection and thus clips the top of the rectangles in that band.
+     * ytop is the top of the next intersection between the two regions and
+     * serves to clip the bottom of the rectangles in the current band.
+     *	For an overlapping band (where the two regions intersect), ytop clips
+     * the top of the rectangles of both regions and ybot clips the bottoms.
+     */
+    if (reg1->extents.y1 < reg2->extents.y1)
+	ybot = reg1->extents.y1;
+    else
+	ybot = reg2->extents.y1;
+    
+    /*
+     * prevBand serves to mark the start of the previous band so rectangles
+     * can be coalesced into larger rectangles. qv. miCoalesce, above.
+     * In the beginning, there is no previous band, so prevBand == curBand
+     * (curBand is set later on, of course, but the first band will always
+     * start at index 0). prevBand and curBand must be indices because of
+     * the possible expansion, and resultant moving, of the new region's
+     * array of rectangles.
+     */
+    prevBand = 0;
+    
+    do
+    {
+	curBand = newReg->numRects;
+
+	/*
+	 * This algorithm proceeds one source-band (as opposed to a
+	 * destination band, which is determined by where the two regions
+	 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+	 * rectangle after the last one in the current band for their
+	 * respective regions.
+	 */
+	r1BandEnd = r1;
+	while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
+	{
+	    r1BandEnd++;
+	}
+	
+	r2BandEnd = r2;
+	while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
+	{
+	    r2BandEnd++;
+	}
+	
+	/*
+	 * First handle the band that doesn't intersect, if any.
+	 *
+	 * Note that attention is restricted to one band in the
+	 * non-intersecting region at once, so if a region has n
+	 * bands between the current position and the next place it overlaps
+	 * the other, this entire loop will be passed through n times.
+	 */
+	if (r1->y1 < r2->y1)
+	{
+	    top = max(r1->y1,ybot);
+	    bot = min(r1->y2,r2->y1);
+
+	    if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
+	    {
+		(* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
+	    }
+
+	    ytop = r2->y1;
+	}
+	else if (r2->y1 < r1->y1)
+	{
+	    top = max(r2->y1,ybot);
+	    bot = min(r2->y2,r1->y1);
+
+	    if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
+	    {
+		(* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
+	    }
+
+	    ytop = r1->y1;
+	}
+	else
+	{
+	    ytop = r1->y1;
+	}
+
+	/*
+	 * If any rectangles got added to the region, try and coalesce them
+	 * with rectangles from the previous band. Note we could just do
+	 * this test in miCoalesce, but some machines incur a not
+	 * inconsiderable cost for function calls, so...
+	 */
+	if (newReg->numRects != curBand)
+	{
+	    prevBand = miCoalesce (newReg, prevBand, curBand);
+	}
+
+	/*
+	 * Now see if we've hit an intersecting band. The two bands only
+	 * intersect if ybot > ytop
+	 */
+	ybot = min(r1->y2, r2->y2);
+	curBand = newReg->numRects;
+	if (ybot > ytop)
+	{
+	    (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+	}
+	
+	if (newReg->numRects != curBand)
+	{
+	    prevBand = miCoalesce (newReg, prevBand, curBand);
+	}
+
+	/*
+	 * If we've finished with a band (y2 == ybot) we skip forward
+	 * in the region to the next band.
+	 */
+	if (r1->y2 == ybot)
+	{
+	    r1 = r1BandEnd;
+	}
+	if (r2->y2 == ybot)
+	{
+	    r2 = r2BandEnd;
+	}
+    } while ((r1 != r1End) && (r2 != r2End));
+
+    /*
+     * Deal with whichever region still has rectangles left.
+     */
+    curBand = newReg->numRects;
+    if (r1 != r1End)
+    {
+	if (nonOverlap1Func != (void (*)())NULL)
+	{
+	    do
+	    {
+		r1BandEnd = r1;
+		while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
+		{
+		    r1BandEnd++;
+		}
+		(* nonOverlap1Func) (newReg, r1, r1BandEnd,
+				     max(r1->y1,ybot), r1->y2);
+		r1 = r1BandEnd;
+	    } while (r1 != r1End);
+	}
+    }
+    else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
+    {
+	do
+	{
+	    r2BandEnd = r2;
+	    while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
+	    {
+		 r2BandEnd++;
+	    }
+	    (* nonOverlap2Func) (newReg, r2, r2BandEnd,
+				max(r2->y1,ybot), r2->y2);
+	    r2 = r2BandEnd;
+	} while (r2 != r2End);
+    }
+
+    if (newReg->numRects != curBand)
+    {
+	(void) miCoalesce (newReg, prevBand, curBand);
+    }
+
+    /*
+     * A bit of cleanup. To keep regions from growing without bound,
+     * we shrink the array of rectangles to match the new number of
+     * rectangles in the region. This never goes to 0, however...
+     *
+     * Only do this stuff if the number of rectangles allocated is more than
+     * twice the number of rectangles in the region (a simple optimization...).
+     */
+    if (newReg->numRects < (newReg->size >> 1))
+    {
+	if (REGION_NOT_EMPTY(newReg))
+	{
+	    BoxPtr prev_rects = newReg->rects;
+	    newReg->size = newReg->numRects;
+	    newReg->rects = (BoxPtr) Xrealloc ((char *) newReg->rects,
+				   (unsigned) (sizeof(BoxRec) * newReg->size));
+	    if (! newReg->rects)
+		newReg->rects = prev_rects;
+	}
+	else
+	{
+	    /*
+	     * No point in doing the extra work involved in an Xrealloc if
+	     * the region is empty
+	     */
+	    newReg->size = 1;
+	    Xfree((char *) newReg->rects);
+	    newReg->rects = (BoxPtr) Xmalloc(sizeof(BoxRec));
+	}
+    }
+    Xfree ((char *) oldRects);
+    return;
+}
+
+
+/*======================================================================
+ *	    Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ *	Handle a non-overlapping band for the union operation. Just
+ *	Adds the rectangles into the region. Doesn't have to check for
+ *	subsumption or anything.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	pReg->numRects is incremented and the final rectangles overwritten
+ *	with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miUnionNonO (pReg, r, rEnd, y1, y2)
+    register Region	pReg;
+    register BoxPtr	r;
+    BoxPtr  	  	rEnd;
+    register short  	y1;
+    register short  	y2;
+{
+    register BoxPtr	pNextRect;
+
+    pNextRect = &pReg->rects[pReg->numRects];
+
+    assert(y1 < y2);
+
+    while (r != rEnd)
+    {
+	assert(r->x1 < r->x2);
+	MEMCHECK(pReg, pNextRect, pReg->rects);
+	pNextRect->x1 = r->x1;
+	pNextRect->y1 = y1;
+	pNextRect->x2 = r->x2;
+	pNextRect->y2 = y2;
+	pReg->numRects += 1;
+	pNextRect++;
+
+	assert(pReg->numRects<=pReg->size);
+	r++;
+    }
+    return 0;	/* lint */
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ *	Handle an overlapping band for the union operation. Picks the
+ *	left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Rectangles are overwritten in pReg->rects and pReg->numRects will
+ *	be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+/* static void*/
+static int
+miUnionO (pReg, r1, r1End, r2, r2End, y1, y2)
+    register Region	pReg;
+    register BoxPtr	r1;
+    BoxPtr  	  	r1End;
+    register BoxPtr	r2;
+    BoxPtr  	  	r2End;
+    register short	y1;
+    register short	y2;
+{
+    register BoxPtr	pNextRect;
+    
+    pNextRect = &pReg->rects[pReg->numRects];
+
+#define MERGERECT(r) \
+    if ((pReg->numRects != 0) &&  \
+	(pNextRect[-1].y1 == y1) &&  \
+	(pNextRect[-1].y2 == y2) &&  \
+	(pNextRect[-1].x2 >= r->x1))  \
+    {  \
+	if (pNextRect[-1].x2 < r->x2)  \
+	{  \
+	    pNextRect[-1].x2 = r->x2;  \
+	    assert(pNextRect[-1].x1<pNextRect[-1].x2); \
+	}  \
+    }  \
+    else  \
+    {  \
+	MEMCHECK(pReg, pNextRect, pReg->rects);  \
+	pNextRect->y1 = y1;  \
+	pNextRect->y2 = y2;  \
+	pNextRect->x1 = r->x1;  \
+	pNextRect->x2 = r->x2;  \
+	pReg->numRects += 1;  \
+        pNextRect += 1;  \
+    }  \
+    assert(pReg->numRects<=pReg->size);\
+    r++;
+    
+    assert (y1<y2);
+    while ((r1 != r1End) && (r2 != r2End))
+    {
+	if (r1->x1 < r2->x1)
+	{
+	    MERGERECT(r1);
+	}
+	else
+	{
+	    MERGERECT(r2);
+	}
+    }
+    
+    if (r1 != r1End)
+    {
+	do
+	{
+	    MERGERECT(r1);
+	} while (r1 != r1End);
+    }
+    else while (r2 != r2End)
+    {
+	MERGERECT(r2);
+    }
+    return 0;	/* lint */
+}
+
+int
+XUnionRegion(reg1, reg2, newReg)
+    Region 	  reg1;
+    Region	  reg2;             /* source regions     */
+    Region 	  newReg;                  /* destination Region */
+{
+    /*  checks all the simple cases */
+
+    /*
+     * Region 1 and 2 are the same or region 1 is empty
+     */
+    if ( (reg1 == reg2) || (!(reg1->numRects)) )
+    {
+        if (newReg != reg2)
+            miRegionCopy(newReg, reg2);
+        return 1;
+    }
+
+    /*
+     * if nothing to union (region 2 empty)
+     */
+    if (!(reg2->numRects))
+    {
+        if (newReg != reg1)
+            miRegionCopy(newReg, reg1);
+        return 1;
+    }
+
+    /*
+     * Region 1 completely subsumes region 2
+     */
+    if ((reg1->numRects == 1) && 
+	(reg1->extents.x1 <= reg2->extents.x1) &&
+	(reg1->extents.y1 <= reg2->extents.y1) &&
+	(reg1->extents.x2 >= reg2->extents.x2) &&
+	(reg1->extents.y2 >= reg2->extents.y2))
+    {
+        if (newReg != reg1)
+            miRegionCopy(newReg, reg1);
+        return 1;
+    }
+
+    /*
+     * Region 2 completely subsumes region 1
+     */
+    if ((reg2->numRects == 1) && 
+	(reg2->extents.x1 <= reg1->extents.x1) &&
+	(reg2->extents.y1 <= reg1->extents.y1) &&
+	(reg2->extents.x2 >= reg1->extents.x2) &&
+	(reg2->extents.y2 >= reg1->extents.y2))
+    {
+        if (newReg != reg2)
+            miRegionCopy(newReg, reg2);
+        return 1;
+    }
+
+    miRegionOp (newReg, reg1, reg2, (voidProcp) miUnionO, 
+    		(voidProcp) miUnionNonO, (voidProcp) miUnionNonO);
+
+    newReg->extents.x1 = min(reg1->extents.x1, reg2->extents.x1);
+    newReg->extents.y1 = min(reg1->extents.y1, reg2->extents.y1);
+    newReg->extents.x2 = max(reg1->extents.x2, reg2->extents.x2);
+    newReg->extents.y2 = max(reg1->extents.y2, reg2->extents.y2);
+
+    return 1;
+}
+
+
+/*======================================================================
+ * 	    	  Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ *	Deal with non-overlapping band for subtraction. Any parts from
+ *	region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	pReg may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miSubtractNonO1 (pReg, r, rEnd, y1, y2)
+    register Region	pReg;
+    register BoxPtr	r;
+    BoxPtr  	  	rEnd;
+    register short  	y1;
+    register short   	y2;
+{
+    register BoxPtr	pNextRect;
+	
+    pNextRect = &pReg->rects[pReg->numRects];
+	
+    assert(y1<y2);
+
+    while (r != rEnd)
+    {
+	assert(r->x1<r->x2);
+	MEMCHECK(pReg, pNextRect, pReg->rects);
+	pNextRect->x1 = r->x1;
+	pNextRect->y1 = y1;
+	pNextRect->x2 = r->x2;
+	pNextRect->y2 = y2;
+	pReg->numRects += 1;
+	pNextRect++;
+
+	assert(pReg->numRects <= pReg->size);
+
+	r++;
+    }
+    return 0;	/* lint */
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ *	Overlapping band subtraction. x1 is the left-most point not yet
+ *	checked.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	pReg may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miSubtractO (pReg, r1, r1End, r2, r2End, y1, y2)
+    register Region	pReg;
+    register BoxPtr	r1;
+    BoxPtr  	  	r1End;
+    register BoxPtr	r2;
+    BoxPtr  	  	r2End;
+    register short  	y1;
+    register short  	y2;
+{
+    register BoxPtr	pNextRect;
+    register int  	x1;
+    
+    x1 = r1->x1;
+    
+    assert(y1<y2);
+    pNextRect = &pReg->rects[pReg->numRects];
+
+    while ((r1 != r1End) && (r2 != r2End))
+    {
+	if (r2->x2 <= x1)
+	{
+	    /*
+	     * Subtrahend missed the boat: go to next subtrahend.
+	     */
+	    r2++;
+	}
+	else if (r2->x1 <= x1)
+	{
+	    /*
+	     * Subtrahend preceeds minuend: nuke left edge of minuend.
+	     */
+	    x1 = r2->x2;
+	    if (x1 >= r1->x2)
+	    {
+		/*
+		 * Minuend completely covered: advance to next minuend and
+		 * reset left fence to edge of new minuend.
+		 */
+		r1++;
+		if (r1 != r1End)
+		    x1 = r1->x1;
+	    }
+	    else
+	    {
+		/*
+		 * Subtrahend now used up since it doesn't extend beyond
+		 * minuend
+		 */
+		r2++;
+	    }
+	}
+	else if (r2->x1 < r1->x2)
+	{
+	    /*
+	     * Left part of subtrahend covers part of minuend: add uncovered
+	     * part of minuend to region and skip to next subtrahend.
+	     */
+	    assert(x1<r2->x1);
+	    MEMCHECK(pReg, pNextRect, pReg->rects);
+	    pNextRect->x1 = x1;
+	    pNextRect->y1 = y1;
+	    pNextRect->x2 = r2->x1;
+	    pNextRect->y2 = y2;
+	    pReg->numRects += 1;
+	    pNextRect++;
+
+	    assert(pReg->numRects<=pReg->size);
+
+	    x1 = r2->x2;
+	    if (x1 >= r1->x2)
+	    {
+		/*
+		 * Minuend used up: advance to new...
+		 */
+		r1++;
+		if (r1 != r1End)
+		    x1 = r1->x1;
+	    }
+	    else
+	    {
+		/*
+		 * Subtrahend used up
+		 */
+		r2++;
+	    }
+	}
+	else
+	{
+	    /*
+	     * Minuend used up: add any remaining piece before advancing.
+	     */
+	    if (r1->x2 > x1)
+	    {
+		MEMCHECK(pReg, pNextRect, pReg->rects);
+		pNextRect->x1 = x1;
+		pNextRect->y1 = y1;
+		pNextRect->x2 = r1->x2;
+		pNextRect->y2 = y2;
+		pReg->numRects += 1;
+		pNextRect++;
+		assert(pReg->numRects<=pReg->size);
+	    }
+	    r1++;
+            if (r1 != r1End)
+              x1 = r1->x1;
+	}
+    }
+
+    /*
+     * Add remaining minuend rectangles to region.
+     */
+    while (r1 != r1End)
+    {
+	assert(x1<r1->x2);
+	MEMCHECK(pReg, pNextRect, pReg->rects);
+	pNextRect->x1 = x1;
+	pNextRect->y1 = y1;
+	pNextRect->x2 = r1->x2;
+	pNextRect->y2 = y2;
+	pReg->numRects += 1;
+	pNextRect++;
+
+	assert(pReg->numRects<=pReg->size);
+
+	r1++;
+	if (r1 != r1End)
+	{
+	    x1 = r1->x1;
+	}
+    }
+    return 0;	/* lint */
+}
+	
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ *	Subtract regS from regM and leave the result in regD.
+ *	S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Results:
+ *	TRUE.
+ *
+ * Side Effects:
+ *	regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+int
+XSubtractRegion(regM, regS, regD)
+    Region 	  	regM;
+    Region	  	regS;          
+    register Region	regD;
+{
+   /* check for trivial reject */
+    if ( (!(regM->numRects)) || (!(regS->numRects))  ||
+	(!EXTENTCHECK(&regM->extents, &regS->extents)) )
+    {
+	miRegionCopy(regD, regM);
+        return 1;
+    }
+ 
+    miRegionOp (regD, regM, regS, (voidProcp) miSubtractO, 
+    		(voidProcp) miSubtractNonO1, (voidProcp) NULL);
+
+    /*
+     * Can't alter newReg's extents before we call miRegionOp because
+     * it might be one of the source regions and miRegionOp depends
+     * on the extents of those regions being the unaltered. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    miSetExtents (regD);
+    return 1;
+}
+
+int
+XXorRegion( sra, srb, dr )
+    Region sra, srb, dr;
+{
+    Region tra, trb;
+
+    if ((! (tra = XCreateRegion())) || (! (trb = XCreateRegion())))
+	return 0;
+    (void) XSubtractRegion(sra,srb,tra);
+    (void) XSubtractRegion(srb,sra,trb);
+    (void) XUnionRegion(tra,trb,dr);
+    XDestroyRegion(tra);
+    XDestroyRegion(trb);
+    return 0;
+}
+
+/*
+ * Check to see if the region is empty.  Assumes a region is passed 
+ * as a parameter
+ */
+int 
+XEmptyRegion( r )
+    Region r;
+{
+    if( r->numRects == 0 ) return TRUE;
+    else  return FALSE;
+}
+
+/*
+ *	Check to see if two regions are equal	
+ */
+int 
+XEqualRegion( r1, r2 )
+    Region r1, r2;
+{
+    int i;
+
+    if( r1->numRects != r2->numRects ) return FALSE;
+    else if( r1->numRects == 0 ) return TRUE;
+    else if ( r1->extents.x1 != r2->extents.x1 ) return FALSE;
+    else if ( r1->extents.x2 != r2->extents.x2 ) return FALSE;
+    else if ( r1->extents.y1 != r2->extents.y1 ) return FALSE;
+    else if ( r1->extents.y2 != r2->extents.y2 ) return FALSE;
+    else for( i=0; i < r1->numRects; i++ ) {
+    	if ( r1->rects[i].x1 != r2->rects[i].x1 ) return FALSE;
+    	else if ( r1->rects[i].x2 != r2->rects[i].x2 ) return FALSE;
+    	else if ( r1->rects[i].y1 != r2->rects[i].y1 ) return FALSE;
+    	else if ( r1->rects[i].y2 != r2->rects[i].y2 ) return FALSE;
+    }
+    return TRUE;
+}
+
+int 
+XPointInRegion( pRegion, x, y )
+    Region pRegion;
+    int x, y;
+{
+    int i;
+
+    if (pRegion->numRects == 0)
+        return FALSE;
+    if (!INBOX(pRegion->extents, x, y))
+        return FALSE;
+    for (i=0; i<pRegion->numRects; i++)
+    {
+        if (INBOX (pRegion->rects[i], x, y))
+	    return TRUE;
+    }
+    return FALSE;
+}
+
+int 
+XRectInRegion(region, rx, ry, rwidth, rheight)
+    register Region	region;
+    int rx, ry;
+    unsigned int rwidth, rheight;
+{
+    register BoxPtr pbox;
+    register BoxPtr pboxEnd;
+    Box rect;
+    register BoxPtr prect = &rect;
+    int      partIn, partOut;
+
+    prect->x1 = rx;
+    prect->y1 = ry;
+    prect->x2 = rwidth + rx;
+    prect->y2 = rheight + ry;
+    
+    /* this is (just) a useful optimization */
+    if ((region->numRects == 0) || !EXTENTCHECK(&region->extents, prect))
+        return(RectangleOut);
+
+    partOut = FALSE;
+    partIn = FALSE;
+
+    /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
+    for (pbox = region->rects, pboxEnd = pbox + region->numRects;
+	 pbox < pboxEnd;
+	 pbox++)
+    {
+
+	if (pbox->y2 <= ry)
+	   continue;	/* getting up to speed or skipping remainder of band */
+
+	if (pbox->y1 > ry)
+	{
+	   partOut = TRUE;	/* missed part of rectangle above */
+	   if (partIn || (pbox->y1 >= prect->y2))
+	      break;
+	   ry = pbox->y1;	/* x guaranteed to be == prect->x1 */
+	}
+
+	if (pbox->x2 <= rx)
+	   continue;		/* not far enough over yet */
+
+	if (pbox->x1 > rx)
+	{
+	   partOut = TRUE;	/* missed part of rectangle to left */
+	   if (partIn)
+	      break;
+	}
+
+	if (pbox->x1 < prect->x2)
+	{
+	    partIn = TRUE;	/* definitely overlap */
+	    if (partOut)
+	       break;
+	}
+
+	if (pbox->x2 >= prect->x2)
+	{
+	   ry = pbox->y2;	/* finished with this band */
+	   if (ry >= prect->y2)
+	      break;
+	   rx = prect->x1;	/* reset x out to left again */
+	} else
+	{
+	    /*
+	     * Because boxes in a band are maximal width, if the first box
+	     * to overlap the rectangle doesn't completely cover it in that
+	     * band, the rectangle must be partially out, since some of it
+	     * will be uncovered in that band. partIn will have been set true
+	     * by now...
+	     */
+	    break;
+	}
+
+    }
+
+    return(partIn ? ((ry < prect->y2) ? RectanglePart : RectangleIn) : 
+		RectangleOut);
+}
diff --git a/Xregion/Xregion.dsp b/Xregion/Xregion.dsp
new file mode 100644
index 0000000..87d04bf
--- /dev/null
+++ b/Xregion/Xregion.dsp
@@ -0,0 +1,132 @@
+# Microsoft Developer Studio Project File - Name="Xregion" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=Xregion - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Xregion.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Xregion.mak" CFG="Xregion - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Xregion - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "Xregion - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "Xregion - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Xregion - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "Xregion - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "Xregion - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Xregion___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "Xregion___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FD /GZ /c
+# SUBTRACT BASE CPP /YX
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Xregion - Win32 Release"
+# Name "Xregion - Win32 Debug"
+# Name "Xregion - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Region.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Xregion.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Xregion/Xregion.h b/Xregion/Xregion.h
new file mode 100644
index 0000000..7fa44d9
--- /dev/null
+++ b/Xregion/Xregion.h
@@ -0,0 +1,220 @@
+/* $Xorg: Xutil.h,v 1.8 2001/02/09 02:03:39 xorgcvs Exp $ */
+
+/***********************************************************
+
+Copyright 1987, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+/* $XFree86: xc/lib/X11/Xutil.h,v 3.4 2001/12/14 19:54:10 dawes Exp $ */
+
+#ifndef _XREGION_H_
+#define _XREGION_H_
+
+// - Faked defines to fool the X11 region code
+
+#include <stdlib.h>
+#include <string.h>
+
+#define Bool int
+#define Xmalloc malloc
+#define Xfree free
+#define Xrealloc realloc
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+#define NeedFunctionPrototypes 1
+
+// - Cribbed from Xlib.h
+
+typedef struct {
+    short x, y;
+} XPoint;
+
+typedef struct {
+    short x, y;
+    unsigned short width, height;
+} XRectangle;
+
+/*
+ * opaque reference to Region data type 
+ */
+typedef struct _XRegion *Region; 
+
+/* Return values from XRectInRegion() */
+ 
+#define RectangleOut 0
+#define RectangleIn  1
+#define RectanglePart 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int XClipBox(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    XRectangle*		/* rect_return */
+#endif
+);
+
+extern Region XCreateRegion(
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern const char *XDefaultString (void);
+
+extern int XDestroyRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */
+#endif
+);
+
+extern int XEmptyRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */
+#endif
+);
+
+extern int XEqualRegion(
+#if NeedFunctionPrototypes
+    Region		/* r1 */,
+    Region		/* r2 */
+#endif
+);
+
+extern int XIntersectRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XOffsetRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* dx */,
+    int			/* dy */
+#endif
+);
+
+extern Bool XPointInRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* x */,
+    int			/* y */
+#endif
+);
+
+extern Region XPolygonRegion(
+#if NeedFunctionPrototypes
+    XPoint*		/* points */,
+    int			/* n */,
+    int			/* fill_rule */
+#endif
+);
+
+extern int XRectInRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* x */,
+    int			/* y */,
+    unsigned int	/* width */,
+    unsigned int	/* height */
+#endif
+);
+
+extern int XShrinkRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* dx */,
+    int			/* dy */
+#endif
+);
+
+extern int XSubtractRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XUnionRectWithRegion(
+#if NeedFunctionPrototypes
+    XRectangle*		/* rectangle */,
+    Region		/* src_region */,
+    Region		/* dest_region_return */
+#endif
+);
+
+extern int XUnionRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XXorRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* _XUTIL_H_ */
diff --git a/Xregion/region.h b/Xregion/region.h
new file mode 100644
index 0000000..2ddf12c
--- /dev/null
+++ b/Xregion/region.h
@@ -0,0 +1,190 @@
+/* $Xorg: region.h,v 1.4 2001/02/09 02:03:40 xorgcvs Exp $ */
+/************************************************************************
+
+Copyright 1987, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+typedef struct {
+    short x1, x2, y1, y2;
+} Box, BOX, BoxRec, *BoxPtr;
+
+typedef struct {
+    short x, y, width, height;
+}RECTANGLE, RectangleRec, *RectanglePtr;
+
+#define TRUE 1
+#define FALSE 0
+#define MAXSHORT 32767
+#define MINSHORT -MAXSHORT
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+/* 
+ *   clip region
+ */
+
+typedef struct _XRegion {
+    long size;
+    long numRects;
+    BOX *rects;
+    BOX extents;
+} REGION;
+
+/* Xutil.h contains the declaration: 
+ * typedef struct _XRegion *Region; 
+ */   
+
+/*  1 if two BOXs overlap.
+ *  0 if two BOXs do not overlap.
+ *  Remember, x2 and y2 are not in the region 
+ */
+#define EXTENTCHECK(r1, r2) \
+	((r1)->x2 > (r2)->x1 && \
+	 (r1)->x1 < (r2)->x2 && \
+	 (r1)->y2 > (r2)->y1 && \
+	 (r1)->y1 < (r2)->y2)
+
+/*
+ *  update region extents
+ */
+#define EXTENTS(r,idRect){\
+            if((r)->x1 < (idRect)->extents.x1)\
+              (idRect)->extents.x1 = (r)->x1;\
+            if((r)->y1 < (idRect)->extents.y1)\
+              (idRect)->extents.y1 = (r)->y1;\
+            if((r)->x2 > (idRect)->extents.x2)\
+              (idRect)->extents.x2 = (r)->x2;\
+            if((r)->y2 > (idRect)->extents.y2)\
+              (idRect)->extents.y2 = (r)->y2;\
+        }
+
+/*
+ *   Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(reg, rect, firstrect){\
+        if ((reg)->numRects >= ((reg)->size - 1)){\
+          (firstrect) = (BOX *) Xrealloc \
+          ((char *)(firstrect), (unsigned) (2 * (sizeof(BOX)) * ((reg)->size)));\
+          if ((firstrect) == 0)\
+            return(0);\
+          (reg)->size *= 2;\
+          (rect) = &(firstrect)[(reg)->numRects];\
+         }\
+       }
+
+/*  this routine checks to see if the previous rectangle is the same
+ *  or subsumes the new rectangle to add.
+ */
+
+#define CHECK_PREVIOUS(Reg, R, Rx1, Ry1, Rx2, Ry2)\
+               (!(((Reg)->numRects > 0)&&\
+                  ((R-1)->y1 == (Ry1)) &&\
+                  ((R-1)->y2 == (Ry2)) &&\
+                  ((R-1)->x1 <= (Rx1)) &&\
+                  ((R-1)->x2 >= (Rx2))))
+
+/*  add a rectangle to the given Region */
+#define ADDRECT(reg, r, rx1, ry1, rx2, ry2){\
+    if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&\
+        CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\
+              (r)->x1 = (rx1);\
+              (r)->y1 = (ry1);\
+              (r)->x2 = (rx2);\
+              (r)->y2 = (ry2);\
+              EXTENTS((r), (reg));\
+              (reg)->numRects++;\
+              (r)++;\
+            }\
+        }
+
+
+
+/*  add a rectangle to the given Region */
+#define ADDRECTNOX(reg, r, rx1, ry1, rx2, ry2){\
+            if ((rx1 < rx2) && (ry1 < ry2) &&\
+                CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\
+              (r)->x1 = (rx1);\
+              (r)->y1 = (ry1);\
+              (r)->x2 = (rx2);\
+              (r)->y2 = (ry2);\
+              (reg)->numRects++;\
+              (r)++;\
+            }\
+        }
+
+#define EMPTY_REGION(pReg) pReg->numRects = 0
+
+#define REGION_NOT_EMPTY(pReg) pReg->numRects
+
+#define INBOX(r, x, y) \
+      ( ( ((r).x2 >  x)) && \
+        ( ((r).x1 <= x)) && \
+        ( ((r).y2 >  y)) && \
+        ( ((r).y1 <= y)) )
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines() :  Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+    XPoint pts[NUMPTSTOBUFFER];
+    struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
diff --git a/boilerplate.mk b/boilerplate.mk
new file mode 100644
index 0000000..979731c
--- /dev/null
+++ b/boilerplate.mk
@@ -0,0 +1,35 @@
+
+all::
+	@subdirs="$(SUBDIRS)"; for d in $$subdirs; do (cd $$d; $(MAKE) $@) || exit 1; done
+
+clean::
+	@subdirs="$(SUBDIRS)"; for d in $$subdirs; do (cd $$d; $(MAKE) $@) || exit 1; done
+
+clean::
+	rm -f $(program) $(library) *.o
+
+SHELL = @SHELL@
+top_srcdir = @top_srcdir@
+@SET_MAKE@
+CC = @CC@
+CFLAGS = @CFLAGS@ $(DIR_CFLAGS)
+CCLD = $(CC)
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@
+CXXLD = $(CXX)
+CPPFLAGS = @CPPFLAGS@
+DEFS = @DEFS@
+ALL_CPPFLAGS = $(CPPFLAGS) $(DEFS) $(DIR_CPPFLAGS)
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+RANLIB = @RANLIB@
+AR = ar cq
+
+.SUFFIXES:
+.SUFFIXES: .cxx .c .o
+
+.c.o:
+	$(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c $<
+
+.cxx.o:
+	$(CXX) $(ALL_CPPFLAGS) $(CXXFLAGS) -c $<
diff --git a/configure b/configure
new file mode 100755
index 0000000..40951d2
--- /dev/null
+++ b/configure
@@ -0,0 +1,2299 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --with-x                use the X Window System"
+ac_help="$ac_help
+  --with-installed-zlib   use the version of zlib which is installed on the
+                          system instead of the one distributed with VNC"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=rdr/InStream.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='	'
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_cv_prog_cc_g=no
+ac_cv_prog_cxx_g=no
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:537: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:567: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+	continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:618: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:650: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 661 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:666: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:692: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:697: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:706: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:725: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:761: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CXX="$ac_prog"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+  echo "$ac_t""$CXX" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$CXX" && break
+done
+test -n "$CXX" || CXX="gcc"
+
+
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:793: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 804 "configure"
+#include "confdefs.h"
+
+int main(){return(0);}
+EOF
+if { (eval echo configure:809: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cxx_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cxx_cross=no
+  else
+    ac_cv_prog_cxx_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cxx_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6
+if test $ac_cv_prog_cxx_works = no; then
+  { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:835: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6
+cross_compiling=$ac_cv_prog_cxx_cross
+
+echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6
+echo "configure:840: checking whether we are using GNU C++" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.C <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:849: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gxx=yes
+else
+  ac_cv_prog_gxx=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gxx" 1>&6
+
+if test $ac_cv_prog_gxx = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+
+ac_test_CXXFLAGS="${CXXFLAGS+set}"
+ac_save_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS=
+echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6
+echo "configure:868: checking whether ${CXX-g++} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.cc
+if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then
+  ac_cv_prog_cxx_g=yes
+else
+  ac_cv_prog_cxx_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS="$ac_save_CXXFLAGS"
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:902: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:930: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+	@echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+
+case "`(uname -sr) 2>/dev/null`" in
+"SunOS 5"*)
+  SOLARIS=yes
+  USE_MITSHM=yes
+  ;;
+"Linux"*)
+  LINUX=yes
+  USE_MITSHM=yes
+  ;;
+esac
+
+if test "$USE_MITSHM" = yes; then
+  MITSHM_CPPFLAGS="-DMITSHM"
+fi
+
+
+if test "$GCC" = yes; then
+  CFLAGS="$CFLAGS -Wall"
+  if test "$SOLARIS" = yes; then
+    CFLAGS="$CFLAGS -Wno-unknown-pragmas -Wno-implicit-int"
+  fi
+fi
+if test "$GXX" = yes; then
+  CXXFLAGS="$CXXFLAGS -Wall"
+  if test "$SOLARIS" = yes; then
+    CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas -Wno-implicit-int -fpermissive"
+  fi
+fi
+
+echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6
+echo "configure:994: checking how to run the C++ preprocessor" >&5
+if test -z "$CXXCPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+  CXXCPP="${CXX-g++} -E"
+  cat > conftest.$ac_ext <<EOF
+#line 1007 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1012: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CXXCPP=/lib/cpp
+fi
+rm -f conftest*
+  ac_cv_prog_CXXCPP="$CXXCPP"
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+fi
+fi
+CXXCPP="$ac_cv_prog_CXXCPP"
+echo "$ac_t""$CXXCPP" 1>&6
+
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+echo "configure:1041: checking for X" >&5
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+  withval="$with_x"
+  :
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+  # The user explicitly disabled X.
+  have_x=disabled
+else
+  if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+    # Both variables are already set.
+    have_x=yes
+  else
+if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=NO ac_x_libraries=NO
+rm -fr conftestdir
+if mkdir conftestdir; then
+  cd conftestdir
+  # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+  cat > Imakefile <<'EOF'
+acfindx:
+	@echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+  if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+    # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+    eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+    # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+    for ac_extension in a so sl; do
+      if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+        test -f $ac_im_libdir/libX11.$ac_extension; then
+        ac_im_usrlibdir=$ac_im_libdir; break
+      fi
+    done
+    # Screen out bogus values from the imake configuration.  They are
+    # bogus both because they are the default anyway, and because
+    # using them would break gcc on systems where it needs fixed includes.
+    case "$ac_im_incroot" in
+	/usr/include) ;;
+	*) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+    esac
+    case "$ac_im_usrlibdir" in
+	/usr/lib | /lib) ;;
+	*) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+    esac
+  fi
+  cd ..
+  rm -fr conftestdir
+fi
+
+if test "$ac_x_includes" = NO; then
+  # Guess where to find include files, by looking for this one X11 .h file.
+  test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+
+  # First, try using that file with no special directory specified.
+cat > conftest.$ac_ext <<EOF
+#line 1103 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1108: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  # Look for the header file in a standard set of common directories.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+  for ac_dir in               \
+    /usr/X11/include          \
+    /usr/X11R6/include        \
+    /usr/X11R5/include        \
+    /usr/X11R4/include        \
+                              \
+    /usr/include/X11          \
+    /usr/include/X11R6        \
+    /usr/include/X11R5        \
+    /usr/include/X11R4        \
+                              \
+    /usr/local/X11/include    \
+    /usr/local/X11R6/include  \
+    /usr/local/X11R5/include  \
+    /usr/local/X11R4/include  \
+                              \
+    /usr/local/include/X11    \
+    /usr/local/include/X11R6  \
+    /usr/local/include/X11R5  \
+    /usr/local/include/X11R4  \
+                              \
+    /usr/X386/include         \
+    /usr/x386/include         \
+    /usr/XFree86/include/X11  \
+                              \
+    /usr/include              \
+    /usr/local/include        \
+    /usr/unsupported/include  \
+    /usr/athena/include       \
+    /usr/local/x11r5/include  \
+    /usr/lpp/Xamples/include  \
+                              \
+    /usr/openwin/include      \
+    /usr/openwin/share/include \
+    ; \
+  do
+    if test -r "$ac_dir/$x_direct_test_include"; then
+      ac_x_includes=$ac_dir
+      break
+    fi
+  done
+fi
+rm -f conftest*
+fi # $ac_x_includes = NO
+
+if test "$ac_x_libraries" = NO; then
+  # Check for the libraries.
+
+  test -z "$x_direct_test_library" && x_direct_test_library=Xt
+  test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+
+  # See if we find them without any special options.
+  # Don't add to $LIBS permanently.
+  ac_save_LIBS="$LIBS"
+  LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1177 "configure"
+#include "confdefs.h"
+
+int main() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if { (eval echo configure:1184: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  LIBS="$ac_save_LIBS"
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+    /usr/X11/lib          \
+    /usr/X11R6/lib        \
+    /usr/X11R5/lib        \
+    /usr/X11R4/lib        \
+                          \
+    /usr/lib/X11          \
+    /usr/lib/X11R6        \
+    /usr/lib/X11R5        \
+    /usr/lib/X11R4        \
+                          \
+    /usr/local/X11/lib    \
+    /usr/local/X11R6/lib  \
+    /usr/local/X11R5/lib  \
+    /usr/local/X11R4/lib  \
+                          \
+    /usr/local/lib/X11    \
+    /usr/local/lib/X11R6  \
+    /usr/local/lib/X11R5  \
+    /usr/local/lib/X11R4  \
+                          \
+    /usr/X386/lib         \
+    /usr/x386/lib         \
+    /usr/XFree86/lib/X11  \
+                          \
+    /usr/lib              \
+    /usr/local/lib        \
+    /usr/unsupported/lib  \
+    /usr/athena/lib       \
+    /usr/local/x11r5/lib  \
+    /usr/lpp/Xamples/lib  \
+    /lib/usr/lib/X11	  \
+                          \
+    /usr/openwin/lib      \
+    /usr/openwin/share/lib \
+    ; \
+do
+  for ac_extension in a so sl; do
+    if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+      ac_x_libraries=$ac_dir
+      break 2
+    fi
+  done
+done
+fi
+rm -f conftest*
+fi # $ac_x_libraries = NO
+
+if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then
+  # Didn't find X anywhere.  Cache the known absence of X.
+  ac_cv_have_x="have_x=no"
+else
+  # Record where we found X for the cache.
+  ac_cv_have_x="have_x=yes \
+	        ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+  fi
+  eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+  echo "$ac_t""$have_x" 1>&6
+  no_x=yes
+else
+  # If each of the values was on the command line, it overrides each guess.
+  test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+  test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+  # Update the cache value to reflect the command line values.
+  ac_cv_have_x="have_x=yes \
+		ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+  echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+if test "$no_x" = yes; then
+  # Not all programs may use this symbol, but it does not hurt to define it.
+  cat >> confdefs.h <<\EOF
+#define X_DISPLAY_MISSING 1
+EOF
+
+  X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+  if test -n "$x_includes"; then
+    X_CFLAGS="$X_CFLAGS -I$x_includes"
+  fi
+
+  # It would also be nice to do this for all -L options, not just this one.
+  if test -n "$x_libraries"; then
+    X_LIBS="$X_LIBS -L$x_libraries"
+    # For Solaris; some versions of Sun CC require a space after -R and
+    # others require no space.  Words are not sufficient . . . .
+    case "`(uname -sr) 2>/dev/null`" in
+    "SunOS 5"*)
+      echo $ac_n "checking whether -R must be followed by a space""... $ac_c" 1>&6
+echo "configure:1290: checking whether -R must be followed by a space" >&5
+      ac_xsave_LIBS="$LIBS"; LIBS="$LIBS -R$x_libraries"
+      cat > conftest.$ac_ext <<EOF
+#line 1293 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1300: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_R_nospace=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_R_nospace=no
+fi
+rm -f conftest*
+      if test $ac_R_nospace = yes; then
+	echo "$ac_t""no" 1>&6
+	X_LIBS="$X_LIBS -R$x_libraries"
+      else
+	LIBS="$ac_xsave_LIBS -R $x_libraries"
+	cat > conftest.$ac_ext <<EOF
+#line 1316 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1323: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_R_space=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_R_space=no
+fi
+rm -f conftest*
+	if test $ac_R_space = yes; then
+	  echo "$ac_t""yes" 1>&6
+	  X_LIBS="$X_LIBS -R $x_libraries"
+	else
+	  echo "$ac_t""neither works" 1>&6
+	fi
+      fi
+      LIBS="$ac_xsave_LIBS"
+    esac
+  fi
+
+  # Check for system-dependent libraries X programs must link with.
+  # Do this before checking for the system-independent R6 libraries
+  # (-lICE), since we may need -lsocket or whatever for X linking.
+
+  if test "$ISC" = yes; then
+    X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+  else
+    # Martyn.Johnson@cl.cam.ac.uk says this is needed for Ultrix, if the X
+    # libraries were built with DECnet support.  And karl@cs.umb.edu says
+    # the Alpha needs dnet_stub (dnet does not exist).
+    echo $ac_n "checking for dnet_ntoa in -ldnet""... $ac_c" 1>&6
+echo "configure:1355: checking for dnet_ntoa in -ldnet" >&5
+ac_lib_var=`echo dnet'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldnet  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1363 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1377: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+      echo $ac_n "checking for dnet_ntoa in -ldnet_stub""... $ac_c" 1>&6
+echo "configure:1399: checking for dnet_ntoa in -ldnet_stub" >&5
+ac_lib_var=`echo dnet_stub'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldnet_stub  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1407 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    fi
+
+    # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
+    # to get the SysV transport functions.
+    # chad@anasazi.com says the Pyramis MIS-ES running DC/OSx (SVR4)
+    # needs -lnsl.
+    # The nsl library prevents programs from opening the X display
+    # on Irix 5.2, according to dickey@clark.net.
+    echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:1450: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1455 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char gethostbyname(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1481: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_gethostbyname=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    if test $ac_cv_func_gethostbyname = no; then
+      echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:1502: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1510 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:1524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    fi
+
+    # lieder@skyler.mavd.honeywell.com says without -lsocket,
+    # socket/setsockopt and other routines are undefined under SCO ODT
+    # 2.0.  But -lsocket is broken on IRIX 5.2 (and is not necessary
+    # on later versions), says simon@lia.di.epfl.ch: it contains
+    # gethostby* variants that don't use the nameserver (or something).
+    # -lsocket must be given before -lnsl if both are needed.
+    # We assume that if connect needs -lnsl, so does gethostbyname.
+    echo $ac_n "checking for connect""... $ac_c" 1>&6
+echo "configure:1554: checking for connect" >&5
+if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1559 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char connect(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char connect();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_connect) || defined (__stub___connect)
+choke me
+#else
+connect();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1585: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_connect=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_connect=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    if test $ac_cv_func_connect = no; then
+      echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6
+echo "configure:1606: checking for connect in -lsocket" >&5
+ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1614 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char connect();
+
+int main() {
+connect()
+; return 0; }
+EOF
+if { (eval echo configure:1628: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    fi
+
+    # gomez@mi.uni-erlangen.de says -lposix is necessary on A/UX.
+    echo $ac_n "checking for remove""... $ac_c" 1>&6
+echo "configure:1652: checking for remove" >&5
+if eval "test \"`echo '$''{'ac_cv_func_remove'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1657 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char remove(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char remove();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_remove) || defined (__stub___remove)
+choke me
+#else
+remove();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1683: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_remove=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_remove=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'remove`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    if test $ac_cv_func_remove = no; then
+      echo $ac_n "checking for remove in -lposix""... $ac_c" 1>&6
+echo "configure:1704: checking for remove in -lposix" >&5
+ac_lib_var=`echo posix'_'remove | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lposix  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1712 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char remove();
+
+int main() {
+remove()
+; return 0; }
+EOF
+if { (eval echo configure:1726: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    fi
+
+    # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+    echo $ac_n "checking for shmat""... $ac_c" 1>&6
+echo "configure:1750: checking for shmat" >&5
+if eval "test \"`echo '$''{'ac_cv_func_shmat'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1755 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shmat(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char shmat();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shmat) || defined (__stub___shmat)
+choke me
+#else
+shmat();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1781: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_shmat=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_shmat=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'shmat`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    if test $ac_cv_func_shmat = no; then
+      echo $ac_n "checking for shmat in -lipc""... $ac_c" 1>&6
+echo "configure:1802: checking for shmat in -lipc" >&5
+ac_lib_var=`echo ipc'_'shmat | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lipc  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1810 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char shmat();
+
+int main() {
+shmat()
+; return 0; }
+EOF
+if { (eval echo configure:1824: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    fi
+  fi
+
+  # Check for libraries that X11R6 Xt/Xaw programs need.
+  ac_save_LDFLAGS="$LDFLAGS"
+  test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+  # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+  # check for ICE first), but we must link in the order -lSM -lICE or
+  # we get undefined symbols.  So assume we have SM if we have ICE.
+  # These have to be linked with before -lX11, unlike the other
+  # libraries we check for below, so use a different variable.
+  #  --interran@uluru.Stanford.EDU, kb@cs.umb.edu.
+  echo $ac_n "checking for IceConnectionNumber in -lICE""... $ac_c" 1>&6
+echo "configure:1857: checking for IceConnectionNumber in -lICE" >&5
+ac_lib_var=`echo ICE'_'IceConnectionNumber | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1865 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char IceConnectionNumber();
+
+int main() {
+IceConnectionNumber()
+; return 0; }
+EOF
+if { (eval echo configure:1879: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  LDFLAGS="$ac_save_LDFLAGS"
+
+fi
+
+
+# Check whether --with-installed-zlib or --without-installed-zlib was given.
+if test "${with_installed_zlib+set}" = set; then
+  withval="$with_installed_zlib"
+  :
+fi
+
+
+if test "$with_installed_zlib" = yes; then
+  echo "using installed zlib"
+  ZLIB_LIB=-lz
+else
+  ZLIB_DIR=zlib
+  ZLIB_INCLUDE='-I$(top_srcdir)/zlib'
+  ZLIB_LIB='$(top_srcdir)/zlib/libz.a'
+  echo "configuring zlib..."
+  (cd zlib; ./configure)
+  echo "...done configuring zlib"
+fi
+
+
+
+
+
+echo $ac_n "checking for vsnprintf""... $ac_c" 1>&6
+echo "configure:1928: checking for vsnprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vsnprintf'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1933 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vsnprintf(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vsnprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vsnprintf) || defined (__stub___vsnprintf)
+choke me
+#else
+vsnprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1959: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_vsnprintf=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vsnprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vsnprintf`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  VSNPRINTF_DEFINE='-DHAVE_VSNPRINTF'
+else
+  echo "$ac_t""no" 1>&6
+VSNPRINTF_DEFINE=
+fi
+
+
+
+echo $ac_n "checking for socklen_t""... $ac_c" 1>&6
+echo "configure:1982: checking for socklen_t" >&5
+cat > conftest.$ac_ext <<EOF
+#line 1984 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+ #include <sys/socket.h>
+int main() {
+socklen_t x;
+accept(0, 0, &x);
+; return 0; }
+EOF
+if { (eval echo configure:1993: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=socklen_t'
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  echo "$ac_t""using int" 1>&6
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=int'
+fi
+rm -f conftest*
+
+
+BOILERPLATE=boilerplate.mk
+
+if (sh -c "make --version" 2>/dev/null | grep GNU 2>&1 >/dev/null); then
+  if sh -c "vncmkdepend" >/dev/null 2>&1; then
+    BOILERPLATE="$BOILERPLATE:depend.mk"
+  fi
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[ 	]*VPATH[ 	]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ 	`~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile:Makefile.in:$BOILERPLATE \
+         rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+         network/Makefile:network/Makefile.in:$BOILERPLATE \
+         Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+         rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+         tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+         x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+         vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+         vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+         vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@RANLIB@%$RANLIB%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@MITSHM_CPPFLAGS@%$MITSHM_CPPFLAGS%g
+s%@CXXCPP@%$CXXCPP%g
+s%@X_CFLAGS@%$X_CFLAGS%g
+s%@X_PRE_LIBS@%$X_PRE_LIBS%g
+s%@X_LIBS@%$X_LIBS%g
+s%@X_EXTRA_LIBS@%$X_EXTRA_LIBS%g
+s%@ZLIB_DIR@%$ZLIB_DIR%g
+s%@ZLIB_INCLUDE@%$ZLIB_INCLUDE%g
+s%@ZLIB_LIB@%$ZLIB_LIB%g
+s%@VSNPRINTF_DEFINE@%$VSNPRINTF_DEFINE%g
+s%@SOCKLEN_T_DEFINE@%$SOCKLEN_T_DEFINE%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile:Makefile.in:$BOILERPLATE \
+         rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+         network/Makefile:network/Makefile.in:$BOILERPLATE \
+         Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+         rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+         tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+         x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+         vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+         vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+         vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..bb49f83
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,99 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(rdr/InStream.h)
+
+dnl dirty hack to prevent use of -g in CFLAGS and CXXFLAGS
+ac_cv_prog_cc_g=no
+ac_cv_prog_cxx_g=no
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_RANLIB
+AC_PROG_MAKE_SET
+AC_LANG_CPLUSPLUS
+
+case "`(uname -sr) 2>/dev/null`" in
+"SunOS 5"*)
+  SOLARIS=yes
+  USE_MITSHM=yes
+  ;;
+"Linux"*)
+  LINUX=yes
+  USE_MITSHM=yes
+  ;;
+esac
+
+if test "$USE_MITSHM" = yes; then
+  MITSHM_CPPFLAGS="-DMITSHM"
+fi
+AC_SUBST(MITSHM_CPPFLAGS)
+
+if test "$GCC" = yes; then
+  CFLAGS="$CFLAGS -Wall"
+  if test "$SOLARIS" = yes; then
+    CFLAGS="$CFLAGS -Wno-unknown-pragmas -Wno-implicit-int"
+  fi
+fi
+if test "$GXX" = yes; then
+  CXXFLAGS="$CXXFLAGS -Wall"
+  if test "$SOLARIS" = yes; then
+    CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas -Wno-implicit-int -fpermissive"
+  fi
+fi
+
+AC_PATH_XTRA
+
+AC_ARG_WITH(installed-zlib,
+[  --with-installed-zlib   use the version of zlib which is installed on the
+                          system instead of the one distributed with VNC])
+
+if test "$with_installed_zlib" = yes; then
+  echo "using installed zlib"
+  ZLIB_LIB=-lz
+else
+  ZLIB_DIR=zlib
+  ZLIB_INCLUDE='-I$(top_srcdir)/zlib'
+  ZLIB_LIB='$(top_srcdir)/zlib/libz.a'
+  echo "configuring zlib..."
+  (cd zlib; ./configure)
+  echo "...done configuring zlib"
+fi
+
+AC_SUBST(ZLIB_DIR)
+AC_SUBST(ZLIB_INCLUDE)
+AC_SUBST(ZLIB_LIB)
+
+AC_CHECK_FUNC(vsnprintf,VSNPRINTF_DEFINE='-DHAVE_VSNPRINTF',VSNPRINTF_DEFINE=)
+AC_SUBST(VSNPRINTF_DEFINE)
+
+AC_MSG_CHECKING(for socklen_t)
+AC_TRY_COMPILE(
+[#include <sys/types.h>
+ #include <sys/socket.h>],
+[socklen_t x;
+accept(0, 0, &x);],
+AC_MSG_RESULT(yes)
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=socklen_t',
+AC_MSG_RESULT(using int)
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=int')
+AC_SUBST(SOCKLEN_T_DEFINE)
+
+BOILERPLATE=boilerplate.mk
+
+if (sh -c "make --version" 2>/dev/null | grep GNU 2>&1 >/dev/null); then
+  if sh -c "vncmkdepend" >/dev/null 2>&1; then
+    BOILERPLATE="$BOILERPLATE:depend.mk"
+  fi
+fi
+
+AC_OUTPUT(Makefile:Makefile.in:$BOILERPLATE \
+         rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+         network/Makefile:network/Makefile.in:$BOILERPLATE \
+         Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+         rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+         tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+         x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+         vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+         vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+         vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+)
diff --git a/depend.mk b/depend.mk
new file mode 100644
index 0000000..51d4cd6
--- /dev/null
+++ b/depend.mk
@@ -0,0 +1,76 @@
+#
+# C / C++ header dependency stuff
+#
+# Needs GNU make and vncmkdepend, a hacked version of makedepend
+
+.SUFFIXES: .d
+
+CMAKEDEPEND = vncmkdepend
+CXXMAKEDEPEND = vncmkdepend
+
+#
+# The recommended method of doing dependency analysis in the GNU make manual
+# turns out to be painfully slow.  This method is similar but it's
+# substantially faster and retains the desirable property that the user doesn't
+# need to manually invoke a "make depend" step.
+#
+# As with the method described in the manual, we generate a separate dependency
+# (.d) file for each source file.  The .d file records the header files that
+# each C or C++ source file includes.  Any source file recorded in SRCS or
+# CXXSRCS will cause us to try and include the corresponding .d file and GNU
+# make then treats each .d file as a target to be remade.
+#
+# Unlike the manual's method, the rule we provide for making the .d file is
+# actually a fake.  All it does is record in a temporary file that the .d file
+# needs to be remade.  But as well as all the .d files, we also try to include
+# a file called "depend.phony".  This file never exists, but it causes GNU make
+# to try and make the target "depend.phony".  The rule for depend.phony then
+# looks at the temporary files generated by the .d rules and then invokes the
+# "omkdepend" program on all of the source files in one go.
+#
+
+#
+# We use simple assignment here to remove any of the depend.tmp files
+# at the time make parses this bit.
+#
+
+dummyvariable := $(shell $(RM) cdepend.tmp cxxdepend.tmp)
+
+#
+# Now the "fake" rules for generating .d files.
+#
+
+%.d: %.c
+	@echo "$<" >> cdepend.tmp
+
+%.d: %.cxx
+	@echo "$<" >> cxxdepend.tmp
+
+#
+# The depend.phony rule which actually runs omkdepend.
+#
+
+depend.phony:
+	@if [ -f cdepend.tmp ]; then \
+	   echo $(CMAKEDEPEND) $(ALL_CPPFLAGS) `cat cdepend.tmp`; \
+	   $(CMAKEDEPEND) $(ALL_CPPFLAGS) `cat cdepend.tmp`; \
+	   rm -f cdepend.tmp; \
+	 fi; \
+	 if [ -f cxxdepend.tmp ]; then \
+	   echo $(CXXMAKEDEPEND) $(ALL_CPPFLAGS) `cat cxxdepend.tmp`; \
+	   $(CXXMAKEDEPEND) $(ALL_CPPFLAGS) `cat cxxdepend.tmp`; \
+	   rm -f cxxdepend.tmp; \
+	 fi
+
+#
+# Now include the .d files and the "depend.phony" file which never exists.
+# For some reason GNU make evaluates the targets in reverse order, so we need
+# to include depend.phony first.  The "-" tells make not to complain that it
+# can't find the file.
+#
+
+-include depend.phony
+
+ifdef SRCS
+-include $(patsubst %.c,%.d,$(patsubst %.cxx,%.d,$(SRCS)))
+endif
diff --git a/hpux.patch b/hpux.patch
new file mode 100644
index 0000000..389c675
--- /dev/null
+++ b/hpux.patch
@@ -0,0 +1,255 @@
+*** xc.orig/config/cf/X11.tmpl	Tue Jan 15 22:55:26 2002
+--- xc/config/cf/X11.tmpl	Sun Sep  7 19:52:01 2003
+***************
+*** 3120,3126 ****
+  	$(RM) index.raw file.nPS file.PS file.txt
+  #endif
+  
+! #ifndef MakeSimpleDoc(file,srcs)
+  #define MakeSimpleDoc(file,srcs) MakeDepSimpleDoc(file,srcs,srcs)
+  #endif
+  
+--- 3120,3126 ----
+  	$(RM) index.raw file.nPS file.PS file.txt
+  #endif
+  
+! #ifndef MakeSimpleDoc
+  #define MakeSimpleDoc(file,srcs) MakeDepSimpleDoc(file,srcs,srcs)
+  #endif
+  
+*** xc.orig/config/cf/hp.cf	Wed Jan 17 16:22:31 2001
+--- xc/config/cf/hp.cf	Mon Sep  8 19:54:52 2003
+***************
+*** 131,137 ****
+  #endif
+  #define HPFastScrolling		YES
+  #ifndef BuildServer
+! # define BuildServer		__hp9000s700
+  #endif
+  #if OSMajorVersion < 10
+  #define NeedBerklib		(BuildServer|BuildFontServer)
+--- 131,137 ----
+  #endif
+  #define HPFastScrolling		YES
+  #ifndef BuildServer
+! # define BuildServer		YES
+  #endif
+  #if OSMajorVersion < 10
+  #define NeedBerklib		(BuildServer|BuildFontServer)
+***************
+*** 139,145 ****
+  #define XawI18nDefines		-DHAS_WCHAR_H -DHAS_ISW_FUNCS
+  
+  #if OSMajorVersion < 6 || (OSMajorVersion == 6 && OSMinorVersion < 2)
+! # define ConnectionFlags	-DTCPCONN	/* no unix sockets */
+  #endif
+  
+  #if OSMajorVersion > 8
+--- 139,145 ----
+  #define XawI18nDefines		-DHAS_WCHAR_H -DHAS_ISW_FUNCS
+  
+  #if OSMajorVersion < 6 || (OSMajorVersion == 6 && OSMinorVersion < 2)
+! /*# define ConnectionFlags	-DTCPCONN*/	/* no unix sockets */
+  #endif
+  
+  #if OSMajorVersion > 8
+*** xc/config/cf/site.def.orig  Tue Sep  9 17:42:56 2003
+--- xc/config/cf/site.def       Tue Sep  9 17:43:07 2003
+***************
+*** 84,90 ****
+  #ifdef AfterVendorCF
+  
+  #ifndef ProjectRoot
+! #define ProjectRoot /usr/X11R6
+  #endif
+  
+  /*
+--- 84,90 ----
+  #ifdef AfterVendorCF
+  
+  #ifndef ProjectRoot
+! /*#define ProjectRoot /usr/X11R6*/
+  #endif
+  
+  /*
+*** xc.orig/config/imake/imake.c	Fri Dec 14 19:53:18 2001
+--- xc/config/imake/imake.c	Mon Sep  8 19:35:33 2003
+***************
+*** 288,293 ****
+--- 288,294 ----
+  #define	TRUE		1
+  #define	FALSE		0
+  
++ #define FIXUP_CPP_WHITESPACE
+  #ifdef FIXUP_CPP_WHITESPACE
+  int	InRule = FALSE;
+  # ifdef INLINE_SYNTAX
+***************
+*** 389,394 ****
+--- 390,401 ----
+  	FILE	*tmpfd = NULL;
+  	char	makeMacro[ BUFSIZ ];
+  	char	makefileMacro[ BUFSIZ ];
++ 
++ #ifdef FIXUP_CPP_WHITESPACE
++ 	fprintf(stderr,"\nFIXUP_CPP_WHITESPACE is defined!!\n\n");
++ #else
++ #error "FIXUP_CPP_WHITESPACE is not defined"
++ #endif
+  
+  	program = argv[0];
+  	init();
+*** xc.orig/config/imake/imakemdep.h	Fri Dec 14 19:53:19 2001
+--- xc/config/imake/imakemdep.h	Tue Sep  9 16:38:18 2003
+***************
+*** 48,54 ****
+  #ifdef hp9000s800
+  #define imake_ccflags "-DSYSV"
+  #else
+! #define imake_ccflags "-Wc,-Nd4000,-Ns3000 -DSYSV"
+  #endif
+  #endif
+  
+--- 48,54 ----
+  #ifdef hp9000s800
+  #define imake_ccflags "-DSYSV"
+  #else
+! #define imake_ccflags "-DSYSV"
+  #endif
+  #endif
+  
+***************
+*** 211,217 ****
+   *     all colons).  One way to tell if you need this is to see whether or not
+   *     your Makefiles have no tabs in them and lots of @@ strings.
+   */
+! #if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || defined(sco) || (defined(AMOEBA) && defined(CROSS_COMPILE)) || defined(__QNX__) || defined(__sgi)
+  #define FIXUP_CPP_WHITESPACE
+  #endif
+  #ifdef WIN32
+--- 211,217 ----
+   *     all colons).  One way to tell if you need this is to see whether or not
+   *     your Makefiles have no tabs in them and lots of @@ strings.
+   */
+! #if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || defined(sco) || (defined(AMOEBA) && defined(CROSS_COMPILE)) || defined(__QNX__) || defined(__sgi) || defined(hpux)
+  #define FIXUP_CPP_WHITESPACE
+  #endif
+  #ifdef WIN32
+*** xc.orig/include/Xfuncs.h	Fri Dec 14 19:53:25 2001
+--- xc/include/Xfuncs.h	Sun Sep  7 20:10:35 2003
+***************
+*** 42,48 ****
+  #else
+  #if defined(SYSV)
+  #include <memory.h>
+! void bcopy();
+  #define bzero(b,len) memset(b, 0, len)
+  #define bcmp(b1,b2,len) memcmp(b1, b2, len)
+  #elif defined(__EMX__)
+--- 42,48 ----
+  #else
+  #if defined(SYSV)
+  #include <memory.h>
+! /*void bcopy();*/
+  #define bzero(b,len) memset(b, 0, len)
+  #define bcmp(b1,b2,len) memcmp(b1, b2, len)
+  #elif defined(__EMX__)
+*** xc.orig/include/extensions/security.h	Fri Dec 14 19:53:29 2001
+--- xc/include/extensions/security.h	Fri Aug  1 17:43:44 2003
+***************
+*** 110,115 ****
+--- 110,116 ----
+  
+  #include "input.h"    /* for DeviceIntPtr */
+  #include "property.h" /* for PropertyPtr */
++ #include "resource.h"
+  
+  /* resource type to pass in LookupIDByType for authorizations */
+  extern RESTYPE SecurityAuthorizationResType;
+*** xc.orig/lib/font/Type1/fontfcn.c	Fri Nov 23 19:21:31 2001
+--- xc/lib/font/Type1/fontfcn.c	Sun Sep  7 19:29:27 2003
+***************
+*** 47,52 ****
+--- 47,53 ----
+   */
+  /* $XFree86: xc/lib/font/Type1/fontfcn.c,v 1.11 2001/11/23 19:21:31 dawes Exp $ */
+   
++ #include <stdlib.h>
+  #ifndef FONTMODULE
+  #include <stdio.h>
+  #include <string.h>
+*** xc.orig/lib/font/Type1/objects.h	Mon Aug 27 20:49:52 2001
+--- xc/lib/font/Type1/objects.h	Sun Sep  7 19:29:37 2003
+***************
+*** 50,56 ****
+  #include <Xdefs.h>
+  #include <Xfuncproto.h>
+  #ifndef FONTMODULE
+! #include <stdlib.h>
+  #endif
+  /*SHARED*/
+  
+--- 50,56 ----
+  #include <Xdefs.h>
+  #include <Xfuncproto.h>
+  #ifndef FONTMODULE
+! /*#include <stdlib.h>*/
+  #endif
+  /*SHARED*/
+  
+*** xc.orig/lib/xtrans/Xtransutil.c     Tue Sep  9 17:40:14 2003
+--- xc/lib/xtrans/Xtransutil.c  Tue Sep  9 17:40:20 2003
+***************
+*** 503,514 ****
+  	    if (updateOwner && !updatedOwner) {
+  	  	PRMSG(1, "mkdir: Owner of %s should be set to root\n",
+  		      path, 0, 0);
+- 		sleep(5);
+  	    }
+  	    if (updateMode && !updatedMode) {
+  	  	PRMSG(1, "mkdir: Mode of %s should be set to %04o\n",
+  		      path, mode, 0);
+- 		sleep(5);
+  	    }
+  	    return 0;
+  	}
+--- 503,512 ----
+*** xc.orig/programs/Xserver/vnc/Xvnc/xvnc.cc     12 Aug 2003 11:00:14 -0000
+--- xc/programs/Xserver/vnc/Xvnc/xvnc.cc     9 Sep 2003 16:15:53 -0000
+***************
+*** 1221,1223 ****
+--- 1221,1229 ----
+    miRegisterPointerDevice(screenInfo.screens[0], p);
+    (void)mieqInit ((DevicePtr)k, (DevicePtr)p);
+  }
++ 
++ extern "C" {
++   void XTestGenerateEvent() {}
++   void XTestGetPointerPos() {}
++   void XTestJumpPointer() {}
++ }
+*** xc.orig/config/cf/vnc.def        7 Jul 2003 09:51:22
+--- xc/config/cf/vnc.def        9 Sep 2003 15:54:23
+***************
+*** 9,14 ****
+--- 9,20 ----
+  #define XnestServer NO
+  #define XprtServer NO
+  
++ #define BuildXKB NO
++ #define HasCplusplus YES
++ #define CplusplusCmd /opt/aCC/bin/aCC
++ #define CplusplusOptions -AA +W749 +W740
++ #define ProjectRoot /usr
++ 
+  #ifdef SunArchitecture
+  #define ProjectRoot /usr/openwin
+  #define HasGcc2 YES
+***************
+*** 29,32 ****
+--- 34,38 ----
+  
+  #define	ServerTarget(server,subdirs,objects,libs,syslibs)		@@\
+  CCLINK = $(CXXENVSETUP) $(CXX) @@\
++ CCOPTIONS = -AA @@\
+  ServerTargetWithFlags(server,subdirs,objects,libs,syslibs,$(_NOOP_))
diff --git a/java/index.vnc b/java/index.vnc
new file mode 100644
index 0000000..aecb613
--- /dev/null
+++ b/java/index.vnc
@@ -0,0 +1,13 @@
+<HTML>
+<HEAD>
+<TITLE>
+VNC viewer for Java
+</TITLE>
+</HEAD>
+<BODY>
+<APPLET CODE=vncviewer/VNCViewer.class ARCHIVE=vncviewer.jar
+        WIDTH=400 HEIGHT=250>
+<PARAM name="port" value="$PORT">
+</APPLET>
+</BODY>
+</HTML>
diff --git a/java/logo150x150.gif b/java/logo150x150.gif
new file mode 100644
index 0000000..f1699ba
--- /dev/null
+++ b/java/logo150x150.gif
Binary files differ
diff --git a/java/vncviewer.jar b/java/vncviewer.jar
new file mode 100644
index 0000000..3c92b5b
--- /dev/null
+++ b/java/vncviewer.jar
Binary files differ
diff --git a/logmessages/logmessages.dsp b/logmessages/logmessages.dsp
new file mode 100644
index 0000000..b2c8615
--- /dev/null
+++ b/logmessages/logmessages.dsp
@@ -0,0 +1,202 @@
+# Microsoft Developer Studio Project File - Name="logmessages" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=logmessages - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "logmessages.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "logmessages.mak" CFG="logmessages - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "logmessages - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "logmessages - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "logmessages - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "logmessages - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 /nologo /dll /machine:I386 /out:"messages.mc"
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+	mc messages.mc \
+	rc -r -fo messages.res messages.rc \
+	link -dll -noentry -out:..\Release\logmessages.dll messages.res \
+	
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"..\Release\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "logmessages - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "../Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+	mc messages.mc \
+	rc -r -fo messages.res messages.rc \
+	link -dll -noentry -out:..\Debug\logmessages.dll messages.res \
+	
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"..\Debug\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "logmessages - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "logmessages___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "logmessages___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 1
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+	mc messages.mc \
+	rc -r -fo messages.res messages.rc \
+	link -dll -noentry -out:..\Debug\logmessages.dll messages.res \
+	
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+
+"..\Debug\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+   $(BuildCmds)
+# End Custom Build
+
+!ENDIF 
+
+# Begin Target
+
+# Name "logmessages - Win32 Release"
+# Name "logmessages - Win32 Debug"
+# Name "logmessages - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Source File
+
+SOURCE=.\messages.mc
+# End Source File
+# End Target
+# End Project
diff --git a/logmessages/messages.h b/logmessages/messages.h
new file mode 100644
index 0000000..bfb8c56
--- /dev/null
+++ b/logmessages/messages.h
@@ -0,0 +1,47 @@
+//
+//  Values are 32 bit values layed out as follows:
+//
+//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+//  +---+-+-+-----------------------+-------------------------------+
+//  |Sev|C|R|     Facility          |               Code            |
+//  +---+-+-+-----------------------+-------------------------------+
+//
+//  where
+//
+//      Sev - is the severity code
+//
+//          00 - Success
+//          01 - Informational
+//          10 - Warning
+//          11 - Error
+//
+//      C - is the Customer code flag
+//
+//      R - is a reserved bit
+//
+//      Facility - is the facility code
+//
+//      Code - is the facility's status code
+//
+//
+// Define the facility codes
+//
+
+
+//
+// Define the severity codes
+//
+
+
+//
+// MessageId: VNC4LogMessage
+//
+// MessageText:
+//
+//  %1: %2
+//  
+//  
+//
+#define VNC4LogMessage                   0x00000001L
+
diff --git a/logmessages/messages.mc b/logmessages/messages.mc
new file mode 100644
index 0000000..0bc8329
--- /dev/null
+++ b/logmessages/messages.mc
@@ -0,0 +1,7 @@
+MessageId=0x1
+Severity=Success
+SymbolicName=VNC4LogMessage
+Language=English
+%1: %2
+
+
diff --git a/logmessages/messages.rc b/logmessages/messages.rc
new file mode 100644
index 0000000..0885a89
--- /dev/null
+++ b/logmessages/messages.rc
@@ -0,0 +1,2 @@
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/network/Makefile.in b/network/Makefile.in
new file mode 100644
index 0000000..8aed303
--- /dev/null
+++ b/network/Makefile.in
@@ -0,0 +1,17 @@
+
+SRCS = TcpSocket.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @SOCKLEN_T_DEFINE@
+
+library = libnetwork.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/network/Socket.h b/network/Socket.h
new file mode 100644
index 0000000..a08afe5
--- /dev/null
+++ b/network/Socket.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Socket.h - abstract base-class for any kind of network stream/socket
+
+#ifndef __NETWORK_SOCKET_H__
+#define __NETWORK_SOCKET_H__
+
+#include <rdr/FdInStream.h>
+#include <rdr/FdOutStream.h>
+#include <rdr/Exception.h>
+
+namespace network {
+
+  class Socket {
+  public:
+    Socket(int fd)
+      : instream(new rdr::FdInStream(fd)),
+      outstream(new rdr::FdOutStream(fd)),
+      own_streams(true) {}
+    virtual ~Socket() {
+      if (own_streams) {
+        delete instream;
+        delete outstream;
+      }
+    }
+    rdr::FdInStream &inStream() {return *instream;}
+    rdr::FdOutStream &outStream() {return *outstream;}
+    int getFd() {return outstream->getFd();}
+    virtual void shutdown() = 0;
+
+    // information about this end of the socket
+    virtual char* getMyAddress() = 0; // a string e.g. "192.168.0.1"
+    virtual int getMyPort() = 0;
+    virtual char* getMyEndpoint() = 0; // <address>::<port>
+
+    // information about the remote end of the socket
+    virtual char* getPeerAddress() = 0; // a string e.g. "192.168.0.1"
+    virtual int getPeerPort() = 0;
+    virtual char* getPeerEndpoint() = 0; // <address>::<port>
+
+    // Is the remote end on the same machine?
+    virtual bool sameMachine() = 0;
+
+  protected:
+    Socket() : instream(0), outstream(0), own_streams(false) {}
+    Socket(rdr::FdInStream* i, rdr::FdOutStream* o, bool own)
+      : instream(i), outstream(o), own_streams(own) {}
+    rdr::FdInStream* instream;
+    rdr::FdOutStream* outstream;
+    bool own_streams;
+  };
+
+  class ConnectionFilter {
+  public:
+    virtual bool verifyConnection(Socket* s) = 0;
+    virtual bool queryUserAcceptConnection(Socket*) {return false;}
+  };
+
+  class SocketListener {
+  public:
+    SocketListener() : fd(0), filter(0) {}
+    virtual ~SocketListener() {}
+
+    // shutdown() stops the socket from accepting further connections
+    virtual void shutdown() = 0;
+
+    // accept() returns a new Socket object if there is a connection
+    // attempt in progress AND if the connection passes the filter
+    // if one is installed.  Otherwise, returns 0.
+    virtual Socket* accept() = 0;
+
+    void setFilter(ConnectionFilter* f) {filter = f;}
+    int getFd() {return fd;}
+  protected:
+    int fd;
+    ConnectionFilter* filter;
+  };
+
+  struct SocketException : public rdr::SystemException {
+    SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {}
+  };
+
+  class SocketServer {
+  public:
+    virtual ~SocketServer() {}
+
+    // addClient() tells the server to manage the socket.
+    //   If the server can't manage the socket, it must shutdown() it.
+    virtual void addClient(network::Socket* sock) = 0;
+
+    // processSocketEvent() tells the server there is a socket read event.
+    //   If there is an error, or the socket has been closed/shutdown then
+    //   the server MUST delete the socket AND return false.
+    virtual bool processSocketEvent(network::Socket* sock) = 0;
+
+    // checkTimeouts() allows the server to check socket timeouts, etc.  The
+    // return value is the number of milliseconds to wait before
+    // checkTimeouts() should be called again.  If this number is zero then
+    // there is no timeout and checkTimeouts() should be called the next time
+    // an event occurs.
+    virtual int checkTimeouts() = 0;
+
+    // soonestTimeout() is a function to help work out the soonest of several
+    // timeouts.
+    static void soonestTimeout(int* timeout, int newTimeout) {
+      if (newTimeout && (!*timeout || newTimeout < *timeout))
+        *timeout = newTimeout;
+    }
+  };
+
+}
+
+#endif // __NETWORK_SOCKET_H__
diff --git a/network/TcpSocket.cxx b/network/TcpSocket.cxx
new file mode 100644
index 0000000..b536e67
--- /dev/null
+++ b/network/TcpSocket.cxx
@@ -0,0 +1,458 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifdef WIN32
+//#include <io.h>
+#include <winsock2.h>
+#define errorNumber WSAGetLastError()
+#define snprintf _snprintf
+#else
+#define errorNumber errno
+#define closesocket close
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif
+
+#include <network/TcpSocket.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+
+#ifndef VNC_SOCKLEN_T
+#define VNC_SOCKLEN_T int
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long)-1)
+#endif
+
+using namespace network;
+using namespace rdr;
+
+static rfb::LogWriter vlog("TcpSocket");
+
+
+void
+TcpSocket::initTcpSockets() {
+#ifdef WIN32
+  WORD requiredVersion = MAKEWORD(2,0);
+  WSADATA initResult;
+  
+  if (WSAStartup(requiredVersion, &initResult) != 0)
+    throw SocketException("unable to initialise Winsock2", errorNumber);
+#else
+  signal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+// -=- TcpSocket
+
+TcpSocket::TcpSocket(int sock, bool close)
+  : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
+{
+}
+
+TcpSocket::TcpSocket(const char *host, int port)
+  : closeFd(true)
+{
+  int sock;
+
+  // - Create a socket
+  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    throw SocketException("unable to create socket", errorNumber);
+
+#ifndef WIN32
+  // - By default, close the socket on exec()
+  fcntl(sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+  // - Connect it to something
+
+  // Try processing the host as an IP address
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = inet_addr(host);
+  addr.sin_port = htons(port);
+  if ((int)addr.sin_addr.s_addr == -1) {
+    // Host was not an IP address - try resolving as DNS name
+    struct hostent *hostinfo;
+    hostinfo = gethostbyname(host);
+    if (hostinfo && hostinfo->h_addr) {
+      addr.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
+    } else {
+      int e = errorNumber;
+      closesocket(sock);
+      throw SocketException("unable to resolve host by name", e);
+    }
+  }
+
+  // Attempt to connect to the remote host
+  if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+    int e = errorNumber;
+    closesocket(sock);
+    throw SocketException("unable to connect to host", e);
+  }
+
+  int one = 1;
+  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+		 (char *)&one, sizeof(one)) < 0) {
+    int e = errorNumber;
+    closesocket(sock);
+    throw SocketException("unable to setsockopt TCP_NODELAY", e);
+  }
+
+  // Create the input and output streams
+  instream = new FdInStream(sock);
+  outstream = new FdOutStream(sock);
+  own_streams = true;
+}
+
+TcpSocket::~TcpSocket() {
+  if (closeFd)
+    closesocket(getFd());
+}
+
+char* TcpSocket::getMyAddress() {
+  struct sockaddr_in  info;
+  struct in_addr    addr;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+
+  getsockname(getFd(), (struct sockaddr *)&info, &info_size);
+  memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+  char* name = inet_ntoa(addr);
+  if (name) {
+    return rfb::strDup(name);
+  } else {
+    return rfb::strDup("");
+  }
+}
+
+int TcpSocket::getMyPort() {
+  return getSockPort(getFd());
+}
+
+char* TcpSocket::getMyEndpoint() {
+  rfb::CharArray address; address.buf = getMyAddress();
+  int port = getMyPort();
+
+  int buflen = strlen(address.buf) + 32;
+  char* buffer = new char[buflen];
+  sprintf(buffer, "%s::%d", address.buf, port);
+  return buffer;
+}
+
+char* TcpSocket::getPeerAddress() {
+  struct sockaddr_in  info;
+  struct in_addr    addr;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+
+  getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+  memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+  char* name = inet_ntoa(addr);
+  if (name) {
+    return rfb::strDup(name);
+  } else {
+    return rfb::strDup("");
+  }
+}
+
+int TcpSocket::getPeerPort() {
+  struct sockaddr_in  info;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+
+  getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+  return ntohs(info.sin_port);
+}
+
+char* TcpSocket::getPeerEndpoint() {
+  rfb::CharArray address; address.buf = getPeerAddress();
+  int port = getPeerPort();
+
+  int buflen = strlen(address.buf) + 32;
+  char* buffer = new char[buflen];
+  sprintf(buffer, "%s::%d", address.buf, port);
+  return buffer;
+}
+
+bool TcpSocket::sameMachine() {
+  struct sockaddr_in peeraddr, myaddr;
+  VNC_SOCKLEN_T addrlen = sizeof(struct sockaddr_in);
+
+  getpeername(getFd(), (struct sockaddr *)&peeraddr, &addrlen);
+  getsockname(getFd(), (struct sockaddr *)&myaddr, &addrlen);
+
+  return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
+}
+
+void TcpSocket::shutdown()
+{
+  ::shutdown(getFd(), 2);
+}
+
+bool TcpSocket::isSocket(int sock)
+{
+  struct sockaddr_in info;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+  return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+bool TcpSocket::isConnected(int sock)
+{
+  struct sockaddr_in info;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+  return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+int TcpSocket::getSockPort(int sock)
+{
+  struct sockaddr_in info;
+  VNC_SOCKLEN_T info_size = sizeof(info);
+  if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
+    return 0;
+  return ntohs(info.sin_port);
+}
+
+
+TcpListener::TcpListener(int port, bool localhostOnly, int sock, bool close_)
+  : closeFd(close_)
+{
+  if (sock != -1) {
+    fd = sock;
+    return;
+  }
+
+  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    throw SocketException("unable to create listening socket", errorNumber);
+
+#ifndef WIN32
+  // - By default, close the socket on exec()
+  fcntl(sock, F_SETFD, FD_CLOEXEC);
+
+  int one = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+		 (const char *)&one, sizeof(one)) < 0) {
+    int e = errorNumber;
+    closesocket(fd);
+    throw SocketException("unable to create listening socket", e);
+  }
+#endif
+
+  // - Bind it to the desired port
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(port);
+  if (localhostOnly)
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  else
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    int e = errorNumber;
+    closesocket(fd);
+    throw SocketException("unable to bind listening socket", e);
+  }
+
+  // - Set it to be a listening socket
+  if (listen(fd, 5) < 0) {
+    int e = errorNumber;
+    closesocket(fd);
+    throw SocketException("unable to set socket to listening mode", e);
+  }
+}
+
+TcpListener::~TcpListener() {
+  if (closeFd) closesocket(fd);
+}
+
+void TcpListener::shutdown()
+{
+#ifdef WIN32
+  closesocket(getFd());
+#else
+  ::shutdown(getFd(), 2);
+#endif
+}
+
+
+Socket*
+TcpListener::accept() {
+  int new_sock = -1;
+
+  // Accept an incoming connection
+  if ((new_sock = ::accept(fd, 0, 0)) < 0)
+    throw SocketException("unable to accept new connection", errorNumber);
+
+#ifndef WIN32
+  // - By default, close the socket on exec()
+  fcntl(new_sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+  // Disable Nagle's algorithm
+  int one = 1;
+  if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY,
+   (char *)&one, sizeof(one)) < 0) {
+    int e = errorNumber;
+    closesocket(new_sock);
+    throw SocketException("unable to setsockopt TCP_NODELAY", e);
+  }
+
+  // Create the socket object & check connection is allowed
+  TcpSocket* s = new TcpSocket(new_sock);
+  if (filter && !filter->verifyConnection(s)) {
+    delete s;
+    return 0;
+  }
+  return s;
+}
+
+void TcpListener::getMyAddresses(std::list<char*>* result) {
+  const hostent* addrs = gethostbyname(0);
+  if (addrs == 0)
+    throw rdr::SystemException("gethostbyname", errorNumber);
+  if (addrs->h_addrtype != AF_INET)
+    throw rdr::Exception("getMyAddresses: bad family");
+  for (int i=0; addrs->h_addr_list[i] != 0; i++) {
+    const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
+    char* addr = new char[strlen(addrC)+1];
+    strcpy(addr, addrC);
+    result->push_back(addr);
+  }
+}
+
+int TcpListener::getMyPort() {
+  return TcpSocket::getSockPort(getFd());
+}
+
+
+TcpFilter::TcpFilter(const char* spec) {
+  rfb::CharArray tmp;
+  tmp.buf = rfb::strDup(spec);
+  while (tmp.buf) {
+    rfb::CharArray first;
+    rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
+    if (strlen(first.buf))
+      filter.push_back(parsePattern(first.buf));
+  }
+}
+
+TcpFilter::~TcpFilter() {
+}
+
+
+static bool
+patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
+  unsigned long address = inet_addr(value);
+  if (address == INADDR_NONE) return false;
+  return ((pattern.address & pattern.mask) == (address & pattern.mask));
+}
+
+bool
+TcpFilter::verifyConnection(Socket* s) {
+  rfb::CharArray name;
+
+  name.buf = s->getPeerAddress();
+  std::list<TcpFilter::Pattern>::iterator i;
+  for (i=filter.begin(); i!=filter.end(); i++) {
+    if (patternMatchIP(*i, name.buf)) {
+      switch ((*i).action) {
+      case Accept:
+        vlog.debug("ACCEPT %s", name.buf);
+        return true;
+      case Query:
+        vlog.debug("QUERY %s", name.buf);
+        return queryUserAcceptConnection(s);
+      case Reject:
+        vlog.debug("REJECT %s", name.buf);
+        return false;
+      }
+    }
+  }
+
+  vlog.debug("[REJECT] %s", name.buf);
+  return false;
+}
+
+
+TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
+  TcpFilter::Pattern pattern;
+
+  bool expandMask = false;
+  rfb::CharArray addr, mask;
+
+  if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
+    if (rfb::strContains(mask.buf, '.')) {
+      pattern.mask = inet_addr(mask.buf);
+    } else {
+      pattern.mask = atoi(mask.buf);
+      expandMask = true;
+    }
+  } else {
+    pattern.mask = 32;
+    expandMask = true;
+  }
+  if (expandMask) {
+    unsigned long expanded = 0;
+    // *** check endianness!
+    for (int i=0; i<(int)pattern.mask; i++)
+      expanded |= 1<<(31-i);
+    pattern.mask = htonl(expanded);
+  }
+
+  pattern.address = inet_addr(addr.buf) & pattern.mask;
+  if ((pattern.address == INADDR_NONE) ||
+      (pattern.address == 0)) pattern.mask = 0;
+
+  switch(p[0]) {
+  case '+': pattern.action = TcpFilter::Accept; break;
+  case '-': pattern.action = TcpFilter::Reject; break;
+  case '?': pattern.action = TcpFilter::Query; break;
+  };
+
+  return pattern;
+}
+
+char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
+  in_addr tmp;
+  rfb::CharArray addr, mask;
+  tmp.s_addr = p.address;
+  addr.buf = rfb::strDup(inet_ntoa(tmp));
+  tmp.s_addr = p.mask;
+  mask.buf = rfb::strDup(inet_ntoa(tmp));
+  char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
+  switch (p.action) {
+  case Accept: result[0] = '+'; break;
+  case Reject: result[0] = '-'; break;
+  case Query: result[0] = '?'; break;
+  };
+  result[1] = 0;
+  strcat(result, addr.buf);
+  strcat(result, "/");
+  strcat(result, mask.buf);
+  return result;
+}
diff --git a/network/TcpSocket.h b/network/TcpSocket.h
new file mode 100644
index 0000000..9533340
--- /dev/null
+++ b/network/TcpSocket.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- TcpSocket.h - base-class for TCP stream sockets.
+//     This header also defines the TcpListener class, used
+//     to listen for incoming socket connections over TCP
+//
+//     NB: Any file descriptors created by the TcpSocket or
+//     TcpListener classes are close-on-exec if the OS supports
+//     it.  TcpSockets initialised with a caller-supplied fd
+//     are NOT set to close-on-exec.
+
+#ifndef __NETWORK_TCP_SOCKET_H__
+#define __NETWORK_TCP_SOCKET_H__
+
+#include <network/Socket.h>
+
+#include <list>
+
+namespace network {
+
+  class TcpSocket : public Socket {
+  public:
+    TcpSocket(int sock, bool close=true);
+    TcpSocket(const char *name, int port);
+    virtual ~TcpSocket();
+
+    virtual char* getMyAddress();
+    virtual int getMyPort();
+    virtual char* getMyEndpoint();
+
+    virtual char* getPeerAddress();
+    virtual int getPeerPort();
+    virtual char* getPeerEndpoint();
+    virtual bool sameMachine();
+
+    virtual void shutdown();
+
+    static void initTcpSockets();
+
+    static bool isSocket(int sock);
+    static bool isConnected(int sock);
+    static int getSockPort(int sock);
+  private:
+    bool closeFd;
+  };
+
+  class TcpListener : public SocketListener {
+  public:
+    TcpListener(int port, bool localhostOnly=false, int sock=-1,
+                bool close=true);
+    virtual ~TcpListener();
+
+    virtual void shutdown();
+    virtual Socket* accept();
+
+    void getMyAddresses(std::list<char*>* addrs);
+    int getMyPort();
+
+  private:
+    bool closeFd;
+  };
+
+  class TcpFilter : public ConnectionFilter {
+  public:
+    TcpFilter(const char* filter);
+    virtual ~TcpFilter();
+
+    virtual bool verifyConnection(Socket* s);
+
+    typedef enum {Accept, Reject, Query} Action;
+    struct Pattern {
+      Action action;
+      unsigned long address;
+      unsigned long mask;
+    };
+    static Pattern parsePattern(const char* s);
+    static char* patternToStr(const Pattern& p);
+  protected:
+    std::list<Pattern> filter;
+  };
+
+}
+
+#endif // __NETWORK_TCP_SOCKET_H__
diff --git a/network/msvcwarning.h b/network/msvcwarning.h
new file mode 100644
index 0000000..e93f2bb
--- /dev/null
+++ b/network/msvcwarning.h
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/network/network.dsp b/network/network.dsp
new file mode 100644
index 0000000..4290700
--- /dev/null
+++ b/network/network.dsp
@@ -0,0 +1,129 @@
+# Microsoft Developer Studio Project File - Name="network" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=network - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "network.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "network.mak" CFG="network - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "network - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "network - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "network - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "network - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "network - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "network - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "network___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "network___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "network - Win32 Release"
+# Name "network - Win32 Debug"
+# Name "network - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\TcpSocket.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Socket.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TcpSocket.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rdr/Exception.cxx b/rdr/Exception.cxx
new file mode 100644
index 0000000..5f7799f
--- /dev/null
+++ b/rdr/Exception.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/Exception.h>
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#endif
+
+using namespace rdr;
+
+SystemException::SystemException(const char* s, int err_)
+  : Exception(s, "rdr::SystemException"), err(err_)
+{
+  strncat(str_, ": ", len-1-strlen(str_));
+#ifdef _WIN32
+  // Windows error messages are crap, so use unix ones for common errors.
+  const char* msg = 0;
+  switch (err) {
+  case WSAECONNREFUSED: msg = "Connection refused";       break;
+  case WSAETIMEDOUT:    msg = "Connection timed out";     break;
+  case WSAECONNRESET:   msg = "Connection reset by peer"; break;
+  case WSAECONNABORTED: msg = "Connection aborted";       break;
+  }
+  if (msg) {
+    strncat(str_, msg, len-1-strlen(str_));
+  } else {
+#ifdef UNICODE
+    WCHAR* tmsg = new WCHAR[len-strlen(str_)];
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                  0, err, 0, tmsg, len-1-strlen(str_), 0);
+    WideCharToMultiByte(CP_ACP, 0, tmsg, wcslen(tmsg)+1,
+		              str_+strlen(str_), len-strlen(str_), 0, 0);
+    delete [] tmsg;
+#else
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                  0, err, 0, str_+strlen(str_), len-1-strlen(str_), 0);
+#endif
+  }
+    
+#else
+  strncat(str_, strerror(err), len-1-strlen(str_));
+#endif
+  strncat(str_, " (", len-1-strlen(str_));
+  char buf[20];
+  sprintf(buf,"%d",err);
+  strncat(str_, buf, len-1-strlen(str_));
+  strncat(str_, ")", len-1-strlen(str_));
+}
diff --git a/rdr/Exception.h b/rdr/Exception.h
new file mode 100644
index 0000000..98b3f0e
--- /dev/null
+++ b/rdr/Exception.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_EXCEPTION_H__
+#define __RDR_EXCEPTION_H__
+
+#include <stdio.h>
+#include <string.h>
+
+namespace rdr {
+
+  struct Exception {
+    enum { len = 256 };
+    char str_[len];
+    char type_[len];
+    Exception(const char* s=0, const char* e="rdr::Exception") {
+      str_[0] = 0;
+      if (s)
+        strncat(str_, s, len-1);
+      else
+        strcat(str_, "Exception");
+      type_[0] = 0;
+      strncat(type_, e, len-1);
+    }
+    virtual const char* str() const { return str_; }
+    virtual const char* type() const { return type_; }
+  };
+
+  struct SystemException : public Exception {
+    int err;
+    SystemException(const char* s, int err_);
+  }; 
+
+  struct TimedOut : public Exception {
+    TimedOut(const char* s="Timed out") : Exception(s,"rdr::TimedOut") {}
+  };
+ 
+  struct EndOfStream : public Exception {
+    EndOfStream(const char* s="End of stream")
+      : Exception(s,"rdr::EndOfStream") {}
+  };
+}
+
+#endif
diff --git a/rdr/FdInStream.cxx b/rdr/FdInStream.cxx
new file mode 100644
index 0000000..397847a
--- /dev/null
+++ b/rdr/FdInStream.cxx
@@ -0,0 +1,281 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#ifndef _WIN32_WCE
+#include <sys/timeb.h>
+#endif
+#define read(s,b,l) recv(s,(char*)b,l,0)
+#define close closesocket
+#undef errno
+#define errno WSAGetLastError()
+#undef EINTR
+#define EINTR WSAEINTR
+#else
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+// XXX should use autoconf HAVE_SYS_SELECT_H
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include <rdr/FdInStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 8192,
+       MIN_BULK_SIZE = 1024 };
+
+FdInStream::FdInStream(int fd_, int timeoutms_, int bufSize_,
+                       bool closeWhenDone_)
+  : fd(fd_), closeWhenDone(closeWhenDone_),
+    timeoutms(timeoutms_), blockCallback(0),
+    timing(false), timeWaitedIn100us(5), timedKbits(0),
+    bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+  ptr = end = start = new U8[bufSize];
+}
+
+FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_,
+                       int bufSize_)
+  : fd(fd_), timeoutms(0), blockCallback(blockCallback_),
+    timing(false), timeWaitedIn100us(5), timedKbits(0),
+    bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+  ptr = end = start = new U8[bufSize];
+}
+
+FdInStream::~FdInStream()
+{
+  delete [] start;
+  if (closeWhenDone) close(fd);
+}
+
+
+void FdInStream::setTimeout(int timeoutms_) {
+  timeoutms = timeoutms_;
+}
+
+void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_)
+{
+  blockCallback = blockCallback_;
+  timeoutms = 0;
+}
+
+int FdInStream::pos()
+{
+  return offset + ptr - start;
+}
+
+void FdInStream::readBytes(void* data, int length)
+{
+  if (length < MIN_BULK_SIZE) {
+    InStream::readBytes(data, length);
+    return;
+  }
+
+  U8* dataPtr = (U8*)data;
+
+  int n = end - ptr;
+  if (n > length) n = length;
+
+  memcpy(dataPtr, ptr, n);
+  dataPtr += n;
+  length -= n;
+  ptr += n;
+
+  while (length > 0) {
+    n = readWithTimeoutOrCallback(dataPtr, length);
+    dataPtr += n;
+    length -= n;
+    offset += n;
+  }
+}
+
+
+int FdInStream::overrun(int itemSize, int nItems, bool wait)
+{
+  if (itemSize > bufSize)
+    throw Exception("FdInStream overrun: max itemSize exceeded");
+
+  if (end - ptr != 0)
+    memmove(start, ptr, end - ptr);
+
+  offset += ptr - start;
+  end -= ptr - start;
+  ptr = start;
+
+  while (end < start + itemSize) {
+    int n = readWithTimeoutOrCallback((U8*)end, start + bufSize - end, wait);
+    if (n == 0) return 0;
+    end += n;
+  }
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
+
+#ifdef _WIN32
+static void gettimeofday(struct timeval* tv, void*)
+{
+  LARGE_INTEGER counts, countsPerSec;
+  static double usecPerCount = 0.0;
+
+  if (QueryPerformanceCounter(&counts)) {
+    if (usecPerCount == 0.0) {
+      QueryPerformanceFrequency(&countsPerSec);
+      usecPerCount = 1000000.0 / countsPerSec.QuadPart;
+    }
+
+    LONGLONG usecs = (LONGLONG)(counts.QuadPart * usecPerCount);
+    tv->tv_usec = (long)(usecs % 1000000);
+    tv->tv_sec = (long)(usecs / 1000000);
+
+  } else {
+#ifndef _WIN32_WCE
+    struct timeb tb;
+    ftime(&tb);
+    tv->tv_sec = tb.time;
+    tv->tv_usec = tb.millitm * 1000;
+#else
+    throw SystemException("QueryPerformanceCounter", GetLastError());
+#endif
+  }
+}
+#endif
+
+//
+// readWithTimeoutOrCallback() reads up to the given length in bytes from the
+// file descriptor into a buffer.  If the wait argument is false, then zero is
+// returned if no bytes can be read without blocking.  Otherwise if a
+// blockCallback is set, it will be called (repeatedly) instead of blocking.
+// If alternatively there is a timeout set and that timeout expires, it throws
+// a TimedOut exception.  Otherwise it returns the number of bytes read.  It
+// never attempts to read() unless select() indicates that the fd is readable -
+// this means it can be used on an fd which has been set non-blocking.  It also
+// has to cope with the annoying possibility of both select() and read()
+// returning EINTR.
+//
+
+int FdInStream::readWithTimeoutOrCallback(void* buf, int len, bool wait)
+{
+  struct timeval before, after;
+  if (timing)
+    gettimeofday(&before, 0);
+
+  int n;
+  while (true) {
+    do {
+      fd_set fds;
+      struct timeval tv;
+      struct timeval* tvp = &tv;
+
+      if (!wait) {
+        tv.tv_sec = tv.tv_usec = 0;
+      } else if (timeoutms != -1) {
+        tv.tv_sec = timeoutms / 1000;
+        tv.tv_usec = (timeoutms % 1000) * 1000;
+      } else {
+        tvp = 0;
+      }
+
+      FD_ZERO(&fds);
+      FD_SET(fd, &fds);
+      n = select(fd+1, &fds, 0, 0, tvp);
+    } while (n < 0 && errno == EINTR);
+
+    if (n > 0) break;
+    if (n < 0) throw SystemException("select",errno);
+    if (!wait) return 0;
+    if (!blockCallback) throw TimedOut();
+
+    blockCallback->blockCallback();
+  }
+
+  do {
+    n = ::read(fd, buf, len);
+  } while (n < 0 && errno == EINTR);
+
+  if (n < 0) throw SystemException("read",errno);
+  if (n == 0) throw EndOfStream();
+
+  if (timing) {
+    gettimeofday(&after, 0);
+//      fprintf(stderr,"%d.%06d\n",(after.tv_sec - before.tv_sec),
+//              (after.tv_usec - before.tv_usec));
+    int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
+                         (after.tv_usec - before.tv_usec) / 100);
+    int newKbits = n * 8 / 1000;
+
+//      if (newTimeWaited == 0) {
+//        fprintf(stderr,"new kbps infinite t %d k %d\n",
+//                newTimeWaited, newKbits);
+//      } else {
+//        fprintf(stderr,"new kbps %d t %d k %d\n",
+//                newKbits * 10000 / newTimeWaited, newTimeWaited, newKbits);
+//      }
+
+    // limit rate to between 10kbit/s and 40Mbit/s
+
+    if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
+    if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
+
+    timeWaitedIn100us += newTimeWaited;
+    timedKbits += newKbits;
+  }
+
+  return n;
+}
+
+void FdInStream::startTiming()
+{
+  timing = true;
+
+  // Carry over up to 1s worth of previous rate for smoothing.
+
+  if (timeWaitedIn100us > 10000) {
+    timedKbits = timedKbits * 10000 / timeWaitedIn100us;
+    timeWaitedIn100us = 10000;
+  }
+}
+
+void FdInStream::stopTiming()
+{
+  timing = false; 
+  if (timeWaitedIn100us < timedKbits/2)
+    timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
+}
+
+unsigned int FdInStream::kbitsPerSecond()
+{
+  // The following calculation will overflow 32-bit arithmetic if we have
+  // received more than about 50Mbytes (400Mbits) since we started timing, so
+  // it should be OK for a single RFB update.
+
+  return timedKbits * 10000 / timeWaitedIn100us;
+}
diff --git a/rdr/FdInStream.h b/rdr/FdInStream.h
new file mode 100644
index 0000000..d038b1b
--- /dev/null
+++ b/rdr/FdInStream.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// FdInStream streams from a file descriptor.
+//
+
+#ifndef __RDR_FDINSTREAM_H__
+#define __RDR_FDINSTREAM_H__
+
+#include <rdr/InStream.h>
+
+namespace rdr {
+
+  class FdInStreamBlockCallback {
+  public:
+    virtual void blockCallback() = 0;
+  };
+
+  class FdInStream : public InStream {
+
+  public:
+
+    FdInStream(int fd, int timeoutms=-1, int bufSize=0,
+               bool closeWhenDone_=false);
+    FdInStream(int fd, FdInStreamBlockCallback* blockCallback, int bufSize=0);
+    virtual ~FdInStream();
+
+    void setTimeout(int timeoutms);
+    void setBlockCallback(FdInStreamBlockCallback* blockCallback);
+    int getFd() { return fd; }
+    int pos();
+    void readBytes(void* data, int length);
+
+    void startTiming();
+    void stopTiming();
+    unsigned int kbitsPerSecond();
+    unsigned int timeWaited() { return timeWaitedIn100us; }
+
+  protected:
+    int overrun(int itemSize, int nItems, bool wait);
+
+  private:
+    int readWithTimeoutOrCallback(void* buf, int len, bool wait=true);
+
+    int fd;
+    bool closeWhenDone;
+    int timeoutms;
+    FdInStreamBlockCallback* blockCallback;
+
+    bool timing;
+    unsigned int timeWaitedIn100us;
+    unsigned int timedKbits;
+
+    int bufSize;
+    int offset;
+    U8* start;
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/FdOutStream.cxx b/rdr/FdOutStream.cxx
new file mode 100644
index 0000000..6795fc8
--- /dev/null
+++ b/rdr/FdOutStream.cxx
@@ -0,0 +1,174 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#define write(s,b,l) send(s,(const char*)b,l,0)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef errno
+#define errno WSAGetLastError()
+#undef EINTR
+#define EINTR WSAEINTR
+#else
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#include <rdr/FdOutStream.h>
+#include <rdr/Exception.h>
+
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384,
+       MIN_BULK_SIZE = 1024 };
+
+FdOutStream::FdOutStream(int fd_, int timeoutms_, int bufSize_)
+  : fd(fd_), timeoutms(timeoutms_),
+    bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+  ptr = start = new U8[bufSize];
+  end = start + bufSize;
+}
+
+FdOutStream::~FdOutStream()
+{
+  try {
+    flush();
+  } catch (Exception&) {
+  }
+  delete [] start;
+}
+
+void FdOutStream::setTimeout(int timeoutms_) {
+  timeoutms = timeoutms_;
+}
+
+void FdOutStream::writeBytes(const void* data, int length)
+{
+  if (length < MIN_BULK_SIZE) {
+    OutStream::writeBytes(data, length);
+    return;
+  }
+
+  const U8* dataPtr = (const U8*)data;
+
+  flush();
+
+  while (length > 0) {
+    int n = writeWithTimeout(dataPtr, length);
+    length -= n;
+    dataPtr += n;
+    offset += n;
+  }
+}
+
+int FdOutStream::length()
+{
+  return offset + ptr - start;
+}
+
+void FdOutStream::flush()
+{
+  U8* sentUpTo = start;
+  while (sentUpTo < ptr) {
+    int n = writeWithTimeout((const void*) sentUpTo, ptr - sentUpTo);
+    sentUpTo += n;
+    offset += n;
+  }
+
+  ptr = start;
+}
+
+
+int FdOutStream::overrun(int itemSize, int nItems)
+{
+  if (itemSize > bufSize)
+    throw Exception("FdOutStream overrun: max itemSize exceeded");
+
+  flush();
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
+
+//
+// writeWithTimeout() writes up to the given length in bytes from the given
+// buffer to the file descriptor.  If there is a timeout set and that timeout
+// expires, it throws a TimedOut exception.  Otherwise it returns the number of
+// bytes written.  It never attempts to write() unless select() indicates that
+// the fd is writable - this means it can be used on an fd which has been set
+// non-blocking.  It also has to cope with the annoying possibility of both
+// select() and write() returning EINTR.
+//
+
+int FdOutStream::writeWithTimeout(const void* data, int length)
+{
+  int n;
+
+  do {
+
+    do {
+      fd_set fds;
+      struct timeval tv;
+      struct timeval* tvp = &tv;
+
+      if (timeoutms != -1) {
+        tv.tv_sec = timeoutms / 1000;
+        tv.tv_usec = (timeoutms % 1000) * 1000;
+      } else {
+        tvp = 0;
+      }
+
+      FD_ZERO(&fds);
+      FD_SET(fd, &fds);
+#ifdef _WIN32_WCE
+      // NB: This fixes a broken Winsock2 select() behaviour.  select()
+      // never returns for non-blocking sockets, unless they're already
+      // ready to be written to...
+      u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
+#endif
+      n = select(fd+1, 0, &fds, 0, tvp);
+#ifdef _WIN32_WCE
+      u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
+#endif
+    } while (n < 0 && errno == EINTR);
+
+    if (n < 0) throw SystemException("select",errno);
+
+    if (n == 0) throw TimedOut();
+
+    do {
+      n = ::write(fd, data, length);
+    } while (n < 0 && (errno == EINTR));
+      
+    // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
+    // condition, found only under Win98 (first edition), with slow
+    // network connections.  Should in fact never ever happen...
+  } while (n < 0 && (errno == EWOULDBLOCK));
+
+  if (n < 0) throw SystemException("write",errno);
+
+  return n;
+}
diff --git a/rdr/FdOutStream.h b/rdr/FdOutStream.h
new file mode 100644
index 0000000..9c46db9
--- /dev/null
+++ b/rdr/FdOutStream.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// FdOutStream streams to a file descriptor.
+//
+
+#ifndef __RDR_FDOUTSTREAM_H__
+#define __RDR_FDOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+  class FdOutStream : public OutStream {
+
+  public:
+
+    FdOutStream(int fd, int timeoutms=-1, int bufSize=0);
+    virtual ~FdOutStream();
+
+    void setTimeout(int timeoutms);
+    int getFd() { return fd; }
+
+    void flush();
+    int length();
+    void writeBytes(const void* data, int length);
+
+  private:
+    int overrun(int itemSize, int nItems);
+    int writeWithTimeout(const void* data, int length);
+    int fd;
+    int timeoutms;
+    int bufSize;
+    int offset;
+    U8* start;
+  };
+
+}
+
+#endif
diff --git a/rdr/FixedMemOutStream.h b/rdr/FixedMemOutStream.h
new file mode 100644
index 0000000..f149e60
--- /dev/null
+++ b/rdr/FixedMemOutStream.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// A FixedMemOutStream writes to a buffer of a fixed length.
+//
+
+#ifndef __RDR_FIXEDMEMOUTSTREAM_H__
+#define __RDR_FIXEDMEMOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+  class FixedMemOutStream : public OutStream {
+
+  public:
+
+    FixedMemOutStream(void* buf, int len) {
+      ptr = start = (U8*)buf;
+      end = start + len;
+    }
+
+    int length() { return ptr - start; }
+    void reposition(int pos) { ptr = start + pos; }
+    const void* data() { return (const void*)start; }
+
+  private:
+
+    int overrun(int itemSize, int nItems) { throw EndOfStream(); }
+    U8* start;
+  };
+
+}
+
+#endif
diff --git a/rdr/HexInStream.cxx b/rdr/HexInStream.cxx
new file mode 100644
index 0000000..a454ca8
--- /dev/null
+++ b/rdr/HexInStream.cxx
@@ -0,0 +1,117 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/HexInStream.h>
+#include <rdr/Exception.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 16384;
+
+static inline int min(int a, int b) {return a<b ? a : b;}
+
+HexInStream::HexInStream(InStream& is, int bufSize_)
+: bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_LEN), offset(0), in_stream(is)
+{
+  ptr = end = start = new U8[bufSize];
+}
+
+HexInStream::~HexInStream() {
+  delete [] start;
+}
+
+
+bool HexInStream::readHexAndShift(char c, int* v) {
+  c=tolower(c);
+  if ((c >= '0') && (c <= '9'))
+    *v = (*v << 4) + (c - '0');
+  else if ((c >= 'a') && (c <= 'f'))
+    *v = (*v << 4) + (c - 'a' + 10);
+  else
+    return false;
+  return true;
+}
+
+bool HexInStream::hexStrToBin(const char* s, char** data, int* length) {
+  int l=strlen(s);
+  if ((l % 2) == 0) {
+    delete [] *data;
+    *data = 0; *length = 0;
+    if (l == 0)
+      return true;
+    *data = new char[l/2];
+    *length = l/2;
+    for(int i=0;i<l;i+=2) {
+      int byte = 0;
+      if (!readHexAndShift(s[i], &byte) ||
+        !readHexAndShift(s[i+1], &byte))
+        goto decodeError;
+      (*data)[i/2] = byte;
+    }
+    return true;
+  }
+decodeError:
+  delete [] *data;
+  *data = 0;
+  *length = 0;
+  return false;
+}
+
+
+int HexInStream::pos() {
+  return offset + ptr - start;
+}
+
+int HexInStream::overrun(int itemSize, int nItems, bool wait) {
+  if (itemSize > bufSize)
+    throw Exception("HexInStream overrun: max itemSize exceeded");
+
+  if (end - ptr != 0)
+    memmove(start, ptr, end - ptr);
+
+  end -= ptr - start;
+  offset += ptr - start;
+  ptr = start;
+
+  while (end < ptr + itemSize) {
+    int n = in_stream.check(2, 1, wait);
+    if (n == 0) return 0;
+    const U8* iptr = in_stream.getptr();
+    const U8* eptr = in_stream.getend();
+    int length = min((eptr - iptr)/2, start + bufSize - end);
+
+    U8* optr = (U8*) end;
+    for (int i=0; i<length; i++) {
+      int v = 0;
+      readHexAndShift(iptr[i*2], &v);
+      readHexAndShift(iptr[i*2+1], &v);
+      optr[i] = v;
+    }
+
+    in_stream.setptr(iptr + length*2);
+    end += length;
+  }
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
diff --git a/rdr/HexInStream.h b/rdr/HexInStream.h
new file mode 100644
index 0000000..fbfc273
--- /dev/null
+++ b/rdr/HexInStream.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_HEX_INSTREAM_H__
+#define __RDR_HEX_INSTREAM_H__
+
+#include <rdr/InStream.h>
+
+namespace rdr {
+
+  class HexInStream : public InStream {
+  public:
+
+    HexInStream(InStream& is, int bufSize=0);
+    virtual ~HexInStream();
+
+    int pos();
+
+    static bool readHexAndShift(char c, int* v);
+    static bool hexStrToBin(const char* s, char** data, int* length);
+
+  protected:
+    int overrun(int itemSize, int nItems, bool wait);
+
+  private:
+    int bufSize;
+    U8* start;
+    int offset;
+
+    InStream& in_stream;
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/HexOutStream.cxx b/rdr/HexOutStream.cxx
new file mode 100644
index 0000000..f82d9f5
--- /dev/null
+++ b/rdr/HexOutStream.cxx
@@ -0,0 +1,110 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/HexOutStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 16384;
+
+static inline int min(int a, int b) {return a<b ? a : b;}
+
+HexOutStream::HexOutStream(OutStream& os, int buflen)
+: out_stream(os), offset(0), bufSize(buflen ? buflen : DEFAULT_BUF_LEN)
+{
+  if (bufSize % 2)
+    bufSize--;
+  ptr = start = new U8[bufSize];
+  end = start + bufSize;
+}
+
+HexOutStream::~HexOutStream() {
+  delete [] start;
+}
+
+
+char HexOutStream::intToHex(int i) {
+  if ((i>=0) && (i<=9))
+    return '0'+i;
+  else if ((i>=10) && (i<=15))
+    return 'a'+(i-10);
+  else
+    throw rdr::Exception("intToHex failed");
+}
+
+char* HexOutStream::binToHexStr(const char* data, int length) {
+  char* buffer = new char[length*2+1];
+  for (int i=0; i<length; i++) {
+    buffer[i*2] = intToHex((data[i] >> 4) & 15);
+    buffer[i*2+1] = intToHex((data[i] & 15));
+    if (!buffer[i*2] || !buffer[i*2+1]) {
+      delete [] buffer;
+      return 0;
+    }
+  }
+  buffer[length*2] = 0;
+  return buffer;
+}
+
+
+void
+HexOutStream::writeBuffer() {
+  U8* pos = start;
+  while (pos != ptr) {
+    out_stream.check(2);
+    U8* optr = out_stream.getptr();
+    U8* oend = out_stream.getend();
+    int length = min(ptr-pos, (oend-optr)/2);
+
+    for (int i=0; i<length; i++) {
+      optr[i*2] = intToHex((pos[i] >> 4) & 0xf);
+      optr[i*2+1] = intToHex(pos[i] & 0xf);
+    }
+
+    out_stream.setptr(optr + length*2);
+    pos += length;
+  }
+  offset += ptr - start;
+  ptr = start;
+}
+
+int HexOutStream::length()
+{
+  return offset + ptr - start;
+}
+
+void
+HexOutStream::flush() {
+  writeBuffer();
+  out_stream.flush();
+}
+
+int
+HexOutStream::overrun(int itemSize, int nItems) {
+  if (itemSize > bufSize)
+    throw Exception("HexOutStream overrun: max itemSize exceeded");
+
+  writeBuffer();
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
+
diff --git a/rdr/HexOutStream.h b/rdr/HexOutStream.h
new file mode 100644
index 0000000..691a16b
--- /dev/null
+++ b/rdr/HexOutStream.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_HEX_OUTSTREAM_H__
+#define __RDR_HEX_OUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+  class HexOutStream : public OutStream {
+  public:
+
+    HexOutStream(OutStream& os, int buflen=0);
+    virtual ~HexOutStream();
+
+    void flush();
+    int length();
+
+    static char intToHex(int i);
+    static char* binToHexStr(const char* data, int length);
+
+  private:
+    void writeBuffer();
+    int overrun(int itemSize, int nItems);
+
+    OutStream& out_stream;
+
+    U8* start;
+    int offset;
+    int bufSize;
+  };
+
+}
+
+#endif
diff --git a/rdr/InStream.cxx b/rdr/InStream.cxx
new file mode 100644
index 0000000..13a6fa1
--- /dev/null
+++ b/rdr/InStream.cxx
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+U32 InStream::maxStringLength = 65535;
+
+char* InStream::readString()
+{
+  U32 len = readU32();
+  if (len > maxStringLength)
+    throw Exception("InStream max string length exceeded");
+  char* str = new char[len+1];
+  readBytes(str, len);
+  str[len] = 0;
+  return str;
+}
diff --git a/rdr/InStream.h b/rdr/InStream.h
new file mode 100644
index 0000000..2048daa
--- /dev/null
+++ b/rdr/InStream.h
@@ -0,0 +1,153 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+#ifndef __RDR_INSTREAM_H__
+#define __RDR_INSTREAM_H__
+
+#include <rdr/types.h>
+#include <string.h> // for memcpy
+
+namespace rdr {
+
+  class InStream {
+
+  public:
+
+    virtual ~InStream() {}
+
+    // check() ensures there is buffer data for at least one item of size
+    // itemSize bytes.  Returns the number of items in the buffer (up to a
+    // maximum of nItems).  If wait is false, then instead of blocking to wait
+    // for the bytes, zero is returned if the bytes are not immediately
+    // available.
+
+    inline int check(int itemSize, int nItems=1, bool wait=true)
+    {
+      if (ptr + itemSize * nItems > end) {
+        if (ptr + itemSize > end)
+          return overrun(itemSize, nItems, wait);
+
+        nItems = (end - ptr) / itemSize;
+      }
+      return nItems;
+    }
+
+    // checkNoWait() tries to make sure that the given number of bytes can
+    // be read without blocking.  It returns true if this is the case, false
+    // otherwise.  The length must be "small" (less than the buffer size).
+
+    inline bool checkNoWait(int length) { return check(length, 1, false)!=0; }
+
+    // readU/SN() methods read unsigned and signed N-bit integers.
+
+    inline U8  readU8()  { check(1); return *ptr++; }
+    inline U16 readU16() { check(2); int b0 = *ptr++; int b1 = *ptr++;
+                           return b0 << 8 | b1; }
+    inline U32 readU32() { check(4); int b0 = *ptr++; int b1 = *ptr++;
+                                     int b2 = *ptr++; int b3 = *ptr++;
+                           return b0 << 24 | b1 << 16 | b2 << 8 | b3; }
+
+    inline S8  readS8()  { return (S8) readU8();  }
+    inline S16 readS16() { return (S16)readU16(); }
+    inline S32 readS32() { return (S32)readU32(); }
+
+    // readString() reads a string - a U32 length followed by the data.
+    // Returns a null-terminated string - the caller should delete[] it
+    // afterwards.
+
+    char* readString();
+
+    // maxStringLength protects against allocating a huge buffer.  Set it
+    // higher if you need longer strings.
+
+    static U32 maxStringLength;
+
+    inline void skip(int bytes) {
+      while (bytes > 0) {
+        int n = check(1, bytes);
+        ptr += n;
+        bytes -= n;
+      }
+    }
+
+    // readBytes() reads an exact number of bytes.
+
+    virtual void readBytes(void* data, int length) {
+      U8* dataPtr = (U8*)data;
+      U8* dataEnd = dataPtr + length;
+      while (dataPtr < dataEnd) {
+        int n = check(1, dataEnd - dataPtr);
+        memcpy(dataPtr, ptr, n);
+        ptr += n;
+        dataPtr += n;
+      }
+    }
+
+    // readOpaqueN() reads a quantity without byte-swapping.
+
+    inline U8  readOpaque8()  { return readU8(); }
+    inline U16 readOpaque16() { check(2); U16 r; ((U8*)&r)[0] = *ptr++;
+                                ((U8*)&r)[1] = *ptr++; return r; }
+    inline U32 readOpaque32() { check(4); U32 r; ((U8*)&r)[0] = *ptr++;
+                                ((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
+                                ((U8*)&r)[3] = *ptr++; return r; }
+    inline U32 readOpaque24A() { check(3); U32 r=0; ((U8*)&r)[0] = *ptr++;
+                                 ((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
+                                 return r; }
+    inline U32 readOpaque24B() { check(3); U32 r=0; ((U8*)&r)[1] = *ptr++;
+                                 ((U8*)&r)[2] = *ptr++; ((U8*)&r)[3] = *ptr++;
+                                 return r; }
+
+    // pos() returns the position in the stream.
+
+    virtual int pos() = 0;
+
+    // getptr(), getend() and setptr() are "dirty" methods which allow you to
+    // manipulate the buffer directly.  This is useful for a stream which is a
+    // wrapper around an underlying stream.
+
+    inline const U8* getptr() const { return ptr; }
+    inline const U8* getend() const { return end; }
+    inline void setptr(const U8* p) { ptr = p; }
+
+  private:
+
+    // overrun() is implemented by a derived class to cope with buffer overrun.
+    // It ensures there are at least itemSize bytes of buffer data.  Returns
+    // the number of items in the buffer (up to a maximum of nItems).  itemSize
+    // is supposed to be "small" (a few bytes).  If wait is false, then
+    // instead of blocking to wait for the bytes, zero is returned if the bytes
+    // are not immediately available.
+
+    virtual int overrun(int itemSize, int nItems, bool wait=true) = 0;
+
+  protected:
+
+    InStream() {}
+    const U8* ptr;
+    const U8* end;
+  };
+
+}
+
+#endif
diff --git a/rdr/Makefile.in b/rdr/Makefile.in
new file mode 100644
index 0000000..9edf284
--- /dev/null
+++ b/rdr/Makefile.in
@@ -0,0 +1,19 @@
+
+SRCS = Exception.cxx FdInStream.cxx FdOutStream.cxx InStream.cxx \
+       NullOutStream.cxx RandomStream.cxx ZlibInStream.cxx ZlibOutStream.cxx \
+       HexInStream.cxx HexOutStream.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @ZLIB_INCLUDE@
+
+library = librdr.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/rdr/MemInStream.h b/rdr/MemInStream.h
new file mode 100644
index 0000000..2b05e3d
--- /dev/null
+++ b/rdr/MemInStream.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// rdr::MemInStream is an InStream which streams from a given memory buffer.
+// If the deleteWhenDone parameter is true then the buffer will be delete[]d in
+// the destructor.  Note that it is delete[]d as a U8* - strictly speaking this
+// means it ought to be new[]ed as a U8* as well, but on most platforms this
+// doesn't matter.
+//
+
+#ifndef __RDR_MEMINSTREAM_H__
+#define __RDR_MEMINSTREAM_H__
+
+#include <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+  class MemInStream : public InStream {
+
+  public:
+
+    MemInStream(const void* data, int len, bool deleteWhenDone_=false)
+      : start((const U8*)data), deleteWhenDone(deleteWhenDone_)
+    {
+      ptr = start;
+      end = start + len;
+    }
+
+    virtual ~MemInStream() {
+      if (deleteWhenDone)
+        delete [] (U8*)start;
+    }
+
+    int pos() { return ptr - start; }
+    void reposition(int pos) { ptr = start + pos; }
+
+  private:
+
+    int overrun(int itemSize, int nItems, bool wait) { throw EndOfStream(); }
+    const U8* start;
+    bool deleteWhenDone;
+  };
+
+}
+
+#endif
diff --git a/rdr/MemOutStream.h b/rdr/MemOutStream.h
new file mode 100644
index 0000000..3456f5c
--- /dev/null
+++ b/rdr/MemOutStream.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// A MemOutStream grows as needed when data is written to it.
+//
+
+#ifndef __RDR_MEMOUTSTREAM_H__
+#define __RDR_MEMOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+  class MemOutStream : public OutStream {
+
+  public:
+
+    MemOutStream(int len=1024) {
+      start = ptr = new U8[len];
+      end = start + len;
+    }
+
+    virtual ~MemOutStream() {
+      delete [] start;
+    }
+
+    void writeBytes(const void* data, int length) {
+      check(length);
+      memcpy(ptr, data, length);
+      ptr += length;
+    }
+
+    int length() { return ptr - start; }
+    void clear() { ptr = start; };
+    void reposition(int pos) { ptr = start + pos; }
+
+    // data() returns a pointer to the buffer.
+
+    const void* data() { return (const void*)start; }
+
+  private:
+
+    // overrun() either doubles the buffer or adds enough space for nItems of
+    // size itemSize bytes.
+
+    int overrun(int itemSize, int nItems) {
+      int len = ptr - start + itemSize * nItems;
+      if (len < (end - start) * 2)
+        len = (end - start) * 2;
+
+      U8* newStart = new U8[len];
+      memcpy(newStart, start, ptr - start);
+      ptr = newStart + (ptr - start);
+      delete [] start;
+      start = newStart;
+      end = newStart + len;
+
+      return nItems;
+    }
+
+    U8* start;
+  };
+
+}
+
+#endif
diff --git a/rdr/NullOutStream.cxx b/rdr/NullOutStream.cxx
new file mode 100644
index 0000000..e940f2a
--- /dev/null
+++ b/rdr/NullOutStream.cxx
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/NullOutStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+static const int bufferSize = 1024;
+
+NullOutStream::NullOutStream()
+  : offset(0)
+{
+  start = ptr = new U8[bufferSize];
+  end = start + bufferSize;
+}
+
+NullOutStream::~NullOutStream()
+{
+  delete [] start;
+}
+
+int NullOutStream::length()
+{
+  return offset + ptr - start;
+}
+
+void NullOutStream::writeBytes(const void* data, int length)
+{
+  offset += length;
+}
+
+int NullOutStream::overrun(int itemSize, int nItems)
+{
+  if (itemSize > bufferSize)
+    throw Exception("NullOutStream overrun: max itemSize exceeded");
+
+  offset += ptr - start;
+  ptr = start;
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
diff --git a/rdr/NullOutStream.h b/rdr/NullOutStream.h
new file mode 100644
index 0000000..84a56e5
--- /dev/null
+++ b/rdr/NullOutStream.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_NULLOUTSTREAM_H__
+#define __RDR_NULLOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+  class NullOutStream : public OutStream {
+
+  public:
+    NullOutStream();
+    virtual ~NullOutStream();
+    int length();
+    void writeBytes(const void* data, int length);
+
+  private:
+    int overrun(int itemSize, int nItems);
+    int offset;
+    U8* start;
+  };
+
+}
+
+#endif
diff --git a/rdr/OutStream.h b/rdr/OutStream.h
new file mode 100644
index 0000000..a064bcc
--- /dev/null
+++ b/rdr/OutStream.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// rdr::OutStream marshalls data into a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+#ifndef __RDR_OUTSTREAM_H__
+#define __RDR_OUTSTREAM_H__
+
+#include <rdr/types.h>
+#include <string.h> // for memcpy
+
+namespace rdr {
+
+  class OutStream {
+
+  protected:
+
+    OutStream() {}
+
+  public:
+
+    virtual ~OutStream() {}
+
+    // check() ensures there is buffer space for at least one item of size
+    // itemSize bytes.  Returns the number of items which fit (up to a maximum
+    // of nItems).
+
+    inline int check(int itemSize, int nItems=1)
+    {
+      if (ptr + itemSize * nItems > end) {
+        if (ptr + itemSize > end)
+          return overrun(itemSize, nItems);
+
+        nItems = (end - ptr) / itemSize;
+      }
+      return nItems;
+    }
+
+    // writeU/SN() methods write unsigned and signed N-bit integers.
+
+    inline void writeU8( U8  u) { check(1); *ptr++ = u; }
+    inline void writeU16(U16 u) { check(2); *ptr++ = u >> 8; *ptr++ = (U8)u; }
+    inline void writeU32(U32 u) { check(4); *ptr++ = u >> 24; *ptr++ = u >> 16;
+                                            *ptr++ = u >> 8; *ptr++ = u; }
+
+    inline void writeS8( S8  s) { writeU8((U8)s); }
+    inline void writeS16(S16 s) { writeU16((U16)s); }
+    inline void writeS32(S32 s) { writeU32((U32)s); }
+
+    // writeString() writes a string - a U32 length followed by the data.  The
+    // given string should be null-terminated (but the terminating null is not
+    // written to the stream).
+
+    inline void writeString(const char* str) {
+      U32 len = strlen(str);
+      writeU32(len);
+      writeBytes(str, len);
+    }
+
+    inline void pad(int bytes) {
+      while (bytes-- > 0) writeU8(0);
+    }
+
+    inline void skip(int bytes) {
+      while (bytes > 0) {
+        int n = check(1, bytes);
+        ptr += n;
+        bytes -= n;
+      }
+    }
+
+    // writeBytes() writes an exact number of bytes.
+
+    virtual void writeBytes(const void* data, int length) {
+      const U8* dataPtr = (const U8*)data;
+      const U8* dataEnd = dataPtr + length;
+      while (dataPtr < dataEnd) {
+        int n = check(1, dataEnd - dataPtr);
+        memcpy(ptr, dataPtr, n);
+        ptr += n;
+        dataPtr += n;
+      }
+    }
+
+    // writeOpaqueN() writes a quantity without byte-swapping.
+
+    inline void writeOpaque8( U8  u) { writeU8(u); }
+    inline void writeOpaque16(U16 u) { check(2); *ptr++ = ((U8*)&u)[0];
+                                       *ptr++ = ((U8*)&u)[1]; }
+    inline void writeOpaque32(U32 u) { check(4); *ptr++ = ((U8*)&u)[0];
+                                       *ptr++ = ((U8*)&u)[1];
+                                       *ptr++ = ((U8*)&u)[2];
+                                       *ptr++ = ((U8*)&u)[3]; }
+    inline void writeOpaque24A(U32 u) { check(3); *ptr++ = ((U8*)&u)[0];
+                                        *ptr++ = ((U8*)&u)[1];
+                                        *ptr++ = ((U8*)&u)[2]; }
+    inline void writeOpaque24B(U32 u) { check(3); *ptr++ = ((U8*)&u)[1];
+                                        *ptr++ = ((U8*)&u)[2];
+                                        *ptr++ = ((U8*)&u)[3]; }
+
+    // length() returns the length of the stream.
+
+    virtual int length() = 0;
+
+    // flush() requests that the stream be flushed.
+
+    virtual void flush() {}
+
+    // getptr(), getend() and setptr() are "dirty" methods which allow you to
+    // manipulate the buffer directly.  This is useful for a stream which is a
+    // wrapper around an underlying stream.
+
+    inline U8* getptr() { return ptr; }
+    inline U8* getend() { return end; }
+    inline void setptr(U8* p) { ptr = p; }
+
+  private:
+
+    // overrun() is implemented by a derived class to cope with buffer overrun.
+    // It ensures there are at least itemSize bytes of buffer space.  Returns
+    // the number of items which fit (up to a maximum of nItems).  itemSize is
+    // supposed to be "small" (a few bytes).
+
+    virtual int overrun(int itemSize, int nItems) = 0;
+
+  protected:
+
+    U8* ptr;
+    U8* end;
+  };
+
+}
+
+#endif
diff --git a/rdr/RandomStream.cxx b/rdr/RandomStream.cxx
new file mode 100644
index 0000000..7f62e09
--- /dev/null
+++ b/rdr/RandomStream.cxx
@@ -0,0 +1,118 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/RandomStream.h>
+#include <rdr/Exception.h>
+#include <time.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <errno.h>
+#else
+#define getpid() GetCurrentProcessId()
+#endif
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 256;
+
+unsigned int RandomStream::seed;
+
+RandomStream::RandomStream()
+  : offset(0)
+{
+  ptr = end = start = new U8[DEFAULT_BUF_LEN];
+
+#ifdef WIN32
+  provider = 0;
+  if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
+    if (GetLastError() == NTE_BAD_KEYSET) {
+      if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+        fprintf(stderr, "RandomStream: unable to create keyset\n");
+        provider = 0;
+      }
+    } else {
+      fprintf(stderr, "RandomStream: unable to acquire context\n");
+      provider = 0;
+    }
+  }
+  if (!provider) {
+#else
+  fp = fopen("/dev/urandom", "r");
+  if (!fp)
+    fp = fopen("/dev/random", "r");
+  if (!fp) {
+#endif
+    fprintf(stderr,"RandomStream: warning: no OS supplied random source - using rand()\n");
+    seed += (unsigned int) time(0) + getpid() + getpid() * 987654 + rand();
+    srand(seed);
+  }
+}
+
+RandomStream::~RandomStream() {
+  delete [] start;
+
+#ifdef WIN32
+  if (provider) {
+    CryptReleaseContext(provider, 0);
+  }
+#else
+  if (fp) fclose(fp);
+#endif
+}
+
+int RandomStream::pos() {
+  return offset + ptr - start;
+}
+
+int RandomStream::overrun(int itemSize, int nItems, bool wait) {
+  if (itemSize > DEFAULT_BUF_LEN)
+    throw Exception("RandomStream overrun: max itemSize exceeded");
+
+  if (end - ptr != 0)
+    memmove(start, ptr, end - ptr);
+
+  end -= ptr - start;
+  offset += ptr - start;
+  ptr = start;
+
+  int length = start + DEFAULT_BUF_LEN - end;
+
+#ifdef WIN32
+  if (provider) {
+    if (!CryptGenRandom(provider, length, (U8*)end))
+      throw rdr::SystemException("unable to CryptGenRandom", GetLastError());
+    end += length;
+#else
+  if (fp) {
+    int n = fread((U8*)end, length, 1, fp);
+    if (n != 1)
+      throw rdr::SystemException("reading /dev/urandom or /dev/random failed",
+                                 errno);
+    end += length;
+#endif
+  } else {
+    for (int i=0; i<length; i++)
+      *(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
+  }
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
diff --git a/rdr/RandomStream.h b/rdr/RandomStream.h
new file mode 100644
index 0000000..c4aaaa6
--- /dev/null
+++ b/rdr/RandomStream.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_RANDOMSTREAM_H__
+#define __RDR_RANDOMSTREAM_H__
+
+#include <stdio.h>
+#include <rdr/InStream.h>
+
+#ifdef WIN32
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+namespace rdr {
+
+  class RandomStream : public InStream {
+
+  public:
+
+    RandomStream();
+    virtual ~RandomStream();
+
+    int pos();
+
+  protected:
+    int overrun(int itemSize, int nItems, bool wait);
+
+  private:
+    U8* start;
+    int offset;
+
+    static unsigned int seed;
+#ifdef WIN32
+    HCRYPTPROV provider;
+#else
+    FILE* fp;
+#endif
+
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/SubstitutingInStream.h b/rdr/SubstitutingInStream.h
new file mode 100644
index 0000000..3a0559b
--- /dev/null
+++ b/rdr/SubstitutingInStream.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_SUBSTITUTINGINSTREAM_H__
+#define __RDR_SUBSTITUTINGINSTREAM_H__
+
+#include <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+  class Substitutor {
+  public:
+    virtual char* substitute(const char* varName) = 0;
+  };
+
+  class SubstitutingInStream : public InStream {
+  public:
+    SubstitutingInStream(InStream* underlying_, Substitutor* s,
+                         int maxVarNameLen_)
+      : underlying(underlying_), dollar(0), substitutor(s), subst(0),
+        maxVarNameLen(maxVarNameLen_)
+    {
+      ptr = end = underlying->getptr();
+      varName = new char[maxVarNameLen+1];
+    }
+    ~SubstitutingInStream() {
+      delete underlying;
+      delete [] varName;
+      delete [] subst;
+    }
+
+    int pos() { return underlying->pos(); }
+
+    virtual int overrun(int itemSize, int nItems, bool wait=true) {
+      if (itemSize != 1)
+        throw new rdr::Exception("SubstitutingInStream: itemSize must be 1");
+
+      if (subst) {
+        delete [] subst;
+        subst = 0;
+      } else {
+        underlying->setptr(ptr);
+      }
+
+      underlying->check(1);
+      ptr = underlying->getptr();
+      end = underlying->getend();
+      dollar = (const U8*)memchr(ptr, '$', end-ptr);
+      if (dollar) {
+        if (dollar == ptr) {
+          try {
+            int i = 0;
+            while (i < maxVarNameLen) {
+              varName[i++] = underlying->readS8();
+              varName[i] = 0;
+              subst = substitutor->substitute(varName);
+              if (subst) {
+                ptr = (U8*)subst;
+                end = (U8*)subst + strlen(subst);
+                break;
+              }
+            }
+          } catch (EndOfStream&) {
+          }
+
+          if (!subst)
+            dollar = (const U8*)memchr(ptr+1, '$', end-ptr-1);
+        }
+        if (!subst && dollar) end = dollar;
+      }
+
+      if (itemSize * nItems > end - ptr)
+        nItems = (end - ptr) / itemSize;
+
+      return nItems;
+    }
+
+    InStream* underlying;
+    const U8* dollar;
+    Substitutor* substitutor;
+    char* varName;
+    char* subst;
+    int maxVarNameLen;
+  };
+}
+#endif
diff --git a/rdr/ZlibInStream.cxx b/rdr/ZlibInStream.cxx
new file mode 100644
index 0000000..52e4dd3
--- /dev/null
+++ b/rdr/ZlibInStream.cxx
@@ -0,0 +1,125 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/ZlibInStream.h>
+#include <rdr/Exception.h>
+#include <zlib.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384 };
+
+ZlibInStream::ZlibInStream(int bufSize_)
+  : underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
+    bytesIn(0)
+{
+  zs = new z_stream;
+  zs->zalloc    = Z_NULL;
+  zs->zfree     = Z_NULL;
+  zs->opaque    = Z_NULL;
+  zs->next_in   = Z_NULL;
+  zs->avail_in  = 0;
+  if (inflateInit(zs) != Z_OK) {
+    delete zs;
+    throw Exception("ZlibInStream: inflateInit failed");
+  }
+  ptr = end = start = new U8[bufSize];
+}
+
+ZlibInStream::~ZlibInStream()
+{
+  delete [] start;
+  inflateEnd(zs);
+  delete zs;
+}
+
+void ZlibInStream::setUnderlying(InStream* is, int bytesIn_)
+{
+  underlying = is;
+  bytesIn = bytesIn_;
+  ptr = end = start;
+}
+
+int ZlibInStream::pos()
+{
+  return offset + ptr - start;
+}
+
+void ZlibInStream::reset()
+{
+  ptr = end = start;
+  if (!underlying) return;
+
+  while (bytesIn > 0) {
+    decompress(true);
+    end = start; // throw away any data
+  }
+  underlying = 0;
+}
+
+int ZlibInStream::overrun(int itemSize, int nItems, bool wait)
+{
+  if (itemSize > bufSize)
+    throw Exception("ZlibInStream overrun: max itemSize exceeded");
+  if (!underlying)
+    throw Exception("ZlibInStream overrun: no underlying stream");
+
+  if (end - ptr != 0)
+    memmove(start, ptr, end - ptr);
+
+  offset += ptr - start;
+  end -= ptr - start;
+  ptr = start;
+
+  while (end - ptr < itemSize) {
+    if (!decompress(wait))
+      return 0;
+  }
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
+
+// decompress() calls the decompressor once.  Note that this won't necessarily
+// generate any output data - it may just consume some input data.  Returns
+// false if wait is false and we would block on the underlying stream.
+
+bool ZlibInStream::decompress(bool wait)
+{
+  zs->next_out = (U8*)end;
+  zs->avail_out = start + bufSize - end;
+
+  int n = underlying->check(1, 1, wait);
+  if (n == 0) return false;
+  zs->next_in = (U8*)underlying->getptr();
+  zs->avail_in = underlying->getend() - underlying->getptr();
+  if ((int)zs->avail_in > bytesIn)
+    zs->avail_in = bytesIn;
+
+  int rc = inflate(zs, Z_SYNC_FLUSH);
+  if (rc != Z_OK) {
+    throw Exception("ZlibInStream: inflate failed");
+  }
+
+  bytesIn -= zs->next_in - underlying->getptr();
+  end = zs->next_out;
+  underlying->setptr(zs->next_in);
+  return true;
+}
diff --git a/rdr/ZlibInStream.h b/rdr/ZlibInStream.h
new file mode 100644
index 0000000..81eb161
--- /dev/null
+++ b/rdr/ZlibInStream.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// ZlibInStream streams from a compressed data stream ("underlying"),
+// decompressing with zlib on the fly.
+//
+
+#ifndef __RDR_ZLIBINSTREAM_H__
+#define __RDR_ZLIBINSTREAM_H__
+
+#include <rdr/InStream.h>
+
+struct z_stream_s;
+
+namespace rdr {
+
+  class ZlibInStream : public InStream {
+
+  public:
+
+    ZlibInStream(int bufSize=0);
+    virtual ~ZlibInStream();
+
+    void setUnderlying(InStream* is, int bytesIn);
+    void reset();
+    int pos();
+
+  private:
+
+    int overrun(int itemSize, int nItems, bool wait);
+    bool decompress(bool wait);
+
+    InStream* underlying;
+    int bufSize;
+    int offset;
+    z_stream_s* zs;
+    int bytesIn;
+    U8* start;
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/ZlibOutStream.cxx b/rdr/ZlibOutStream.cxx
new file mode 100644
index 0000000..6aadde1
--- /dev/null
+++ b/rdr/ZlibOutStream.cxx
@@ -0,0 +1,140 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rdr/ZlibOutStream.h>
+#include <rdr/Exception.h>
+#include <zlib.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384 };
+
+ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
+  : underlying(os), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+  zs = new z_stream;
+  zs->zalloc    = Z_NULL;
+  zs->zfree     = Z_NULL;
+  zs->opaque    = Z_NULL;
+  if (deflateInit(zs, compressLevel) != Z_OK) {
+    delete zs;
+    throw Exception("ZlibOutStream: deflateInit failed");
+  }
+  ptr = start = new U8[bufSize];
+  end = start + bufSize;
+}
+
+ZlibOutStream::~ZlibOutStream()
+{
+  try {
+    flush();
+  } catch (Exception&) {
+  }
+  delete [] start;
+  deflateEnd(zs);
+  delete zs;
+}
+
+void ZlibOutStream::setUnderlying(OutStream* os)
+{
+  underlying = os;
+}
+
+int ZlibOutStream::length()
+{
+  return offset + ptr - start;
+}
+
+void ZlibOutStream::flush()
+{
+  zs->next_in = start;
+  zs->avail_in = ptr - start;
+
+//    fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
+
+  while (zs->avail_in != 0) {
+
+    do {
+      underlying->check(1);
+      zs->next_out = underlying->getptr();
+      zs->avail_out = underlying->getend() - underlying->getptr();
+
+//        fprintf(stderr,"zos flush: calling deflate, avail_in %d, avail_out %d\n",
+//                zs->avail_in,zs->avail_out);
+      int rc = deflate(zs, Z_SYNC_FLUSH);
+      if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
+
+//        fprintf(stderr,"zos flush: after deflate: %d bytes\n",
+//                zs->next_out-underlying->getptr());
+
+      underlying->setptr(zs->next_out);
+    } while (zs->avail_out == 0);
+  }
+
+  offset += ptr - start;
+  ptr = start;
+}
+
+int ZlibOutStream::overrun(int itemSize, int nItems)
+{
+//    fprintf(stderr,"ZlibOutStream overrun\n");
+
+  if (itemSize > bufSize)
+    throw Exception("ZlibOutStream overrun: max itemSize exceeded");
+
+  while (end - ptr < itemSize) {
+    zs->next_in = start;
+    zs->avail_in = ptr - start;
+
+    do {
+      underlying->check(1);
+      zs->next_out = underlying->getptr();
+      zs->avail_out = underlying->getend() - underlying->getptr();
+
+//        fprintf(stderr,"zos overrun: calling deflate, avail_in %d, avail_out %d\n",
+//                zs->avail_in,zs->avail_out);
+
+      int rc = deflate(zs, 0);
+      if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
+
+//        fprintf(stderr,"zos overrun: after deflate: %d bytes\n",
+//                zs->next_out-underlying->getptr());
+
+      underlying->setptr(zs->next_out);
+    } while (zs->avail_out == 0);
+
+    // output buffer not full
+
+    if (zs->avail_in == 0) {
+      offset += ptr - start;
+      ptr = start;
+    } else {
+      // but didn't consume all the data?  try shifting what's left to the
+      // start of the buffer.
+      fprintf(stderr,"z out buf not full, but in data not consumed\n");
+      memmove(start, zs->next_in, ptr - zs->next_in);
+      offset += zs->next_in - start;
+      ptr -= zs->next_in - start;
+    }
+  }
+
+  if (itemSize * nItems > end - ptr)
+    nItems = (end - ptr) / itemSize;
+
+  return nItems;
+}
diff --git a/rdr/ZlibOutStream.h b/rdr/ZlibOutStream.h
new file mode 100644
index 0000000..e51db73
--- /dev/null
+++ b/rdr/ZlibOutStream.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// ZlibOutStream streams to a compressed data stream (underlying), compressing
+// with zlib on the fly.
+//
+
+#ifndef __RDR_ZLIBOUTSTREAM_H__
+#define __RDR_ZLIBOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+struct z_stream_s;
+
+namespace rdr {
+
+  class ZlibOutStream : public OutStream {
+
+  public:
+
+    ZlibOutStream(OutStream* os=0, int bufSize=0, int compressionLevel=-1);
+    virtual ~ZlibOutStream();
+
+    void setUnderlying(OutStream* os);
+    void flush();
+    int length();
+
+  private:
+
+    int overrun(int itemSize, int nItems);
+
+    OutStream* underlying;
+    int bufSize;
+    int offset;
+    z_stream_s* zs;
+    U8* start;
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/msvcwarning.h b/rdr/msvcwarning.h
new file mode 100644
index 0000000..e93f2bb
--- /dev/null
+++ b/rdr/msvcwarning.h
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/rdr/rdr.dsp b/rdr/rdr.dsp
new file mode 100644
index 0000000..260f8f0
--- /dev/null
+++ b/rdr/rdr.dsp
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Project File - Name="rdr" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rdr - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "rdr.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "rdr.mak" CFG="rdr - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "rdr - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rdr - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rdr - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "rdr - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rdr - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rdr - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rdr___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rdr___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "rdr - Win32 Release"
+# Name "rdr - Win32 Debug"
+# Name "rdr - Win32 Debug Unicode"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FixedMemOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MemInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MemOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NullOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RandomStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SubstitutingInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibOutStream.h
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Exception.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdInStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexInStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexOutStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\InStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\NullOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\RandomStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibInStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rdr/types.h b/rdr/types.h
new file mode 100644
index 0000000..3798c97
--- /dev/null
+++ b/rdr/types.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RDR_TYPES_H__
+#define __RDR_TYPES_H__
+
+namespace rdr {
+
+  typedef unsigned char U8;
+  typedef unsigned short U16;
+  typedef unsigned int U32;
+  typedef signed char S8;
+  typedef signed short S16;
+  typedef signed int S32;
+
+  class U8Array {
+  public:
+    U8Array() : buf(0) {}
+    U8Array(U8* a) : buf(a) {} // note: assumes ownership
+    U8Array(int len) : buf(new U8[len]) {}
+    ~U8Array() { delete [] buf; }
+
+    // Get the buffer pointer & clear it (i.e. caller takes ownership)
+    U8* takeBuf() { U8* tmp = buf; buf = 0; return tmp; }
+
+    U8* buf;
+  };
+
+  class U16Array {
+  public:
+    U16Array() : buf(0) {}
+    U16Array(U16* a) : buf(a) {} // note: assumes ownership
+    U16Array(int len) : buf(new U16[len]) {}
+    ~U16Array() { delete [] buf; }
+    U16* takeBuf() { U16* tmp = buf; buf = 0; return tmp; }
+    U16* buf;
+  };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rfb/Blacklist.cxx b/rfb/Blacklist.cxx
new file mode 100644
index 0000000..4c4f95b
--- /dev/null
+++ b/rfb/Blacklist.cxx
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/Blacklist.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+IntParameter Blacklist::threshold("BlacklistThreshold",
+                              "The number of unauthenticated connection attempts allowed from any "
+                              "individual host before that host is black-listed",
+                              5);
+IntParameter Blacklist::initialTimeout("BlacklistTimeout",
+                              "The initial timeout applied when a host is first black-listed.  "
+                              "The host cannot re-attempt a connection until the timeout expires.",
+                              10);
+
+
+Blacklist::Blacklist() {
+}
+
+Blacklist::~Blacklist() {
+  // Free the map keys
+  BlacklistMap::iterator i;
+  for (i=blm.begin(); i!=blm.end(); i++) {
+    strFree((char*)(*i).first);
+  }
+}
+
+bool Blacklist::isBlackmarked(const char* name) {
+  BlacklistMap::iterator i = blm.find(name);
+  if (i == blm.end()) {
+    // Entry is not already black-marked.
+    // Create the entry unmarked, unblocked,
+    // with suitable defaults set.
+    BlacklistInfo bi;
+    bi.marks = 1;
+    bi.blockUntil = 0;
+    bi.blockTimeout = initialTimeout;
+    blm[strDup(name)] = bi;
+    i = blm.find(name);
+  }
+
+  // Entry exists - has it reached the threshold yet?
+  if ((*i).second.marks >= threshold) {
+    // Yes - entry is blocked - has the timeout expired?        
+    time_t now = time(0);
+    if (now >= (*i).second.blockUntil) {
+      // Timeout has expired.  Reset timeout and allow
+      // a re-try.
+      (*i).second.blockUntil = now + (*i).second.blockTimeout;
+      (*i).second.blockTimeout = (*i).second.blockTimeout * 2;
+      return false;
+    }
+    // Blocked and timeout still in effect - reject!
+    return true;
+  }
+
+  // We haven't reached the threshold yet.
+  // Increment the black-mark counter but allow
+  // the entry to pass.
+  (*i).second.marks++;
+  return false;
+}
+
+void Blacklist::clearBlackmark(const char* name) {
+  BlacklistMap::iterator i = blm.find(name);
+  if (i != blm.end()) {
+    strFree((char*)(*i).first);
+    blm.erase(i);
+  }
+}
diff --git a/rfb/Blacklist.h b/rfb/Blacklist.h
new file mode 100644
index 0000000..4df7ec8
--- /dev/null
+++ b/rfb/Blacklist.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// Blacklist.h - Handling of black-listed entities.
+// Just keeps a table mapping strings to timing information, including
+// how many times the entry has been black-listed and when to next
+// put it on probation (e.g. allow a connection in from the host, and
+// re-blacklist it if that fails). 
+//
+
+#ifndef __RFB_BLACKLIST_H__
+#define __RFB_BLACKLIST_H__
+
+#include <string.h>
+#include <time.h>
+#include <map>
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  //
+  // -=- Blacklist handler
+  //
+  // Parameters include a threshold after which to blacklist the named
+  // host, and a timeout after which to re-consider them.
+  //
+  // Threshold means that isBlackmarked can be called that number of times
+  // before it will return true.
+  //
+  // Timeout means that after that many seconds, the next call to isBlackmarked
+  // will return false.  At the same time, the timeout is doubled, so that the
+  // next calls will fail, until the timeout expires again or clearBlackmark is
+  // called.
+  //
+  // When clearBlackMark is called, the corresponding entry is completely
+  // removed, causing the next isBlackmarked call to return false.
+
+  // KNOWN BUG:  Client can keep making rejected requests, thus increasing
+  // their timeout.  If client does this for 30 years, timeout may wrap round
+  // to a very small value again.
+
+  // THIS CLASS IS NOT THREAD-SAFE!
+
+  class Blacklist {
+  public:
+    Blacklist();
+    ~Blacklist();
+
+    bool isBlackmarked(const char* name);
+    void clearBlackmark(const char* name);
+
+    static IntParameter threshold;
+    static IntParameter initialTimeout;
+
+  protected:
+    struct ltStr {
+      bool operator()(const char* s1, const char* s2) const {
+        return strcmp(s1, s2) < 0;
+      };
+    };
+    struct BlacklistInfo {
+      int marks;
+      time_t blockUntil;
+      unsigned int blockTimeout;
+    };
+    typedef std::map<const char*,BlacklistInfo,ltStr> BlacklistMap;
+    BlacklistMap blm;
+  };
+
+}
+
+#endif
+
diff --git a/rfb/CConnection.cxx b/rfb/CConnection.cxx
new file mode 100644
index 0000000..c6a3eed
--- /dev/null
+++ b/rfb/CConnection.cxx
@@ -0,0 +1,291 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgWriterV3.h>
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/CConnection.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("CConnection");
+
+CConnection::CConnection()
+  : is(0), os(0), reader_(0), writer_(0),
+    shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false),
+    state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
+{
+}
+
+CConnection::~CConnection()
+{
+  if (security) security->destroy();
+  deleteReaderAndWriter();
+}
+
+void CConnection::setServerName(const char* serverName_) {
+  serverName.buf = strDup(serverName_);
+}
+
+void CConnection::deleteReaderAndWriter()
+{
+  delete reader_;
+  reader_ = 0;
+  delete writer_;
+  writer_ = 0;
+}
+
+void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+  is = is_;
+  os = os_;
+}
+
+void CConnection::addSecType(rdr::U8 secType)
+{
+  if (nSecTypes == maxSecTypes)
+    throw Exception("too many security types");
+  secTypes[nSecTypes++] = secType;
+}
+
+void CConnection::setClientSecTypeOrder(bool clientOrder) {
+  clientSecTypeOrder = clientOrder;
+}
+
+void CConnection::initialiseProtocol()
+{
+  state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void CConnection::processMsg()
+{
+  switch (state_) {
+
+  case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();       break;
+  case RFBSTATE_SECURITY_TYPES:   processSecurityTypesMsg(); break;
+  case RFBSTATE_SECURITY:         processSecurityMsg();      break;
+  case RFBSTATE_SECURITY_RESULT:  processSecurityResultMsg(); break;
+  case RFBSTATE_INITIALISATION:   processInitMsg();          break;
+  case RFBSTATE_NORMAL:           reader_->readMsg();        break;
+  case RFBSTATE_UNINITIALISED:
+    throw Exception("CConnection::processMsg: not initialised yet?");
+  default:
+    throw Exception("CConnection::processMsg: invalid state");
+  }
+}
+
+void CConnection::processVersionMsg()
+{
+  vlog.debug("reading protocol version");
+  bool done;
+  if (!cp.readVersion(is, &done)) {
+    state_ = RFBSTATE_INVALID;
+    throw Exception("reading version failed: not an RFB server?");
+  }
+  if (!done) return;
+
+  vlog.info("Server supports RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+
+  // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
+  if (cp.beforeVersion(3,3)) {
+    char msg[256];
+    sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+    vlog.error(msg);
+    state_ = RFBSTATE_INVALID;
+    throw Exception(msg);
+  } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
+    cp.setVersion(3,3);
+  } else if (cp.afterVersion(3,8)) {
+    cp.setVersion(3,8);
+  }
+
+  cp.writeVersion(os);
+  state_ = RFBSTATE_SECURITY_TYPES;
+
+  vlog.info("Using RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+}
+
+
+void CConnection::processSecurityTypesMsg()
+{
+  vlog.debug("processing security types message");
+
+  int secType = secTypeInvalid;
+
+  if (cp.isVersion(3,3)) {
+
+    // legacy 3.3 server may only offer "vnc authentication" or "none"
+
+    secType = is->readU32();
+    if (secType == secTypeInvalid) {
+      throwConnFailedException();
+
+    } else if (secType == secTypeNone || secType == secTypeVncAuth) {
+      int j;
+      for (j = 0; j < nSecTypes; j++)
+        if (secTypes[j] == secType) break;
+      if (j == nSecTypes)
+        secType = secTypeInvalid;
+    } else {
+      vlog.error("Unknown 3.3 security type %d", secType);
+      throw Exception("Unknown 3.3 security type");
+    }
+
+  } else {
+
+    // >=3.7 server will offer us a list
+
+    int nServerSecTypes = is->readU8();
+    if (nServerSecTypes == 0)
+      throwConnFailedException();
+
+    int secTypePos = nSecTypes;
+    for (int i = 0; i < nServerSecTypes; i++) {
+      rdr::U8 serverSecType = is->readU8();
+      vlog.debug("Server offers security type %s(%d)",
+                 secTypeName(serverSecType),serverSecType);
+
+      // If we haven't already chosen a secType, try this one
+      // If we are using the client's preference for types,
+      // we keep trying types, to find the one that matches and
+      // which appears first in the client's list of supported types.
+      if (secType == secTypeInvalid || clientSecTypeOrder) {
+        for (int j = 0; j < nSecTypes; j++) {
+          if (secTypes[j] == serverSecType && j < secTypePos) {
+            secType = secTypes[j];
+            secTypePos = j;
+            break;
+          }
+        }
+        // NB: Continue reading the remaining server secTypes, but ignore them
+      }
+    }
+
+    // Inform the server of our decision
+    if (secType != secTypeInvalid) {
+      os->writeU8(secType);
+      os->flush();
+      vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
+    }
+  }
+
+  if (secType == secTypeInvalid) {
+    state_ = RFBSTATE_INVALID;
+    vlog.error("No matching security types");
+    throw Exception("No matching security types");
+  }
+
+  state_ = RFBSTATE_SECURITY;
+  security = getCSecurity(secType);
+  processSecurityMsg();
+}
+
+void CConnection::processSecurityMsg()
+{
+  vlog.debug("processing security message");
+  bool done;
+  if (!security->processMsg(this, &done))
+    throwAuthFailureException();
+  if (done) {
+    state_ = RFBSTATE_SECURITY_RESULT;
+    processSecurityResultMsg();
+  }
+}
+
+void CConnection::processSecurityResultMsg()
+{
+  vlog.debug("processing security result message");
+  int result;
+  if (cp.beforeVersion(3,8) && security->getType() == secTypeNone) {
+    result = secResultOK;
+  } else {
+    if (!is->checkNoWait(1)) return;
+    result = is->readU32();
+  }
+  switch (result) {
+  case secResultOK:
+    securityCompleted();
+    break;
+  case secResultFailed:
+    vlog.debug("auth failed");
+    throwAuthFailureException();
+  case secResultTooMany:
+    vlog.debug("auth failed - too many tries");
+    throwAuthFailureException();
+  default:
+    vlog.error("unknown security result");
+    throwAuthFailureException();
+  };
+}
+
+void CConnection::processInitMsg()
+{
+  vlog.debug("reading server initialisation");
+  reader_->readServerInit();
+}
+
+void CConnection::throwAuthFailureException()
+{
+  CharArray reason;
+  vlog.debug("state=%d, ver=%d.%d", state(), cp.majorVersion, cp.minorVersion);
+  if (state()==RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
+    reason.buf = is->readString();
+  } else {
+    reason.buf = strDup("Authentication failure");
+  }
+  state_ = RFBSTATE_INVALID;
+  vlog.error(reason.buf);
+  throw AuthFailureException(reason.buf);
+}
+
+void CConnection::throwConnFailedException()
+{
+  state_ = RFBSTATE_INVALID;
+  CharArray reason;
+  reason.buf = is->readString();
+  throw ConnFailedException(reason.buf);
+}
+
+void CConnection::securityCompleted()
+{
+  state_ = RFBSTATE_INITIALISATION;
+  reader_ = new CMsgReaderV3(this, is);
+  writer_ = new CMsgWriterV3(&cp, os);
+  vlog.debug("Authentication success!");
+  authSuccess();
+  writer_->writeClientInit(shared);
+}
+
+void CConnection::authSuccess()
+{
+}
+
+void CConnection::serverInit()
+{
+  state_ = RFBSTATE_NORMAL;
+  vlog.debug("initialisation done");
+}
diff --git a/rfb/CConnection.h b/rfb/CConnection.h
new file mode 100644
index 0000000..480fca3
--- /dev/null
+++ b/rfb/CConnection.h
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// CConnection - class on the client side representing a connection to a
+// server.  A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_CCONNECTION_H__
+#define __RFB_CCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class CMsgReader;
+  class CMsgWriter;
+  class CSecurity;
+  class IdentityVerifier;
+
+  class CConnection : public CMsgHandler {
+  public:
+
+    CConnection();
+    virtual ~CConnection();
+
+    // ***
+    void setServerName(const char* serverName_);
+
+    // Methods to initialise the connection
+
+    // setStreams() sets the streams to be used for the connection.  These must
+    // be set before initialiseProtocol() and processMsg() are called.  The
+    // CSecurity object may call setStreams() again to provide alternative
+    // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+    // streams).  Ownership of the streams remains with the caller
+    // (i.e. SConnection will not delete them).
+    void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+    // addSecType() should be called once for each security type which the
+    // client supports.  The order in which they're added is such that the
+    // first one is most preferred.
+    void addSecType(rdr::U8 secType);
+
+    // setClientSecTypeOrder() determines whether the client should obey
+    // the server's security type preference, by picking the first server security
+    // type that the client supports, or whether it should pick the first type
+    // that the server supports, from the client-supported list of types.
+    void setClientSecTypeOrder(bool clientOrder);
+
+    // setShared sets the value of the shared flag which will be sent to the
+    // server upon initialisation.
+    void setShared(bool s) { shared = s; }
+
+    // setProtocol3_3 configures whether or not the CConnection should
+    // only ever support protocol version 3.3
+    void setProtocol3_3(bool s) {useProtocol3_3 = s;}
+
+    // initialiseProtocol() should be called once the streams and security
+    // types are set.  Subsequently, processMsg() should be called whenever
+    // there is data to read on the InStream.
+    void initialiseProtocol();
+
+    // processMsg() should be called whenever there is either:
+    // - data available on the underlying network stream
+    //   In this case, processMsg may return without processing an RFB message,
+    //   if the available data does not result in an RFB message being ready
+    //   to handle. e.g. if data is encrypted.
+    // NB: This makes it safe to call processMsg() in response to select()
+    // - data available on the CConnection's current InStream
+    //   In this case, processMsg should always process the available RFB
+    //   message before returning.
+    // NB: In either case, you must have called initialiseProtocol() first.
+    void processMsg();
+
+
+    // Methods to be overridden in a derived class
+
+    // getCSecurity() gets the CSecurity object for the given type.  The type
+    // is guaranteed to be one of the secTypes passed in to addSecType().  The
+    // CSecurity object's destroy() method will be called by the CConnection
+    // from its destructor.
+    virtual CSecurity* getCSecurity(int secType)=0;
+
+    // getCurrentCSecurity() gets the CSecurity instance used for this connection.
+    const CSecurity* getCurrentCSecurity() const {return security;} 
+
+    // getIdVerifier() returns the identity verifier associated with the connection.
+    // Ownership of the IdentityVerifier is retained by the CConnection instance.
+    virtual IdentityVerifier* getIdentityVerifier() {return 0;}
+
+    // authSuccess() is called when authentication has succeeded.
+    virtual void authSuccess();
+
+    // serverInit() is called when the ServerInit message is received.  The
+    // derived class must call on to CConnection::serverInit().
+    virtual void serverInit();
+
+
+    // Other methods
+
+    // deleteReaderAndWriter() deletes the reader and writer associated with
+    // this connection.  This may be useful if you want to delete the streams
+    // before deleting the SConnection to make sure that no attempt by the
+    // SConnection is made to read or write.
+    // XXX Do we really need this at all???
+    void deleteReaderAndWriter();
+
+    CMsgReader* reader() { return reader_; }
+    CMsgWriter* writer() { return writer_; }
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::OutStream* getOutStream() { return os; }
+
+    char* getServerName() {return strDup(serverName.buf);}
+
+    enum stateEnum {
+      RFBSTATE_UNINITIALISED,
+      RFBSTATE_PROTOCOL_VERSION,
+      RFBSTATE_SECURITY_TYPES,
+      RFBSTATE_SECURITY,
+      RFBSTATE_SECURITY_RESULT,
+      RFBSTATE_INITIALISATION,
+      RFBSTATE_NORMAL,
+      RFBSTATE_INVALID
+    };
+
+    stateEnum state() { return state_; }
+
+  protected:
+    void setState(stateEnum s) { state_ = s; }
+
+  private:
+    void processVersionMsg();
+    void processSecurityTypesMsg();
+    void processSecurityMsg();
+    void processSecurityResultMsg();
+    void processInitMsg();
+    void throwAuthFailureException();
+    void throwConnFailedException();
+    void securityCompleted();
+
+    rdr::InStream* is;
+    rdr::OutStream* os;
+    CMsgReader* reader_;
+    CMsgWriter* writer_;
+    bool deleteStreamsWhenDone;
+    bool shared;
+    CSecurity* security;
+    enum { maxSecTypes = 8 };
+    int nSecTypes;
+    rdr::U8 secTypes[maxSecTypes];
+    bool clientSecTypeOrder;
+    stateEnum state_;
+
+    CharArray serverName;
+
+    bool useProtocol3_3;
+  };
+}
+#endif
diff --git a/rfb/CMsgHandler.cxx b/rfb/CMsgHandler.cxx
new file mode 100644
index 0000000..1010916
--- /dev/null
+++ b/rfb/CMsgHandler.cxx
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/Exception.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgHandler::CMsgHandler()
+{
+}
+
+CMsgHandler::~CMsgHandler()
+{
+}
+
+void CMsgHandler::setDesktopSize(int width, int height)
+{
+  cp.width = width;
+  cp.height = height;
+}
+
+void CMsgHandler::setCursor(const Point& hotspot, const Point& size, void* data, void* mask)
+{
+}
+
+void CMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+  cp.setPF(pf);
+}
+
+void CMsgHandler::setName(const char* name)
+{
+  cp.setName(name);
+}
+
+void CMsgHandler::serverInit()
+{
+  throw Exception("CMsgHandler::serverInit called");
+}
+
+void CMsgHandler::framebufferUpdateStart()
+{
+}
+
+void CMsgHandler::framebufferUpdateEnd()
+{
+}
+
+void CMsgHandler::beginRect(const Rect& r, unsigned int encoding)
+{
+}
+
+void CMsgHandler::endRect(const Rect& r, unsigned int encoding)
+{
+}
+
+
+void CMsgHandler::setColourMapEntries(int firstColour, int nColours,
+                                      rdr::U16* rgbs)
+{
+  throw Exception("CMsgHandler::setColourMapEntries called");
+}
+
+void CMsgHandler::bell()
+{
+}
+
+void CMsgHandler::serverCutText(const char* str, int len)
+{
+}
+
+void CMsgHandler::fillRect(const Rect& r, Pixel pix)
+{
+}
+
+void CMsgHandler::imageRect(const Rect& r, void* pixels)
+{
+}
+
+void CMsgHandler::copyRect(const Rect& r, int srcX, int srcY)
+{
+}
+
+
diff --git a/rfb/CMsgHandler.h b/rfb/CMsgHandler.h
new file mode 100644
index 0000000..7a69f14
--- /dev/null
+++ b/rfb/CMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// CMsgHandler - class to handle incoming messages on the client side.
+//
+
+#ifndef __RFB_CMSGHANDLER_H__
+#define __RFB_CMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/Pixel.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class CMsgHandler {
+  public:
+    CMsgHandler();
+    virtual ~CMsgHandler();
+
+    virtual void setDesktopSize(int w, int h);
+    virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void setName(const char* name);
+    virtual void serverInit();
+
+    virtual void framebufferUpdateStart();
+    virtual void framebufferUpdateEnd();
+    virtual void beginRect(const Rect& r, unsigned int encoding);
+    virtual void endRect(const Rect& r, unsigned int encoding);
+
+    virtual void setColourMapEntries(int firstColour, int nColours,
+				     rdr::U16* rgbs);
+    virtual void bell();
+    virtual void serverCutText(const char* str, int len);
+
+    virtual void fillRect(const Rect& r, Pixel pix);
+    virtual void imageRect(const Rect& r, void* pixels);
+    virtual void copyRect(const Rect& r, int srcX, int srcY);
+
+    ConnParams cp;
+  };
+}
+#endif
diff --git a/rfb/CMsgReader.cxx b/rfb/CMsgReader.cxx
new file mode 100644
index 0000000..46973eb
--- /dev/null
+++ b/rfb/CMsgReader.cxx
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/CMsgReader.h>
+
+using namespace rfb;
+
+CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
+  : imageBufIdealSize(0), handler(handler_), is(is_),
+    imageBuf(0), imageBufSize(0)
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    decoders[i] = 0;
+  }
+}
+
+CMsgReader::~CMsgReader()
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    delete decoders[i];
+  }
+  delete [] imageBuf;
+}
+
+void CMsgReader::endMsg()
+{
+}
+
+void CMsgReader::readSetColourMapEntries()
+{
+  is->skip(1);
+  int firstColour = is->readU16();
+  int nColours = is->readU16();
+  rdr::U16Array rgbs(nColours * 3);
+  for (int i = 0; i < nColours * 3; i++)
+    rgbs.buf[i] = is->readU16();
+  endMsg();
+  handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
+}
+
+void CMsgReader::readBell()
+{
+  endMsg();
+  handler->bell();
+}
+
+void CMsgReader::readServerCutText()
+{
+  is->skip(3);
+  int len = is->readU32();
+  if (len > 256*1024) {
+    is->skip(len);
+    fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+    return;
+  }
+  CharArray ca(len+1);
+  ca.buf[len] = 0;
+  is->readBytes(ca.buf, len);
+  endMsg();
+  handler->serverCutText(ca.buf, len);
+}
+
+void CMsgReader::readFramebufferUpdateStart()
+{
+  endMsg();
+  handler->framebufferUpdateStart();
+}
+
+void CMsgReader::readFramebufferUpdateEnd()
+{
+  endMsg();
+  handler->framebufferUpdateEnd();
+}
+
+void CMsgReader::readRect(const Rect& r, unsigned int encoding)
+{
+  if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) {
+    fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
+	    r.width(), r.height(), r.tl.x, r.tl.y,
+            handler->cp.width, handler->cp.height);
+    throw Exception("Rect too big");
+  }
+
+  if (r.is_empty())
+    fprintf(stderr, "Warning: zero size rect\n");
+
+  handler->beginRect(r, encoding);
+
+  if (encoding == encodingCopyRect) {
+    readCopyRect(r);
+  } else {
+    if (!decoders[encoding]) {
+      decoders[encoding] = Decoder::createDecoder(encoding, this);
+      if (!decoders[encoding]) {
+        fprintf(stderr, "Unknown rect encoding %d\n", encoding);
+        throw Exception("Unknown rect encoding");
+      }
+    }
+    decoders[encoding]->readRect(r, handler);
+  }
+
+  handler->endRect(r, encoding);
+}
+
+void CMsgReader::readCopyRect(const Rect& r)
+{
+  int srcX = is->readU16();
+  int srcY = is->readU16();
+  handler->copyRect(r, srcX, srcY);
+}
+
+void CMsgReader::readSetCursor(const Point& hotspot, const Point& size)
+{
+  int data_len = size.x * size.y * (handler->cp.pf().bpp/8);
+  int mask_len = ((size.x+7)/8) * size.y;
+  rdr::U8Array data(data_len);
+  rdr::U8Array mask(mask_len);
+
+  is->readBytes(data.buf, data_len);
+  is->readBytes(mask.buf, mask_len);
+
+  handler->setCursor(hotspot, size, data.buf, mask.buf);
+}
+
+rdr::U8* CMsgReader::getImageBuf(int required, int requested, int* nPixels)
+{
+  int requiredBytes = required * (handler->cp.pf().bpp / 8);
+  int requestedBytes = requested * (handler->cp.pf().bpp / 8);
+  int size = requestedBytes;
+  if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+  if (size < requiredBytes)
+    size = requiredBytes;
+
+  if (imageBufSize < size) {
+    imageBufSize = size;
+    delete [] imageBuf;
+    imageBuf = new rdr::U8[imageBufSize];
+  }
+  if (nPixels)
+    *nPixels = imageBufSize / (handler->cp.pf().bpp / 8);
+  return imageBuf;
+}
+
+int CMsgReader::bpp()
+{
+  return handler->cp.pf().bpp;
+}
diff --git a/rfb/CMsgReader.h b/rfb/CMsgReader.h
new file mode 100644
index 0000000..8b4638c
--- /dev/null
+++ b/rfb/CMsgReader.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// CMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_CMSGREADER_H__
+#define __RFB_CMSGREADER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Decoder.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+  class CMsgHandler;
+  struct Rect;
+
+  class CMsgReader {
+  public:
+    virtual ~CMsgReader();
+
+    virtual void readServerInit()=0;
+
+    // readMsg() reads a message, calling the handler as appropriate.
+    virtual void readMsg()=0;
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+    int bpp();
+
+    int imageBufIdealSize;
+
+  protected:
+    virtual void readSetColourMapEntries();
+    virtual void readBell();
+    virtual void readServerCutText();
+
+    virtual void endMsg();
+
+    virtual void readFramebufferUpdateStart();
+    virtual void readFramebufferUpdateEnd();
+    virtual void readRect(const Rect& r, unsigned int encoding);
+
+    virtual void readCopyRect(const Rect& r);
+
+    virtual void readSetCursor(const Point& hotspot, const Point& size);
+
+    CMsgReader(CMsgHandler* handler, rdr::InStream* is);
+
+    CMsgHandler* handler;
+    rdr::InStream* is;
+    Decoder* decoders[encodingMax+1];
+    rdr::U8* imageBuf;
+    int imageBufSize;
+  };
+}
+#endif
diff --git a/rfb/CMsgReaderV3.cxx b/rfb/CMsgReaderV3.cxx
new file mode 100644
index 0000000..1f974fd
--- /dev/null
+++ b/rfb/CMsgReaderV3.cxx
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgReaderV3::CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is)
+  : CMsgReader(handler, is), nUpdateRectsLeft(0)
+{
+}
+
+CMsgReaderV3::~CMsgReaderV3()
+{
+}
+
+void CMsgReaderV3::readServerInit()
+{
+  int width = is->readU16();
+  int height = is->readU16();
+  handler->setDesktopSize(width, height);
+  PixelFormat pf;
+  pf.read(is);
+  handler->setPixelFormat(pf);
+  char* name = is->readString();
+  handler->setName(name);
+  delete [] name;
+  endMsg();
+  handler->serverInit();
+}
+
+void CMsgReaderV3::readMsg()
+{
+  if (nUpdateRectsLeft == 0) {
+
+    int type = is->readU8();
+    switch (type) {
+    case msgTypeFramebufferUpdate:   readFramebufferUpdate(); break;
+    case msgTypeSetColourMapEntries: readSetColourMapEntries(); break;
+    case msgTypeBell:                readBell(); break;
+    case msgTypeServerCutText:       readServerCutText(); break;
+    default:
+      fprintf(stderr, "unknown message type %d\n", type);
+      throw Exception("unknown message type");
+    }
+
+  } else {
+
+    int x = is->readU16();
+    int y = is->readU16();
+    int w = is->readU16();
+    int h = is->readU16();
+    unsigned int encoding = is->readU32();
+
+    switch (encoding) {
+    case pseudoEncodingDesktopSize:
+      handler->setDesktopSize(w, h);
+      break;
+    case pseudoEncodingCursor:
+      readSetCursor(Point(x, y), Point(w, h));
+      break;
+    default:
+      readRect(Rect(x, y, x+w, y+h), encoding);
+      break;
+    };
+
+    nUpdateRectsLeft--;
+    if (nUpdateRectsLeft == 0) handler->framebufferUpdateEnd();
+  }
+}
+
+void CMsgReaderV3::readFramebufferUpdate()
+{
+  is->skip(1);
+  nUpdateRectsLeft = is->readU16();
+  endMsg();
+  handler->framebufferUpdateStart();
+}
diff --git a/rfb/CMsgReaderV3.h b/rfb/CMsgReaderV3.h
new file mode 100644
index 0000000..93c8c6a
--- /dev/null
+++ b/rfb/CMsgReaderV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_CMSGREADERV3_H__
+#define __RFB_CMSGREADERV3_H__
+
+#include <rfb/CMsgReader.h>
+
+namespace rfb {
+  class CMsgReaderV3 : public CMsgReader {
+  public:
+    CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is);
+    virtual ~CMsgReaderV3();
+    virtual void readServerInit();
+    virtual void readMsg();
+  private:
+    void readFramebufferUpdate();
+    int nUpdateRectsLeft;
+  };
+}
+#endif
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx
new file mode 100644
index 0000000..a7e4fb9
--- /dev/null
+++ b/rfb/CMsgWriter.cxx
@@ -0,0 +1,130 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Decoder.h>
+#include <rfb/CMsgWriter.h>
+
+using namespace rfb;
+
+CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+  : cp(cp_), os(os_)
+{
+}
+
+CMsgWriter::~CMsgWriter()
+{
+}
+
+void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
+{
+  startMsg(msgTypeSetPixelFormat);                                 
+  os->pad(3);
+  pf.write(os);
+  endMsg();
+}
+
+void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
+{
+  startMsg(msgTypeSetEncodings);
+  os->skip(1);
+  os->writeU16(nEncodings);
+  for (int i = 0; i < nEncodings; i++)
+    os->writeU32(encodings[i]);
+  endMsg();
+}
+
+// Ask for encodings based on which decoders are supported.  Assumes higher
+// encoding numbers are more desirable.
+
+void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
+{
+  int nEncodings = 0;
+  rdr::U32 encodings[encodingMax+2];
+  if (cp->supportsLocalCursor)
+    encodings[nEncodings++] = pseudoEncodingCursor;
+  if (cp->supportsDesktopResize)
+    encodings[nEncodings++] = pseudoEncodingDesktopSize;
+  if (Decoder::supported(preferredEncoding)) {
+    encodings[nEncodings++] = preferredEncoding;
+  }
+  if (useCopyRect) {
+    encodings[nEncodings++] = encodingCopyRect;
+  }
+  for (int i = encodingMax; i >= 0; i--) {
+    if (i != preferredEncoding && Decoder::supported(i)) {
+      encodings[nEncodings++] = i;
+    }
+  }
+  writeSetEncodings(nEncodings, encodings);
+}
+  
+void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
+{
+  startMsg(msgTypeFramebufferUpdateRequest);
+  os->writeU8(incremental);
+  os->writeU16(r.tl.x);
+  os->writeU16(r.tl.y);
+  os->writeU16(r.width());
+  os->writeU16(r.height());
+  endMsg();
+}
+
+
+void CMsgWriter::writeKeyEvent(rdr::U32 key, bool down)
+{
+  startMsg(msgTypeKeyEvent);
+  os->writeU8(down);
+  os->pad(2);
+  os->writeU32(key);
+  endMsg();
+}
+
+
+void CMsgWriter::writePointerEvent(int x, int y, int buttonMask)
+{
+  if (x < 0) x = 0;
+  if (y < 0) y = 0;
+  if (x >= cp->width) x = cp->width - 1;
+  if (y >= cp->height) y = cp->height - 1;
+
+  startMsg(msgTypePointerEvent);
+  os->writeU8(buttonMask);
+  os->writeU16(x);
+  os->writeU16(y);
+  endMsg();
+}
+
+
+void CMsgWriter::writeClientCutText(const char* str, int len)
+{
+  startMsg(msgTypeClientCutText);
+  os->pad(3);
+  os->writeU32(len);
+  os->writeBytes(str, len);
+  endMsg();
+}
+
+void CMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+  os = os_;
+}
diff --git a/rfb/CMsgWriter.h b/rfb/CMsgWriter.h
new file mode 100644
index 0000000..8d6e373
--- /dev/null
+++ b/rfb/CMsgWriter.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// CMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_CMSGWRITER_H__
+#define __RFB_CMSGWRITER_H__
+
+#include <rdr/types.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat;
+  class ConnParams;
+  struct Rect;
+
+  class CMsgWriter {
+  public:
+    virtual ~CMsgWriter();
+
+    virtual void writeClientInit(bool shared)=0;
+
+    virtual void writeSetPixelFormat(const PixelFormat& pf);
+    virtual void writeSetEncodings(int nEncodings, rdr::U32* encodings);
+    virtual void writeSetEncodings(int preferredEncoding, bool useCopyRect);
+    virtual void writeFramebufferUpdateRequest(const Rect& r,bool incremental);
+    virtual void writeKeyEvent(rdr::U32 key, bool down);
+    virtual void writePointerEvent(int x, int y, int buttonMask);
+    virtual void writeClientCutText(const char* str, int len);
+
+    virtual void startMsg(int type)=0;
+    virtual void endMsg()=0;
+
+    virtual void setOutStream(rdr::OutStream* os);
+
+    ConnParams* getConnParams() { return cp; }
+    rdr::OutStream* getOutStream() { return os; }
+
+  protected:
+    CMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+    ConnParams* cp;
+    rdr::OutStream* os;
+  };
+}
+#endif
diff --git a/rfb/CMsgWriterV3.cxx b/rfb/CMsgWriterV3.cxx
new file mode 100644
index 0000000..a6ad237
--- /dev/null
+++ b/rfb/CMsgWriterV3.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/CMsgWriterV3.h>
+
+using namespace rfb;
+
+CMsgWriterV3::CMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+  : CMsgWriter(cp, os)
+{
+}
+
+CMsgWriterV3::~CMsgWriterV3()
+{
+}
+
+void CMsgWriterV3::writeClientInit(bool shared)
+{
+  os->writeU8(shared);
+  endMsg();
+}
+
+void CMsgWriterV3::startMsg(int type)
+{
+  os->writeU8(type);
+}
+
+void CMsgWriterV3::endMsg()
+{
+  os->flush();
+}
diff --git a/rfb/CMsgWriterV3.h b/rfb/CMsgWriterV3.h
new file mode 100644
index 0000000..0cf6157
--- /dev/null
+++ b/rfb/CMsgWriterV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_CMSGWRITERV3_H__
+#define __RFB_CMSGWRITERV3_H__
+
+#include <rfb/CMsgWriter.h>
+
+namespace rfb {
+  class CMsgWriterV3 : public CMsgWriter {
+  public:
+    CMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+    virtual ~CMsgWriterV3();
+
+    virtual void writeClientInit(bool shared);
+    virtual void startMsg(int type);
+    virtual void endMsg();
+
+  };
+}
+#endif
diff --git a/rfb/CSecurity.h b/rfb/CSecurity.h
new file mode 100644
index 0000000..639d550
--- /dev/null
+++ b/rfb/CSecurity.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// CSecurity - class on the client side for handling security handshaking.  A
+// derived class for a particular security type overrides the processMsg()
+// method.  processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the server until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the CConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// description is a string describing the level of encryption applied to the
+// session, or null if no encryption will be used.
+
+#ifndef __RFB_CSECURITY_H__
+#define __RFB_CSECURITY_H__
+
+namespace rfb {
+  class CConnection;
+  class CSecurity {
+  public:
+    virtual ~CSecurity() {}
+    virtual bool processMsg(CConnection* cc, bool* done)=0;
+    virtual void destroy() { delete this; }
+    virtual int getType() const = 0;
+    virtual const char* description() const = 0;
+  };
+}
+#endif
diff --git a/rfb/CSecurityNone.h b/rfb/CSecurityNone.h
new file mode 100644
index 0000000..23b36ce
--- /dev/null
+++ b/rfb/CSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// CSecurityNone.h
+//
+
+#ifndef __CSECURITYNONE_H__
+#define __CSECURITYNONE_H__
+
+#include <rfb/CSecurity.h>
+
+namespace rfb {
+
+  class CSecurityNone : public CSecurity {
+  public:
+    virtual bool processMsg(CConnection* cc, bool* done) {
+      *done = true; return true;
+    }
+    virtual int getType() const {return secTypeNone;}
+    virtual const char* description() const {return "No Encryption";}
+  };
+}
+#endif
diff --git a/rfb/CSecurityVncAuth.cxx b/rfb/CSecurityVncAuth.cxx
new file mode 100644
index 0000000..3d6c87c
--- /dev/null
+++ b/rfb/CSecurityVncAuth.cxx
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// CSecurityVncAuth
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <rfb/CConnection.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rfb/vncAuth.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+CSecurityVncAuth::CSecurityVncAuth(UserPasswdGetter* upg_)
+  : upg(upg_)
+{
+}
+
+CSecurityVncAuth::~CSecurityVncAuth()
+{
+}
+
+bool CSecurityVncAuth::processMsg(CConnection* cc, bool* done)
+{
+  *done = false;
+  rdr::InStream* is = cc->getInStream();
+  rdr::OutStream* os = cc->getOutStream();
+
+  rdr::U8 challenge[vncAuthChallengeSize];
+  is->readBytes(challenge, vncAuthChallengeSize);
+  CharArray passwd;
+  if (!upg->getUserPasswd(0, &passwd.buf)) {
+    vlog.error("Getting password failed");
+    return false;
+  }
+  vncAuthEncryptChallenge(challenge, passwd.buf);
+  memset(passwd.buf, 0, strlen(passwd.buf));
+  os->writeBytes(challenge, vncAuthChallengeSize);
+  os->flush();
+  *done = true;
+  return true;
+}
diff --git a/rfb/CSecurityVncAuth.h b/rfb/CSecurityVncAuth.h
new file mode 100644
index 0000000..bfa40b3
--- /dev/null
+++ b/rfb/CSecurityVncAuth.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_CSECURITYVNCAUTH_H__
+#define __RFB_CSECURITYVNCAUTH_H__
+
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+  class UserPasswdGetter;
+
+  class CSecurityVncAuth : public CSecurity {
+  public:
+    CSecurityVncAuth(UserPasswdGetter* pg);
+    virtual ~CSecurityVncAuth();
+    virtual bool processMsg(CConnection* cc, bool* done);
+    virtual int getType() const {return secTypeVncAuth;};
+    virtual const char* description() const {return "No Encryption";}
+  private:
+    UserPasswdGetter* upg;
+  };
+}
+#endif
diff --git a/rfb/ColourCube.h b/rfb/ColourCube.h
new file mode 100644
index 0000000..904256c
--- /dev/null
+++ b/rfb/ColourCube.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// ColourCube - structure to represent a colour cube.  The colour cube consists
+// of its dimensions (nRed x nGreen x nBlue) and a table mapping an (r,g,b)
+// triple to a pixel value.
+//
+// A colour cube is used in two cases.  The first is internally in a viewer
+// when it cannot use a trueColour format, nor can it have exclusive access to
+// a writable colour map.  This is most notably the case for an X viewer
+// wishing to use a PseudoColor X server's default colormap.
+//
+// The second use is on the server side when a client has asked for a colour
+// map and the server is trueColour.  Instead of setting an uneven trueColour
+// format like bgr233, it can set the client's colour map up with a 6x6x6
+// colour cube.  For this use the colour cube table has a null mapping, which
+// makes it easy to perform the reverse lookup operation from pixel value to
+// r,g,b values.
+
+#ifndef __RFB_COLOURCUBE_H__
+#define __RFB_COLOURCUBE_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+  class ColourCube : public ColourMap {
+  public:
+    ColourCube(int nr, int ng, int nb, Pixel* table_=0)
+      : nRed(nr), nGreen(ng), nBlue(nb), table(table_), deleteTable(false)
+    {
+      if (!table) {
+        table = new Pixel[size()];
+        deleteTable = true;
+        // set a null mapping by default
+        for (int i = 0; i < size(); i++)
+          table[i] = i;
+      }
+    }
+
+    ColourCube() : deleteTable(false) {}
+
+    virtual ~ColourCube() {
+      if (deleteTable) delete [] table;
+    }
+
+    void set(int r, int g, int b, Pixel p) {
+      table[(r * nGreen + g) * nBlue + b] = p;
+    }
+
+    Pixel lookup(int r, int g, int b) const {
+      return table[(r * nGreen + g) * nBlue + b];
+    }
+
+    int size()      const { return nRed*nGreen*nBlue; }
+    int redMult()   const { return nGreen*nBlue; }
+    int greenMult() const { return nBlue; }
+    int blueMult()  const { return 1; }
+
+    // ColourMap lookup() method.  Note that this only works when the table has
+    // the default null mapping.
+    virtual void lookup(int i, int* r, int* g, int* b) {
+      if (i >= size()) return;
+      *b = i % nBlue;
+      i /= nBlue;
+      *g = i % nGreen;
+      *r = i / nGreen;
+      *r = (*r * 65535 + (nRed-1)   / 2) / (nRed-1);
+      *g = (*g * 65535 + (nGreen-1) / 2) / (nGreen-1);
+      *b = (*b * 65535 + (nBlue-1)  / 2) / (nBlue-1);
+    }
+
+    int nRed;
+    int nGreen;
+    int nBlue;
+    Pixel* table;
+    bool deleteTable;
+  };
+}
+#endif
diff --git a/rfb/ColourMap.h b/rfb/ColourMap.h
new file mode 100644
index 0000000..ee7783d
--- /dev/null
+++ b/rfb/ColourMap.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_COLOURMAP_H__
+#define __RFB_COLOURMAP_H__
+namespace rfb {
+  struct Colour {
+    Colour() : r(0), g(0), b(0) {}
+    Colour(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {}
+    int r, g, b;
+    bool operator==(const Colour& c) const {return c.r == r && c.g == g && c.b == b;}
+    bool operator!=(const Colour& c) const {return !(c == *this);}
+  };
+
+  class ColourMap {
+  public:
+    virtual void lookup(int index, int* r, int* g, int* b)=0;
+  };
+}
+#endif
diff --git a/rfb/ComparingUpdateTracker.cxx b/rfb/ComparingUpdateTracker.cxx
new file mode 100644
index 0000000..0c44d85
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <vector>
+#include <rdr/types.h>
+#include <rfb/Exception.h>
+#include <rfb/ComparingUpdateTracker.h>
+
+using namespace rfb;
+
+ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer)
+  : SimpleUpdateTracker(true), fb(buffer),
+    oldFb(fb->getPF(), 0, 0), firstCompare(true)
+{
+    changed.assign_union(fb->getRect());
+}
+
+ComparingUpdateTracker::~ComparingUpdateTracker()
+{
+}
+
+
+void ComparingUpdateTracker::flush_update(UpdateInfo* info,
+                                          const Region& cliprgn, int maxArea)
+{
+  throw rfb::Exception("flush_update(UpdateInfo*) not implemented");
+}
+
+void ComparingUpdateTracker::flush_update(UpdateTracker &ut,
+                                          const Region &cliprgn)
+{
+  throw rfb::Exception("flush_update(UpdateTracker&) not implemented");
+}
+
+
+#define BLOCK_SIZE 16
+
+void ComparingUpdateTracker::compare()
+{
+  std::vector<Rect> rects;
+  std::vector<Rect>::iterator i;
+
+  if (firstCompare) {
+    // NB: We leave the change region untouched on this iteration,
+    // since in effect the entire framebuffer has changed.
+    oldFb.setSize(fb->width(), fb->height());
+    for (int y=0; y<fb->height(); y+=BLOCK_SIZE) {
+      Rect pos(0, y, fb->width(), min(fb->height(), y+BLOCK_SIZE));
+      int srcStride;
+      const rdr::U8* srcData = fb->getPixelsR(pos, &srcStride);
+      oldFb.imageRect(pos, srcData, srcStride);
+    }
+    firstCompare = false;
+  } else {
+    copied.get_rects(&rects, copy_delta.x<=0, copy_delta.y<=0);
+    for (i = rects.begin(); i != rects.end(); i++)
+      oldFb.copyRect(*i, copy_delta);
+
+    Region to_check = changed.union_(copied);
+    to_check.get_rects(&rects);
+
+    Region newChanged;
+    for (i = rects.begin(); i != rects.end(); i++)
+      compareRect(*i, &newChanged);
+
+    copied.assign_subtract(newChanged);
+    changed = newChanged;
+  }
+}
+
+void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged)
+{
+  if (!r.enclosed_by(fb->getRect())) {
+    fprintf(stderr,"ComparingUpdateTracker: rect outside fb (%d,%d-%d,%d)\n", r.tl.x, r.tl.y, r.br.x, r.br.y);
+    return;
+  }
+
+  int bytesPerPixel = fb->getPF().bpp/8;
+  int oldStride;
+  rdr::U8* oldData = oldFb.getPixelsRW(r, &oldStride);
+  int oldStrideBytes = oldStride * bytesPerPixel;
+
+  std::vector<Rect> changedBlocks;
+
+  for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE)
+  {
+    // Get a strip of the source buffer
+    Rect pos(r.tl.x, blockTop, r.br.x, min(r.br.y, blockTop+BLOCK_SIZE));
+    int fbStride;
+    const rdr::U8* newBlockPtr = fb->getPixelsR(pos, &fbStride);
+    int newStrideBytes = fbStride * bytesPerPixel;
+
+    rdr::U8* oldBlockPtr = oldData;
+    int blockBottom = min(blockTop+BLOCK_SIZE, r.br.y);
+
+    for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE)
+    {
+      const rdr::U8* newPtr = newBlockPtr;
+      rdr::U8* oldPtr = oldBlockPtr;
+
+      int blockRight = min(blockLeft+BLOCK_SIZE, r.br.x);
+      int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel;
+
+      for (int y = blockTop; y < blockBottom; y++)
+      {
+        if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0)
+        {
+          // A block has changed - copy the remainder to the oldFb
+          changedBlocks.push_back(Rect(blockLeft, blockTop,
+                                       blockRight, blockBottom));
+          for (int y2 = y; y2 < blockBottom; y2++)
+          {
+            memcpy(oldPtr, newPtr, blockWidthInBytes);
+            newPtr += newStrideBytes;
+            oldPtr += oldStrideBytes;
+          }
+          break;
+        }
+
+        newPtr += newStrideBytes;
+        oldPtr += oldStrideBytes;
+      }
+
+      oldBlockPtr += blockWidthInBytes;
+      newBlockPtr += blockWidthInBytes;
+    }
+
+    oldData += oldStrideBytes * BLOCK_SIZE;
+  }
+
+  if (!changedBlocks.empty()) {
+    Region temp;
+    temp.setOrderedRects(changedBlocks);
+    newChanged->assign_union(temp);
+  }
+}
diff --git a/rfb/ComparingUpdateTracker.h b/rfb/ComparingUpdateTracker.h
new file mode 100644
index 0000000..bec93d1
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RFB_COMPARINGUPDATETRACKER_H__
+#define __RFB_COMPARINGUPDATETRACKER_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+  class ComparingUpdateTracker : public SimpleUpdateTracker {
+  public:
+    ComparingUpdateTracker(PixelBuffer* buffer);
+    ~ComparingUpdateTracker();
+
+    // compare() does the comparison and reduces its changed and copied regions
+    // as appropriate.
+
+    virtual void compare();
+
+    virtual void flush_update(UpdateInfo* info, const Region& cliprgn,
+                              int maxArea);
+    virtual void flush_update(UpdateTracker &info, const Region &cliprgn);
+  private:
+    void compareRect(const Rect& r, Region* newchanged);
+    PixelBuffer* fb;
+    ManagedPixelBuffer oldFb;
+    bool firstCompare;
+  };
+
+}
+#endif
diff --git a/rfb/Configuration.cxx b/rfb/Configuration.cxx
new file mode 100644
index 0000000..048fb77
--- /dev/null
+++ b/rfb/Configuration.cxx
@@ -0,0 +1,410 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Configuration.cxx
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#endif
+
+#include <rfb/util.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#ifdef WIN32
+
+// Under Win32, we use these routines from several threads,
+// so we must provide suitable locking.
+#include <rfb/Threading.h>
+
+static rfb::Mutex configLock;
+
+#endif
+
+#include <rdr/HexOutStream.h>
+#include <rdr/HexInStream.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Config");
+
+
+// -=- Configuration
+
+VoidParameter* Configuration::head = 0;
+
+bool Configuration::setParam(const char* n, const char* v, bool immutable) {
+  return setParam(n, strlen(n), v, immutable);
+}
+
+bool Configuration::setParam(const char* name, int len,
+                             const char* val, bool immutable)
+{
+  VoidParameter* current = head;
+  while (current) {
+    if ((int)strlen(current->getName()) == len &&
+        strncasecmp(current->getName(), name, len) == 0)
+    {
+      bool b = current->setParam(val);
+      if (b && immutable)
+        current->setImmutable();
+      return b;
+    }
+    current = current->_next;
+  }
+  return false;
+}
+
+bool Configuration::setParam(const char* config, bool immutable) {
+  bool hyphen = false;
+  if (config[0] == '-') {
+    hyphen = true;
+    config++;
+    if (config[0] == '-') config++; // allow gnu-style --<option>
+  }
+  const char* equal = strchr(config, '=');
+  if (equal) {
+    return setParam(config, equal-config, equal+1, immutable);
+  } else if (hyphen) {
+    VoidParameter* current = head;
+    while (current) {
+      if (strcasecmp(current->getName(), config) == 0) {
+        bool b = current->setParam();
+        if (b && immutable)
+          current->setImmutable();
+        return b;
+      }
+      current = current->_next;
+    }
+  }    
+  return false;
+}
+
+VoidParameter* Configuration::getParam(const char* param)
+{
+  VoidParameter* current = head;
+  while (current) {
+    if (strcasecmp(current->getName(), param) == 0)
+      return current;
+    current = current->_next;
+  }
+  return 0;
+}
+
+void Configuration::listParams(int width, int nameWidth) {
+  VoidParameter* current = head;
+  while (current) {
+    char* def_str = current->getDefaultStr();
+    const char* desc = current->getDescription();
+    fprintf(stderr,"  %-*s -", nameWidth, current->getName());
+    int column = strlen(current->getName());
+    if (column < nameWidth) column = nameWidth;
+    column += 4;
+    while (true) {
+      const char* s = strchr(desc, ' ');
+      int wordLen;
+      if (s) wordLen = s-desc;
+      else wordLen = strlen(desc);
+
+      if (column + wordLen + 1 > width) {
+        fprintf(stderr,"\n%*s",nameWidth+4,"");
+        column = nameWidth+4;
+      }
+      fprintf(stderr," %.*s",wordLen,desc);
+      column += wordLen + 1;
+      desc += wordLen + 1;
+      if (!s) break;
+    }
+
+    if (def_str) {
+      if (column + (int)strlen(def_str) + 11 > width)
+        fprintf(stderr,"\n%*s",nameWidth+4,"");
+      fprintf(stderr," (default=%s)\n",def_str);
+      strFree(def_str);
+    } else {
+      fprintf(stderr,"\n");
+    }
+    current = current->_next;
+  }
+}
+
+// -=- VoidParameter
+
+VoidParameter::VoidParameter(const char* name_, const char* desc_)
+  : immutable(false), name(name_), description(desc_) {
+  _next = Configuration::head;
+  Configuration::head = this;
+}
+
+VoidParameter::~VoidParameter() {
+}
+
+const char*
+VoidParameter::getName() const {
+  return name;
+}
+
+const char*
+VoidParameter::getDescription() const {
+  return description;
+}
+
+bool VoidParameter::setParam() {
+  return false;
+}
+
+bool VoidParameter::isBool() const {
+  return false;
+}
+
+void
+VoidParameter::setImmutable() {
+  vlog.debug("set immutable %s", getName());
+  immutable = true;
+}
+
+// -=- AliasParameter
+
+AliasParameter::AliasParameter(const char* name_, const char* desc_,
+                               VoidParameter* param_)
+  : VoidParameter(name_, desc_), param(param_) {
+}
+
+bool
+AliasParameter::setParam(const char* v) {
+  return param->setParam(v);
+}
+
+bool AliasParameter::setParam() {
+  return param->setParam();
+}
+
+char*
+AliasParameter::getDefaultStr() const {
+  return 0;
+}
+
+char* AliasParameter::getValueStr() const {
+  return param->getValueStr();
+}
+
+bool AliasParameter::isBool() const {
+  return param->isBool();
+}
+
+// -=- BoolParameter
+
+BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+BoolParameter::setParam(const char* v) {
+  if (immutable) return true;
+
+  if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
+      || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
+    value = 1;
+  else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
+           || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
+    value = 0;
+  else {
+    vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
+    return false;
+  }
+
+  vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
+  return true;
+}
+
+bool BoolParameter::setParam() {
+  setParam(true);
+  return true;
+}
+
+void BoolParameter::setParam(bool b) {
+  if (immutable) return;
+  value = b;
+  vlog.debug("set %s(Bool) to %d", getName(), value);
+}
+
+char*
+BoolParameter::getDefaultStr() const {
+  char* result = new char[8];
+  sprintf(result, "%d", (int)def_value);
+  return result;
+}
+
+char* BoolParameter::getValueStr() const {
+  char* result = new char[8];
+  sprintf(result, "%d", (int)value);
+  return result;
+}
+
+bool BoolParameter::isBool() const {
+  return true;
+}
+
+BoolParameter::operator bool() const {
+  return value;
+}
+
+// -=- IntParameter
+
+IntParameter::IntParameter(const char* name_, const char* desc_, int v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+IntParameter::setParam(const char* v) {
+  if (immutable) return true;
+  vlog.debug("set %s(Int) to %s", getName(), v);
+  value = atoi(v);
+  return true;
+}
+
+bool
+IntParameter::setParam(int v) {
+  if (immutable) return true;
+  vlog.debug("set %s(Int) to %d", getName(), v);
+  value = v;
+  return true;
+}
+
+char*
+IntParameter::getDefaultStr() const {
+  char* result = new char[16];
+  sprintf(result, "%d", def_value);
+  return result;
+}
+
+char* IntParameter::getValueStr() const {
+  char* result = new char[16];
+  sprintf(result, "%d", value);
+  return result;
+}
+
+IntParameter::operator int() const {
+  return value;
+}
+
+// -=- StringParameter
+
+StringParameter::StringParameter(const char* name_, const char* desc_,
+                                 const char* v)
+  : VoidParameter(name_, desc_), value(strDup(v)), def_value(v)
+{
+  if (!v) {
+    fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
+    throw rfb::Exception("Default value <null> not allowed");
+  }
+}
+
+StringParameter::~StringParameter() {
+  strFree(value);
+}
+
+bool StringParameter::setParam(const char* v) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return true;
+  if (!v)
+    throw rfb::Exception("setParam(<null>) not allowed");
+  vlog.debug("set %s(String) to %s", getName(), v);
+  strFree(value);
+  value = strDup(v);
+  return value != 0;
+}
+
+char* StringParameter::getDefaultStr() const {
+  return strDup(def_value);
+}
+
+char* StringParameter::getValueStr() const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  return strDup(value);
+}
+
+// -=- BinaryParameter
+
+BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l)
+: VoidParameter(name_, desc_), value(0), length(0), def_value((char*)v), def_length(l) {
+  if (l) {
+    value = new char[l];
+    length = l;
+    memcpy(value, v, l);
+  }
+}
+BinaryParameter::~BinaryParameter() {
+  if (value)
+    delete [] value;
+}
+
+bool BinaryParameter::setParam(const char* v) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return true;
+  vlog.debug("set %s(Binary) to %s", getName(), v);
+  return rdr::HexInStream::hexStrToBin(v, &value, &length);
+}
+
+void BinaryParameter::setParam(const void* v, int len) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return;
+  vlog.debug("set %s(Binary)", getName());
+  delete [] value; value = 0;
+  if (len) {
+    value = new char[len];
+    length = len;
+    memcpy(value, v, len);
+  }
+}
+
+char* BinaryParameter::getDefaultStr() const {
+  return rdr::HexOutStream::binToHexStr(def_value, def_length);
+}
+
+char* BinaryParameter::getValueStr() const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  return rdr::HexOutStream::binToHexStr(value, length);
+}
+
+void BinaryParameter::getData(void** data_, int* length_) const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (length_) *length_ = length;
+  if (data_) {
+    *data_ = new char[length];
+    memcpy(*data_, value, length);
+  }
+}
diff --git a/rfb/Configuration.h b/rfb/Configuration.h
new file mode 100644
index 0000000..1b37ac9
--- /dev/null
+++ b/rfb/Configuration.h
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Configuration.h
+//
+// This header defines a set of classes used to represent configuration
+// parameters of different types.  Instances of the different parameter
+// types are associated with instances of the Configuration class, and
+// are each given a unique name.  The Configuration class provides a
+// generic API through which parameters may be located by name and their
+// value set, thus removing the need to write platform-specific code.
+// Simply defining a new parameter and associating it with a Configuration
+// will allow it to be configured by the user.
+//
+// The Configuration class is used to allow multiple distinct configurations
+// to co-exist at the same time.  A process serving several desktops, for
+// instance, can create a Configuration instance for each, to allow them
+// to be configured independently, from the command-line, registry, etc.
+
+#ifndef __RFB_CONFIGURATION_H__
+#define __RFB_CONFIGURATION_H__
+
+namespace rfb {
+  class VoidParameter;
+
+  // -=- Configuration
+  //     Class used to access parameters.
+
+  class Configuration {
+  public:
+    // - Set named parameter to value
+    static bool setParam(const char* param, const char* value, bool immutable=false);
+
+    // - Set parameter to value (separated by "=")
+    static bool setParam(const char* config, bool immutable=false);
+
+    // - Set named parameter to value, with name truncated at len
+    static bool setParam(const char* name, int len,
+                         const char* val, bool immutable);
+
+    // - Get named parameter
+    static VoidParameter* getParam(const char* param);
+
+    static void listParams(int width=79, int nameWidth=10);
+
+    static VoidParameter* head;
+  };
+
+  // -=- VoidParameter
+  //     Configuration parameter base-class.
+
+  class VoidParameter {
+  public:
+    VoidParameter(const char* name_, const char* desc_);
+    virtual  ~VoidParameter();
+    const char* getName() const;
+    const char* getDescription() const;
+
+    virtual bool setParam(const char* value)  = 0;
+    virtual bool setParam();
+    virtual char* getDefaultStr() const = 0;
+    virtual char* getValueStr() const = 0;
+    virtual bool isBool() const;
+
+    virtual void setImmutable();
+
+    VoidParameter* _next;
+  protected:
+    bool immutable;
+    const char* name;
+    const char* description;
+  };
+
+  class AliasParameter : public VoidParameter {
+  public:
+    AliasParameter(const char* name_, const char* desc_,VoidParameter* param_);
+    virtual bool setParam(const char* value);
+    virtual bool setParam();
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    virtual bool isBool() const;
+  private:
+    VoidParameter* param;
+  };
+
+  class BoolParameter : public VoidParameter {
+  public:
+    BoolParameter(const char* name_, const char* desc_, bool v);
+    virtual bool setParam(const char* value);
+    virtual bool setParam();
+    virtual void setParam(bool b);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    virtual bool isBool() const;
+    operator bool() const;
+  protected:
+    bool value;
+    bool def_value;
+  };
+
+  class IntParameter : public VoidParameter {
+  public:
+    IntParameter(const char* name_, const char* desc_, int v);
+    virtual bool setParam(const char* value);
+    virtual bool setParam(int v);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    operator int() const;
+  protected:
+    int value;
+    int def_value;
+  };
+
+  class StringParameter : public VoidParameter {
+  public:
+    // StringParameter contains a null-terminated string, which CANNOT
+    // be Null, and so neither can the default value!
+    StringParameter(const char* name_, const char* desc_, const char* v);
+    virtual ~StringParameter();
+    virtual bool setParam(const char* value);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+
+    // getData() returns a copy of the data - it must be delete[]d by the
+    // caller.
+    char* getData() const { return getValueStr(); }
+  protected:
+    char* value;
+    const char* def_value;
+  };
+
+  class BinaryParameter : public VoidParameter {
+  public:
+    BinaryParameter(const char* name_, const char* desc_, const void* v, int l);
+    virtual ~BinaryParameter();
+    virtual bool setParam(const char* value);
+    virtual void setParam(const void* v, int l);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+
+    void getData(void** data, int* length) const;
+
+  protected:
+    char* value;
+    int length;
+    char* def_value;
+    int def_length;
+  };
+
+};
+
+#endif // __RFB_CONFIGURATION_H__
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
new file mode 100644
index 0000000..9552940
--- /dev/null
+++ b/rfb/ConnParams.cxx
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+#include <rfb/ConnParams.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+ConnParams::ConnParams()
+  : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
+    supportsLocalCursor(false), supportsDesktopResize(false),
+    name_(0), nEncodings_(0), encodings_(0),
+    currentEncoding_(encodingRaw), verStrPos(0)
+{
+  setName("");
+}
+
+ConnParams::~ConnParams()
+{
+  delete [] name_;
+  delete [] encodings_;
+}
+
+bool ConnParams::readVersion(rdr::InStream* is, bool* done)
+{
+  if (verStrPos >= 12) return false;
+  while (is->checkNoWait(1) && verStrPos < 12) {
+    verStr[verStrPos++] = is->readU8();
+  }
+
+  if (verStrPos < 12) {
+    *done = false;
+    return true;
+  }
+  *done = true;
+  verStr[12] = 0;
+  return (sscanf(verStr, "RFB %03d.%03d\n", &majorVersion,&minorVersion) == 2);
+}
+
+void ConnParams::writeVersion(rdr::OutStream* os)
+{
+  char str[13];
+  sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);
+  os->writeBytes(str, 12);
+  os->flush();
+}
+
+void ConnParams::setPF(const PixelFormat& pf)
+{
+  pf_ = pf;
+
+  if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
+    throw Exception("setPF: not 8, 16 or 32 bpp?");
+}
+
+void ConnParams::setName(const char* name)
+{
+  delete [] name_;
+  name_ = strDup(name);
+}
+
+void ConnParams::setEncodings(int nEncodings, const rdr::U32* encodings)
+{
+  if (nEncodings > nEncodings_) {
+    delete [] encodings_;
+    encodings_ = new rdr::U32[nEncodings];
+  }
+  nEncodings_ = nEncodings;
+  useCopyRect = false;
+  supportsLocalCursor = false;
+  currentEncoding_ = encodingRaw;
+
+  for (int i = nEncodings-1; i >= 0; i--) {
+    encodings_[i] = encodings[i];
+
+    if (encodings[i] == encodingCopyRect)
+      useCopyRect = true;
+    else if (encodings[i] == pseudoEncodingCursor)
+      supportsLocalCursor = true;
+    else if (encodings[i] == pseudoEncodingDesktopSize)
+      supportsDesktopResize = true;
+    else if (encodings[i] <= encodingMax && Encoder::supported(encodings[i]))
+      currentEncoding_ = encodings[i];
+  }
+}
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
new file mode 100644
index 0000000..fb96a7a
--- /dev/null
+++ b/rfb/ConnParams.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// ConnParams - structure containing the connection parameters.
+//
+
+#ifndef __RFB_CONNPARAMS_H__
+#define __RFB_CONNPARAMS_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class ConnParams {
+  public:
+    ConnParams();
+    ~ConnParams();
+
+    bool readVersion(rdr::InStream* is, bool* done);
+    void writeVersion(rdr::OutStream* os);
+
+    int majorVersion;
+    int minorVersion;
+
+    void setVersion(int major, int minor) {
+      majorVersion = major; minorVersion = minor;
+    }
+    bool isVersion(int major, int minor) {
+      return majorVersion == major && minorVersion == minor;
+    }
+    bool beforeVersion(int major, int minor) {
+      return (majorVersion < major ||
+              (majorVersion == major && minorVersion < minor));
+    }
+    bool afterVersion(int major, int minor) {
+      return !beforeVersion(major,minor+1);
+    }
+
+    int width;
+    int height;
+
+    const PixelFormat& pf() { return pf_; }
+    void setPF(const PixelFormat& pf);
+
+    const char* name() { return name_; }
+    void setName(const char* name);
+
+    rdr::U32 currentEncoding() { return currentEncoding_; }
+    int nEncodings() { return nEncodings_; }
+    const rdr::U32* encodings() { return encodings_; }
+    void setEncodings(int nEncodings, const rdr::U32* encodings);
+    bool useCopyRect;
+
+    bool supportsLocalCursor;
+    bool supportsDesktopResize;
+
+  private:
+
+    PixelFormat pf_;
+    char* name_;
+    int nEncodings_;
+    rdr::U32* encodings_;
+    int currentEncoding_;
+    char verStr[13];
+    int verStrPos;
+  };
+}
+#endif
diff --git a/rfb/Cursor.cxx b/rfb/Cursor.cxx
new file mode 100644
index 0000000..b50d925
--- /dev/null
+++ b/rfb/Cursor.cxx
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <string.h>
+#include <rfb/Cursor.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Cursor");
+
+void Cursor::setSize(int w, int h) {
+  int oldMaskLen = maskLen();
+  ManagedPixelBuffer::setSize(w, h);
+  if (maskLen() > oldMaskLen) {
+    delete [] mask.buf;
+    mask.buf = new rdr::U8[maskLen()];
+  }
+}
+
+void Cursor::drawOutline(const Pixel& c)
+{
+  Cursor outlined;
+
+  // Create a mirror of the existing cursor
+  outlined.setPF(getPF());
+  outlined.setSize(width(), height());
+  outlined.hotspot = hotspot;
+
+  // Clear the mirror's background to the outline colour
+  outlined.fillRect(getRect(), c);
+
+  // Blit the existing cursor, using its mask
+  outlined.maskRect(getRect(), data, mask.buf);
+
+  // Now just adjust the mask to add the outline.  The outline pixels
+  // will already be the right colour. :)
+  int maskBytesPerRow = (width() + 7) / 8;
+  for (int y = 0; y < height(); y++) {
+    for (int byte=0; byte<maskBytesPerRow; byte++) {
+      rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
+
+      // Handle above & below outline
+      if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
+      if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
+
+      // Left outline
+      m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
+      if (byte < maskBytesPerRow-1)
+        m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
+
+      // Right outline
+      m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
+      if (byte > 0)
+        m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
+
+      outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
+    }
+  }
+
+  // Replace the existing cursor & mask with the new one
+  delete [] data;
+  delete [] mask.buf;
+  data = outlined.data; outlined.data = 0;
+  mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
+}
+
+rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1)
+{
+  bool gotPix0 = false;
+  bool gotPix1 = false;
+  rdr::U8Array source(maskLen());
+  memset(source.buf, 0, maskLen());
+
+  int maskBytesPerRow = (width() + 7) / 8;
+  for (int y = 0; y < height(); y++) {
+    for (int x = 0; x < width(); x++) {
+      int byte = y * maskBytesPerRow + x / 8;
+      int bit = 7 - x % 8;
+      if (mask.buf[byte] & (1 << bit)) {
+        Pixel pix=0;
+        switch (getPF().bpp) {
+        case 8:  pix = ((rdr::U8*) data)[y * width() + x]; break;
+        case 16: pix = ((rdr::U16*)data)[y * width() + x]; break;
+        case 32: pix = ((rdr::U32*)data)[y * width() + x]; break;
+        }
+        if (!gotPix0 || pix == *pix0) {
+          gotPix0 = true;
+          *pix0 = pix;
+        } else if (!gotPix1 || pix == *pix1) {
+          gotPix1 = true;
+          *pix1 = pix;
+          source.buf[byte] |= (1 << bit);
+        } else {
+          // not a bitmap
+          return 0;
+        }
+      }
+    }
+  }
+  return source.takeBuf();
+}
+
+// crop() determines the "busy" rectangle for the cursor - the minimum bounding
+// rectangle containing actual pixels.  This isn't the most efficient algorithm
+// but it's short.  For sanity, we make sure that the busy rectangle always
+// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
+// would cause problems if it was above or left of the actual pixels)
+
+void Cursor::crop()
+{
+  Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
+                                       hotspot.x+1, hotspot.y+1));
+  int maskBytesPerRow = (width() + 7) / 8;
+  int x, y;
+  for (y = 0; y < height(); y++) {
+    for (x = 0; x < width(); x++) {
+      int byte = y * maskBytesPerRow + x / 8;
+      int bit = 7 - x % 8;
+      if (mask.buf[byte] & (1 << bit)) {
+        if (x < busy.tl.x) busy.tl.x = x;
+        if (x+1 > busy.br.x) busy.br.x = x+1;
+        if (y < busy.tl.y) busy.tl.y = y;
+        if (y+1 > busy.br.y) busy.br.y = y+1;
+      }
+    }
+  }
+
+  if (width() == busy.width() && height() == busy.height()) return;
+
+  vlog.debug("cropping %dx%d to %dx%d", width(), height(),
+             busy.width(), busy.height());
+
+  // Copy the pixel data
+  int newDataLen = busy.area() * (getPF().bpp/8);
+  rdr::U8* newData = new rdr::U8[newDataLen];
+  getImage(newData, busy);
+
+  // Copy the mask
+  int newMaskBytesPerRow = (busy.width()+7)/8;
+  int newMaskLen = newMaskBytesPerRow * busy.height();
+  rdr::U8* newMask = new rdr::U8[newMaskLen];
+  memset(newMask, 0, newMaskLen);
+  for (y = 0; y < busy.height(); y++) {
+    int newByte, newBit;
+    for (x = 0; x < busy.width(); x++) {
+      int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
+      int oldBit = 7 - (x+busy.tl.x) % 8;
+      newByte = y * newMaskBytesPerRow + x / 8;
+      newBit = 7 - x % 8;
+      if (mask.buf[oldByte] & (1 << oldBit))
+        newMask[newByte] |= (1 << newBit);
+    }
+  }
+
+  // Set the size and data to the new, cropped cursor.
+  setSize(busy.width(), busy.height());
+  hotspot = hotspot.subtract(busy.tl);
+  delete [] data;
+  delete [] mask.buf;
+  datasize = newDataLen;
+  data = newData;
+  mask.buf = newMask;
+}
diff --git a/rfb/Cursor.h b/rfb/Cursor.h
new file mode 100644
index 0000000..0f18775
--- /dev/null
+++ b/rfb/Cursor.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Cursor - structure containing information describing
+//          the current cursor shape
+//
+
+#ifndef __RFB_CURSOR_H__
+#define __RFB_CURSOR_H__
+
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+  class Cursor : public ManagedPixelBuffer {
+  public:
+    Cursor() {}
+    rdr::U8Array mask;
+    Point hotspot;
+
+    int maskLen() { return (width() + 7) / 8 * height(); }
+
+    // setSize() resizes the cursor.  The contents of the data and mask are
+    // undefined after this call.
+    virtual void setSize(int w, int h);
+
+    // drawOutline() adds an outline to the cursor in the given colour.
+    void drawOutline(const Pixel& c);
+
+    // getBitmap() tests whether the cursor is monochrome, and if so returns a
+    // bitmap together with background and foreground colours.  The size and
+    // layout of the bitmap are the same as the mask.
+    rdr::U8* getBitmap(Pixel* pix0, Pixel* pix1);
+
+    // crop() crops the cursor down to the smallest possible size, based on the
+    // mask.
+    void crop();
+  };
+
+}
+#endif
diff --git a/rfb/Decoder.cxx b/rfb/Decoder.cxx
new file mode 100644
index 0000000..816cb6e
--- /dev/null
+++ b/rfb/Decoder.cxx
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Decoder.h>
+#include <rfb/RawDecoder.h>
+#include <rfb/RREDecoder.h>
+#include <rfb/HextileDecoder.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+Decoder::~Decoder()
+{
+}
+
+DecoderCreateFnType Decoder::createFns[encodingMax+1] = { 0 };
+
+bool Decoder::supported(unsigned int encoding)
+{
+  return encoding <= encodingMax && createFns[encoding];
+}
+
+Decoder* Decoder::createDecoder(unsigned int encoding, CMsgReader* reader)
+{
+  if (encoding <= encodingMax && createFns[encoding])
+    return (*createFns[encoding])(reader);
+  return 0;
+}
+
+void Decoder::registerDecoder(unsigned int encoding,
+                              DecoderCreateFnType createFn)
+{
+  if (encoding > encodingMax)
+    throw Exception("Decoder::registerDecoder: encoding out of range");
+
+  if (createFns[encoding])
+    fprintf(stderr,"Replacing existing decoder for encoding %s (%d)\n",
+            encodingName(encoding), encoding);
+  createFns[encoding] = createFn;
+}
+
+int DecoderInit::count = 0;
+
+DecoderInit::DecoderInit()
+{
+  if (count++ != 0) return;
+
+  Decoder::registerDecoder(encodingRaw, RawDecoder::create);
+  Decoder::registerDecoder(encodingRRE, RREDecoder::create);
+  Decoder::registerDecoder(encodingHextile, HextileDecoder::create);
+  Decoder::registerDecoder(encodingZRLE, ZRLEDecoder::create);
+}
diff --git a/rfb/Decoder.h b/rfb/Decoder.h
new file mode 100644
index 0000000..914b26a
--- /dev/null
+++ b/rfb/Decoder.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_DECODER_H__
+#define __RFB_DECODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+  class CMsgReader;
+  class CMsgHandler;
+  class Decoder;
+  typedef Decoder* (*DecoderCreateFnType)(CMsgReader*);
+
+  class Decoder {
+  public:
+    virtual ~Decoder();
+    virtual void readRect(const Rect& r, CMsgHandler* handler)=0;
+
+    static bool supported(unsigned int encoding);
+    static Decoder* createDecoder(unsigned int encoding, CMsgReader* reader);
+    static void registerDecoder(unsigned int encoding,
+                                DecoderCreateFnType createFn);
+  private:
+    static DecoderCreateFnType createFns[encodingMax+1];
+  };
+
+  class DecoderInit {
+    static int count;
+  public:
+    DecoderInit();
+  };
+
+  static DecoderInit decoderInitObj;
+}
+
+#endif
diff --git a/rfb/Encoder.cxx b/rfb/Encoder.cxx
new file mode 100644
index 0000000..f2d6d3d
--- /dev/null
+++ b/rfb/Encoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Encoder.h>
+#include <rfb/RawEncoder.h>
+#include <rfb/RREEncoder.h>
+#include <rfb/HextileEncoder.h>
+#include <rfb/ZRLEEncoder.h>
+
+using namespace rfb;
+
+Encoder::~Encoder()
+{
+}
+
+EncoderCreateFnType Encoder::createFns[encodingMax+1] = { 0 };
+
+bool Encoder::supported(unsigned int encoding)
+{
+  return encoding <= encodingMax && createFns[encoding];
+}
+
+Encoder* Encoder::createEncoder(unsigned int encoding, SMsgWriter* writer)
+{
+  if (encoding <= encodingMax && createFns[encoding])
+    return (*createFns[encoding])(writer);
+  return 0;
+}
+
+void Encoder::registerEncoder(unsigned int encoding,
+                              EncoderCreateFnType createFn)
+{
+  if (encoding > encodingMax)
+    throw Exception("Encoder::registerEncoder: encoding out of range");
+
+  if (createFns[encoding])
+    fprintf(stderr,"Replacing existing encoder for encoding %s (%d)\n",
+            encodingName(encoding), encoding);
+  createFns[encoding] = createFn;
+}
+
+void Encoder::unregisterEncoder(unsigned int encoding)
+{
+  if (encoding > encodingMax)
+    throw Exception("Encoder::unregisterEncoder: encoding out of range");
+  createFns[encoding] = 0;
+}
+
+int EncoderInit::count = 0;
+
+EncoderInit::EncoderInit()
+{
+  if (count++ != 0) return;
+
+  Encoder::registerEncoder(encodingRaw, RawEncoder::create);
+  Encoder::registerEncoder(encodingRRE, RREEncoder::create);
+  Encoder::registerEncoder(encodingHextile, HextileEncoder::create);
+  Encoder::registerEncoder(encodingZRLE, ZRLEEncoder::create);
+}
diff --git a/rfb/Encoder.h b/rfb/Encoder.h
new file mode 100644
index 0000000..7795d90
--- /dev/null
+++ b/rfb/Encoder.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_ENCODER_H__
+#define __RFB_ENCODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+  class SMsgWriter;
+  class Encoder;
+  class ImageGetter;
+  typedef Encoder* (*EncoderCreateFnType)(SMsgWriter*);
+
+  class Encoder {
+  public:
+    virtual ~Encoder();
+
+    // writeRect() tries to write the given rectangle.  If it is unable to
+    // write the whole rectangle it returns false and sets actual to the actual
+    // rectangle which was updated.
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual)=0;
+
+    static bool supported(unsigned int encoding);
+    static Encoder* createEncoder(unsigned int encoding, SMsgWriter* writer);
+    static void registerEncoder(unsigned int encoding,
+                                EncoderCreateFnType createFn);
+    static void unregisterEncoder(unsigned int encoding);
+  private:
+    static EncoderCreateFnType createFns[encodingMax+1];
+  };
+
+  class EncoderInit {
+    static int count;
+  public:
+    EncoderInit();
+  };
+
+  static EncoderInit encoderInitObj;
+}
+
+#endif
diff --git a/rfb/Exception.h b/rfb/Exception.h
new file mode 100644
index 0000000..aa98271
--- /dev/null
+++ b/rfb/Exception.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_EXCEPTION_H__
+#define __RFB_EXCEPTION_H__
+
+#include <rdr/Exception.h>
+
+namespace rfb {
+  struct Exception : public rdr::Exception {
+    Exception(const char* s=0, const char* e="rfb::Exception")
+      : rdr::Exception(s,e) {}
+  };
+  struct AuthFailureException : public Exception {
+    AuthFailureException(const char* s="Authentication failure")
+      : Exception(s,"rfb::AuthFailureException") {}
+  };
+  struct ConnFailedException : public Exception {
+    ConnFailedException(const char* s="Connection failed")
+      : Exception(s,"rfb::ConnFailedException") {}
+  };
+}
+#endif
diff --git a/rfb/HTTPServer.cxx b/rfb/HTTPServer.cxx
new file mode 100644
index 0000000..55e6909
--- /dev/null
+++ b/rfb/HTTPServer.cxx
@@ -0,0 +1,386 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rfb/HTTPServer.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/MemOutStream.h>
+#include <time.h>
+
+// *** Shouldn't really link against this - only for ClientWaitTimeMillis
+//     and IdleTimeout
+#include <rfb/ServerCore.h>
+
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("HTTPServer");
+
+
+//
+// -=- LineReader
+//     Helper class which is repeatedly called until a line has been read
+//     (lines end in \n or \r\n).
+//     Returns true when line complete, and resets internal state so that
+//     next read() call will start reading a new line.
+//     Only one buffer is kept - process line before reading next line!
+//
+
+class LineReader : public CharArray {
+public:
+  LineReader(InStream& is_, int l)
+    : CharArray(l), is(is_), pos(0), len(l), bufferOverrun(false) {}
+
+  // Returns true if line complete, false otherwise
+  bool read() {
+    while (is.checkNoWait(1)) {
+      char c = is.readU8();
+
+      if (c == '\n') {
+        if (pos && (buf[pos-1] == '\r'))
+          pos--;
+        bufferOverrun = false;
+        buf[pos++] = 0;
+        pos = 0;
+        return true;
+      }
+
+      if (pos == (len-1)) {
+        bufferOverrun = true;
+        buf[pos] = 0;
+        return true;
+      }
+
+      buf[pos++] = c;
+    }
+
+    return false;
+  }
+  bool didBufferOverrun() const {return bufferOverrun;}
+protected:
+  InStream& is;
+  int pos, len;
+  bool bufferOverrun;
+};
+
+
+//
+// -=- HTTPServer::Session
+//     Manages the internal state for an HTTP session.
+//     processHTTP returns true when request has completed,
+//     indicating that socket & session data can be deleted.
+//
+
+class rfb::HTTPServer::Session {
+public:
+  Session(network::Socket& s, rfb::HTTPServer& srv)
+    : contentType(0), line(s.inStream(), 256), sock(s),
+      server(srv), state(ReadRequestLine), lastActive(time(0)) {
+  }
+  ~Session() {
+  }
+
+  void writeResponse(int result, const char* text);
+  bool writeResponse(int code);
+
+  bool processHTTP();
+
+  network::Socket* getSock() const {return &sock;}
+
+  int checkIdleTimeout();
+protected:
+  CharArray uri;
+  const char* contentType;
+  LineReader line;
+  network::Socket& sock;
+  rfb::HTTPServer& server;
+  enum {ReadRequestLine, ReadHeaders, WriteResponse} state;
+  enum {GetRequest, HeadRequest} request;
+  time_t lastActive;
+};
+
+
+// - Internal helper routines
+
+void
+copyStream(InStream& is, OutStream& os) {
+  try {
+    while (1) {
+      os.writeU8(is.readU8());
+    }
+  } catch (rdr::EndOfStream) {
+  }
+}
+
+void writeLine(OutStream& os, const char* text) {
+  os.writeBytes(text, strlen(text));
+  os.writeBytes("\r\n", 2);
+}
+
+
+// - Write an HTTP-compliant response to the client
+
+void
+HTTPServer::Session::writeResponse(int result, const char* text) {
+  char buffer[1024];
+  if (strlen(text) > 512)
+    throw new rdr::Exception("Internal error - HTTP response text too big");
+  sprintf(buffer, "%s %d %s", "HTTP/1.1", result, text);
+  OutStream& os=sock.outStream();
+  writeLine(os, buffer);
+  writeLine(os, "Server: RealVNC/4.0");
+  writeLine(os, "Connection: close");
+  os.writeBytes("Content-Type: ", 14);
+  if (result == 200) {
+    if (!contentType)
+      contentType = guessContentType(uri.buf, "text/html");
+    os.writeBytes(contentType, strlen(contentType));
+  } else {
+    os.writeBytes("text/html", 9);
+  }
+  os.writeBytes("\r\n", 2);
+  writeLine(os, "");
+  if (result != 200) {
+    writeLine(os, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">");
+    writeLine(os, "<HTML><HEAD>");
+    sprintf(buffer, "<TITLE>%d %s</TITLE>", result, text);
+    writeLine(os, buffer);
+    writeLine(os, "</HEAD><BODY><H1>");
+    writeLine(os, text);
+    writeLine(os, "</H1></BODY></HTML>");
+    sock.outStream().flush();
+  }
+}
+
+bool
+HTTPServer::Session::writeResponse(int code) {
+  switch (code) {
+  case 200: writeResponse(code, "OK"); break;
+  case 400: writeResponse(code, "Bad Request"); break;
+  case 404: writeResponse(code, "Not Found"); break;
+  case 501: writeResponse(code, "Not Implemented"); break;
+  default: writeResponse(500, "Unknown Error"); break;
+  };
+
+  // This return code is passed straight out of processHTTP().
+  // true indicates that the request has been completely processed.
+  return true;
+}
+
+// - Main HTTP request processing routine
+
+bool
+HTTPServer::Session::processHTTP() {
+  lastActive = time(0);
+
+  while (sock.inStream().checkNoWait(1)) {
+
+    switch (state) {
+
+      // Reading the Request-Line
+    case ReadRequestLine:
+
+      // Either read a line, or run out of incoming data
+      if (!line.read())
+        return false;
+
+      // We have read a line!  Skip it if it's blank
+      if (strlen(line.buf) == 0)
+        continue;
+
+      // The line contains a request to process.
+      {
+        char method[16], path[128], version[16];
+        int matched = sscanf(line.buf, "%15s%127s%15s",
+          method, path, version);
+        if (matched != 3)
+          return writeResponse(400);
+
+        // Store the required "method"
+        if (strcmp(method, "GET") == 0)
+          request = GetRequest;
+        else if (strcmp(method, "HEAD") == 0)
+          request = HeadRequest;
+        else
+          return writeResponse(501);
+
+        // Store the URI to the "document"
+        uri.buf = strDup(path);
+      }
+
+      // Move on to reading the request headers
+      state = ReadHeaders;
+      break;
+
+      // Reading the request headers
+    case ReadHeaders:
+
+      // Try to read a line
+      if (!line.read())
+        return false;
+
+      // Skip headers until we hit a blank line
+      if (strlen(line.buf) != 0)
+        continue;
+
+      // Headers ended - write the response!
+      {
+        CharArray address(sock.getPeerAddress());
+        vlog.info("getting %s for %s", uri.buf, address.buf);
+        InStream* data = server.getFile(uri.buf, &contentType);
+        if (!data)
+          return writeResponse(404);
+
+        try {
+          writeResponse(200);
+          if (request == GetRequest)
+            copyStream(*data, sock.outStream());
+          sock.outStream().flush();
+        } catch (rdr::Exception& e) {
+          vlog.error("error writing HTTP document:%s", e.str());
+        }
+        delete data;
+      }
+
+      // The operation is complete!
+      return true;
+
+    default:
+      throw rdr::Exception("invalid HTTPSession state!");
+    };
+
+  }
+
+  // Indicate that we're still processing the HTTP request.
+  return false;
+}
+
+int HTTPServer::Session::checkIdleTimeout() {
+  time_t now = time(0);
+  int timeout = (lastActive + rfb::Server::idleTimeout) - now;
+  if (timeout > 0)
+    return timeout * 1000;
+  sock.shutdown();
+  return 0;
+}
+
+// -=- Constructor / destructor
+
+HTTPServer::HTTPServer() {
+}
+
+HTTPServer::~HTTPServer() {
+  std::list<Session*>::iterator i;
+  for (i=sessions.begin(); i!=sessions.end(); i++) {
+    delete (*i)->getSock();
+    delete *i;
+  }
+}
+
+
+// -=- SocketServer interface implementation
+
+void
+HTTPServer::addClient(network::Socket* sock) {
+  Session* s = new Session(*sock, *this);
+  if (!s) {
+    sock->shutdown();
+  } else {
+    sock->inStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+    sock->outStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+    sessions.push_front(s);
+  }
+}
+
+bool
+HTTPServer::processSocketEvent(network::Socket* sock) {
+  std::list<Session*>::iterator i;
+  for (i=sessions.begin(); i!=sessions.end(); i++) {
+    if ((*i)->getSock() == sock) {
+      try {
+        if ((*i)->processHTTP()) {
+          vlog.info("completed HTTP request");
+          delete *i;
+          sessions.erase(i);
+          break;
+        }
+        return true;
+      } catch (rdr::Exception& e) {
+        vlog.error("untrapped: %s", e.str());
+        delete *i;
+        sessions.erase(i);
+        break;
+      }
+    }
+  }
+  delete sock;
+  return false;
+}
+
+void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
+{
+  sockets->clear();
+  std::list<Session*>::iterator ci;
+  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+    sockets->push_back((*ci)->getSock());
+  }
+}
+
+int HTTPServer::checkTimeouts() {
+  std::list<Session*>::iterator ci;
+  int timeout = 0;
+  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+  }
+  return timeout;
+}
+
+
+// -=- Default getFile implementation
+
+InStream*
+HTTPServer::getFile(const char* name, const char** contentType) {
+  return 0;
+}
+
+const char*
+HTTPServer::guessContentType(const char* name, const char* defType) {
+  CharArray file, ext;
+  if (!strSplit(name, '.', &file.buf, &ext.buf))
+    return defType;
+  if (strcasecmp(ext.buf, "html") == 0 ||
+    strcasecmp(ext.buf, "htm") == 0) {
+    return "text/html";
+  } else if (strcasecmp(ext.buf, "txt") == 0) {
+    return "text/plain";
+  } else if (strcasecmp(ext.buf, "gif") == 0) {
+    return "image/gif";
+  } else if (strcasecmp(ext.buf, "jpg") == 0) {
+    return "image/jpeg";
+  } else if (strcasecmp(ext.buf, "jar") == 0) {
+    return "application/java-archive";
+  } else if (strcasecmp(ext.buf, "exe") == 0) {
+    return "application/octet-stream";
+  }
+  return defType;
+}
diff --git a/rfb/HTTPServer.h b/rfb/HTTPServer.h
new file mode 100644
index 0000000..9431195
--- /dev/null
+++ b/rfb/HTTPServer.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- HTTPServer.h
+
+// Single-threaded HTTP server implementation.
+// All I/O is handled by the processSocketEvent routine,
+// which is called by the main-loop of the VNC server whenever
+// there is an event on an HTTP socket.
+
+#ifndef __RFB_HTTP_SERVER_H__
+#define __RFB_HTTP_SERVER_H__
+
+#include <list>
+
+#include <rdr/MemInStream.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+  class HTTPServer : public network::SocketServer {
+  public:
+    // -=- Constructors
+
+    // - HTTPServer(files)
+    //   Create an HTTP server which will use the getFile method
+    //   to satisfy HTTP GET requests.
+    HTTPServer();
+
+    virtual ~HTTPServer();
+
+    // -=- Client management
+
+    // - Run a client connection on the supplied socket
+    //   This causes the server to perform HTTP protocol on the
+    //   supplied socket.
+    //   The socket will be closed if protocol initialisation
+    //   fails.
+    virtual void addClient(network::Socket* sock);
+
+    // -=- Event processing methods
+
+    // - Process an input event on a particular Socket
+    //   The platform-specific side of the server implementation calls
+    //   this method whenever data arrives on one of the active
+    //   network sockets.
+    //   The method returns true if the Socket is still in use by the
+    //   server, or false if it is no longer required and should be
+    //   deleted.
+    //   NB:  If false is returned then the Socket is deleted and must
+    //   not be accessed again!
+
+    virtual bool processSocketEvent(network::Socket* sock);
+
+    // - Check for socket timeouts
+    virtual int checkTimeouts();
+
+    // getSockets() gets a list of sockets.  This can be used to generate an
+    // fd_set for calling select().
+
+    virtual void getSockets(std::list<network::Socket*>* sockets);
+
+
+    // -=- File interface
+
+    // - getFile is passed the path portion of a URL and returns an
+    //   InStream containing the data to return.  If the requested
+    //   file is available then the contentType should be set to the
+    //   type of the file, or left untouched if the file type is to
+    //   be determined automatically by HTTPServer.
+    //   If the file is not available then null is returned.
+    //   Overridden getFile functions should call the default version
+    //   if they do not recognise a path name.
+    //   NB: The caller assumes ownership of the returned InStream.
+    //   NB: The contentType is statically allocated by the getFile impl.
+    //   NB: contentType is *guaranteed* to be valid when getFile is called.
+
+    virtual rdr::InStream* getFile(const char* name, const char** contentType);
+
+    // - guessContentType is passed the name of a file and returns the
+    //   name of an HTTP content type, based on the file's extension.  If
+    //   the extension isn't recognised then defType is returned.  This can
+    //   be used from getFile to easily default to the supplied contentType,
+    //   or by passing zero in to determine whether a type is recognised or not.
+
+    static const char* guessContentType(const char* name, const char* defType);
+
+  protected:
+    class Session;
+    std::list<Session*> sessions;
+  };
+}
+
+#endif
+
diff --git a/rfb/HextileDecoder.cxx b/rfb/HextileDecoder.cxx
new file mode 100644
index 0000000..97c7ca7
--- /dev/null
+++ b/rfb/HextileDecoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/HextileDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileDecode.h>
+#undef BPP
+
+Decoder* HextileDecoder::create(CMsgReader* reader)
+{
+  return new HextileDecoder(reader);
+}
+
+HextileDecoder::HextileDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+HextileDecoder::~HextileDecoder()
+{
+}
+
+void HextileDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  rdr::U8* buf = reader->getImageBuf(16 * 16 * 4);
+  switch (reader->bpp()) {
+  case 8:  hextileDecode8 (r, is, (rdr::U8*) buf, handler); break;
+  case 16: hextileDecode16(r, is, (rdr::U16*)buf, handler); break;
+  case 32: hextileDecode32(r, is, (rdr::U32*)buf, handler); break;
+  }
+}
diff --git a/rfb/HextileDecoder.h b/rfb/HextileDecoder.h
new file mode 100644
index 0000000..718bd38
--- /dev/null
+++ b/rfb/HextileDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_HEXTILEDECODER_H__
+#define __RFB_HEXTILEDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class HextileDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~HextileDecoder();
+  private:
+    HextileDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/HextileEncoder.cxx b/rfb/HextileEncoder.cxx
new file mode 100644
index 0000000..736bd5f
--- /dev/null
+++ b/rfb/HextileEncoder.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/HextileEncoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileEncode.h>
+#undef BPP
+
+Encoder* HextileEncoder::create(SMsgWriter* writer)
+{
+  return new HextileEncoder(writer);
+}
+
+HextileEncoder::HextileEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+HextileEncoder::~HextileEncoder()
+{
+}
+
+bool HextileEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  writer->startRect(r, encodingHextile);
+  rdr::OutStream* os = writer->getOutStream();
+  switch (writer->bpp()) {
+  case 8:  hextileEncode8(r, os, ig);  break;
+  case 16: hextileEncode16(r, os, ig); break;
+  case 32: hextileEncode32(r, os, ig); break;
+  }
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/HextileEncoder.h b/rfb/HextileEncoder.h
new file mode 100644
index 0000000..f09ead8
--- /dev/null
+++ b/rfb/HextileEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_HEXTILEENCODER_H__
+#define __RFB_HEXTILEENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class HextileEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~HextileEncoder();
+  private:
+    HextileEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+  };
+}
+#endif
diff --git a/rfb/Hostname.h b/rfb/Hostname.h
new file mode 100644
index 0000000..bdff474
--- /dev/null
+++ b/rfb/Hostname.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RFB_HOSTNAME_H__
+#define __RFB_HOSTNAME_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
+    CharArray portBuf;
+    CharArray hostBuf;
+    if (hi[0] == '[') {
+      if (!strSplit(&hi[1], ']', &hostBuf.buf, &portBuf.buf))
+        throw rdr::Exception("unmatched [ in host");
+    } else {
+      portBuf.buf = strDup(hi);
+    }
+    if (strSplit(portBuf.buf, ':', hostBuf.buf ? 0 : &hostBuf.buf, &portBuf.buf)) {
+      if (portBuf.buf[0] == ':') {
+        *port = atoi(&portBuf.buf[1]);
+      } else {
+        *port = atoi(portBuf.buf);
+        if (*port < 100) *port += basePort;
+      }
+    } else {
+      *port = basePort;
+    }
+    if (strlen(hostBuf.buf) == 0)
+      *host = strDup("localhost");
+    else
+      *host = hostBuf.takeBuf();
+  }
+
+};
+
+#endif // __RFB_HOSTNAME_H__
diff --git a/rfb/ImageGetter.h b/rfb/ImageGetter.h
new file mode 100644
index 0000000..b550a12
--- /dev/null
+++ b/rfb/ImageGetter.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_IMAGEGETTER_H__
+#define __RFB_IMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+
+namespace rfb {
+  class ImageGetter {
+  public:
+    virtual void getImage(void* imageBuf,
+                          const Rect& r, int stride=0) = 0;
+  };
+}
+#endif
diff --git a/rfb/LogWriter.cxx b/rfb/LogWriter.cxx
new file mode 100644
index 0000000..6f267f1
--- /dev/null
+++ b/rfb/LogWriter.cxx
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- LogWriter.cxx - client-side logging interface
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+#include <stdlib.h>
+
+rfb::LogParameter rfb::logParams;
+
+using namespace rfb;
+
+
+LogWriter::LogWriter(const char* name) : m_name(name), m_level(0), m_log(0), m_next(log_writers) {
+  log_writers = this;
+}
+
+LogWriter::~LogWriter() {
+  // *** Should remove this logger here!
+}
+
+void LogWriter::setLog(Logger *logger) {
+  m_log = logger;
+}
+
+void LogWriter::setLevel(int level) {
+  m_level = level;
+}
+
+void
+LogWriter::listLogWriters(int width) {
+  // *** make this respect width...
+  LogWriter* current = log_writers;
+  printf("  ");
+  while (current) {
+    printf("%s", current->m_name);
+    current = current->m_next;
+    if (current) printf(", ");
+  }
+  printf("\n");
+}
+
+LogWriter* LogWriter::log_writers;
+
+LogWriter*
+LogWriter::getLogWriter(const char* name) {
+  LogWriter* current = log_writers;
+  while (current) {
+    if (strcasecmp(name, current->m_name) == 0) return current;
+      current = current->m_next;
+    }
+  return 0;
+}
+
+bool LogWriter::setLogParams(const char* params) {
+  CharArray logwriterName, loggerName, logLevel;
+  if (!strSplit(params, ':', &logwriterName.buf, &loggerName.buf) ||
+    !strSplit(loggerName.buf, ':', &loggerName.buf, &logLevel.buf)) {
+    fprintf(stderr,"failed to parse log params:%s\n",params);
+    return false;
+  }
+  int level = atoi(logLevel.buf);
+  Logger* logger = 0;
+  if (strcmp("", loggerName.buf) != 0) {
+    logger = Logger::getLogger(loggerName.buf);
+    if (!logger) fprintf(stderr,"no logger found! %s\n",loggerName.buf);
+  }
+  if (strcmp("*", logwriterName.buf) == 0) {
+    LogWriter* current = log_writers;
+    while (current) {
+      current->setLog(logger);
+      current->setLevel(level);
+      current = current->m_next;
+    }
+    return true;
+  } else {
+    LogWriter* logwriter = getLogWriter(logwriterName.buf);
+    if (!logwriter) {
+      fprintf(stderr,"no logwriter found! %s\n",logwriterName.buf);
+    } else {
+      logwriter->setLog(logger);
+      logwriter->setLevel(level);
+      return true;
+    }
+  }
+  return false;
+}
+
+
+LogParameter::LogParameter()
+  : StringParameter("Log",
+    "Specifies which log output should be directed to "
+    "which target logger, and the level of output to log. "
+    "Format is <log>:<target>:<level>[, ...].",
+    "") {
+}
+
+bool LogParameter::setParam(const char* v) {
+  if (immutable) return true;
+  LogWriter::setLogParams("*::0");
+  StringParameter::setParam(v);
+  CharArray logParam;
+  CharArray params(getData());
+  while (params.buf) {
+    strSplit(params.buf, ',', &logParam.buf, &params.buf);
+    if (strlen(logParam.buf) && !LogWriter::setLogParams(logParam.buf))
+      return false;
+  }
+  return true;
+}
+
+void LogParameter::setDefault(const char* d) {
+  def_value = d;
+  setParam(def_value);
+}
diff --git a/rfb/LogWriter.h b/rfb/LogWriter.h
new file mode 100644
index 0000000..58e81f2
--- /dev/null
+++ b/rfb/LogWriter.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- LogWriter.h - The Log writer class.
+
+#ifndef __RFB_LOG_WRITER_H__
+#define __RFB_LOG_WRITER_H__
+
+#include <stdarg.h>
+#include <rfb/Logger.h>
+#include <rfb/Configuration.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Log instance and
+// is assigned a particular log level.
+
+#define DEF_LOGFUNCTION(name, level) \
+  inline void name(const char* fmt, ...) { \
+    if (m_log && (level <= m_level)) {     \
+      va_list ap; va_start(ap, fmt);       \
+      m_log->write(level, m_name, fmt, ap);\
+      va_end(ap);                          \
+    }                                      \
+  }
+
+namespace rfb {
+
+  class LogWriter;
+
+  class LogWriter {
+  public:
+    LogWriter(const char* name);
+    ~LogWriter();
+
+    const char *getName() {return m_name;}
+
+    void setLog(Logger *logger);
+    void setLevel(int level);
+
+    inline void write(int level, const char* format, ...) {
+      if (m_log && (level <= m_level)) {
+        va_list ap;
+        va_start(ap, format);
+        m_log->write(level, m_name, format, ap);
+        va_end(ap);
+      }
+    }
+
+    DEF_LOGFUNCTION(error, 0)
+    DEF_LOGFUNCTION(status, 10)
+    DEF_LOGFUNCTION(info, 30)
+    DEF_LOGFUNCTION(debug, 100)
+
+    // -=- DIAGNOSTIC & HELPER ROUTINES
+
+    static void listLogWriters(int width=79);
+
+    // -=- CLASS FIELDS & FUNCTIONS
+
+    static LogWriter* log_writers;
+
+    static LogWriter* getLogWriter(const char* name);
+
+    static bool setLogParams(const char* params);
+
+  private:
+    const char* m_name;
+    int m_level;
+    Logger* m_log;
+    LogWriter* m_next;
+  };
+
+  class LogParameter : public StringParameter {
+  public:
+    LogParameter();
+    virtual bool setParam(const char* v);
+
+    // Call this to set a suitable default value.
+    // Can't use the normal default mechanism for
+    // this because there is no guarantee on C++
+    // constructor ordering - some LogWriters may
+    // not exist when LogParameter gets constructed.
+    // NB: The default value must exist for the
+    //     lifetime of the process!
+    void setDefault(const char* v);
+  };
+  extern LogParameter logParams;
+
+};
+
+#endif // __RFB_LOG_WRITER_H__
diff --git a/rfb/Logger.cxx b/rfb/Logger.cxx
new file mode 100644
index 0000000..84b8f55
--- /dev/null
+++ b/rfb/Logger.cxx
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger.cxx - support for the Logger and LogWriter classes
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define vsnprintf _vsnprintf
+#define HAVE_VSNPRINTF
+#endif
+
+#include <rfb/Logger.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+#ifndef HAVE_VSNPRINTF
+static int vsnprintf(char *str, size_t n, const char *format, va_list ap)
+{
+  str[0] = 0;
+  FILE* fp = fopen("/dev/null","w");
+  if (!fp) return 0;
+  int len = vfprintf(fp, format, ap);
+  if (len <= 0) return 0;
+  fclose(fp);
+
+  CharArray s(len+1);
+  vsprintf(s.buf, format, ap);
+
+  if (len > (int)n-1) len = n-1;
+  memcpy(str, s.buf, len);
+  str[len] = 0;
+  return len;
+}
+#endif
+
+
+Logger* Logger::loggers = 0;
+
+Logger::Logger(const char* name) : registered(false), m_name(name), m_next(0) {
+}
+
+Logger::~Logger() {
+  // *** Should remove this logger here!
+}
+
+void Logger::write(int level, const char *logname, const char* format,
+                   va_list ap)
+{
+  // - Format the supplied data, and pass it to the
+  //   actual log_message function
+  //   The log level is included as a hint for loggers capable of representing
+  //   different log levels in some way.
+  char buf1[4096];
+  vsnprintf(buf1, sizeof(buf1)-1, format, ap);
+  buf1[sizeof(buf1)-1] = 0;
+  write(level, logname, buf1);
+}
+
+void
+Logger::registerLogger() {
+  if (!registered) {
+    registered = true;
+    m_next = loggers;
+    loggers=this;
+  }
+}
+    
+Logger*
+Logger::getLogger(const char* name) {
+  Logger* current = loggers;
+  while (current) {
+    if (strcasecmp(name, current->m_name) == 0) return current;
+    current = current->m_next;
+  }
+  return 0;
+}
+
+void
+Logger::listLoggers() {
+  Logger* current = loggers;
+  while (current) {
+    printf("  %s\n", current->m_name);
+    current = current->m_next;
+  }
+}
+
+
diff --git a/rfb/Logger.h b/rfb/Logger.h
new file mode 100644
index 0000000..4233964
--- /dev/null
+++ b/rfb/Logger.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger.h - The Logger class.
+
+#ifndef __RFB_LOGGER_H__
+#define __RFB_LOGGER_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Logger instance and
+// is assigned a particular log level.
+
+namespace rfb {
+
+  class Logger {
+  public:
+
+    // -=- Create / Destroy a logger
+
+    Logger(const char* name);
+    virtual ~Logger();
+
+    // -=- Get the name of a logger
+
+    const char *getName() {return m_name;}
+
+    // -=- Write data to a log
+
+    virtual void write(int level, const char *logname, const char *text) = 0;
+    void write(int level, const char *logname, const char* format, va_list ap);
+
+    // -=- Register a logger
+
+    void registerLogger();
+
+    // -=- CLASS FIELDS & FUNCTIONS
+
+    static Logger* loggers;
+
+    static Logger* getLogger(const char* name);
+
+    static void listLoggers();
+
+  private:
+    bool registered;
+    const char *m_name;
+    Logger *m_next;
+  };
+
+};
+
+#endif // __RFB_LOGGER_H__
diff --git a/rfb/Logger_file.cxx b/rfb/Logger_file.cxx
new file mode 100644
index 0000000..ac249b3
--- /dev/null
+++ b/rfb/Logger_file.cxx
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger_file.cxx - Logger instance for a file
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <rfb/util.h>
+#include <rfb/Logger_file.h>
+#include <rfb/Threading.h>
+
+using namespace rfb;
+
+
+// If threading is available then protect the write() operation
+// from concurrent accesses
+#ifdef __RFB_THREADING_IMPL
+static Mutex logLock;
+#endif
+
+
+Logger_File::Logger_File(const char* loggerName)
+  : Logger(loggerName), indent(13), width(79), m_filename(0), m_file(0),
+    m_lastLogTime(0)
+{
+}
+
+Logger_File::~Logger_File()
+{
+  closeFile();
+}
+
+void Logger_File::write(int level, const char *logname, const char *message)
+{
+#ifdef __RFB_THREADING_IMPL
+  Lock l(logLock);
+#endif
+  if (!m_file) {
+    if (!m_filename) return;
+    m_file = fopen(m_filename, "w+");
+    if (!m_file) return;
+  }
+
+#ifndef _WIN32_WCE
+  time_t current = time(0);
+  if (current != m_lastLogTime) {
+    m_lastLogTime = current;
+    fprintf(m_file, "\n%s", ctime(&m_lastLogTime));
+  }
+#endif
+
+  fprintf(m_file," %s:", logname);
+  int column = strlen(logname) + 2;
+  if (column < indent) {
+    fprintf(m_file,"%*s",indent-column,"");
+    column = indent;
+  }
+  while (true) {
+    const char* s = strchr(message, ' ');
+    int wordLen;
+    if (s) wordLen = s-message;
+    else wordLen = strlen(message);
+
+    if (column + wordLen + 1 > width) {
+      fprintf(m_file,"\n%*s",indent,"");
+      column = indent;
+    }
+    fprintf(m_file," %.*s",wordLen,message);
+    column += wordLen + 1;
+    message += wordLen + 1;
+    if (!s) break;
+  }
+  fprintf(m_file,"\n");
+  fflush(m_file);
+}
+
+void Logger_File::setFilename(const char* filename)
+{
+  closeFile();
+  m_filename = strDup(filename);
+}
+
+void Logger_File::setFile(FILE* file)
+{
+  closeFile();
+  m_file = file;
+}
+
+void Logger_File::closeFile()
+{
+  if (m_filename) {
+    if (m_file) {
+      fclose(m_file);
+      m_file = 0;
+    }
+    strFree(m_filename);
+    m_filename = 0;
+  }
+}
+
+static Logger_File logger("file");
+
+bool rfb::initFileLogger(const char* filename) {
+  logger.setFilename(filename);
+  logger.registerLogger();
+  return true;
+}
diff --git a/rfb/Logger_file.h b/rfb/Logger_file.h
new file mode 100644
index 0000000..30c3f40
--- /dev/null
+++ b/rfb/Logger_file.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger_file - log to a file
+
+#ifndef __RFB_LOGGER_FILE_H__
+#define __RFB_LOGGER_FILE_H__
+
+#include <time.h>
+#include <rfb/Logger.h>
+
+namespace rfb {
+
+  class Logger_File : public Logger {
+  public:
+    Logger_File(const char* loggerName);
+    ~Logger_File();
+
+    virtual void write(int level, const char *logname, const char *message);
+    void setFilename(const char* filename);
+    void setFile(FILE* file);
+
+    int indent;
+    int width;
+
+  protected:
+    void closeFile();
+    char* m_filename;
+    FILE* m_file;
+    time_t m_lastLogTime;
+  };
+
+  bool initFileLogger(const char* filename);
+};
+
+#endif
diff --git a/rfb/Logger_stdio.cxx b/rfb/Logger_stdio.cxx
new file mode 100644
index 0000000..ac9556e
--- /dev/null
+++ b/rfb/Logger_stdio.cxx
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger_stdio.cxx - Logger instances for stderr and stdout
+
+#include <rfb/Logger_stdio.h>
+
+using namespace rfb;
+
+static Logger_StdIO logStdErr("stderr", stderr);
+static Logger_StdIO logStdOut("stdout", stdout);
+
+bool rfb::initStdIOLoggers() {
+  logStdErr.registerLogger();
+  logStdOut.registerLogger();
+  return true;
+}
diff --git a/rfb/Logger_stdio.h b/rfb/Logger_stdio.h
new file mode 100644
index 0000000..68f795f
--- /dev/null
+++ b/rfb/Logger_stdio.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Logger_stdio - standard output logger instances
+
+#ifndef __RFB_LOGGER_STDIO_H__
+#define __RFB_LOGGER_STDIO_H__
+
+#include <rfb/Logger_file.h>
+
+namespace rfb {
+
+  class Logger_StdIO : public Logger_File {
+  public:
+    Logger_StdIO(const char *name, FILE* file) : Logger_File(name) {
+      setFile(file);
+    }
+  };
+
+  bool initStdIOLoggers();
+
+};
+
+#endif
diff --git a/rfb/Makefile.in b/rfb/Makefile.in
new file mode 100644
index 0000000..50bc04c
--- /dev/null
+++ b/rfb/Makefile.in
@@ -0,0 +1,66 @@
+
+CXXSRCS = \
+  Blacklist.cxx \
+  CConnection.cxx \
+  CMsgHandler.cxx \
+  CMsgReader.cxx \
+  CMsgReaderV3.cxx \
+  CMsgWriter.cxx \
+  CMsgWriterV3.cxx \
+  CSecurityVncAuth.cxx \
+  ComparingUpdateTracker.cxx \
+  Configuration.cxx \
+  ConnParams.cxx \
+  Cursor.cxx \
+  Decoder.cxx \
+  Encoder.cxx \
+  HTTPServer.cxx \
+  HextileDecoder.cxx \
+  HextileEncoder.cxx \
+  LogWriter.cxx \
+  Logger.cxx \
+  Logger_file.cxx \
+  Logger_stdio.cxx \
+  PixelBuffer.cxx \
+  PixelFormat.cxx \
+  RREEncoder.cxx \
+  RREDecoder.cxx \
+  RawDecoder.cxx \
+  RawEncoder.cxx \
+  Region.cxx \
+  SConnection.cxx \
+  SMsgHandler.cxx \
+  SMsgReader.cxx \
+  SMsgReaderV3.cxx \
+  SMsgWriter.cxx \
+  SMsgWriterV3.cxx \
+  ServerCore.cxx \
+  SSecurityFactoryStandard.cxx \
+  SSecurityVncAuth.cxx \
+  TransImageGetter.cxx \
+  UpdateTracker.cxx \
+  VNCSConnectionST.cxx \
+  VNCServerST.cxx \
+  ZRLEEncoder.cxx \
+  ZRLEDecoder.cxx \
+  encodings.cxx \
+  secTypes.cxx \
+  util.cxx \
+  vncAuth.cxx
+
+SRCS = d3des.c $(CXXSRCS)
+
+OBJS = d3des.o $(CXXSRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @VSNPRINTF_DEFINE@
+
+library = librfb.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/rfb/Pixel.h b/rfb/Pixel.h
new file mode 100644
index 0000000..2b1aaf0
--- /dev/null
+++ b/rfb/Pixel.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_PIXEL_H__
+#define __RFB_PIXEL_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+  typedef rdr::U32 Pixel; // must be big enough to hold any pixel value
+}
+#endif
diff --git a/rfb/PixelBuffer.cxx b/rfb/PixelBuffer.cxx
new file mode 100644
index 0000000..fcad227
--- /dev/null
+++ b/rfb/PixelBuffer.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- PixelBuffer.cxx
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/PixelBuffer.h>
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("PixelBuffer");
+
+
+// -=- Generic pixel buffer class
+
+PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h, ColourMap* cm)
+  : format(pf), width_(w), height_(h), colourmap(cm) {}
+PixelBuffer::PixelBuffer() : width_(0), height_(0), colourmap(0) {}
+
+PixelBuffer::~PixelBuffer() {}
+
+
+void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;}
+const PixelFormat& PixelBuffer::getPF() const {return format;}
+ColourMap* PixelBuffer::getColourMap() const {return colourmap;}
+
+
+void
+PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
+  int inStride;
+  const U8* data = getPixelsR(r, &inStride);
+  // We assume that the specified rectangle is pre-clipped to the buffer
+  int bytesPerPixel = format.bpp/8;
+  int inBytesPerRow = inStride * bytesPerPixel;
+  if (!outStride) outStride = r.width();
+  int outBytesPerRow = outStride * bytesPerPixel;
+  int bytesPerMemCpy = r.width() * bytesPerPixel;
+  U8* imageBufPos = (U8*)imageBuf;
+  const U8* end = data + (inBytesPerRow * r.height());
+  while (data < end) {
+    memcpy(imageBufPos, data, bytesPerMemCpy);
+    imageBufPos += outBytesPerRow;
+    data += inBytesPerRow;
+  }
+}
+
+/* ***
+Pixel PixelBuffer::getPixel(const Point& p) {
+  int stride;
+  Rect r = Rect(p.x, p.y, p.x+1, p.y+1);
+  switch(format.bpp) {
+  case 8: return *((rdr::U8*)getDataAt(r, &stride));
+  case 16: return *((rdr::U16*)getDataAt(r, &stride));
+  case 32: return *((rdr::U32*)getDataAt(r, &stride));
+  default: return 0;
+  };
+}
+*/
+
+
+FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
+                                           rdr::U8* data_, ColourMap* cm)
+  : PixelBuffer(pf, w, h, cm), data(data_)
+{
+}
+
+FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
+
+FullFramePixelBuffer::~FullFramePixelBuffer() {}
+
+
+int FullFramePixelBuffer::getStride() const { return width(); }
+
+rdr::U8* FullFramePixelBuffer::getPixelsRW(const Rect& r, int* stride)
+{
+  *stride = getStride();
+  return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
+}
+
+
+void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
+  int stride;
+  U8* data = getPixelsRW(r, &stride);
+  int bytesPerPixel = getPF().bpp/8;
+  int bytesPerRow = bytesPerPixel * stride;
+  int bytesPerFill = bytesPerPixel * r.width();
+
+  U8* end = data + (bytesPerRow * r.height());
+  while (data < end) {
+    switch (bytesPerPixel) {
+    case 1:
+      memset(data, pix, bytesPerFill);
+      break;
+    case 2:
+      {
+        U16* optr = (U16*)data;
+        U16* eol = optr + r.width();
+        while (optr < eol)
+          *optr++ = pix;
+      }
+      break;
+    case 4:
+      {
+        U32* optr = (U32*)data;
+        U32* eol = optr + r.width();
+        while (optr < eol)
+          *optr++ = pix;
+      }
+      break;
+    }
+    data += bytesPerRow;
+  }
+}
+
+void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
+  int bytesPerPixel = getPF().bpp/8;
+  int destStride;
+  U8* dest = getPixelsRW(r, &destStride);
+  int bytesPerDestRow = bytesPerPixel * destStride;
+  if (!srcStride) srcStride = r.width();
+  int bytesPerSrcRow = bytesPerPixel * srcStride;
+  int bytesPerFill = bytesPerPixel * r.width();
+  const U8* src = (const U8*)pixels;
+  U8* end = dest + (bytesPerDestRow * r.height());
+  while (dest < end) {
+    memcpy(dest, src, bytesPerFill);
+    dest += bytesPerDestRow;
+    src += bytesPerSrcRow;
+  }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
+  Rect cr = getRect().intersect(r);
+  if (cr.is_empty()) return;
+  int stride;
+  U8* data = getPixelsRW(cr, &stride);
+  U8* mask = (U8*) mask_;
+  int w = cr.width();
+  int h = cr.height();
+  int bpp = getPF().bpp;
+  int pixelStride = r.width();
+  int maskStride = (r.width() + 7) / 8;
+
+  Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+  mask += offset.y * maskStride;
+  for (int y = 0; y < h; y++) {
+    int cy = offset.y + y;
+    for (int x = 0; x < w; x++) {
+      int cx = offset.x + x;
+      U8* byte = mask + (cx / 8);
+      int bit = 7 - cx % 8;
+      if ((*byte) & (1 << bit)) {
+        switch (bpp) {
+        case 8:
+          ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
+          break;
+        case 16:
+          ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
+          break;
+        case 32:
+          ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
+          break;
+        }
+      }
+    }
+    mask += maskStride;
+  }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
+  Rect cr = getRect().intersect(r);
+  if (cr.is_empty()) return;
+  int stride;
+  U8* data = getPixelsRW(cr, &stride);
+  U8* mask = (U8*) mask_;
+  int w = cr.width();
+  int h = cr.height();
+  int bpp = getPF().bpp;
+  int maskStride = (r.width() + 7) / 8;
+
+  Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+  mask += offset.y * maskStride;
+  for (int y = 0; y < h; y++) {
+    for (int x = 0; x < w; x++) {
+      int cx = offset.x + x;
+      U8* byte = mask + (cx / 8);
+      int bit = 7 - cx % 8;
+      if ((*byte) & (1 << bit)) {
+        switch (bpp) {
+        case 8:
+          ((U8*)data)[y * stride + x] = pixel;
+          break;
+        case 16:
+          ((U16*)data)[y * stride + x] = pixel;
+          break;
+        case 32:
+          ((U32*)data)[y * stride + x] = pixel;
+          break;
+        }
+      }
+    }
+    mask += maskStride;
+  }
+}
+
+void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
+  int stride;
+  U8* data = getPixelsRW(getRect(), &stride);
+  // We assume that the specified rectangle is pre-clipped to the buffer
+  unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
+  Rect srect = rect.translate(move_by_delta.negate());
+  bytesPerPixel = getPF().bpp/8;
+  bytesPerRow = stride * bytesPerPixel;
+  bytesPerMemCpy = rect.width() * bytesPerPixel;
+  if (move_by_delta.y <= 0) {
+    U8* dest = data + rect.tl.x*bytesPerPixel + rect.tl.y*bytesPerRow;
+    U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
+    for (int i=rect.tl.y; i<rect.br.y; i++) {
+      memmove(dest, src, bytesPerMemCpy);
+      dest += bytesPerRow;
+      src += bytesPerRow;
+    }
+  } else {
+    U8* dest = data + rect.tl.x*bytesPerPixel + (rect.br.y-1)*bytesPerRow;
+    U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
+    for (int i=rect.tl.y; i<rect.br.y; i++) {
+      memmove(dest, src, bytesPerMemCpy);
+      dest -= bytesPerRow;
+      src -= bytesPerRow;
+    }
+  }
+}
+
+
+// -=- Managed pixel buffer class
+// Automatically allocates enough space for the specified format & area
+
+ManagedPixelBuffer::ManagedPixelBuffer()
+  : datasize(0), own_colourmap(false)
+{
+  checkDataSize();
+};
+
+ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
+  : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false)
+{
+  checkDataSize();
+};
+
+ManagedPixelBuffer::~ManagedPixelBuffer() {
+  if (data) delete [] data;
+  if (colourmap && own_colourmap) delete colourmap;
+};
+
+
+void
+ManagedPixelBuffer::setPF(const PixelFormat &pf) {
+  format = pf; checkDataSize();
+};
+void
+ManagedPixelBuffer::setSize(int w, int h) {
+  width_ = w; height_ = h; checkDataSize();
+};
+
+
+void
+ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) {
+  if (colourmap && own_colourmap) delete colourmap;
+  colourmap = cm;
+  own_colourmap = own_cm;
+}
+
+inline void
+ManagedPixelBuffer::checkDataSize() {
+  unsigned long new_datasize = width_ * height_ * (format.bpp/8);
+  if (datasize < new_datasize) {
+    vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
+    if (data) {
+      delete [] data;
+      datasize = 0; data = 0;
+    }
+    if (new_datasize) {
+      data = new U8[new_datasize];
+      if (!data)
+        throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
+      datasize = new_datasize;
+    }
+  }
+};
diff --git a/rfb/PixelBuffer.h b/rfb/PixelBuffer.h
new file mode 100644
index 0000000..2ba105a
--- /dev/null
+++ b/rfb/PixelBuffer.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- PixelBuffer.h
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#ifndef __RFB_PIXEL_BUFFER_H__
+#define __RFB_PIXEL_BUFFER_H__
+
+#include <rfb/ImageGetter.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Rect.h>
+#include <rfb/Pixel.h>
+
+namespace rfb {
+
+  class Region;
+
+  class PixelBuffer : public ImageGetter {
+  public:
+    PixelBuffer(const PixelFormat& pf, int width, int height, ColourMap* cm);
+    virtual ~PixelBuffer();
+
+    ///////////////////////////////////////////////
+    // Format / Layout
+    //
+
+    // Set/get pixel format & colourmap
+    virtual void setPF(const PixelFormat &pf);
+    virtual const PixelFormat &getPF() const;
+    virtual ColourMap* getColourMap() const;
+
+    // Get width, height and number of pixels
+    int width()  const { return width_; }
+    int height() const { return height_; }
+    int area() const { return width_ * height_; }
+
+    // Get rectangle encompassing this buffer
+    //   Top-left of rectangle is either at (0,0), or the specified point.
+    Rect getRect() const { return Rect(0, 0, width_, height_); }
+    Rect getRect(const Point& pos) const {
+      return Rect(pos, pos.translate(Point(width_, height_)));
+    }
+
+    ///////////////////////////////////////////////
+    // Access to pixel data
+    //
+
+    // Get a pointer into the buffer
+    //   The pointer is to the top-left pixel of the specified Rect.
+    //   The buffer stride (in pixels) is returned.
+    virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) = 0;
+
+    // Get pixel data for a given part of the buffer
+    //   Data is copied into the supplied buffer, with the specified
+    //   stride.
+    virtual void getImage(void* imageBuf, const Rect& r, int stride=0);
+
+    // Get the data at (x,y) as a Pixel.
+    //   VERY INEFFICIENT!!!
+    // *** Pixel getPixel(const Point& p);
+
+    ///////////////////////////////////////////////
+    // Framebuffer update methods
+    //
+
+    // Ensure that the specified rectangle of buffer is up to date.
+    //   Overridden by derived classes implementing framebuffer access
+    //   to copy the required display data into place.
+    virtual void grabRegion(const Region& region) {}
+
+  protected:
+    PixelBuffer();
+    PixelFormat format;
+    int width_, height_;
+    ColourMap* colourmap;
+  };
+
+  // FullFramePixelBuffer
+
+  class FullFramePixelBuffer : public PixelBuffer {
+  public:
+    FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
+                         rdr::U8* data_, ColourMap* cm);
+    virtual ~FullFramePixelBuffer();
+
+    // - Get the number of pixels per row in the actual pixel buffer data area
+    //   This may in some cases NOT be the same as width().
+    virtual int getStride() const;
+
+    // Get a pointer to specified pixel data
+    virtual rdr::U8* getPixelsRW(const Rect& r, int* stride);
+    virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) {
+      return getPixelsRW(r, stride);
+    }
+
+    ///////////////////////////////////////////////
+    // Basic rendering operations
+    // These operations DO NOT clip to the pixelbuffer area, or trap overruns.
+
+    // Fill a rectangle
+    virtual void fillRect(const Rect &dest, Pixel pix);
+
+    // Copy pixel data to the buffer
+    virtual void imageRect(const Rect &dest, const void* pixels, int stride=0);
+
+    // Copy pixel data from one PixelBuffer location to another
+    virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+
+    // Copy pixel data to the buffer through a mask
+    //   pixels is a pointer to the pixel to be copied to r.tl.
+    //   maskPos specifies the pixel offset in the mask to start from.
+    //   mask_ is a pointer to the mask bits at (0,0).
+    //   pStride and mStride are the strides of the pixel and mask buffers.
+    virtual void maskRect(const Rect& r, const void* pixels, const void* mask_);
+
+    //   pixel is the Pixel value to be used where mask_ is set
+    virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_);
+
+    // *** Should this be visible?
+    rdr::U8* data;
+
+  protected:
+    FullFramePixelBuffer();
+  };
+
+  // -=- Managed pixel buffer class
+  // Automatically allocates enough space for the specified format & area
+
+  class ManagedPixelBuffer : public FullFramePixelBuffer {
+  public:
+    ManagedPixelBuffer();
+    ManagedPixelBuffer(const PixelFormat& pf, int width, int height);
+    virtual ~ManagedPixelBuffer();
+
+    // Manage the pixel buffer layout
+    virtual void setPF(const PixelFormat &pf);
+    virtual void setSize(int w, int h);
+
+    // Assign a colour map to the buffer
+    virtual void setColourMap(ColourMap* cm, bool own_cm);
+
+    // Return the total number of bytes of pixel data in the buffer
+    int dataLen() const { return width_ * height_ * (format.bpp/8); }
+
+  protected:
+    unsigned long datasize;
+    bool own_colourmap;
+    void checkDataSize();
+  };
+
+};
+
+#endif // __RFB_PIXEL_BUFFER_H__
diff --git a/rfb/PixelFormat.cxx b/rfb/PixelFormat.cxx
new file mode 100644
index 0000000..683b53a
--- /dev/null
+++ b/rfb/PixelFormat.cxx
@@ -0,0 +1,238 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/PixelFormat.h>
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+using namespace rfb;
+
+PixelFormat::PixelFormat(int b, int d, bool e, bool t,
+                         int rm, int gm, int bm, int rs, int gs, int bs)
+  : bpp(b), depth(d), bigEndian(e), trueColour(t),
+    redMax(rm), greenMax(gm), blueMax(bm),
+    redShift(rs), greenShift(gs), blueShift(bs)
+{
+}
+
+PixelFormat::PixelFormat()
+  : bpp(8), depth(8), bigEndian(false), trueColour(true),
+    redMax(7), greenMax(7), blueMax(3),
+    redShift(0), greenShift(3), blueShift(6)
+{
+}
+
+bool PixelFormat::equal(const PixelFormat& other) const
+{
+  return (bpp == other.bpp &&
+          depth == other.depth &&
+          (bigEndian == other.bigEndian || bpp == 8) &&
+          trueColour == other.trueColour &&
+          (!trueColour || (redMax == other.redMax &&
+                           greenMax == other.greenMax &&
+                           blueMax == other.blueMax &&
+                           redShift == other.redShift &&
+                           greenShift == other.greenShift &&
+                           blueShift == other.blueShift)));
+}
+
+void PixelFormat::read(rdr::InStream* is)
+{
+  bpp = is->readU8();
+  depth = is->readU8();
+  bigEndian = is->readU8();
+  trueColour = is->readU8();
+  redMax = is->readU16();
+  greenMax = is->readU16();
+  blueMax = is->readU16();
+  redShift = is->readU8();
+  greenShift = is->readU8();
+  blueShift = is->readU8();
+  is->skip(3);
+}
+
+void PixelFormat::write(rdr::OutStream* os) const
+{
+  os->writeU8(bpp);
+  os->writeU8(depth);
+  os->writeU8(bigEndian);
+  os->writeU8(trueColour);
+  os->writeU16(redMax);
+  os->writeU16(greenMax);
+  os->writeU16(blueMax);
+  os->writeU8(redShift);
+  os->writeU8(greenShift);
+  os->writeU8(blueShift);
+  os->pad(3);
+}
+
+Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue,
+                                ColourMap* cm) const
+{
+  if (trueColour) {
+    rdr::U32 r = ((rdr::U32)red   * redMax   + 32767) / 65535;
+    rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535;
+    rdr::U32 b = ((rdr::U32)blue  * blueMax  + 32767) / 65535;
+
+    return (r << redShift) | (g << greenShift) | (b << blueShift);
+  } else if (cm) {
+    // Try to find the closest pixel by Cartesian distance
+    int colours = 1 << depth;
+    int diff = 256 * 256 * 4;
+    int col = 0;
+    for (int i=0; i<colours; i++) {
+      int r, g, b;
+      cm->lookup(i, &r, &g, &b);
+      int rd = (r-red) >> 8;
+      int gd = (g-green) >> 8;
+      int bd = (b-blue) >> 8;
+      int d = rd*rd + gd*gd + bd*bd;
+      if (d < diff) {
+        col = i;
+        diff = d;
+      }
+    }
+    return col;
+  }
+  // XXX just return 0 for colour map?
+  return 0;
+}
+
+
+void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
+{
+  if (trueColour) {
+    rgb->r = (((p >> redShift  ) & redMax  ) * 65535 + redMax  /2) / redMax;
+    rgb->g = (((p >> greenShift) & greenMax) * 65535 + greenMax/2) / greenMax;
+    rgb->b = (((p >> blueShift ) & blueMax ) * 65535 + blueMax /2) / blueMax;
+  } else {
+    cm->lookup(p, &rgb->r, &rgb->g, &rgb->b);
+  }
+}
+
+
+void PixelFormat::print(char* str, int len) const
+{
+  // Unfortunately snprintf is not widely available so we build the string up
+  // using strncat - not pretty, but should be safe against buffer overruns.
+
+  char num[20];
+  if (len < 1) return;
+  str[0] = 0;
+  strncat(str, "depth ", len-1-strlen(str));
+  sprintf(num,"%d",depth);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, " (", len-1-strlen(str));
+  sprintf(num,"%d",bpp);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, "bpp)", len-1-strlen(str));
+  if (bpp != 8) {
+    if (bigEndian)
+      strncat(str, " big-endian", len-1-strlen(str));
+    else
+      strncat(str, " little-endian", len-1-strlen(str));
+  }
+
+  if (!trueColour) {
+    strncat(str, " colour-map", len-1-strlen(str));
+    return;
+  }
+
+  if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
+      blueMax  == (1 << greenShift) - 1 &&
+      greenMax == (1 << (redShift-greenShift)) - 1 &&
+      redMax   == (1 << (depth-redShift)) - 1)
+  {
+    strncat(str, " rgb", len-1-strlen(str));
+    sprintf(num,"%d",depth-redShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",redShift-greenShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",greenShift);
+    strncat(str, num, len-1-strlen(str));
+    return;
+  }
+
+  if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
+      redMax   == (1 << greenShift) - 1 &&
+      greenMax == (1 << (blueShift-greenShift)) - 1 &&
+      blueMax  == (1 << (depth-blueShift)) - 1)
+  {
+    strncat(str, " bgr", len-1-strlen(str));
+    sprintf(num,"%d",depth-blueShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",blueShift-greenShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",greenShift);
+    strncat(str, num, len-1-strlen(str));
+    return;
+  }
+
+  strncat(str, " rgb max ", len-1-strlen(str));
+  sprintf(num,"%d,",redMax);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d,",greenMax);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d",blueMax);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, " shift ", len-1-strlen(str));
+  sprintf(num,"%d,",redShift);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d,",greenShift);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d",blueShift);
+  strncat(str, num, len-1-strlen(str));
+}
+
+
+bool PixelFormat::parse(const char* str)
+{
+  char rgbbgr[4];
+  int bits1, bits2, bits3;
+  if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
+    return false;
+  
+  depth = bits1 + bits2 + bits3;
+  bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
+  trueColour = true;
+  rdr::U32 endianTest = 1;
+  bigEndian = (*(rdr::U8*)&endianTest == 0);
+
+  greenShift = bits3;
+  greenMax = (1 << bits2) - 1;
+
+  if (strcasecmp(rgbbgr, "bgr") == 0) {
+    redShift = 0;
+    redMax = (1 << bits3) - 1;
+    blueShift = bits3 + bits2;
+    blueMax = (1 << bits1) - 1;
+  } else if (strcasecmp(rgbbgr, "rgb") == 0) {
+    blueShift = 0;
+    blueMax = (1 << bits3) - 1;
+    redShift = bits3 + bits2;
+    redMax = (1 << bits1) - 1;
+  } else {
+    return false;
+  }
+  return true;
+}
diff --git a/rfb/PixelFormat.h b/rfb/PixelFormat.h
new file mode 100644
index 0000000..0f2edd2
--- /dev/null
+++ b/rfb/PixelFormat.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// PixelFormat - structure to represent a pixel format.  Also has useful
+// methods for reading & writing to streams, etc.
+//
+
+#ifndef __RFB_PIXELFORMAT_H__
+#define __RFB_PIXELFORMAT_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rdr { class InStream; class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat {
+  public:
+    PixelFormat(int b, int d, bool e, bool t,
+                int rm=0, int gm=0, int bm=0, int rs=0, int gs=0, int bs=0);
+    PixelFormat();
+    bool equal(const PixelFormat& other) const;
+    void read(rdr::InStream* is);
+    void write(rdr::OutStream* os) const;
+    Pixel pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue, ColourMap* cm=0) const;
+    void rgbFromPixel(Pixel pix, ColourMap* cm, Colour* rgb) const;
+    void print(char* str, int len) const;
+    bool parse(const char* str);
+
+    int bpp;
+    int depth;
+    bool bigEndian;
+    bool trueColour;
+    int redMax;
+    int greenMax;
+    int blueMax;
+    int redShift;
+    int greenShift;
+    int blueShift;
+  };
+}
+#endif
diff --git a/rfb/RREDecoder.cxx b/rfb/RREDecoder.cxx
new file mode 100644
index 0000000..c613dbb
--- /dev/null
+++ b/rfb/RREDecoder.cxx
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RREDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreDecode.h>
+#undef BPP
+
+Decoder* RREDecoder::create(CMsgReader* reader)
+{
+  return new RREDecoder(reader);
+}
+
+RREDecoder::RREDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RREDecoder::~RREDecoder()
+{
+}
+
+void RREDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  switch (reader->bpp()) {
+  case 8:  rreDecode8 (r, is, handler); break;
+  case 16: rreDecode16(r, is, handler); break;
+  case 32: rreDecode32(r, is, handler); break;
+  }
+}
diff --git a/rfb/RREDecoder.h b/rfb/RREDecoder.h
new file mode 100644
index 0000000..75a5e85
--- /dev/null
+++ b/rfb/RREDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_RREDECODER_H__
+#define __RFB_RREDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class RREDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~RREDecoder();
+  private:
+    RREDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/RREEncoder.cxx b/rfb/RREEncoder.cxx
new file mode 100644
index 0000000..612a869
--- /dev/null
+++ b/rfb/RREEncoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RREEncoder.h>
+
+using namespace rfb;
+
+#define BPP 8
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreEncode.h>
+#undef BPP
+
+Encoder* RREEncoder::create(SMsgWriter* writer)
+{
+  return new RREEncoder(writer);
+}
+
+RREEncoder::RREEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RREEncoder::~RREEncoder()
+{
+}
+
+bool RREEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  int w = r.width();
+  int h = r.height();
+  rdr::U8* imageBuf = writer->getImageBuf(w*h);
+  ig->getImage(imageBuf, r);
+
+  mos.clear();
+
+  int nSubrects = -1;
+  switch (writer->bpp()) {
+  case 8:  nSubrects = rreEncode8(imageBuf, w, h, &mos);  break;
+  case 16: nSubrects = rreEncode16(imageBuf, w, h, &mos); break;
+  case 32: nSubrects = rreEncode32(imageBuf, w, h, &mos); break;
+  }
+  
+  if (nSubrects < 0) {
+    return writer->writeRect(r, encodingRaw, ig, actual);
+  }
+
+  writer->startRect(r, encodingRRE);
+  rdr::OutStream* os = writer->getOutStream();
+  os->writeU32(nSubrects);
+  os->writeBytes(mos.data(), mos.length());
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/RREEncoder.h b/rfb/RREEncoder.h
new file mode 100644
index 0000000..40b203e
--- /dev/null
+++ b/rfb/RREEncoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_RREENCODER_H__
+#define __RFB_RREENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class RREEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~RREEncoder();
+  private:
+    RREEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+    rdr::MemOutStream mos;
+  };
+}
+#endif
diff --git a/rfb/RawDecoder.cxx b/rfb/RawDecoder.cxx
new file mode 100644
index 0000000..5a5d62b
--- /dev/null
+++ b/rfb/RawDecoder.cxx
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/InStream.h>
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RawDecoder.h>
+
+using namespace rfb;
+
+Decoder* RawDecoder::create(CMsgReader* reader)
+{
+  return new RawDecoder(reader);
+}
+
+RawDecoder::RawDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RawDecoder::~RawDecoder()
+{
+}
+
+void RawDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  int nPixels;
+  rdr::U8* imageBuf = reader->getImageBuf(w, w*h, &nPixels);
+  int bytesPerRow = w * (reader->bpp() / 8);
+  while (h > 0) {
+    int nRows = nPixels / w;
+    if (nRows > h) nRows = h;
+    reader->getInStream()->readBytes(imageBuf, nRows * bytesPerRow);
+    handler->imageRect(Rect(x, y, x+w, y+nRows), imageBuf);
+    h -= nRows;
+    y += nRows;
+  }
+}
diff --git a/rfb/RawDecoder.h b/rfb/RawDecoder.h
new file mode 100644
index 0000000..b3dd9b7
--- /dev/null
+++ b/rfb/RawDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_RAWDECODER_H__
+#define __RFB_RAWDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class RawDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~RawDecoder();
+  private:
+    RawDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/RawEncoder.cxx b/rfb/RawEncoder.cxx
new file mode 100644
index 0000000..d758ec6
--- /dev/null
+++ b/rfb/RawEncoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RawEncoder.h>
+
+using namespace rfb;
+
+Encoder* RawEncoder::create(SMsgWriter* writer)
+{
+  return new RawEncoder(writer);
+}
+
+RawEncoder::RawEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RawEncoder::~RawEncoder()
+{
+}
+
+bool RawEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  int nPixels;
+  rdr::U8* imageBuf = writer->getImageBuf(w, w*h, &nPixels);
+  int bytesPerRow = w * (writer->bpp() / 8);
+  writer->startRect(r, encodingRaw);
+  while (h > 0) {
+    int nRows = nPixels / w;
+    if (nRows > h) nRows = h;
+    ig->getImage(imageBuf, Rect(x, y, x+w, y+nRows));
+    writer->getOutStream()->writeBytes(imageBuf, nRows * bytesPerRow);
+    h -= nRows;
+    y += nRows;
+  }
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/RawEncoder.h b/rfb/RawEncoder.h
new file mode 100644
index 0000000..c8b6a62
--- /dev/null
+++ b/rfb/RawEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_RAWENCODER_H__
+#define __RFB_RAWENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class RawEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~RawEncoder();
+  private:
+    RawEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+  };
+}
+#endif
diff --git a/rfb/Rect.h b/rfb/Rect.h
new file mode 100644
index 0000000..ee43e66
--- /dev/null
+++ b/rfb/Rect.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// rfb::Rect and rfb::Point structures
+
+#ifndef __RFB_RECT_INCLUDED__
+#define __RFB_RECT_INCLUDED__
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+namespace rfb {
+
+  // rfb::Point
+  //
+  // Represents a point in 2D space, by X and Y coordinates.
+  // Can also be used to represent a delta, or offset, between
+  // two Points.
+  // Functions are provided to allow Points to be compared for
+  // equality and translated by a supplied offset.
+  // Functions are also provided to negate offset Points.
+
+  struct Point {
+    Point() : x(0), y(0) {}
+    Point(int x_, int y_) : x(x_), y(y_) {}
+    inline Point negate() const {return Point(-x, -y);}
+    inline bool equals(const Point &p) const {return x==p.x && y==p.y;}
+    inline Point translate(const Point &p) const {return Point(x+p.x, y+p.y);}
+    inline Point subtract(const Point &p) const {return Point(x-p.x, y-p.y);}
+    int x, y;
+  };
+
+  // rfb::Rect
+  //
+  // Represents a rectangular region defined by its top-left (tl)
+  // and bottom-right (br) Points.
+  // Rects may be compared for equality, checked to determine whether
+  // or not they are empty, cleared (made empty), or intersected with
+  // one another.  The bounding rectangle of two existing Rects
+  // may be calculated, as may the area of a Rect.
+  // Rects may also be translated, in the same way as Points, by
+  // an offset specified in a Point structure.
+
+  struct Rect {
+    Rect() {}
+    Rect(Point tl_, Point br_) : tl(tl_), br(br_) {}
+    Rect(int x1, int y1, int x2, int y2) : tl(x1, y1), br(x2, y2) {}
+    inline void setXYWH(int x, int y, int w, int h) {
+      tl.x = x; tl.y = y; br.x = x+w; br.y = y+h;
+    }
+    inline Rect intersect(const Rect &r) const {
+      Rect result;
+      result.tl.x = max(tl.x, r.tl.x);
+      result.tl.y = max(tl.y, r.tl.y);
+      result.br.x = max(min(br.x, r.br.x), result.tl.x);
+      result.br.y = max(min(br.y, r.br.y), result.tl.y);
+      return result;
+    }
+    inline Rect union_boundary(const Rect &r) const {
+      if (r.is_empty()) return *this;
+      if (is_empty()) return r;
+      Rect result;
+      result.tl.x = min(tl.x, r.tl.x);
+      result.tl.y = min(tl.y, r.tl.y);
+      result.br.x = max(br.x, r.br.x);
+      result.br.y = max(br.y, r.br.y);
+      return result;
+    }
+    inline Rect translate(const Point &p) const {
+      return Rect(tl.translate(p), br.translate(p));
+    }
+    inline bool equals(const Rect &r) const {return r.tl.equals(tl) && r.br.equals(br);}
+    inline bool is_empty() const {return (tl.x >= br.x) || (tl.y >= br.y);}
+    inline void clear() {tl = Point(); br = Point();}
+    inline bool enclosed_by(const Rect &r) const {
+      return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y);
+    }
+    inline bool overlaps(const Rect &r) const {
+      return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y;
+    }
+    inline unsigned int area() const {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);}
+    inline Point dimensions() const {return Point(width(), height());}
+    inline int width() const {return br.x-tl.x;}
+    inline int height() const {return br.y-tl.y;}
+    inline bool contains(const Point &p) const {
+      return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y);
+    }
+    Point tl;
+    Point br;
+  };
+}
+#endif // __RFB_RECT_INCLUDED__
diff --git a/rfb/Region.cxx b/rfb/Region.cxx
new file mode 100644
index 0000000..bbcc892
--- /dev/null
+++ b/rfb/Region.cxx
@@ -0,0 +1,248 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// Cross-platform Region class based on the X11 region implementation.  Note
+// that for efficiency this code manipulates the Xlib region structure
+// directly.  Apart from the layout of the structure, there is one other key
+// assumption made: a Region returned from XCreateRegion must always have its
+// rects member allocated so that there is space for at least one rectangle.
+//
+
+#include <rfb/Region.h>
+#include <Xregion/Xregion.h>
+#include <Xregion/region.h>
+#include <assert.h>
+#include <stdio.h>
+
+// A _RectRegion must never be passed as a return parameter to the Xlib region
+// operations.  This is because for efficiency its "rects" member has not been
+// allocated with Xmalloc.  It is however safe to pass it as an input
+// parameter.
+
+class _RectRegion {
+public:
+  _RectRegion(const rfb::Rect& r) {
+    region.rects = &region.extents;
+    region.numRects = 1;
+    region.extents.x1 = r.tl.x;
+    region.extents.y1 = r.tl.y;
+    region.extents.x2 = r.br.x;
+    region.extents.y2 = r.br.y;
+    region.size = 1;
+    if (r.is_empty())
+      region.numRects = 0;
+  }
+  REGION region;
+};
+
+
+rfb::Region::Region() {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+}
+
+rfb::Region::Region(const Rect& r) {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+  reset(r);
+}
+
+rfb::Region::Region(const rfb::Region& r) {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region::~Region() {
+  XDestroyRegion(xrgn);
+}
+
+rfb::Region& rfb::Region::operator=(const rfb::Region& r) {
+  clear();
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+  return *this;
+}
+
+void rfb::Region::clear() {
+  xrgn->numRects = 0;
+  xrgn->extents.x1 = 0;
+  xrgn->extents.y1 = 0;
+  xrgn->extents.x2 = 0;
+  xrgn->extents.y2 = 0;
+}
+
+void rfb::Region::reset(const Rect& r) {
+  if (r.is_empty()) {
+    clear();
+  } else {
+    xrgn->numRects = 1;
+    xrgn->rects[0].x1 = xrgn->extents.x1 = r.tl.x;
+    xrgn->rects[0].y1 = xrgn->extents.y1 = r.tl.y;
+    xrgn->rects[0].x2 = xrgn->extents.x2 = r.br.x;
+    xrgn->rects[0].y2 = xrgn->extents.y2 = r.br.y;
+  }
+}
+
+void rfb::Region::translate(const Point& delta) {
+  XOffsetRegion(xrgn, delta.x, delta.y);
+}
+
+void rfb::Region::setOrderedRects(const std::vector<Rect>& rects) {
+  clear();
+  std::vector<Rect>::const_iterator i;
+  for (i=rects.begin(); i != rects.end(); i++) {
+    _RectRegion rr(*i);
+    XUnionRegion(xrgn, &rr.region, xrgn);
+  }
+}
+
+void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents,
+                                            int nRects, const ShortRect* rects)
+{
+  if (xrgn->size < nRects)
+  {
+    BOX* prevRects = xrgn->rects;
+    xrgn->rects = (BOX*)Xrealloc((char*)xrgn->rects, nRects * sizeof(BOX));
+    if (!xrgn->rects) {
+      fprintf(stderr,"Xrealloc failed\n");
+      Xfree(prevRects);
+      return;
+    }
+    xrgn->size = nRects;
+  }
+
+  xrgn->numRects = nRects;
+  xrgn->extents.x1 = extents->x1;
+  xrgn->extents.y1 = extents->y1;
+  xrgn->extents.x2 = extents->x2;
+  xrgn->extents.y2 = extents->y2;
+  for (int i = 0; i < nRects; i++) {
+    xrgn->rects[i].x1 = rects[i].x1;
+    xrgn->rects[i].y1 = rects[i].y1;
+    xrgn->rects[i].x2 = rects[i].x2;
+    xrgn->rects[i].y2 = rects[i].y2;
+  }
+}
+
+void rfb::Region::copyFrom(const rfb::Region& r) {
+  XUnionRegion(r.xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_intersect(const rfb::Region& r) {
+  XIntersectRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_union(const rfb::Region& r) {
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_subtract(const rfb::Region& r) {
+  XSubtractRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region rfb::Region::intersect(const rfb::Region& r) const {
+  rfb::Region ret;
+  XIntersectRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+rfb::Region rfb::Region::union_(const rfb::Region& r) const {
+  rfb::Region ret;
+  XUnionRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+rfb::Region rfb::Region::subtract(const rfb::Region& r) const {
+  rfb::Region ret;
+  XSubtractRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+bool rfb::Region::equals(const rfb::Region& r) const {
+  return XEqualRegion(xrgn, r.xrgn);
+}
+
+int rfb::Region::numRects() const {
+  return xrgn->numRects;
+}
+
+bool rfb::Region::get_rects(std::vector<Rect>* rects,
+                            bool left2right, bool topdown, int maxArea) const
+{
+  int nRects = xrgn->numRects;
+  int xInc = left2right ? 1 : -1;
+  int yInc = topdown ? 1 : -1;
+  int i = topdown ? 0 : nRects-1;
+  rects->clear();
+  rects->reserve(nRects);
+
+  while (nRects > 0) {
+    int firstInNextBand = i;
+    int nRectsInBand = 0;
+
+    while (nRects > 0 && xrgn->rects[firstInNextBand].y1 == xrgn->rects[i].y1)
+    {
+      firstInNextBand += yInc;
+      nRects--;
+      nRectsInBand++;
+    }
+
+    if (xInc != yInc)
+      i = firstInNextBand - yInc;
+
+    while (nRectsInBand > 0) {
+      int y = xrgn->rects[i].y1;
+      int h = maxArea / (xrgn->rects[i].x2 - xrgn->rects[i].x1);
+      if (!h) h = xrgn->rects[i].y2 - y;
+      do {
+        if (h > xrgn->rects[i].y2 - y)
+          h = xrgn->rects[i].y2 - y;
+        Rect r(xrgn->rects[i].x1, y, xrgn->rects[i].x2, y+h);
+        rects->push_back(r);
+        y += h;
+      } while (y < xrgn->rects[i].y2);
+      i += xInc;
+      nRectsInBand--;
+    }
+
+    i = firstInNextBand;
+  }
+
+  return !rects->empty();
+}
+
+rfb::Rect rfb::Region::get_bounding_rect() const {
+  return Rect(xrgn->extents.x1, xrgn->extents.y1,
+              xrgn->extents.x2, xrgn->extents.y2);
+}
+
+
+void rfb::Region::debug_print(const char* prefix) const
+{
+  fprintf(stderr,"%s num rects %3ld extents %3d,%3d %3dx%3d\n",
+          prefix, xrgn->numRects, xrgn->extents.x1, xrgn->extents.y1,
+          xrgn->extents.x2-xrgn->extents.x1,
+          xrgn->extents.y2-xrgn->extents.y1);
+
+  for (int i = 0; i < xrgn->numRects; i++) {
+    fprintf(stderr,"    rect %3d,%3d %3dx%3d\n",
+            xrgn->rects[i].x1, xrgn->rects[i].y1,
+            xrgn->rects[i].x2-xrgn->rects[i].x1,
+            xrgn->rects[i].y2-xrgn->rects[i].y1);
+  }
+}
diff --git a/rfb/Region.h b/rfb/Region.h
new file mode 100644
index 0000000..8fb9889
--- /dev/null
+++ b/rfb/Region.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// Cross-platform Region class based on the X11 region implementation
+
+#ifndef __RFB_REGION_INCLUDED__
+#define __RFB_REGION_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <vector>
+
+struct _XRegion;
+
+namespace rfb {
+
+  struct ShortRect {
+    short x1, y1, x2, y2;
+  };
+
+  class Region {
+  public:
+    // Create an empty region
+    Region();
+    // Create a rectangular region
+    Region(const Rect& r);
+
+    Region(const Region& r);
+    Region &operator=(const Region& src);
+
+    ~Region();
+
+    // the following methods alter the region in place:
+
+    void clear();
+    void reset(const Rect& r);
+    void translate(const rfb::Point& delta);
+    void setOrderedRects(const std::vector<Rect>& rects);
+    void setExtentsAndOrderedRects(const ShortRect* extents, int nRects,
+                                   const ShortRect* rects);
+    void copyFrom(const Region& r);
+
+    void assign_intersect(const Region& r);
+    void assign_union(const Region& r);
+    void assign_subtract(const Region& r);
+
+    // the following three operations return a new region:
+
+    Region intersect(const Region& r) const;
+    Region union_(const Region& r) const;
+    Region subtract(const Region& r) const;
+
+    bool equals(const Region& b) const;
+    int numRects() const;
+    bool is_empty() const { return numRects() == 0; }
+
+    bool get_rects(std::vector<Rect>* rects, bool left2right=true,
+                   bool topdown=true, int maxArea=0) const;
+    Rect get_bounding_rect() const;
+
+    void debug_print(const char *prefix) const;
+
+  protected:
+
+    struct _XRegion* xrgn;
+  };
+
+};
+
+#endif // __RFB_REGION_INCLUDED__
diff --git a/rfb/SConnection.cxx b/rfb/SConnection.cxx
new file mode 100644
index 0000000..e969ed8
--- /dev/null
+++ b/rfb/SConnection.cxx
@@ -0,0 +1,330 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/secTypes.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgWriterV3.h>
+#include <rfb/SSecurity.h>
+#include <rfb/SConnection.h>
+#include <rfb/ServerCore.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SConnection");
+
+// AccessRights values
+const SConnection::AccessRights SConnection::AccessView       = 0x0001;
+const SConnection::AccessRights SConnection::AccessKeyEvents  = 0x0002;
+const SConnection::AccessRights SConnection::AccessPtrEvents  = 0x0004;
+const SConnection::AccessRights SConnection::AccessCutText    = 0x0008;
+const SConnection::AccessRights SConnection::AccessDefault    = 0x03ff;
+const SConnection::AccessRights SConnection::AccessNoQuery    = 0x0400;
+const SConnection::AccessRights SConnection::AccessFull       = 0xffff;
+
+
+SConnection::SConnection()
+  : readyForSetColourMapEntries(false),
+    is(0), os(0), reader_(0), writer_(0),
+    nSecTypes(0), security(0), state_(RFBSTATE_UNINITIALISED)
+{
+  defaultMajorVersion = 3;
+  defaultMinorVersion = 8;
+  if (rfb::Server::protocol3_3)
+    defaultMinorVersion = 3;
+
+  cp.setVersion(defaultMajorVersion, defaultMinorVersion);
+}
+
+SConnection::~SConnection()
+{
+  if (security) security->destroy();
+  deleteReaderAndWriter();
+}
+
+void SConnection::deleteReaderAndWriter()
+{
+  delete reader_;
+  reader_ = 0;
+  delete writer_;
+  writer_ = 0;
+}
+
+void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+  is = is_;
+  os = os_;
+}
+
+void SConnection::addSecType(rdr::U8 secType)
+{
+  if (nSecTypes == maxSecTypes)
+    throw Exception("too many security types");
+  secTypes[nSecTypes++] = secType;
+  vlog.debug("Offering security type %s(%d)",
+             secTypeName(secType),secType);
+}
+
+void SConnection::initialiseProtocol()
+{
+  cp.writeVersion(os);
+  state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void SConnection::processMsg()
+{
+  switch (state_) {
+  case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();      break;
+  case RFBSTATE_SECURITY_TYPE:    processSecurityTypeMsg(); break;
+  case RFBSTATE_SECURITY:         processSecurityMsg();     break;
+  case RFBSTATE_INITIALISATION:   processInitMsg();         break;
+  case RFBSTATE_NORMAL:           reader_->readMsg();       break;
+  case RFBSTATE_QUERYING:
+    throw Exception("SConnection::processMsg: bogus data from client while "
+                    "querying");
+  case RFBSTATE_UNINITIALISED:
+    throw Exception("SConnection::processMsg: not initialised yet?");
+  default:
+    throw Exception("SConnection::processMsg: invalid state");
+  }
+}
+
+void SConnection::processVersionMsg()
+{
+  vlog.debug("reading protocol version");
+  bool done;
+  if (!cp.readVersion(is, &done)) {
+    state_ = RFBSTATE_INVALID;
+    throw Exception("reading version failed: not an RFB client?");
+  }
+  if (!done) return;
+
+  vlog.info("Client needs protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+
+  if (cp.majorVersion != 3) {
+    // unknown protocol version
+    char msg[256];
+    sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
+            cp.majorVersion, cp.minorVersion,
+            defaultMajorVersion, defaultMinorVersion);
+    throwConnFailedException(msg);
+  }
+
+  if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
+    vlog.error("Client uses unofficial protocol version %d.%d",
+               cp.majorVersion,cp.minorVersion);
+    if (cp.minorVersion >= 8)
+      cp.minorVersion = 8;
+    else if (cp.minorVersion == 7)
+      cp.minorVersion = 7;
+    else
+      cp.minorVersion = 3;
+    vlog.error("Assuming compatibility with version %d.%d",
+               cp.majorVersion,cp.minorVersion);
+  }
+
+  versionReceived();
+
+  if (cp.isVersion(3,3)) {
+
+    // cope with legacy 3.3 client only if "no authentication" or "vnc
+    // authentication" is supported.
+
+    int i;
+    for (i = 0; i < nSecTypes; i++) {
+      if (secTypes[i] == secTypeNone || secTypes[i] == secTypeVncAuth) break;
+    }
+    if (i == nSecTypes) {
+      char msg[256];
+      sprintf(msg,"No supported security type for %d.%d client",
+              cp.majorVersion, cp.minorVersion);
+      throwConnFailedException(msg);
+    }
+
+    os->writeU32(secTypes[i]);
+    if (secTypes[i] == secTypeNone) os->flush();
+    state_ = RFBSTATE_SECURITY;
+    security = getSSecurity(secTypes[i]);
+    processSecurityMsg();
+    return;
+  }
+
+  // list supported security types for >=3.7 clients
+
+  if (nSecTypes == 0)
+    throwConnFailedException("No supported security types");
+
+  os->writeU8(nSecTypes);
+  os->writeBytes(secTypes, nSecTypes);
+  os->flush();
+  state_ = RFBSTATE_SECURITY_TYPE;
+}
+
+
+void SConnection::processSecurityTypeMsg()
+{
+  vlog.debug("processing security type message");
+  int secType = is->readU8();
+  vlog.info("Client requests security type %s(%d)",
+            secTypeName(secType),secType);
+  int i;
+  for (i = 0; i < nSecTypes; i++) {
+    if (secType == secTypes[i]) break;
+  }
+  if (i == nSecTypes) {
+    char msg[256];
+    sprintf(msg,"Security type %s(%d) from client not supported",
+            secTypeName(secType),secType);
+    throwConnFailedException(msg);
+  }
+  state_ = RFBSTATE_SECURITY;
+  security = getSSecurity(secType);
+  processSecurityMsg();
+}
+
+void SConnection::processSecurityMsg()
+{
+  vlog.debug("processing security message");
+  bool done;
+  bool ok = security->processMsg(this, &done);
+  if (done) {
+    state_ = RFBSTATE_QUERYING;
+    if (ok) {
+      queryConnection(security->getUserName());
+    } else {
+      const char* failureMsg = security->failureMessage();
+      if (!failureMsg) failureMsg = "Authentication failure";
+      approveConnection(false, failureMsg);
+    }
+  }
+  if (!ok) {
+    state_ = RFBSTATE_INVALID;
+    authFailure();
+    throw AuthFailureException();
+  }
+}
+
+void SConnection::processInitMsg()
+{
+  vlog.debug("reading client initialisation");
+  reader_->readClientInit();
+}
+
+void SConnection::throwConnFailedException(const char* msg)
+{
+  vlog.info(msg);
+  if (state_ == RFBSTATE_PROTOCOL_VERSION) {
+    if (cp.majorVersion == 3 && cp.minorVersion == 3) {
+      os->writeU32(0);
+      os->writeString(msg);
+      os->flush();
+    } else {
+      os->writeU8(0);
+      os->writeString(msg);
+      os->flush();
+    }
+  }
+  state_ = RFBSTATE_INVALID;
+  throw ConnFailedException(msg);
+}
+
+void SConnection::writeConnFailedFromScratch(const char* msg,
+                                             rdr::OutStream* os)
+{
+  os->writeBytes("RFB 003.003\n", 12);
+  os->writeU32(0);
+  os->writeString(msg);
+  os->flush();
+}
+
+void SConnection::versionReceived()
+{
+}
+
+void SConnection::authSuccess()
+{
+}
+
+void SConnection::authFailure()
+{
+}
+
+void SConnection::queryConnection(const char* userName)
+{
+  approveConnection(true);
+}
+
+void SConnection::approveConnection(bool accept, const char* reason)
+{
+  if (state_ != RFBSTATE_QUERYING)
+    throw Exception("SConnection::approveConnection: invalid state");
+
+  if (!reason) reason = "Authentication failure";
+
+  if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
+    if (accept) {
+      os->writeU32(secResultOK);
+    } else {
+      os->writeU32(secResultFailed);
+      if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
+        os->writeString(reason);
+    }
+    os->flush();
+  }
+
+  if (accept) {
+    state_ = RFBSTATE_INITIALISATION;
+    reader_ = new SMsgReaderV3(this, is);
+    writer_ = new SMsgWriterV3(&cp, os);
+    authSuccess();
+  } else {
+    state_ = RFBSTATE_INVALID;
+    authFailure();
+    throw AuthFailureException(reason);
+  }
+}
+
+void SConnection::setInitialColourMap()
+{
+}
+
+void SConnection::clientInit(bool shared)
+{
+  writer_->writeServerInit();
+  state_ = RFBSTATE_NORMAL;
+}
+
+void SConnection::setPixelFormat(const PixelFormat& pf)
+{
+  SMsgHandler::setPixelFormat(pf);
+  readyForSetColourMapEntries = true;
+}
+
+void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+  if (!readyForSetColourMapEntries) {
+    readyForSetColourMapEntries = true;
+    if (!cp.pf().trueColour) {
+      setInitialColourMap();
+    }
+  }
+}
diff --git a/rfb/SConnection.h b/rfb/SConnection.h
new file mode 100644
index 0000000..19453c6
--- /dev/null
+++ b/rfb/SConnection.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// SConnection - class on the server side representing a connection to a
+// client.  A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_SCONNECTION_H__
+#define __RFB_SCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/SMsgHandler.h>
+
+namespace rfb {
+
+  class SMsgReader;
+  class SMsgWriter;
+  class SSecurity;
+
+  class SConnection : public SMsgHandler {
+  public:
+
+    SConnection();
+    virtual ~SConnection();
+
+    // Methods to initialise the connection
+
+    // setStreams() sets the streams to be used for the connection.  These must
+    // be set before initialiseProtocol() and processMsg() are called.  The
+    // SSecurity object may call setStreams() again to provide alternative
+    // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+    // streams).  Ownership of the streams remains with the caller
+    // (i.e. SConnection will not delete them).
+    void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+    // addSecType() should be called once for each security type which the
+    // server supports to this client.
+    void addSecType(rdr::U8 secType);
+
+    // initialiseProtocol() should be called once the streams and security
+    // types are set.  Subsequently, processMsg() should be called whenever
+    // there is data to read on the InStream.
+    void initialiseProtocol();
+
+    // processMsg() should be called whenever there is data to read on the
+    // InStream.  You must have called initialiseProtocol() first.
+    void processMsg();
+
+    // approveConnection() is called to either accept or reject the connection.
+    // If accept is false, the reason string gives the reason for the
+    // rejection.  It can either be called directly from queryConnection() or
+    // later, after queryConnection() has returned.  It can only be called when
+    // in state RFBSTATE_QUERYING.  On rejection, an AuthFailureException is
+    // thrown, so this must be handled appropriately by the caller.
+    void approveConnection(bool accept, const char* reason=0);
+
+
+    // Methods to be overridden in a derived class
+
+    // versionReceived() indicates that the version number has just been read
+    // from the client.  The version will already have been "cooked"
+    // to deal with unknown/bogus viewer protocol numbers.
+    virtual void versionReceived();
+
+    // getSSecurity() gets the SSecurity object for the given type.  The type
+    // is guaranteed to be one of the secTypes passed in to addSecType().  The
+    // SSecurity object's destroy() method will be called by the SConnection
+    // from its destructor.
+    virtual SSecurity* getSSecurity(int secType)=0;
+
+    // authSuccess() is called when authentication has succeeded.
+    virtual void authSuccess();
+
+    // authFailure() is called when authentication has failed.  This method is
+    // not normally overridden since an exception is thrown anyway.
+    virtual void authFailure();
+
+    // queryConnection() is called when authentication has succeeded, but
+    // before informing the client.  It can be overridden to query a local user
+    // to accept the incoming connection, for example.  The userName argument
+    // is the name of the user making the connection, or null (note that the
+    // storage for userName is owned by the caller).  The connection must be
+    // accepted or rejected by calling approveConnection(), either directly
+    // from queryConnection() or some time later.
+    virtual void queryConnection(const char* userName);
+
+    // clientInit() is called when the ClientInit message is received.  The
+    // derived class must call on to SConnection::clientInit().
+    virtual void clientInit(bool shared);
+
+    // setPixelFormat() is called when a SetPixelFormat message is received.
+    // The derived class must call on to SConnection::setPixelFormat().
+    virtual void setPixelFormat(const PixelFormat& pf);
+
+    // framebufferUpdateRequest() is called when a FramebufferUpdateRequest
+    // message is received.  The derived class must call on to
+    // SConnection::framebufferUpdateRequest().
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+
+    // setInitialColourMap() is called when the client needs an initial
+    // SetColourMapEntries message.  In fact this only happens when the client
+    // accepts the server's default pixel format and it uses a colour map.
+    virtual void setInitialColourMap();
+
+    // setAccessRights() allows a security package to limit the access rights
+    // of a VNCSConnectionST to the server.  How the access rights are treated
+    // is up to the derived class.
+
+    typedef rdr::U16 AccessRights;
+    static const AccessRights AccessView;         // View display contents
+    static const AccessRights AccessKeyEvents;    // Send key events
+    static const AccessRights AccessPtrEvents;    // Send pointer events
+    static const AccessRights AccessCutText;      // Send/receive clipboard events
+    static const AccessRights AccessDefault;      // The default rights, INCLUDING FUTURE ONES
+    static const AccessRights AccessNoQuery;      // Connect without local user accepting
+    static const AccessRights AccessFull;         // All of the available AND FUTURE rights
+    virtual void setAccessRights(AccessRights ar) = 0;
+
+    // Other methods
+
+    // authenticated() returns true if the client has authenticated
+    // successfully.
+    bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
+                                   state_ == RFBSTATE_NORMAL); }
+
+    // deleteReaderAndWriter() deletes the reader and writer associated with
+    // this connection.  This may be useful if you want to delete the streams
+    // before deleting the SConnection to make sure that no attempt by the
+    // SConnection is made to read or write.
+    // XXX Do we really need this at all???
+    void deleteReaderAndWriter();
+
+    // throwConnFailedException() prints a message to the log, sends a conn
+    // failed message to the client (if possible) and throws a
+    // ConnFailedException.
+    void throwConnFailedException(const char* msg);
+
+    // writeConnFailedFromScratch() sends a conn failed message to an OutStream
+    // without the need to negotiate the protocol version first.  It actually
+    // does this by assuming that the client will understand version 3.3 of the
+    // protocol.
+    static void writeConnFailedFromScratch(const char* msg,
+                                           rdr::OutStream* os);
+
+    SMsgReader* reader() { return reader_; }
+    SMsgWriter* writer() { return writer_; }
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::OutStream* getOutStream() { return os; }
+
+    enum stateEnum {
+      RFBSTATE_UNINITIALISED,
+      RFBSTATE_PROTOCOL_VERSION,
+      RFBSTATE_SECURITY_TYPE,
+      RFBSTATE_SECURITY,
+      RFBSTATE_QUERYING,
+      RFBSTATE_INITIALISATION,
+      RFBSTATE_NORMAL,
+      RFBSTATE_CLOSING,
+      RFBSTATE_INVALID
+    };
+
+    stateEnum state() { return state_; }
+
+    // ssecurity() returns a pointer to this connection's SSecurity object, if any
+    const SSecurity* ssecurity() const { return security; }
+
+  protected:
+    void setState(stateEnum s) { state_ = s; }
+
+    bool readyForSetColourMapEntries;
+
+  private:
+    void processVersionMsg();
+    void processSecurityTypeMsg();
+    void processSecurityMsg();
+    void processInitMsg();
+
+    int defaultMajorVersion, defaultMinorVersion;
+    rdr::InStream* is;
+    rdr::OutStream* os;
+    SMsgReader* reader_;
+    SMsgWriter* writer_;
+    enum { maxSecTypes = 8 };
+    int nSecTypes;
+    rdr::U8 secTypes[maxSecTypes];
+    SSecurity* security;
+    stateEnum state_;
+  };
+}
+#endif
diff --git a/rfb/SDesktop.h b/rfb/SDesktop.h
new file mode 100644
index 0000000..eb17a52
--- /dev/null
+++ b/rfb/SDesktop.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+
+// SDesktop is an interface implemented by back-ends, on which callbacks are
+// made by the VNCServer as appropriate for pointer and keyboard events, etc.
+// SDesktop objects are always created before the VNCServer - the SDesktop
+// will be passed a pointer to the VNCServer in the start() call.  If a more
+// implementation-specific pointer to the VNCServer is required then this
+// can be provided to the SDesktop via an implementation-specific method.
+//
+// An SDesktop usually has an associated PixelBuffer which it tells the
+// VNCServer via the VNCServer's setPixelBuffer() method.  It can do this at
+// any time, but the PixelBuffer MUST be valid by the time the call to start()
+// returns.  The PixelBuffer may be set to null again if desired when stop() is
+// called.  Note that start() and stop() are guaranteed to be called
+// alternately; there should never be two calls to start() without an
+// intervening stop() and vice-versa.
+//
+
+#ifndef __RFB_SDESKTOP_H__
+#define __RFB_SDESKTOP_H__
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  class VNCServer;
+
+  class SDesktop {
+  public:
+    // start() is called by the server when the first client authenticates
+    // successfully, and can be used to begin any expensive tasks which are not
+    // needed when there are no clients.  A valid PixelBuffer must have been
+    // set via the VNCServer's setPixelBuffer() method by the time this call
+    // returns.
+
+    virtual void start(VNCServer* vs) {}
+
+    // stop() is called by the server when there are no longer any
+    // authenticated clients, and therefore the desktop can cease any
+    // expensive tasks.  No further calls to the VNCServer passed to start()
+    // can be made once stop has returned.
+
+    virtual void stop() {}
+
+    // pointerEvent(), keyEvent() and clientCutText() are called in response to
+    // the relevant RFB protocol messages from clients.
+
+    virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask) {}
+    virtual void keyEvent(rdr::U32 key, bool down) {}
+    virtual void clientCutText(const char* str, int len) {}
+
+    // framebufferUpdateRequest() is called to let the desktop know that at
+    // least one client has become ready for an update.  Desktops can check
+    // whether there are clients ready at any time by calling the VNCServer's
+    // clientsReadyForUpdate() method.
+
+    virtual void framebufferUpdateRequest() {}
+
+    // getFbSize() returns the current dimensions of the framebuffer.
+    // This can be called even while the SDesktop is not start()ed.
+
+    virtual Point getFbSize() = 0;
+
+  protected:
+    virtual ~SDesktop() {}
+  };
+
+  // -=- SStaticDesktop
+  //     Trivial implementation of the SDesktop interface, which provides
+  //     dummy input handlers and event processing routine, and exports
+  //     a plain black desktop of the specified format.
+  class SStaticDesktop : public SDesktop {
+  public:
+    SStaticDesktop(const Point& size) : server(0), buffer(0) {
+      PixelFormat pf;
+      buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+      if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+    }
+    SStaticDesktop(const Point& size, const PixelFormat& pf) : buffer(0) {
+      buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+      if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+    }
+    virtual ~SStaticDesktop() {
+      if (buffer) delete buffer;
+    }
+
+    virtual void start(VNCServer* vs) {
+      server = vs;
+      server->setPixelBuffer(buffer);
+    }
+    virtual void stop() {
+      server->setPixelBuffer(0);
+      server = 0;
+    }
+
+  protected:
+    VNCServer* server;
+    ManagedPixelBuffer* buffer;
+  };
+
+};
+
+#endif // __RFB_SDESKTOP_H__
diff --git a/rfb/SMsgHandler.cxx b/rfb/SMsgHandler.cxx
new file mode 100644
index 0000000..d6a139c
--- /dev/null
+++ b/rfb/SMsgHandler.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/Exception.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgHandler::SMsgHandler()
+{
+}
+
+SMsgHandler::~SMsgHandler()
+{
+}
+
+void SMsgHandler::clientInit(bool shared)
+{
+}
+
+void SMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+  cp.setPF(pf);
+}
+
+void SMsgHandler::setEncodings(int nEncodings, rdr::U32* encodings)
+{
+  cp.setEncodings(nEncodings, encodings);
+  supportsLocalCursor();
+}
+
+void SMsgHandler::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+}
+
+void SMsgHandler::keyEvent(rdr::U32 key, bool down)
+{
+}
+
+void SMsgHandler::pointerEvent(int x, int y, int buttonMask)
+{
+}
+
+void SMsgHandler::clientCutText(const char* str, int len)
+{
+}
+
+void SMsgHandler::supportsLocalCursor()
+{
+}
diff --git a/rfb/SMsgHandler.h b/rfb/SMsgHandler.h
new file mode 100644
index 0000000..f326ad4
--- /dev/null
+++ b/rfb/SMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// SMsgHandler - class to handle incoming messages on the server side.
+//
+
+#ifndef __RFB_SMSGHANDLER_H__
+#define __RFB_SMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class SMsgHandler {
+  public:
+    SMsgHandler();
+    virtual ~SMsgHandler();
+
+    // The following methods are called as corresponding messages are read.  A
+    // derived class should override these methods as desired.  Note that for
+    // the setPixelFormat() and setEncodings() methods, a derived class must
+    // call on to SMsgHandler's methods.
+
+    virtual void clientInit(bool shared);
+
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void setEncodings(int nEncodings, rdr::U32* encodings);
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void pointerEvent(int x, int y, int buttonMask);
+    virtual void clientCutText(const char* str, int len);
+
+    // supportsLocalCursor() is called whenever the status of
+    // cp.supportsLocalCursor has changed.  At the moment this happens on a
+    // setEncodings message, but in the future this may be due to a message
+    // specially for this purpose.
+    virtual void supportsLocalCursor();
+
+    ConnParams cp;
+  };
+}
+#endif
diff --git a/rfb/SMsgReader.cxx b/rfb/SMsgReader.cxx
new file mode 100644
index 0000000..2939aa1
--- /dev/null
+++ b/rfb/SMsgReader.cxx
@@ -0,0 +1,105 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/SMsgHandler.h>
+#include <rfb/SMsgReader.h>
+
+using namespace rfb;
+
+SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
+  : handler(handler_), is(is_)
+{
+}
+
+SMsgReader::~SMsgReader()
+{
+}
+
+void SMsgReader::endMsg()
+{
+}
+
+void SMsgReader::readSetPixelFormat()
+{
+  is->skip(3);
+  PixelFormat pf;
+  pf.read(is);
+  endMsg();
+  handler->setPixelFormat(pf);
+}
+
+void SMsgReader::readSetEncodings()
+{
+  is->skip(1);
+  int nEncodings = is->readU16();
+  rdr::U32* encodings = new rdr::U32[nEncodings];
+  for (int i = 0; i < nEncodings; i++)
+    encodings[i] = is->readU32();
+  endMsg();
+  handler->setEncodings(nEncodings, encodings);
+  delete [] encodings;
+}
+
+void SMsgReader::readFramebufferUpdateRequest()
+{
+  bool inc = is->readU8();
+  int x = is->readU16();
+  int y = is->readU16();
+  int w = is->readU16();
+  int h = is->readU16();
+  endMsg();
+  handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
+}
+
+void SMsgReader::readKeyEvent()
+{
+  bool down = is->readU8();
+  is->skip(2);
+  rdr::U32 key = is->readU32();
+  endMsg();
+  handler->keyEvent(key, down);
+}
+
+void SMsgReader::readPointerEvent()
+{
+  int mask = is->readU8();
+  int x = is->readU16();
+  int y = is->readU16();
+  endMsg();
+  handler->pointerEvent(x, y, mask);
+}
+
+
+void SMsgReader::readClientCutText()
+{
+  is->skip(3);
+  int len = is->readU32();
+  if (len > 256*1024) {
+    is->skip(len);
+    fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+    return;
+  }
+  CharArray ca(len+1);
+  ca.buf[len] = 0;
+  is->readBytes(ca.buf, len);
+  endMsg();
+  handler->clientCutText(ca.buf, len);
+}
diff --git a/rfb/SMsgReader.h b/rfb/SMsgReader.h
new file mode 100644
index 0000000..4d26938
--- /dev/null
+++ b/rfb/SMsgReader.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// SMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_SMSGREADER_H__
+#define __RFB_SMSGREADER_H__
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+  class SMsgHandler;
+
+  class SMsgReader {
+  public:
+    virtual ~SMsgReader();
+
+    virtual void readClientInit()=0;
+
+    // readMsg() reads a message, calling the handler as appropriate.
+    virtual void readMsg()=0;
+
+    rdr::InStream* getInStream() { return is; }
+
+  protected:
+    virtual void readSetPixelFormat();
+    virtual void readSetEncodings();
+    virtual void readFramebufferUpdateRequest();
+    virtual void readKeyEvent();
+    virtual void readPointerEvent();
+    virtual void readClientCutText();
+    virtual void endMsg();
+
+    SMsgReader(SMsgHandler* handler, rdr::InStream* is);
+
+    SMsgHandler* handler;
+    rdr::InStream* is;
+  };
+}
+#endif
diff --git a/rfb/SMsgReaderV3.cxx b/rfb/SMsgReaderV3.cxx
new file mode 100644
index 0000000..e5ae744
--- /dev/null
+++ b/rfb/SMsgReaderV3.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgReaderV3::SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is)
+  : SMsgReader(handler, is)
+{
+}
+
+SMsgReaderV3::~SMsgReaderV3()
+{
+}
+
+void SMsgReaderV3::readClientInit()
+{
+  bool shared = is->readU8();
+  endMsg();
+  handler->clientInit(shared);
+}
+
+void SMsgReaderV3::readMsg()
+{
+  int msgType = is->readU8();
+  switch (msgType) {
+  case msgTypeSetPixelFormat:           readSetPixelFormat(); break;
+  case msgTypeSetEncodings:             readSetEncodings(); break;
+  case msgTypeFramebufferUpdateRequest: readFramebufferUpdateRequest(); break;
+  case msgTypeKeyEvent:                 readKeyEvent(); break;
+  case msgTypePointerEvent:             readPointerEvent(); break;
+  case msgTypeClientCutText:            readClientCutText(); break;
+  default:
+    fprintf(stderr, "unknown message type %d\n", msgType);
+    throw Exception("unknown message type");
+  }
+}
diff --git a/rfb/SMsgReaderV3.h b/rfb/SMsgReaderV3.h
new file mode 100644
index 0000000..28cc7a6
--- /dev/null
+++ b/rfb/SMsgReaderV3.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_SMSGREADERV3_H__
+#define __RFB_SMSGREADERV3_H__
+
+#include <rfb/SMsgReader.h>
+
+namespace rfb {
+  class SMsgReaderV3 : public SMsgReader {
+  public:
+    SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is);
+    virtual ~SMsgReaderV3();
+    virtual void readClientInit();
+    virtual void readMsg();
+  };
+}
+#endif
diff --git a/rfb/SMsgWriter.cxx b/rfb/SMsgWriter.cxx
new file mode 100644
index 0000000..ac74382
--- /dev/null
+++ b/rfb/SMsgWriter.cxx
@@ -0,0 +1,180 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ConnParams.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SMsgWriter");
+
+SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+  : imageBufIdealSize(0), cp(cp_), os(os_), lenBeforeRect(0),
+    currentEncoding(0), updatesSent(0), rawBytesEquivalent(0),
+    imageBuf(0), imageBufSize(0)
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    encoders[i] = 0;
+    bytesSent[i] = 0;
+    rectsSent[i] = 0;
+  }
+}
+
+SMsgWriter::~SMsgWriter()
+{
+  vlog.info("framebuffer updates %d",updatesSent);
+  int bytes = 0;
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    delete encoders[i];
+    if (i != encodingCopyRect)
+      bytes += bytesSent[i];
+    if (rectsSent[i])
+      vlog.info("  %s rects %d, bytes %d",
+                encodingName(i), rectsSent[i], bytesSent[i]);
+  }
+  vlog.info("  raw bytes equivalent %d, compression ratio %f",
+          rawBytesEquivalent, (double)rawBytesEquivalent / bytes);
+  delete [] imageBuf;
+}
+
+void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
+                                          ColourMap* cm)
+{
+  startMsg(msgTypeSetColourMapEntries);
+  os->pad(1);
+  os->writeU16(firstColour);
+  os->writeU16(nColours);
+  for (int i = firstColour; i < firstColour+nColours; i++) {
+    int r, g, b;
+    cm->lookup(i, &r, &g, &b);
+    os->writeU16(r);
+    os->writeU16(g);
+    os->writeU16(b);
+  }
+  endMsg();
+}
+
+void SMsgWriter::writeBell()
+{
+  startMsg(msgTypeBell);
+  endMsg();
+}
+
+void SMsgWriter::writeServerCutText(const char* str, int len)
+{
+  startMsg(msgTypeServerCutText);
+  os->pad(3);
+  os->writeU32(len);
+  os->writeBytes(str, len);
+  endMsg();
+}
+
+void SMsgWriter::writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+                                        Region* updatedRegion)
+{
+  writeFramebufferUpdateStart(ui.numRects());
+  writeRects(ui, ig, updatedRegion);
+  writeFramebufferUpdateEnd();
+}
+
+void SMsgWriter::writeRects(const UpdateInfo& ui, ImageGetter* ig,
+                            Region* updatedRegion)
+{
+  std::vector<Rect> rects;
+  std::vector<Rect>::const_iterator i;
+  updatedRegion->copyFrom(ui.changed);
+  updatedRegion->assign_union(ui.copied);
+
+  ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
+  for (i = rects.begin(); i != rects.end(); i++)
+    writeCopyRect(*i, i->tl.x - ui.copy_delta.x, i->tl.y - ui.copy_delta.y);
+
+  ui.changed.get_rects(&rects);
+  for (i = rects.begin(); i != rects.end(); i++) {
+    Rect actual;
+    if (!writeRect(*i, ig, &actual)) {
+      updatedRegion->assign_subtract(*i);
+      updatedRegion->assign_union(actual);
+    }
+  }
+}
+
+
+bool SMsgWriter::needFakeUpdate()
+{
+  return false;
+}
+
+bool SMsgWriter::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  return writeRect(r, cp->currentEncoding(), ig, actual);
+}
+
+bool SMsgWriter::writeRect(const Rect& r, unsigned int encoding,
+                           ImageGetter* ig, Rect* actual)
+{
+  if (!encoders[encoding]) {
+    encoders[encoding] = Encoder::createEncoder(encoding, this);
+    assert(encoders[encoding]);
+  }
+  return encoders[encoding]->writeRect(r, ig, actual);
+}
+
+void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
+{
+  startRect(r,encodingCopyRect);
+  os->writeU16(srcX);
+  os->writeU16(srcY);
+  endRect();
+}
+
+void SMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+  os = os_;
+}
+
+rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels)
+{
+  int requiredBytes = required * (cp->pf().bpp / 8);
+  int requestedBytes = requested * (cp->pf().bpp / 8);
+  int size = requestedBytes;
+  if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+  if (size < requiredBytes)
+    size = requiredBytes;
+
+  if (imageBufSize < size) {
+    imageBufSize = size;
+    delete [] imageBuf;
+    imageBuf = new rdr::U8[imageBufSize];
+  }
+  if (nPixels)
+    *nPixels = imageBufSize / (cp->pf().bpp / 8);
+  return imageBuf;
+}
+
+int SMsgWriter::bpp()
+{
+  return cp->pf().bpp;
+}
diff --git a/rfb/SMsgWriter.h b/rfb/SMsgWriter.h
new file mode 100644
index 0000000..6eba068
--- /dev/null
+++ b/rfb/SMsgWriter.h
@@ -0,0 +1,156 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// SMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_SMSGWRITER_H__
+#define __RFB_SMSGWRITER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat;
+  class ConnParams;
+  class ImageGetter;
+  class ColourMap;
+  class Region;
+  class UpdateInfo;
+
+  class WriteSetCursorCallback {
+  public:
+    virtual void writeSetCursorCallback() = 0;
+  };
+
+  class SMsgWriter {
+  public:
+    virtual ~SMsgWriter();
+
+    // writeServerInit() must only be called at the appropriate time in the
+    // protocol initialisation.
+    virtual void writeServerInit()=0;
+
+    // Methods to write normal protocol messages
+
+    // writeSetColourMapEntries() writes a setColourMapEntries message, using
+    // the given ColourMap object to lookup the RGB values of the given range
+    // of colours.
+    virtual void writeSetColourMapEntries(int firstColour, int nColours,
+                                          ColourMap* cm);
+
+    // writeBell() and writeServerCutText() do the obvious thing.
+    virtual void writeBell();
+    virtual void writeServerCutText(const char* str, int len);
+
+    // writeSetDesktopSize() on a V3 writer won't actually write immediately,
+    // but will write the relevant pseudo-rectangle as part of the next update.
+    virtual bool writeSetDesktopSize()=0;
+
+    // Like setDestkopSize, we can't just write out a setCursor message
+    // immediately on a V3 writer.  Instead of calling writeSetCursor()
+    // directly, you must call cursorChange(), and then invoke writeSetCursor()
+    // in response to the writeSetCursorCallback() callback.  For a V3 writer
+    // this will happen when the next update is sent.
+    virtual void cursorChange(WriteSetCursorCallback* cb)=0;
+    virtual void writeSetCursor(int width, int height, int hotspotX,
+                                int hotspotY, void* data, void* mask)=0;
+
+    // needFakeUpdate() returns true when an immediate update is needed in
+    // order to flush out setDesktopSize or setCursor pseudo-rectangles to the
+    // client.
+    virtual bool needFakeUpdate();
+
+    // writeFramebufferUpdate() writes a framebuffer update using the given
+    // UpdateInfo and ImageGetter.  On a V3 writer this may have
+    // pseudo-rectangles for setDesktopSize and setCursor added to it, and so
+    // may invoke writeSetCursorCallback().
+    virtual void writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+                                        Region* updatedRegion);
+
+    // writeRects() accepts an UpdateInfo (changed & copied regions) and an
+    // ImageGetter to fetch pixels from.  It then calls writeCopyRect() and
+    // writeRect() as appropriate.  writeFramebufferUpdateStart() must be used
+    // before the first writeRects() call and writeFrameBufferUpdateEnd() after
+    // the last one.  It returns the actual region sent to the client, which
+    // may be smaller than the update passed in.
+    virtual void writeRects(const UpdateInfo& update, ImageGetter* ig,
+                            Region* updatedRegion);
+
+    // To construct a framebuffer update you can call
+    // writeFramebufferUpdateStart(), followed by a number of writeCopyRect()s
+    // and writeRect()s, finishing with writeFramebufferUpdateEnd().  If you
+    // know the exact number of rectangles ahead of time you can specify it to
+    // writeFramebufferUpdateStart() which can be more efficient.
+    virtual void writeFramebufferUpdateStart(int nRects)=0;
+    virtual void writeFramebufferUpdateStart()=0;
+    virtual void writeFramebufferUpdateEnd()=0;
+
+    // writeRect() tries to write the given rectangle.  If it is unable to
+    // write the whole rectangle it returns false and sets actual to the actual
+    // rectangle which was updated.
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, unsigned int encoding,
+                           ImageGetter* ig, Rect* actual);
+
+    virtual void writeCopyRect(const Rect& r, int srcX, int srcY);
+
+    virtual void startRect(const Rect& r, unsigned int enc)=0;
+    virtual void endRect()=0;
+
+    // setOutStream() changes the OutStream on the fly.
+    virtual void setOutStream(rdr::OutStream* os);
+
+    ConnParams* getConnParams() { return cp; }
+    rdr::OutStream* getOutStream() { return os; }
+    rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+    int bpp();
+
+    int getUpdatesSent()           { return updatesSent; }
+    int getRectsSent(int encoding) { return rectsSent[encoding]; }
+    int getBytesSent(int encoding) { return bytesSent[encoding]; }
+    int getRawBytesEquivalent()    { return rawBytesEquivalent; }
+
+    int imageBufIdealSize;
+
+  protected:
+    SMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+    virtual void startMsg(int type)=0;
+    virtual void endMsg()=0;
+
+    ConnParams* cp;
+    rdr::OutStream* os;
+
+    Encoder* encoders[encodingMax+1];
+    int lenBeforeRect;
+    unsigned int currentEncoding;
+    int updatesSent;
+    int bytesSent[encodingMax+1];
+    int rectsSent[encodingMax+1];
+    int rawBytesEquivalent;
+
+    rdr::U8* imageBuf;
+    int imageBufSize;
+  };
+}
+#endif
diff --git a/rfb/SMsgWriterV3.cxx b/rfb/SMsgWriterV3.cxx
new file mode 100644
index 0000000..20a7280
--- /dev/null
+++ b/rfb/SMsgWriterV3.cxx
@@ -0,0 +1,173 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/OutStream.h>
+#include <rdr/MemOutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriterV3.h>
+
+using namespace rfb;
+
+SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+  : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
+    nRectsInHeader(0), wsccb(0),
+    needSetDesktopSize(false)
+{
+}
+
+SMsgWriterV3::~SMsgWriterV3()
+{
+  delete updateOS;
+}
+
+void SMsgWriterV3::writeServerInit()
+{
+  os->writeU16(cp->width);
+  os->writeU16(cp->height);
+  cp->pf().write(os);
+  os->writeString(cp->name());
+  endMsg();
+}
+
+void SMsgWriterV3::startMsg(int type)
+{
+  if (os != realOS)
+    throw Exception("startMsg called while writing an update?");
+
+  os->writeU8(type);
+}
+
+void SMsgWriterV3::endMsg()
+{
+  os->flush();
+}
+
+bool SMsgWriterV3::writeSetDesktopSize() {
+  if (!cp->supportsDesktopResize) return false;
+  needSetDesktopSize = true;
+  return true;
+}
+
+void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
+{
+  wsccb = cb;
+}
+
+void SMsgWriterV3::writeSetCursor(int width, int height, int hotspotX,
+                                  int hotspotY, void* data, void* mask)
+{
+  if (!wsccb) return;
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingCursor);
+  os->writeBytes(data, width * height * (cp->pf().bpp/8));
+  os->writeBytes(mask, (width+7)/8 * height);
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
+{
+  startMsg(msgTypeFramebufferUpdate);
+  os->pad(1);
+  if (wsccb) nRects++;
+  if (needSetDesktopSize) nRects++;
+  os->writeU16(nRects);
+  nRectsInUpdate = 0;
+  nRectsInHeader = nRects;
+  if (wsccb) {
+    wsccb->writeSetCursorCallback();
+    wsccb = 0;
+  }
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart()
+{
+  nRectsInUpdate = nRectsInHeader = 0;
+  if (!updateOS)
+    updateOS = new rdr::MemOutStream;
+  os = updateOS;
+}
+
+void SMsgWriterV3::writeFramebufferUpdateEnd()
+{
+  if (needSetDesktopSize) {
+    if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
+    os->writeS16(0);
+    os->writeS16(0);
+    os->writeU16(cp->width);
+    os->writeU16(cp->height);
+    os->writeU32(pseudoEncodingDesktopSize);
+    needSetDesktopSize = false;
+  }
+
+  if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
+                    "nRects out of sync");
+  if (os == updateOS) {
+    os = realOS;
+    startMsg(msgTypeFramebufferUpdate);
+    os->pad(1);
+    os->writeU16(nRectsInUpdate);
+    os->writeBytes(updateOS->data(), updateOS->length());
+    updateOS->clear();
+  }
+
+  updatesSent++;
+  endMsg();
+}
+
+bool SMsgWriterV3::needFakeUpdate()
+{
+  return wsccb || needSetDesktopSize;
+}
+
+void SMsgWriterV3::startRect(const Rect& r, unsigned int encoding)
+{
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::startRect: nRects out of sync");
+
+  currentEncoding = encoding;
+  lenBeforeRect = os->length();
+  if (encoding != encodingCopyRect)
+    rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
+
+  os->writeS16(r.tl.x);
+  os->writeS16(r.tl.y);
+  os->writeU16(r.width());
+  os->writeU16(r.height());
+  os->writeU32(encoding);
+}
+
+void SMsgWriterV3::endRect()
+{
+  if (currentEncoding <= encodingMax) {
+    bytesSent[currentEncoding] += os->length() - lenBeforeRect;
+    rectsSent[currentEncoding]++;
+  }
+}
+
+void SMsgWriterV3::setOutStream(rdr::OutStream* os_)
+{
+  SMsgWriter::setOutStream(os_);
+  realOS = os;
+}
diff --git a/rfb/SMsgWriterV3.h b/rfb/SMsgWriterV3.h
new file mode 100644
index 0000000..3881061
--- /dev/null
+++ b/rfb/SMsgWriterV3.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_SMSGWRITERV3_H__
+#define __RFB_SMSGWRITERV3_H__
+
+#include <rfb/SMsgWriter.h>
+
+namespace rdr { class MemOutStream; }
+
+namespace rfb {
+  class SMsgWriterV3 : public SMsgWriter {
+  public:
+    SMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+    virtual ~SMsgWriterV3();
+
+    virtual void writeServerInit();
+    virtual void startMsg(int type);
+    virtual void endMsg();
+    virtual bool writeSetDesktopSize();
+    virtual void cursorChange(WriteSetCursorCallback* cb);
+    virtual void writeSetCursor(int width, int height, int hotspotX,
+                                int hotspotY, void* data, void* mask);
+    virtual void writeFramebufferUpdateStart(int nRects);
+    virtual void writeFramebufferUpdateStart();
+    virtual void writeFramebufferUpdateEnd();
+    virtual bool needFakeUpdate();
+    virtual void startRect(const Rect& r, unsigned int encoding);
+    virtual void endRect();
+
+    virtual void setOutStream(rdr::OutStream* os);
+
+  private:
+    rdr::MemOutStream* updateOS;
+    rdr::OutStream* realOS;
+    int nRectsInUpdate;
+    int nRectsInHeader;
+    WriteSetCursorCallback* wsccb;
+    bool needSetDesktopSize;
+  };
+}
+#endif
diff --git a/rfb/SSecurity.h b/rfb/SSecurity.h
new file mode 100644
index 0000000..2ca5344
--- /dev/null
+++ b/rfb/SSecurity.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// SSecurity - class on the server side for handling security handshaking.  A
+// derived class for a particular security type overrides the processMsg()
+// method.  processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the client until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// processMsg() must never block (or at least must never block until the client
+// has been authenticated) - this is to prevent denial of service attacks.
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the SConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// getType() should return the secType value corresponding to the SSecurity
+// implementation.
+//
+// failureMessage_.buf can be set to a string which will be passed to the client
+// if processMsg returns false, to indicate the reason for the failure.
+
+#ifndef __RFB_SSECURITY_H__
+#define __RFB_SSECURITY_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class SConnection;
+
+  class SSecurity {
+  public:
+    virtual ~SSecurity() {}
+    virtual bool processMsg(SConnection* sc, bool* done)=0;
+    virtual void destroy() { delete this; }
+    virtual int getType() const = 0;
+
+    // getUserName() gets the name of the user attempting authentication.  The
+    // storage is owned by the SSecurity object, so a copy must be taken if
+    // necessary.  Null may be returned to indicate that there is no user name
+    // for this security type.
+    virtual const char* getUserName() const = 0;
+
+    virtual const char* failureMessage() {return failureMessage_.buf;}
+  protected:
+    CharArray failureMessage_;
+  };
+
+  // SSecurityFactory creates new SSecurity instances for
+  // particular security types.
+  // The instances must be destroyed by calling destroy()
+  // on them when done.
+  class SSecurityFactory {
+  public:
+    virtual ~SSecurityFactory() {}
+    virtual SSecurity* getSSecurity(int secType, bool noAuth=false)=0;
+  };
+
+}
+#endif
diff --git a/rfb/SSecurityFactoryStandard.cxx b/rfb/SSecurityFactoryStandard.cxx
new file mode 100644
index 0000000..e3a40aa
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// SSecurityFactoryStandard
+//
+
+#include <rfb/secTypes.h>
+#include <rfb/SSecurityNone.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rfb/SSecurityFactoryStandard.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SSecurityFactoryStandard");
+
+VncAuthPasswdParameter* SSecurityFactoryStandard::vncAuthPasswd = 0;
+
+
+SSecurity* SSecurityFactoryStandard::getSSecurity(int secType, bool noAuth) {
+  switch (secType) {
+  case secTypeNone:    return new SSecurityNone();
+  case secTypeVncAuth:
+    if (!vncAuthPasswd)
+      throw rdr::Exception("No VncAuthPasswdParameter defined!");
+    return new SSecurityVncAuth(vncAuthPasswd);
+  default:
+    throw Exception("Unsupported secType?");
+  }
+}
+
+VncAuthPasswdParameter::VncAuthPasswdParameter() {
+  if (SSecurityFactoryStandard::vncAuthPasswd)
+    throw rdr::Exception("duplicate VncAuthPasswdParameter!");
+  SSecurityFactoryStandard::vncAuthPasswd = this;
+}
+
+
+VncAuthPasswdConfigParameter::VncAuthPasswdConfigParameter()
+: passwdParam("Password",
+   "Obfuscated binary encoding of the password which clients must supply to "
+   "access the server", 0, 0) {
+}
+
+char* VncAuthPasswdConfigParameter::getVncAuthPasswd() {
+  CharArray obfuscated;
+  int len;
+  passwdParam.getData((void**)&obfuscated.buf, &len);
+  printf("vnc password len=%d\n", len); // ***
+  if (len == 8) {
+    CharArray password(9);
+    memcpy(password.buf, obfuscated.buf, 8);
+    vncAuthUnobfuscatePasswd(password.buf);
+    return password.takeBuf();
+  }
+  return 0;
+}
+
+
+VncAuthPasswdFileParameter::VncAuthPasswdFileParameter()
+  : param("PasswordFile", "Password file for VNC authentication", "") {
+}
+
+char* VncAuthPasswdFileParameter::getVncAuthPasswd() {
+  CharArray fname(param.getData());
+  if (!fname.buf[0]) {
+    vlog.error("passwordFile parameter not set");
+    return 0;
+  }
+  FILE* fp = fopen(fname.buf, "r");
+  if (!fp) {
+    vlog.error("opening password file '%s' failed",fname.buf);
+    return 0;
+  }
+  CharArray passwd(9);
+  int len = fread(passwd.buf, 1, 9, fp);
+  fclose(fp);
+  if (len != 8) {
+    vlog.error("password file '%s' is the wrong length",fname.buf);
+    return 0;
+  }
+  vncAuthUnobfuscatePasswd(passwd.buf);
+  return passwd.takeBuf();
+}
+
diff --git a/rfb/SSecurityFactoryStandard.h b/rfb/SSecurityFactoryStandard.h
new file mode 100644
index 0000000..5fced04
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// SSecurityFactoryStandard - implementation of the SSecurityFactory
+// interface.
+//
+// Server implementations must define an instance of a
+// VncAuthPasswdParameter-based class somewhere.  Any class based on
+// VncAuthPasswdParameter will automatically register itself as the
+// password parameter to be used by the Standard factory.
+//
+// Two implementations are provided here:
+//
+// VncAuthPasswdConfigParameter - reads the password from the Binary
+//                                parameter "Password".
+// VncAuthPasswdFileParameter   - reads the password from the file named
+//                                in the String parameter "PasswordFile".
+//
+// This factory supports only the "None" and "VncAuth" security types.
+//
+
+#ifndef __RFB_SSECURITYFACTORY_STANDARD_H__
+#define __RFB_SSECURITYFACTORY_STANDARD_H__
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class VncAuthPasswdParameter : public VncAuthPasswdGetter {
+  public:
+    VncAuthPasswdParameter();
+    virtual ~VncAuthPasswdParameter() {}
+  };
+
+  class SSecurityFactoryStandard : public SSecurityFactory {
+  public:
+    virtual SSecurity* getSSecurity(int secType, bool noAuth);
+    static VncAuthPasswdParameter* vncAuthPasswd;
+  };
+
+  class VncAuthPasswdConfigParameter : public VncAuthPasswdParameter {
+  public:
+    VncAuthPasswdConfigParameter();
+    virtual char* getVncAuthPasswd();
+  protected:
+    BinaryParameter passwdParam;
+  };
+
+  class VncAuthPasswdFileParameter : public VncAuthPasswdParameter {
+  public:
+    VncAuthPasswdFileParameter();
+    virtual char* getVncAuthPasswd();
+    StringParameter param;
+  };
+
+}
+#endif
diff --git a/rfb/SSecurityNone.h b/rfb/SSecurityNone.h
new file mode 100644
index 0000000..09b2db4
--- /dev/null
+++ b/rfb/SSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// SSecurityNone.h
+//
+
+#ifndef __SSECURITYNONE_H__
+#define __SSECURITYNONE_H__
+
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+  class SSecurityNone : public SSecurity {
+  public:
+    virtual bool processMsg(SConnection* sc, bool* done) {
+      *done = true; return true;
+    }
+    virtual int getType() const {return secTypeNone;}
+    virtual const char* getUserName() const {return 0;}
+  };
+}
+#endif
diff --git a/rfb/SSecurityVncAuth.cxx b/rfb/SSecurityVncAuth.cxx
new file mode 100644
index 0000000..532d1a6
--- /dev/null
+++ b/rfb/SSecurityVncAuth.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// SSecurityVncAuth
+//
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rdr/RandomStream.h>
+#include <rfb/SConnection.h>
+#include <rfb/vncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <string.h>
+#include <stdio.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+
+SSecurityVncAuth::SSecurityVncAuth(VncAuthPasswdGetter* pg_)
+  : sentChallenge(false), responsePos(0), pg(pg_)
+{
+}
+
+bool SSecurityVncAuth::processMsg(SConnection* sc, bool* done)
+{
+  *done = false;
+  rdr::InStream* is = sc->getInStream();
+  rdr::OutStream* os = sc->getOutStream();
+
+  if (!sentChallenge) {
+    rdr::RandomStream rs;
+    rs.readBytes(challenge, vncAuthChallengeSize);
+    os->writeBytes(challenge, vncAuthChallengeSize);
+    os->flush();
+    sentChallenge = true;
+    return true;
+  }
+
+  if (responsePos >= vncAuthChallengeSize) return false;
+  while (is->checkNoWait(1) && responsePos < vncAuthChallengeSize) {
+    response[responsePos++] = is->readU8();
+  }
+
+  if (responsePos < vncAuthChallengeSize) return true;
+
+  CharArray passwd(pg->getVncAuthPasswd());
+
+  // Beyond this point, there is no more VNCAuth protocol to perform.
+  *done = true;
+
+  if (!passwd.buf) {
+    failureMessage_.buf = strDup("No password configured for VNC Auth");
+    vlog.error(failureMessage_.buf);
+    return false;
+  }
+
+  vncAuthEncryptChallenge(challenge, passwd.buf);
+  memset(passwd.buf, 0, strlen(passwd.buf));
+
+  if (memcmp(challenge, response, vncAuthChallengeSize) != 0) {
+    return false;
+  }
+
+  return true;
+}
diff --git a/rfb/SSecurityVncAuth.h b/rfb/SSecurityVncAuth.h
new file mode 100644
index 0000000..edbd720
--- /dev/null
+++ b/rfb/SSecurityVncAuth.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+// SSecurityVncAuth - legacy VNC authentication protocol.
+// The getPasswd call can be overridden if you wish to store
+// the VncAuth password in an implementation-specific place.
+// Otherwise, the password is read from a BinaryParameter
+// called Password.
+
+#ifndef __RFB_SSECURITYVNCAUTH_H__
+#define __RFB_SSECURITYVNCAUTH_H__
+
+#include <rfb/SSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+  class VncAuthPasswdGetter {
+  public:
+    // getPasswd() returns a string or null if unsuccessful.  The
+    // SSecurityVncAuth object delete[]s the string when done.
+    virtual char* getVncAuthPasswd()=0;
+  };
+
+  class SSecurityVncAuth : public SSecurity {
+  public:
+    SSecurityVncAuth(VncAuthPasswdGetter* pg);
+    virtual bool processMsg(SConnection* sc, bool* done);
+    virtual int getType() const {return secTypeVncAuth;}
+    virtual const char* getUserName() const {return 0;}
+  private:
+    rdr::U8 challenge[vncAuthChallengeSize];
+    rdr::U8 response[vncAuthChallengeSize];
+    bool sentChallenge;
+    int responsePos;
+    VncAuthPasswdGetter* pg;
+  };
+}
+#endif
diff --git a/rfb/ServerCore.cxx b/rfb/ServerCore.cxx
new file mode 100644
index 0000000..7a20c56
--- /dev/null
+++ b/rfb/ServerCore.cxx
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- ServerCore.cxx
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#include <string.h>
+#include <rfb/util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/vncAuth.h>
+
+rfb::IntParameter rfb::Server::idleTimeout
+("IdleTimeout",
+ "The number of seconds after which an idle VNC connection will be dropped",
+ 3600);
+rfb::IntParameter rfb::Server::clientWaitTimeMillis
+("ClientWaitTimeMillis",
+ "The number of milliseconds to wait for a client which is no longer "
+ "responding",
+ 20000);
+rfb::StringParameter rfb::Server::sec_types
+("SecurityTypes",
+ "Specify which security scheme to use for incoming connections (None, VncAuth)",
+ "VncAuth");
+rfb::StringParameter rfb::Server::rev_sec_types
+("ReverseSecurityTypes",
+ "Specify encryption scheme to use for reverse connections (None)",
+ "None");
+rfb::BoolParameter rfb::Server::compareFB
+("CompareFB",
+ "Perform pixel comparison on framebuffer to reduce unnecessary updates",
+ true);
+rfb::BoolParameter rfb::Server::protocol3_3
+("Protocol3.3",
+ "Always use protocol version 3.3 for backwards compatibility with "
+ "badly-behaved clients",
+ false);
+rfb::BoolParameter rfb::Server::alwaysShared
+("AlwaysShared",
+ "Always treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::neverShared
+("NeverShared",
+ "Never treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::disconnectClients
+("DisconnectClients",
+ "Disconnect existing clients if an incoming connection is non-shared. "
+ "If combined with NeverShared then new connections will be refused "
+ "while there is a client active",
+ true);
+rfb::BoolParameter rfb::Server::acceptKeyEvents
+("AcceptKeyEvents",
+ "Accept key press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptPointerEvents
+("AcceptPointerEvents",
+ "Accept pointer press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptCutText
+("AcceptCutText",
+ "Accept clipboard updates from clients.",
+ true);
+rfb::BoolParameter rfb::Server::sendCutText
+("SendCutText",
+ "Send clipboard changes to clients.",
+ true);
+rfb::BoolParameter rfb::Server::queryConnect
+("QueryConnect",
+ "Prompt the local user to accept or reject incoming connections.",
+ false);
+rfb::IntParameter rfb::Server::blacklistLevel
+("BlacklistLevel",
+ "When to test whether particular host should be blacklisted.  (0 = Never, "
+ "1 = Test before authentication, 2 = Test on connect)",
+ 1);
diff --git a/rfb/ServerCore.h b/rfb/ServerCore.h
new file mode 100644
index 0000000..74d7443
--- /dev/null
+++ b/rfb/ServerCore.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- ServerCore.h
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#ifndef __RFB_SERVER_CORE_H__
+#define __RFB_SERVER_CORE_H__
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class Server {
+  public:
+
+    static IntParameter idleTimeout;
+    static IntParameter clientWaitTimeMillis;
+    static StringParameter sec_types;
+    static StringParameter rev_sec_types;
+    static BoolParameter compareFB;
+    static BoolParameter protocol3_3;
+    static BoolParameter alwaysShared;
+    static BoolParameter neverShared;
+    static BoolParameter disconnectClients;
+    static BoolParameter acceptKeyEvents;
+    static BoolParameter acceptPointerEvents;
+    static BoolParameter acceptCutText;
+    static BoolParameter sendCutText;
+    static BoolParameter queryConnect;
+    static IntParameter blacklistLevel;
+
+  };
+
+};
+
+#endif // __RFB_SERVER_CORE_H__
+
diff --git a/rfb/Threading.h b/rfb/Threading.h
new file mode 100644
index 0000000..effc436
--- /dev/null
+++ b/rfb/Threading.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Threading.h
+// General purpose threading interface.
+// If the current platform supports threading then __RFB_THREADING_IMPL
+// will be defined after this header has been included.
+
+#ifndef __RFB_THREADING_H__
+#define __RFB_THREADING_H__
+
+#ifdef WIN32
+#include <rfb/win32/Threading_win32.h>
+#endif
+
+#endif // __RFB_THREADING_H__
diff --git a/rfb/TransImageGetter.cxx b/rfb/TransImageGetter.cxx
new file mode 100644
index 0000000..693b18c
--- /dev/null
+++ b/rfb/TransImageGetter.cxx
@@ -0,0 +1,278 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ColourMap.h>
+#include <rfb/TrueColourMap.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourCube.h>
+#include <rfb/TransImageGetter.h>
+
+using namespace rfb;
+
+const PixelFormat bgr233PF(8, 8, false, true, 7, 7, 3, 0, 3, 6);
+
+static void noTransFn(void* table_,
+                      const PixelFormat& inPF, void* inPtr, int inStride,
+                      const PixelFormat& outPF, void* outPtr, int outStride,
+                      int width, int height)
+{
+  rdr::U8* ip = (rdr::U8*)inPtr;
+  rdr::U8* op = (rdr::U8*)outPtr;
+  int inStrideBytes = inStride * (inPF.bpp/8);
+  int outStrideBytes = outStride * (outPF.bpp/8);
+  int widthBytes = width * (outPF.bpp/8);
+
+  while (height > 0) {
+    memcpy(op, ip, widthBytes);
+    ip += inStrideBytes;
+    op += outStrideBytes;
+    height--;
+  }
+}
+
+#define BPPOUT 8
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 16
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 32
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+
+// Translation functions.  Note that transSimple* is only used for 8/16bpp and
+// transRGB* is used for 16/32bpp
+
+static transFnType transSimpleFns[][3] = {
+  { transSimple8to8,  transSimple8to16,  transSimple8to32 },
+  { transSimple16to8, transSimple16to16, transSimple16to32 },
+};
+static transFnType transRGBFns[][3] = {
+  { transRGB16to8, transRGB16to16, transRGB16to32 },
+  { transRGB32to8, transRGB32to16, transRGB32to32 }
+};
+static transFnType transRGBCubeFns[][3] = {
+  { transRGBCube16to8, transRGBCube16to16, transRGBCube16to32 },
+  { transRGBCube32to8, transRGBCube32to16, transRGBCube32to32 }
+};
+
+// Table initialisation functions.
+
+typedef void (*initCMtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 ColourMap* cm, const PixelFormat& outPF);
+typedef void (*initTCtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 const PixelFormat& outPF);
+typedef void (*initCMtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourMap* cm, ColourCube* cube);
+typedef void (*initTCtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourCube* cube);
+
+
+static initCMtoTCFnType initSimpleCMtoTCFns[] = {
+    initSimpleCMtoTC8, initSimpleCMtoTC16, initSimpleCMtoTC32
+};
+
+static initTCtoTCFnType initSimpleTCtoTCFns[] = {
+    initSimpleTCtoTC8, initSimpleTCtoTC16, initSimpleTCtoTC32
+};
+
+static initCMtoCubeFnType initSimpleCMtoCubeFns[] = {
+    initSimpleCMtoCube8, initSimpleCMtoCube16, initSimpleCMtoCube32
+};
+
+static initTCtoCubeFnType initSimpleTCtoCubeFns[] = {
+    initSimpleTCtoCube8, initSimpleTCtoCube16, initSimpleTCtoCube32
+};
+
+static initTCtoTCFnType initRGBTCtoTCFns[] = {
+    initRGBTCtoTC8, initRGBTCtoTC16, initRGBTCtoTC32
+};
+
+static initTCtoCubeFnType initRGBTCtoCubeFns[] = {
+    initRGBTCtoCube8, initRGBTCtoCube16, initRGBTCtoCube32
+};
+
+
+TransImageGetter::TransImageGetter(bool econ)
+  : economic(econ), pb(0), table(0), transFn(0), cube(0)
+{
+}
+
+TransImageGetter::~TransImageGetter()
+{
+  delete [] table;
+}
+
+void TransImageGetter::init(PixelBuffer* pb_, const PixelFormat& out,
+                            SMsgWriter* writer, ColourCube* cube_)
+{
+  pb = pb_;
+  outPF = out;
+  transFn = 0;
+  cube = cube_;
+  const PixelFormat& inPF = pb->getPF();
+
+  if ((inPF.bpp != 8) && (inPF.bpp != 16) && (inPF.bpp != 32))
+    throw Exception("TransImageGetter: bpp in not 8, 16 or 32");
+
+  if ((outPF.bpp != 8) && (outPF.bpp != 16) && (outPF.bpp != 32))
+    throw Exception("TransImageGetter: bpp out not 8, 16 or 32");
+
+  if (!outPF.trueColour) {
+    if (outPF.bpp != 8)
+      throw Exception("TransImageGetter: outPF has colour map but not 8bpp");
+
+    if (!inPF.trueColour) {
+      if (inPF.bpp != 8)
+        throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+
+      // CM to CM/Cube
+
+      if (cube) {
+        transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+        (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, inPF,
+                                                pb->getColourMap(), cube);
+      } else {
+        transFn = noTransFn;
+        setColourMapEntries(0, 256, writer);
+      }
+      return;
+    }
+
+    // TC to CM/Cube
+
+    ColourCube defaultCube(6,6,6);
+    if (!cube) cube = &defaultCube;
+
+    if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+      transFn = transRGBCubeFns[inPF.bpp/32][outPF.bpp/16];
+      (*initRGBTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+    } else {
+      transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+      (*initSimpleTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+    }
+
+    if (cube != &defaultCube)
+      return;
+
+    if (writer) writer->writeSetColourMapEntries(0, 216, cube);
+    cube = 0;
+    return;
+  }
+
+  if (inPF.equal(outPF)) {
+    transFn = noTransFn;
+    return;
+  }
+
+  if (!inPF.trueColour) {
+
+    // CM to TC
+
+    if (inPF.bpp != 8)
+      throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+    transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, pb->getColourMap(),
+                                          outPF);
+    return;
+  }
+
+  // TC to TC
+
+  if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+    transFn = transRGBFns[inPF.bpp/32][outPF.bpp/16];
+    (*initRGBTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  } else {
+    transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+    (*initSimpleTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  }
+}
+
+void TransImageGetter::setColourMapEntries(int firstCol, int nCols,
+                                           SMsgWriter* writer)
+{
+  if (nCols == 0)
+    nCols = (1 << pb->getPF().depth) - firstCol;
+  if (pb->getPF().trueColour) return; // shouldn't be called in this case
+
+  if (outPF.trueColour) {
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, pb->getPF(),
+                                          pb->getColourMap(), outPF);
+  } else if (cube) {
+    (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, pb->getPF(),
+                                            pb->getColourMap(), cube);
+  } else if (writer && pb->getColourMap()) {
+    writer->writeSetColourMapEntries(firstCol, nCols, pb->getColourMap());
+  }
+}
+
+void TransImageGetter::getImage(void* outPtr, const Rect& r, int outStride)
+{
+  if (!transFn)
+    throw Exception("TransImageGetter: not initialised yet");
+
+  int inStride;
+  const rdr::U8* inPtr = pb->getPixelsR(r.translate(offset.negate()), &inStride);
+
+  if (!outStride) outStride = r.width();
+
+  (*transFn)(table, pb->getPF(), (void*)inPtr, inStride,
+             outPF, outPtr, outStride, r.width(), r.height());
+}
+
+void TransImageGetter::translatePixels(void* inPtr, void* outPtr,
+                                       int nPixels) const
+{
+  (*transFn)(table, pb->getPF(), inPtr, nPixels,
+             outPF, outPtr, nPixels, nPixels, 1);
+}
diff --git a/rfb/TransImageGetter.h b/rfb/TransImageGetter.h
new file mode 100644
index 0000000..60ab069
--- /dev/null
+++ b/rfb/TransImageGetter.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TransImageGetter - class to perform translation between pixel formats,
+// implementing the ImageGetter interface.
+//
+
+#ifndef __RFB_TRANSIMAGEGETTER_H__
+#define __RFB_TRANSIMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ImageGetter.h>
+
+namespace rfb {
+  typedef void (*transFnType)(void* table_,
+                              const PixelFormat& inPF, void* inPtr,
+                              int inStride,
+                              const PixelFormat& outPF, void* outPtr,
+                              int outStride, int width, int height);
+
+  class SMsgWriter;
+  class ColourMap;
+  class PixelBuffer;
+  class ColourCube;
+
+  class TransImageGetter : public ImageGetter {
+  public:
+
+    TransImageGetter(bool econ=false);
+    virtual ~TransImageGetter();
+
+    // init() is called to initialise the translation tables.  The PixelBuffer
+    // argument gives the source data and format details, outPF gives the
+    // client's pixel format.  If the client has a colour map, then the writer
+    // argument is used to send a SetColourMapEntries message to the client.
+
+    void init(PixelBuffer* pb, const PixelFormat& outPF, SMsgWriter* writer=0,
+              ColourCube* cube=0);
+
+    // setColourMapEntries() is called when the PixelBuffer has a colour map
+    // which has changed.  firstColour and nColours specify which part of the
+    // colour map has changed.  If nColours is 0, this means the rest of the
+    // colour map.  The PixelBuffer previously passed to init() must have a
+    // valid ColourMap object.  If the client also has a colour map, then the
+    // writer argument is used to send a SetColourMapEntries message to the
+    // client.  If the client is true colour then instead we update the
+    // internal translation table - in this case the caller should also make
+    // sure that the client receives an update of the relevant parts of the
+    // framebuffer (the simplest thing to do is just update the whole
+    // framebuffer, though it is possible to be smarter than this).
+
+    void setColourMapEntries(int firstColour, int nColours,
+                             SMsgWriter* writer=0);
+
+    // getImage() gets the given rectangle of data from the PixelBuffer,
+    // translates it into the client's pixel format and puts it in the buffer
+    // pointed to by the outPtr argument.  The optional outStride argument can
+    // be used where padding is required between the output scanlines (the
+    // padding will be outStride-r.width() pixels).
+    void getImage(void* outPtr, const Rect& r, int outStride=0);
+
+    // translatePixels() translates the given number of pixels from inPtr,
+    // putting it into the buffer pointed to by outPtr.  The pixels at inPtr
+    // should be in the same format as the PixelBuffer, and the translated
+    // pixels will be in the format previously given by the outPF argument to
+    // init().  Note that this call does not use the PixelBuffer's pixel data.
+    void translatePixels(void* inPtr, void* outPtr, int nPixels) const;
+
+    // setPixelBuffer() changes the pixel buffer to be used.  The new pixel
+    // buffer MUST have the same pixel format as the old one - if not you
+    // should call init() instead.
+    void setPixelBuffer(PixelBuffer* pb_) { pb = pb_; }
+
+    // setOffset() sets an offset which is subtracted from the coordinates of
+    // the rectangle given to getImage().
+    void setOffset(const Point& offset_) { offset = offset_; }
+
+  private:
+    bool economic;
+    PixelBuffer* pb;
+    PixelFormat outPF;
+    rdr::U8* table;
+    transFnType transFn;
+    ColourCube* cube;
+    Point offset;
+  };
+}
+#endif
diff --git a/rfb/TrueColourMap.h b/rfb/TrueColourMap.h
new file mode 100644
index 0000000..c0d4907
--- /dev/null
+++ b/rfb/TrueColourMap.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_TRUECOLOURMAP_H__
+#define __RFB_TRUECOLOURMAP_H__
+
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+  class TrueColourMap : public ColourMap {
+  public:
+    TrueColourMap(const PixelFormat& pf_) : pf(pf_) {}
+
+    virtual void lookup(int i, int* r, int* g, int* b)
+    {
+      *r = (((i >> pf.redShift  ) & pf.redMax)
+            * 65535 + pf.redMax/2) / pf.redMax;
+      *g = (((i >> pf.greenShift) & pf.greenMax)
+            * 65535 + pf.greenMax/2) / pf.greenMax;
+      *b = (((i >> pf.blueShift) & pf.blueMax)
+            * 65535 + pf.blueMax/2) / pf.blueMax;
+    }
+  private:
+    PixelFormat pf;
+  };
+}
+#endif
diff --git a/rfb/UpdateTracker.cxx b/rfb/UpdateTracker.cxx
new file mode 100644
index 0000000..cc0fb10
--- /dev/null
+++ b/rfb/UpdateTracker.cxx
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- rfbUpdateTracker.cpp
+//
+// Tracks updated regions and a region-copy event, too
+//
+
+#include <assert.h>
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("UpdateTracker");
+
+// -=- ClippedUpdateTracker
+
+void ClippedUpdateTracker::add_changed(const Region &region) {
+  child.add_changed(region.intersect(cliprgn));
+}
+
+void ClippedUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+  // Clip the destination to the display area
+  Region clipdest = dest.intersect(cliprgn);
+  if (clipdest.is_empty())  return;
+
+  // Clip the source to the screen
+  Region tmp = clipdest;
+  tmp.translate(delta.negate());
+  tmp.assign_intersect(cliprgn);
+  if (!tmp.is_empty()) {
+    // Translate the source back to a destination region
+    tmp.translate(delta);
+
+    // Pass the copy region to the child tracker
+    child.add_copied(tmp, delta);
+  }
+
+  // And add any bits that we had to remove to the changed region
+  tmp = clipdest.subtract(tmp);
+  if (!tmp.is_empty()) {
+    child.add_changed(tmp);
+  }
+}
+
+// SimpleUpdateTracker
+
+SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) {
+  copy_enabled = use_copyrect;
+}
+
+SimpleUpdateTracker::~SimpleUpdateTracker() {
+}
+
+void SimpleUpdateTracker::enable_copyrect(bool enable) {
+  if (!enable && copy_enabled) {
+    add_changed(copied);
+    copied.clear();
+  }
+  copy_enabled=enable;
+}
+
+void SimpleUpdateTracker::add_changed(const Region &region) {
+  changed.assign_union(region);
+}
+
+void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+  // Do we support copyrect?
+  if (!copy_enabled) {
+    add_changed(dest);
+    return;
+  }
+
+  // Is there anything to do?
+  if (dest.is_empty()) return;
+
+  // Calculate whether any of this copy can be treated as a continuation
+  // of an earlier one
+  Region src = dest;
+  src.translate(delta.negate());
+  Region overlap = src.intersect(copied);
+
+  if (overlap.is_empty()) {
+    // There is no overlap
+
+    Rect newbr = dest.get_bounding_rect();
+    Rect oldbr = copied.get_bounding_rect();
+    if (oldbr.area() > newbr.area()) {
+      // Old copyrect is (probably) bigger - use it
+      changed.assign_union(dest);
+    } else {
+      // New copyrect is probably bigger
+      // Use the new one
+      // But be careful not to copy stuff that still needs
+      // to be updated.
+      Region invalid_src = src.intersect(changed);
+      invalid_src.translate(delta);
+      changed.assign_union(invalid_src);
+      changed.assign_union(copied);
+      copied = dest;
+      copy_delta = delta;
+    }
+    return;
+  }
+
+  Region invalid_src = overlap.intersect(changed);
+  invalid_src.translate(delta);
+  changed.assign_union(invalid_src);
+  
+  overlap.translate(delta);
+
+  Region nonoverlapped_copied = dest.union_(copied).subtract(overlap);
+  changed.assign_union(nonoverlapped_copied);
+
+  copied = overlap;
+  copy_delta = copy_delta.translate(delta);
+
+  return;
+}
+
+void SimpleUpdateTracker::subtract(const Region& region) {
+  copied.assign_subtract(region);
+  changed.assign_subtract(region);
+}
+
+void SimpleUpdateTracker::get_update(UpdateInfo* info, const Region& clip)
+{
+  copied.assign_subtract(changed);
+  info->changed = changed.intersect(clip);
+  info->copied = copied.intersect(clip);
+  info->copy_delta = copy_delta;
+}
+
+void SimpleUpdateTracker::flush_update(UpdateTracker &info,
+                                       const Region &cliprgn)
+{
+  Region copied_clipped = copied.intersect(cliprgn);
+  Region changed_clipped = changed.intersect(cliprgn);
+  copied.assign_subtract(copied_clipped);
+  changed.assign_subtract(changed_clipped);
+  if (!copied_clipped.is_empty()) {
+    info.add_copied(copied_clipped, copy_delta);
+  }
+  if (!changed_clipped.is_empty())
+    info.add_changed(changed_clipped);
+}
+
+void SimpleUpdateTracker::get_update(UpdateTracker &to) const {
+  if (!copied.is_empty()) {
+    to.add_copied(copied, copy_delta);
+  }
+  if (!changed.is_empty()) {
+    to.add_changed(changed);
+  }
+}
diff --git a/rfb/UpdateTracker.h b/rfb/UpdateTracker.h
new file mode 100644
index 0000000..5015a25
--- /dev/null
+++ b/rfb/UpdateTracker.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __RFB_UPDATETRACKER_INCLUDED__
+#define __RFB_UPDATETRACKER_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <rfb/Region.h>
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+  class UpdateInfo {
+  public:
+    Region changed;
+    Region copied;
+    Point copy_delta;
+    bool is_empty() const {
+      return copied.is_empty() && changed.is_empty();
+    }
+    int numRects() const {
+      return copied.numRects() + changed.numRects();
+    }
+  };
+
+  class UpdateTracker {
+  public:
+    UpdateTracker() {};
+    virtual ~UpdateTracker() {};
+
+    virtual void add_changed(const Region &region) = 0;
+    virtual void add_copied(const Region &dest, const Point &delta) = 0;
+  };
+
+  class ClippedUpdateTracker : public UpdateTracker {
+  public:
+    ClippedUpdateTracker(UpdateTracker &child_) : child(child_) {};
+    ClippedUpdateTracker(UpdateTracker &child_,
+      const Region &cliprgn_) : child(child_), cliprgn(cliprgn_) {};
+    virtual ~ClippedUpdateTracker() {};
+
+    virtual void set_clip_region(const Region cliprgn_) {cliprgn = cliprgn_;};
+
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+  protected:
+    UpdateTracker &child;
+    Region cliprgn;
+  };
+
+  class SimpleUpdateTracker : public UpdateTracker {
+  public:
+    SimpleUpdateTracker(bool use_copyrect=false);
+    virtual ~SimpleUpdateTracker();
+
+    virtual void enable_copyrect(bool enable);
+
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+    virtual void subtract(const Region& region);
+
+    // Fill the supplied UpdateInfo structure with update information
+    virtual void get_update(UpdateInfo* info, const Region& cliprgn);
+
+    // Pass the current updates to the supplied tracker
+    virtual void get_update(UpdateTracker &to) const;
+
+    // Also removes the updates that are returned from this update tracker
+    virtual void flush_update(UpdateTracker &to, const Region &cliprgn);
+
+
+    // Get the changed/copied regions
+    const Region& get_changed() const {return changed;}
+    const Region& get_copied() const {return copied;}
+    const Point& get_delta() const {return copy_delta;}
+
+    // Move the entire update region by an offset
+    void translate(const Point& p) {changed.translate(p); copied.translate(p);}
+
+    virtual bool is_empty() const {return changed.is_empty() && copied.is_empty();}
+
+    virtual void clear() {changed.clear(); copied.clear();};
+  protected:
+    Region changed;
+    Region copied;
+    Point copy_delta;
+    bool copy_enabled;
+  };
+
+}
+
+#endif // __RFB_UPDATETRACKER_INCLUDED__
diff --git a/rfb/UserPasswdGetter.h b/rfb/UserPasswdGetter.h
new file mode 100644
index 0000000..c242ed0
--- /dev/null
+++ b/rfb/UserPasswdGetter.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_USERPASSWDGETTER_H__
+#define __RFB_USERPASSWDGETTER_H__
+namespace rfb {
+  class UserPasswdGetter {
+  public:
+    // getUserPasswd gets the username and password.  This might
+    // involve a dialog, getpass(), etc.  The user buffer pointer
+    // can be null, in which case no user name will be retrieved.
+    // The caller MUST delete [] the result(s) iff the
+    // call succeeds (returns true), and ignore them if failed.
+    virtual bool getUserPasswd(char** user, char** password)=0;
+  };
+}
+#endif
diff --git a/rfb/VNCSConnectionST.cxx b/rfb/VNCSConnectionST.cxx
new file mode 100644
index 0000000..3ae378e
--- /dev/null
+++ b/rfb/VNCSConnectionST.cxx
@@ -0,0 +1,629 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rfb/VNCSConnectionST.h>
+#include <rfb/LogWriter.h>
+#include <rfb/secTypes.h>
+#include <rfb/ServerCore.h>
+#include <rfb/ComparingUpdateTracker.h>
+#define XK_MISCELLANY
+#define XK_XKB_KEYS
+#include <rfb/keysymdef.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VNCSConnST");
+
+VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
+                                   bool reverse)
+  : sock(s), reverseConnection(reverse), server(server_),
+    image_getter(server->useEconomicTranslate),
+    drawRenderedCursor(false), removeRenderedCursor(false),
+    pointerEventTime(0), accessRights(AccessDefault)
+{
+  setStreams(&sock->inStream(), &sock->outStream());
+  peerEndpoint.buf = sock->getPeerEndpoint();
+  VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
+
+  setSocketTimeouts();
+  lastEventTime = time(0);
+
+  // Initialise security
+  CharArray sec_types_str;
+  if (reverseConnection)
+    sec_types_str.buf = rfb::Server::rev_sec_types.getData();
+  else
+    sec_types_str.buf = rfb::Server::sec_types.getData();
+  std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
+  std::list<int>::iterator i;
+  for (i=sec_types.begin(); i!=sec_types.end(); i++) {
+    addSecType(*i);
+  }
+
+  server->clients.push_front(this);
+}
+
+
+VNCSConnectionST::~VNCSConnectionST()
+{
+  // If we reach here then VNCServerST is deleting us!
+  VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
+                                    peerEndpoint.buf, closeReason.buf);
+
+  // Release any keys the client still had pressed
+  std::set<rdr::U32>::iterator i;
+  for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
+    server->desktop->keyEvent(*i, false);
+  if (server->pointerClient == this)
+    server->pointerClient = 0;
+
+  // Remove this client from the server
+  server->clients.remove(this);
+}
+
+
+// Methods called from VNCServerST
+
+bool VNCSConnectionST::init()
+{
+  try {
+    initialiseProtocol();
+  } catch (rdr::Exception& e) {
+    close(e.str());
+    return false;
+  }
+  return true;
+}
+
+void VNCSConnectionST::close(const char* reason)
+{
+  // Log the reason for the close
+  if (!closeReason.buf)
+    closeReason.buf = strDup(reason);
+  else
+    vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
+
+  // Just shutdown the socket.  This will cause processMessages to
+  // eventually fail, causing us and our socket to be deleted.
+  sock->shutdown();
+  setState(RFBSTATE_CLOSING);
+}
+
+
+bool VNCSConnectionST::processMessages()
+{
+  if (state() == RFBSTATE_CLOSING) return false;
+  try {
+    // - Now set appropriate socket timeouts and process data
+    setSocketTimeouts();
+    bool clientsReadyBefore = server->clientsReadyForUpdate();
+
+    while (getInStream()->checkNoWait(1)) {
+      processMsg();
+    }
+
+    if (!clientsReadyBefore && !requested.is_empty())
+      server->desktop->framebufferUpdateRequest();
+
+    return true;
+
+  } catch (rdr::EndOfStream&) {
+    close("Clean disconnection");
+  } catch (rdr::Exception &e) {
+    close(e.str());
+  }
+  return false;
+}
+
+void VNCSConnectionST::writeFramebufferUpdateOrClose()
+{
+  try {
+    writeFramebufferUpdate();
+  } catch(rdr::Exception &e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::pixelBufferChange()
+{
+  try {
+    if (!authenticated()) return;
+    if (cp.width && cp.height && (server->pb->width() != cp.width ||
+                                  server->pb->height() != cp.height))
+    {
+      // We need to clip the next update to the new size, but also add any
+      // extra bits if it's bigger.  If we wanted to do this exactly, something
+      // like the code below would do it, but at the moment we just update the
+      // entire new size.  However, we do need to clip the renderedCursorRect
+      // because that might be added to updates in writeFramebufferUpdate().
+
+      //updates.intersect(server->pb->getRect());
+      //
+      //if (server->pb->width() > cp.width)
+      //  updates.add_changed(Rect(cp.width, 0, server->pb->width(),
+      //                           server->pb->height()));
+      //if (server->pb->height() > cp.height)
+      //  updates.add_changed(Rect(0, cp.height, cp.width,
+      //                           server->pb->height()));
+
+      renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
+
+      cp.width = server->pb->width();
+      cp.height = server->pb->height();
+      if (!writer()->writeSetDesktopSize()) {
+        close("Client does not support desktop resize");
+        return;
+      }
+    }
+    // Just update the whole screen at the moment because we're too lazy to
+    // work out what's actually changed.
+    updates.clear();
+    updates.add_changed(server->pb->getRect());
+    vlog.debug("pixel buffer changed - re-initialising image getter");
+    image_getter.init(server->pb, cp.pf(), writer());
+    if (writer()->needFakeUpdate())
+      writeFramebufferUpdate();
+  } catch(rdr::Exception &e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
+{
+  try {
+    setColourMapEntries(firstColour, nColours);
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::bell()
+{
+  try {
+    if (state() == RFBSTATE_NORMAL) writer()->writeBell();
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::serverCutText(const char *str, int len)
+{
+  try {
+    if (!(accessRights & AccessCutText)) return;
+    if (!rfb::Server::sendCutText) return;
+    if (state() == RFBSTATE_NORMAL)
+      writer()->writeServerCutText(str, len);
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::setCursorOrClose()
+{
+  try {
+    setCursor();
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+int VNCSConnectionST::checkIdleTimeout()
+{
+  int idleTimeout = rfb::Server::idleTimeout;
+  if (idleTimeout == 0) return 0;
+  if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
+    idleTimeout = 15; // minimum of 15 seconds while authenticating
+  time_t now = time(0);
+  if (now < lastEventTime) {
+    // Someone must have set the time backwards.  Set lastEventTime so that the
+    // idleTimeout will count from now.
+    vlog.info("Time has gone backwards - resetting idle timeout");
+    lastEventTime = now;
+  }
+  int timeLeft = lastEventTime + idleTimeout - now;
+  if (timeLeft < -60) {
+    // Our callback is over a minute late - someone must have set the time
+    // forwards.  Set lastEventTime so that the idleTimeout will count from
+    // now.
+    vlog.info("Time has gone forwards - resetting idle timeout");
+    lastEventTime = now;
+    return idleTimeout;
+  }
+  if (timeLeft <= 0) {
+    close("Idle timeout");
+    return 0;
+  }
+  return timeLeft * 1000;
+}
+
+// renderedCursorChange() is called whenever the server-side rendered cursor
+// changes shape or position.  It ensures that the next update will clean up
+// the old rendered cursor and if necessary draw the new rendered cursor.
+
+void VNCSConnectionST::renderedCursorChange()
+{
+  if (state() != RFBSTATE_NORMAL) return;
+  removeRenderedCursor = true;
+  if (needRenderedCursor())
+    drawRenderedCursor = true;
+}
+
+// needRenderedCursor() returns true if this client needs the server-side
+// rendered cursor.  This may be because it does not support local cursor or
+// because the current cursor position has not been set by this client.
+// Unfortunately we can't know for sure when the current cursor position has
+// been set by this client.  We guess that this is the case when the current
+// cursor position is the same as the last pointer event from this client, or
+// if it is a very short time since this client's last pointer event (up to a
+// second).  [ Ideally we should do finer-grained timing here and make the time
+// configurable, but I don't think it's that important. ]
+
+bool VNCSConnectionST::needRenderedCursor()
+{
+  return (state() == RFBSTATE_NORMAL
+          && (!cp.supportsLocalCursor
+              || (!server->cursorPos.equals(pointerEventPos) &&
+                  (time(0) - pointerEventTime) > 0)));
+}
+
+
+void VNCSConnectionST::approveConnectionOrClose(bool accept,
+                                                const char* reason)
+{
+  try {
+    approveConnection(accept, reason);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+
+// -=- Callbacks from SConnection
+
+void VNCSConnectionST::versionReceived() {
+  CharArray address(sock->getPeerAddress());
+  if ((rfb::Server::blacklistLevel == 1) &&
+      server->blHosts->isBlackmarked(address.buf)) {
+    server->connectionsLog.error("blacklisted: %s", address.buf);
+    throwConnFailedException("Too many security failures");
+  }
+}
+
+SSecurity* VNCSConnectionST::getSSecurity(int secType) {
+  if (!server->securityFactory)
+    throw rdr::Exception("no SSecurityFactory registered!");
+  return server->securityFactory->getSSecurity(secType, reverseConnection);
+}
+
+void VNCSConnectionST::authSuccess()
+{
+  lastEventTime = time(0);
+
+  // - Authentication succeeded - clear from blacklist
+  CharArray name; name.buf = sock->getPeerAddress();
+  server->blHosts->clearBlackmark(name.buf);
+
+  server->startDesktop();
+
+  // - Set the connection parameters appropriately
+  cp.width = server->pb->width();
+  cp.height = server->pb->height();
+  cp.setName(server->getName());
+  
+  // - Set the default pixel format
+  cp.setPF(server->pb->getPF());
+  char buffer[256];
+  cp.pf().print(buffer, 256);
+  vlog.info("Server default pixel format %s", buffer);
+  image_getter.init(server->pb, cp.pf(), 0);
+
+  // - Mark the entire display as "dirty"
+  updates.add_changed(server->pb->getRect());
+}
+
+void VNCSConnectionST::queryConnection(const char* userName)
+{
+  // - Does the client have the right to bypass the query?
+  if (reverseConnection || !rfb::Server::queryConnect ||
+      (accessRights & AccessNoQuery))
+  {
+    approveConnection(true);
+    return;
+  }
+
+  CharArray reason;
+  VNCServerST::queryResult qr = server->queryConnection(sock, userName,
+                                                        &reason.buf);
+  if (qr == VNCServerST::PENDING) return;
+  approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
+}
+
+void VNCSConnectionST::clientInit(bool shared)
+{
+  lastEventTime = time(0);
+  if (rfb::Server::alwaysShared || reverseConnection) shared = true;
+  if (rfb::Server::neverShared) shared = false;
+  if (!shared) {
+    if (rfb::Server::disconnectClients) {
+      // - Close all the other connected clients
+      vlog.debug("non-shared connection - closing clients");
+      server->closeClients("Non-shared connection requested", getSock());
+    } else {
+      // - Refuse this connection if there are existing clients, in addition to this one
+      if (server->authClientCount() > 1) {
+        close("Server is already in use");
+        return;
+      }
+    }
+  }
+  SConnection::clientInit(shared);
+}
+
+void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
+{
+  SConnection::setPixelFormat(pf);
+  char buffer[256];
+  pf.print(buffer, 256);
+  vlog.info("Client pixel format %s", buffer);
+  image_getter.init(server->pb, pf, writer());
+  setCursor();
+}
+
+void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
+{
+  pointerEventTime = lastEventTime = time(0);
+  if (!(accessRights & AccessPtrEvents)) return;
+  if (!rfb::Server::acceptPointerEvents) return;
+  if (!server->pointerClient || server->pointerClient == this) {
+    pointerEventPos = Point(x, y);
+    if (buttonMask)
+      server->pointerClient = this;
+    else
+      server->pointerClient = 0;
+    server->desktop->pointerEvent(pointerEventPos, buttonMask);
+  }
+}
+
+
+class VNCSConnectionSTShiftPresser {
+public:
+  VNCSConnectionSTShiftPresser(SDesktop* desktop_)
+    : desktop(desktop_), pressed(false) {}
+  ~VNCSConnectionSTShiftPresser() {
+    if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
+  }
+  void press() {
+    desktop->keyEvent(XK_Shift_L, true);
+    pressed = true;
+  }
+  SDesktop* desktop;
+  bool pressed;
+};
+
+// keyEvent() - record in the pressedKeys which keys were pressed.  Allow
+// multiple down events (for autorepeat), but only allow a single up event.
+void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
+  lastEventTime = time(0);
+  if (!(accessRights & AccessKeyEvents)) return;
+  if (!rfb::Server::acceptKeyEvents) return;
+
+  // Turn ISO_Left_Tab into shifted Tab.
+  VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
+  if (key == XK_ISO_Left_Tab) {
+    if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
+        pressedKeys.find(XK_Shift_R) == pressedKeys.end())
+      shiftPresser.press();
+    key = XK_Tab;
+  }
+
+  if (down) {
+    pressedKeys.insert(key);
+  } else {
+    if (!pressedKeys.erase(key)) return;
+  }
+  server->desktop->keyEvent(key, down);
+}
+
+void VNCSConnectionST::clientCutText(const char* str, int len)
+{
+  if (!(accessRights & AccessCutText)) return;
+  if (!rfb::Server::acceptCutText) return;
+  server->desktop->clientCutText(str, len);
+}
+
+void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
+{
+  if (!(accessRights & AccessView)) return;
+
+  SConnection::framebufferUpdateRequest(r, incremental);
+
+  Region reqRgn(r);
+  requested.assign_union(reqRgn);
+
+  if (!incremental) {
+    // Non-incremental update - treat as if area requested has changed
+    updates.add_changed(reqRgn);
+    server->comparer->add_changed(reqRgn);
+  }
+
+  writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setInitialColourMap()
+{
+  setColourMapEntries(0, 0);
+}
+
+// supportsLocalCursor() is called whenever the status of
+// cp.supportsLocalCursor has changed.  If the client does now support local
+// cursor, we make sure that the old server-side rendered cursor is cleaned up
+// and the cursor is sent to the client.
+
+void VNCSConnectionST::supportsLocalCursor()
+{
+  if (cp.supportsLocalCursor) {
+    removeRenderedCursor = true;
+    drawRenderedCursor = false;
+    setCursor();
+  }
+}
+
+void VNCSConnectionST::writeSetCursorCallback()
+{
+  rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
+  image_getter.translatePixels(server->cursor.data, transData,
+                               server->cursor.area());
+
+  writer()->writeSetCursor(server->cursor.width(),
+                           server->cursor.height(),
+                           server->cursor.hotspot.x,
+                           server->cursor.hotspot.y,
+                           transData, server->cursor.mask.buf);
+}
+
+
+void VNCSConnectionST::writeFramebufferUpdate()
+{
+  if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
+
+  server->checkUpdate();
+
+  // If the previous position of the rendered cursor overlaps the source of the
+  // copy, then when the copy happens the corresponding rectangle in the
+  // destination will be wrong, so add it to the changed region.
+
+  if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
+    Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
+                              .intersect(server->pb->getRect()));
+    if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
+      updates.add_changed(bogusCopiedCursor);
+    }
+  }
+
+  // If we need to remove the old rendered cursor, just add the rectangle to
+  // the changed region.
+
+  if (removeRenderedCursor) {
+    updates.add_changed(renderedCursorRect);
+    renderedCursorRect.clear();
+    removeRenderedCursor = false;
+  }
+
+  // Return if there is nothing to send the client.
+
+  if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
+    return;
+
+  // If the client needs a server-side rendered cursor, work out the cursor
+  // rectangle.  If it's empty then don't bother drawing it, but if it overlaps
+  // with the update region, we need to draw the rendered cursor regardless of
+  // whether it has changed.
+
+  if (needRenderedCursor()) {
+    renderedCursorRect
+      = (server->renderedCursor.getRect(server->renderedCursorTL)
+         .intersect(requested.get_bounding_rect()));
+
+    if (renderedCursorRect.is_empty()) {
+      drawRenderedCursor = false;
+    } else if (!updates.get_changed().union_(updates.get_copied())
+        .intersect(renderedCursorRect).is_empty()) {
+      drawRenderedCursor = true;
+    }
+
+    // We could remove the new cursor rect from updates here.  It's not clear
+    // whether this is worth it.  If we do remove it, then we won't draw over
+    // the same bit of screen twice, but we have the overhead of a more complex
+    // region.
+
+    //if (drawRenderedCursor)
+    //  updates.subtract(renderedCursorRect);
+  }
+
+  UpdateInfo update;
+  updates.enable_copyrect(cp.useCopyRect);
+  updates.get_update(&update, requested);
+  if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
+    int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);
+    writer()->writeFramebufferUpdateStart(nRects);
+    Region updatedRegion;
+    writer()->writeRects(update, &image_getter, &updatedRegion);
+    updates.subtract(updatedRegion);
+    if (drawRenderedCursor)
+      writeRenderedCursorRect();
+    writer()->writeFramebufferUpdateEnd();
+    requested.clear();
+  }
+}
+
+
+// writeRenderedCursorRect() writes a single rectangle drawing the rendered
+// cursor on the client.
+
+void VNCSConnectionST::writeRenderedCursorRect()
+{
+  image_getter.setPixelBuffer(&server->renderedCursor);
+  image_getter.setOffset(server->renderedCursorTL);
+
+  Rect actual;
+  writer()->writeRect(renderedCursorRect, &image_getter, &actual);
+
+  image_getter.setPixelBuffer(server->pb);
+  image_getter.setOffset(Point(0,0));
+
+  drawRenderedCursor = false;
+}
+
+void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
+{
+  if (!readyForSetColourMapEntries) return;
+  if (server->pb->getPF().trueColour) return;
+
+  image_getter.setColourMapEntries(firstColour, nColours, writer());
+
+  if (cp.pf().trueColour) {
+    updates.add_changed(server->pb->getRect());
+  }
+}
+
+
+// setCursor() is called whenever the cursor has changed shape or pixel format.
+// If the client supports local cursor then it will arrange for the cursor to
+// be sent to the client.
+
+void VNCSConnectionST::setCursor()
+{
+  if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
+  writer()->cursorChange(this);
+  if (writer()->needFakeUpdate())
+    writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setSocketTimeouts()
+{
+  int timeoutms = rfb::Server::clientWaitTimeMillis;
+  if (timeoutms == 0 || timeoutms > rfb::Server::idleTimeout * 1000) {
+    timeoutms = rfb::Server::idleTimeout * 1000;
+    if (timeoutms == 0)
+      timeoutms = -1;
+  }
+  sock->inStream().setTimeout(timeoutms);
+  sock->outStream().setTimeout(timeoutms);
+}
diff --git a/rfb/VNCSConnectionST.h b/rfb/VNCSConnectionST.h
new file mode 100644
index 0000000..ba480e5
--- /dev/null
+++ b/rfb/VNCSConnectionST.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// VNCSConnectionST is our derived class of SConnection for VNCServerST - there
+// is one for each connected client.  We think of VNCSConnectionST as part of
+// the VNCServerST implementation, so its methods are allowed full access to
+// members of VNCServerST.
+//
+
+#ifndef __RFB_VNCSCONNECTIONST_H__
+#define __RFB_VNCSCONNECTIONST_H__
+
+#include <set>
+#include <rfb/SConnection.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/TransImageGetter.h>
+#include <rfb/VNCServerST.h>
+
+namespace rfb {
+  class VNCSConnectionST : public SConnection,
+                           public WriteSetCursorCallback {
+  public:
+    VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse);
+    virtual ~VNCSConnectionST();
+
+    // Methods called from VNCServerST.  None of these methods ever knowingly
+    // throw an exception.
+
+    // Unless otherwise stated, the SConnectionST may not be valid after any of
+    // these methods are called, since they catch exceptions and may have
+    // called close() which deletes the object.
+
+    // init() must be called to initialise the protocol.  If it fails it
+    // returns false, and close() will have been called.
+    bool init();
+
+    // close() shuts down the socket to the client and deletes the
+    // SConnectionST object.
+    void close(const char* reason);
+
+    // processMessages() processes incoming messages from the client, invoking
+    // various callbacks as a result.  It continues to process messages until
+    // reading might block.  Returns true if the client is still valid &
+    // active, or false if it has disconnected or an error has occurred.
+    bool processMessages();
+
+    void writeFramebufferUpdateOrClose();
+    void pixelBufferChange();
+    void setColourMapEntriesOrClose(int firstColour, int nColours);
+    void bell();
+    void serverCutText(const char *str, int len);
+    void setCursorOrClose();
+
+    // checkIdleTimeout() returns the number of milliseconds left until the
+    // idle timeout expires.  If it has expired, the connection is closed and
+    // zero is returned.  Zero is also returned if there is no idle timeout.
+    int checkIdleTimeout();
+
+    // The following methods never throw exceptions nor do they ever delete the
+    // SConnectionST object.
+
+    // renderedCursorChange() is called whenever the server-side rendered
+    // cursor changes shape or position.  It ensures that the next update will
+    // clean up the old rendered cursor and if necessary draw the new rendered
+    // cursor.
+    void renderedCursorChange();
+
+    // needRenderedCursor() returns true if this client needs the server-side
+    // rendered cursor.  This may be because it does not support local cursor
+    // or because the current cursor position has not been set by this client.
+    bool needRenderedCursor();
+
+    network::Socket* getSock() { return sock; }
+    bool readyForUpdate() { return !requested.is_empty(); }
+    void add_changed(const Region& region) { updates.add_changed(region); }
+    void add_copied(const Region& dest, const Point& delta) {
+      updates.add_copied(dest, delta);
+    }
+
+    const char* getPeerEndpoint() const {return peerEndpoint.buf;}
+
+    // approveConnectionOrClose() is called some time after
+    // VNCServerST::queryConnection() has returned with PENDING to accept or
+    // reject the connection.  The accept argument should be true for
+    // acceptance, or false for rejection, in which case a string reason may
+    // also be given.
+
+    void approveConnectionOrClose(bool accept, const char* reason);
+
+  private:
+    // SConnection callbacks
+
+    // These methods are invoked as callbacks from processMsg().  Note that
+    // none of these methods should call any of the above methods which may
+    // delete the SConnectionST object.
+
+    virtual void versionReceived();
+    virtual SSecurity* getSSecurity(int secType);
+    virtual void authSuccess();
+    virtual void queryConnection(const char* userName);
+    virtual void clientInit(bool shared);
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void pointerEvent(int x, int y, int buttonMask);
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void clientCutText(const char* str, int len);
+    virtual void setInitialColourMap();
+    virtual void supportsLocalCursor();
+
+    // setAccessRights() allows a security package to limit the access rights
+    // of a VNCSConnectioST to the server.  These access rights are applied
+    // such that the actual rights granted are the minimum of the server's
+    // default access settings and the connection's access settings.
+    virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
+
+    // WriteSetCursorCallback
+    virtual void writeSetCursorCallback();
+
+    // Internal methods
+
+    // writeFramebufferUpdate() attempts to write a framebuffer update to the
+    // client.
+
+    void writeFramebufferUpdate();
+
+    void writeRenderedCursorRect();
+    void setColourMapEntries(int firstColour, int nColours);
+    void setCursor();
+    void setSocketTimeouts();
+
+    network::Socket* sock;
+    CharArray peerEndpoint;
+    bool reverseConnection;
+    VNCServerST* server;
+    SimpleUpdateTracker updates;
+    TransImageGetter image_getter;
+    Region requested;
+    bool drawRenderedCursor, removeRenderedCursor;
+    Rect renderedCursorRect;
+
+    std::set<rdr::U32> pressedKeys;
+
+    time_t lastEventTime;
+    time_t pointerEventTime;
+    Point pointerEventPos;
+
+    AccessRights accessRights;
+
+    CharArray closeReason;
+  };
+}
+#endif
diff --git a/rfb/VNCServer.h b/rfb/VNCServer.h
new file mode 100644
index 0000000..e80044c
--- /dev/null
+++ b/rfb/VNCServer.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// VNCServer - abstract interface implemented by the RFB library.  The back-end
+// code calls the relevant methods as appropriate.
+
+#ifndef __RFB_VNCSERVER_H__
+#define __RFB_VNCSERVER_H__
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+  class VNCServer : public UpdateTracker {
+  public:
+
+    // setPixelBuffer() tells the server to use the given pixel buffer.  If
+    // this differs in size from the previous pixel buffer, this may result in
+    // protocol messages being sent, or clients being disconnected.
+    virtual void setPixelBuffer(PixelBuffer* pb) = 0;
+
+    // setColourMapEntries() tells the server that some entries in the colour
+    // map have changed.  The server will retrieve them via the PixelBuffer's
+    // ColourMap object.  This may result in protocol messages being sent.
+    // If nColours is 0, this means the rest of the colour map.
+    virtual void setColourMapEntries(int firstColour=0, int nColours=0) = 0;
+
+    // serverCutText() tells the server that the cut text has changed.  This
+    // will normally be sent to all clients.
+    virtual void serverCutText(const char* str, int len) = 0;
+
+    // bell() tells the server that it should make all clients make a bell sound.
+    virtual void bell() = 0;
+
+    // clientsReadyForUpdate() returns true if there is at least one client
+    // waiting for an update, false if no clients are ready.
+    virtual bool clientsReadyForUpdate() = 0;
+
+    // - Close all currently-connected clients, by calling
+    //   their close() method with the supplied reason.
+    virtual void closeClients(const char* reason) = 0;
+
+    // tryUpdate() causes the server to attempt to send updates to any waiting
+    // clients.
+    virtual void tryUpdate() = 0;
+
+    // setCursor() tells the server that the cursor has changed.  The
+    // cursorData argument contains width*height pixel values in the pixel
+    // buffer's format.  The mask argument is a bitmask with a 1-bit meaning
+    // the corresponding pixel in cursorData is valid.  The mask consists of
+    // left-to-right, top-to-bottom scanlines, where each scanline is padded to
+    // a whole number of bytes [(width+7)/8].  Within each byte the most
+    // significant bit represents the leftmost pixel, and the bytes are simply
+    // in left-to-right order.  The server takes its own copy of the data in
+    // cursorData and mask.
+    virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+                           void* cursorData, void* mask) = 0;
+
+    // setCursorPos() tells the server the current position of the cursor.
+    virtual void setCursorPos(int x, int y) = 0;
+
+    // setSSecurityFactory() tells the server which factory to use when
+    // attempting to authenticate connections.
+    virtual void setSSecurityFactory(SSecurityFactory* f) = 0;
+
+    // setName() tells the server what desktop title to supply to clients
+    virtual void setName(const char* name) = 0;
+  };
+}
+#endif
diff --git a/rfb/VNCServerST.cxx b/rfb/VNCServerST.cxx
new file mode 100644
index 0000000..b3f9e88
--- /dev/null
+++ b/rfb/VNCServerST.cxx
@@ -0,0 +1,430 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Single-Threaded VNC Server implementation
+
+
+// Note about how sockets get closed:
+//
+// Closing sockets to clients is non-trivial because the code which calls
+// VNCServerST must explicitly know about all the sockets (so that it can block
+// on them appropriately).  However, VNCServerST may want to close clients for
+// a number of reasons, and from a variety of entry points.  The simplest is
+// when processSocketEvent() is called for a client, and the remote end has
+// closed its socket.  A more complex reason is when processSocketEvent() is
+// called for a client which has just sent a ClientInit with the shared flag
+// set to false - in this case we want to close all other clients.  Yet another
+// reason for disconnecting clients is when the desktop size has changed as a
+// result of a call to setPixelBuffer().
+//
+// Because we don't want to mess up any data structures which the calling code
+// may maintain regarding sockets with data to process, we can't just delete a
+// socket when we decide to close the connection to a client.  Instead, we only
+// go as far as calling shutdown() on the socket.  This should ensure that
+// eventually the calling code will get round to calling processSocketEvent()
+// for that socket.  Then we can delete the VNCSConnectionST object and its
+// associated network::Socket object, and return false from that call to let
+// the calling code know that the socket has been deleted.  This is the only
+// way that these objects get deleted.
+//
+// It is possible that there are platforms where calling shutdown() cannot
+// guarantee that processSocketEvent() will be called - if so then it may be
+// necessary to introduce some kind of "socket closure callback", but we'll
+// only do that if it proves absolutely necessary.
+//
+// One minor complication is that we don't allocate a VNCSConnectionST object
+// for a blacklisted host (since we want to minimise the resources used for
+// dealing with such a connection).  So we maintain a separate list of
+// closingSockets for this purpose.
+
+
+#include <rfb/ServerCore.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/VNCSConnectionST.h>
+#include <rfb/ComparingUpdateTracker.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/util.h>
+
+#include <rdr/types.h>
+
+using namespace rfb;
+
+static LogWriter slog("VNCServerST");
+LogWriter VNCServerST::connectionsLog("Connections");
+static SSecurityFactoryStandard defaultSecurityFactory;
+
+//
+// -=- VNCServerST Implementation
+//
+
+// -=- Constructors/Destructor
+
+VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_,
+                         SSecurityFactory* sf)
+  : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), pb(0),
+    name(strDup(name_)), pointerClient(0), comparer(0),
+    renderedCursorInvalid(false),
+    securityFactory(sf ? sf : &defaultSecurityFactory),
+    queryConnectionHandler(0), useEconomicTranslate(false)
+{
+  slog.debug("creating single-threaded server %s", name.buf);
+}
+
+VNCServerST::~VNCServerST()
+{
+  slog.debug("shutting down server %s", name.buf);
+
+  // Close any active clients, with appropriate logging & cleanup
+  closeClients("Server shutdown");
+
+  // Delete all the clients, and their sockets, and any closing sockets
+  //   NB: Deleting a client implicitly removes it from the clients list
+  while (!clients.empty()) {
+    delete clients.front()->getSock();
+    delete clients.front();
+  }
+  while (!closingSockets.empty()) {
+    delete closingSockets.front();
+    closingSockets.pop_front();
+  }
+
+  // Stop the desktop object if active, *only* after deleting all clients!
+  if (desktopStarted) {
+    desktopStarted = false;
+    desktop->stop();
+  }
+
+  delete comparer;
+}
+
+
+// SocketServer methods
+
+void VNCServerST::addClient(network::Socket* sock)
+{
+  addClient(sock, false);
+}
+
+void VNCServerST::addClient(network::Socket* sock, bool reverse)
+{
+  // - Check the connection isn't black-marked
+  // *** do this in getSecurity instead?
+  CharArray address(sock->getPeerAddress());
+  if ((rfb::Server::blacklistLevel == 2) &&
+      blHosts->isBlackmarked(address.buf)) {
+    connectionsLog.error("blacklisted: %s", address.buf);
+    try {
+      SConnection::writeConnFailedFromScratch("Too many security failures",
+                                              &sock->outStream());
+    } catch (rdr::Exception&) {
+    }
+    sock->shutdown();
+    closingSockets.push_back(sock);
+    return;
+  }
+
+  VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
+  client->init();
+}
+
+bool VNCServerST::processSocketEvent(network::Socket* sock)
+{
+  // - Find the appropriate VNCSConnectionST and process the event
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock) {
+      if ((*ci)->processMessages())
+        return true;
+      // processMessages failed, so delete the client
+      delete *ci;
+      break;
+    }
+  }
+
+  // - If no client is using the Socket then delete it
+  closingSockets.remove(sock);
+  delete sock;
+
+  // - Check that the desktop object is still required
+  if (authClientCount() == 0 && desktopStarted) {
+    slog.debug("no authenticated clients - stopping desktop");
+    desktopStarted = false;
+    desktop->stop();
+  }
+  
+  // - Inform the caller not to continue handling the Socket
+  return false;
+}
+
+int VNCServerST::checkTimeouts()
+{
+  int timeout = 0;
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+    ci_next = ci; ci_next++;
+    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+  }
+  return timeout;
+}
+
+
+// VNCServer methods
+
+void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
+{
+  pb = pb_;
+  delete comparer;
+  comparer = 0;
+
+  if (pb) {
+    comparer = new ComparingUpdateTracker(pb);
+    cursor.setPF(pb->getPF());
+    renderedCursor.setPF(pb->getPF());
+
+    std::list<VNCSConnectionST*>::iterator ci, ci_next;
+    for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+      ci_next = ci; ci_next++;
+      (*ci)->pixelBufferChange();
+    }
+  } else {
+    if (desktopStarted)
+      throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
+  }
+}
+
+void VNCServerST::setColourMapEntries(int firstColour, int nColours)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->setColourMapEntriesOrClose(firstColour, nColours);
+  }
+}
+
+void VNCServerST::bell()
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->bell();
+  }
+}
+
+void VNCServerST::serverCutText(const char* str, int len)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->serverCutText(str, len);
+  }
+}
+
+void VNCServerST::add_changed(const Region& region)
+{
+  comparer->add_changed(region);
+}
+
+void VNCServerST::add_copied(const Region& dest, const Point& delta)
+{
+  comparer->add_copied(dest, delta);
+}
+
+bool VNCServerST::clientsReadyForUpdate()
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->readyForUpdate())
+      return true;
+  }
+  return false;
+}
+
+void VNCServerST::tryUpdate()
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->writeFramebufferUpdateOrClose();
+  }
+}
+
+void VNCServerST::setCursor(int width, int height, int newHotspotX,
+                            int newHotspotY, void* data, void* mask)
+{
+  cursor.hotspot.x = newHotspotX;
+  cursor.hotspot.y = newHotspotY;
+  cursor.setSize(width, height);
+  memcpy(cursor.data, data, cursor.dataLen());
+  memcpy(cursor.mask.buf, mask, cursor.maskLen());
+
+  cursor.crop();
+
+  renderedCursorInvalid = true;
+
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->renderedCursorChange();
+    (*ci)->setCursorOrClose();
+  }
+}
+
+void VNCServerST::setCursorPos(int x, int y)
+{
+  if (cursorPos.x != x || cursorPos.y != y) {
+    cursorPos.x = x;
+    cursorPos.y = y;
+    renderedCursorInvalid = true;
+    std::list<VNCSConnectionST*>::iterator ci;
+    for (ci = clients.begin(); ci != clients.end(); ci++)
+      (*ci)->renderedCursorChange();
+  }
+}
+
+// Other public methods
+
+void VNCServerST::approveConnection(network::Socket* sock, bool accept,
+                                    const char* reason)
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock) {
+      (*ci)->approveConnectionOrClose(accept, reason);
+      return;
+    }
+  }
+}
+
+void VNCServerST::closeClients(const char* reason, network::Socket* except)
+{
+  std::list<VNCSConnectionST*>::iterator i, next_i;
+  for (i=clients.begin(); i!=clients.end(); i=next_i) {
+    next_i = i; next_i++;
+    if ((*i)->getSock() != except)
+      (*i)->close(reason);
+  }
+}
+
+void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
+{
+  sockets->clear();
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    sockets->push_back((*ci)->getSock());
+  }
+  std::list<network::Socket*>::iterator si;
+  for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
+    sockets->push_back(*si);
+  }
+}
+
+SConnection* VNCServerST::getSConnection(network::Socket* sock) {
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock)
+      return *ci;
+  }
+  return 0;
+}
+
+
+// -=- Internal methods
+
+void VNCServerST::startDesktop()
+{
+  if (!desktopStarted) {
+    slog.debug("starting desktop");
+    desktop->start(this);
+    desktopStarted = true;
+    if (!pb)
+      throw Exception("SDesktop::start() did not set a valid PixelBuffer");
+  }
+}
+
+int VNCServerST::authClientCount() {
+  int count = 0;
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->authenticated())
+      count++;
+  }
+  return count;
+}
+
+inline bool VNCServerST::needRenderedCursor()
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++)
+    if ((*ci)->needRenderedCursor()) return true;
+  return false;
+}
+
+// checkUpdate() is called just before sending an update.  It checks to see
+// what updates are pending and propagates them to the update tracker for each
+// client.  It uses the ComparingUpdateTracker's compare() method to filter out
+// areas of the screen which haven't actually changed.  It also checks the
+// state of the (server-side) rendered cursor, if necessary rendering it again
+// with the correct background.
+
+void VNCServerST::checkUpdate()
+{
+  bool renderCursor = needRenderedCursor();
+
+  if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid))
+    return;
+
+  Region toCheck = comparer->get_changed().union_(comparer->get_copied());
+
+  if (renderCursor) {
+    Rect clippedCursorRect
+      = cursor.getRect(cursorTL()).intersect(pb->getRect());
+
+    if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
+                                   .is_empty())) {
+      renderCursor = false;
+    } else {
+      renderedCursorTL = clippedCursorRect.tl;
+      renderedCursor.setSize(clippedCursorRect.width(),
+                             clippedCursorRect.height());
+      toCheck.assign_union(clippedCursorRect);
+    }
+  }
+
+  pb->grabRegion(toCheck);
+
+  if (rfb::Server::compareFB)
+    comparer->compare();
+
+  if (renderCursor) {
+    pb->getImage(renderedCursor.data,
+                 renderedCursor.getRect(renderedCursorTL));
+    renderedCursor.maskRect(cursor.getRect(cursorTL()
+                                           .subtract(renderedCursorTL)),
+                            cursor.data, cursor.mask.buf);
+    renderedCursorInvalid = false;
+  }
+
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->add_copied(comparer->get_copied(), comparer->get_delta());
+    (*ci)->add_changed(comparer->get_changed());
+  }
+
+  comparer->clear();
+}
diff --git a/rfb/VNCServerST.h b/rfb/VNCServerST.h
new file mode 100644
index 0000000..6655a0d
--- /dev/null
+++ b/rfb/VNCServerST.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- VNCServerST.h
+
+// Single-threaded VNCServer implementation
+
+#ifndef __RFB_VNCSERVERST_H__
+#define __RFB_VNCSERVERST_H__
+
+#include <list>
+
+#include <rfb/SDesktop.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Blacklist.h>
+#include <rfb/Cursor.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+  class VNCSConnectionST;
+  class ComparingUpdateTracker;
+  class PixelBuffer;
+
+  class VNCServerST : public VNCServer, public network::SocketServer {
+  public:
+    // -=- Constructors
+
+    //   Create a server exporting the supplied desktop.
+    VNCServerST(const char* name_, SDesktop* desktop_,
+                SSecurityFactory* securityFactory_=0);
+    virtual ~VNCServerST();
+
+
+    // Methods overridden from SocketServer
+
+    // - Run a client connection on the supplied socket
+    //   This causes the server to allocate the required structures
+    //   to handle a client connection, and to initialise the RFB
+    //   protocol.
+    //   NB:  The server assumes ownership of the Socket object.
+
+    virtual void addClient(network::Socket* sock);
+
+    // - Process an input event on a particular Socket
+    //   The platform-specific side of the server implementation calls
+    //   this method whenever data arrives on one of the active
+    //   network sockets.
+    //   The method returns true if the Socket is still in use by the
+    //   server, or false if it is no longer required and has been
+    //   deleted.
+    //   NB:  If false is returned then the Socket is deleted and must
+    //   not be accessed again!
+
+    virtual bool processSocketEvent(network::Socket* sock);
+
+    // - checkTimeouts() returns the number of milliseconds left until the next
+    //   idle timeout expires.  If any have already expired, the corresponding
+    //   connections are closed.  Zero is returned if there is no idle timeout.
+
+    virtual int checkTimeouts();
+
+
+    // Methods overridden from VNCServer
+
+    virtual void setPixelBuffer(PixelBuffer* pb);
+    virtual void setColourMapEntries(int firstColour=0, int nColours=0);
+    virtual void serverCutText(const char* str, int len);
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+    virtual bool clientsReadyForUpdate();
+    virtual void tryUpdate();
+    virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+                           void* cursorData, void* mask);
+    virtual void setCursorPos(int x, int y);
+    virtual void setSSecurityFactory(SSecurityFactory* f) {securityFactory=f;}
+
+    virtual void bell();
+
+    // - Close all currently-connected clients, by calling
+    //   their close() method with the supplied reason.
+    virtual void closeClients(const char* reason) {closeClients(reason, 0);}
+
+    // VNCServerST-only methods
+
+    //   If a particular VNCSConnectionST* is specified then
+    //   that connection will NOT be closed.
+    void closeClients(const char* reason, network::Socket* sock);
+
+    // addClient() with an extra flag to say if this is a reverse connection to
+    // a listening client.  Reverse connections are not authenticated and are
+    // always shared (unless the NeverShared parameter is set).
+
+    void addClient(network::Socket* sock, bool reverse);
+
+
+    // getSockets() gets a list of sockets.  This can be used to generate an
+    // fd_set for calling select().
+
+    void getSockets(std::list<network::Socket*>* sockets);
+
+    // getSConnection() gets the SConnection for a particular Socket.  If
+    // the Socket is not recognised then null is returned.
+
+    SConnection* getSConnection(network::Socket* sock);
+
+    // getDesktopSize() returns the size of the SDesktop exported by this
+    // server.
+    Point getDesktopSize() const {return desktop->getFbSize();}
+
+    // getName() returns the name of this VNC Server.  NB: The value returned
+    // is the server's internal buffer which may change after any other methods
+    // are called - take a copy if necessary.
+    const char* getName() const {return name.buf;}
+
+    // setName() specifies the desktop name that the server should provide to
+    // clients
+    void setName(const char* name_) {name.replaceBuf(strDup(name_));}
+
+    // A QueryConnectionHandler, if supplied, is passed details of incoming
+    // connections to approve, reject, or query the user about.
+    //
+    // queryConnection() is called when a connection has been
+    // successfully authenticated.  The sock and userName arguments identify
+    // the socket and the name of the authenticated user, if any.  It should
+    // return ACCEPT if the connection should be accepted, REJECT if it should
+    // be rejected, or PENDING if a decision cannot yet be reached.  If REJECT
+    // is returned, *reason can be set to a string describing the reason - this
+    // will be delete[]ed when it is finished with.  If PENDING is returned,
+    // approveConnection() must be called some time later to accept or reject
+    // the connection.
+    enum queryResult { ACCEPT, REJECT, PENDING };
+    struct QueryConnectionHandler {
+      virtual ~QueryConnectionHandler() {}
+      virtual queryResult queryConnection(network::Socket* sock,
+                                          const char* userName,
+                                          char** reason) = 0;
+    };
+    void setQueryConnectionHandler(QueryConnectionHandler* qch) {
+      queryConnectionHandler = qch;
+    }
+
+    // queryConnection is called as described above, and either passes the
+    // request on to the registered handler, or accepts the connection if
+    // no handler has been specified.
+    virtual queryResult queryConnection(network::Socket* sock,
+                                        const char* userName,
+                                        char** reason) {
+      return queryConnectionHandler
+        ? queryConnectionHandler->queryConnection(sock, userName, reason)
+        : ACCEPT;
+    }
+
+    // approveConnection() is called by the active QueryConnectionHandler,
+    // some time after queryConnection() has returned with PENDING, to accept
+    // or reject the connection.  The accept argument should be true for
+    // acceptance, or false for rejection, in which case a string reason may
+    // also be given.
+    void approveConnection(network::Socket* sock, bool accept,
+                           const char* reason);
+
+    // setBlacklist() is called to replace the VNCServerST's internal
+    // Blacklist instance with another instance.  This allows a single
+    // Blacklist to be shared by multiple VNCServerST instances.
+    void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;}
+
+    // setEconomicTranslate() determines (for new connections) whether pixels
+    // should be translated for <=16bpp clients using a large lookup table (fast)
+    // or separate, smaller R, G and B tables (slower).  If set to true, small tables
+    // are used, to save memory.
+    void setEconomicTranslate(bool et) { useEconomicTranslate = et; }
+
+  protected:
+
+    friend class VNCSConnectionST;
+
+    void startDesktop();
+
+    static LogWriter connectionsLog;
+    Blacklist blacklist;
+    Blacklist* blHosts;
+
+    SDesktop* desktop;
+    bool desktopStarted;
+    PixelBuffer* pb;
+
+    CharArray name;
+
+    std::list<VNCSConnectionST*> clients;
+    VNCSConnectionST* pointerClient;
+    std::list<network::Socket*> closingSockets;
+
+    ComparingUpdateTracker* comparer;
+
+    Point cursorPos;
+    Cursor cursor;
+    Point cursorTL() { return cursorPos.subtract(cursor.hotspot); }
+    Point renderedCursorTL;
+    ManagedPixelBuffer renderedCursor;
+    bool renderedCursorInvalid;
+
+    // - Check how many of the clients are authenticated.
+    int authClientCount();
+
+    bool needRenderedCursor();
+    void checkUpdate();
+
+    SSecurityFactory* securityFactory;
+    QueryConnectionHandler* queryConnectionHandler;
+    bool useEconomicTranslate;
+  };
+
+};
+
+#endif
+
diff --git a/rfb/ZRLEDecoder.cxx b/rfb/ZRLEDecoder.cxx
new file mode 100644
index 0000000..2dd4a12
--- /dev/null
+++ b/rfb/ZRLEDecoder.cxx
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleDecode.h>
+#define CPIXEL 24A
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#undef BPP
+
+Decoder* ZRLEDecoder::create(CMsgReader* reader)
+{
+  return new ZRLEDecoder(reader);
+}
+
+ZRLEDecoder::ZRLEDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+ZRLEDecoder::~ZRLEDecoder()
+{
+}
+
+void ZRLEDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  rdr::U8* buf = reader->getImageBuf(64 * 64 * 4);
+  switch (reader->bpp()) {
+  case 8:  zrleDecode8 (r, is, &zis, (rdr::U8*) buf, handler); break;
+  case 16: zrleDecode16(r, is, &zis, (rdr::U16*)buf, handler); break;
+  case 32:
+    {
+      const rfb::PixelFormat& pf = handler->cp.pf();
+      bool fitsInLS3Bytes = ((pf.redMax   << pf.redShift)   < (1<<24) &&
+                             (pf.greenMax << pf.greenShift) < (1<<24) &&
+                             (pf.blueMax  << pf.blueShift)  < (1<<24));
+
+      bool fitsInMS3Bytes = (pf.redShift   > 7  &&
+                             pf.greenShift > 7  &&
+                             pf.blueShift  > 7);
+
+      if ((fitsInLS3Bytes && !pf.bigEndian) ||
+          (fitsInMS3Bytes && pf.bigEndian))
+      {
+        zrleDecode24A(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      else if ((fitsInLS3Bytes && pf.bigEndian) ||
+               (fitsInMS3Bytes && !pf.bigEndian))
+      {
+        zrleDecode24B(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      else
+      {
+        zrleDecode32(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      break;
+    }
+  }
+}
diff --git a/rfb/ZRLEDecoder.h b/rfb/ZRLEDecoder.h
new file mode 100644
index 0000000..c8b1feb
--- /dev/null
+++ b/rfb/ZRLEDecoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_ZRLEDECODER_H__
+#define __RFB_ZRLEDECODER_H__
+
+#include <rdr/ZlibInStream.h>
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class ZRLEDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~ZRLEDecoder();
+  private:
+    ZRLEDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+    rdr::ZlibInStream zis;
+  };
+}
+#endif
diff --git a/rfb/ZRLEEncoder.cxx b/rfb/ZRLEEncoder.cxx
new file mode 100644
index 0000000..1f28869
--- /dev/null
+++ b/rfb/ZRLEEncoder.cxx
@@ -0,0 +1,122 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ZRLEEncoder.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+rdr::MemOutStream* ZRLEEncoder::sharedMos = 0;
+int ZRLEEncoder::maxLen = 513 * 1024; // enough for width 2048 32-bit pixels
+
+IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleEncode.h>
+#define CPIXEL 24A
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#undef BPP
+
+Encoder* ZRLEEncoder::create(SMsgWriter* writer)
+{
+  return new ZRLEEncoder(writer);
+}
+
+ZRLEEncoder::ZRLEEncoder(SMsgWriter* writer_)
+  : writer(writer_), zos(0,0,zlibLevel)
+{
+  if (sharedMos)
+    mos = sharedMos;
+  else
+    mos = new rdr::MemOutStream(129*1024);
+}
+
+ZRLEEncoder::~ZRLEEncoder()
+{
+  if (!sharedMos)
+    delete mos;
+}
+
+bool ZRLEEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  rdr::U8* imageBuf = writer->getImageBuf(64 * 64 * 4 + 4);
+  mos->clear();
+  bool wroteAll = true;
+  *actual = r;
+
+  switch (writer->bpp()) {
+  case 8:
+    wroteAll = zrleEncode8(r, mos, &zos, imageBuf, maxLen, actual, ig);
+    break;
+  case 16:
+    wroteAll = zrleEncode16(r, mos, &zos, imageBuf, maxLen, actual, ig);
+    break;
+  case 32:
+    {
+      const PixelFormat& pf = writer->getConnParams()->pf();
+
+      bool fitsInLS3Bytes = ((pf.redMax   << pf.redShift)   < (1<<24) &&
+                             (pf.greenMax << pf.greenShift) < (1<<24) &&
+                             (pf.blueMax  << pf.blueShift)  < (1<<24));
+
+      bool fitsInMS3Bytes = (pf.redShift   > 7  &&
+                             pf.greenShift > 7  &&
+                             pf.blueShift  > 7);
+
+      if ((fitsInLS3Bytes && !pf.bigEndian) ||
+          (fitsInMS3Bytes && pf.bigEndian))
+      {
+        wroteAll = zrleEncode24A(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      else if ((fitsInLS3Bytes && pf.bigEndian) ||
+               (fitsInMS3Bytes && !pf.bigEndian))
+      {
+        wroteAll = zrleEncode24B(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      else
+      {
+        wroteAll = zrleEncode32(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      break;
+    }
+  }
+
+  writer->startRect(*actual, encodingZRLE);
+  rdr::OutStream* os = writer->getOutStream();
+  os->writeU32(mos->length());
+  os->writeBytes(mos->data(), mos->length());
+  writer->endRect();
+  return wroteAll;
+}
diff --git a/rfb/ZRLEEncoder.h b/rfb/ZRLEEncoder.h
new file mode 100644
index 0000000..17222a3
--- /dev/null
+++ b/rfb/ZRLEEncoder.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_ZRLEENCODER_H__
+#define __RFB_ZRLEENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class ZRLEEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~ZRLEEncoder();
+
+    // setMaxLen() sets the maximum size in bytes of any ZRLE rectangle.  This
+    // can be used to stop the MemOutStream from growing too large.  The value
+    // must be large enough to allow for at least one row of ZRLE tiles.  So
+    // for example for a screen width of 2048 32-bit pixels this is 2K*4*64 =
+    // 512Kbytes plus a bit of overhead.
+    static void setMaxLen(int m) { maxLen = m; }
+
+    // setSharedMos() sets a MemOutStream to be shared amongst all
+    // ZRLEEncoders.  Should be called before any ZRLEEncoders are created.
+    static void setSharedMos(rdr::MemOutStream* mos_) { sharedMos = mos_; }
+
+  private:
+    ZRLEEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+    rdr::ZlibOutStream zos;
+    rdr::MemOutStream* mos;
+    static rdr::MemOutStream* sharedMos;
+    static int maxLen;
+  };
+}
+#endif
diff --git a/rfb/d3des.c b/rfb/d3des.c
new file mode 100644
index 0000000..9227ddd
--- /dev/null
+++ b/rfb/d3des.c
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.  Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * 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.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static unsigned short bytebit[8]	= {
+	01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+	0x800000L,	0x400000L,	0x200000L,	0x100000L,
+	0x80000L,	0x40000L,	0x20000L,	0x10000L,
+	0x8000L,	0x4000L,	0x2000L,	0x1000L,
+	0x800L, 	0x400L, 	0x200L, 	0x100L,
+	0x80L,		0x40L,		0x20L,		0x10L,
+	0x8L,		0x4L,		0x2L,		0x1L	};
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
+
+static unsigned char totrot[16] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+	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 };
+
+void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+	register int i, j, l, m, n;
+	unsigned char pc1m[56], pcr[56];
+	unsigned long kn[32];
+
+	for ( j = 0; j < 56; j++ ) {
+		l = pc1[j];
+		m = l & 07;
+		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+		}
+	for( i = 0; i < 16; i++ ) {
+		if( edf == DE1 ) m = (15 - i) << 1;
+		else m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for( j = 0; j < 28; j++ ) {
+			l = j + totrot[i];
+			if( l < 28 ) pcr[j] = pc1m[l];
+			else pcr[j] = pc1m[l - 28];
+			}
+		for( j = 28; j < 56; j++ ) {
+		    l = j + totrot[i];
+		    if( l < 56 ) pcr[j] = pc1m[l];
+		    else pcr[j] = pc1m[l - 28];
+		    }
+		for( j = 0; j < 24; j++ ) {
+			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+			}
+		}
+	cookey(kn);
+	return;
+	}
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+	register unsigned long *cook, *raw0;
+	unsigned long dough[32];
+	register int i;
+
+	cook = dough;
+	for( i = 0; i < 16; i++, raw1++ ) {
+		raw0 = raw1++;
+		*cook	 = (*raw0 & 0x00fc0000L) << 6;
+		*cook	|= (*raw0 & 0x00000fc0L) << 10;
+		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook	 = (*raw0 & 0x0003f000L) << 12;
+		*cook	|= (*raw0 & 0x0000003fL) << 16;
+		*cook	|= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+		}
+	usekey(dough);
+	return;
+	}
+
+void cpkey(into)
+register unsigned long *into;
+{
+	register unsigned long *from, *endp;
+
+	from = KnL, endp = &KnL[32];
+	while( from < endp ) *into++ = *from++;
+	return;
+	}
+
+void usekey(from)
+register unsigned long *from;
+{
+	register unsigned long *to, *endp;
+
+	to = KnL, endp = &KnL[32];
+	while( to < endp ) *to++ = *from++;
+	return;
+	}
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+	unsigned long work[2];
+
+	scrunch(inblock, work);
+	desfunc(work, KnL);
+	unscrun(work, outblock);
+	return;
+	}
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into++ |= (*outof++ & 0xffL);
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into	|= (*outof   & 0xffL);
+	return;
+	}
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+	*into++ = (*outof >> 24) & 0xffL;
+	*into++ = (*outof >> 16) & 0xffL;
+	*into++ = (*outof >>  8) & 0xffL;
+	*into++ =  *outof++	 & 0xffL;
+	*into++ = (*outof >> 24) & 0xffL;
+	*into++ = (*outof >> 16) & 0xffL;
+	*into++ = (*outof >>  8) & 0xffL;
+	*into	=  *outof	 & 0xffL;
+	return;
+	}
+
+static unsigned long SP1[64] = {
+	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+	register unsigned long fval, work, right, leftt;
+	register int round;
+
+	leftt = block[0];
+	right = block[1];
+	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+	work = ((right >> 2) ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+	for( round = 0; round < 8; round++ ) {
+		work  = (right << 28) | (right >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		leftt ^= fval;
+		work  = (leftt << 28) | (leftt >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		right ^= fval;
+		}
+
+	right = (right << 31) | (right >> 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = (leftt << 31) | (leftt >> 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+	*block++ = right;
+	*block = leftt;
+	return;
+	}
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/rfb/d3des.h b/rfb/d3des.h
new file mode 100644
index 0000000..ea3da44
--- /dev/null
+++ b/rfb/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * 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.
+ */
+
+/* d3des.h -
+ *
+ *	Headers and defines for d3des.c
+ *	Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ *	(GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0	0	/* MODE == encrypt */
+#define DE1	1	/* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/*		      hexkey[8]     MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/*		    cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/*		   cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/*		    from[8]	      to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'.  They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/rfb/encodings.cxx b/rfb/encodings.cxx
new file mode 100644
index 0000000..56a64ee
--- /dev/null
+++ b/rfb/encodings.cxx
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/encodings.h>
+
+int rfb::encodingNum(const char* name)
+{
+  if (strcasecmp(name, "raw") == 0)      return encodingRaw;
+  if (strcasecmp(name, "copyRect") == 0) return encodingCopyRect;
+  if (strcasecmp(name, "RRE") == 0)      return encodingRRE;
+  if (strcasecmp(name, "CoRRE") == 0)    return encodingCoRRE;
+  if (strcasecmp(name, "hextile") == 0)  return encodingHextile;
+  if (strcasecmp(name, "ZRLE") == 0)     return encodingZRLE;
+  return -1;
+}
+
+const char* rfb::encodingName(unsigned int num)
+{
+  switch (num) {
+  case encodingRaw:      return "raw";
+  case encodingCopyRect: return "copyRect";
+  case encodingRRE:      return "RRE";
+  case encodingCoRRE:    return "CoRRE";
+  case encodingHextile:  return "hextile";
+  case encodingZRLE:     return "ZRLE";
+  default:               return "[unknown encoding]";
+  }
+}
diff --git a/rfb/encodings.h b/rfb/encodings.h
new file mode 100644
index 0000000..ae104b4
--- /dev/null
+++ b/rfb/encodings.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_ENCODINGS_H__
+#define __RFB_ENCODINGS_H__
+
+namespace rfb {
+
+  const unsigned int encodingRaw = 0;
+  const unsigned int encodingCopyRect = 1;
+  const unsigned int encodingRRE = 2;
+  const unsigned int encodingCoRRE = 4;
+  const unsigned int encodingHextile = 5;
+  const unsigned int encodingZRLE = 16;
+
+  const unsigned int encodingMax = 255;
+
+  const unsigned int pseudoEncodingCursor = 0xffffff11;
+  const unsigned int pseudoEncodingDesktopSize = 0xffffff21;
+
+  int encodingNum(const char* name);
+  const char* encodingName(unsigned int num);
+}
+#endif
diff --git a/rfb/hextileConstants.h b/rfb/hextileConstants.h
new file mode 100644
index 0000000..272afbb
--- /dev/null
+++ b/rfb/hextileConstants.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_HEXTILECONSTANTS_H__
+#define __RFB_HEXTILECONSTANTS_H__
+namespace rfb {
+  const int hextileRaw = (1 << 0);
+  const int hextileBgSpecified = (1 << 1);
+  const int hextileFgSpecified = (1 << 2);
+  const int hextileAnySubrects = (1 << 3);
+  const int hextileSubrectsColoured = (1 << 4);
+}
+#endif
diff --git a/rfb/hextileDecode.h b/rfb/hextileDecode.h
new file mode 100644
index 0000000..dc685e3
--- /dev/null
+++ b/rfb/hextileDecode.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Hextile decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+// IMAGE_RECT         - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define HEXTILE_DECODE CONCAT2E(hextileDecode,BPP)
+
+void HEXTILE_DECODE (const Rect& r, rdr::InStream* is, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+                     , EXTRA_ARGS
+#endif
+                     )
+{
+  Rect t;
+  PIXEL_T bg = 0;
+  PIXEL_T fg = 0;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+    t.br.y = min(r.br.y, t.tl.y + 16);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+      t.br.x = min(r.br.x, t.tl.x + 16);
+
+      int tileType = is->readU8();
+
+      if (tileType & hextileRaw) {
+	is->readBytes(buf, t.area() * (BPP/8));
+	IMAGE_RECT(t, buf);
+	continue;
+      }
+
+      if (tileType & hextileBgSpecified)
+	bg = is->READ_PIXEL();
+
+#ifdef FAVOUR_FILL_RECT
+      FILL_RECT(t, bg);
+#else
+      int len = t.area();
+      PIXEL_T* ptr = (PIXEL_T*)buf;
+      while (len-- > 0) *ptr++ = bg;
+#endif
+
+      if (tileType & hextileFgSpecified)
+	fg = is->READ_PIXEL();
+
+      if (tileType & hextileAnySubrects) {
+        int nSubrects = is->readU8();
+
+        for (int i = 0; i < nSubrects; i++) {
+
+          if (tileType & hextileSubrectsColoured)
+            fg = is->READ_PIXEL();
+
+          int xy = is->readU8();
+          int wh = is->readU8();
+
+#ifdef FAVOUR_FILL_RECT
+          Rect s;
+          s.tl.x = t.tl.x + ((xy >> 4) & 15);
+          s.tl.y = t.tl.y + (xy & 15);
+          s.br.x = s.tl.x + ((wh >> 4) & 15) + 1;
+          s.br.y = s.tl.y + (wh & 15) + 1;
+          FILL_RECT(s, fg);
+#else
+          int x = ((xy >> 4) & 15);
+          int y = (xy & 15);
+          int w = ((wh >> 4) & 15) + 1;
+          int h = (wh & 15) + 1;
+          PIXEL_T* ptr = (PIXEL_T*)buf + y * t.width() + x;
+          int rowAdd = t.width() - w;
+          while (h-- > 0) {
+            int len = w;
+            while (len-- > 0) *ptr++ = fg;
+            ptr += rowAdd;
+          }
+#endif
+        }
+      }
+#ifndef FAVOUR_FILL_RECT
+      IMAGE_RECT(t, buf);
+#endif
+    }
+  }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef HEXTILE_DECODE
+}
diff --git a/rfb/hextileEncode.h b/rfb/hextileEncode.h
new file mode 100644
index 0000000..a55842a
--- /dev/null
+++ b/rfb/hextileEncode.h
@@ -0,0 +1,247 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Hextile encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+
+#include <rdr/OutStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define HEXTILE_ENCODE CONCAT2E(hextileEncode,BPP)
+#define HEXTILE_ENCODE_TILE CONCAT2E(hextileEncodeTile,BPP)
+#define TEST_TILE_TYPE CONCAT2E(hextileTestTileType,BPP)
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg);
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+                         rdr::U8* encoded, PIXEL_T bg);
+
+void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
+#ifdef EXTRA_ARGS
+                    , EXTRA_ARGS
+#endif
+                    )
+{
+  Rect t;
+  PIXEL_T buf[256];
+  PIXEL_T oldBg = 0, oldFg = 0;
+  bool oldBgValid = false;
+  bool oldFgValid = false;
+  rdr::U8 encoded[256*(BPP/8)];
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+    t.br.y = min(r.br.y, t.tl.y + 16);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+      t.br.x = min(r.br.x, t.tl.x + 16);
+
+      GET_IMAGE_INTO_BUF(t,buf);
+
+      PIXEL_T bg, fg;
+      int tileType = TEST_TILE_TYPE(buf, t.width(), t.height(), &bg, &fg);
+
+      if (!oldBgValid || oldBg != bg) {
+        tileType |= hextileBgSpecified;
+        oldBg = bg;
+        oldBgValid = true;
+      }
+
+      int encodedLen = 0;
+
+      if (tileType & hextileAnySubrects) {
+
+        if (tileType & hextileSubrectsColoured) {
+          oldFgValid = false;
+        } else {
+          if (!oldFgValid || oldFg != fg) {
+            tileType |= hextileFgSpecified;
+            oldFg = fg;
+            oldFgValid = true;
+          }
+        }
+
+        encodedLen = HEXTILE_ENCODE_TILE(buf, t.width(), t.height(), tileType,
+                                         encoded, bg);
+
+        if (encodedLen < 0) {
+          GET_IMAGE_INTO_BUF(t,buf);
+          os->writeU8(hextileRaw);
+          os->writeBytes(buf, t.width() * t.height() * (BPP/8));
+          oldBgValid = oldFgValid = false;
+          continue;
+        }
+      }
+
+      os->writeU8(tileType);
+      if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
+      if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
+      if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
+    }
+  }
+}
+
+
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+                         rdr::U8* encoded, PIXEL_T bg)
+{
+  rdr::U8* nSubrectsPtr = encoded;
+  *nSubrectsPtr = 0;
+  encoded++;
+
+  for (int y = 0; y < h; y++)
+  {
+    int x = 0;
+    while (x < w) {
+      if (*data == bg) {
+        x++;
+        data++;
+        continue;
+      }
+
+      // Find horizontal subrect first
+      PIXEL_T* ptr = data+1;
+      PIXEL_T* eol = data+w-x;
+      while (ptr < eol && *ptr == *data) ptr++;
+      int sw = ptr - data;
+
+      ptr = data + w;
+      int sh = 1;
+      while (sh < h-y) {
+        eol = ptr + sw;
+        while (ptr < eol)
+          if (*ptr++ != *data) goto endOfHorizSubrect;
+        ptr += w - sw;
+        sh++;
+      }
+    endOfHorizSubrect:
+
+      // Find vertical subrect
+      int vh;
+      for (vh = sh; vh < h-y; vh++)
+        if (data[vh*w] != *data) break;
+
+      if (vh != sh) {
+        ptr = data+1;
+        int vw;
+        for (vw = 1; vw < sw; vw++) {
+          for (int i = 0; i < vh; i++)
+            if (ptr[i*w] != *data) goto endOfVertSubrect;
+          ptr++;
+        }
+      endOfVertSubrect:
+
+        // If vertical subrect bigger than horizontal then use that.
+        if (sw*sh < vw*vh) {
+          sw = vw;
+          sh = vh;
+        }
+      }
+
+      (*nSubrectsPtr)++;
+
+      if (tileType & hextileSubrectsColoured) {
+        if (encoded - nSubrectsPtr + (BPP/8) > w*h*(BPP/8)) return -1;
+#if (BPP == 8)
+        *encoded++ = *data;
+#elif (BPP == 16)
+        *encoded++ = ((rdr::U8*)data)[0];
+        *encoded++ = ((rdr::U8*)data)[1];
+#elif (BPP == 32)
+        *encoded++ = ((rdr::U8*)data)[0];
+        *encoded++ = ((rdr::U8*)data)[1];
+        *encoded++ = ((rdr::U8*)data)[2];
+        *encoded++ = ((rdr::U8*)data)[3];
+#endif
+      }
+
+      if (encoded - nSubrectsPtr + 2 > w*h*(BPP/8)) return -1;
+      *encoded++ = (x << 4) | y;
+      *encoded++ = ((sw-1) << 4) | (sh-1);
+
+      ptr = data+w;
+      PIXEL_T* eor = data+w*sh;
+      while (ptr < eor) {
+        eol = ptr + sw;
+        while (ptr < eol) *ptr++ = bg;
+        ptr += w - sw;
+      }
+      x += sw;
+      data += sw;
+    }
+  }
+  return encoded - nSubrectsPtr;
+}
+
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg)
+{
+  int tileType = 0;
+  PIXEL_T pix1 = *data, pix2 = 0;
+  int count1 = 0, count2 = 0;
+  PIXEL_T* end = data + w*h;
+
+  for (PIXEL_T* ptr = data; ptr < end; ptr++) {
+    if (*ptr == pix1) {
+      count1++;
+      continue;
+    }
+
+    if (count2 == 0) {
+      tileType |= hextileAnySubrects;
+      pix2 = *ptr;
+    }
+
+    if (*data == pix2) {
+      count2++;
+      continue;
+    }
+
+    tileType |= hextileSubrectsColoured;
+    break;
+  }
+
+  if (count1 >= count2) {
+    *bg = pix1; *fg = pix2;
+  } else {
+    *bg = pix2; *fg = pix1;
+  }
+  return tileType;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef HEXTILE_ENCODE
+#undef HEXTILE_ENCODE_TILE
+#undef TEST_TILE_TYPE
+}
diff --git a/rfb/keysymdef.h b/rfb/keysymdef.h
new file mode 100644
index 0000000..979ebdd
--- /dev/null
+++ b/rfb/keysymdef.h
@@ -0,0 +1,1595 @@
+/* $TOG: keysymdef.h /main/28 1998/05/22 16:18:01 kaleb $ */
+
+/***********************************************************
+Copyright 1987, 1994, 1998  The Open Group
+
+All Rights Reserved.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define XK_VoidSymbol		0xFFFFFF	/* void symbol */
+
+#ifdef XK_MISCELLANY
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace		0xFF08	/* back space, back char */
+#define XK_Tab			0xFF09
+#define XK_Linefeed		0xFF0A	/* Linefeed, LF */
+#define XK_Clear		0xFF0B
+#define XK_Return		0xFF0D	/* Return, enter */
+#define XK_Pause		0xFF13	/* Pause, hold */
+#define XK_Scroll_Lock		0xFF14
+#define XK_Sys_Req		0xFF15
+#define XK_Escape		0xFF1B
+#define XK_Delete		0xFFFF	/* Delete, rubout */
+
+
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key		0xFF20  /* Multi-key character compose */
+#define XK_Codeinput		0xFF37
+#define XK_SingleCandidate	0xFF3C
+#define XK_MultipleCandidate	0xFF3D
+#define XK_PreviousCandidate	0xFF3E
+
+/* Japanese keyboard support */
+
+#define XK_Kanji		0xFF21	/* Kanji, Kanji convert */
+#define XK_Muhenkan		0xFF22  /* Cancel Conversion */
+#define XK_Henkan_Mode		0xFF23  /* Start/Stop Conversion */
+#define XK_Henkan		0xFF23  /* Alias for Henkan_Mode */
+#define XK_Romaji		0xFF24  /* to Romaji */
+#define XK_Hiragana		0xFF25  /* to Hiragana */
+#define XK_Katakana		0xFF26  /* to Katakana */
+#define XK_Hiragana_Katakana	0xFF27  /* Hiragana/Katakana toggle */
+#define XK_Zenkaku		0xFF28  /* to Zenkaku */
+#define XK_Hankaku		0xFF29  /* to Hankaku */
+#define XK_Zenkaku_Hankaku	0xFF2A  /* Zenkaku/Hankaku toggle */
+#define XK_Touroku		0xFF2B  /* Add to Dictionary */
+#define XK_Massyo		0xFF2C  /* Delete from Dictionary */
+#define XK_Kana_Lock		0xFF2D  /* Kana Lock */
+#define XK_Kana_Shift		0xFF2E  /* Kana Shift */
+#define XK_Eisu_Shift		0xFF2F  /* Alphanumeric Shift */
+#define XK_Eisu_toggle		0xFF30  /* Alphanumeric toggle */
+#define XK_Kanji_Bangou		0xFF37  /* Codeinput */
+#define XK_Zen_Koho		0xFF3D	/* Multiple/All Candidate(s) */
+#define XK_Mae_Koho		0xFF3E	/* Previous Candidate */
+
+/* 0xFF31 thru 0xFF3F are under XK_KOREAN */
+
+/* Cursor control & motion */
+
+#define XK_Home			0xFF50
+#define XK_Left			0xFF51	/* Move left, left arrow */
+#define XK_Up			0xFF52	/* Move up, up arrow */
+#define XK_Right		0xFF53	/* Move right, right arrow */
+#define XK_Down			0xFF54	/* Move down, down arrow */
+#define XK_Prior		0xFF55	/* Prior, previous */
+#define XK_Page_Up		0xFF55
+#define XK_Next			0xFF56	/* Next */
+#define XK_Page_Down		0xFF56
+#define XK_End			0xFF57	/* EOL */
+#define XK_Begin		0xFF58	/* BOL */
+
+
+/* Misc Functions */
+
+#define XK_Select		0xFF60	/* Select, mark */
+#define XK_Print		0xFF61
+#define XK_Execute		0xFF62	/* Execute, run, do */
+#define XK_Insert		0xFF63	/* Insert, insert here */
+#define XK_Undo			0xFF65	/* Undo, oops */
+#define XK_Redo			0xFF66	/* redo, again */
+#define XK_Menu			0xFF67
+#define XK_Find			0xFF68	/* Find, search */
+#define XK_Cancel		0xFF69	/* Cancel, stop, abort, exit */
+#define XK_Help			0xFF6A	/* Help */
+#define XK_Break		0xFF6B
+#define XK_Mode_switch		0xFF7E	/* Character set switch */
+#define XK_script_switch        0xFF7E  /* Alias for mode_switch */
+#define XK_Num_Lock		0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space		0xFF80	/* space */
+#define XK_KP_Tab		0xFF89
+#define XK_KP_Enter		0xFF8D	/* enter */
+#define XK_KP_F1		0xFF91	/* PF1, KP_A, ... */
+#define XK_KP_F2		0xFF92
+#define XK_KP_F3		0xFF93
+#define XK_KP_F4		0xFF94
+#define XK_KP_Home		0xFF95
+#define XK_KP_Left		0xFF96
+#define XK_KP_Up		0xFF97
+#define XK_KP_Right		0xFF98
+#define XK_KP_Down		0xFF99
+#define XK_KP_Prior		0xFF9A
+#define XK_KP_Page_Up		0xFF9A
+#define XK_KP_Next		0xFF9B
+#define XK_KP_Page_Down		0xFF9B
+#define XK_KP_End		0xFF9C
+#define XK_KP_Begin		0xFF9D
+#define XK_KP_Insert		0xFF9E
+#define XK_KP_Delete		0xFF9F
+#define XK_KP_Equal		0xFFBD	/* equals */
+#define XK_KP_Multiply		0xFFAA
+#define XK_KP_Add		0xFFAB
+#define XK_KP_Separator		0xFFAC	/* separator, often comma */
+#define XK_KP_Subtract		0xFFAD
+#define XK_KP_Decimal		0xFFAE
+#define XK_KP_Divide		0xFFAF
+
+#define XK_KP_0			0xFFB0
+#define XK_KP_1			0xFFB1
+#define XK_KP_2			0xFFB2
+#define XK_KP_3			0xFFB3
+#define XK_KP_4			0xFFB4
+#define XK_KP_5			0xFFB5
+#define XK_KP_6			0xFFB6
+#define XK_KP_7			0xFFB7
+#define XK_KP_8			0xFFB8
+#define XK_KP_9			0xFFB9
+
+
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys;  Sun keyboards and a few other manufactures 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.
+ */
+
+#define XK_F1			0xFFBE
+#define XK_F2			0xFFBF
+#define XK_F3			0xFFC0
+#define XK_F4			0xFFC1
+#define XK_F5			0xFFC2
+#define XK_F6			0xFFC3
+#define XK_F7			0xFFC4
+#define XK_F8			0xFFC5
+#define XK_F9			0xFFC6
+#define XK_F10			0xFFC7
+#define XK_F11			0xFFC8
+#define XK_L1			0xFFC8
+#define XK_F12			0xFFC9
+#define XK_L2			0xFFC9
+#define XK_F13			0xFFCA
+#define XK_L3			0xFFCA
+#define XK_F14			0xFFCB
+#define XK_L4			0xFFCB
+#define XK_F15			0xFFCC
+#define XK_L5			0xFFCC
+#define XK_F16			0xFFCD
+#define XK_L6			0xFFCD
+#define XK_F17			0xFFCE
+#define XK_L7			0xFFCE
+#define XK_F18			0xFFCF
+#define XK_L8			0xFFCF
+#define XK_F19			0xFFD0
+#define XK_L9			0xFFD0
+#define XK_F20			0xFFD1
+#define XK_L10			0xFFD1
+#define XK_F21			0xFFD2
+#define XK_R1			0xFFD2
+#define XK_F22			0xFFD3
+#define XK_R2			0xFFD3
+#define XK_F23			0xFFD4
+#define XK_R3			0xFFD4
+#define XK_F24			0xFFD5
+#define XK_R4			0xFFD5
+#define XK_F25			0xFFD6
+#define XK_R5			0xFFD6
+#define XK_F26			0xFFD7
+#define XK_R6			0xFFD7
+#define XK_F27			0xFFD8
+#define XK_R7			0xFFD8
+#define XK_F28			0xFFD9
+#define XK_R8			0xFFD9
+#define XK_F29			0xFFDA
+#define XK_R9			0xFFDA
+#define XK_F30			0xFFDB
+#define XK_R10			0xFFDB
+#define XK_F31			0xFFDC
+#define XK_R11			0xFFDC
+#define XK_F32			0xFFDD
+#define XK_R12			0xFFDD
+#define XK_F33			0xFFDE
+#define XK_R13			0xFFDE
+#define XK_F34			0xFFDF
+#define XK_R14			0xFFDF
+#define XK_F35			0xFFE0
+#define XK_R15			0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L		0xFFE1	/* Left shift */
+#define XK_Shift_R		0xFFE2	/* Right shift */
+#define XK_Control_L		0xFFE3	/* Left control */
+#define XK_Control_R		0xFFE4	/* Right control */
+#define XK_Caps_Lock		0xFFE5	/* Caps lock */
+#define XK_Shift_Lock		0xFFE6	/* Shift lock */
+
+#define XK_Meta_L		0xFFE7	/* Left meta */
+#define XK_Meta_R		0xFFE8	/* Right meta */
+#define XK_Alt_L		0xFFE9	/* Left alt */
+#define XK_Alt_R		0xFFEA	/* Right alt */
+#define XK_Super_L		0xFFEB	/* Left super */
+#define XK_Super_R		0xFFEC	/* Right super */
+#define XK_Hyper_L		0xFFED	/* Left hyper */
+#define XK_Hyper_R		0xFFEE	/* Right hyper */
+#endif /* XK_MISCELLANY */
+
+/*
+ * ISO 9995 Function and Modifier Keys
+ * Byte 3 = 0xFE
+ */
+
+#ifdef XK_XKB_KEYS
+#define	XK_ISO_Lock					0xFE01
+#define	XK_ISO_Level2_Latch				0xFE02
+#define	XK_ISO_Level3_Shift				0xFE03
+#define	XK_ISO_Level3_Latch				0xFE04
+#define	XK_ISO_Level3_Lock				0xFE05
+#define	XK_ISO_Group_Shift		0xFF7E	/* Alias for mode_switch */
+#define	XK_ISO_Group_Latch				0xFE06
+#define	XK_ISO_Group_Lock				0xFE07
+#define	XK_ISO_Next_Group				0xFE08
+#define	XK_ISO_Next_Group_Lock				0xFE09
+#define	XK_ISO_Prev_Group				0xFE0A
+#define	XK_ISO_Prev_Group_Lock				0xFE0B
+#define	XK_ISO_First_Group				0xFE0C
+#define	XK_ISO_First_Group_Lock				0xFE0D
+#define	XK_ISO_Last_Group				0xFE0E
+#define	XK_ISO_Last_Group_Lock				0xFE0F
+
+#define	XK_ISO_Left_Tab					0xFE20
+#define	XK_ISO_Move_Line_Up				0xFE21
+#define	XK_ISO_Move_Line_Down				0xFE22
+#define	XK_ISO_Partial_Line_Up				0xFE23
+#define	XK_ISO_Partial_Line_Down			0xFE24
+#define	XK_ISO_Partial_Space_Left			0xFE25
+#define	XK_ISO_Partial_Space_Right			0xFE26
+#define	XK_ISO_Set_Margin_Left				0xFE27
+#define	XK_ISO_Set_Margin_Right				0xFE28
+#define	XK_ISO_Release_Margin_Left			0xFE29
+#define	XK_ISO_Release_Margin_Right			0xFE2A
+#define	XK_ISO_Release_Both_Margins			0xFE2B
+#define	XK_ISO_Fast_Cursor_Left				0xFE2C
+#define	XK_ISO_Fast_Cursor_Right			0xFE2D
+#define	XK_ISO_Fast_Cursor_Up				0xFE2E
+#define	XK_ISO_Fast_Cursor_Down				0xFE2F
+#define	XK_ISO_Continuous_Underline			0xFE30
+#define	XK_ISO_Discontinuous_Underline			0xFE31
+#define	XK_ISO_Emphasize				0xFE32
+#define	XK_ISO_Center_Object				0xFE33
+#define	XK_ISO_Enter					0xFE34
+
+#define	XK_dead_grave					0xFE50
+#define	XK_dead_acute					0xFE51
+#define	XK_dead_circumflex				0xFE52
+#define	XK_dead_tilde					0xFE53
+#define	XK_dead_macron					0xFE54
+#define	XK_dead_breve					0xFE55
+#define	XK_dead_abovedot				0xFE56
+#define	XK_dead_diaeresis				0xFE57
+#define	XK_dead_abovering				0xFE58
+#define	XK_dead_doubleacute				0xFE59
+#define	XK_dead_caron					0xFE5A
+#define	XK_dead_cedilla					0xFE5B
+#define	XK_dead_ogonek					0xFE5C
+#define	XK_dead_iota					0xFE5D
+#define	XK_dead_voiced_sound				0xFE5E
+#define	XK_dead_semivoiced_sound			0xFE5F
+#define	XK_dead_belowdot				0xFE60
+
+#define	XK_First_Virtual_Screen				0xFED0
+#define	XK_Prev_Virtual_Screen				0xFED1
+#define	XK_Next_Virtual_Screen				0xFED2
+#define	XK_Last_Virtual_Screen				0xFED4
+#define	XK_Terminate_Server				0xFED5
+
+#define	XK_AccessX_Enable				0xFE70
+#define	XK_AccessX_Feedback_Enable			0xFE71
+#define	XK_RepeatKeys_Enable				0xFE72
+#define	XK_SlowKeys_Enable				0xFE73
+#define	XK_BounceKeys_Enable				0xFE74
+#define	XK_StickyKeys_Enable				0xFE75
+#define	XK_MouseKeys_Enable				0xFE76
+#define	XK_MouseKeys_Accel_Enable			0xFE77
+#define	XK_Overlay1_Enable				0xFE78
+#define	XK_Overlay2_Enable				0xFE79
+#define	XK_AudibleBell_Enable				0xFE7A
+
+#define	XK_Pointer_Left					0xFEE0
+#define	XK_Pointer_Right				0xFEE1
+#define	XK_Pointer_Up					0xFEE2
+#define	XK_Pointer_Down					0xFEE3
+#define	XK_Pointer_UpLeft				0xFEE4
+#define	XK_Pointer_UpRight				0xFEE5
+#define	XK_Pointer_DownLeft				0xFEE6
+#define	XK_Pointer_DownRight				0xFEE7
+#define	XK_Pointer_Button_Dflt				0xFEE8
+#define	XK_Pointer_Button1				0xFEE9
+#define	XK_Pointer_Button2				0xFEEA
+#define	XK_Pointer_Button3				0xFEEB
+#define	XK_Pointer_Button4				0xFEEC
+#define	XK_Pointer_Button5				0xFEED
+#define	XK_Pointer_DblClick_Dflt			0xFEEE
+#define	XK_Pointer_DblClick1				0xFEEF
+#define	XK_Pointer_DblClick2				0xFEF0
+#define	XK_Pointer_DblClick3				0xFEF1
+#define	XK_Pointer_DblClick4				0xFEF2
+#define	XK_Pointer_DblClick5				0xFEF3
+#define	XK_Pointer_Drag_Dflt				0xFEF4
+#define	XK_Pointer_Drag1				0xFEF5
+#define	XK_Pointer_Drag2				0xFEF6
+#define	XK_Pointer_Drag3				0xFEF7
+#define	XK_Pointer_Drag4				0xFEF8
+#define	XK_Pointer_Drag5				0xFEFD
+
+#define	XK_Pointer_EnableKeys				0xFEF9
+#define	XK_Pointer_Accelerate				0xFEFA
+#define	XK_Pointer_DfltBtnNext				0xFEFB
+#define	XK_Pointer_DfltBtnPrev				0xFEFC
+
+#endif
+
+/*
+ * 3270 Terminal Keys
+ * Byte 3 = 0xFD
+ */
+
+#ifdef XK_3270
+#define XK_3270_Duplicate      0xFD01
+#define XK_3270_FieldMark      0xFD02
+#define XK_3270_Right2         0xFD03
+#define XK_3270_Left2          0xFD04
+#define XK_3270_BackTab        0xFD05
+#define XK_3270_EraseEOF       0xFD06
+#define XK_3270_EraseInput     0xFD07
+#define XK_3270_Reset          0xFD08
+#define XK_3270_Quit           0xFD09
+#define XK_3270_PA1            0xFD0A
+#define XK_3270_PA2            0xFD0B
+#define XK_3270_PA3            0xFD0C
+#define XK_3270_Test           0xFD0D
+#define XK_3270_Attn           0xFD0E
+#define XK_3270_CursorBlink    0xFD0F
+#define XK_3270_AltCursor      0xFD10
+#define XK_3270_KeyClick       0xFD11
+#define XK_3270_Jump           0xFD12
+#define XK_3270_Ident          0xFD13
+#define XK_3270_Rule           0xFD14
+#define XK_3270_Copy           0xFD15
+#define XK_3270_Play           0xFD16
+#define XK_3270_Setup          0xFD17
+#define XK_3270_Record         0xFD18
+#define XK_3270_ChangeScreen   0xFD19
+#define XK_3270_DeleteWord     0xFD1A
+#define XK_3270_ExSelect       0xFD1B
+#define XK_3270_CursorSelect   0xFD1C
+#define XK_3270_PrintScreen    0xFD1D
+#define XK_3270_Enter          0xFD1E
+#endif
+
+/*
+ *  Latin 1
+ *  Byte 3 = 0
+ */
+#ifdef XK_LATIN1
+#define XK_space               0x020
+#define XK_exclam              0x021
+#define XK_quotedbl            0x022
+#define XK_numbersign          0x023
+#define XK_dollar              0x024
+#define XK_percent             0x025
+#define XK_ampersand           0x026
+#define XK_apostrophe          0x027
+#define XK_quoteright          0x027	/* deprecated */
+#define XK_parenleft           0x028
+#define XK_parenright          0x029
+#define XK_asterisk            0x02a
+#define XK_plus                0x02b
+#define XK_comma               0x02c
+#define XK_minus               0x02d
+#define XK_period              0x02e
+#define XK_slash               0x02f
+#define XK_0                   0x030
+#define XK_1                   0x031
+#define XK_2                   0x032
+#define XK_3                   0x033
+#define XK_4                   0x034
+#define XK_5                   0x035
+#define XK_6                   0x036
+#define XK_7                   0x037
+#define XK_8                   0x038
+#define XK_9                   0x039
+#define XK_colon               0x03a
+#define XK_semicolon           0x03b
+#define XK_less                0x03c
+#define XK_equal               0x03d
+#define XK_greater             0x03e
+#define XK_question            0x03f
+#define XK_at                  0x040
+#define XK_A                   0x041
+#define XK_B                   0x042
+#define XK_C                   0x043
+#define XK_D                   0x044
+#define XK_E                   0x045
+#define XK_F                   0x046
+#define XK_G                   0x047
+#define XK_H                   0x048
+#define XK_I                   0x049
+#define XK_J                   0x04a
+#define XK_K                   0x04b
+#define XK_L                   0x04c
+#define XK_M                   0x04d
+#define XK_N                   0x04e
+#define XK_O                   0x04f
+#define XK_P                   0x050
+#define XK_Q                   0x051
+#define XK_R                   0x052
+#define XK_S                   0x053
+#define XK_T                   0x054
+#define XK_U                   0x055
+#define XK_V                   0x056
+#define XK_W                   0x057
+#define XK_X                   0x058
+#define XK_Y                   0x059
+#define XK_Z                   0x05a
+#define XK_bracketleft         0x05b
+#define XK_backslash           0x05c
+#define XK_bracketright        0x05d
+#define XK_asciicircum         0x05e
+#define XK_underscore          0x05f
+#define XK_grave               0x060
+#define XK_quoteleft           0x060	/* deprecated */
+#define XK_a                   0x061
+#define XK_b                   0x062
+#define XK_c                   0x063
+#define XK_d                   0x064
+#define XK_e                   0x065
+#define XK_f                   0x066
+#define XK_g                   0x067
+#define XK_h                   0x068
+#define XK_i                   0x069
+#define XK_j                   0x06a
+#define XK_k                   0x06b
+#define XK_l                   0x06c
+#define XK_m                   0x06d
+#define XK_n                   0x06e
+#define XK_o                   0x06f
+#define XK_p                   0x070
+#define XK_q                   0x071
+#define XK_r                   0x072
+#define XK_s                   0x073
+#define XK_t                   0x074
+#define XK_u                   0x075
+#define XK_v                   0x076
+#define XK_w                   0x077
+#define XK_x                   0x078
+#define XK_y                   0x079
+#define XK_z                   0x07a
+#define XK_braceleft           0x07b
+#define XK_bar                 0x07c
+#define XK_braceright          0x07d
+#define XK_asciitilde          0x07e
+
+#define XK_nobreakspace        0x0a0
+#define XK_exclamdown          0x0a1
+#define XK_cent        	       0x0a2
+#define XK_sterling            0x0a3
+#define XK_currency            0x0a4
+#define XK_yen                 0x0a5
+#define XK_brokenbar           0x0a6
+#define XK_section             0x0a7
+#define XK_diaeresis           0x0a8
+#define XK_copyright           0x0a9
+#define XK_ordfeminine         0x0aa
+#define XK_guillemotleft       0x0ab	/* left angle quotation mark */
+#define XK_notsign             0x0ac
+#define XK_hyphen              0x0ad
+#define XK_registered          0x0ae
+#define XK_macron              0x0af
+#define XK_degree              0x0b0
+#define XK_plusminus           0x0b1
+#define XK_twosuperior         0x0b2
+#define XK_threesuperior       0x0b3
+#define XK_acute               0x0b4
+#define XK_mu                  0x0b5
+#define XK_paragraph           0x0b6
+#define XK_periodcentered      0x0b7
+#define XK_cedilla             0x0b8
+#define XK_onesuperior         0x0b9
+#define XK_masculine           0x0ba
+#define XK_guillemotright      0x0bb	/* right angle quotation mark */
+#define XK_onequarter          0x0bc
+#define XK_onehalf             0x0bd
+#define XK_threequarters       0x0be
+#define XK_questiondown        0x0bf
+#define XK_Agrave              0x0c0
+#define XK_Aacute              0x0c1
+#define XK_Acircumflex         0x0c2
+#define XK_Atilde              0x0c3
+#define XK_Adiaeresis          0x0c4
+#define XK_Aring               0x0c5
+#define XK_AE                  0x0c6
+#define XK_Ccedilla            0x0c7
+#define XK_Egrave              0x0c8
+#define XK_Eacute              0x0c9
+#define XK_Ecircumflex         0x0ca
+#define XK_Ediaeresis          0x0cb
+#define XK_Igrave              0x0cc
+#define XK_Iacute              0x0cd
+#define XK_Icircumflex         0x0ce
+#define XK_Idiaeresis          0x0cf
+#define XK_ETH                 0x0d0
+#define XK_Eth                 0x0d0	/* deprecated */
+#define XK_Ntilde              0x0d1
+#define XK_Ograve              0x0d2
+#define XK_Oacute              0x0d3
+#define XK_Ocircumflex         0x0d4
+#define XK_Otilde              0x0d5
+#define XK_Odiaeresis          0x0d6
+#define XK_multiply            0x0d7
+#define XK_Ooblique            0x0d8
+#define XK_Ugrave              0x0d9
+#define XK_Uacute              0x0da
+#define XK_Ucircumflex         0x0db
+#define XK_Udiaeresis          0x0dc
+#define XK_Yacute              0x0dd
+#define XK_THORN               0x0de
+#define XK_Thorn               0x0de	/* deprecated */
+#define XK_ssharp              0x0df
+#define XK_agrave              0x0e0
+#define XK_aacute              0x0e1
+#define XK_acircumflex         0x0e2
+#define XK_atilde              0x0e3
+#define XK_adiaeresis          0x0e4
+#define XK_aring               0x0e5
+#define XK_ae                  0x0e6
+#define XK_ccedilla            0x0e7
+#define XK_egrave              0x0e8
+#define XK_eacute              0x0e9
+#define XK_ecircumflex         0x0ea
+#define XK_ediaeresis          0x0eb
+#define XK_igrave              0x0ec
+#define XK_iacute              0x0ed
+#define XK_icircumflex         0x0ee
+#define XK_idiaeresis          0x0ef
+#define XK_eth                 0x0f0
+#define XK_ntilde              0x0f1
+#define XK_ograve              0x0f2
+#define XK_oacute              0x0f3
+#define XK_ocircumflex         0x0f4
+#define XK_otilde              0x0f5
+#define XK_odiaeresis          0x0f6
+#define XK_division            0x0f7
+#define XK_oslash              0x0f8
+#define XK_ugrave              0x0f9
+#define XK_uacute              0x0fa
+#define XK_ucircumflex         0x0fb
+#define XK_udiaeresis          0x0fc
+#define XK_yacute              0x0fd
+#define XK_thorn               0x0fe
+#define XK_ydiaeresis          0x0ff
+#endif /* XK_LATIN1 */
+
+/*
+ *   Latin 2
+ *   Byte 3 = 1
+ */
+
+#ifdef XK_LATIN2
+#define XK_Aogonek             0x1a1
+#define XK_breve               0x1a2
+#define XK_Lstroke             0x1a3
+#define XK_Lcaron              0x1a5
+#define XK_Sacute              0x1a6
+#define XK_Scaron              0x1a9
+#define XK_Scedilla            0x1aa
+#define XK_Tcaron              0x1ab
+#define XK_Zacute              0x1ac
+#define XK_Zcaron              0x1ae
+#define XK_Zabovedot           0x1af
+#define XK_aogonek             0x1b1
+#define XK_ogonek              0x1b2
+#define XK_lstroke             0x1b3
+#define XK_lcaron              0x1b5
+#define XK_sacute              0x1b6
+#define XK_caron               0x1b7
+#define XK_scaron              0x1b9
+#define XK_scedilla            0x1ba
+#define XK_tcaron              0x1bb
+#define XK_zacute              0x1bc
+#define XK_doubleacute         0x1bd
+#define XK_zcaron              0x1be
+#define XK_zabovedot           0x1bf
+#define XK_Racute              0x1c0
+#define XK_Abreve              0x1c3
+#define XK_Lacute              0x1c5
+#define XK_Cacute              0x1c6
+#define XK_Ccaron              0x1c8
+#define XK_Eogonek             0x1ca
+#define XK_Ecaron              0x1cc
+#define XK_Dcaron              0x1cf
+#define XK_Dstroke             0x1d0
+#define XK_Nacute              0x1d1
+#define XK_Ncaron              0x1d2
+#define XK_Odoubleacute        0x1d5
+#define XK_Rcaron              0x1d8
+#define XK_Uring               0x1d9
+#define XK_Udoubleacute        0x1db
+#define XK_Tcedilla            0x1de
+#define XK_racute              0x1e0
+#define XK_abreve              0x1e3
+#define XK_lacute              0x1e5
+#define XK_cacute              0x1e6
+#define XK_ccaron              0x1e8
+#define XK_eogonek             0x1ea
+#define XK_ecaron              0x1ec
+#define XK_dcaron              0x1ef
+#define XK_dstroke             0x1f0
+#define XK_nacute              0x1f1
+#define XK_ncaron              0x1f2
+#define XK_odoubleacute        0x1f5
+#define XK_udoubleacute        0x1fb
+#define XK_rcaron              0x1f8
+#define XK_uring               0x1f9
+#define XK_tcedilla            0x1fe
+#define XK_abovedot            0x1ff
+#endif /* XK_LATIN2 */
+
+/*
+ *   Latin 3
+ *   Byte 3 = 2
+ */
+
+#ifdef XK_LATIN3
+#define XK_Hstroke             0x2a1
+#define XK_Hcircumflex         0x2a6
+#define XK_Iabovedot           0x2a9
+#define XK_Gbreve              0x2ab
+#define XK_Jcircumflex         0x2ac
+#define XK_hstroke             0x2b1
+#define XK_hcircumflex         0x2b6
+#define XK_idotless            0x2b9
+#define XK_gbreve              0x2bb
+#define XK_jcircumflex         0x2bc
+#define XK_Cabovedot           0x2c5
+#define XK_Ccircumflex         0x2c6
+#define XK_Gabovedot           0x2d5
+#define XK_Gcircumflex         0x2d8
+#define XK_Ubreve              0x2dd
+#define XK_Scircumflex         0x2de
+#define XK_cabovedot           0x2e5
+#define XK_ccircumflex         0x2e6
+#define XK_gabovedot           0x2f5
+#define XK_gcircumflex         0x2f8
+#define XK_ubreve              0x2fd
+#define XK_scircumflex         0x2fe
+#endif /* XK_LATIN3 */
+
+
+/*
+ *   Latin 4
+ *   Byte 3 = 3
+ */
+
+#ifdef XK_LATIN4
+#define XK_kra                 0x3a2
+#define XK_kappa               0x3a2	/* deprecated */
+#define XK_Rcedilla            0x3a3
+#define XK_Itilde              0x3a5
+#define XK_Lcedilla            0x3a6
+#define XK_Emacron             0x3aa
+#define XK_Gcedilla            0x3ab
+#define XK_Tslash              0x3ac
+#define XK_rcedilla            0x3b3
+#define XK_itilde              0x3b5
+#define XK_lcedilla            0x3b6
+#define XK_emacron             0x3ba
+#define XK_gcedilla            0x3bb
+#define XK_tslash              0x3bc
+#define XK_ENG                 0x3bd
+#define XK_eng                 0x3bf
+#define XK_Amacron             0x3c0
+#define XK_Iogonek             0x3c7
+#define XK_Eabovedot           0x3cc
+#define XK_Imacron             0x3cf
+#define XK_Ncedilla            0x3d1
+#define XK_Omacron             0x3d2
+#define XK_Kcedilla            0x3d3
+#define XK_Uogonek             0x3d9
+#define XK_Utilde              0x3dd
+#define XK_Umacron             0x3de
+#define XK_amacron             0x3e0
+#define XK_iogonek             0x3e7
+#define XK_eabovedot           0x3ec
+#define XK_imacron             0x3ef
+#define XK_ncedilla            0x3f1
+#define XK_omacron             0x3f2
+#define XK_kcedilla            0x3f3
+#define XK_uogonek             0x3f9
+#define XK_utilde              0x3fd
+#define XK_umacron             0x3fe
+#endif /* XK_LATIN4 */
+
+/*
+ * Latin-9 (a.k.a. Latin-0)
+ * Byte 3 = 19
+ */
+
+#ifdef XK_LATIN9
+#define XK_OE                  0x13bc
+#define XK_oe                  0x13bd
+#define XK_Ydiaeresis          0x13be
+#endif /* XK_LATIN9 */
+
+/*
+ * Katakana
+ * Byte 3 = 4
+ */
+
+#ifdef XK_KATAKANA
+#define XK_overline				       0x47e
+#define XK_kana_fullstop                               0x4a1
+#define XK_kana_openingbracket                         0x4a2
+#define XK_kana_closingbracket                         0x4a3
+#define XK_kana_comma                                  0x4a4
+#define XK_kana_conjunctive                            0x4a5
+#define XK_kana_middledot                              0x4a5  /* deprecated */
+#define XK_kana_WO                                     0x4a6
+#define XK_kana_a                                      0x4a7
+#define XK_kana_i                                      0x4a8
+#define XK_kana_u                                      0x4a9
+#define XK_kana_e                                      0x4aa
+#define XK_kana_o                                      0x4ab
+#define XK_kana_ya                                     0x4ac
+#define XK_kana_yu                                     0x4ad
+#define XK_kana_yo                                     0x4ae
+#define XK_kana_tsu                                    0x4af
+#define XK_kana_tu                                     0x4af  /* deprecated */
+#define XK_prolongedsound                              0x4b0
+#define XK_kana_A                                      0x4b1
+#define XK_kana_I                                      0x4b2
+#define XK_kana_U                                      0x4b3
+#define XK_kana_E                                      0x4b4
+#define XK_kana_O                                      0x4b5
+#define XK_kana_KA                                     0x4b6
+#define XK_kana_KI                                     0x4b7
+#define XK_kana_KU                                     0x4b8
+#define XK_kana_KE                                     0x4b9
+#define XK_kana_KO                                     0x4ba
+#define XK_kana_SA                                     0x4bb
+#define XK_kana_SHI                                    0x4bc
+#define XK_kana_SU                                     0x4bd
+#define XK_kana_SE                                     0x4be
+#define XK_kana_SO                                     0x4bf
+#define XK_kana_TA                                     0x4c0
+#define XK_kana_CHI                                    0x4c1
+#define XK_kana_TI                                     0x4c1  /* deprecated */
+#define XK_kana_TSU                                    0x4c2
+#define XK_kana_TU                                     0x4c2  /* deprecated */
+#define XK_kana_TE                                     0x4c3
+#define XK_kana_TO                                     0x4c4
+#define XK_kana_NA                                     0x4c5
+#define XK_kana_NI                                     0x4c6
+#define XK_kana_NU                                     0x4c7
+#define XK_kana_NE                                     0x4c8
+#define XK_kana_NO                                     0x4c9
+#define XK_kana_HA                                     0x4ca
+#define XK_kana_HI                                     0x4cb
+#define XK_kana_FU                                     0x4cc
+#define XK_kana_HU                                     0x4cc  /* deprecated */
+#define XK_kana_HE                                     0x4cd
+#define XK_kana_HO                                     0x4ce
+#define XK_kana_MA                                     0x4cf
+#define XK_kana_MI                                     0x4d0
+#define XK_kana_MU                                     0x4d1
+#define XK_kana_ME                                     0x4d2
+#define XK_kana_MO                                     0x4d3
+#define XK_kana_YA                                     0x4d4
+#define XK_kana_YU                                     0x4d5
+#define XK_kana_YO                                     0x4d6
+#define XK_kana_RA                                     0x4d7
+#define XK_kana_RI                                     0x4d8
+#define XK_kana_RU                                     0x4d9
+#define XK_kana_RE                                     0x4da
+#define XK_kana_RO                                     0x4db
+#define XK_kana_WA                                     0x4dc
+#define XK_kana_N                                      0x4dd
+#define XK_voicedsound                                 0x4de
+#define XK_semivoicedsound                             0x4df
+#define XK_kana_switch          0xFF7E  /* Alias for mode_switch */
+#endif /* XK_KATAKANA */
+
+/*
+ *  Arabic
+ *  Byte 3 = 5
+ */
+
+#ifdef XK_ARABIC
+#define XK_Arabic_comma                                0x5ac
+#define XK_Arabic_semicolon                            0x5bb
+#define XK_Arabic_question_mark                        0x5bf
+#define XK_Arabic_hamza                                0x5c1
+#define XK_Arabic_maddaonalef                          0x5c2
+#define XK_Arabic_hamzaonalef                          0x5c3
+#define XK_Arabic_hamzaonwaw                           0x5c4
+#define XK_Arabic_hamzaunderalef                       0x5c5
+#define XK_Arabic_hamzaonyeh                           0x5c6
+#define XK_Arabic_alef                                 0x5c7
+#define XK_Arabic_beh                                  0x5c8
+#define XK_Arabic_tehmarbuta                           0x5c9
+#define XK_Arabic_teh                                  0x5ca
+#define XK_Arabic_theh                                 0x5cb
+#define XK_Arabic_jeem                                 0x5cc
+#define XK_Arabic_hah                                  0x5cd
+#define XK_Arabic_khah                                 0x5ce
+#define XK_Arabic_dal                                  0x5cf
+#define XK_Arabic_thal                                 0x5d0
+#define XK_Arabic_ra                                   0x5d1
+#define XK_Arabic_zain                                 0x5d2
+#define XK_Arabic_seen                                 0x5d3
+#define XK_Arabic_sheen                                0x5d4
+#define XK_Arabic_sad                                  0x5d5
+#define XK_Arabic_dad                                  0x5d6
+#define XK_Arabic_tah                                  0x5d7
+#define XK_Arabic_zah                                  0x5d8
+#define XK_Arabic_ain                                  0x5d9
+#define XK_Arabic_ghain                                0x5da
+#define XK_Arabic_tatweel                              0x5e0
+#define XK_Arabic_feh                                  0x5e1
+#define XK_Arabic_qaf                                  0x5e2
+#define XK_Arabic_kaf                                  0x5e3
+#define XK_Arabic_lam                                  0x5e4
+#define XK_Arabic_meem                                 0x5e5
+#define XK_Arabic_noon                                 0x5e6
+#define XK_Arabic_ha                                   0x5e7
+#define XK_Arabic_heh                                  0x5e7  /* deprecated */
+#define XK_Arabic_waw                                  0x5e8
+#define XK_Arabic_alefmaksura                          0x5e9
+#define XK_Arabic_yeh                                  0x5ea
+#define XK_Arabic_fathatan                             0x5eb
+#define XK_Arabic_dammatan                             0x5ec
+#define XK_Arabic_kasratan                             0x5ed
+#define XK_Arabic_fatha                                0x5ee
+#define XK_Arabic_damma                                0x5ef
+#define XK_Arabic_kasra                                0x5f0
+#define XK_Arabic_shadda                               0x5f1
+#define XK_Arabic_sukun                                0x5f2
+#define XK_Arabic_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_ARABIC */
+
+/*
+ * Cyrillic
+ * Byte 3 = 6
+ */
+#ifdef XK_CYRILLIC
+#define XK_Serbian_dje                                 0x6a1
+#define XK_Macedonia_gje                               0x6a2
+#define XK_Cyrillic_io                                 0x6a3
+#define XK_Ukrainian_ie                                0x6a4
+#define XK_Ukranian_je                                 0x6a4  /* deprecated */
+#define XK_Macedonia_dse                               0x6a5
+#define XK_Ukrainian_i                                 0x6a6
+#define XK_Ukranian_i                                  0x6a6  /* deprecated */
+#define XK_Ukrainian_yi                                0x6a7
+#define XK_Ukranian_yi                                 0x6a7  /* deprecated */
+#define XK_Cyrillic_je                                 0x6a8
+#define XK_Serbian_je                                  0x6a8  /* deprecated */
+#define XK_Cyrillic_lje                                0x6a9
+#define XK_Serbian_lje                                 0x6a9  /* deprecated */
+#define XK_Cyrillic_nje                                0x6aa
+#define XK_Serbian_nje                                 0x6aa  /* deprecated */
+#define XK_Serbian_tshe                                0x6ab
+#define XK_Macedonia_kje                               0x6ac
+#define XK_Byelorussian_shortu                         0x6ae
+#define XK_Cyrillic_dzhe                               0x6af
+#define XK_Serbian_dze                                 0x6af  /* deprecated */
+#define XK_numerosign                                  0x6b0
+#define XK_Serbian_DJE                                 0x6b1
+#define XK_Macedonia_GJE                               0x6b2
+#define XK_Cyrillic_IO                                 0x6b3
+#define XK_Ukrainian_IE                                0x6b4
+#define XK_Ukranian_JE                                 0x6b4  /* deprecated */
+#define XK_Macedonia_DSE                               0x6b5
+#define XK_Ukrainian_I                                 0x6b6
+#define XK_Ukranian_I                                  0x6b6  /* deprecated */
+#define XK_Ukrainian_YI                                0x6b7
+#define XK_Ukranian_YI                                 0x6b7  /* deprecated */
+#define XK_Cyrillic_JE                                 0x6b8
+#define XK_Serbian_JE                                  0x6b8  /* deprecated */
+#define XK_Cyrillic_LJE                                0x6b9
+#define XK_Serbian_LJE                                 0x6b9  /* deprecated */
+#define XK_Cyrillic_NJE                                0x6ba
+#define XK_Serbian_NJE                                 0x6ba  /* deprecated */
+#define XK_Serbian_TSHE                                0x6bb
+#define XK_Macedonia_KJE                               0x6bc
+#define XK_Byelorussian_SHORTU                         0x6be
+#define XK_Cyrillic_DZHE                               0x6bf
+#define XK_Serbian_DZE                                 0x6bf  /* deprecated */
+#define XK_Cyrillic_yu                                 0x6c0
+#define XK_Cyrillic_a                                  0x6c1
+#define XK_Cyrillic_be                                 0x6c2
+#define XK_Cyrillic_tse                                0x6c3
+#define XK_Cyrillic_de                                 0x6c4
+#define XK_Cyrillic_ie                                 0x6c5
+#define XK_Cyrillic_ef                                 0x6c6
+#define XK_Cyrillic_ghe                                0x6c7
+#define XK_Cyrillic_ha                                 0x6c8
+#define XK_Cyrillic_i                                  0x6c9
+#define XK_Cyrillic_shorti                             0x6ca
+#define XK_Cyrillic_ka                                 0x6cb
+#define XK_Cyrillic_el                                 0x6cc
+#define XK_Cyrillic_em                                 0x6cd
+#define XK_Cyrillic_en                                 0x6ce
+#define XK_Cyrillic_o                                  0x6cf
+#define XK_Cyrillic_pe                                 0x6d0
+#define XK_Cyrillic_ya                                 0x6d1
+#define XK_Cyrillic_er                                 0x6d2
+#define XK_Cyrillic_es                                 0x6d3
+#define XK_Cyrillic_te                                 0x6d4
+#define XK_Cyrillic_u                                  0x6d5
+#define XK_Cyrillic_zhe                                0x6d6
+#define XK_Cyrillic_ve                                 0x6d7
+#define XK_Cyrillic_softsign                           0x6d8
+#define XK_Cyrillic_yeru                               0x6d9
+#define XK_Cyrillic_ze                                 0x6da
+#define XK_Cyrillic_sha                                0x6db
+#define XK_Cyrillic_e                                  0x6dc
+#define XK_Cyrillic_shcha                              0x6dd
+#define XK_Cyrillic_che                                0x6de
+#define XK_Cyrillic_hardsign                           0x6df
+#define XK_Cyrillic_YU                                 0x6e0
+#define XK_Cyrillic_A                                  0x6e1
+#define XK_Cyrillic_BE                                 0x6e2
+#define XK_Cyrillic_TSE                                0x6e3
+#define XK_Cyrillic_DE                                 0x6e4
+#define XK_Cyrillic_IE                                 0x6e5
+#define XK_Cyrillic_EF                                 0x6e6
+#define XK_Cyrillic_GHE                                0x6e7
+#define XK_Cyrillic_HA                                 0x6e8
+#define XK_Cyrillic_I                                  0x6e9
+#define XK_Cyrillic_SHORTI                             0x6ea
+#define XK_Cyrillic_KA                                 0x6eb
+#define XK_Cyrillic_EL                                 0x6ec
+#define XK_Cyrillic_EM                                 0x6ed
+#define XK_Cyrillic_EN                                 0x6ee
+#define XK_Cyrillic_O                                  0x6ef
+#define XK_Cyrillic_PE                                 0x6f0
+#define XK_Cyrillic_YA                                 0x6f1
+#define XK_Cyrillic_ER                                 0x6f2
+#define XK_Cyrillic_ES                                 0x6f3
+#define XK_Cyrillic_TE                                 0x6f4
+#define XK_Cyrillic_U                                  0x6f5
+#define XK_Cyrillic_ZHE                                0x6f6
+#define XK_Cyrillic_VE                                 0x6f7
+#define XK_Cyrillic_SOFTSIGN                           0x6f8
+#define XK_Cyrillic_YERU                               0x6f9
+#define XK_Cyrillic_ZE                                 0x6fa
+#define XK_Cyrillic_SHA                                0x6fb
+#define XK_Cyrillic_E                                  0x6fc
+#define XK_Cyrillic_SHCHA                              0x6fd
+#define XK_Cyrillic_CHE                                0x6fe
+#define XK_Cyrillic_HARDSIGN                           0x6ff
+#endif /* XK_CYRILLIC */
+
+/*
+ * Greek
+ * Byte 3 = 7
+ */
+
+#ifdef XK_GREEK
+#define XK_Greek_ALPHAaccent                           0x7a1
+#define XK_Greek_EPSILONaccent                         0x7a2
+#define XK_Greek_ETAaccent                             0x7a3
+#define XK_Greek_IOTAaccent                            0x7a4
+#define XK_Greek_IOTAdiaeresis                         0x7a5
+#define XK_Greek_OMICRONaccent                         0x7a7
+#define XK_Greek_UPSILONaccent                         0x7a8
+#define XK_Greek_UPSILONdieresis                       0x7a9
+#define XK_Greek_OMEGAaccent                           0x7ab
+#define XK_Greek_accentdieresis                        0x7ae
+#define XK_Greek_horizbar                              0x7af
+#define XK_Greek_alphaaccent                           0x7b1
+#define XK_Greek_epsilonaccent                         0x7b2
+#define XK_Greek_etaaccent                             0x7b3
+#define XK_Greek_iotaaccent                            0x7b4
+#define XK_Greek_iotadieresis                          0x7b5
+#define XK_Greek_iotaaccentdieresis                    0x7b6
+#define XK_Greek_omicronaccent                         0x7b7
+#define XK_Greek_upsilonaccent                         0x7b8
+#define XK_Greek_upsilondieresis                       0x7b9
+#define XK_Greek_upsilonaccentdieresis                 0x7ba
+#define XK_Greek_omegaaccent                           0x7bb
+#define XK_Greek_ALPHA                                 0x7c1
+#define XK_Greek_BETA                                  0x7c2
+#define XK_Greek_GAMMA                                 0x7c3
+#define XK_Greek_DELTA                                 0x7c4
+#define XK_Greek_EPSILON                               0x7c5
+#define XK_Greek_ZETA                                  0x7c6
+#define XK_Greek_ETA                                   0x7c7
+#define XK_Greek_THETA                                 0x7c8
+#define XK_Greek_IOTA                                  0x7c9
+#define XK_Greek_KAPPA                                 0x7ca
+#define XK_Greek_LAMDA                                 0x7cb
+#define XK_Greek_LAMBDA                                0x7cb
+#define XK_Greek_MU                                    0x7cc
+#define XK_Greek_NU                                    0x7cd
+#define XK_Greek_XI                                    0x7ce
+#define XK_Greek_OMICRON                               0x7cf
+#define XK_Greek_PI                                    0x7d0
+#define XK_Greek_RHO                                   0x7d1
+#define XK_Greek_SIGMA                                 0x7d2
+#define XK_Greek_TAU                                   0x7d4
+#define XK_Greek_UPSILON                               0x7d5
+#define XK_Greek_PHI                                   0x7d6
+#define XK_Greek_CHI                                   0x7d7
+#define XK_Greek_PSI                                   0x7d8
+#define XK_Greek_OMEGA                                 0x7d9
+#define XK_Greek_alpha                                 0x7e1
+#define XK_Greek_beta                                  0x7e2
+#define XK_Greek_gamma                                 0x7e3
+#define XK_Greek_delta                                 0x7e4
+#define XK_Greek_epsilon                               0x7e5
+#define XK_Greek_zeta                                  0x7e6
+#define XK_Greek_eta                                   0x7e7
+#define XK_Greek_theta                                 0x7e8
+#define XK_Greek_iota                                  0x7e9
+#define XK_Greek_kappa                                 0x7ea
+#define XK_Greek_lamda                                 0x7eb
+#define XK_Greek_lambda                                0x7eb
+#define XK_Greek_mu                                    0x7ec
+#define XK_Greek_nu                                    0x7ed
+#define XK_Greek_xi                                    0x7ee
+#define XK_Greek_omicron                               0x7ef
+#define XK_Greek_pi                                    0x7f0
+#define XK_Greek_rho                                   0x7f1
+#define XK_Greek_sigma                                 0x7f2
+#define XK_Greek_finalsmallsigma                       0x7f3
+#define XK_Greek_tau                                   0x7f4
+#define XK_Greek_upsilon                               0x7f5
+#define XK_Greek_phi                                   0x7f6
+#define XK_Greek_chi                                   0x7f7
+#define XK_Greek_psi                                   0x7f8
+#define XK_Greek_omega                                 0x7f9
+#define XK_Greek_switch         0xFF7E  /* Alias for mode_switch */
+#endif /* XK_GREEK */
+
+/*
+ * Technical
+ * Byte 3 = 8
+ */
+
+#ifdef XK_TECHNICAL
+#define XK_leftradical                                 0x8a1
+#define XK_topleftradical                              0x8a2
+#define XK_horizconnector                              0x8a3
+#define XK_topintegral                                 0x8a4
+#define XK_botintegral                                 0x8a5
+#define XK_vertconnector                               0x8a6
+#define XK_topleftsqbracket                            0x8a7
+#define XK_botleftsqbracket                            0x8a8
+#define XK_toprightsqbracket                           0x8a9
+#define XK_botrightsqbracket                           0x8aa
+#define XK_topleftparens                               0x8ab
+#define XK_botleftparens                               0x8ac
+#define XK_toprightparens                              0x8ad
+#define XK_botrightparens                              0x8ae
+#define XK_leftmiddlecurlybrace                        0x8af
+#define XK_rightmiddlecurlybrace                       0x8b0
+#define XK_topleftsummation                            0x8b1
+#define XK_botleftsummation                            0x8b2
+#define XK_topvertsummationconnector                   0x8b3
+#define XK_botvertsummationconnector                   0x8b4
+#define XK_toprightsummation                           0x8b5
+#define XK_botrightsummation                           0x8b6
+#define XK_rightmiddlesummation                        0x8b7
+#define XK_lessthanequal                               0x8bc
+#define XK_notequal                                    0x8bd
+#define XK_greaterthanequal                            0x8be
+#define XK_integral                                    0x8bf
+#define XK_therefore                                   0x8c0
+#define XK_variation                                   0x8c1
+#define XK_infinity                                    0x8c2
+#define XK_nabla                                       0x8c5
+#define XK_approximate                                 0x8c8
+#define XK_similarequal                                0x8c9
+#define XK_ifonlyif                                    0x8cd
+#define XK_implies                                     0x8ce
+#define XK_identical                                   0x8cf
+#define XK_radical                                     0x8d6
+#define XK_includedin                                  0x8da
+#define XK_includes                                    0x8db
+#define XK_intersection                                0x8dc
+#define XK_union                                       0x8dd
+#define XK_logicaland                                  0x8de
+#define XK_logicalor                                   0x8df
+#define XK_partialderivative                           0x8ef
+#define XK_function                                    0x8f6
+#define XK_leftarrow                                   0x8fb
+#define XK_uparrow                                     0x8fc
+#define XK_rightarrow                                  0x8fd
+#define XK_downarrow                                   0x8fe
+#endif /* XK_TECHNICAL */
+
+/*
+ *  Special
+ *  Byte 3 = 9
+ */
+
+#ifdef XK_SPECIAL
+#define XK_blank                                       0x9df
+#define XK_soliddiamond                                0x9e0
+#define XK_checkerboard                                0x9e1
+#define XK_ht                                          0x9e2
+#define XK_ff                                          0x9e3
+#define XK_cr                                          0x9e4
+#define XK_lf                                          0x9e5
+#define XK_nl                                          0x9e8
+#define XK_vt                                          0x9e9
+#define XK_lowrightcorner                              0x9ea
+#define XK_uprightcorner                               0x9eb
+#define XK_upleftcorner                                0x9ec
+#define XK_lowleftcorner                               0x9ed
+#define XK_crossinglines                               0x9ee
+#define XK_horizlinescan1                              0x9ef
+#define XK_horizlinescan3                              0x9f0
+#define XK_horizlinescan5                              0x9f1
+#define XK_horizlinescan7                              0x9f2
+#define XK_horizlinescan9                              0x9f3
+#define XK_leftt                                       0x9f4
+#define XK_rightt                                      0x9f5
+#define XK_bott                                        0x9f6
+#define XK_topt                                        0x9f7
+#define XK_vertbar                                     0x9f8
+#endif /* XK_SPECIAL */
+
+/*
+ *  Publishing
+ *  Byte 3 = a
+ */
+
+#ifdef XK_PUBLISHING
+#define XK_emspace                                     0xaa1
+#define XK_enspace                                     0xaa2
+#define XK_em3space                                    0xaa3
+#define XK_em4space                                    0xaa4
+#define XK_digitspace                                  0xaa5
+#define XK_punctspace                                  0xaa6
+#define XK_thinspace                                   0xaa7
+#define XK_hairspace                                   0xaa8
+#define XK_emdash                                      0xaa9
+#define XK_endash                                      0xaaa
+#define XK_signifblank                                 0xaac
+#define XK_ellipsis                                    0xaae
+#define XK_doubbaselinedot                             0xaaf
+#define XK_onethird                                    0xab0
+#define XK_twothirds                                   0xab1
+#define XK_onefifth                                    0xab2
+#define XK_twofifths                                   0xab3
+#define XK_threefifths                                 0xab4
+#define XK_fourfifths                                  0xab5
+#define XK_onesixth                                    0xab6
+#define XK_fivesixths                                  0xab7
+#define XK_careof                                      0xab8
+#define XK_figdash                                     0xabb
+#define XK_leftanglebracket                            0xabc
+#define XK_decimalpoint                                0xabd
+#define XK_rightanglebracket                           0xabe
+#define XK_marker                                      0xabf
+#define XK_oneeighth                                   0xac3
+#define XK_threeeighths                                0xac4
+#define XK_fiveeighths                                 0xac5
+#define XK_seveneighths                                0xac6
+#define XK_trademark                                   0xac9
+#define XK_signaturemark                               0xaca
+#define XK_trademarkincircle                           0xacb
+#define XK_leftopentriangle                            0xacc
+#define XK_rightopentriangle                           0xacd
+#define XK_emopencircle                                0xace
+#define XK_emopenrectangle                             0xacf
+#define XK_leftsinglequotemark                         0xad0
+#define XK_rightsinglequotemark                        0xad1
+#define XK_leftdoublequotemark                         0xad2
+#define XK_rightdoublequotemark                        0xad3
+#define XK_prescription                                0xad4
+#define XK_minutes                                     0xad6
+#define XK_seconds                                     0xad7
+#define XK_latincross                                  0xad9
+#define XK_hexagram                                    0xada
+#define XK_filledrectbullet                            0xadb
+#define XK_filledlefttribullet                         0xadc
+#define XK_filledrighttribullet                        0xadd
+#define XK_emfilledcircle                              0xade
+#define XK_emfilledrect                                0xadf
+#define XK_enopencircbullet                            0xae0
+#define XK_enopensquarebullet                          0xae1
+#define XK_openrectbullet                              0xae2
+#define XK_opentribulletup                             0xae3
+#define XK_opentribulletdown                           0xae4
+#define XK_openstar                                    0xae5
+#define XK_enfilledcircbullet                          0xae6
+#define XK_enfilledsqbullet                            0xae7
+#define XK_filledtribulletup                           0xae8
+#define XK_filledtribulletdown                         0xae9
+#define XK_leftpointer                                 0xaea
+#define XK_rightpointer                                0xaeb
+#define XK_club                                        0xaec
+#define XK_diamond                                     0xaed
+#define XK_heart                                       0xaee
+#define XK_maltesecross                                0xaf0
+#define XK_dagger                                      0xaf1
+#define XK_doubledagger                                0xaf2
+#define XK_checkmark                                   0xaf3
+#define XK_ballotcross                                 0xaf4
+#define XK_musicalsharp                                0xaf5
+#define XK_musicalflat                                 0xaf6
+#define XK_malesymbol                                  0xaf7
+#define XK_femalesymbol                                0xaf8
+#define XK_telephone                                   0xaf9
+#define XK_telephonerecorder                           0xafa
+#define XK_phonographcopyright                         0xafb
+#define XK_caret                                       0xafc
+#define XK_singlelowquotemark                          0xafd
+#define XK_doublelowquotemark                          0xafe
+#define XK_cursor                                      0xaff
+#endif /* XK_PUBLISHING */
+
+/*
+ *  APL
+ *  Byte 3 = b
+ */
+
+#ifdef XK_APL
+#define XK_leftcaret                                   0xba3
+#define XK_rightcaret                                  0xba6
+#define XK_downcaret                                   0xba8
+#define XK_upcaret                                     0xba9
+#define XK_overbar                                     0xbc0
+#define XK_downtack                                    0xbc2
+#define XK_upshoe                                      0xbc3
+#define XK_downstile                                   0xbc4
+#define XK_underbar                                    0xbc6
+#define XK_jot                                         0xbca
+#define XK_quad                                        0xbcc
+#define XK_uptack                                      0xbce
+#define XK_circle                                      0xbcf
+#define XK_upstile                                     0xbd3
+#define XK_downshoe                                    0xbd6
+#define XK_rightshoe                                   0xbd8
+#define XK_leftshoe                                    0xbda
+#define XK_lefttack                                    0xbdc
+#define XK_righttack                                   0xbfc
+#endif /* XK_APL */
+
+/*
+ * Hebrew
+ * Byte 3 = c
+ */
+
+#ifdef XK_HEBREW
+#define XK_hebrew_doublelowline                        0xcdf
+#define XK_hebrew_aleph                                0xce0
+#define XK_hebrew_bet                                  0xce1
+#define XK_hebrew_beth                                 0xce1  /* deprecated */
+#define XK_hebrew_gimel                                0xce2
+#define XK_hebrew_gimmel                               0xce2  /* deprecated */
+#define XK_hebrew_dalet                                0xce3
+#define XK_hebrew_daleth                               0xce3  /* deprecated */
+#define XK_hebrew_he                                   0xce4
+#define XK_hebrew_waw                                  0xce5
+#define XK_hebrew_zain                                 0xce6
+#define XK_hebrew_zayin                                0xce6  /* deprecated */
+#define XK_hebrew_chet                                 0xce7
+#define XK_hebrew_het                                  0xce7  /* deprecated */
+#define XK_hebrew_tet                                  0xce8
+#define XK_hebrew_teth                                 0xce8  /* deprecated */
+#define XK_hebrew_yod                                  0xce9
+#define XK_hebrew_finalkaph                            0xcea
+#define XK_hebrew_kaph                                 0xceb
+#define XK_hebrew_lamed                                0xcec
+#define XK_hebrew_finalmem                             0xced
+#define XK_hebrew_mem                                  0xcee
+#define XK_hebrew_finalnun                             0xcef
+#define XK_hebrew_nun                                  0xcf0
+#define XK_hebrew_samech                               0xcf1
+#define XK_hebrew_samekh                               0xcf1  /* deprecated */
+#define XK_hebrew_ayin                                 0xcf2
+#define XK_hebrew_finalpe                              0xcf3
+#define XK_hebrew_pe                                   0xcf4
+#define XK_hebrew_finalzade                            0xcf5
+#define XK_hebrew_finalzadi                            0xcf5  /* deprecated */
+#define XK_hebrew_zade                                 0xcf6
+#define XK_hebrew_zadi                                 0xcf6  /* deprecated */
+#define XK_hebrew_qoph                                 0xcf7
+#define XK_hebrew_kuf                                  0xcf7  /* deprecated */
+#define XK_hebrew_resh                                 0xcf8
+#define XK_hebrew_shin                                 0xcf9
+#define XK_hebrew_taw                                  0xcfa
+#define XK_hebrew_taf                                  0xcfa  /* deprecated */
+#define XK_Hebrew_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_HEBREW */
+
+/*
+ * Thai
+ * Byte 3 = d
+ */
+
+#ifdef XK_THAI
+#define XK_Thai_kokai					0xda1
+#define XK_Thai_khokhai					0xda2
+#define XK_Thai_khokhuat				0xda3
+#define XK_Thai_khokhwai				0xda4
+#define XK_Thai_khokhon					0xda5
+#define XK_Thai_khorakhang			        0xda6  
+#define XK_Thai_ngongu					0xda7  
+#define XK_Thai_chochan					0xda8  
+#define XK_Thai_choching				0xda9   
+#define XK_Thai_chochang				0xdaa  
+#define XK_Thai_soso					0xdab
+#define XK_Thai_chochoe					0xdac
+#define XK_Thai_yoying					0xdad
+#define XK_Thai_dochada					0xdae
+#define XK_Thai_topatak					0xdaf
+#define XK_Thai_thothan					0xdb0
+#define XK_Thai_thonangmontho			        0xdb1
+#define XK_Thai_thophuthao			        0xdb2
+#define XK_Thai_nonen					0xdb3
+#define XK_Thai_dodek					0xdb4
+#define XK_Thai_totao					0xdb5
+#define XK_Thai_thothung				0xdb6
+#define XK_Thai_thothahan				0xdb7
+#define XK_Thai_thothong	 			0xdb8
+#define XK_Thai_nonu					0xdb9
+#define XK_Thai_bobaimai				0xdba
+#define XK_Thai_popla					0xdbb
+#define XK_Thai_phophung				0xdbc
+#define XK_Thai_fofa					0xdbd
+#define XK_Thai_phophan					0xdbe
+#define XK_Thai_fofan					0xdbf
+#define XK_Thai_phosamphao			        0xdc0
+#define XK_Thai_moma					0xdc1
+#define XK_Thai_yoyak					0xdc2
+#define XK_Thai_rorua					0xdc3
+#define XK_Thai_ru					0xdc4
+#define XK_Thai_loling					0xdc5
+#define XK_Thai_lu					0xdc6
+#define XK_Thai_wowaen					0xdc7
+#define XK_Thai_sosala					0xdc8
+#define XK_Thai_sorusi					0xdc9
+#define XK_Thai_sosua					0xdca
+#define XK_Thai_hohip					0xdcb
+#define XK_Thai_lochula					0xdcc
+#define XK_Thai_oang					0xdcd
+#define XK_Thai_honokhuk				0xdce
+#define XK_Thai_paiyannoi				0xdcf
+#define XK_Thai_saraa					0xdd0
+#define XK_Thai_maihanakat				0xdd1
+#define XK_Thai_saraaa					0xdd2
+#define XK_Thai_saraam					0xdd3
+#define XK_Thai_sarai					0xdd4   
+#define XK_Thai_saraii					0xdd5   
+#define XK_Thai_saraue					0xdd6    
+#define XK_Thai_sarauee					0xdd7    
+#define XK_Thai_sarau					0xdd8    
+#define XK_Thai_sarauu					0xdd9   
+#define XK_Thai_phinthu					0xdda
+#define XK_Thai_maihanakat_maitho   			0xdde
+#define XK_Thai_baht					0xddf
+#define XK_Thai_sarae					0xde0    
+#define XK_Thai_saraae					0xde1
+#define XK_Thai_sarao					0xde2
+#define XK_Thai_saraaimaimuan				0xde3   
+#define XK_Thai_saraaimaimalai				0xde4  
+#define XK_Thai_lakkhangyao				0xde5
+#define XK_Thai_maiyamok				0xde6
+#define XK_Thai_maitaikhu				0xde7
+#define XK_Thai_maiek					0xde8   
+#define XK_Thai_maitho					0xde9
+#define XK_Thai_maitri					0xdea
+#define XK_Thai_maichattawa				0xdeb
+#define XK_Thai_thanthakhat				0xdec
+#define XK_Thai_nikhahit				0xded
+#define XK_Thai_leksun					0xdf0 
+#define XK_Thai_leknung					0xdf1  
+#define XK_Thai_leksong					0xdf2 
+#define XK_Thai_leksam					0xdf3
+#define XK_Thai_leksi					0xdf4  
+#define XK_Thai_lekha					0xdf5  
+#define XK_Thai_lekhok					0xdf6  
+#define XK_Thai_lekchet					0xdf7  
+#define XK_Thai_lekpaet					0xdf8  
+#define XK_Thai_lekkao					0xdf9 
+#endif /* XK_THAI */
+
+/*
+ *   Korean
+ *   Byte 3 = e
+ */
+
+#ifdef XK_KOREAN
+
+#define XK_Hangul		0xff31    /* Hangul start/stop(toggle) */
+#define XK_Hangul_Start		0xff32    /* Hangul start */
+#define XK_Hangul_End		0xff33    /* Hangul end, English start */
+#define XK_Hangul_Hanja		0xff34    /* Start Hangul->Hanja Conversion */
+#define XK_Hangul_Jamo		0xff35    /* Hangul Jamo mode */
+#define XK_Hangul_Romaja	0xff36    /* Hangul Romaja mode */
+#define XK_Hangul_Codeinput	0xff37    /* Hangul code input mode */
+#define XK_Hangul_Jeonja	0xff38    /* Jeonja mode */
+#define XK_Hangul_Banja		0xff39    /* Banja mode */
+#define XK_Hangul_PreHanja	0xff3a    /* Pre Hanja conversion */
+#define XK_Hangul_PostHanja	0xff3b    /* Post Hanja conversion */
+#define XK_Hangul_SingleCandidate	0xff3c    /* Single candidate */
+#define XK_Hangul_MultipleCandidate	0xff3d    /* Multiple candidate */
+#define XK_Hangul_PreviousCandidate	0xff3e    /* Previous candidate */
+#define XK_Hangul_Special	0xff3f    /* Special symbols */
+#define XK_Hangul_switch	0xFF7E    /* Alias for mode_switch */
+
+/* Hangul Consonant Characters */
+#define XK_Hangul_Kiyeog				0xea1
+#define XK_Hangul_SsangKiyeog				0xea2
+#define XK_Hangul_KiyeogSios				0xea3
+#define XK_Hangul_Nieun					0xea4
+#define XK_Hangul_NieunJieuj				0xea5
+#define XK_Hangul_NieunHieuh				0xea6
+#define XK_Hangul_Dikeud				0xea7
+#define XK_Hangul_SsangDikeud				0xea8
+#define XK_Hangul_Rieul					0xea9
+#define XK_Hangul_RieulKiyeog				0xeaa
+#define XK_Hangul_RieulMieum				0xeab
+#define XK_Hangul_RieulPieub				0xeac
+#define XK_Hangul_RieulSios				0xead
+#define XK_Hangul_RieulTieut				0xeae
+#define XK_Hangul_RieulPhieuf				0xeaf
+#define XK_Hangul_RieulHieuh				0xeb0
+#define XK_Hangul_Mieum					0xeb1
+#define XK_Hangul_Pieub					0xeb2
+#define XK_Hangul_SsangPieub				0xeb3
+#define XK_Hangul_PieubSios				0xeb4
+#define XK_Hangul_Sios					0xeb5
+#define XK_Hangul_SsangSios				0xeb6
+#define XK_Hangul_Ieung					0xeb7
+#define XK_Hangul_Jieuj					0xeb8
+#define XK_Hangul_SsangJieuj				0xeb9
+#define XK_Hangul_Cieuc					0xeba
+#define XK_Hangul_Khieuq				0xebb
+#define XK_Hangul_Tieut					0xebc
+#define XK_Hangul_Phieuf				0xebd
+#define XK_Hangul_Hieuh					0xebe
+
+/* Hangul Vowel Characters */
+#define XK_Hangul_A					0xebf
+#define XK_Hangul_AE					0xec0
+#define XK_Hangul_YA					0xec1
+#define XK_Hangul_YAE					0xec2
+#define XK_Hangul_EO					0xec3
+#define XK_Hangul_E					0xec4
+#define XK_Hangul_YEO					0xec5
+#define XK_Hangul_YE					0xec6
+#define XK_Hangul_O					0xec7
+#define XK_Hangul_WA					0xec8
+#define XK_Hangul_WAE					0xec9
+#define XK_Hangul_OE					0xeca
+#define XK_Hangul_YO					0xecb
+#define XK_Hangul_U					0xecc
+#define XK_Hangul_WEO					0xecd
+#define XK_Hangul_WE					0xece
+#define XK_Hangul_WI					0xecf
+#define XK_Hangul_YU					0xed0
+#define XK_Hangul_EU					0xed1
+#define XK_Hangul_YI					0xed2
+#define XK_Hangul_I					0xed3
+
+/* Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_Kiyeog				0xed4
+#define XK_Hangul_J_SsangKiyeog				0xed5
+#define XK_Hangul_J_KiyeogSios				0xed6
+#define XK_Hangul_J_Nieun				0xed7
+#define XK_Hangul_J_NieunJieuj				0xed8
+#define XK_Hangul_J_NieunHieuh				0xed9
+#define XK_Hangul_J_Dikeud				0xeda
+#define XK_Hangul_J_Rieul				0xedb
+#define XK_Hangul_J_RieulKiyeog				0xedc
+#define XK_Hangul_J_RieulMieum				0xedd
+#define XK_Hangul_J_RieulPieub				0xede
+#define XK_Hangul_J_RieulSios				0xedf
+#define XK_Hangul_J_RieulTieut				0xee0
+#define XK_Hangul_J_RieulPhieuf				0xee1
+#define XK_Hangul_J_RieulHieuh				0xee2
+#define XK_Hangul_J_Mieum				0xee3
+#define XK_Hangul_J_Pieub				0xee4
+#define XK_Hangul_J_PieubSios				0xee5
+#define XK_Hangul_J_Sios				0xee6
+#define XK_Hangul_J_SsangSios				0xee7
+#define XK_Hangul_J_Ieung				0xee8
+#define XK_Hangul_J_Jieuj				0xee9
+#define XK_Hangul_J_Cieuc				0xeea
+#define XK_Hangul_J_Khieuq				0xeeb
+#define XK_Hangul_J_Tieut				0xeec
+#define XK_Hangul_J_Phieuf				0xeed
+#define XK_Hangul_J_Hieuh				0xeee
+
+/* Ancient Hangul Consonant Characters */
+#define XK_Hangul_RieulYeorinHieuh			0xeef
+#define XK_Hangul_SunkyeongeumMieum			0xef0
+#define XK_Hangul_SunkyeongeumPieub			0xef1
+#define XK_Hangul_PanSios				0xef2
+#define XK_Hangul_KkogjiDalrinIeung			0xef3
+#define XK_Hangul_SunkyeongeumPhieuf			0xef4
+#define XK_Hangul_YeorinHieuh				0xef5
+
+/* Ancient Hangul Vowel Characters */
+#define XK_Hangul_AraeA					0xef6
+#define XK_Hangul_AraeAE				0xef7
+
+/* Ancient Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_PanSios				0xef8
+#define XK_Hangul_J_KkogjiDalrinIeung			0xef9
+#define XK_Hangul_J_YeorinHieuh				0xefa
+
+/* Korean currency symbol */
+#define XK_Korean_Won					0xeff
+
+#endif /* XK_KOREAN */
+
+#ifdef XK_CURRENCY
+#define XK_EcuSign					0x20a0
+#define XK_ColonSign					0x20a1
+#define XK_CruzeiroSign					0x20a2
+#define XK_FFrancSign					0x20a3
+#define XK_LiraSign					0x20a4
+#define XK_MillSign					0x20a5
+#define XK_NairaSign					0x20a6
+#define XK_PesetaSign					0x20a7
+#define XK_RupeeSign					0x20a8
+#define XK_WonSign					0x20a9
+#define XK_NewSheqelSign				0x20aa
+#define XK_DongSign					0x20ab
+#define XK_EuroSign					0x20ac
+#endif
diff --git a/rfb/msgTypes.h b/rfb/msgTypes.h
new file mode 100644
index 0000000..9bcf4d5
--- /dev/null
+++ b/rfb/msgTypes.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_MSGTYPES_H__
+#define __RFB_MSGTYPES_H__
+
+namespace rfb {
+  // server to client
+
+  const int msgTypeFramebufferUpdate = 0;
+  const int msgTypeSetColourMapEntries = 1;
+  const int msgTypeBell = 2;
+  const int msgTypeServerCutText = 3;
+
+  // client to server
+
+  const int msgTypeSetPixelFormat = 0;
+  const int msgTypeFixColourMapEntries = 1;
+  const int msgTypeSetEncodings = 2;
+  const int msgTypeFramebufferUpdateRequest = 3;
+  const int msgTypeKeyEvent = 4;
+  const int msgTypePointerEvent = 5;
+  const int msgTypeClientCutText = 6;
+}
+#endif
diff --git a/rfb/msvcwarning.h b/rfb/msvcwarning.h
new file mode 100644
index 0000000..4127ce9
--- /dev/null
+++ b/rfb/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/rfb/rfb.dsp b/rfb/rfb.dsp
new file mode 100644
index 0000000..2521bfd
--- /dev/null
+++ b/rfb/rfb.dsp
@@ -0,0 +1,625 @@
+# Microsoft Developer Studio Project File - Name="rfb" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb.mak" CFG="rfb - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "rfb - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "rfb - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "rfb - Win32 Release"
+# Name "rfb - Win32 Debug"
+# Name "rfb - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Blacklist.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Blacklist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourCube.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileConstants.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hostname.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keysymdef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msgTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pixel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rect.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transInitTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrueColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\util_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rfb/rreDecode.h b/rfb/rreDecode.h
new file mode 100644
index 0000000..9f69cee
--- /dev/null
+++ b/rfb/rreDecode.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// RRE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+
+#include <rdr/InStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define RRE_DECODE CONCAT2E(rreDecode,BPP)
+
+void RRE_DECODE (const Rect& r, rdr::InStream* is
+#ifdef EXTRA_ARGS
+                 , EXTRA_ARGS
+#endif
+                 )
+{
+  int nSubrects = is->readU32();
+  PIXEL_T bg = is->READ_PIXEL();
+  FILL_RECT(r, bg);
+
+  for (int i = 0; i < nSubrects; i++) {
+    PIXEL_T pix = is->READ_PIXEL();
+    int x = is->readU16();
+    int y = is->readU16();
+    int w = is->readU16();
+    int h = is->readU16();
+    FILL_RECT(Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix);
+  }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef RRE_DECODE
+}
diff --git a/rfb/rreEncode.h b/rfb/rreEncode.h
new file mode 100644
index 0000000..4877a12
--- /dev/null
+++ b/rfb/rreEncode.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// RRE encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+//
+// The data argument to RRE_ENCODE contains the pixel data, and it writes the
+// encoded version to the given OutStream.  If the encoded version exceeds w*h
+// it aborts and returns -1, otherwise it returns the number of subrectangles.
+//
+
+#include <rdr/OutStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define RRE_ENCODE CONCAT2E(rreEncode,BPP)
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg);
+
+int RRE_ENCODE (void* data, int w, int h, rdr::OutStream* os)
+{
+  // Find the background colour - count occurrences of up to 4 different pixel
+  // values, and choose the one which occurs most often.
+
+  const int nCols = 4;
+  PIXEL_T pix[nCols];
+  int count[nCols] = { 0, };
+  PIXEL_T* ptr = (PIXEL_T*)data;
+  PIXEL_T* end = ptr + w*h;
+
+  while (ptr < end) {
+    int i;
+    for (i = 0; i < nCols; i++) {
+      if (count[i] == 0)
+        pix[i] = *ptr;
+
+      if (pix[i] == *ptr) {
+        count[i]++;
+        break;
+      }
+    }
+
+    if (i == nCols) break;
+    ptr++;
+  }
+  
+  int bg = 0;
+  for (int i = 1; i < nCols; i++)
+    if (count[i] > count[bg]) bg = i;
+
+  // Now call the function to do the encoding.
+
+  return RRE_ENCODE ((PIXEL_T*)data, w, h, os, pix[bg]);
+}
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg)
+{
+  int oldLen = os->length();
+  os->WRITE_PIXEL(bg);
+
+  int nSubrects = 0;
+
+  for (int y = 0; y < h; y++)
+  {
+    int x = 0;
+    while (x < w) {
+      if (*data == bg) {
+        x++;
+        data++;
+        continue;
+      }
+
+      // Find horizontal subrect first
+      PIXEL_T* ptr = data+1;
+      PIXEL_T* eol = data+w-x;
+      while (ptr < eol && *ptr == *data) ptr++;
+      int sw = ptr - data;
+
+      ptr = data + w;
+      int sh = 1;
+      while (sh < h-y) {
+        eol = ptr + sw;
+        while (ptr < eol)
+          if (*ptr++ != *data) goto endOfHorizSubrect;
+        ptr += w - sw;
+        sh++;
+      }
+    endOfHorizSubrect:
+
+      // Find vertical subrect
+      int vh;
+      for (vh = sh; vh < h-y; vh++)
+        if (data[vh*w] != *data) break;
+
+      if (vh != sh) {
+        ptr = data+1;
+        int vw;
+        for (vw = 1; vw < sw; vw++) {
+          for (int i = 0; i < vh; i++)
+            if (ptr[i*w] != *data) goto endOfVertSubrect;
+          ptr++;
+        }
+      endOfVertSubrect:
+
+        // If vertical subrect bigger than horizontal then use that.
+        if (sw*sh < vw*vh) {
+          sw = vw;
+          sh = vh;
+        }
+      }
+
+      nSubrects++;
+      os->WRITE_PIXEL(*data);
+      os->writeU16(x);
+      os->writeU16(y);
+      os->writeU16(sw);
+      os->writeU16(sh);
+      if (os->length() > oldLen + w*h) return -1;
+
+      ptr = data+w;
+      PIXEL_T* eor = data+w*sh;
+      while (ptr < eor) {
+        eol = ptr + sw;
+        while (ptr < eol) *ptr++ = bg;
+        ptr += w - sw;
+      }
+      x += sw;
+      data += sw;
+    }
+  }
+
+  return nSubrects;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef RRE_ENCODE
+}
diff --git a/rfb/secTypes.cxx b/rfb/secTypes.cxx
new file mode 100644
index 0000000..7c6c25c
--- /dev/null
+++ b/rfb/secTypes.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/secTypes.h>
+#include <rfb/util.h>
+
+int rfb::secTypeNum(const char* name)
+{
+  if (strcasecmp(name, "None") == 0)       return secTypeNone;
+  if (strcasecmp(name, "VncAuth") == 0)    return secTypeVncAuth;
+  if (strcasecmp(name, "RA2") == 0)        return secTypeRA2;
+  if (strcasecmp(name, "RA2ne") == 0)      return secTypeRA2ne;
+  return secTypeInvalid;
+}
+
+const char* rfb::secTypeName(int num)
+{
+  switch (num) {
+  case secTypeNone:       return "None";
+  case secTypeVncAuth:    return "VncAuth";
+  case secTypeRA2:        return "RA2";
+  case secTypeRA2ne:      return "RA2ne";
+  default:                return "[unknown secType]";
+  }
+}
+
+bool rfb::secTypeEncrypts(int num)
+{
+  switch (num) {
+  case secTypeRA2:        return true;
+  default:                return false;
+  }
+}
+
+std::list<int> rfb::parseSecTypes(const char* types_)
+{
+  std::list<int> result;
+  CharArray types(strDup(types_)), type;
+  while (types.buf) {
+    strSplit(types.buf, ',', &type.buf, &types.buf);
+    int typeNum = secTypeNum(type.buf);
+    if (typeNum != secTypeInvalid)
+      result.push_back(typeNum);
+  }
+  return result;
+}
diff --git a/rfb/secTypes.h b/rfb/secTypes.h
new file mode 100644
index 0000000..f0b326e
--- /dev/null
+++ b/rfb/secTypes.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// secTypes.h - constants for the various security types.
+//
+
+#ifndef __RFB_SECTYPES_H__
+#define __RFB_SECTYPES_H__
+
+#include <list>
+
+namespace rfb {
+  const int secTypeInvalid = 0;
+  const int secTypeNone    = 1;
+  const int secTypeVncAuth = 2;
+
+  const int secTypeRA2     = 5;
+  const int secTypeRA2ne   = 6;
+
+  const int secTypeTight   = 16;
+  const int secTypeUltra   = 17;
+  const int secTypeTLS     = 18;
+
+  // result types
+
+  const int secResultOK = 0;
+  const int secResultFailed = 1;
+  const int secResultTooMany = 2; // deprecated
+
+  const char* secTypeName(int num);
+  int secTypeNum(const char* name);
+  bool secTypeEncrypts(int num);
+  std::list<int> parseSecTypes(const char* types);
+}
+
+#endif
diff --git a/rfb/transInitTempl.h b/rfb/transInitTempl.h
new file mode 100644
index 0000000..2658f9f
--- /dev/null
+++ b/rfb/transInitTempl.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// transInitTempl.h - templates for functions to initialise lookup tables for
+// the translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPOUT)
+#error "transInitTempl.h: BPPOUT not defined"
+#endif
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef SWAP16
+#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
+#endif
+
+#ifndef SWAP32
+#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
+                   (((n) & 0x0000ff00) << 8) | ((n) << 24))
+#endif
+
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define SWAPOUT CONCAT2E(SWAP,BPPOUT)
+#define initSimpleCMtoTCOUT    CONCAT2E(initSimpleCMtoTC,BPPOUT)
+#define initSimpleTCtoTCOUT    CONCAT2E(initSimpleTCtoTC,BPPOUT)
+#define initSimpleCMtoCubeOUT  CONCAT2E(initSimpleCMtoCube,BPPOUT)
+#define initSimpleTCtoCubeOUT  CONCAT2E(initSimpleTCtoCube,BPPOUT)
+#define initRGBTCtoTCOUT       CONCAT2E(initRGBTCtoTC,BPPOUT)
+#define initRGBTCtoCubeOUT     CONCAT2E(initRGBTCtoCube,BPPOUT)
+#define initOneRGBTableOUT     CONCAT2E(initOneRGBTable,BPPOUT)
+#define initOneRGBCubeTableOUT CONCAT2E(initOneRGBCubeTable,BPPOUT)
+
+#ifndef TRANS_INIT_TEMPL_ENDIAN_TEST
+#define TRANS_INIT_TEMPL_ENDIAN_TEST
+  static rdr::U32 endianTest = 1;
+  static bool nativeBigEndian = *(rdr::U8*)(&endianTest) != 1;
+#endif
+
+void initSimpleCMtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                          ColourMap* cm, const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r,g,b;
+    cm->lookup(i,&r,&g,&b);
+
+    table[i] = ((((r * outPF.redMax   + 32767) / 65535) << outPF.redShift) |
+                (((g * outPF.greenMax + 32767) / 65535) << outPF.greenShift) |
+                (((b * outPF.blueMax  + 32767) / 65535) << outPF.blueShift));
+#if (BPPOUT != 8)
+    if (outPF.bigEndian != nativeBigEndian)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initSimpleTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                          const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r = (i >> inPF.redShift)   & inPF.redMax;
+    int g = (i >> inPF.greenShift) & inPF.greenMax;
+    int b = (i >> inPF.blueShift)  & inPF.blueMax;
+      
+    r = (r * outPF.redMax   + inPF.redMax/2)   / inPF.redMax;
+    g = (g * outPF.greenMax + inPF.greenMax/2) / inPF.greenMax;
+    b = (b * outPF.blueMax  + inPF.blueMax/2)  / inPF.blueMax;
+      
+    table[i] = ((r << outPF.redShift)   |
+                (g << outPF.greenShift) |
+                (b << outPF.blueShift));
+#if (BPPOUT != 8)
+    if (outPF.bigEndian != nativeBigEndian)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initSimpleCMtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                            ColourMap* cm, ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r,g,b;
+    cm->lookup(i,&r,&g,&b);
+    r = (r * (cube->nRed-1)   + 32767) / 65535;
+    g = (g * (cube->nGreen-1) + 32767) / 65535;
+    b = (b * (cube->nBlue-1)  + 32767) / 65535;
+    table[i] = cube->lookup(r, g, b);
+  }
+}
+
+void initSimpleTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                            ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r = (i >> inPF.redShift)   & inPF.redMax;
+    int g = (i >> inPF.greenShift) & inPF.greenMax;
+    int b = (i >> inPF.blueShift)  & inPF.blueMax;
+
+    r = (r * (cube->nRed-1)   + inPF.redMax/2)   / inPF.redMax;
+    g = (g * (cube->nGreen-1) + inPF.greenMax/2) / inPF.greenMax;
+    b = (b * (cube->nBlue-1)  + inPF.blueMax/2)  / inPF.blueMax;
+
+    table[i] = cube->lookup(r, g, b);
+  }
+}
+
+void initOneRGBTableOUT (OUTPIXEL* table, int inMax, int outMax,
+                         int outShift, bool swap)
+{
+  int size = inMax + 1;
+
+  for (int i = 0; i < size; i++) {
+    table[i] = ((i * outMax + inMax / 2) / inMax) << outShift;
+#if (BPPOUT != 8)
+    if (swap)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initRGBTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                       const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+  OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+
+  bool swap = (outPF.bigEndian != nativeBigEndian);
+
+  initOneRGBTableOUT (redTable, inPF.redMax, outPF.redMax, 
+                           outPF.redShift, swap);
+  initOneRGBTableOUT (greenTable, inPF.greenMax, outPF.greenMax,
+                           outPF.greenShift, swap);
+  initOneRGBTableOUT (blueTable, inPF.blueMax, outPF.blueMax,
+                           outPF.blueShift, swap);
+}
+
+
+void initOneRGBCubeTableOUT (OUTPIXEL* table, int inMax, int outMax,
+                             int outMult)
+{
+  int size = inMax + 1;
+
+  for (int i = 0; i < size; i++) {
+    table[i] = ((i * outMax + inMax / 2) / inMax) * outMult;
+  }
+}
+
+void initRGBTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                         ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3 + cube->size();
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+  OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+
+  initOneRGBCubeTableOUT (redTable,   inPF.redMax,   cube->nRed-1,
+                               cube->redMult());
+  initOneRGBCubeTableOUT (greenTable, inPF.greenMax, cube->nGreen-1,
+                               cube->greenMult());
+  initOneRGBCubeTableOUT (blueTable,  inPF.blueMax,  cube->nBlue-1,
+                               cube->blueMult());
+  for (int i = 0; i < cube->size(); i++) {
+    cubeTable[i] = cube->table[i];
+  }
+}
+
+#undef OUTPIXEL
+#undef initSimpleCMtoTCOUT
+#undef initSimpleTCtoTCOUT
+#undef initSimpleCMtoCubeOUT
+#undef initSimpleTCtoCubeOUT
+#undef initRGBTCtoTCOUT
+#undef initRGBTCtoCubeOUT
+#undef initOneRGBTableOUT
+#undef initOneRGBCubeTableOUT
+}
diff --git a/rfb/transTempl.h b/rfb/transTempl.h
new file mode 100644
index 0000000..907b839
--- /dev/null
+++ b/rfb/transTempl.h
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// transTempl.h - templates for translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPIN  - 8, 16 or 32
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPIN) || !defined(BPPOUT)
+#error "transTempl.h: BPPIN or BPPOUT not defined"
+#endif
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef CONCAT4E
+#define CONCAT4(a,b,c,d) a##b##c##d
+#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d)
+#endif
+
+#define INPIXEL rdr::CONCAT2E(U,BPPIN)
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define transSimpleINtoOUT CONCAT4E(transSimple,BPPIN,to,BPPOUT)
+#define transRGBINtoOUT CONCAT4E(transRGB,BPPIN,to,BPPOUT)
+#define transRGBCubeINtoOUT CONCAT4E(transRGBCube,BPPIN,to,BPPOUT)
+
+#if (BPPIN <= 16)
+
+// transSimpleINtoOUT uses a single table.  This can be used for any incoming
+// and outgoing pixel formats, as long as the incoming pixel format is not too
+// large (for 16bpp, the table needs 64K entries).
+
+void transSimpleINtoOUT (void* table_,
+                         const PixelFormat& inPF, void* inPtr, int inStride,
+                         const PixelFormat& outPF, void* outPtr, int outStride,
+                         int width, int height)
+{
+  OUTPIXEL* table = (OUTPIXEL*)table_;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow)
+      *op++ = table[*ip++];
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+#endif
+
+#if (BPPIN >= 16)
+
+// transRGBINtoOUT uses three tables, one each for red, green and blue
+// components and adds the values to produce the result.  This can be used
+// where a single table would be too large (e.g. 32bpp).  It only works for a
+// trueColour incoming pixel format.  Usually the outgoing pixel format is
+// trueColour, but we add rather than ORing the three values so that it is also
+// possible to generate an index into a colour cube.  I believe that in most
+// cases adding is just as fast as ORing - if not then we should split this
+// into two different functions for efficiency.
+
+void transRGBINtoOUT (void* table,
+                      const PixelFormat& inPF, void* inPtr, int inStride,
+                      const PixelFormat& outPF, void* outPtr, int outStride,
+                      int width, int height)
+{
+  OUTPIXEL* redTable = (OUTPIXEL*)table;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow) {
+      *op++ = (redTable  [(*ip >> inPF.redShift)   & inPF.redMax] +
+               greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+               blueTable [(*ip >> inPF.blueShift)  & inPF.blueMax]);
+      ip++;
+    }
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+// transRGBCubeINtoOUT is similar to transRGBINtoOUT but also looks up the
+// colour cube index in a fourth table to yield a pixel value.
+
+void transRGBCubeINtoOUT (void* table,
+                          const PixelFormat& inPF, void* inPtr, int inStride,
+                          const PixelFormat& outPF, void* outPtr,
+                          int outStride, int width, int height)
+{
+  OUTPIXEL* redTable = (OUTPIXEL*)table;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow) {
+      *op++ = cubeTable[(redTable  [(*ip >> inPF.redShift)   & inPF.redMax] +
+                         greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+                         blueTable [(*ip >> inPF.blueShift)  & inPF.blueMax])];
+      ip++;
+    }
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+#endif
+
+#undef INPIXEL
+#undef OUTPIXEL
+#undef transSimpleINtoOUT
+#undef transRGBINtoOUT
+#undef transRGBCubeINtoOUT
diff --git a/rfb/util.cxx b/rfb/util.cxx
new file mode 100644
index 0000000..2dbc2df
--- /dev/null
+++ b/rfb/util.cxx
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/util.h>
+
+namespace rfb {
+
+  char* strDup(const char* s) {
+    if (!s) return 0;
+    int l = strlen(s);
+    char* r = new char[l+1];
+    memcpy(r, s, l+1);
+    return r;
+  };
+
+  void strFree(char* s) {
+    delete [] s;
+  }
+
+
+  bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
+    CharArray out1old, out2old;
+    if (out1) out1old.buf = *out1;
+    if (out2) out2old.buf = *out2;
+    int len = strlen(src);
+    int i=0, increment=1, limit=len;
+    if (fromEnd) {
+      i=len-1; increment = -1; limit = -1;
+    }
+    while (i!=limit) {
+      if (src[i] == limiter) {
+        if (out1) {
+          *out1 = new char[i+1];
+          if (i) memcpy(*out1, src, i);
+          (*out1)[i] = 0;
+        }
+        if (out2) {
+          *out2 = new char[len-i];
+          if (len-i-1) memcpy(*out2, &src[i+1], len-i-1);
+          (*out2)[len-i-1] = 0;
+        }
+        return true;
+      }
+      i+=increment;
+    }
+    if (out1) *out1 = strDup(src);
+    if (out2) *out2 = 0;
+    return false;
+  }
+
+  bool strContains(const char* src, char c) {
+    int l=strlen(src);
+    for (int i=0; i<l; i++)
+      if (src[i] == c) return true;
+    return false;
+  }
+
+  void strCopy(char* dest, const char* src, int destlen) {
+    if (src)
+      strncpy(dest, src, destlen-1);
+    dest[src ? destlen-1 : 0] = 0;
+  }
+
+};
diff --git a/rfb/util.h b/rfb/util.h
new file mode 100644
index 0000000..d792c8d
--- /dev/null
+++ b/rfb/util.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// util.h - miscellaneous useful bits
+//
+
+#ifndef __RFB_UTIL_H__
+#define __RFB_UTIL_H__
+
+#include <string.h>
+
+namespace rfb {
+
+  // -=- Class to handle cleanup of arrays of characters
+  class CharArray {
+  public:
+    CharArray() : buf(0) {}
+    CharArray(char* str) : buf(str) {} // note: assumes ownership
+    CharArray(int len) {
+      buf = new char[len];
+    }
+    ~CharArray() {
+      delete [] buf;
+    }
+    // Get the buffer pointer & clear it (i.e. caller takes ownership)
+    char* takeBuf() {char* tmp = buf; buf = 0; return tmp;}
+    void replaceBuf(char* b) {delete [] buf; buf = b;}
+    char* buf;
+  private:
+    CharArray(const CharArray&);
+    CharArray& operator=(const CharArray&);
+  };
+
+  char* strDup(const char* s);
+  void strFree(char* s);
+
+  // Returns true if split successful.  Returns false otherwise.
+  // ALWAYS *copies* first part of string to out1 buffer.
+  // If limiter not found, leaves out2 alone (null) and just copies to out1.
+  // If out1 or out2 non-zero, calls strFree and zeroes them.
+  // If fromEnd is true, splits at end of string rather than beginning.
+  // Either out1 or out2 may be null, in which case the split will not return
+  // that part of the string.  Obviously, setting both to 0 is not useful...
+  bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd=false);
+
+  // Returns true if src contains c
+  bool strContains(const char* src, char c);
+
+  // Copies src to dest, up to specified length-1, and guarantees termination
+  void strCopy(char* dest, const char* src, int destlen);
+}
+#endif
+
+// Some platforms (e.g. Windows) include max() and min() macros in their
+// standard headers, so we define them only when not already defined.  Note
+// also that max() & min() are standard C++ template functions, so some C++
+// headers will undefine them.  We place our definitions outside the #ifndef
+// __RFB_UTIL_H__, so that you can always guarantee they will be defined if
+// this file is the last #include before you use them.
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+
+// -=- PLATFORM SPECIFIC UTILITY FUNCTIONS/IMPLEMENTATIONS
+#ifdef WIN32
+#include "win32/util_win32.h"
+#endif
+
diff --git a/rfb/vncAuth.cxx b/rfb/vncAuth.cxx
new file mode 100644
index 0000000..6bd6a62
--- /dev/null
+++ b/rfb/vncAuth.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// vncAuth
+//
+// XXX not thread-safe, because d3des isn't - do we need to worry about this?
+//
+
+#include <string.h>
+extern "C" {
+#include <rfb/d3des.h>
+}
+#include <rfb/vncAuth.h>
+
+using namespace rfb;
+
+void rfb::vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd)
+{
+  unsigned char key[8] = { 0, };
+  int len = strlen(passwd);
+  if (len > 8) len = 8;
+  for (int i = 0; i < len; i++)
+    key[i] = passwd[i];
+
+  deskey(key, EN0);
+
+  for (int j = 0; j < vncAuthChallengeSize; j += 8)
+    des(challenge+j, challenge+j);
+}
+
+static unsigned char obfuscationKey[] = {23,82,107,6,35,78,88,7};
+
+void rfb::vncAuthObfuscatePasswd(char* passwd)
+{
+  for (int i = strlen(passwd); i < 8; i++)
+    passwd[i] = 0;
+  deskey(obfuscationKey, EN0);
+  des((unsigned char*)passwd, (unsigned char*)passwd);
+}
+
+void rfb::vncAuthUnobfuscatePasswd(char* passwd)
+{
+  deskey(obfuscationKey, DE1);
+  des((unsigned char*)passwd, (unsigned char*)passwd);
+  passwd[8] = 0;
+}
diff --git a/rfb/vncAuth.h b/rfb/vncAuth.h
new file mode 100644
index 0000000..18d87ad
--- /dev/null
+++ b/rfb/vncAuth.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __RFB_VNCAUTH_H__
+#define __RFB_VNCAUTH_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+
+  const int vncAuthChallengeSize = 16;
+
+  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
+  void vncAuthObfuscatePasswd(char* passwd);
+  void vncAuthUnobfuscatePasswd(char* passwd);
+}
+#endif
diff --git a/rfb/win32/Threading_win32.cxx b/rfb/win32/Threading_win32.cxx
new file mode 100644
index 0000000..28cfdb7
--- /dev/null
+++ b/rfb/win32/Threading_win32.cxx
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Threading_win32.cxx
+// Win32 Threading interface implementation
+
+#include <malloc.h>
+
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/win32/Threading_win32.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Threading");
+
+static DWORD threadStorage = TlsAlloc();
+
+
+inline logAction(Thread* t, const char* action) {
+  vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
+}
+
+inline logError(Thread* t, const char* err) {
+  vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err);
+}
+
+
+DWORD WINAPI
+Thread::threadProc(LPVOID lpParameter) {
+  Thread* thread = (Thread*) lpParameter;
+  TlsSetValue(threadStorage, thread);
+  logAction(thread, "started");
+  try {
+    thread->run();
+    logAction(thread, "stopped");
+  } catch (rdr::Exception& e) {
+    logError(thread, e.str());
+  }
+  bool deleteThread = false;
+  {
+    Lock l(thread->mutex);
+    thread->state = ThreadStopped;
+    thread->sig->signal();
+    deleteThread = thread->deleteAfterRun;
+  }
+  if (deleteThread)
+    delete thread;
+  return 0;
+}
+
+Thread::Thread(const char* name_) : sig(0), deleteAfterRun(false) {
+  sig = new Condition(mutex);
+  cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (!name_)
+    name_ = "Unnamed";
+  name = strDup(name_);
+  thread = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
+  state = ThreadCreated;
+  logAction(this, "created");
+}
+
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : sig(0), deleteAfterRun(false), thread(thread_), thread_id(thread_id_) {
+  sig = new Condition(mutex);
+  cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  name = strDup("Native");
+  state = ThreadNative;
+  logAction(this, "created");
+}
+
+Thread::~Thread() {
+  logAction(this, "destroying");
+  if (!deleteAfterRun) this->join();
+  if (sig)
+    delete sig;
+  if (cond_event)
+    CloseHandle(cond_event);
+  logAction(this, "destroyed");
+  strFree(name);
+}
+
+void
+Thread::run() {
+}
+
+void
+Thread::start() {
+  Lock l(mutex);
+  if (state == ThreadCreated) {
+    state = ThreadStarted;
+    sig->signal();
+    ResumeThread(thread);
+  }
+}
+
+Thread*
+Thread::join() {
+  if (deleteAfterRun)
+    throw rdr::Exception("attempt to join() with deleteAfterRun thread");
+  Lock l(mutex);
+  if (!thread) {
+    logAction(this, "already joined");
+  } else {
+    logAction(this, "joining");
+    while (state == ThreadStarted) {
+      sig->wait();
+      logAction(this, "checking");
+    }
+    CloseHandle(thread);
+    thread = 0;
+    logAction(this, "joined");
+  }
+  return this;
+}
+
+const char*
+Thread::getName() const {
+  return name;
+}
+
+ThreadState
+Thread::getState() const {
+  return state;
+}
+
+unsigned long
+Thread::getThreadId() const {
+  return thread_id;
+}
+
+Thread*
+Thread::self() {
+  Thread* thread = (Thread*) TlsGetValue(threadStorage);
+  if (!thread) {
+    thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
+    TlsSetValue(threadStorage, thread);
+  }
+  return thread;
+}
\ No newline at end of file
diff --git a/rfb/win32/Threading_win32.h b/rfb/win32/Threading_win32.h
new file mode 100644
index 0000000..e95e0f7
--- /dev/null
+++ b/rfb/win32/Threading_win32.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Threading_win32.h
+// Win32 Threading interface implementation
+
+#ifndef __RFB_THREADING_IMPL_WIN32
+#define __RFB_THREADING_IMPL_WIN32
+
+#define __RFB_THREADING_IMPL WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <stdio.h>
+
+namespace rfb {
+
+  class Mutex {
+  public:
+    Mutex() {
+      InitializeCriticalSection(&crit);
+    }
+    ~Mutex() {
+      DeleteCriticalSection(&crit);
+    }
+    friend class Lock;
+    friend class Condition;
+  protected:
+    void enter() {EnterCriticalSection(&crit);}
+    void exit() {LeaveCriticalSection(&crit);}
+    CRITICAL_SECTION crit;
+  };
+
+  class Lock {
+  public:
+    Lock(Mutex& m) : mutex(m) {m.enter();}
+    ~Lock() {mutex.exit();}
+  protected:
+    Mutex& mutex;
+  };
+
+  enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadNative};
+
+  class Thread {
+  public:
+    Thread(const char* name_=0);
+    virtual ~Thread();
+
+    virtual void run();
+
+    virtual void start();
+    virtual Thread* join();
+
+    const char* getName() const;
+    ThreadState getState() const;
+
+    // Determines whether the thread should delete itself when run() returns
+    // If you set this, you must NEVER call join()!
+    void setDeleteAfterRun() {deleteAfterRun = true;};
+
+    unsigned long getThreadId() const;
+
+    static Thread* self();
+
+    friend class Condition;
+
+  protected:
+    Thread(HANDLE thread_, DWORD thread_id_);
+    static DWORD WINAPI threadProc(LPVOID lpParameter);
+
+    HANDLE thread;
+    DWORD thread_id;
+    char* name;
+    ThreadState state;
+    Condition* sig;
+    Mutex mutex;
+
+    HANDLE cond_event;
+	  Thread* cond_next;
+
+    bool deleteAfterRun;
+  };
+
+  class Condition {
+  public:
+    Condition(Mutex& m) : mutex(m), waiting(0) {
+    }
+    ~Condition() {
+    }
+    void signal() {
+      Lock l(cond_lock);
+      if (waiting) {
+        SetEvent(waiting->cond_event);
+        waiting = waiting->cond_next;
+      }
+    }
+    // - MUST hold "mutex" to call wait()
+    // WIN32: if processMsg is true then wait will continue
+    // to process messages in the thread's queue.
+    // Avoid using this unless you have to!
+    void wait(bool processMsgs=false) {
+      Thread* self = Thread::self();
+      ResetEvent(self->cond_event);
+      { Lock l(cond_lock);
+        self->cond_next = waiting;
+        waiting = self;
+      }
+      mutex.exit();
+      if (processMsgs) {
+        while (1) {
+          DWORD result = MsgWaitForMultipleObjects(1, &self->cond_event, FALSE, INFINITE, QS_ALLINPUT);
+          if (result == WAIT_OBJECT_0)
+            break;
+          MSG msg;
+          while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+            DispatchMessage(&msg);
+          }
+        }
+      } else {
+        WaitForSingleObject(self->cond_event, INFINITE);
+      }
+      mutex.enter();
+    }
+    
+  protected:
+    Mutex& mutex;
+    Mutex cond_lock;
+	  Thread* waiting;
+  };
+
+};
+
+#endif // __RFB_THREADING_IMPL
diff --git a/rfb/win32/msvcwarning.h b/rfb/win32/msvcwarning.h
new file mode 100644
index 0000000..d0c98c3
--- /dev/null
+++ b/rfb/win32/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/rfb/win32/util_win32.h b/rfb/win32/util_win32.h
new file mode 100644
index 0000000..4bde5ec
--- /dev/null
+++ b/rfb/win32/util_win32.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// util_win32.h - miscellaneous useful bits for Win32 only
+//
+
+#ifndef __RFB_UTIL_WIN32_H__
+#define __RFB_UTIL_WIN32_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+// *** #include <iostream.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  // WIN32-ONLY PROFILING CODE
+  //
+  // CpuTime and CpuTimer provide a simple way to profile particular
+  // sections of code
+  //
+  // Use one CpuTime object per task to be profiled.  CpuTime instances
+  // maintain a cumulative total of time spent in user and kernel space
+  // by threads.
+  // When a CpuTime object is created, a label must be specified to
+  // identify the task being profiled.
+  // When the object is destroyed, it will print debugging information
+  // containing the user and kernel times accumulated.
+  //
+  // Place a CpuTimer object in each section of code which is to be
+  // profiled.  When the object is created, it snapshots the current
+  // kernel and user times and stores them.  These are used when the
+  // object is destroyed to establish how much time has elapsed in the
+  // intervening period.  The accumulated time is then added to the
+  // associated CpuTime object.
+  //
+  // This code works only on platforms providing __int64
+
+	class CpuTime {
+	public:
+		CpuTime(const char *name)
+			: timer_name(strDup(name)),
+			  kernel_time(0), user_time(0), max_user_time(0), iterations(0) {}
+		~CpuTime() {
+      g_log_writer.info("timer %s : %I64ums (krnl), %I64ums (user), %I64uus (user-max) (%I64u its)\n",
+				timer_name, kernel_time/10000, user_time/10000, max_user_time/10,
+				iterations);
+			delete [] timer_name;
+		}
+    static LogWriter g_log_writer;
+		char* timer_name;
+		__int64 kernel_time;
+		__int64 user_time;
+		__int64 iterations;
+		__int64 max_user_time;
+	};
+
+	class CpuTimer {
+	public:
+		inline CpuTimer(CpuTime &ct) : cputime(ct) {
+			FILETIME create_time, end_time;
+			if (!GetThreadTimes(GetCurrentThread(),
+				&create_time, &end_time,
+				(LPFILETIME)&start_kernel_time,
+				(LPFILETIME)&start_user_time)) {
+        throw rdr::SystemException("rfb::CpuTimer failed to initialise", GetLastError());
+			}
+		}
+		inline ~CpuTimer() {
+			FILETIME create_time, end_time;
+			__int64 end_kernel_time, end_user_time;
+			if (!GetThreadTimes(GetCurrentThread(),
+				&create_time, &end_time,
+				(LPFILETIME)&end_kernel_time,
+				(LPFILETIME)&end_user_time)) {
+        throw rdr::SystemException("rfb::CpuTimer destructor failed", GetLastError());
+			}
+			cputime.kernel_time += end_kernel_time - start_kernel_time;
+			cputime.user_time += end_user_time - start_user_time;
+			if (end_user_time - start_user_time > cputime.max_user_time) {
+				cputime.max_user_time = end_user_time - start_user_time;
+			}
+			cputime.iterations++;
+		}
+	private:
+		CpuTime& cputime;
+		__int64 start_kernel_time;
+		__int64 start_user_time;
+	};
+
+};
+
+#endif // __RFB_UTIL_WIN32_H__
diff --git a/rfb/zrleDecode.h b/rfb/zrleDecode.h
new file mode 100644
index 0000000..b5391b1
--- /dev/null
+++ b/rfb/zrleDecode.h
@@ -0,0 +1,251 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// ZRLE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+// IMAGE_RECT         - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,CPIXEL)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,CPIXEL)
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,BPP)
+#endif
+
+void ZRLE_DECODE (const Rect& r, rdr::InStream* is,
+                      rdr::ZlibInStream* zis, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+                      , EXTRA_ARGS
+#endif
+                      )
+{
+  int length = is->readU32();
+  zis->setUnderlying(is, length);
+  Rect t;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+    t.br.y = min(r.br.y, t.tl.y + 64);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+      t.br.x = min(r.br.x, t.tl.x + 64);
+
+      int mode = zis->readU8();
+      bool rle = mode & 128;
+      int palSize = mode & 127;
+      PIXEL_T palette[128];
+
+      for (int i = 0; i < palSize; i++) {
+        palette[i] = zis->READ_PIXEL();
+      }
+
+      if (palSize == 1) {
+        PIXEL_T pix = palette[0];
+        FILL_RECT(t,pix);
+        continue;
+      }
+
+      if (!rle) {
+        if (palSize == 0) {
+
+          // raw
+
+#ifdef CPIXEL
+          for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) {
+            *ptr = zis->READ_PIXEL();
+          }
+#else
+          zis->readBytes(buf, t.area() * (BPP / 8));
+#endif
+
+        } else {
+
+          // packed pixels
+          int bppp = ((palSize > 16) ? 8 :
+                      ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+
+          PIXEL_T* ptr = buf;
+
+          for (int i = 0; i < t.height(); i++) {
+            PIXEL_T* eol = ptr + t.width();
+            rdr::U8 byte = 0;
+            rdr::U8 nbits = 0;
+
+            while (ptr < eol) {
+              if (nbits == 0) {
+                byte = zis->readU8();
+                nbits = 8;
+              }
+              nbits -= bppp;
+              rdr::U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127;
+              *ptr++ = palette[index];
+            }
+          }
+        }
+
+#ifdef FAVOUR_FILL_RECT
+       //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+        //t.width(),t.height(),t.tl.x,t.tl.y);
+        IMAGE_RECT(t,buf);
+#endif
+
+      } else {
+
+        if (palSize == 0) {
+
+          // plain RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + t.area();
+          while (ptr < end) {
+            PIXEL_T pix = zis->READ_PIXEL();
+            int len = 1;
+            int b;
+            do {
+              b = zis->readU8();
+              len += b;
+            } while (b == 255);
+
+            assert(len <= end - ptr);
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % t.width();
+            int runY = i / t.width();
+
+            if (runX + len > t.width()) {
+              if (runX != 0) {
+                FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+                          pix);
+                len -= t.width()-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > t.width()) {
+                FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+                          pix);
+                runY += len / t.width();
+                len = len % t.width();
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+
+          }
+        } else {
+
+          // palette RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + t.area();
+          while (ptr < end) {
+            int index = zis->readU8();
+            int len = 1;
+            if (index & 128) {
+              int b;
+              do {
+                b = zis->readU8();
+                len += b;
+              } while (b == 255);
+
+              assert(len <= end - ptr);
+            }
+
+            index &= 127;
+
+            PIXEL_T pix = palette[index];
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % t.width();
+            int runY = i / t.width();
+
+            if (runX + len > t.width()) {
+              if (runX != 0) {
+                FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+                          pix);
+                len -= t.width()-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > t.width()) {
+                FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+                          pix);
+                runY += len / t.width();
+                len = len % t.width();
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+          }
+        }
+      }
+
+#ifndef FAVOUR_FILL_RECT
+      //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+      //t.width(),t.height(),t.tl.x,t.tl.y);
+      IMAGE_RECT(t,buf);
+#endif
+    }
+  }
+
+  zis->reset();
+}
+
+#undef ZRLE_DECODE
+#undef READ_PIXEL
+#undef PIXEL_T
+}
diff --git a/rfb/zrleEncode.h b/rfb/zrleEncode.h
new file mode 100644
index 0000000..a1582f2
--- /dev/null
+++ b/rfb/zrleEncode.h
@@ -0,0 +1,328 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+//
+// zrleEncode.h - zrle encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+//
+// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel
+// bigger than the largest tile of pixel data, since the ZRLE encoding
+// algorithm writes to the position one past the end of the pixel data.
+//
+
+#include <rdr/OutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,CPIXEL)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,CPIXEL)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,CPIXEL)
+#define BPPOUT 24
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,BPP)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,BPP)
+#define BPPOUT BPP
+#endif
+
+#ifndef ZRLE_ONCE
+#define ZRLE_ONCE
+static const int bitsPerPackedPixel[] = {
+  0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
+};
+
+// The PaletteHelper class helps us build up the palette from pixel data by
+// storing a reverse index using a simple hash-table
+
+class PaletteHelper {
+public:
+  enum { MAX_SIZE = 127 };
+
+  PaletteHelper()
+  {
+    memset(index, 255, sizeof(index));
+    size = 0;
+  }
+
+  inline int hash(rdr::U32 pix)
+  {
+    return (pix ^ (pix >> 17)) & 4095;
+  }
+
+  inline void insert(rdr::U32 pix)
+  {
+    if (size < MAX_SIZE) {
+      int i = hash(pix);
+      while (index[i] != 255 && key[i] != pix)
+        i++;
+      if (index[i] != 255) return;
+
+      index[i] = size;
+      key[i] = pix;
+      palette[size] = pix;
+    }
+    size++;
+  }
+
+  inline int lookup(rdr::U32 pix)
+  {
+    assert(size <= MAX_SIZE);
+    int i = hash(pix);
+    while (index[i] != 255 && key[i] != pix)
+      i++;
+    if (index[i] != 255) return index[i];
+    return -1;
+  }
+
+  rdr::U32 palette[MAX_SIZE];
+  rdr::U8 index[4096+MAX_SIZE];
+  rdr::U32 key[4096+MAX_SIZE];
+  int size;
+};
+#endif
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os);
+
+bool ZRLE_ENCODE (const Rect& r, rdr::OutStream* os,
+                  rdr::ZlibOutStream* zos, void* buf, int maxLen, Rect* actual
+#ifdef EXTRA_ARGS
+                  , EXTRA_ARGS
+#endif
+                  )
+{
+  zos->setUnderlying(os);
+  // RLE overhead is at worst 1 byte per 64x64 (4Kpixel) block
+  int worstCaseLine = r.width() * 64 * (BPPOUT/8) + 1 + r.width() / 64;
+  // Zlib overhead is at worst 6 bytes plus 5 bytes per 32Kbyte block.
+  worstCaseLine += 11 + 5 * (worstCaseLine >> 15);
+  Rect t;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+    t.br.y = min(r.br.y, t.tl.y + 64);
+
+    if (os->length() + worstCaseLine > maxLen) {
+      if (t.tl.y == r.tl.y)
+        throw Exception("ZRLE: not enough space for first line?");
+      actual->tl = r.tl;
+      actual->br.x = r.br.x;
+      actual->br.y = t.tl.y;
+      return false;
+    }
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+      t.br.x = min(r.br.x, t.tl.x + 64);
+
+      GET_IMAGE_INTO_BUF(t,buf);
+
+      ZRLE_ENCODE_TILE((PIXEL_T*)buf, t.width(), t.height(), zos);
+    }
+
+    zos->flush();
+  }
+  return true;
+}
+
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os)
+{
+  // First find the palette and the number of runs
+
+  PaletteHelper ph;
+
+  int runs = 0;
+  int singlePixels = 0;
+
+  PIXEL_T* ptr = data;
+  PIXEL_T* end = ptr + h * w;
+  *end = ~*(end-1); // one past the end is different so the while loop ends
+
+  while (ptr < end) {
+    PIXEL_T pix = *ptr;
+    if (*++ptr != pix) {
+      singlePixels++;
+    } else {
+      while (*++ptr == pix) ;
+      runs++;
+    }
+    ph.insert(pix);
+  }
+
+  //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n",
+  //        runs, singlePixels, ph.size);
+
+  // Solid tile is a special case
+
+  if (ph.size == 1) {
+    os->writeU8(1);
+    os->WRITE_PIXEL(ph.palette[0]);
+    return;
+  }
+
+  // Try to work out whether to use RLE and/or a palette.  We do this by
+  // estimating the number of bytes which will be generated and picking the
+  // method which results in the fewest bytes.  Of course this may not result
+  // in the fewest bytes after compression...
+
+  bool useRle = false;
+  bool usePalette = false;
+
+  int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw
+
+  int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels);
+
+  if (plainRleBytes < estimatedBytes) {
+    useRle = true;
+    estimatedBytes = plainRleBytes;
+  }
+
+  if (ph.size < 128) {
+    int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels;
+
+    if (paletteRleBytes < estimatedBytes) {
+      useRle = true;
+      usePalette = true;
+      estimatedBytes = paletteRleBytes;
+    }
+
+    if (ph.size < 17) {
+      int packedBytes = ((BPPOUT/8) * ph.size +
+                         w * h * bitsPerPackedPixel[ph.size-1] / 8);
+
+      if (packedBytes < estimatedBytes) {
+        useRle = false;
+        usePalette = true;
+        estimatedBytes = packedBytes;
+      }
+    }
+  }
+
+  if (!usePalette) ph.size = 0;
+
+  os->writeU8((useRle ? 128 : 0) | ph.size);
+
+  for (int i = 0; i < ph.size; i++) {
+    os->WRITE_PIXEL(ph.palette[i]);
+  }
+
+  if (useRle) {
+
+    PIXEL_T* ptr = data;
+    PIXEL_T* end = ptr + w * h;
+    PIXEL_T* runStart;
+    PIXEL_T pix;
+    while (ptr < end) {
+      runStart = ptr;
+      pix = *ptr++;
+      while (*ptr == pix && ptr < end)
+        ptr++;
+      int len = ptr - runStart;
+      if (len <= 2 && usePalette) {
+        int index = ph.lookup(pix);
+        if (len == 2)
+          os->writeU8(index);
+        os->writeU8(index);
+        continue;
+      }
+      if (usePalette) {
+        int index = ph.lookup(pix);
+        os->writeU8(index | 128);
+      } else {
+        os->WRITE_PIXEL(pix);
+      }
+      len -= 1;
+      while (len >= 255) {
+        os->writeU8(255);
+        len -= 255;
+      }
+      os->writeU8(len);
+    }
+
+  } else {
+
+    // no RLE
+
+    if (usePalette) {
+
+      // packed pixels
+
+      assert (ph.size < 17);
+
+      int bppp = bitsPerPackedPixel[ph.size-1];
+
+      PIXEL_T* ptr = data;
+
+      for (int i = 0; i < h; i++) {
+        rdr::U8 nbits = 0;
+        rdr::U8 byte = 0;
+
+        PIXEL_T* eol = ptr + w;
+
+        while (ptr < eol) {
+          PIXEL_T pix = *ptr++;
+          rdr::U8 index = ph.lookup(pix);
+          byte = (byte << bppp) | index;
+          nbits += bppp;
+          if (nbits >= 8) {
+            os->writeU8(byte);
+            nbits = 0;
+          }
+        }
+        if (nbits > 0) {
+          byte <<= 8 - nbits;
+          os->writeU8(byte);
+        }
+      }
+    } else {
+
+      // raw
+
+#ifdef CPIXEL
+      for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) {
+        os->WRITE_PIXEL(*ptr);
+      }
+#else
+      os->writeBytes(data, w*h*(BPP/8));
+#endif
+    }
+  }
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef ZRLE_ENCODE
+#undef ZRLE_ENCODE_TILE
+#undef BPPOUT
+}
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx
new file mode 100644
index 0000000..efb15c0
--- /dev/null
+++ b/rfb_win32/AboutDialog.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("AboutDialog");
+
+AboutDialog AboutDialog::instance;
+
+
+AboutDialog::AboutDialog() : Dialog(GetModuleHandle(0)) {
+}
+
+bool AboutDialog::showDialog() {
+  return Dialog::showDialog(MAKEINTRESOURCE(DialogId));
+}
+
+void AboutDialog::initDialog() {
+  // Set the build time field
+  SetWindowText(GetDlgItem(handle, BuildTime), TStr(buildTime));
+
+  // Get our executable's version info
+  FileVersionInfo verInfo;
+
+  SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("FileVersion")));
+  SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
+  SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription")));
+}
diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h
new file mode 100644
index 0000000..cb1713d
--- /dev/null
+++ b/rfb_win32/AboutDialog.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- AboutDialog.h
+
+#ifndef __RFB_WIN32_ABOUT_DIALOG_H__
+#define __RFB_WIN32_ABOUT_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+extern const char* buildTime;
+
+namespace rfb {
+
+  namespace win32 {
+
+    class AboutDialog : Dialog {
+    public:
+      AboutDialog();
+      virtual bool showDialog();
+      virtual void initDialog();
+
+      static AboutDialog instance;
+
+      typedef WORD LabelId;
+      static const LabelId DialogId;    // Resource ID of the About dialog
+      static const LabelId BuildTime;   // Resource ID of the BuildTime label in the dialog
+      static const LabelId Version;     // etc...
+      static const LabelId Copyright;
+      static const LabelId Description;
+    protected:
+      WORD dialogId;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx
new file mode 100644
index 0000000..ad852a0
--- /dev/null
+++ b/rfb_win32/CKeyboard.cxx
@@ -0,0 +1,259 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <map>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+#include <rfb_win32/CKeyboard.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("CKeyboard");
+
+
+// Client-side RFB keyboard event sythesis
+
+class CKeymapper {
+
+public:
+  CKeymapper()
+  {
+    for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+      int extendedVkey = keymap[i].vk + (keymap[i].extended ? 256 : 0);
+      if (keysymMap.find(extendedVkey) == keysymMap.end()) {
+        keysymMap[extendedVkey] = keymap[i].keysym;
+      }
+    }
+  }
+
+  // lookup() tries to find a match for vkey with the extended flag.  We check
+  // first for an exact match including the extended flag, then try without the
+  // extended flag.
+  rdr::U32 lookup(int extendedVkey) {
+    if (keysymMap.find(extendedVkey) != keysymMap.end())
+      return keysymMap[extendedVkey];
+    if (keysymMap.find(extendedVkey ^ 256) != keysymMap.end())
+      return keysymMap[extendedVkey ^ 256];
+    return 0;
+  }
+
+private:
+  std::map<int,rdr::U32> keysymMap;
+} ckeymapper;
+
+
+class ModifierKeyReleaser {
+public:
+  ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended)
+    : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
+      keysym(0)
+  {}
+  void release(std::map<int,rdr::U32>* downKeysym) {
+    if (downKeysym->find(extendedVkey) != downKeysym->end()) {
+      keysym = (*downKeysym)[extendedVkey];
+      vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
+                 extendedVkey, keysym);
+      writer->writeKeyEvent(keysym, false);
+    }
+  }
+  ~ModifierKeyReleaser() {
+    if (keysym) {
+      vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
+                 extendedVkey, keysym);
+      writer->writeKeyEvent(keysym, true);
+    }
+  }
+  CMsgWriter* writer;
+  int extendedVkey;
+  rdr::U32 keysym;
+};
+
+// IS_PRINTABLE_LATIN1 tests if a character is either a printable latin1
+// character, or 128, which is the Euro symbol on Windows.
+#define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \
+                                ((c) >= 160 && (c) <= 255))
+
+void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey,
+                                rdr::U32 flags, bool down)
+{
+  bool extended = (flags & 0x1000000);
+  int extendedVkey = vkey + (extended ? 256 : 0);
+
+  // If it's a release, just release whichever keysym corresponded to the same
+  // key being pressed, regardless of how it would be interpreted in the
+  // current keyboard state.
+  if (!down) {
+    releaseKey(writer, extendedVkey);
+    return;
+  }
+
+  // We should always pass every down event to ToAscii() otherwise it can get
+  // out of sync.
+
+  // XXX should we pass CapsLock, ScrollLock or NumLock to ToAscii - they
+  // actually alter the lock state on the keyboard?
+
+  BYTE keystate[256];
+  GetKeyboardState(keystate);
+  rdr::U8 chars[2];
+
+  int nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+  // See if it's in the Windows VK code -> X keysym map.  We do this before
+  // looking at the result of ToAscii so that e.g. we recognise that it's
+  // XK_KP_Add rather than '+'.
+
+  rdr::U32 keysym = ckeymapper.lookup(extendedVkey);
+  if (keysym) {
+    vlog.debug("mapped key: extendedVkey 0x%x", extendedVkey);
+    pressKey(writer, extendedVkey, keysym);
+    return;
+  }
+
+  if (nchars < 0) {
+    // Dead key - the next call to ToAscii() will give us either the accented
+    // character or two characters.
+    vlog.debug("ToAscii dead key (1): extendedVkey 0x%x", extendedVkey);
+    return;
+  }
+
+  if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+    // Got a printable latin1 character.  We must release Control and Alt
+    // (AltGr) if they were both pressed, so that the latin1 character is seen
+    // without them by the VNC server.
+    ModifierKeyReleaser lctrl(writer, VK_CONTROL, 0);
+    ModifierKeyReleaser rctrl(writer, VK_CONTROL, 1);
+    ModifierKeyReleaser lalt(writer, VK_MENU, 0);
+    ModifierKeyReleaser ralt(writer, VK_MENU, 1);
+
+    if ((keystate[VK_CONTROL] & 0x80) && (keystate[VK_MENU] & 0x80)) {
+      lctrl.release(&downKeysym);
+      rctrl.release(&downKeysym);
+      lalt.release(&downKeysym);
+      ralt.release(&downKeysym);
+    }
+
+    for (int i = 0; i < nchars; i++) {
+      vlog.debug("ToAscii key (1): extendedVkey 0x%x", extendedVkey);
+      if (chars[i] == 128) { // special hack for euro!
+        pressKey(writer, extendedVkey, XK_EuroSign);
+      } else {
+        pressKey(writer, extendedVkey, chars[i]);
+      }
+    }
+    return;
+  }
+
+  // Either no chars were generated, or something outside the printable
+  // character range.  Try ToAscii() without the Control and Alt keys down to
+  // see if that yields an ordinary character.
+
+  keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0;
+  keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0;
+
+  nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+  if (nchars < 0) {
+    // So it would be a dead key if neither control nor alt were pressed.
+    // However, we know that at least one of control and alt must be pressed.
+    // We can't leave it at this stage otherwise the next call to ToAscii()
+    // with a valid character will get wrongly interpreted in the context of
+    // this bogus dead key.  Working on the assumption that a dead key followed
+    // by space usually returns the dead character itself, try calling ToAscii
+    // with VK_SPACE.
+    vlog.debug("ToAscii dead key (2): extendedVkey 0x%x", extendedVkey);
+    nchars = ToAscii(VK_SPACE, 0, keystate, (WORD*)&chars, 0);
+    if (nchars < 0) {
+      vlog.debug("ToAscii dead key (3): extendedVkey 0x%x - giving up!",
+                 extendedVkey);
+      return;
+    }
+  }
+
+  if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+    for (int i = 0; i < nchars; i++) {
+      vlog.debug("ToAscii key (2) (no ctrl/alt): extendedVkey 0x%x",
+                 extendedVkey);
+      if (chars[i] == 128) { // special hack for euro!
+        pressKey(writer, extendedVkey, XK_EuroSign);
+      } else {
+        pressKey(writer, extendedVkey, chars[i]);
+      }
+    }
+    return;
+  }
+
+  vlog.debug("no chars regardless of control and alt: extendedVkey 0x%x",
+             extendedVkey);
+}
+
+// releaseAllKeys() - write key release events to the server for all keys
+// that are currently regarded as being down.
+void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) {
+  std::map<int,rdr::U32>::iterator i, next_i;
+  for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
+    next_i = i; next_i++;
+    writer->writeKeyEvent((*i).second, false);
+    downKeysym.erase(i);
+  }
+}
+
+// releaseKey() - write a key up event to the server, but only if we've
+// actually sent a key down event for the given key.  The key up event always
+// contains the same keysym we used in the key down event, regardless of what
+// it would look up as using the current keyboard state.
+void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey)
+{
+  if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+    vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+               extendedVkey, downKeysym[extendedVkey]);
+    writer->writeKeyEvent(downKeysym[extendedVkey], false);
+    downKeysym.erase(extendedVkey);
+  }
+}
+
+// pressKey() - write a key down event to the server, and record which keysym
+// was sent as corresponding to the given extendedVkey.  The only tricky bit is
+// that if we are trying to press an extendedVkey which is already marked as
+// down but with a different keysym, then we need to release the old keysym
+// first.  This can happen in two cases: (a) when a single key press results in
+// more than one character, and (b) when shift is released while another key is
+// autorepeating.
+void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+                                rdr::U32 keysym)
+{
+  if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+    if (downKeysym[extendedVkey] != keysym) {
+      vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+                 extendedVkey, downKeysym[extendedVkey]);
+      writer->writeKeyEvent(downKeysym[extendedVkey], false);
+    }
+  }
+  vlog.debug("press   extendedVkey 0x%x, keysym 0x%x",
+             extendedVkey, keysym);
+  writer->writeKeyEvent(keysym, true);
+  downKeysym[extendedVkey] = keysym;
+}
diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h
new file mode 100644
index 0000000..10346ff
--- /dev/null
+++ b/rfb_win32/CKeyboard.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CKeyboard.h
+//
+// Client-side keyboard handling for Win32
+
+#ifndef __RFB_WIN32_CKEYBOARD_H__
+#define __RFB_WIN32_CKEYBOARD_H__
+
+#include <rdr/types.h>
+#include <map>
+
+namespace rfb {
+
+  class CMsgWriter;
+
+  namespace win32 {
+
+    class CKeyboard {
+    public:
+      void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags,
+                    bool down);
+      void releaseAllKeys(CMsgWriter* writer);
+      const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
+      bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
+    private:
+      void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey);
+      void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+                                      rdr::U32 keysym);
+      std::map<int,rdr::U32> downKeysym;
+    };
+
+  }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CKEYBOARD_H__
diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx
new file mode 100644
index 0000000..1cab662
--- /dev/null
+++ b/rfb_win32/CPointer.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CPointer.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CPointer");
+
+
+CPointer::CPointer() : currButtonMask(0), intervalQueued(false), threeEmulating(false) {
+}
+
+CPointer::~CPointer() {
+  intervalTimer.stop();
+  threeTimer.stop();
+}
+
+
+void CPointer::pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+  //
+  // - Duplicate Event Filtering
+  //
+
+  bool maskChanged = buttonMask != currButtonMask;
+  bool posChanged = !Point(x, y).equals(currPos);
+  if (!(posChanged || maskChanged))
+    return;
+
+  // Pass on the event to the event-interval handler
+  threePointerEvent(writer, x, y, buttonMask);
+
+  // Save the position and mask
+  currPos = Point(x, y);
+  currButtonMask = buttonMask;
+}
+
+
+inline abs(int x) {return x>0 ? x : 0;}
+
+int emulate3Mask(int buttonMask) {
+  // - Release left & right and press middle
+  vlog.debug("emulate3: active");
+  buttonMask &= ~5;
+  buttonMask |= 2;
+  return buttonMask;
+}
+
+void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+  //
+  // - 3-Button Mouse Emulation
+  //
+
+  if (emulate3) {
+
+    bool leftChanged = (buttonMask & 1) != (currButtonMask & 1);
+    bool rightChanged = (buttonMask & 4) != (currButtonMask & 4);
+
+    if (leftChanged || rightChanged) {
+      // - One of left or right have changed
+
+      if ((buttonMask & 5) == 1 || (buttonMask & 5) == 4) {
+        // - One is up, one is down.  Start a timer, so that if it
+        //   expires then we know we should actually send this event
+        vlog.debug("emulate3: start timer");
+        threeTimer.start(100);
+        threePos = Point(x, y);
+        threeMask = buttonMask;
+        return;
+
+      } else if (threeTimer.isActive()) {
+        // - Both are up or both are down, and we were timing for an emulation event
+        //   Stop the timer and flush the stored event
+        vlog.debug("emulate3: stop timer (state)");
+        threeTimer.stop();
+        if (threeEmulating == ((buttonMask & 5) == 5))
+          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+        else
+          threeEmulating = ((buttonMask & 5) == 5);
+      }
+
+    } else {
+    
+      if (threeTimer.isActive()) {
+        // - We are timing for an emulation event
+
+        if (abs(threePos.x - x) <= 4 || abs(threePos.y - y) <= 4) {
+          //   If the mouse has moved too far since the button-change event then flush
+          vlog.debug("emulate3: stop timer (moved)");
+          threeTimer.stop();
+          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+
+        } else {
+          //   Otherwise, we ignore the new event
+          return;
+        }
+      }
+
+    }
+
+    // - If neither left nor right are down, stop emulating
+    if ((buttonMask & 5) == 0)
+      threeEmulating = false;
+
+    // - If emulating, release left & right and press middle
+    if (threeEmulating)
+      buttonMask = emulate3Mask(buttonMask);
+
+  }
+
+  // - Let the event pass through to the next stage of processing
+  intervalPointerEvent(writer, x, y, buttonMask);
+}
+
+void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+  //
+  // - Pointer Event Interval
+  //
+  vlog.write(101, "ptrEvent: %d,%d (%lx)", x, y, buttonMask);
+
+  // Send the event immediately if we haven't sent one for a while
+  bool sendNow = !intervalTimer.isActive();
+
+  if (intervalMask != buttonMask) {
+    // If the buttons have changed then flush queued events and send now
+    sendNow = true;
+    if (intervalQueued)
+      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+    intervalQueued = false;
+  }
+
+  if (!sendNow) {
+    // If we're not sending now then just queue the event
+    intervalQueued = true;
+    intervalPos = Point(x, y);
+    intervalMask = buttonMask;
+  } else {
+    // Start the interval timer if required, and send the event
+    intervalQueued = false;
+    intervalMask = buttonMask;
+    if (pointerEventInterval)
+      intervalTimer.start(pointerEventInterval);
+    writer->writePointerEvent(x, y, buttonMask);
+  }
+}
+
+void CPointer::handleTimer(CMsgWriter* writer, int timerId) {
+  if (timerId == intervalTimer.getId()) {
+    // Pointer interval has expired - send any queued events
+    if (intervalQueued) {
+      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+      intervalQueued = false;
+    } else {
+      intervalTimer.stop();
+    }
+
+  } else if (timerId = threeTimer.getId()) {
+    // 3-Button emulation timer has expired - send what we've got
+    vlog.debug("emulate3: timeout");
+    threeTimer.stop();
+
+    // If emulating, release left & right and press middle
+    if (threeEmulating)
+      threeMask = emulate3Mask(threeMask);
+
+    intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+  }
+}
diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h
new file mode 100644
index 0000000..f111de7
--- /dev/null
+++ b/rfb_win32/CPointer.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CPointer.h
+//
+// Client-side pointer event handling for Win32
+
+#ifndef __RFB_WIN32_CPOINTER_H__
+#define __RFB_WIN32_CPOINTER_H__
+
+#include <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/IntervalTimer.h>
+
+namespace rfb {
+
+  class CMsgWriter;
+
+  namespace win32 {
+
+    class CPointer {
+    public:
+      CPointer();
+      ~CPointer();
+
+      void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      void handleTimer(CMsgWriter* writer, int timerId);
+
+      void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);}
+      void setIntervalTimerId(int id) {intervalTimer.setId(id);}
+      void set3ButtonTimerId(int id) {threeTimer.setId(id);}
+
+      void enableEmulate3(bool enable) {emulate3 = enable;}
+      void enableInterval(int millis) {pointerEventInterval = millis;}
+    private:
+      Point currPos;
+      int currButtonMask;
+
+      bool emulate3;
+      int pointerEventInterval;
+
+      void intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      IntervalTimer intervalTimer;
+      bool intervalQueued;
+      Point intervalPos;
+      int intervalMask;
+
+      void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      IntervalTimer threeTimer;
+      Point threePos;
+      int threeMask;
+      bool threeEmulating;
+    };
+
+  }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CPOINTER_H__
diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx
new file mode 100644
index 0000000..9fb8347
--- /dev/null
+++ b/rfb_win32/CleanDesktop.cxx
@@ -0,0 +1,255 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CleanDesktop.cxx
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wininet.h>
+#include <shlobj.h>
+
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("CleanDesktop");
+
+
+struct ActiveDesktop {
+  ActiveDesktop() : handle(0) {
+    // - Contact Active Desktop
+    HRESULT result = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
+                                      IID_IActiveDesktop, (PVOID*)&handle);
+    if (result != S_OK)
+      throw rdr::SystemException("failed to contact Active Desktop", result);
+  }
+  ~ActiveDesktop() {
+    if (handle)
+      handle->Release();
+  }
+  bool enable(bool enable_) {
+    // - Get the current Active Desktop options
+    COMPONENTSOPT adOptions;
+    memset(&adOptions, 0, sizeof(adOptions));
+    adOptions.dwSize = sizeof(adOptions);
+
+    HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0);
+    if (result != S_OK) {
+      vlog.error("failed to get Active Desktop options: %d", result);
+      return false;
+    }
+
+    // - If Active Desktop is active, disable it
+    if ((adOptions.fActiveDesktop==0) != (enable_==0)) {
+      if (enable_)
+        vlog.debug("enabling Active Desktop");
+      else
+        vlog.debug("disabling Active Desktop");
+
+      adOptions.fActiveDesktop = enable_;
+      result = handle->SetDesktopItemOptions(&adOptions, 0);
+      if (result != S_OK) {
+        vlog.error("failed to disable ActiveDesktop: %d", result);
+        return false;
+      }
+      handle->ApplyChanges(AD_APPLY_REFRESH);
+      return true;
+    }
+    return false;
+  }
+  IActiveDesktop* handle;
+};
+
+
+DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) {
+  DWORD r = ERROR_SUCCESS;
+  if (!SystemParametersInfo(action, param, ptr, ini)) {
+    r = GetLastError();
+    vlog.info("SPI error: %d", r);
+  }
+  return r;
+}
+
+
+CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), restoreEffects(false) {
+  CoInitialize(0);
+}
+
+CleanDesktop::~CleanDesktop() {
+  enableEffects();
+  enablePattern();
+  enableWallpaper();
+  CoUninitialize();
+}
+
+void CleanDesktop::disableWallpaper() {
+  try {
+    ImpersonateCurrentUser icu;
+
+    vlog.debug("disable desktop wallpaper/Active Desktop");
+
+    // -=- First attempt to remove the wallpaper using Active Desktop
+    try {
+      ActiveDesktop ad;
+      if (ad.enable(false))
+        restoreActiveDesktop = true;
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+    }
+
+    // -=- Switch of normal wallpaper and notify apps
+    SysParamsInfo(SPI_SETDESKWALLPAPER, 0, "", SPIF_SENDCHANGE);
+    restoreWallpaper = true;
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+}
+
+void CleanDesktop::enableWallpaper() {
+  try {
+    ImpersonateCurrentUser icu;
+
+    if (restoreActiveDesktop) {
+      vlog.debug("restore Active Desktop");
+
+      // -=- First attempt to re-enable Active Desktop
+      try {
+        ActiveDesktop ad;
+        ad.enable(true);
+        restoreActiveDesktop = false;
+      } catch (rdr::Exception& e) {
+        vlog.error(e.str());
+      }
+    }
+
+    if (restoreWallpaper) {
+      vlog.debug("restore desktop wallpaper");
+
+      // -=- Then restore the standard wallpaper if required
+	    SysParamsInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
+      restoreWallpaper = false;
+    }
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+}
+
+
+void CleanDesktop::disablePattern() {
+  try {
+    ImpersonateCurrentUser icu;
+
+    vlog.debug("disable desktop pattern");
+    SysParamsInfo(SPI_SETDESKPATTERN, 0, "", SPIF_SENDCHANGE);
+    restorePattern = true;
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+}
+
+void CleanDesktop::enablePattern() {
+  try {
+    if (restorePattern) {
+      ImpersonateCurrentUser icu;
+
+      vlog.debug("restoring pattern...");
+
+      TCharArray pattern;
+      if (osVersion.isPlatformWindows) {
+        RegKey cfgKey;
+        cfgKey.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+        pattern.buf = cfgKey.getString(_T("Pattern"));
+      }
+      SysParamsInfo(SPI_SETDESKPATTERN, 0, pattern.buf, SPIF_SENDCHANGE);
+      restorePattern = false;
+    }
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+}
+
+
+void CleanDesktop::disableEffects() {
+#if (WINVER >= 0x500)
+  try {
+    ImpersonateCurrentUser icu;
+
+    vlog.debug("disable desktop effects");
+
+    SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+    if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) {
+      SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0);
+      SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0);
+      SysParamsInfo(SPI_GETHOTTRACKING, 0, &hotTracking, 0);
+      SysParamsInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &listBoxSmoothScroll, 0);
+      SysParamsInfo(SPI_GETMENUANIMATION, 0, &menuAnim, 0);
+      SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+      SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, FALSE, SPIF_SENDCHANGE);
+      SysParamsInfo(SPI_SETHOTTRACKING, 0, FALSE, SPIF_SENDCHANGE);
+      SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, FALSE, SPIF_SENDCHANGE);
+      SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+    } else {
+      SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE);
+    }
+    restoreEffects = true;
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+#else
+      vlog.info("disableffects not implemented");
+#endif
+}
+
+void CleanDesktop::enableEffects() {
+  try {
+    if (restoreEffects) {
+#if (WINVER >= 0x500)
+      ImpersonateCurrentUser icu;
+
+      vlog.debug("restore desktop effects");
+
+      RegKey desktopCfg;
+      desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+      SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE);
+      if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) {
+        SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE);
+        SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE);
+        SysParamsInfo(SPI_SETHOTTRACKING, 0, (void*)hotTracking, SPIF_SENDCHANGE);
+        SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (void*)listBoxSmoothScroll, SPIF_SENDCHANGE);
+        SysParamsInfo(SPI_SETMENUANIMATION, 0, (void*)menuAnim, SPIF_SENDCHANGE);
+      }
+      restoreEffects = false;
+#else
+      vlog.info("enableEffects not implemented");
+#endif
+    }
+
+  } catch (rdr::Exception& e) {
+    vlog.info(e.str());
+  }
+}
diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h
new file mode 100644
index 0000000..73f4153
--- /dev/null
+++ b/rfb_win32/CleanDesktop.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CleanDesktop.h
+
+#ifndef __RFB_WIN32_CLEANDESKTOP_H__
+#define __RFB_WIN32_CLEANDESKTOP_H__
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CleanDesktop {
+    public:
+      CleanDesktop();
+      ~CleanDesktop();
+
+      void disableWallpaper();
+      void enableWallpaper();
+
+      void disablePattern();
+      void enablePattern();
+
+      void disableEffects();
+      void enableEffects();
+
+    private:
+      bool restoreActiveDesktop;
+      bool restoreWallpaper;
+      bool restorePattern;
+      bool restoreEffects;
+      BOOL uiEffects;
+      BOOL comboBoxAnim, gradientCaptions, hotTracking, listBoxSmoothScroll, menuAnim;
+    };
+
+  }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CLEANDESKTOP_H__
diff --git a/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx
new file mode 100644
index 0000000..96d1e94
--- /dev/null
+++ b/rfb_win32/Clipboard.cxx
@@ -0,0 +1,199 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Clipboard.cxx
+
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Clipboard");
+
+
+//
+// -=- CR/LF handlers
+//
+
+char*
+dos2unix(const char* text) {
+  int len = strlen(text)+1;
+  char* unix = new char[strlen(text)+1];
+  int i, j=0;
+  for (i=0; i<len; i++) {
+    if (text[i] != '\x0d')
+      unix[j++] = text[i];
+  }
+  return unix;
+}
+
+char*
+unix2dos(const char* text) {
+  int len = strlen(text)+1;
+  char* dos = new char[strlen(text)*2+1];
+  int i, j=0;
+  for (i=0; i<len; i++) {
+    if (text[i] == '\x0a')
+      dos[j++] = '\x0d';
+    dos[j++] = text[i];
+  }
+  return dos;
+}
+
+
+//
+// -=- ASCII filter (in-place)
+//
+
+void
+removeNonAsciiChars(char* text) {
+  int len = strlen(text);
+  int i=0, j=0;
+  for (; i<len; i++) {
+    if ((text[i] >= 1) && (text[i] <= 127))
+      text[j++] = text[i];
+  }
+  text[j] = 0;
+}
+
+//
+// -=- Clipboard object
+//
+
+Clipboard::Clipboard()
+  : MsgWindow(_T("Clipboard")), notifier(0), next_window(0) {
+  next_window = SetClipboardViewer(getHandle());
+  vlog.debug("registered clipboard handler");
+}
+
+Clipboard::~Clipboard() {
+  vlog.debug("removing %x from chain (next is %x)", getHandle(), next_window);
+  ChangeClipboardChain(getHandle(), next_window);
+}
+
+LRESULT
+Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+
+  case WM_CHANGECBCHAIN:
+    vlog.debug("change clipboard chain (%x, %x)", wParam, lParam);
+    if ((HWND) wParam == next_window)
+      next_window = (HWND) lParam;
+    else if (next_window != 0)
+      SendMessage(next_window, msg, wParam, lParam);
+    else
+      vlog.error("bad clipboard chain change!");
+    break;
+
+  case WM_DRAWCLIPBOARD:
+    {
+      HWND owner = GetClipboardOwner();
+      if (owner == getHandle()) {
+        vlog.debug("local clipboard changed by me");
+      } else {
+        vlog.debug("local clipboard changed by %x", owner);
+
+			  // Open the clipboard
+			  if (OpenClipboard(getHandle())) {
+				  // Get the clipboard data
+				  HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
+				  if (cliphandle) {
+					  char* clipdata = (char*) GlobalLock(cliphandle);
+
+            // Notify clients
+            if (notifier) {
+              if (!clipdata) {
+                notifier->notifyClipboardChanged(0, 0);
+              } else {
+                CharArray unix_text;
+                unix_text.buf = dos2unix(clipdata);
+                removeNonAsciiChars(unix_text.buf);
+                notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
+              }
+            } else {
+              vlog.debug("no clipboard notifier registered");
+            }
+
+					  // Release the buffer and close the clipboard
+					  GlobalUnlock(cliphandle);
+				  }
+
+				  CloseClipboard();
+        }
+			}
+    }
+    if (next_window)
+		  SendMessage(next_window, msg, wParam, lParam);
+    return 0;
+
+  };
+  return MsgWindow::processMessage(msg, wParam, lParam);
+};
+
+void
+Clipboard::setClipText(const char* text) {
+  HANDLE clip_handle = 0;
+
+  try {
+
+    // - Firstly, we must open the clipboard
+    if (!OpenClipboard(getHandle()))
+      throw rdr::SystemException("unable to open Win32 clipboard", GetLastError());
+
+    // - Pre-process the supplied clipboard text into DOS format
+    CharArray dos_text;
+    dos_text.buf = unix2dos(text);
+    removeNonAsciiChars(dos_text.buf);
+    int dos_text_len = strlen(dos_text.buf);
+
+    // - Allocate global memory for the data
+    clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1);
+
+    char* data = (char*) GlobalLock(clip_handle);
+    memcpy(data, dos_text.buf, dos_text_len+1);
+    data[dos_text_len] = 0;
+    GlobalUnlock(clip_handle);
+
+    // - Next, we must clear out any existing data
+    if (!EmptyClipboard())
+      throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError());
+
+    // - Set the new clipboard data
+    if (!SetClipboardData(CF_TEXT, clip_handle))
+      throw rdr::SystemException("unable to set Win32 clipboard", GetLastError());
+    clip_handle = 0;
+
+    vlog.debug("set clipboard");
+  } catch (rdr::Exception& e) {
+    vlog.debug(e.str());
+  }
+
+  // - Close the clipboard
+  if (!CloseClipboard())
+    vlog.debug("unable to close Win32 clipboard: %u", GetLastError());
+  else
+    vlog.debug("closed clipboard");
+  if (clip_handle) {
+    vlog.debug("freeing clipboard handle");
+    GlobalFree(clip_handle);
+  }
+}
diff --git a/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h
new file mode 100644
index 0000000..57297e1
--- /dev/null
+++ b/rfb_win32/Clipboard.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Clipboard.h
+//
+// The Clipboard is used to set the system clipboard, and to get callbacks
+// when the system clipboard has changed.
+
+#ifndef __RFB_WIN32_CLIPBOARD_H__
+#define __RFB_WIN32_CLIPBOARD_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class Clipboard : MsgWindow {
+    public:
+
+      // -=- Abstract base class for callback recipients
+      class Notifier {
+      public:
+        virtual void notifyClipboardChanged(const char* text, int len) = 0;
+      };
+
+      Clipboard();
+      ~Clipboard();
+
+      // - Set the notifier to use
+      void setNotifier(Notifier* cbn) {notifier = cbn;}
+
+      // - Set the clipboard contents
+      void setClipText(const char* text);
+
+    protected:
+      // - Internal MsgWindow callback
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      Notifier* notifier;
+      HWND next_window;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_CLIPBOARD_H__
diff --git a/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
new file mode 100644
index 0000000..1a24485
--- /dev/null
+++ b/rfb_win32/CurrentUser.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Currentuser.cxx
+
+#include <windows.h>
+#include <lmcons.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+CurrentUserToken::CurrentUserToken() : isValid_(false) {
+  // - If the OS doesn't support security, ignore it
+  if (!isServiceProcess()) {
+    // - Running in User-Mode - just get our current token
+    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+      DWORD err = GetLastError();
+      if (err != ERROR_CALL_NOT_IMPLEMENTED)
+       throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+    }
+    isValid_ = true;
+  } else {
+    // - Under XP/2003 and above, we can just ask the operating system
+    typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
+    DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
+    if (_WTSQueryUserToken.isValid()) {
+      (*_WTSQueryUserToken)(-1, &h);
+      isValid_ = true;
+    } else {
+      // - Under NT/2K we have to resort to a nasty hack...
+      HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
+      if (!tray)
+        return;
+      DWORD processId = 0;
+      GetWindowThreadProcessId(tray, &processId);
+      if (!processId)
+        return;
+      Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+      if (!process.h)
+        return;
+      OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
+      isValid_ = true;
+    }
+  }
+}
+
+
+ImpersonateCurrentUser::ImpersonateCurrentUser() {
+  RegCloseKey(HKEY_CURRENT_USER);
+  if (!isServiceProcess())
+    return;
+  if (!token.isValid())
+    throw rdr::Exception("CurrentUserToken is not valid");
+  if (!ImpersonateLoggedOnUser(token)) {
+    DWORD err = GetLastError();
+    if (err != ERROR_CALL_NOT_IMPLEMENTED)
+      throw rdr::SystemException("Failed to impersonate user", GetLastError());
+  }
+}
+
+ImpersonateCurrentUser::~ImpersonateCurrentUser() {
+  if (!RevertToSelf()) {
+    DWORD err = GetLastError();
+    if (err != ERROR_CALL_NOT_IMPLEMENTED)
+      exit(err);
+  }
+}
+
+
+UserName::UserName() : TCharArray(UNLEN+1) {
+  DWORD len = UNLEN+1;
+  if (!GetUserName(buf, &len))
+    throw rdr::SystemException("GetUserName failed", GetLastError());
+}
diff --git a/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h
new file mode 100644
index 0000000..469946b
--- /dev/null
+++ b/rfb_win32/CurrentUser.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// CurrentUser.h
+
+// Helper class providing the session's logged on username, if
+// a user is logged on.  Also allows processes running under
+// XP/2K3 etc to masquerade as the logged on user for security
+// purposes
+
+#ifndef __RFB_WIN32_CURRENT_USER_H__
+#define __RFB_WIN32_CURRENT_USER_H__
+
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    // CurrentUserToken
+    //   h == 0
+    //     if platform is not NT *or* unable to get token
+    //     *or* if process is hosting service.
+    //   h != 0
+    //     if process is hosting service *and*
+    //     if platform is NT *and* able to get token.
+    //   isValid() == true
+    //     if platform is not NT *or* token is valid.
+
+    struct CurrentUserToken : public Handle {
+      CurrentUserToken();
+      bool isValid() const {return isValid_;};
+    private:
+      bool isValid_;
+    };
+
+    // ImpersonateCurrentUser
+    //   Throws an exception on failure.
+    //   Succeeds (trivially) if process is not running as service.
+    //   Fails if CurrentUserToken is not valid.
+    //   Fails if platform is NT AND cannot impersonate token.
+    //   Succeeds otherwise.
+
+    struct ImpersonateCurrentUser {
+      ImpersonateCurrentUser();
+      ~ImpersonateCurrentUser();
+      CurrentUserToken token;
+    };
+
+    // UserName
+    //   Returns the name of the user the thread is currently running as.
+
+    struct UserName : public TCharArray {
+      UserName();
+    };
+
+  }
+
+}
+
+#endif
diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx
new file mode 100644
index 0000000..2174376
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.cxx
@@ -0,0 +1,221 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <rfb/LogWriter.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/Win32Util.h>
+
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DIBSection");
+
+
+DIBSectionBuffer::DIBSectionBuffer(HWND window_)
+  : bitmap(0), device(0), window(window_) {
+  memset(&format, 0, sizeof(format));
+  memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::DIBSectionBuffer(HDC device_)
+  : bitmap(0), window(0), device(device_) {
+  memset(&format, 0, sizeof(format));
+  memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::~DIBSectionBuffer() {
+  if (bitmap)
+    DeleteObject(bitmap);
+}
+
+
+void DIBSectionBuffer::setPF(const PixelFormat& pf) {
+  if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) {
+    vlog.debug("pixel format unchanged by setPF()");
+    return;
+  }
+  format = pf;
+  recreateBuffer();
+  if ((pf.bpp <= 8) && pf.trueColour) {
+    vlog.debug("creating %d-bit TrueColour palette", pf.depth);
+    for (int i=0; i < (1<<(pf.depth)); i++) {
+      palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
+      palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
+      palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax;
+    }
+    refreshPalette();
+  }
+}
+
+void DIBSectionBuffer::setSize(int w, int h) {
+  if (width_ == w && height_ == h) {
+    vlog.debug("size unchanged by setSize()");
+    return;
+  }
+  width_ = w;
+  height_ = h;
+  recreateBuffer();
+}
+
+
+// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! *
+
+void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) {
+  BitmapDC dibDC(wndDC, dib);
+  RGBQUAD rgb[256];
+  for (unsigned int i=0;i<256;i++) {
+    rgb[i].rgbRed = palette[i].r >> 8;
+    rgb[i].rgbGreen = palette[i].g >> 8;
+    rgb[i].rgbBlue = palette[i].b >> 8;
+  }
+  if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb))
+    throw rdr::SystemException("unable to SetDIBColorTable", GetLastError());
+}
+
+inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+  for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+  (*max) = (rdr::U16)mask;
+}
+
+void DIBSectionBuffer::recreateBuffer() {
+  HBITMAP new_bitmap = 0;
+  rdr::U8* new_data = 0;
+
+  if (width_ && height_ && (format.depth != 0)) {
+    BitmapInfo bi;
+    memset(&bi, 0, sizeof(bi));
+    // *** wrong?
+    UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS;
+    // ***
+    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bi.bmiHeader.biBitCount = format.bpp;
+    bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_;
+    bi.bmiHeader.biPlanes = 1;
+    bi.bmiHeader.biWidth = width_;
+    bi.bmiHeader.biHeight = -height_;
+    bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB;
+    bi.mask.red = format.redMax << format.redShift;
+    bi.mask.green = format.greenMax << format.greenShift;
+    bi.mask.blue = format.blueMax << format.blueShift;
+
+    vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
+
+    // Create a DIBSection to draw into
+    if (device)
+      new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
+                                      (void**)&new_data, NULL, 0);
+    else
+      new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage,
+                                      (void**)&new_data, NULL, 0);
+
+    if (!new_bitmap) {
+      int err = GetLastError();
+      throw rdr::SystemException("unable to create DIB section", err);
+    }
+
+    vlog.debug("recreateBuffer()");
+  } else {
+    vlog.debug("one of area or format not set");
+  }
+
+  if (new_bitmap && bitmap) {
+    vlog.debug("preserving bitmap contents");
+
+    // Copy the contents across
+    if (device) {
+      if (format.bpp <= 8)
+        copyPaletteToDIB(palette, device, new_bitmap);
+      BitmapDC src_dev(device, bitmap);
+      BitmapDC dest_dev(device, new_bitmap);
+      BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+    } else {
+      WindowDC wndDC(window);
+      if (format.bpp <= 8)
+        copyPaletteToDIB(palette, wndDC, new_bitmap);
+      BitmapDC src_dev(wndDC, bitmap);
+      BitmapDC dest_dev(wndDC, new_bitmap);
+      BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+    }
+  }
+  
+  if (bitmap) {
+    // Delete the old bitmap
+    DeleteObject(bitmap);
+    bitmap = 0;
+    data = 0;
+  }
+
+  if (new_bitmap) {
+    // Set up the new bitmap
+    bitmap = new_bitmap;
+    data = new_data;
+
+    // Determine the *actual* DIBSection format
+    DIBSECTION ds;
+    if (!GetObject(bitmap, sizeof(ds), &ds))
+      throw rdr::SystemException("GetObject", GetLastError());
+
+    // Correct the "stride" of the DIB
+    // *** This code DWORD aligns each row - is that right???
+    stride = width_;
+    int bytesPerRow = stride * format.bpp/8;
+    if (bytesPerRow % 4) {
+      bytesPerRow += 4 - (bytesPerRow % 4);
+      stride = (bytesPerRow * 8) / format.bpp;
+      vlog.info("adjusting DIB stride: %d to %d", width_, stride);
+    }
+
+    // Calculate the PixelFormat for the DIB
+    format.bigEndian = 0;
+    format.bpp = format.depth = ds.dsBm.bmBitsPixel;
+    format.trueColour = format.trueColour || format.bpp > 8;
+    if (format.bpp > 8) {
+
+      // Get the truecolour format used by the DIBSection
+      initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift);
+      initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift);
+      initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift);
+
+      // Calculate the effective depth
+      format.depth = 0;
+      Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2];
+      while (bits) {
+        format.depth++;
+        bits = bits >> 1;
+      }
+    } else {
+      // Set the DIBSection's palette
+      refreshPalette();
+    }
+  }
+}
+
+void DIBSectionBuffer::refreshPalette() {
+  if (format.bpp > 8) {
+    vlog.error("refresh palette called for truecolour DIB");
+    return;
+  }
+  vlog.debug("refreshing palette");
+  if (device)
+    copyPaletteToDIB(palette, device, bitmap);
+  else
+    copyPaletteToDIB(palette, WindowDC(window), bitmap);
+}
+
+
diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h
new file mode 100644
index 0000000..51e2da3
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- DIBSectionBuffer.h
+
+// A DIBSectionBuffer acts much like a standard PixelBuffer, but is associated
+// with a particular window on-screen and can be drawn into that window if
+// required, using the standard Win32 drawing operations.
+
+#ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__
+#define __RFB_WIN32_DIB_SECTION_BUFFER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    //
+    // -=- DIBSectionBuffer
+    //
+
+    class DIBSectionBuffer : public FullFramePixelBuffer, ColourMap {
+    public:
+      DIBSectionBuffer(HWND window);
+      DIBSectionBuffer(HDC device);
+      virtual ~DIBSectionBuffer();
+
+      virtual void setPF(const PixelFormat &pf);
+      virtual void setSize(int w, int h);
+
+      virtual int getStride() const {return stride;}
+
+      virtual ColourMap* getColourMap() const {return (ColourMap*)this;}
+
+      // - ColourMap interface
+      virtual void lookup(int index, int* r, int *g, int* b) {
+        *r = palette[index].r;
+        *g = palette[index].g;
+        *b = palette[index].b;
+      }
+  
+      // Custom colourmap interface
+      void setColour(int index, int r, int g, int b) {
+        palette[index].r = r;
+        palette[index].g = g;
+        palette[index].b = b;
+      }
+      void refreshPalette();
+
+      // *** virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+    public:
+      HBITMAP bitmap;
+    protected:
+      void recreateBuffer();
+      Colour palette[256];
+      int stride;
+      HWND window;
+      HDC device;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_DIB_SECTION_BUFFER_H__
diff --git a/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx
new file mode 100644
index 0000000..a4d1021
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.cxx
@@ -0,0 +1,298 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- DeviceFrameBuffer.cxx
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of the system
+// display.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <assert.h>
+
+#include <vector>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rdr/types.h>
+#include <rfb/VNCServer.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+
+namespace rfb {
+
+namespace win32 {
+
+static LogWriter vlog("FrameBuffer");
+
+
+// -=- DeviceFrameBuffer class
+
+DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
+  : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
+    ignoreGrabErrors(false)
+{
+
+  // -=- Firstly, let's check that the device has suitable capabilities
+
+  int capabilities = GetDeviceCaps(device, RASTERCAPS);
+  if (!(capabilities & RC_BITBLT)) {
+    throw Exception("device does not support BitBlt");
+  }
+  if (!(capabilities & RC_DI_BITMAP)) {
+    throw Exception("device does not support GetDIBits");
+  }
+  /*
+  if (GetDeviceCaps(device, PLANES) != 1) {
+    throw Exception("device does not support planar displays");
+  }
+  */
+
+  // -=- Get the display dimensions and pixel format
+
+  // Get the display dimensions
+  RECT cr;
+  if (!GetClipBox(device, &cr))
+    throw rdr::SystemException("GetClipBox", GetLastError());
+  deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
+  if (!wRect.is_empty())
+    deviceCoords = wRect.translate(deviceCoords.tl);
+  int w = deviceCoords.width();
+  int h = deviceCoords.height();
+
+  // We can't handle uneven widths :(
+  if (w % 2) w--;
+
+  // Configure the underlying DIB to match the device
+  DIBSectionBuffer::setPF(DeviceContext::getPF(device));
+  DIBSectionBuffer::setSize(w, h);
+
+  // Configure the cursor buffer
+  cursorBm.setPF(format);
+
+  // Set up a palette if required
+  if (!format.trueColour)
+    updateColourMap();
+}
+
+DeviceFrameBuffer::~DeviceFrameBuffer() {
+}
+
+
+void
+DeviceFrameBuffer::setPF(const PixelFormat &pf) {
+  throw Exception("setPF not supported");
+}
+
+void
+DeviceFrameBuffer::setSize(int w, int h) {
+  throw Exception("setSize not supported");
+}
+
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT 0x40000000
+#endif
+
+void
+DeviceFrameBuffer::grabRect(const Rect &rect) {
+  BitmapDC tmpDC(device, bitmap);
+
+  // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
+  Point src = desktopToDevice(rect.tl);
+
+  // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
+  //       If you try CAPTUREBLT on 98 then you get blank output...
+  if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
+    osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) {
+    if (ignoreGrabErrors)
+      vlog.error("BitBlt failed:%ld", GetLastError());
+    else
+      throw rdr::SystemException("BitBlt failed", GetLastError());
+  }
+}
+
+void
+DeviceFrameBuffer::grabRegion(const Region &rgn) {
+  std::vector<Rect> rects;
+  std::vector<Rect>::const_iterator i;
+  rgn.get_rects(&rects);
+  for(i=rects.begin(); i!=rects.end(); i++) {
+    grabRect(*i);
+  }
+  ::GdiFlush();
+}
+
+
+void copyDevicePaletteToDIB(HDC dc, DIBSectionBuffer* dib) {
+  // - Fetch the system palette for the framebuffer
+  PALETTEENTRY syspalette[256];
+  UINT entries = ::GetSystemPaletteEntries(dc, 0, 256, syspalette);
+
+  if (entries == 0) {
+    vlog.info("resorting to standard 16 colour palette");
+    for (unsigned int i=0;i<256;i++) {
+      int v = (i%16) >= 8 ? 127 : 255;
+      syspalette[i].peRed = i & 1 ? v : 0;
+      syspalette[i].peGreen = i & 2 ? v : 0;
+      syspalette[i].peBlue = i & 4 ? v : 0;
+    }
+  } else {
+    vlog.info("framebuffer has %u palette entries", entries);
+  }
+
+  // - Update the bitmap's stored copy of the palette
+  for (unsigned int i=0;i<256;i++) {
+    int r, g, b;
+    r = (syspalette[i].peRed << 8) + 0x80;
+    g = (syspalette[i].peGreen << 8) + 0x80;
+    b = (syspalette[i].peBlue << 8) + 0x80;
+    dib->setColour(i, r, g, b);
+  }
+
+  // - Update the DIB section to use the palette
+  dib->refreshPalette();
+}
+
+
+void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
+{
+  // - If hCursor is null then there is no cursor - clear the old one
+
+  if (hCursor == 0) {
+    server->setCursor(0, 0, 0, 0, 0, 0);
+    return;
+  }
+
+  try {
+
+    // - Get the size and other details about the cursor.
+
+    IconInfo iconInfo((HICON)hCursor);
+
+    BITMAP maskInfo;
+    if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
+      throw rdr::SystemException("GetObject() failed", GetLastError());
+
+    assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
+
+    // - Create the cursor pixel buffer and mask storage
+    //   NB: The cursor pixel buffer is NOT used here.  Instead, we
+    //   pass the cursorBm.data pointer directly, to save overhead.
+
+    cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight);
+    cursor.setPF(format);
+    cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
+
+    // - Get the AND and XOR masks.  There is only an XOR mask if this is not a
+    // colour cursor.
+
+    if (!iconInfo.hbmColor)
+      cursor.setSize(cursor.width(), cursor.height() / 2);
+    rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
+    rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes;
+
+    if (!GetBitmapBits(iconInfo.hbmMask,
+                       maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
+      throw rdr::SystemException("GetBitmapBits failed", GetLastError());
+
+    // Configure the cursor bitmap
+    cursorBm.setSize(cursor.width(), cursor.height());
+
+    // Copy the palette into it if required
+    if (format.bpp <= 8)
+      copyDevicePaletteToDIB(device, &cursorBm);
+
+    // Draw the cursor into the bitmap
+    BitmapDC dc(device, cursorBm.bitmap);
+    if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT))
+      throw rdr::SystemException("unable to render cursor", GetLastError());
+
+    // Replace any XORed pixels with xorColour, because RFB doesn't support
+    // XORing of cursors.  XORing is used for the I-beam cursor, which is most
+    // often used over a white background, but also sometimes over a black
+    // background.  We set the XOR'd pixels to black, then draw a white outline
+    // around the whole cursor.
+
+    // *** should we replace any pixels not set in mask to zero, to ensure
+    // that irrelevant data doesn't screw compression?
+
+    bool doOutline = false;
+    if (!iconInfo.hbmColor) {
+      Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap());
+      for (int y = 0; y < cursor.height(); y++) {
+        bool first = true;
+        for (int x = 0; x < cursor.width(); x++) {
+          int byte = y * maskInfo.bmWidthBytes + x / 8;
+          int bit = 7 - x % 8;
+          if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit)))
+          {
+            mask.buf[byte] &= ~(1 << bit);
+
+            switch (format.bpp) {
+            case 8:
+              ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour;  break;
+            case 16:
+              ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+            case 32:
+              ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+            }
+
+            doOutline = true;
+          }
+        }
+      }
+    }
+
+    // Finally invert the AND mask so it's suitable for RFB and pack it into
+    // the minimum number of bytes per row.
+
+    int maskBytesPerRow = (cursor.width() + 7) / 8;
+
+    for (int j = 0; j < cursor.height(); j++) {
+      for (int i = 0; i < maskBytesPerRow; i++)
+        cursor.mask.buf[j * maskBytesPerRow + i]
+          = ~mask.buf[j * maskInfo.bmWidthBytes + i];
+    }
+
+    if (doOutline) {
+      vlog.debug("drawing cursor outline!");
+      memcpy(cursor.data, cursorBm.data, cursor.dataLen());
+      cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap()));
+      memcpy(cursorBm.data, cursor.data, cursor.dataLen());
+    }
+
+    server->setCursor(cursor.width(), cursor.height(),
+                      cursor.hotspot.x, cursor.hotspot.y,
+                      cursorBm.data, cursor.mask.buf);
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+}
+
+
+void
+DeviceFrameBuffer::updateColourMap() {
+  if (!format.trueColour)
+    copyDevicePaletteToDIB(device, this);
+}
+
+}; // namespace win32
+
+}; // namespace rfb
diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h
new file mode 100644
index 0000000..5e97b22
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- DeviceFrameBuffer.h
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of a supplied
+// Device Context Handle (HDC)
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+#define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb/Cursor.h>
+#include <rfb/Region.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  class VNCServer;
+
+  namespace win32 {
+
+    // -=- DeviceFrameBuffer interface
+
+    // DeviceFrameBuffer is passed an HDC referring to a window or to
+    // the entire display.  It may also be passed a rectangle specifying
+    // the Device-relative coordinates of the actual rectangle to treat
+    // as the desktop.
+
+    // Coordinate systems start getting really annoying here.  There are
+    // three different "origins" to which coordinates might be relative:
+    //
+    // Desktop - VNC coordinates, top-left always (0,0)
+    // Device - DC coordinates.  Top-left *usually (0,0) but could be other.
+    // Window - coordinates relative to the specified sub-rectangle within
+    //          the supplied DC.
+    // Screen - Coordinates relative to the entire Windows virtual screen.
+    //          The virtual screen includes all monitors that are part of
+    //          the Windows desktop.
+
+    // The data member is made to point to an internal mirror of the
+    // current display data.  Individual rectangles or regions of the
+    // buffer can be brought up to date by calling the grab functions.
+
+    class DeviceFrameBuffer : public DIBSectionBuffer {
+    public:
+      DeviceFrameBuffer(HDC deviceContext, const Rect& area_=Rect());
+      virtual ~DeviceFrameBuffer();
+
+      // - FrameBuffer overrides
+
+      virtual void grabRect(const Rect &rect);
+      virtual void grabRegion(const Region &region);
+
+      // - DIBSectionBuffer overrides
+      
+      virtual void setPF(const PixelFormat& pf);
+      virtual void setSize(int w, int h);
+      
+      // - DeviceFrameBuffer specific methods
+
+      void setCursor(HCURSOR c, VNCServer* server);
+      void updateColourMap();
+
+      // Set whether grabRect should ignore errors or throw exceptions
+      // Only set this if you are sure you'll capture the errors some other way!
+      void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;}
+
+    protected:
+      // Translate supplied Desktop coordinates into Device-relative coordinates
+      // This translation may have been affected at start-time by the supplied sub-rect.
+      Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);}
+
+      HDC device;
+      DIBSectionBuffer cursorBm;
+      Cursor cursor;
+      Rect deviceCoords;
+      bool ignoreGrabErrors;
+    };
+
+    // -=- createDisplayDeviceFrameBuffer
+    // createDisplayDeviceFrameBuffer must be passed the name of a display device,
+    // and will return a new FrameBuffer object attached to that display
+    // device.
+    // If the device name is not specified then the default display is
+    // returned.
+
+    DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0);
+
+    // -=- createDisplayFrameBuffers
+    // Creates a set of framebuffers, one for each available display
+    // device.
+
+    typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers;
+    void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs);
+
+  };
+
+};
+
+#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx
new file mode 100644
index 0000000..157cf5f
--- /dev/null
+++ b/rfb_win32/Dialog.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Dialog.cxx
+
+// Base-class for any Dialog classes we might require
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#ifdef _DIALOG_CAPTURE
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <extra/LoadBMP.cxx>
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter dlog("Dialog");
+static LogWriter plog("PropSheet");
+
+
+Dialog::Dialog(HINSTANCE inst_)
+: inst(inst_), alreadyShowing(false), handle(0)
+{
+}
+
+Dialog::~Dialog()
+{
+}
+
+
+bool Dialog::showDialog(const TCHAR* resource, HWND owner)
+{
+  if (alreadyShowing) return false;
+  handle = 0;
+  alreadyShowing = true;
+  INT_PTR result = DialogBoxParam(inst, resource, owner,
+                                  staticDialogProc, (LPARAM)this);
+  if (result<0)
+    throw rdr::SystemException("DialogBoxParam failed", GetLastError());
+  alreadyShowing = false;
+  return (result == 1);
+}
+
+
+bool Dialog::isItemChecked(int id) {
+  return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED;
+}
+int Dialog::getItemInt(int id) {
+  BOOL trans;
+  int result = GetDlgItemInt(handle, id, &trans, TRUE);
+  if (!trans)
+    throw rdr::Exception("unable to read dialog Int");
+  return result;
+}
+TCHAR* Dialog::getItemString(int id) {
+  TCharArray tmp(256);
+  if (!GetDlgItemText(handle, id, tmp.buf, 256))
+    tmp.buf[0] = 0;
+  return tmp.takeBuf();
+}
+
+void Dialog::setItemChecked(int id, bool state) {
+  dlog.debug("bool[%d]=%d", id, (int)state);
+  SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
+}
+void Dialog::setItemInt(int id, int value) {
+  dlog.debug("int[%d]=%d", id, value);
+  SetDlgItemInt(handle, id, value, TRUE);
+}
+void Dialog::setItemString(int id, const TCHAR* s) {
+  dlog.debug("string[%d]=%s", id, (const char*)CStr(s));
+  SetDlgItemText(handle, id, s);
+}
+
+
+void Dialog::enableItem(int id, bool state) {
+  dlog.debug("enable[%d]=%d", id, (int)state);
+  EnableWindow(GetDlgItem(handle, id), state);
+}
+
+
+
+
+BOOL CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg,
+				       WPARAM wParam, LPARAM lParam)
+{
+  if (msg == WM_INITDIALOG)
+    SetWindowLong(hwnd, GWL_USERDATA, (LONG)lParam);
+
+  LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+  if (!self) return FALSE;
+
+  return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+  switch (msg) {
+
+  case WM_INITDIALOG:
+    handle = hwnd;
+    initDialog();
+    return TRUE;
+
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      if (onOk()) {
+        EndDialog(hwnd, 1);
+        return TRUE;
+      }
+      return FALSE;
+    case IDCANCEL:
+      EndDialog(hwnd, 0);
+      return TRUE;
+    default:
+      return onCommand(LOWORD(wParam), HIWORD(wParam));
+    };
+
+  case WM_HELP:
+    return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+  }
+
+  return FALSE;
+}
+
+
+PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) {
+  page.dwSize = sizeof(page);
+  page.dwFlags = 0; // PSP_USECALLBACK;
+  page.hInstance = inst;
+  page.pszTemplate = id;
+  page.pfnDlgProc = staticPageProc;
+  page.lParam = (LPARAM)this;
+  page.pfnCallback = 0; // staticPageProc;
+}
+
+PropSheetPage::~PropSheetPage() {
+}
+
+
+BOOL CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg,
+				       WPARAM wParam, LPARAM lParam)
+{
+  if (msg == WM_INITDIALOG)
+    SetWindowLong(hwnd, GWL_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam);
+
+  LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+  if (!self) return FALSE;
+
+  return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+  switch (msg) {
+
+  case WM_INITDIALOG:
+    handle = hwnd;
+    initDialog();
+    return TRUE;
+
+  case WM_NOTIFY:
+    switch (((NMHDR*)lParam)->code) {
+    case PSN_APPLY:
+      onOk();
+      return FALSE;
+    };
+    return FALSE;
+
+  case WM_COMMAND:
+    return onCommand(LOWORD(wParam), HIWORD(wParam));
+
+  case WM_HELP:
+    return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+  }
+
+  return FALSE;
+}
+
+
+PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_)
+: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) {
+}
+
+PropSheet::~PropSheet() {
+}
+
+
+bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
+  if (alreadyShowing) return false;
+  alreadyShowing = true;
+  int count = pages.size();
+
+  HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count];
+  try {
+    // Create the PropertSheet page GDI objects.
+    std::list<PropSheetPage*>::iterator pspi;
+    int i = 0;
+    for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+      hpages[i] = CreatePropertySheetPage(&((*pspi)->page));
+      (*pspi)->setPropSheet(this);
+      i++;
+    }
+ 
+    // Initialise and create the PropertySheet itself
+    PROPSHEETHEADER header;
+    header.dwSize = PROPSHEETHEADER_V1_SIZE;
+    header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) /*| (showCtxtHelp ? 0 : PSH_NOCONTEXTHELP)*/;
+    header.hwndParent = owner;
+    header.hInstance = inst;
+    header.pszCaption = title.buf;
+    header.nPages = count;
+    header.nStartPage = 0;
+    header.phpage = hpages;
+    if (icon) {
+      header.hIcon = icon;
+      header.dwFlags |= PSH_USEHICON;
+    }
+
+    handle = (HWND)PropertySheet(&header);
+    if ((handle == 0) || (handle == (HWND)-1))
+      throw rdr::SystemException("PropertySheet failed", GetLastError());
+    centerWindow(handle, owner);
+    plog.info("created %lx", handle);
+
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+    // *** NOT TESTED
+    if (capture) {
+      plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
+      char* tmpdir = getenv("TEMP");
+      HDC dc = GetWindowDC(handle);
+      DeviceFrameBuffer fb(dc);
+      int i=0;
+      while (true) {
+        int id = PropSheet_IndexToId(handle, i);
+        if (!id) break;
+        PropSheet_SetCurSelByID(handle, id);
+        MSG msg;
+        while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
+          if (!PropSheet_IsDialogMessage(handle, &msg))
+            DispatchMessage(&msg);
+        }
+        fb.grabRect(fb.getRect());
+        char filename[256];
+        sprintf(filename, "%s\\capture%d.bmp", tmpdir, i);
+        saveBMP(filename, &fb);
+        i++;
+      }
+      ReleaseDC(handle, dc);
+    } else {
+#endif
+#endif
+      try {
+        if (owner)
+          EnableWindow(owner, FALSE);
+        // Run the PropertySheet
+        MSG msg;
+        while (GetMessage(&msg, 0, 0, 0)) {
+          if (!PropSheet_IsDialogMessage(handle, &msg))
+            DispatchMessage(&msg);
+          if (!PropSheet_GetCurrentPageHwnd(handle))
+            break;
+        }
+        if (owner)
+          EnableWindow(owner, TRUE);
+      } catch (...) {
+        if (owner)
+          EnableWindow(owner, TRUE);
+        throw;
+      }
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+    }
+#endif
+#endif
+
+    plog.info("finished %lx", handle);
+
+    DestroyWindow(handle);
+    handle = 0;
+    alreadyShowing = false;
+
+    // Clear up the pages' GDI objects
+    for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+      (*pspi)->setPropSheet(0);
+    delete [] hpages; hpages = 0;
+
+    return true;
+  } catch (rdr::Exception) {
+    alreadyShowing = false;
+
+    std::list<PropSheetPage*>::iterator pspi;
+    for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+      (*pspi)->setPropSheet(0);
+    delete [] hpages; hpages = 0;
+
+    throw;
+  }
+}
+
+void PropSheet::reInitPages() {
+  plog.debug("reInitPages %lx", handle);
+  std::list<PropSheetPage*>::iterator pspi;
+  for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+    if ((*pspi)->handle)
+      (*pspi)->initDialog();
+  }
+}
+
+bool PropSheet::commitPages() {
+  plog.debug("commitPages %lx", handle);
+  bool result = true;
+  std::list<PropSheetPage*>::iterator pspi;
+  for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+    if ((*pspi)->handle)
+      result = result && (*pspi)->onOk();
+  }
+  return result;
+}
+
+
+void PropSheetPage::setChanged(bool changed) {
+  if (propSheet) {
+    plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed);
+    if (changed)
+      PropSheet_Changed(propSheet->handle, handle);
+    else
+      PropSheet_UnChanged(propSheet->handle, handle);
+  }
+}
diff --git a/rfb_win32/Dialog.h b/rfb_win32/Dialog.h
new file mode 100644
index 0000000..d46133a
--- /dev/null
+++ b/rfb_win32/Dialog.h
@@ -0,0 +1,159 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_DIALOG_H__
+#define __RFB_WIN32_DIALOG_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <prsht.h>
+#include <list>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    // Dialog - A simple Win32 Dialog box.  A derived class of Dialog overrides the
+    // initDialog(), command() and ok() methods to take appropriate action.  A
+    // simple dialog box can be displayed by creating a Dialog object and calling
+    // show().
+
+    class Dialog {
+    public:
+
+      Dialog(HINSTANCE inst);
+      virtual ~Dialog();
+
+      // showDialog() displays the dialog box.  It returns when it has been dismissed,
+      // returning true if "OK" was pressed, false otherwise.  The resource
+      // argument identifies the dialog resource (often a MAKEINTRESOURCE macro
+      // expansion), and owner is an optional window handle - the corresponding
+      // window is disabled while the dialog box is displayed.
+
+      bool showDialog(const TCHAR* resource, HWND owner=0);
+
+      // initDialog() is called upon receipt of the WM_INITDIALOG message.
+
+      virtual void initDialog() {}
+
+      // onCommand() is called upon receipt of a WM_COMMAND message item other than IDOK
+      // or IDCANCEL.  It should return true if the command has been handled.
+
+      virtual bool onCommand(int item, int cmd) { return false; }
+
+      // onHelp() is called upon receipt of a WM_MENU message.  This indicates that
+      // context-specific help should be displayed, for a dialog control, for example.
+      // It should return true if the command has been handled.
+
+      virtual bool onHelp(int item) { return false; }
+
+      // onOk() is called when the OK button is pressed.  The hwnd argument is the
+      // dialog box's window handle.
+
+      virtual bool onOk() { return true; }
+
+      // Read the states of items
+      bool isItemChecked(int id);
+      int getItemInt(int id);
+      TCHAR* getItemString(int id); // Recipient owns string storage
+      
+      // Set the states of items
+      void setItemChecked(int id, bool state);
+      void setItemInt(int id, int value);
+      void setItemString(int id, const TCHAR* s);
+
+      // enableItem is used to grey out an item, making it inaccessible, or to
+      // re-enable it.
+      void enableItem(int id, bool state);
+
+    protected:
+      static BOOL CALLBACK staticDialogProc(HWND hwnd, UINT msg,
+			      WPARAM wParam, LPARAM lParam);
+      virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+      HINSTANCE inst;
+      HWND handle;
+      bool alreadyShowing;
+    };
+
+    // PropertySheetPage 
+    // Class used to define property pages within a PropertySheet.
+    // Each page is associated with a particular dialog resource, indicated by
+    // the "id" parameter supplied to the constructor.
+
+    class PropSheetPage;
+
+    class PropSheet {
+    public:
+      PropSheet(HINSTANCE inst, const TCHAR* title, std::list<PropSheetPage*> pages, HICON icon=0);
+      virtual ~PropSheet();
+
+      // Display the PropertySheet
+      bool showPropSheet(HWND owner, bool showApply = false, bool showCtxtHelp = false, bool capture=false);
+      
+      // Calls initDialog again for each page that has already had it called.
+      // Note: If a page hasn't been seen yet, it won't have been called.
+      // Note: This must only be called while the property sheet is visible.
+      void reInitPages();
+
+      // Calls onOk for each page that has had initDialog called, and returns
+      // false if any one of them returns false, or true otherwise.  ALL the
+      // onOk() methods will be called, even if one of them fails.
+      // Note: If a page hasn't been seen yet, it won't have been called.
+      // Note: This must only be called while the property sheet is visible.
+      bool commitPages();
+
+      friend class PropSheetPage;
+
+    protected:
+      HWND owner;
+      HICON icon;
+      std::list<PropSheetPage*> pages;
+      HINSTANCE inst;
+      TCharArray title;
+      HWND handle;
+      bool alreadyShowing;
+    };
+
+    class PropSheetPage : public Dialog {
+    public:
+      PropSheetPage(HINSTANCE inst, const TCHAR* id);
+      virtual ~PropSheetPage();
+
+      void setChanged(bool changed);
+
+      friend class PropSheet;
+
+    protected:
+      void setPropSheet(PropSheet* ps) {propSheet = ps;};
+      static BOOL CALLBACK staticPageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+      virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+      PROPSHEETPAGE page;
+      PropSheet* propSheet;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_DIALOG_H__
diff --git a/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h
new file mode 100644
index 0000000..645d0ee
--- /dev/null
+++ b/rfb_win32/IntervalTimer.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- IntervalTimer.h
+//
+// Simple wrapper for standard Win32 timers
+
+#ifndef __RFB_WIN32_INTERVAL_TIMER_H__
+#define __RFB_WIN32_INTERVAL_TIMER_H__
+
+namespace rfb {
+
+  namespace win32 {
+
+    struct IntervalTimer {
+      IntervalTimer(HWND hwnd_, int id_)
+        : active(false), hwnd(hwnd_), id(id_) {
+      }
+      IntervalTimer() : active(false), hwnd(0), id(0) {
+      }
+      ~IntervalTimer() {
+        stop();
+      }
+
+      void start(int interval_) {
+        if (!active || interval_ != interval) {
+          interval = interval_;
+          if (!SetTimer(hwnd, id, interval, 0))
+            throw rdr::SystemException("SetTimer", GetLastError());
+          active = true;
+        }
+      }
+      void stop() {
+        if (active)
+          KillTimer(hwnd, id);
+        active = false;
+      }
+
+      void setHWND(HWND hwnd_) {hwnd=hwnd_;}
+      void setId(int id_) {id = id_;}
+      int getId() const {return id;}
+      bool isActive() const {return active;}
+
+    private:
+      HWND hwnd;
+      int id;
+      bool active;
+      int interval;
+    };
+
+  }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INTERVAL_TIMER_H__
diff --git a/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx
new file mode 100644
index 0000000..0a48d60
--- /dev/null
+++ b/rfb_win32/LaunchProcess.cxx
@@ -0,0 +1,92 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- LaunchProcess.cxx
+
+#include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/Win32Util.h>
+
+#include <rfb/util.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+LaunchProcess::LaunchProcess(const TCHAR* exeName_, const TCHAR* params_)
+: exeName(tstrDup(exeName_)), params(tstrDup(params_)) {
+  memset(&procInfo, 0, sizeof(procInfo));
+}
+
+LaunchProcess::~LaunchProcess() {
+  await();
+}
+
+
+void LaunchProcess::start(HANDLE userToken) {
+  if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
+    return;
+  await();
+
+  // - Create storage for the process startup information
+  STARTUPINFO sinfo;
+  memset(&sinfo, 0, sizeof(sinfo));
+  sinfo.cb = sizeof(sinfo);
+
+  // - Concoct a suitable command-line
+  TCharArray exePath;
+  if (!tstrContains(exeName.buf, _T('\\'))) {
+    ModuleFileName filename;
+    TCharArray path; splitPath(filename.buf, &path.buf, 0);
+    exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];
+    _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);
+  } else {
+    exePath.buf = tstrDup(exeName.buf);
+  }
+
+  // - Start the VNC server
+  // Note: We specify the exe's precise path in the ApplicationName parameter,
+  //       AND include the name as the first part of the CommandLine parameter,
+  //       because CreateProcess doesn't make ApplicationName argv[0] in C programs.
+  TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
+  _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
+#ifdef _DEBUG
+  DWORD flags = CREATE_NEW_CONSOLE;
+#else
+  DWORD flags = CREATE_NO_WINDOW;
+#endif
+  BOOL success;
+  if (userToken)
+    success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+  else
+    success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+  if (!success)
+    throw rdr::SystemException("unable to launch process", GetLastError());
+
+  // Wait for it to finish initialising
+  WaitForInputIdle(procInfo.hProcess, 15000);
+}
+
+void LaunchProcess::await() {
+  if (!procInfo.hProcess)
+    return;
+  WaitForSingleObject(procInfo.hProcess, INFINITE);
+  CloseHandle(procInfo.hProcess);
+  CloseHandle(procInfo.hThread);
+  memset(&procInfo, 0, sizeof(procInfo));
+}
+
diff --git a/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h
new file mode 100644
index 0000000..6fd34e9
--- /dev/null
+++ b/rfb_win32/LaunchProcess.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- LaunchProcess.h
+
+// Helper class to launch a names process from the same directory as
+// the current process executable resides in.
+
+#ifndef __RFB_WIN32_LAUNCHPROCESS_H__
+#define __RFB_WIN32_LAUNCHPROCESS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class LaunchProcess {
+    public:
+      LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
+      ~LaunchProcess();
+
+      // If userToken is 0 then starts as current user, otherwise
+      // starts as the specified user.  userToken must be a primary token.
+      void start(HANDLE userToken);
+
+      // Wait for the process to quit, and close the handles to it.
+      void await();
+
+      PROCESS_INFORMATION procInfo;
+    protected:
+      TCharArray exeName;
+      TCharArray params;
+    };
+
+
+  };
+
+};
+
+#endif
diff --git a/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx
new file mode 100644
index 0000000..519d6ab
--- /dev/null
+++ b/rfb_win32/MsgWindow.cxx
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- MsgWindow.cxx
+
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <malloc.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("MsgWindow");
+
+//
+// -=- MsgWindowClass
+//
+
+class MsgWindowClass {
+public:
+  MsgWindowClass();
+  ~MsgWindowClass();
+  ATOM classAtom;
+  HINSTANCE instance;
+};
+
+LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+  LRESULT result;
+
+  if (msg == WM_CREATE)
+    SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+  else if (msg == WM_DESTROY)
+    SetWindowLong(wnd, GWL_USERDATA, 0);
+  MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);
+  if (!_this) {
+    vlog.info("null _this in %x, message %x", wnd, msg);
+    return SafeDefWindowProc(wnd, msg, wParam, lParam);
+  }
+
+  try {
+    result = _this->processMessage(msg, wParam, lParam);
+  } catch (rdr::Exception& e) {
+    vlog.error("untrapped: %s", e.str());
+  }
+
+  return result;
+};
+
+MsgWindowClass::MsgWindowClass() : classAtom(0) {
+  WNDCLASS wndClass;
+  wndClass.style = 0;
+  wndClass.lpfnWndProc = MsgWindowProc;
+  wndClass.cbClsExtra = 0;
+  wndClass.cbWndExtra = 0;
+  wndClass.hInstance = instance = GetModuleHandle(0);
+  wndClass.hIcon = 0;
+  wndClass.hCursor = 0;
+  wndClass.hbrBackground = 0;
+  wndClass.lpszMenuName = 0;
+  wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass");
+  classAtom = RegisterClass(&wndClass);
+  if (!classAtom) {
+    throw rdr::SystemException("unable to register MsgWindow window class", GetLastError());
+  }
+}
+
+MsgWindowClass::~MsgWindowClass() {
+  if (classAtom) {
+    UnregisterClass((const TCHAR*)classAtom, instance);
+  }
+}
+
+MsgWindowClass baseClass;
+
+//
+// -=- MsgWindow
+//
+
+MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) {
+  vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf));
+  handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED,
+    0, 0, 10, 10, 0, 0, baseClass.instance, this);
+  if (!handle) {
+    throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+  }
+  vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
+}
+
+MsgWindow::~MsgWindow() {
+  if (handle)
+    DestroyWindow(handle);
+  vlog.debug("destroyed window \"%s\" (%x)", (const char*)CStr(name.buf), handle); 
+}
+
+LRESULT
+MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  return SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+}
\ No newline at end of file
diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h
new file mode 100644
index 0000000..94baca3
--- /dev/null
+++ b/rfb_win32/MsgWindow.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- MsgWindow.h
+
+// Base-class for any hidden message-handling windows used in the rfb::win32
+// implementation.
+
+#ifndef __RFB_WIN32_MSG_WINDOW_H__
+#define __RFB_WIN32_MSG_WINDOW_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class MsgWindow {
+    public:
+      MsgWindow(const TCHAR* _name);
+      virtual ~MsgWindow();
+
+      const TCHAR* getName() {return name.buf;}
+      HWND getHandle() const {return handle;}
+
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+    protected:
+      TCharArray name;
+      HWND handle;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_MSG_WINDOW_H__
diff --git a/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx
new file mode 100644
index 0000000..1976098
--- /dev/null
+++ b/rfb_win32/OSVersion.cxx
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- OSVersion.cxx
+
+#include <rfb_win32/OSVersion.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+OSVersionInfo::OSVersionInfo() {
+  // Get OS Version Info
+  ZeroMemory(static_cast<OSVERSIONINFO*>(this), sizeof(this));
+  dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  if (!GetVersionEx(this))
+    throw rdr::SystemException("unable to get system version info", GetLastError());
+
+  // Set the special extra flags
+  isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT;
+  isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
+
+  cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) &&
+    ((_tcscmp(szCSDVersion, _T("")) == 0) ||
+     (_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) ||
+     (_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0));
+
+}
+
+OSVersionInfo rfb::win32::osVersion;
diff --git a/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h
new file mode 100644
index 0000000..1d52943
--- /dev/null
+++ b/rfb_win32/OSVersion.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- OSVersion.h
+
+// Operating system version info.
+// GetVersionInfo is called once at process initialisation, and any
+// extra flags (such as isWinNT) are calculated and saved at that
+// point.  It is assumed that the OS Version seldom changes during a
+// program's execution...
+
+#ifndef __RFB_WIN32_OS_VERSION_H__
+#define __RFB_WIN32_OS_VERSION_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    extern struct OSVersionInfo : OSVERSIONINFO {
+      OSVersionInfo();
+
+      // Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)?
+      bool isPlatformNT;
+      // Is one of the Windows family?
+      bool isPlatformWindows;
+
+      // Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)?
+      bool cannotSwitchDesktop;
+
+    } osVersion;
+
+  };
+
+};
+
+#endif // __RFB_WIN32_OS_VERSION_H__
diff --git a/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx
new file mode 100644
index 0000000..fcb309b
--- /dev/null
+++ b/rfb_win32/RegConfig.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- RegConfig.cxx
+
+#include <malloc.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/HexOutStream.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("RegConfig");
+
+
+class rfb::win32::RegReaderThread : public Thread {
+public:
+  RegReaderThread(RegistryReader& reader, const HKEY key);
+  ~RegReaderThread();
+  virtual void run();
+  virtual Thread* join();
+protected:
+  RegistryReader& reader;
+  RegKey key;
+  HANDLE event;
+};
+
+RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) {
+}
+
+RegReaderThread::~RegReaderThread() {
+}
+
+void
+RegReaderThread::run() {
+  vlog.debug("RegReaderThread started");
+  while (key) {
+    // - Wait for changes
+    vlog.debug("waiting for changes");
+    key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+
+    // - Load settings
+    RegistryReader::loadRegistryConfig(key);
+
+    // - Notify specified thread of changes
+    if (reader.notifyThread)
+      PostThreadMessage(reader.notifyThread->getThreadId(),
+        reader.notifyMsg.message, 
+        reader.notifyMsg.wParam, 
+        reader.notifyMsg.lParam);
+    else if (reader.notifyWindow)
+      PostMessage(reader.notifyWindow,
+        reader.notifyMsg.message, 
+        reader.notifyMsg.wParam, 
+        reader.notifyMsg.lParam);
+  }
+}
+
+Thread*
+RegReaderThread::join() {
+  RegKey old_key = key;
+  key.close();
+  if ((HKEY)old_key) {
+    // *** Closing the key doesn't always seem to work
+    //     Writing to it always will, instead...
+    vlog.debug("closing key");
+    old_key.setString(_T("dummy"), _T(""));
+  }
+  return Thread::join();
+}
+
+
+RegistryReader::RegistryReader() : thread(0), notifyThread(0) {
+  memset(&notifyMsg, 0, sizeof(notifyMsg));
+}
+
+RegistryReader::~RegistryReader() {
+  if (thread) delete thread->join();
+}
+
+bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) {
+  if (thread) delete thread->join();
+  thread = 0;
+  
+  RegKey key;
+  try {
+    key.createKey(rootkey, keyname);
+    loadRegistryConfig(key);
+  } catch (rdr::Exception& e) {
+    vlog.debug(e.str());
+    return false;
+  }
+  thread = new RegReaderThread(*this, key);
+  if (thread) thread->start();
+  return true;
+}
+
+void
+RegistryReader::loadRegistryConfig(RegKey& key) {
+  DWORD i = 0;
+  try {
+    while (1) {
+      TCharArray name = tstrDup(key.getValueName(i++));
+      if (!name.buf) break;
+      TCharArray value = key.getRepresentation(name.buf);
+      if (!value.buf || !Configuration::setParam(CStr(name.buf), CStr(value.buf)))
+        vlog.info("unable to process %s", CStr(name.buf));
+    }
+  } catch (rdr::SystemException& e) {
+    if (e.err != 6)
+      vlog.error(e.str());
+  }
+}
+
+bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+  notifyMsg.message = winMsg;
+  notifyMsg.wParam = wParam;
+  notifyMsg.lParam = lParam;
+  notifyThread = thread;
+  notifyWindow = 0;
+  return true;
+}
+
+bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+  notifyMsg.message = winMsg;
+  notifyMsg.wParam = wParam;
+  notifyMsg.lParam = lParam;
+  notifyWindow = window;
+  notifyThread = 0;
+  return true;
+}
+
diff --git a/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h
new file mode 100644
index 0000000..3fced85
--- /dev/null
+++ b/rfb_win32/RegConfig.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_REG_CONFIG_H__
+#define __RFB_WIN32_REG_CONFIG_H__
+
+#include <rfb/Threading.h>
+#include <rfb/Configuration.h>
+
+#include <rfb_win32/Registry.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class RegistryReader {
+    public:
+      RegistryReader();
+      ~RegistryReader();
+      bool setKey(const HKEY rootkey, const TCHAR* keyname);
+      bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+      bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+      static void loadRegistryConfig(RegKey& key);
+    protected:
+      friend class RegReaderThread;
+      Thread* thread;
+      Thread* notifyThread;
+      HWND notifyWindow;
+      MSG notifyMsg;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx
new file mode 100644
index 0000000..de9238f
--- /dev/null
+++ b/rfb_win32/Registry.cxx
@@ -0,0 +1,272 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Registry.cxx
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/Registry.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/HexOutstream.h>
+#include <rdr/HexInStream.h>
+#include <rfb_win32/Security.h>
+
+#include <stdlib.h>
+
+// These flags are required to control access control inheritance,
+// but are not defined by VC6's headers.  These definitions comes
+// from the Microsoft Platform SDK.
+#ifndef PROTECTED_DACL_SECURITY_INFORMATION
+#define PROTECTED_DACL_SECURITY_INFORMATION     (0x80000000L)
+#endif
+#ifndef UNPROTECTED_DACL_SECURITY_INFORMATION
+#define UNPROTECTED_DACL_SECURITY_INFORMATION     (0x20000000L)
+#endif
+
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("Registry");
+
+
+RegKey::RegKey() : key(0), freeKey(false), valueNameBufLen(0) {}
+
+RegKey::RegKey(const HKEY k) : key(0), freeKey(false), valueNameBufLen(0) {
+  LONG result = RegOpenKeyEx(k, 0, 0, KEY_ALL_ACCESS, &key);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegOpenKeyEx(HKEY)", result);
+  vlog.debug("duplicated %x to %x", k, key);
+  freeKey = true;
+}
+
+RegKey::RegKey(const RegKey& k) : key(0), freeKey(false), valueNameBufLen(0) {
+  LONG result = RegOpenKeyEx(k.key, 0, 0, KEY_ALL_ACCESS, &key);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegOpenKeyEx(RegKey&)", result);
+  vlog.debug("duplicated %x to %x", k.key, key);
+  freeKey = true;
+}
+
+RegKey::~RegKey() {
+  close();
+}
+
+
+void RegKey::setHKEY(HKEY k, bool fK) {
+  close();
+  freeKey = fK;
+  key = k;
+}
+
+
+bool RegKey::createKey(const RegKey& root, const TCHAR* name) {
+  close();
+  LONG result = RegCreateKey(root.key, name, &key);
+  if (result != ERROR_SUCCESS) {
+    vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result);
+    throw rdr::SystemException("RegCreateKeyEx", result);
+  }
+  freeKey = true;
+  return true;
+}
+
+void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) {
+  close();
+  LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegOpenKeyEx (open)", result);
+  freeKey = true;
+}
+
+void RegKey::setDACL(const PACL acl, bool inherit) {
+  DWORD result;
+  typedef DWORD (WINAPI *_SetSecurityInfo_proto) (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
+  DynamicFn<_SetSecurityInfo_proto> _SetSecurityInfo(_T("advapi32.dll"), "SetSecurityInfo");
+  if (!_SetSecurityInfo.isValid())
+    throw rdr::SystemException("RegKey::setDACL failed", ERROR_CALL_NOT_IMPLEMENTED);
+  if ((result = (*_SetSecurityInfo)(key, SE_REGISTRY_KEY,
+    DACL_SECURITY_INFORMATION |
+    (inherit ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION),
+    0, 0, acl, 0)) != ERROR_SUCCESS)
+    throw rdr::SystemException("RegKey::setDACL failed", result);
+}
+
+void RegKey::close() {
+  if (freeKey) {
+    RegCloseKey(key);
+    key = 0;
+  }
+}
+
+void RegKey::deleteKey(const TCHAR* name) const {
+  LONG result = RegDeleteKey(key, name);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegDeleteKey", result);
+}
+
+void RegKey::deleteValue(const TCHAR* name) const {
+  LONG result = RegDeleteValue(key, name);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegDeleteValue", result);
+}
+
+void RegKey::awaitChange(bool watchSubTree, DWORD filter) const {
+  LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegNotifyChangeKeyValue", result);
+}
+
+
+RegKey::operator HKEY() const {return key;}
+
+
+void RegKey::setExpandString(const TCHAR* valname, const TCHAR* value) const {
+  LONG result = RegSetValueEx(key, valname, 0, REG_EXPAND_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+  if (result != ERROR_SUCCESS) throw rdr::SystemException("setExpandString", result);
+}
+
+void RegKey::setString(const TCHAR* valname, const TCHAR* value) const {
+  LONG result = RegSetValueEx(key, valname, 0, REG_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+  if (result != ERROR_SUCCESS) throw rdr::SystemException("setString", result);
+}
+
+void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const {
+  LONG result = RegSetValueEx(key, valname, 0, REG_BINARY, (const BYTE*)value, length);
+  if (result != ERROR_SUCCESS) throw rdr::SystemException("setBinary", result);
+}
+
+void RegKey::setInt(const TCHAR* valname, int value) const {
+  LONG result = RegSetValueEx(key, valname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
+  if (result != ERROR_SUCCESS) throw rdr::SystemException("setInt", result);
+}
+
+void RegKey::setBool(const TCHAR* valname, bool value) const {
+  setInt(valname, value ? 1 : 0);
+}
+
+TCHAR* RegKey::getString(const TCHAR* valname) const {return getRepresentation(valname);}
+TCHAR* RegKey::getString(const TCHAR* valname, const TCHAR* def) const {
+  try {
+    return getString(valname);
+  } catch(rdr::Exception) {
+    return tstrDup(def);
+  }
+}
+
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const {
+  TCharArray hex = getRepresentation(valname);
+  if (!rdr::HexInStream::hexStrToBin(CStr(hex.buf), (char**)data, length))
+    throw rdr::Exception("getBinary failed");
+}
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const {
+  try {
+    getBinary(valname, data, length);
+  } catch(rdr::Exception) {
+    if (deflen) {
+      *data = new char[deflen];
+      memcpy(*data, def, deflen);
+    } else
+      *data = 0;
+    *length = deflen;
+  }
+}
+
+int RegKey::getInt(const TCHAR* valname) const {
+  TCharArray tmp = getRepresentation(valname);
+  return _ttoi(tmp.buf);
+}
+int RegKey::getInt(const TCHAR* valname, int def) const {
+  try {
+    return getInt(valname);
+  } catch(rdr::Exception) {
+    return def;
+  }
+}
+
+bool RegKey::getBool(const TCHAR* valname) const {
+  return getInt(valname) > 0;
+}
+bool RegKey::getBool(const TCHAR* valname, bool def) const {
+  return getInt(valname, def ? 1 : 0) > 0;
+}
+
+TCHAR* RegKey::getRepresentation(const TCHAR* valname) const {
+  DWORD type, length;
+  LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("get registry value length", result);
+  CharArray data(length);
+  result = RegQueryValueEx(key, valname, 0, &type, (BYTE*)data.buf, &length);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("get registry value", result);
+
+  switch (type) {
+  case REG_BINARY:
+    {
+      TCharArray hex = rdr::HexOutStream::binToHexStr(data.buf, length);
+      return hex.takeBuf();
+    }
+  case REG_SZ:
+    if (length) {
+      // We must terminate the string, just to be sure.  Stupid Win32...
+      int len = length/sizeof(TCHAR);
+      TCharArray str(len+1);
+      memcpy(str.buf, data.buf, length);
+      str.buf[len] = 0;
+      return str.takeBuf();
+    } else {
+      return tstrDup(_T(""));
+    }
+  case REG_DWORD:
+    {
+      TCharArray tmp(16);
+      _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
+      return tmp.takeBuf();
+    }
+  default:
+    throw rdr::Exception("unsupported registry type");
+  }
+}
+
+bool RegKey::isValue(const TCHAR* valname) const {
+  try {
+    TCharArray tmp = getRepresentation(valname);
+    return true;
+  } catch(rdr::Exception) {
+    return false;
+  }
+}
+
+const TCHAR* RegKey::getValueName(int i) {
+  DWORD maxValueNameLen;
+  LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegQueryInfoKey", result);
+  if (valueNameBufLen < maxValueNameLen + 1) {
+    valueNameBufLen = maxValueNameLen + 1;
+    delete [] valueName.buf;
+    valueName.buf = new TCHAR[valueNameBufLen];
+  }
+  DWORD length = valueNameBufLen;
+  result = RegEnumValue(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+  if (result == ERROR_NO_MORE_ITEMS) return 0;
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegEnumValue", result);
+  return valueName.buf;
+}
diff --git a/rfb_win32/Registry.h b/rfb_win32/Registry.h
new file mode 100644
index 0000000..1998c49
--- /dev/null
+++ b/rfb_win32/Registry.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+// -=- Registry.h
+
+// C++ wrappers around the Win32 Registry APIs
+
+#ifndef __RFB_WIN32_REGISTRY_H__
+#define __RFB_WIN32_REGISTRY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/Security.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class RegKey {
+    public:
+      // No key open
+      RegKey();
+
+      // Duplicate the specified existing key
+      RegKey(const HKEY k);
+      RegKey(const RegKey& k);
+
+      // Calls close() internally
+      ~RegKey();
+
+      void setHKEY(HKEY key, bool freeKey);
+    protected:
+      HKEY operator=(const RegKey& k);
+      HKEY operator=(HKEY k);
+    public:
+
+      // Returns true if key was created, false if already existed
+      bool createKey(const RegKey& root, const TCHAR* name);
+
+      // Opens key if it exists, or raises an exception if not
+      void openKey(const RegKey& root, const TCHAR* name, bool readOnly=false);
+
+      // Set the (discretionary) access control list for the key
+      void setDACL(const PACL acl, bool inheritFromParent=true);
+
+      // Closes current key, if required
+      void close();
+
+      // Delete a subkey/value
+      void deleteKey(const TCHAR* name) const;
+      void deleteValue(const TCHAR* name) const;
+
+
+      void awaitChange(bool watchSubTree, DWORD filter) const;
+
+      void setExpandString(const TCHAR* valname, const TCHAR* s) const;
+      void setString(const TCHAR* valname, const TCHAR* s) const;
+      void setBinary(const TCHAR* valname, const void* data, int length) const;
+      void setInt(const TCHAR* valname, int i) const;
+      void setBool(const TCHAR* valname, bool b) const;
+
+      TCHAR* getString(const TCHAR* valname) const;
+      TCHAR* getString(const TCHAR* valname, const TCHAR* def) const;
+
+      void getBinary(const TCHAR* valname, void** data, int* length) const;
+      void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const;
+
+      int getInt(const TCHAR* valname) const;
+      int getInt(const TCHAR* valname, int def) const;
+
+      bool getBool(const TCHAR* valname) const;
+      bool getBool(const TCHAR* valname, bool def) const;
+
+      TCHAR* getRepresentation(const TCHAR* valname) const;
+
+      bool isValue(const TCHAR* valname) const;
+
+      // Get the name of value number "i"
+      // If there are fewer than "i" values then return 0
+      // NAME IS OWNED BY RegKey OBJECT!
+      const TCHAR* getValueName(int i);
+
+      operator HKEY() const;
+    protected:
+      HKEY key;
+      bool freeKey;
+      TCharArray valueName;
+      DWORD valueNameBufLen;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
new file mode 100644
index 0000000..6fa3ff0
--- /dev/null
+++ b/rfb_win32/SDisplay.cxx
@@ -0,0 +1,612 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- SDisplay.cxx
+//
+// The SDisplay class encapsulates a particular system display.
+
+#include <assert.h>
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/osVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/CleanDesktop.h>
+
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#include <rfb/Configuration.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplay");
+
+// - SDisplay-specific configuration options
+
+BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks",
+  "Set hooks in the operating system to capture display updates more efficiently", true);
+BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs",
+  "Disable local keyboard and pointer input while the server is in use", false);
+StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction",
+  "Action to perform when all clients have disconnected.  (None, Lock, Logoff)", "None");
+
+BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
+  "Remove the desktop wallpaper when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
+  "Remove the desktop background pattern when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
+  "Disable desktop user interface effects when the server is in use.", false);
+
+
+// - WM_TIMER ID values
+
+#define TIMER_CURSOR 1
+#define TIMER_UPDATE 2
+#define TIMER_UPDATE_AND_POLL 3
+
+
+// -=- Polling settings
+
+const int POLLING_SEGMENTS = 16;
+
+const int FG_POLLING_FPS = 20;
+const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS;
+const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+const int BG_POLLING_FS_INTERVAL = 5000;
+const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplayCore
+//
+
+// The SDisplay Core object is created by SDisplay's start() method
+// and deleted by its stop() method.
+// The Core must be created in the current input desktop in order
+// to operate - SDisplay is responsible for ensuring that.
+// The structures contained in the Core are manipulated directly
+// by the SDisplay, which is also responsible for detecting when
+// a desktop-switch is required.
+
+class rfb::win32::SDisplayCore : public MsgWindow {
+public:
+  SDisplayCore(SDisplay* display);
+  ~SDisplayCore();
+
+  void setPixelBuffer(DeviceFrameBuffer* pb_);
+
+  bool isRestartRequired();
+
+  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+  // -=- Timers
+  IntervalTimer pollTimer;
+  IntervalTimer cursorTimer;
+
+  // -=- Input handling
+  rfb::win32::SPointer ptr;
+  rfb::win32::SKeyboard kbd;
+  rfb::win32::Clipboard clipboard;
+
+  // -=- Hook handling objects used outside thread run() method
+  WMCopyRect wm_copyrect;
+  WMPoller wm_poller;
+  WMCursor cursor;
+  WMMonitor wm_monitor;
+  WMHooks wm_hooks;
+  WMBlockInput wm_input;
+
+  // -=- Tidying the desktop
+  CleanDesktop cleanDesktop;
+  bool isWallpaperRemoved;
+  bool isPatternRemoved;
+  bool areEffectsDisabled;
+
+  // -=- Full screen polling
+  int poll_next_y;
+  int poll_y_increment;
+
+  // Are we using hooks?
+  bool use_hooks;
+  bool using_hooks;
+
+  // State of the display object
+  SDisplay* display;
+};
+
+SDisplayCore::SDisplayCore(SDisplay* display_)
+: MsgWindow(_T("SDisplayCore")), display(display_),
+  using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks),
+  isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper),
+  isPatternRemoved(rfb::win32::SDisplay::removePattern),
+  areEffectsDisabled(rfb::win32::SDisplay::disableEffects),
+  pollTimer(getHandle(), TIMER_UPDATE_AND_POLL),
+  cursorTimer(getHandle(), TIMER_CURSOR) {
+  setPixelBuffer(display->pb);
+}
+
+SDisplayCore::~SDisplayCore() {
+}
+
+void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) {
+  poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+  poll_next_y = display->screenRect.tl.y;
+  wm_hooks.setClipRect(display->screenRect);
+  wm_copyrect.setClipRect(display->screenRect);
+  wm_poller.setClipRect(display->screenRect);
+}
+
+
+bool SDisplayCore::isRestartRequired() {
+  // - We must restart the SDesktop if:
+  // 1. We are no longer in the input desktop.
+  // 2. The use_hooks setting has changed.
+
+  // - Check that we are in the input desktop
+  if (rfb::win32::desktopChangeRequired())
+    return true;
+
+  // - Check that the hooks setting hasn't changed
+  // NB: We can't just check using_hooks because that can be false
+  // because they failed, even though use_hooks is true!
+  if (use_hooks != rfb::win32::SDisplay::use_hooks)
+    return true;
+
+  // - Check that the desktop optimisation settings haven't changed
+  //   This isn't very efficient, but it shouldn't change very often!
+  if ((isWallpaperRemoved != rfb::win32::SDisplay::removeWallpaper) ||
+      (isPatternRemoved != rfb::win32::SDisplay::removePattern) ||
+      (areEffectsDisabled != rfb::win32::SDisplay::disableEffects))
+    return true;
+
+  return false;
+}
+
+LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+
+  case WM_TIMER:
+
+    if (display->server && display->server->clientsReadyForUpdate()) {
+
+      // - Check that the SDesktop doesn't need restarting
+      if (isRestartRequired()) {
+        display->restart();
+        return 0;
+      }
+    
+      // - Action depends on the timer message type
+      switch (wParam) {
+
+        // POLL THE SCREEN
+      case TIMER_UPDATE_AND_POLL:
+        // Handle window dragging, polling of consoles, etc.
+        while (wm_poller.processEvent()) {}
+
+        // Poll the next strip of the screen (in Screen coordinates)
+        {
+          Rect pollrect = display->screenRect;
+          if (poll_next_y >= pollrect.br.y) {
+            // Yes.  Reset the counter and return
+            poll_next_y = pollrect.tl.y;
+          } else {
+            // No.  Poll the next section
+            pollrect.tl.y = poll_next_y;
+            poll_next_y += poll_y_increment;
+            pollrect.br.y = min(poll_next_y, pollrect.br.y);
+            display->add_changed(pollrect);
+          }
+        }
+        break;
+
+      case TIMER_CURSOR:
+        display->triggerUpdate();
+        break;
+
+      };
+
+    }
+    return 0;
+
+  };
+
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplay
+//
+
+// -=- Constructor/Destructor
+
+SDisplay::SDisplay(const TCHAR* devName)
+  : server(0), change_tracker(true), pb(0),
+    deviceName(tstrDup(devName)), device(0), releaseDevice(false),
+    core(0), statusLocation(0)
+{
+  updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+SDisplay::~SDisplay()
+{
+  // XXX when the VNCServer has been deleted with clients active, stop()
+  // doesn't get called - this ought to be fixed in VNCServerST.  In any event,
+  // we should never call any methods on VNCServer once we're being deleted.
+  // This is because it is supposed to be guaranteed that the SDesktop exists
+  // throughout the lifetime of the VNCServer.  So if we're being deleted, then
+  // the VNCServer ought not to exist and therefore we shouldn't invoke any
+  // methods on it.  Setting server to zero here ensures that stop() doesn't
+  // call setPixelBuffer(0) on the server.
+  server = 0;
+  if (core) stop();
+}
+
+
+// -=- SDesktop interface
+
+void SDisplay::start(VNCServer* vs)
+{
+  vlog.debug("starting");
+  server = vs;
+
+  // Switch to the current input desktop
+  // ***
+  if (rfb::win32::desktopChangeRequired()) {
+    if (!rfb::win32::changeDesktop())
+      throw rdr::Exception("unable to switch into input desktop");
+  }
+
+  // Clear the change tracker
+  change_tracker.clear();
+
+  // Create the framebuffer object
+  recreatePixelBuffer();
+
+  // Create the SDisplayCore
+  core = new SDisplayCore(this);
+  assert(core);
+
+  // Start display monitor and clipboard handler
+  core->wm_monitor.setNotifier(this);
+  core->clipboard.setNotifier(this);
+
+  // Apply desktop optimisations
+  if (removePattern)
+    core->cleanDesktop.disablePattern();
+  if (removeWallpaper)
+    core->cleanDesktop.disableWallpaper();
+  if (disableEffects)
+    core->cleanDesktop.disableEffects();
+
+  // Start hooks
+  core->wm_hooks.setClipRect(screenRect);
+  if (core->use_hooks) {
+    // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
+    core->using_hooks = core->wm_hooks.setUpdateTracker(this);
+    if (!core->using_hooks)
+      vlog.debug("hook subsystem failed to initialise");
+  }
+
+  // Set up timers
+  core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
+  core->cursorTimer.start(10);
+
+  // Register an interest in faked copyrect events
+  core->wm_copyrect.setUpdateTracker(&change_tracker);
+  core->wm_copyrect.setClipRect(screenRect);
+
+  // Polling of particular windows on the desktop
+  core->wm_poller.setUpdateTracker(&change_tracker);
+  core->wm_poller.setClipRect(screenRect);
+
+  vlog.debug("started");
+
+  if (statusLocation) *statusLocation = true;
+}
+
+void SDisplay::stop()
+{
+  vlog.debug("stopping");
+  if (core) {
+    // If SDisplay was actually active then perform the disconnect action
+    CharArray action = disconnectAction.getData();
+    if (stricmp(action.buf, "Logoff") == 0) {
+      ExitWindowsEx(EWX_LOGOFF, 0);
+    } else if (stricmp(action.buf, "Lock") == 0) {
+      typedef BOOL (WINAPI *_LockWorkStation_proto)();
+      DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+      if (_LockWorkStation.isValid())
+        (*_LockWorkStation)();
+      else
+        ExitWindowsEx(EWX_LOGOFF, 0);
+    }
+  }
+  delete core;
+  core = 0;
+  delete pb;
+  pb = 0;
+  if (device) {
+    if (releaseDevice)
+      ReleaseDC(0, device);
+    else
+      DeleteDC(device);
+  }
+  device = 0;
+  if (server)
+    server->setPixelBuffer(0);
+
+  server = 0;
+  vlog.debug("stopped");
+
+  if (statusLocation) *statusLocation = false;
+}
+
+void SDisplay::restart() {
+  vlog.debug("restarting");
+  // Close down the hooks
+  delete core;
+  core = 0;
+  try {
+    // Re-start the hooks if possible
+    start(server);
+    vlog.debug("restarted");
+  } catch (rdr::Exception& e) {
+    // If start() fails then we MUST disconnect all clients,
+    // to cause the server to stop using the desktop.
+    // Otherwise, the SDesktop is in an inconsistent state
+    // and the server will crash
+    server->closeClients(e.str());
+  }
+}
+
+
+void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) {
+  if (pb->getRect().contains(pos)) {
+    Point screenPos = pos.translate(screenRect.tl);
+    core->ptr.pointerEvent(screenPos, buttonmask);
+  }
+}
+
+void SDisplay::keyEvent(rdr::U32 key, bool down) {
+  core->kbd.keyEvent(key, down);
+}
+
+void SDisplay::clientCutText(const char* text, int len) {
+  CharArray clip_sz(len+1);
+  memcpy(clip_sz.buf, text, len);
+  clip_sz.buf[len] = 0;
+  core->clipboard.setClipText(clip_sz.buf);
+}
+
+
+void SDisplay::framebufferUpdateRequest()
+{
+  triggerUpdate();
+}
+
+Point SDisplay::getFbSize() {
+  bool startAndStop = !core;
+  // If not started, do minimal initialisation to get desktop size.
+  if (startAndStop) recreatePixelBuffer();
+  Point result = Point(pb->width(), pb->height());
+  // Destroy the initialised structures.
+  if (startAndStop) stop();
+  return result;
+}
+
+
+void
+SDisplay::add_changed(const Region& rgn) {
+  change_tracker.add_changed(rgn);
+  triggerUpdate();
+}
+
+void
+SDisplay::add_copied(const Region& dest, const Point& delta) {
+  change_tracker.add_copied(dest, delta);
+  triggerUpdate();
+}
+
+
+void
+SDisplay::notifyClipboardChanged(const char* text, int len) {
+  vlog.debug("clipboard text changed");
+  if (server)
+    server->serverCutText(text, len);
+}
+
+
+void
+SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) {
+  switch (evt) {
+  case WMMonitor::Notifier::DisplaySizeChanged:
+    vlog.debug("desktop size changed");
+    recreatePixelBuffer();
+    break;
+  case WMMonitor::Notifier::DisplayPixelFormatChanged:
+    vlog.debug("desktop format changed");
+    recreatePixelBuffer();
+    break;
+  case WMMonitor::Notifier::DisplayColourMapChanged:
+    vlog.debug("desktop colourmap changed");
+    pb->updateColourMap();
+    if (server)
+      server->setColourMapEntries();
+    break;
+  default:
+    vlog.error("unknown display event received");
+  }
+}
+
+bool
+SDisplay::processEvent(HANDLE event) {
+  if (event == updateEvent) {
+    vlog.info("processEvent");
+    ResetEvent(updateEvent);
+
+    // - If the SDisplay isn't even started then quit now
+    if (!core) {
+      vlog.error("not start()ed");
+      return true;
+    }
+
+    // - Ensure that the disableLocalInputs flag is respected
+    core->wm_input.blockInputs(SDisplay::disableLocalInputs);
+
+    // - Only process updates if the server is ready
+    if (server && server->clientsReadyForUpdate()) {
+      bool try_update = false;
+
+      // - Check that the SDesktop doesn't need restarting
+      if (core->isRestartRequired()) {
+        restart();
+        return true;
+      }
+    
+      // *** window dragging can be improved - more frequent, more cunning about updates
+      while (core->wm_copyrect.processEvent()) {}
+        
+      // Ensure the cursor is up to date
+      WMCursor::Info info = core->cursor.getCursorInfo();
+      if (old_cursor != info) {
+        // Update the cursor shape if the visibility has changed
+        bool set_cursor = info.visible != old_cursor.visible;
+        // OR if the cursor is visible and the shape has changed.
+        set_cursor |= info.visible && (old_cursor.cursor != info.cursor);
+
+        // Update the cursor shape
+        if (set_cursor)
+          pb->setCursor(info.visible ? info.cursor : 0, server);
+
+        // Update the cursor position
+        // NB: First translate from Screen coordinates to Desktop
+        Point desktopPos = info.position.translate(screenRect.tl.negate());
+        server->setCursorPos(desktopPos.x, desktopPos.y);
+        try_update = true;
+
+        old_cursor = info;
+      }
+
+      // Flush any changes to the server
+      try_update = flushChangeTracker() || try_update;
+      if (try_update)
+        server->tryUpdate();
+    }
+  } else {
+    CloseHandle(event);
+    return false;
+  }
+  return true;
+}
+
+
+// -=- Protected methods
+
+void
+SDisplay::recreatePixelBuffer() {
+  vlog.debug("attaching to device %s", deviceName);
+
+  // Open the specified display device
+  HDC new_device;
+  if (deviceName.buf) {
+    new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL);
+    releaseDevice = false;
+  } else {
+    // If no device is specified, open entire screen.
+    // Doing this with CreateDC creates problems on multi-monitor systems.
+    new_device = ::GetDC(0);
+    releaseDevice = true;
+  }
+  if (!new_device)
+    throw SystemException("cannot open the display", GetLastError());
+
+  // Get the coordinates of the entire virtual display
+  Rect newScreenRect;
+  {
+    WindowDC rootDC(0);
+    RECT r;
+    if (!GetClipBox(rootDC, &r))
+      throw rdr::SystemException("GetClipBox", GetLastError());
+    newScreenRect = Rect(r.left, r.top, r.right, r.bottom);
+  }
+
+  // Create a DeviceFrameBuffer attached to it
+  DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
+
+  // Has anything actually changed about the screen or the buffer?
+  if (!pb ||
+      (!newScreenRect.equals(screenRect)) ||
+      (!new_buffer->getPF().equal(pb->getPF())))
+  {
+    // Yes.  Update the buffer state.
+    screenRect = newScreenRect;
+    vlog.debug("creating pixel buffer for device");
+
+    // Flush any existing changes to the server
+    flushChangeTracker();
+
+    // Replace the old PixelBuffer
+    if (pb) delete pb;
+    if (device) DeleteDC(device);
+    pb = new_buffer;
+    device = new_device;
+
+    // Initialise the pixels
+    pb->grabRegion(pb->getRect());
+
+    // Prevent future grabRect operations from throwing exceptions
+    pb->setIgnoreGrabErrors(true);
+
+    // Update the SDisplayCore if required
+    if (core)
+      core->setPixelBuffer(pb);
+
+    // Inform the server of the changes
+    if (server)
+      server->setPixelBuffer(pb);
+
+  } else {
+    delete new_buffer;
+    DeleteDC(new_device);
+  }
+}
+
+bool SDisplay::flushChangeTracker() {
+  if (change_tracker.is_empty())
+    return false;
+  // Translate the update coordinates from Screen coords to Desktop
+  change_tracker.translate(screenRect.tl.negate());
+  // Flush the updates through
+  change_tracker.get_update(*server);
+  change_tracker.clear();
+  return true;
+}
+
+void SDisplay::triggerUpdate() {
+  if (core)
+    SetEvent(updateEvent);
+}
diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h
new file mode 100644
index 0000000..c4c08bf
--- /dev/null
+++ b/rfb_win32/SDisplay.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- SDisplay.h
+//
+// The SDisplay class encapsulates a system display.
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_SDISPLAY_H__
+#define __RFB_SDISPLAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/SDesktop.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+#include <winsock2.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/SocketManager.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    //
+    // -=- SDisplay
+    //
+
+    class SDisplayCore;
+
+    class SDisplay : public SDesktop,
+      WMMonitor::Notifier,
+      Clipboard::Notifier,
+      UpdateTracker,
+      public SocketManager::EventHandler
+    {
+    public:
+      SDisplay(const TCHAR* device=0);
+      virtual ~SDisplay();
+
+      // -=- SDesktop interface
+
+      virtual void start(VNCServer* vs);
+      virtual void stop();
+      virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+      virtual void keyEvent(rdr::U32 key, bool down);
+      virtual void clientCutText(const char* str, int len);
+      virtual void framebufferUpdateRequest();
+      virtual Point getFbSize();
+
+      // -=- UpdateTracker
+
+      virtual void add_changed(const Region& rgn);
+      virtual void add_copied(const Region& dest, const Point& delta);
+
+      // -=- Clipboard
+      
+      virtual void notifyClipboardChanged(const char* text, int len);
+
+      // -=- Display events
+      
+      virtual void notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt);
+
+      // -=- EventHandler interface
+
+      HANDLE getUpdateEvent() {return updateEvent;}
+      virtual bool processEvent(HANDLE event);
+
+      // -=- Notification of whether or not SDisplay is started
+
+      void setStatusLocation(bool* status) {statusLocation = status;}
+
+      friend class SDisplayCore;
+
+      static BoolParameter use_hooks;
+      static BoolParameter disableLocalInputs;
+      static StringParameter disconnectAction;
+      static BoolParameter removeWallpaper;
+      static BoolParameter removePattern;
+      static BoolParameter disableEffects;
+
+    protected:
+      void restart();
+      void recreatePixelBuffer();
+      bool flushChangeTracker();  // true if flushed, false if empty
+
+      void triggerUpdate();
+
+      VNCServer* server;
+
+      // -=- Display pixel buffer
+      DeviceFrameBuffer* pb;
+      TCharArray deviceName;
+      HDC device;
+      bool releaseDevice;
+
+      // -=- The coordinates of Window's entire virtual Screen
+      Rect screenRect;
+
+      // -=- All changes are collected in Display coords and merged
+      SimpleUpdateTracker change_tracker;
+
+      // -=- Internal SDisplay implementation
+      SDisplayCore* core;
+
+      // -=- Cursor
+      WMCursor::Info old_cursor;
+      Region old_cursor_region;
+      Point cursor_renderpos;
+
+      // -=- Event signalled to trigger an update to be flushed
+      Handle updateEvent;
+
+      // -=- Where to write the active/inactive indicator to
+      bool* statusLocation;
+    };
+
+  }
+}
+
+#endif // __RFB_SDISPLAY_H__
diff --git a/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx
new file mode 100644
index 0000000..457a861
--- /dev/null
+++ b/rfb_win32/SInput.cxx
@@ -0,0 +1,459 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- SInput.cxx
+//
+// A number of routines that accept VNC input event data and perform
+// the appropriate actions under Win32
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+// * Force the windows headers to include all the SendInput stuff
+#define _WIN32_WINNT 0x401
+
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Service.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("SInput");
+
+
+typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
+static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+
+//
+// -=- Pointer implementation for Win32
+//
+
+static DWORD buttonDownMapping[8] = {
+  MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN,
+  MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonUpMapping[8] = {
+  MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP,
+  MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonDataMapping[8] = {
+  0, 0, 0, 120, -120, 0, 0, 0
+};
+
+win32::SPointer::SPointer()
+  : last_buttonmask(0)
+{
+}
+
+void
+win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask)
+{
+  // - We are specifying absolute coordinates
+  DWORD flags = MOUSEEVENTF_ABSOLUTE;
+
+  // - Has the pointer moved since the last event?
+  if (!last_position.equals(pos))
+    flags |= MOUSEEVENTF_MOVE;
+
+  // - If the system swaps left and right mouse buttons then we must
+  //   swap them here to negate the effect, so that we do the actual
+  //   action we mean to do
+  if (::GetSystemMetrics(SM_SWAPBUTTON)) {
+    bool leftDown = buttonmask & 1;
+    bool rightDown = buttonmask & 4;
+    buttonmask = (buttonmask & ~(1 | 4));
+    if (leftDown) buttonmask |= 4;
+    if (rightDown) buttonmask |= 1;
+  }
+
+  DWORD data = 0;
+  for (int i = 0; i < 8; i++) {
+    if ((buttonmask & (1<<i)) != (last_buttonmask & (1<<i))) {
+      if (buttonmask & (1<<i)) {
+        flags |= buttonDownMapping[i];
+        if (buttonDataMapping[i]) {
+          if (data) vlog.info("warning - two buttons set mouse_event data field");
+          data = buttonDataMapping[i];
+        }
+      } else {
+        flags |= buttonUpMapping[i];
+      }
+    }
+  }
+
+  last_position = pos;
+  last_buttonmask = buttonmask;
+
+  Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
+  if (primaryDisplay.contains(pos)) {
+    // mouse_event wants coordinates specified as a proportion of the
+    // primary display's size, scaled to the range 0 to 65535
+    Point scaled;
+    scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
+    scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
+    ::mouse_event(flags, scaled.x, scaled.y, data, 0);
+  } else {
+    // The event lies outside the primary monitor.  Under Win2K, we can just use
+    // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
+    // SendInput is available on all multi-monitor-aware platforms.
+#ifdef SM_CXVIRTUALSCREEN
+    if (osVersion.isPlatformNT) {
+      if (!_SendInput.isValid())
+        throw rdr::Exception("SendInput not available");
+      INPUT evt;
+      evt.type = INPUT_MOUSE;
+      Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
+                 pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
+      evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
+      evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
+      evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
+      evt.mi.dwExtraInfo = 0;
+      evt.mi.mouseData = data;
+      evt.mi.time = 0;
+      if ((*_SendInput)(1, &evt, sizeof(evt)) != 1)
+        throw rdr::SystemException("SendInput", GetLastError());
+    } else {
+      // Under Win9x, this is not addressable by either mouse_event or SendInput
+      // *** STUPID KLUDGY HACK ***
+      POINT cursorPos; GetCursorPos(&cursorPos);
+      ULONG oldSpeed, newSpeed = 10;
+      ULONG mouseInfo[3];
+      if (flags & MOUSEEVENTF_MOVE) {
+        flags &= ~MOUSEEVENTF_ABSOLUTE;
+        SystemParametersInfo(SPI_GETMOUSE, 0, &mouseInfo, 0);
+        SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed, 0);
+        vlog.debug("SPI_GETMOUSE %d, %d, %d, speed %d", mouseInfo[0], mouseInfo[1], mouseInfo[2], oldSpeed);
+        ULONG idealMouseInfo[] = {10, 0, 0};
+        SystemParametersInfo(SPI_SETMOUSESPEED, 0, &newSpeed, 0);
+        SystemParametersInfo(SPI_SETMOUSE, 0, &idealMouseInfo, 0);
+      }
+      ::mouse_event(flags, pos.x-cursorPos.x, pos.y-cursorPos.y, data, 0);
+      if (flags & MOUSEEVENTF_MOVE) {
+        SystemParametersInfo(SPI_SETMOUSE, 0, &mouseInfo, 0);
+        SystemParametersInfo(SPI_SETMOUSESPEED, 0, &oldSpeed, 0);
+      }
+    }
+#endif
+  }
+}
+
+//
+// -=- Keyboard implementation
+//
+
+BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
+  "Whether to assume the viewer has already interpreted dead key sequences "
+  "into latin-1 characters", true);
+
+static bool oneShift;
+
+// The keysymToAscii table transforms a couple of awkward keysyms into their
+// ASCII equivalents.
+struct  keysymToAscii_t {
+  rdr::U32 keysym;
+  rdr::U8 ascii;
+};
+
+keysymToAscii_t keysymToAscii[] = {
+  { XK_KP_Space, ' ' },
+  { XK_KP_Equal, '=' },
+};
+
+rdr::U8 latin1DeadChars[] = {
+  XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
+  XK_asciitilde
+};
+
+struct latin1ToDeadChars_t {
+  rdr::U8 latin1Char;
+  rdr::U8 deadChar;
+  rdr::U8 baseChar;
+};
+
+latin1ToDeadChars_t latin1ToDeadChars[] = {
+
+  { XK_Agrave, XK_grave, XK_A },
+  { XK_Egrave, XK_grave, XK_E },
+  { XK_Igrave, XK_grave, XK_I },
+  { XK_Ograve, XK_grave, XK_O },
+  { XK_Ugrave, XK_grave, XK_U },
+  { XK_agrave, XK_grave, XK_a },
+  { XK_egrave, XK_grave, XK_e },
+  { XK_igrave, XK_grave, XK_i },
+  { XK_ograve, XK_grave, XK_o},
+  { XK_ugrave, XK_grave, XK_u },
+
+  { XK_Aacute, XK_acute, XK_A },
+  { XK_Eacute, XK_acute, XK_E },
+  { XK_Iacute, XK_acute, XK_I },
+  { XK_Oacute, XK_acute, XK_O },
+  { XK_Uacute, XK_acute, XK_U },
+  { XK_Yacute, XK_acute, XK_Y },
+  { XK_aacute, XK_acute, XK_a },
+  { XK_eacute, XK_acute, XK_e },
+  { XK_iacute, XK_acute, XK_i },
+  { XK_oacute, XK_acute, XK_o},
+  { XK_uacute, XK_acute, XK_u },
+  { XK_yacute, XK_acute, XK_y },
+
+  { XK_Acircumflex, XK_asciicircum, XK_A },
+  { XK_Ecircumflex, XK_asciicircum, XK_E },
+  { XK_Icircumflex, XK_asciicircum, XK_I },
+  { XK_Ocircumflex, XK_asciicircum, XK_O },
+  { XK_Ucircumflex, XK_asciicircum, XK_U },
+  { XK_acircumflex, XK_asciicircum, XK_a },
+  { XK_ecircumflex, XK_asciicircum, XK_e },
+  { XK_icircumflex, XK_asciicircum, XK_i },
+  { XK_ocircumflex, XK_asciicircum, XK_o},
+  { XK_ucircumflex, XK_asciicircum, XK_u },
+
+  { XK_Adiaeresis, XK_diaeresis, XK_A },
+  { XK_Ediaeresis, XK_diaeresis, XK_E },
+  { XK_Idiaeresis, XK_diaeresis, XK_I },
+  { XK_Odiaeresis, XK_diaeresis, XK_O },
+  { XK_Udiaeresis, XK_diaeresis, XK_U },
+  { XK_adiaeresis, XK_diaeresis, XK_a },
+  { XK_ediaeresis, XK_diaeresis, XK_e },
+  { XK_idiaeresis, XK_diaeresis, XK_i },
+  { XK_odiaeresis, XK_diaeresis, XK_o},
+  { XK_udiaeresis, XK_diaeresis, XK_u },
+  { XK_ydiaeresis, XK_diaeresis, XK_y },
+
+  { XK_Aring, XK_degree, XK_A },
+  { XK_aring, XK_degree, XK_a },
+
+  { XK_Ccedilla, XK_cedilla, XK_C },
+  { XK_ccedilla, XK_cedilla, XK_c },
+
+  { XK_Atilde, XK_asciitilde, XK_A },
+  { XK_Ntilde, XK_asciitilde, XK_N },
+  { XK_Otilde, XK_asciitilde, XK_O },
+  { XK_atilde, XK_asciitilde, XK_a },
+  { XK_ntilde, XK_asciitilde, XK_n },
+  { XK_otilde, XK_asciitilde, XK_o },
+};
+
+// doKeyboardEvent wraps the system keybd_event function and attempts to find
+// the appropriate scancode corresponding to the supplied virtual keycode.
+
+inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
+  vlog.debug("vkCode 0x%x flags 0x%x", vkCode, flags);
+  keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
+}
+
+// KeyStateModifier is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc.  An instance of the class is created
+// for every key which may need to be pressed or released.  Then either press()
+// or release() may be called to make sure that the corresponding key is in the
+// right state.  The destructor of the class automatically reverts to the
+// previous state.
+
+class KeyStateModifier {
+public:
+  KeyStateModifier(int vkCode_, int flags_=0)
+    : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
+  {}
+  void press() {
+    if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
+      doKeyboardEvent(vkCode, flags);
+      pressed = true;
+    }
+  }
+  void release() {
+    if (GetAsyncKeyState(vkCode) & 0x8000) {
+      doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+      released = true;
+    }
+  }
+  ~KeyStateModifier() {
+    if (pressed) {
+      doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+    } else if (released) {
+      doKeyboardEvent(vkCode, flags);
+    }
+  }
+  int vkCode;
+  int flags;
+  bool pressed;
+  bool released;
+};
+
+
+// doKeyEventWithModifiers() generates a key event having first "pressed" or
+// "released" the shift, ctrl or alt modifiers if necessary.
+
+void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
+{
+  KeyStateModifier ctrl(VK_CONTROL);
+  KeyStateModifier alt(VK_MENU);
+  KeyStateModifier shift(VK_SHIFT);
+
+  if (down) {
+    if (modifierState & 2) ctrl.press();
+    if (modifierState & 4) alt.press();
+    if (modifierState & 1) {
+      shift.press(); 
+    } else {
+      shift.release();
+    }
+  }
+  doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
+}
+
+
+win32::SKeyboard::SKeyboard()
+{
+  oneShift = rfb::win32::osVersion.isPlatformWindows;
+  for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+    vkMap[keymap[i].keysym] = keymap[i].vk;
+    extendedMap[keymap[i].keysym] = keymap[i].extended;
+  }
+
+  // Find dead characters for the current keyboard layout
+  // XXX how could we handle the keyboard layout changing?
+  BYTE keystate[256];
+  memset(keystate, 0, 256);
+  for (int j = 0; j < sizeof(latin1DeadChars); j++) {
+    SHORT s = VkKeyScan(latin1DeadChars[j]);
+    if (s != -1) {
+      BYTE vkCode = LOBYTE(s);
+      BYTE modifierState = HIBYTE(s);
+      keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
+      keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
+      keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
+      rdr::U8 chars[2];
+      int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+      if (nchars < 0) {
+        vlog.debug("Found dead key 0x%x '%c'",
+                   latin1DeadChars[j], latin1DeadChars[j]);
+        deadChars.push_back(latin1DeadChars[j]);
+        ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+      }
+    }
+  }
+}
+
+
+void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down)
+{
+  for (int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
+    if (keysymToAscii[i].keysym == keysym) {
+      keysym = keysymToAscii[i].ascii;
+      break;
+    }
+  }
+
+  if ((keysym >= 32 && keysym <= 126) ||
+      (keysym >= 160 && keysym <= 255))
+  {
+    // ordinary Latin-1 character
+
+    if (deadKeyAware) {
+      // Detect dead chars and generate the dead char followed by space so
+      // that we'll end up with the original char.
+      for (int i = 0; i < deadChars.size(); i++) {
+        if (keysym == deadChars[i]) {
+          SHORT dc = VkKeyScan(keysym);
+          if (dc != -1) {
+            if (down) {
+              vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x "
+                        "followed by space", keysym, LOBYTE(dc), HIBYTE(dc));
+              doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+              doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+              doKeyEventWithModifiers(VK_SPACE, 0, true);
+              doKeyEventWithModifiers(VK_SPACE, 0, false);
+            }
+            return;
+          }
+        }
+      }
+    }
+
+    SHORT s = VkKeyScan(keysym);
+    if (s == -1) {
+      if (down) {
+        // not a single keypress - try synthesizing dead chars.
+        for (int j = 0;
+             j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t);
+             j++) {
+          if (keysym == latin1ToDeadChars[j].latin1Char) {
+            for (int i = 0; i < deadChars.size(); i++) {
+              if (deadChars[i] == latin1ToDeadChars[j].deadChar) {
+                SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar);
+                SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar);
+                if (dc != -1 && bc != -1) {
+                  vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x "
+                            "followed by vkCode 0x%x mod 0x%x",
+                            keysym, LOBYTE(dc), HIBYTE(dc),
+                            LOBYTE(bc), HIBYTE(bc));
+                  doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+                  doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+                  doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true);
+                  doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false);
+                  return;
+                }
+                break;
+              }
+            }
+            break;
+          }
+        }
+        vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym);
+      }
+      return;
+    }
+
+    BYTE vkCode = LOBYTE(s);
+    BYTE modifierState = HIBYTE(s);
+    vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d",
+               keysym, vkCode, modifierState, down);
+    doKeyEventWithModifiers(vkCode, modifierState, down);
+
+  } else {
+
+    // see if it's a recognised keyboard key, otherwise ignore it
+
+    if (vkMap.find(keysym) == vkMap.end()) {
+      vlog.info("ignoring unknown keysym 0x%x",keysym);
+      return;
+    }
+    BYTE vkCode = vkMap[keysym];
+    DWORD flags = 0;
+    if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY;
+    if (!down) flags |= KEYEVENTF_KEYUP;
+
+    vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d",
+               keysym, vkCode, extendedMap[keysym], down);
+    if (down && (vkCode == VK_DELETE) &&
+        ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
+        ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
+    {
+      rfb::win32::emulateCtrlAltDel();
+      return;
+    }
+
+    doKeyboardEvent(vkCode, flags);
+  }
+}
diff --git a/rfb_win32/SInput.h b/rfb_win32/SInput.h
new file mode 100644
index 0000000..dcd779e
--- /dev/null
+++ b/rfb_win32/SInput.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Input.h
+//
+// A number of routines that accept VNC-style input event data and perform
+// the appropriate actions under Win32
+
+#ifndef __RFB_WIN32_INPUT_H__
+#define __RFB_WIN32_INPUT_H__
+
+#include <rfb/Rect.h>
+#include <rfb/Configuration.h>
+#include <rdr/types.h>
+#include <map>
+#include <vector>
+
+namespace rfb {
+
+  class CMsgWriter;
+
+  namespace win32 {
+
+    // -=- Pointer event handling
+
+    class SPointer {
+    public:
+      SPointer();
+      // - Create a pointer event at a the given coordinates, with the
+      //   specified button state.  The event must be specified using
+      //   Screen coordinates.
+      void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+    protected:
+      Point last_position;
+      rdr::U8 last_buttonmask;
+    };
+
+    // -=- Keyboard event handling
+
+    class SKeyboard {
+    public:
+      SKeyboard();
+      void keyEvent(rdr::U32 key, bool down);
+      static BoolParameter deadKeyAware;
+    private:
+      std::map<rdr::U32,rdr::U8> vkMap;
+      std::map<rdr::U32,bool> extendedMap;
+      std::vector<rdr::U8> deadChars;
+    };
+
+  }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INPUT_H__
diff --git a/rfb_win32/Security.h b/rfb_win32/Security.h
new file mode 100644
index 0000000..d92e314
--- /dev/null
+++ b/rfb_win32/Security.h
@@ -0,0 +1,198 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// Security.h
+
+// Wrapper classes for a few Windows NT security structures/functions
+// that are used by VNC
+
+#ifndef __RFB_WIN32_SECURITY_H__
+#define __RFB_WIN32_SECURITY_H__
+
+#include <rdr/types.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <aclapi.h>
+
+#include <list>
+
+namespace rfb {
+
+  namespace win32 {
+
+    struct Trustee : public TRUSTEE {
+      Trustee(const TCHAR* name,
+              TRUSTEE_FORM form=TRUSTEE_IS_NAME,
+              TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN)
+      {
+        pMultipleTrustee = 0;
+        MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+        TrusteeForm = form;
+        TrusteeType = type;
+        ptstrName = (TCHAR*)name;
+      }
+    };
+
+    struct ExplicitAccess : public EXPLICIT_ACCESS {
+      ExplicitAccess(const TCHAR* name,
+                     TRUSTEE_FORM type,
+                     DWORD perms,
+                     ACCESS_MODE mode,
+                     DWORD inherit=0)
+      {
+        Trustee = rfb::win32::Trustee(name, type);
+        grfAccessPermissions = perms;
+        grfAccessMode = mode;
+        grfInheritance = inherit;
+      }
+    };
+
+    // Helper class for building access control lists
+    struct AccessEntries {
+      AccessEntries() : entries(0), entry_count(0) {}
+      ~AccessEntries() {delete [] entries;}
+      void allocMinEntries(int count) {
+        if (count > entry_count) {
+          EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
+          if (entries) {
+            memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
+            delete entries;
+          }
+          entries = new_entries;
+        }
+      }
+      void addEntry(const TCHAR* trusteeName,
+                    DWORD permissions,
+                    ACCESS_MODE mode)
+      {
+        allocMinEntries(entry_count+1);
+        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+        entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
+        entry_count++;
+      }
+      void addEntry(const PSID sid, DWORD permissions, ACCESS_MODE mode) {
+        allocMinEntries(entry_count+1);
+        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+        entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
+        entry_count++;
+      }
+
+      EXPLICIT_ACCESS* entries;
+      int entry_count;
+    };
+
+    // Helper class for handling SIDs
+    struct Sid {
+      Sid() : sid(0) {}
+      Sid(PSID sid_) : sid(sid_) {}
+      ~Sid() {
+        if (sid) FreeSid(sid);
+      }
+      operator PSID() const {return sid;}
+      PSID operator=(const PSID sid_) {
+        if (sid) FreeSid(sid);
+        sid = sid_;
+      }
+
+      static PSID Administrators() {
+        PSID sid = 0;
+        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+        if (!AllocateAndInitializeSid(&ntAuth, 2,
+          SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
+          0, 0, 0, 0, 0, 0,
+          &sid)) 
+          throw rdr::SystemException("Sid::Administrators", GetLastError());
+        return sid;
+      }
+      static PSID SYSTEM() {
+        PSID sid = 0;
+        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+        if (!AllocateAndInitializeSid(&ntAuth, 1,
+          SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
+          &sid))
+          throw rdr::SystemException("Sid::SYSTEM", GetLastError());
+        return sid;
+      }
+
+    protected:
+      PSID sid;
+    };
+      
+    // Helper class for handling & freeing ACLs
+    struct AccessControlList : public LocalMem {
+      AccessControlList(int size) : LocalMem(size) {}
+      AccessControlList(PACL acl_=0) : LocalMem(acl_) {}
+      operator PACL() {return (PACL)ptr;}
+    };
+
+    // Create a new ACL based on supplied entries and, if supplied, existing ACL 
+    static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) {
+      typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
+#ifdef UNICODE
+      const char* fnName = "SetEntriesInAclW";
+#else
+      const char* fnName = "SetEntriesInAclA";
+#endif
+      DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
+      if (!_SetEntriesInAcl.isValid())
+        throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
+      PACL new_dacl;
+      DWORD result;
+      if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
+        throw rdr::SystemException("SetEntriesInAcl", result);
+      return new_dacl;
+    }
+
+    // Helper class for memory-management of self-relative SecurityDescriptors
+    struct SecurityDescriptorPtr : LocalMem {
+      SecurityDescriptorPtr(int size) : LocalMem(size) {}
+      SecurityDescriptorPtr(PSECURITY_DESCRIPTOR sd_=0) : LocalMem(sd_) {}
+      PSECURITY_DESCRIPTOR takeSD() {return takePtr();}
+    };
+
+    // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators,
+    //   with the supplied DACL and no SACL.  The returned value can be assigned
+    //   to a SecurityDescriptorPtr to be managed.
+    static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) {
+      SECURITY_DESCRIPTOR absSD;
+      if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+        throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+      Sid owner(Sid::SYSTEM());
+      if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+        throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+      Sid group(Sid::Administrators());
+      if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
+        throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
+      if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
+        throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
+      DWORD sdSize = GetSecurityDescriptorLength(&absSD);
+      SecurityDescriptorPtr sd(sdSize);
+      if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
+        throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
+      return sd.takeSD();
+    }
+
+  }
+
+}
+
+#endif
diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx
new file mode 100644
index 0000000..b00c290
--- /dev/null
+++ b/rfb_win32/Service.cxx
@@ -0,0 +1,638 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Service.cxx
+
+#include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+
+#include <logmessages/messages.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("Service");
+
+
+// - Internal service implementation functions
+
+Service* service = 0;
+
+VOID WINAPI serviceHandler(DWORD control) {
+  vlog.debug("service control %u", control);
+  switch (control) {
+  case SERVICE_CONTROL_INTERROGATE:
+    service->setStatus();
+    break;
+  case SERVICE_CONTROL_PARAMCHANGE:
+    service->readParams();
+    break;
+  case SERVICE_CONTROL_SHUTDOWN:
+    service->osShuttingDown();
+    break;
+  case SERVICE_CONTROL_STOP:
+    service->setStatus(SERVICE_STOP_PENDING);
+    service->stop();
+    break;
+  }
+}
+
+
+// -=- Message window derived class used under Win9x to implement stopService
+
+#define WM_SMSG_SERVICE_STOP WM_USER
+
+class ServiceMsgWindow : public MsgWindow {
+public:
+  ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {}
+  LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+    switch (msg) {
+    case WM_SMSG_SERVICE_STOP:
+      service->stop();
+      return TRUE;
+    }
+    return MsgWindow::processMessage(msg, wParam, lParam);
+  }
+
+  static const TCHAR* baseName;
+};
+
+const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:");
+
+
+// -=- Service main procedure, used under WinNT/2K/XP by the SCM
+
+VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
+  vlog.debug("entering %s serviceProc", service->getName());
+  service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
+  if (!service->status_handle) {
+    vlog.error("unable to register service control handler");
+    return;
+  }
+  service->setStatus(SERVICE_START_PENDING);
+  vlog.debug("entering %s serviceMain", service->getName());
+  service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
+  vlog.debug("leaving %s serviceMain", service->getName());
+  service->setStatus(SERVICE_STOPPED);
+}
+
+
+// -=- Service
+
+Service::Service(const TCHAR* name_) : name(name_) {
+  vlog.debug("Service");
+  status_handle = 0;
+  status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
+  status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
+  status.dwWin32ExitCode = NO_ERROR;
+  status.dwServiceSpecificExitCode = 0;
+  status.dwCheckPoint = 0;
+  status.dwWaitHint = 30000;
+  status.dwCurrentState = SERVICE_STOPPED;
+}
+
+void
+Service::start() {
+  if (osVersion.isPlatformNT) {
+    SERVICE_TABLE_ENTRY entry[2];
+    entry[0].lpServiceName = (TCHAR*)name;
+    entry[0].lpServiceProc = serviceProc;
+    entry[1].lpServiceName = NULL;
+    entry[1].lpServiceProc = NULL;
+    vlog.debug("entering dispatcher");
+    if (!SetProcessShutdownParameters(0x100, 0))
+      vlog.error("unable to set shutdown parameters: %d", GetLastError());
+    service = this;
+    if (!StartServiceCtrlDispatcher(entry))
+      throw SystemException("unable to start service", GetLastError());
+  } else {
+
+    // - Create the service window, so the service can be stopped
+    TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1);
+    _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+    _tcscat(wndName.buf, getName());
+    ServiceMsgWindow service_window(wndName.buf);
+
+    // - Locate the RegisterServiceProcess function
+	  typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD);
+    DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess");
+    if (!_RegisterServiceProcess.isValid())
+      throw Exception("unable to find RegisterServiceProcess");
+
+    // - Run the service
+    (*_RegisterServiceProcess)(NULL, 1);
+    service = this;
+    serviceMain(0, 0);
+	  (*_RegisterServiceProcess)(NULL, 0);
+  }
+}
+
+void
+Service::setStatus() {
+  setStatus(status.dwCurrentState);
+}
+
+void
+Service::setStatus(DWORD state) {
+  if (!osVersion.isPlatformNT)
+    return;
+  if (status_handle == 0) {
+    vlog.debug("warning - cannot setStatus");
+    return;
+  }
+  status.dwCurrentState = state;
+  status.dwCheckPoint++;
+  if (!SetServiceStatus(status_handle, &status)) {
+    status.dwWin32ExitCode = GetLastError();
+    vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
+    stop();
+  }
+  vlog.debug("set status to %u(%u)", state, status.dwCheckPoint);
+}
+
+Service::~Service() {
+  vlog.debug("~Service");
+  service = 0;
+}
+
+
+// Find out whether this process is running as the WinVNC service
+bool thisIsService() {
+  return service && (service->status.dwCurrentState != SERVICE_STOPPED);
+}
+
+
+// -=- Desktop handling code
+
+// Switch the current thread to the specified desktop
+static bool
+switchToDesktop(HDESK desktop) {
+  HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+  if (!SetThreadDesktop(desktop)) {
+    vlog.debug("switchToDesktop failed:%u", GetLastError());
+    return false;
+  }
+  if (!CloseDesktop(old_desktop))
+    vlog.debug("unable to close old desktop:%u", GetLastError());
+  return true;
+}
+
+// Determine whether the thread's current desktop is the input one
+static bool
+inputDesktopSelected() {
+  HDESK current = GetThreadDesktop(GetCurrentThreadId());
+	HDESK input = OpenInputDesktop(0, FALSE,
+  	DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+		DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+		DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+		DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+  if (!input) {
+    vlog.debug("unable to OpenInputDesktop(1):%u", GetLastError());
+    return false;
+  }
+
+  DWORD size;
+  char currentname[256];
+  char inputname[256];
+
+  if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
+    vlog.debug("unable to GetUserObjectInformation(1):%u", GetLastError());
+    CloseDesktop(input);
+    return false;
+  }
+  if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
+    vlog.debug("unable to GetUserObjectInformation(2):%u", GetLastError());
+    CloseDesktop(input);
+    return false;
+  }
+  if (!CloseDesktop(input))
+    vlog.debug("unable to close input desktop:%u", GetLastError());
+
+  // *** vlog.debug("current=%s, input=%s", currentname, inputname);
+  bool result = strcmp(currentname, inputname) == 0;
+  return result;
+}
+
+// Switch the current thread into the input desktop
+static bool
+selectInputDesktop() {
+  // - Open the input desktop
+  HDESK desktop = OpenInputDesktop(0, FALSE,
+		DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+		DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+		DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+		DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+  if (!desktop) {
+    vlog.debug("unable to OpenInputDesktop(2):%u", GetLastError());
+    return false;
+  }
+
+  // - Switch into it
+  if (!switchToDesktop(desktop)) {
+    CloseDesktop(desktop);
+    return false;
+  }
+
+  // ***
+  DWORD size = 256;
+  char currentname[256];
+  if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
+    vlog.debug("switched to %s", currentname);
+  }
+  // ***
+
+  vlog.debug("switched to input desktop");
+
+  return true;
+}
+
+
+// -=- Access points to desktop-switching routines
+
+bool
+rfb::win32::desktopChangeRequired() {
+  if (!osVersion.isPlatformNT)
+    return false;
+
+  return !inputDesktopSelected();
+}
+
+bool
+rfb::win32::changeDesktop() {
+  if (!osVersion.isPlatformNT)
+    return true;
+  if (osVersion.cannotSwitchDesktop)
+    return false;
+
+  return selectInputDesktop();
+}
+
+
+// -=- Ctrl-Alt-Del emulation
+
+class CADThread : public Thread {
+public:
+  CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}
+  virtual void run() {
+	  HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+
+    if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+		  DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+		  DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+      DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {
+	    PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
+      switchToDesktop(old_desktop);
+      result = true;
+    }
+  }
+  bool result;
+};
+
+bool
+rfb::win32::emulateCtrlAltDel() {
+  if (!osVersion.isPlatformNT)
+    return false;
+
+  CADThread* cad_thread = new CADThread();
+  vlog.debug("emulate Ctrl-Alt-Del");
+  if (cad_thread) {
+    cad_thread->start();
+    cad_thread->join();
+    bool result = cad_thread->result;
+    delete cad_thread;
+    return result;
+  }
+  return false;
+}
+
+
+// -=- Application Event Log target Logger class
+
+class Logger_EventLog : public Logger {
+public:
+  Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
+    eventlog = RegisterEventSource(NULL, srcname);
+    if (!eventlog)
+      printf("Unable to open event log:%ld\n", GetLastError());
+  }
+  ~Logger_EventLog() {
+    if (eventlog)
+      DeregisterEventSource(eventlog);
+  }
+
+  virtual void write(int level, const char *logname, const char *message) {
+    if (!eventlog) return;
+    TStr log(logname), msg(message);
+    const TCHAR* strings[] = {log, msg};
+    WORD type = EVENTLOG_INFORMATION_TYPE;
+    if (level == 0) type = EVENTLOG_ERROR_TYPE;
+    if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
+      // *** It's not at all clear what is the correct behaviour if this fails...
+      printf("ReportEvent failed:%ld\n", GetLastError());
+    }
+  }
+
+protected:
+  HANDLE eventlog;
+};
+
+static Logger_EventLog* logger = 0;
+
+bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
+  if (logger)
+    return false;
+  if (osVersion.isPlatformNT) {
+    logger = new Logger_EventLog(srcname);
+    logger->registerLogger();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+
+// -=- Registering and unregistering the service
+
+bool rfb::win32::registerService(const TCHAR* name, const TCHAR* desc,
+                                 int argc, const char* argv[]) {
+
+  // - Initialise the default service parameters
+  const TCHAR* defaultcmdline;
+  if (osVersion.isPlatformNT)
+    defaultcmdline = _T("-service");
+  else
+    defaultcmdline = _T("-noconsole -service");
+
+  // - Get the full pathname of our executable
+  ModuleFileName buffer;
+
+  // - Calculate the command-line length
+  int cmdline_len = _tcslen(buffer.buf) + 4;
+  int i;
+  for (i=0; i<argc; i++) {
+    cmdline_len += strlen(argv[i]) + 3;
+  }
+
+  // - Add the supplied extra parameters to the command line
+  TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
+  _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
+  for (i=0; i<argc; i++) {
+    _tcscat(cmdline.buf, _T(" \""));
+    _tcscat(cmdline.buf, TStr(argv[i]));
+    _tcscat(cmdline.buf, _T("\""));
+  }
+    
+  // - Register the service
+
+  if (osVersion.isPlatformNT) {
+
+    // - Open the SCM
+    ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+    if (!scm)
+      throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+
+    ServiceHandle service = CreateService(scm,
+      name, desc, SC_MANAGER_ALL_ACCESS,
+      SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+      SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+      cmdline.buf, NULL, NULL, NULL, NULL, NULL);
+    if (!service)
+      throw rdr::SystemException("unable to create service", GetLastError());
+
+    // - Register the event log source
+    RegKey hk, hk2;
+
+    hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+    hk.createKey(hk2, name);
+
+    for (i=_tcslen(buffer.buf); i>0; i--) {
+      if (buffer.buf[i] == _T('\\')) {
+        buffer.buf[i+1] = 0;
+        break;
+      }
+    }
+
+    const TCHAR* dllFilename = _T("logmessages.dll");
+    TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
+    _tcscpy(dllPath.buf, buffer.buf);
+    _tcscat(dllPath.buf, dllFilename);
+ 
+    hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
+    hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
+
+  } else {
+
+    RegKey services;
+    services.createKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+    services.setString(name, cmdline.buf);
+
+  }
+
+  Sleep(500);
+
+  return true;
+}
+
+bool rfb::win32::unregisterService(const TCHAR* name) {
+  if (osVersion.isPlatformNT) {
+
+    // - Open the SCM
+    ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+    if (!scm)
+      throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+    // - Create the service
+    ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
+    if (!service)
+      throw rdr::SystemException("unable to locate the service", GetLastError());
+    if (!DeleteService(service))
+      throw rdr::SystemException("unable to remove the service", GetLastError());
+
+    // - Register the event log source
+    RegKey hk;
+    hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+    hk.deleteKey(name);
+
+  } else {
+
+		RegKey services;
+    services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+    services.deleteValue(name);
+
+  }
+
+  Sleep(500);
+
+  return true;
+}
+
+
+// -=- Starting and stopping the service
+
+HWND findServiceWindow(const TCHAR* name) {
+  TCharArray wndName(_tcslen(ServiceMsgWindow::baseName)+_tcslen(name)+1);
+  _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+  _tcscat(wndName.buf, name);
+  vlog.debug("searching for %s window", CStr(wndName.buf));
+  return FindWindow(0, wndName.buf);
+}
+
+bool rfb::win32::startService(const TCHAR* name) {
+
+  if (osVersion.isPlatformNT) {
+    // - Open the SCM
+    ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+    if (!scm)
+      throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+    // - Locate the service
+    ServiceHandle service = OpenService(scm, name, SERVICE_START);
+    if (!service)
+      throw rdr::SystemException("unable to open the service", GetLastError());
+
+    // - Start the service
+    if (!StartService(service, 0, NULL))
+      throw rdr::SystemException("unable to start the service", GetLastError());
+  } else {
+    // - Check there is no service window
+    if (findServiceWindow(name))
+      throw rdr::Exception("the service is already running");
+
+    // - Find the RunServices registry key
+		RegKey services;
+		services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+
+    // - Read the command-line from it
+    TCharArray cmdLine = services.getString(name);
+
+    // - Start the service
+    PROCESS_INFORMATION proc_info;
+    STARTUPINFO startup_info;
+    ZeroMemory(&startup_info, sizeof(startup_info));
+    startup_info.cb = sizeof(startup_info);
+    if (!CreateProcess(0, cmdLine.buf, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &startup_info, &proc_info)) {
+      throw SystemException("unable to start service", GetLastError());
+    }
+  }
+
+  Sleep(500);
+
+  return true;
+}
+
+bool rfb::win32::stopService(const TCHAR* name) {
+  if (osVersion.isPlatformNT) {
+    // - Open the SCM
+    ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+    if (!scm)
+      throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+    // - Locate the service
+    ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
+    if (!service)
+      throw rdr::SystemException("unable to open the service", GetLastError());
+
+    // - Start the service
+    SERVICE_STATUS status;
+    if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
+      throw rdr::SystemException("unable to stop the service", GetLastError());
+
+  } else {
+    // - Find the service window
+    HWND service_window = findServiceWindow(name);
+    if (!service_window)
+      throw Exception("unable to locate running service");
+
+    // Tell it to quit
+    vlog.debug("sending service stop request");
+    if (!SendMessage(service_window, WM_SMSG_SERVICE_STOP, 0, 0))
+      throw Exception("unable to stop service");
+
+    // Check it's quitting...
+    DWORD process_id = 0;
+    HANDLE process = 0;
+    if (!GetWindowThreadProcessId(service_window, &process_id))
+      throw SystemException("unable to verify service has quit", GetLastError());
+    process = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, process_id);
+    if (!process)
+      throw SystemException("unable to obtain service handle", GetLastError());
+    int retries = 5;
+    vlog.debug("checking status");
+    while (retries-- && (WaitForSingleObject(process, 1000) != WAIT_OBJECT_0)) {}
+    if (!retries) {
+      vlog.debug("failed to quit - terminating");
+      // May not have quit because of silly Win9x registry watching bug..
+      if (!TerminateProcess(process, 1))
+        throw SystemException("unable to terminate process!", GetLastError());
+      throw Exception("service failed to quit - called TerminateProcess");
+    }
+  }
+
+  Sleep(500);
+
+  return true;
+}
+
+void rfb::win32::printServiceStatus(const TCHAR* name) {
+  if (osVersion.isPlatformNT) {
+    // - Open the SCM
+    ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+    if (!scm)
+      throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+    // - Locate the service
+    ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
+    if (!service)
+      throw rdr::SystemException("unable to open the service", GetLastError());
+
+    // - Get the service status
+    SERVICE_STATUS status;
+    if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
+      throw rdr::SystemException("unable to query the service", GetLastError());
+
+    printf("Service is in the ");
+    switch (status.dwCurrentState) {
+    case SERVICE_RUNNING: printf("running"); break;
+    case SERVICE_STOPPED: printf("stopped"); break;
+    case SERVICE_STOP_PENDING: printf("stop pending"); break;
+    default: printf("unknown (%lu)", status.dwCurrentState); break;
+    };
+    printf(" state.\n");
+
+  } else {
+    HWND service_window = findServiceWindow(name);
+    printf("Service is in the ");
+    if (!service_window) printf("stopped");
+    else printf("running");
+    printf(" state.\n");
+  }
+}
+
+
+bool rfb::win32::isServiceProcess() {
+  return service != 0;
+}
\ No newline at end of file
diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h
new file mode 100644
index 0000000..164381a
--- /dev/null
+++ b/rfb_win32/Service.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Service.h
+//
+// Win32 service-mode code.
+// Derive your service from this code and let it handle the annoying Win32
+// service API.
+// The underlying implementation takes care of the differences between
+// Windows NT and Windows 95 based systems
+
+#ifndef __RFB_WIN32_SERVICE_H__
+#define __RFB_WIN32_SERVICE_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    //
+    // -=- Service
+    //
+
+    // Application base-class for services.
+
+    class Service {
+    public:
+
+      Service(const TCHAR* name_);
+      virtual ~Service();
+
+      const TCHAR* getName() {return name;}
+      SERVICE_STATUS& getStatus() {return status;}
+
+      void setStatus(DWORD status);
+      void setStatus();
+
+      // - Start the service, having initialised it
+      void start();
+
+      // - Service main procedure - override to implement a service
+      virtual DWORD serviceMain(int argc, TCHAR* argv[]) = 0;
+
+      // - Service control notifications
+
+      // To get notified when the OS is shutting down
+      virtual void osShuttingDown() = 0;
+
+      // To get notified when the service parameters change
+      virtual void readParams() = 0;
+
+      // To cause the serviceMain() routine to return
+      virtual void stop() = 0;
+
+    public:
+      SERVICE_STATUS_HANDLE status_handle;
+      SERVICE_STATUS status;
+    protected:
+      const TCHAR* name;
+    };
+
+    class ServiceHandle {
+    public:
+      ServiceHandle(SC_HANDLE h) : handle(h) {}
+      ~ServiceHandle() {CloseServiceHandle(handle);}
+      operator SC_HANDLE() const {return handle;}
+    protected:
+      SC_HANDLE handle;
+    };
+
+    // -=- Routines used by desktop back-end code to manage desktops/window stations
+
+    //     Returns false under Win9x
+    bool desktopChangeRequired();
+
+    //     Returns true under Win9x
+    bool changeDesktop();
+
+    // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del
+    //     Returns false under Win9x
+    bool emulateCtrlAltDel();
+
+    // -=- Routines to initialise the Event Log target Logger
+    //     Returns false under Win9x
+    bool initEventLogLogger(const TCHAR* srcname);
+
+    // -=- Routines to register/unregister the service
+    //     These routines also take care of registering the required
+    //     event source information, etc.
+    // *** should really accept TCHAR argv
+
+    bool registerService(const TCHAR* name, const TCHAR* desc, int argc, const char* argv[]);
+    bool unregisterService(const TCHAR* name);
+
+    bool startService(const TCHAR* name);
+    bool stopService(const TCHAR* name);
+    void printServiceStatus(const TCHAR* name);
+
+    // -=- Routine to determine whether the host process is running a service
+    bool isServiceProcess();
+
+  };
+
+};
+
+#endif // __RFB_WIN32_SERVICE_NT_H__
diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx
new file mode 100644
index 0000000..6ebd5c0
--- /dev/null
+++ b/rfb_win32/SocketManager.cxx
@@ -0,0 +1,246 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- SocketManager.cxx
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <assert.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/SocketManager.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SocketManager");
+
+
+// -=- SocketManager
+
+SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) {
+}
+
+SocketManager::~SocketManager() {
+  for (int i=0; i<nSockets; i++) {
+    if (!sockets[i].is_event)
+      WSACloseEvent(events[i]);
+  }
+  delete [] events;
+  delete [] sockets;
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) {
+  WSAEVENT event = WSACreateEvent();
+  assert(event != WSA_INVALID_EVENT);
+  addListener(sock_, event, srvr);
+}
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr) {
+  WSAEVENT event = WSACreateEvent();
+  assert(event != WSA_INVALID_EVENT);
+  addSocket(sock_, event, srvr);
+}
+
+
+BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+  while (true) {
+    // First check for idle timeout
+
+    network::SocketServer* server = 0;
+    int timeout = 0;
+    for (int i=0; i<nSockets; i++) {
+      if (!sockets[i].is_event &&
+          sockets[i].server != server) {
+        server = sockets[i].server;
+        int t = server->checkTimeouts();
+        if (t > 0 && (timeout == 0 || t < timeout))
+          timeout = t;
+      }
+    }
+    if (timeout == 0)
+      timeout = INFINITE;
+
+    // - Network IO is less common than messages - process it first
+    DWORD result;
+    if (nSockets) {
+      result = WaitForMultipleObjects(nSockets, events, FALSE, 0);
+      if (result == WAIT_TIMEOUT) {
+        if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+          return msg->message != WM_QUIT;
+
+        result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout,
+                                           QS_ALLINPUT);
+        if (result == WAIT_OBJECT_0 + nSockets) {
+          if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+            return msg->message != WM_QUIT;
+          continue;
+        }
+      }
+    } else
+      return GetMessage(msg, hwnd, minMsg, maxMsg);
+
+    if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + nSockets))) {
+      int index = result - WAIT_OBJECT_0;
+
+      // - Process a socket event
+
+      if (sockets[index].is_event) {
+        // Process a general Win32 event
+        // NB: The handler must reset the event!
+
+        if (!sockets[index].handler->processEvent(events[index])) {
+          removeSocket(index);
+          continue;
+        }
+      } else if (sockets[index].is_conn) {
+        // Process data from an active connection
+
+        // Cancel event notification for this socket
+        if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR)
+          vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+        // Reset the event object
+        WSAResetEvent(events[index]);
+
+        // Call the socket server to process the event
+        if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) {
+          removeSocket(index);
+          continue;
+        }
+
+        // Re-instate the required socket event
+        // If the read event is still valid, the event object gets set here
+        if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR)
+          throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+
+      } else {
+        // Accept an incoming connection
+        vlog.debug("accepting incoming connection");
+
+        // What kind of event is this?
+        WSANETWORKEVENTS network_events;
+        WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events);
+        if (network_events.lNetworkEvents & FD_ACCEPT) {
+          network::Socket* new_sock = sockets[index].sock.listener->accept();
+          if (new_sock) {
+            sockets[index].server->addClient(new_sock);
+            addSocket(new_sock, sockets[index].server);
+          }
+        } else if (network_events.lNetworkEvents & FD_CLOSE) {
+          vlog.info("deleting listening socket");
+          network::SocketListener* s = sockets[index].sock.listener;
+          removeSocket(index);
+          delete s;
+        } else {
+          vlog.error("unknown network event for listener");
+        }
+
+      }
+    } else if (result == WAIT_FAILED) {
+      throw rdr::SystemException("unable to wait for events", GetLastError());
+    }
+  }
+}
+
+
+void SocketManager::resizeArrays(int numSockets) {
+  if (nAvail >= numSockets) return;
+  while (nAvail < numSockets)
+    nAvail = max(16, nAvail*2);
+
+  SocketInfo* newinfo = new SocketInfo[nAvail];
+  HANDLE* newevents = new HANDLE[nAvail];
+  for (int i=0; i<nSockets; i++) {
+    newinfo[i] = sockets[i];
+    newevents[i] = events[i];
+  }
+  delete [] sockets;
+  delete [] events;
+  sockets = newinfo;
+  events = newevents;
+}
+
+void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) {
+  resizeArrays(nSockets+1);
+
+  sockets[nSockets].sock.conn = sock;
+  sockets[nSockets].fd = sock->getFd();
+  sockets[nSockets].server = server;
+  events[nSockets] = event;
+  sockets[nSockets].is_conn = true;
+  sockets[nSockets].is_event = false;
+
+  if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+    throw rdr::SystemException("unable to select on socket", WSAGetLastError());
+  nSockets++;
+}
+
+void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) {
+  resizeArrays(nSockets+1);
+
+  sockets[nSockets].sock.listener = sock;
+  sockets[nSockets].fd = sock->getFd();
+  sockets[nSockets].server = server;
+  events[nSockets] = event;
+  sockets[nSockets].is_conn = false;
+  sockets[nSockets].is_event = false;
+
+  if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
+    throw rdr::SystemException("unable to select on listener", WSAGetLastError());
+  nSockets++;
+}
+
+void SocketManager::remListener(network::SocketListener* sock) {
+  for (int index=0; index<nSockets; index++) {
+    if (!sockets[index].is_conn &&
+        !sockets[index].is_event) {
+      vlog.debug("removing listening socket");
+      removeSocket(index);
+      delete sock;
+    }
+  }
+}
+
+void SocketManager::addEvent(HANDLE event, EventHandler* ecb) {
+  resizeArrays(nSockets+1);
+
+  sockets[nSockets].handler = ecb;
+  events[nSockets] = event;
+  sockets[nSockets].is_conn = false;
+  sockets[nSockets].is_event = true;
+
+  nSockets++;
+}
+
+void SocketManager::removeSocket(int index) {
+  if (index >= nSockets)
+    throw rdr::Exception("attempting to remove unregistered socket");
+
+  if (!sockets[index].is_event)
+    WSACloseEvent(events[index]);
+
+  for (int i=index; i<nSockets-1; i++) {
+    sockets[i] = sockets[i+1];
+    events[i] = events[i+1];
+  }
+
+  nSockets--;
+}
+
diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h
new file mode 100644
index 0000000..791370f
--- /dev/null
+++ b/rfb_win32/SocketManager.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- SocketManager.h
+
+// Socket manager class for Win32.
+// Passed a network::SocketListener and a network::SocketServer when
+// constructed.  Uses WSAAsyncSelect to get notifications of network 
+// connection attempts.  When an incoming connection is received,
+// the manager will call network::SocketServer::addClient().  If
+// addClient returns true then the manager registers interest in
+// network events on that socket, and calls
+// network::SocketServer::processSocketEvent().
+
+#ifndef __RFB_WIN32_SOCKET_MGR_H__
+#define __RFB_WIN32_SOCKET_MGR_H__
+
+#include <list>
+
+#include <network/Socket.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class SocketManager {
+    public:
+      SocketManager();
+      virtual ~SocketManager();
+
+      // Add a listening socket.  Incoming connections will be added to the supplied
+      // SocketServer.
+      void addListener(network::SocketListener* sock_, network::SocketServer* srvr);
+
+      // Remove and delete a listening socket.
+      void remListener(network::SocketListener* sock);
+
+      // Add an already-connected socket.  Socket events will cause the supplied
+      // SocketServer to be called.  The socket must ALREADY BE REGISTERED with
+      // the SocketServer.
+      void addSocket(network::Socket* sock_, network::SocketServer* srvr);
+
+      // Add a Win32 event & handler for it to the SocketManager
+      // This event will be blocked on along with the registered Sockets, and the
+      // handler called whenever it is discovered to be set.
+      // NB: SocketManager does NOT call ResetEvent on the event!
+      // NB: If processEvent returns false then the event is no longer registered,
+      //     and the event object is assumed to have been closed by processEvent()
+      struct EventHandler {
+        virtual ~EventHandler() {}
+        virtual bool processEvent(HANDLE event) = 0;
+      };
+      void addEvent(HANDLE event, EventHandler* ecb);
+
+      // getMessage
+      //
+      // Either return a message from the thread's message queue or process a socket
+      // event.
+      // Returns whenever a message needs processing.  Returns false if message is
+      // WM_QUIT, true for all other messages.
+      BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+    protected:
+      void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server);
+      void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server);
+      void resizeArrays(int numSockets);
+      void removeSocket(int index);
+      struct SocketInfo {
+        union {
+          network::Socket* conn;
+          network::SocketListener* listener;
+        } sock;
+        SOCKET fd;
+        bool is_conn;
+        bool is_event;
+        union {
+          network::SocketServer* server;
+          EventHandler* handler;
+        };
+      };
+      SocketInfo* sockets;
+      HANDLE* events;
+      int nSockets;
+      int nAvail;
+   };
+
+  }
+
+}
+
+#endif
diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx
new file mode 100644
index 0000000..f8f03a6
--- /dev/null
+++ b/rfb_win32/TCharArray.cxx
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  WCHAR* wstrDup(const WCHAR* s) {
+    if (!s) return 0;
+    WCHAR* t = new WCHAR[wcslen(s)+1];
+    memcpy(t, s, sizeof(WCHAR)*(wcslen(s)+1));
+    return t;
+  }
+  void wstrFree(WCHAR* s) {delete [] s;}
+
+  char* strDup(const WCHAR* s) {
+    if (!s) return 0;
+    int len = wcslen(s);
+    char* t = new char[len+1];
+    t[WideCharToMultiByte(CP_ACP, 0, s, len, t, len, 0, 0)] = 0;
+    return t;
+  }
+
+  WCHAR* wstrDup(const char* s) {
+    if (!s) return 0;
+    int len = strlen(s);
+    WCHAR* t = new WCHAR[len+1];
+    t[MultiByteToWideChar(CP_ACP, 0, s, len, t, len)] = 0;
+    return t;
+  }
+
+
+  bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd) {
+    WCharArray out1old, out2old;
+    if (out1) out1old.buf = *out1;
+    if (out2) out2old.buf = *out2;
+    int len = wcslen(src);
+    int i=0, increment=1, limit=len;
+    if (fromEnd) {
+      i=len-1; increment = -1; limit = -1;
+    }
+    while (i!=limit) {
+      if (src[i] == limiter) {
+        if (out1) {
+          *out1 = new WCHAR[i+1];
+          if (i) memcpy(*out1, src, sizeof(WCHAR)*i);
+          (*out1)[i] = 0;
+        }
+        if (out2) {
+          *out2 = new WCHAR[len-i];
+          if (len-i-1) memcpy(*out2, &src[i+1], sizeof(WCHAR)*(len-i-1));
+          (*out2)[len-i-1] = 0;
+        }
+        return true;
+      }
+      i+=increment;
+    }
+    if (out1) *out1 = wstrDup(src);
+    if (out2) *out2 = 0;
+    return false;
+  }
+
+  bool wstrContains(const WCHAR* src, WCHAR c) {
+    int l=wcslen(src);
+    for (int i=0; i<l; i++)
+      if (src[i] == c) return true;
+    return false;
+  }
+
+};
diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h
new file mode 100644
index 0000000..399e00a
--- /dev/null
+++ b/rfb_win32/TCharArray.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- TCharArray.h
+
+// This library contains the wide-character equivalent of CharArray, named
+// WCharArray.  In addition to providing wide-character equivalents of
+// the char* string manipulation functions (strDup, strFree, etc), special
+// versions of those functions are provided which attempt to convert from
+// one format to the other.
+//    e.g. char* t = "hello world"; WCHAR* w = wstrDup(t);
+//    Results in w containing the wide-character text "hello world".
+// For convenience, the WStr and CStr classes are also provided.  These
+// accept an existing (const) WCHAR* or char* null-terminated string and
+// create a read-only copy of that in the desired format.  The new copy
+// will actually be the original copy if the format has not changed, otherwise
+// it will be a new buffer owned by the WStr/CStr.
+
+// In addition to providing wide character functions, this header defines
+// TCHAR* handling classes & functions.  TCHAR is defined at compile time to
+// either char or WCHAR.  Programs can treat this as a third data type and
+// call TStr() whenever a TCHAR* is required but a char* or WCHAR* is supplied,
+// and TStr will do the right thing.
+
+#ifndef __RFB_WIN32_TCHARARRAY_H__
+#define __RFB_WIN32_TCHARARRAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <tchar.h>
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  // -=- String duplication and cleanup functions.
+  //     These routines also handle conversion between WCHAR* and char*
+
+  char* strDup(const WCHAR* s);
+  WCHAR* wstrDup(const WCHAR* s);
+  WCHAR* wstrDup(const char* s);
+  void wstrFree(WCHAR* s);
+
+  bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd=false);
+  bool wstrContains(const WCHAR* src, WCHAR c);
+
+  // -=- Temporary format conversion classes
+  //     CStr accepts WCHAR* or char* and behaves like a char*
+  //     WStr accepts WCHAR* or char* and behaves like a WCHAR*
+
+  struct WStr {
+    WStr(const char* s) : buf(wstrDup(s)), free_(true) {}
+    WStr(const WCHAR* s) : buf(s), free_(false) {}
+    ~WStr() {if (free_) wstrFree((WCHAR*)buf);}
+    operator const WCHAR*() {return buf;}
+    const WCHAR* buf;
+    bool free_;
+  };
+
+  struct CStr {
+    CStr(const char* s) : buf(s), free_(false) {}
+    CStr(const WCHAR* s) : buf(strDup(s)), free_(true) {}
+    ~CStr() {if (free_) strFree((char*)buf);}
+    operator const char*() {return buf;}
+    const char* buf;
+    bool free_;
+  };
+
+  // -=- Class to handle cleanup of arrays of native Win32 characters
+  class WCharArray {
+  public:
+    WCharArray() : buf(0) {}
+    WCharArray(char* str) : buf(wstrDup(str)) {strFree(str);} // note: assumes ownership
+    WCharArray(WCHAR* str) : buf(str) {}                      // note: assumes ownership
+    WCharArray(int len) {
+      buf = new WCHAR[len];
+    }
+    ~WCharArray() {
+      delete [] buf;
+    }
+    // Get the buffer pointer & clear it (i.e. caller takes ownership)
+    WCHAR* takeBuf() {WCHAR* tmp = buf; buf = 0; return tmp;}
+    void replaceBuf(WCHAR* str) {delete [] buf; buf = str;}
+    WCHAR* buf;
+  };
+
+#ifdef _UNICODE
+#define tstrDup wstrDup
+#define tstrFree wstrFree
+#define tstrSplit wstrSplit
+#define tstrContains wstrContains
+  typedef WCharArray TCharArray;
+  typedef WStr TStr;
+#else
+#define tstrDup strDup
+#define tstrFree strFree
+#define tstrSplit strSplit
+#define tstrContains strContains
+  typedef CharArray TCharArray;
+  typedef CStr TStr;
+#endif
+
+};
+
+#endif
\ No newline at end of file
diff --git a/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h
new file mode 100644
index 0000000..85680f3
--- /dev/null
+++ b/rfb_win32/TrayIcon.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CView.h
+
+// An instance of the CView class is created for each VNC Viewer connection.
+
+#ifndef __RFB_WIN32_TRAY_ICON_H__
+#define __RFB_WIN32_TRAY_ICON_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class TrayIcon : public MsgWindow {
+    public:
+      TrayIcon() : MsgWindow(_T("VNCTray")) {
+#ifdef NOTIFYICONDATA_V1_SIZE
+        nid.cbSize = NOTIFYICONDATA_V1_SIZE;
+#else
+        nid.cbSize = sizeof(NOTIFYICONDATA);
+#endif
+
+        nid.hWnd = getHandle();
+        nid.uID = 0;
+        nid.hIcon = 0;
+        nid.uFlags = NIF_ICON | NIF_MESSAGE;
+        nid.uCallbackMessage = WM_USER;
+      }
+      virtual ~TrayIcon() {
+        remove();
+      }
+      bool setIcon(UINT icon) {
+        if (icon == 0) {
+          return remove();
+        } else {
+          nid.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(icon),
+            IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+          return refresh();
+        }
+      }
+      bool setToolTip(const TCHAR* text) {
+        if (text == 0) {
+          nid.uFlags &= ~NIF_TIP;
+        } else {
+          const int tipLen = sizeof(nid.szTip)/sizeof(TCHAR);
+          _tcsncpy(nid.szTip, text, tipLen);
+          nid.szTip[tipLen-1] = 0;
+          nid.uFlags |= NIF_TIP;
+        }
+        return refresh();
+      }
+      bool remove() {
+        return Shell_NotifyIcon(NIM_DELETE, &nid) != 0;
+      }
+      bool refresh() {
+        return Shell_NotifyIcon(NIM_MODIFY, &nid) || Shell_NotifyIcon(NIM_ADD, &nid);
+      }
+    protected:
+      NOTIFYICONDATA nid;
+    };
+
+  };
+
+};
+
+#endif
+
+
diff --git a/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx
new file mode 100644
index 0000000..871d937
--- /dev/null
+++ b/rfb_win32/WMCursor.cxx
@@ -0,0 +1,98 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMCursor.cxx
+
+// *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6
+// *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE
+#define WINVER 0x0500
+
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCursor");
+
+
+typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
+DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+
+
+WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) {
+#if (WINVER >= 0x0500)
+  // Check the OS version
+  bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
+    (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0));
+  bool is_win2K = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osVersion.dwMajorVersion >= 5);
+
+  // Use GetCursorInfo if OS version is sufficient
+  use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid();
+#else
+#pragma message ("not building in GetCursorInfo support")
+#endif
+  if (!use_getCursorInfo) {
+    hooks = new WMCursorHooks();
+    if (hooks && hooks->start()) {
+      vlog.info("falling back to cursor hooking");
+    } else {
+      delete hooks;
+      hooks = 0;
+      vlog.error("unable to monitor cursor shape");
+    }
+  } else {
+    vlog.info("using GetCursorInfo");
+  }
+  cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+}
+
+WMCursor::~WMCursor() {
+  if (hooks) delete hooks;
+  if (library) FreeLibrary(library);
+}
+  
+WMCursor::Info
+WMCursor::getCursorInfo() {
+  Info result;
+#if (WINVER >= 0x0500)
+  if (use_getCursorInfo) {
+    CURSORINFO info;
+    info.cbSize = sizeof(CURSORINFO);
+    if ((*_GetCursorInfo)(&info)) {
+      result.cursor = info.hCursor;
+      result.position = Point(info.ptScreenPos.x, info.ptScreenPos.y);
+      result.visible = info.flags & CURSOR_SHOWING;
+      return result;
+    }
+  }
+#endif
+  // Fall back to the old way of doing things
+  POINT pos;
+  if (hooks) cursor = hooks->getCursor();
+  result.cursor = cursor;
+  result.visible = cursor != 0;
+  GetCursorPos(&pos);
+  result.position.x = pos.x;
+  result.position.y = pos.y;
+  return result;
+}
diff --git a/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h
new file mode 100644
index 0000000..a96822a
--- /dev/null
+++ b/rfb_win32/WMCursor.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMCursor.h
+
+// WMCursor provides a single API through which the cursor state can be obtained
+// The underlying implementation will use either GetCursorInfo, or use the
+// wm_hooks library if GetCursorInfo is not available.
+
+#ifndef __RFB_WIN32_WM_CURSOR_H__
+#define __RFB_WIN32_WM_CURSOR_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb_win32/WMHooks.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class WMCursor {
+    public:
+      WMCursor();
+      ~WMCursor();
+
+      struct Info {
+        HCURSOR cursor;
+        Point position;
+        bool visible;
+        Info() : cursor(0), visible(false) {}
+        bool operator!=(const Info& info) {
+          return ((cursor != info.cursor) ||
+            (!position.equals(info.position)) ||
+            (visible != info.visible));
+        }
+      };
+
+      Info getCursorInfo();
+    protected:
+      WMCursorHooks* hooks;
+      HMODULE library;
+      bool use_getCursorInfo;
+      HCURSOR cursor;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx
new file mode 100644
index 0000000..26a2363
--- /dev/null
+++ b/rfb_win32/WMHooks.cxx
@@ -0,0 +1,324 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMHooks.cxx
+
+#include <wm_hooks/wm_hooks.h>
+
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/Service.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMHooks");
+
+class WMHooksThread : public Thread {
+public:
+  WMHooksThread() : Thread("WMHookThread"), active(true) {}
+  virtual void run();
+  virtual Thread* join();
+protected:
+  bool active;
+};
+
+WMHooksThread* hook_mgr = 0;
+std::list<WMHooks*> hooks;
+std::list<WMCursorHooks*> cursor_hooks;
+Mutex hook_mgr_lock;
+HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+
+bool
+StartHookThread() {
+  if (hook_mgr) return true;
+  vlog.debug("opening hook thread");
+  hook_mgr = new WMHooksThread();
+  if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
+    vlog.error("failed to initialise hooks");
+    delete hook_mgr->join();
+    hook_mgr = 0;
+    return false;
+  }
+  hook_mgr->start();
+  return true;
+}
+
+void
+StopHookThread() {
+  if (!hook_mgr) return;
+  if (!hooks.empty() || !cursor_hooks.empty()) return;
+  vlog.debug("closing hook thread");
+  delete hook_mgr->join();
+  hook_mgr = 0;
+}
+
+
+bool
+AddHook(WMHooks* hook) {
+  vlog.debug("adding hook");
+  Lock l(hook_mgr_lock);
+  if (!StartHookThread()) return false;
+  hooks.push_back(hook);
+  return true;
+}
+
+bool
+AddCursorHook(WMCursorHooks* hook) {
+  vlog.debug("adding cursor hook");
+  Lock l(hook_mgr_lock);
+  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
+  if (!StartHookThread()) return false;
+  cursor_hooks.push_back(hook);
+  return true;
+}
+
+bool
+RemHook(WMHooks* hook) {
+  {
+    vlog.debug("removing hook");
+    Lock l(hook_mgr_lock);
+    hooks.remove(hook);
+  }
+  StopHookThread();
+  return true;
+}
+
+bool
+RemCursorHook(WMCursorHooks* hook) {
+  {
+    vlog.debug("removing cursor hook");
+    Lock l(hook_mgr_lock);
+    cursor_hooks.remove(hook);
+  }
+  StopHookThread();
+  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
+  return true;
+}
+
+void
+NotifyHooksRegion(const Region& r) {
+  Lock l(hook_mgr_lock);
+  std::list<WMHooks*>::iterator i;
+  for (i=hooks.begin(); i!=hooks.end(); i++) {
+    (*i)->new_changes.add_changed(r);
+    if (!(*i)->notified) {
+      (*i)->notified = true;
+      PostMessage((*i)->getHandle(), WM_USER, 0, 0);
+    }
+  }
+}
+
+void
+NotifyHooksCursor(HCURSOR c) {
+  Lock l(hook_mgr_lock);
+  hook_cursor = c;
+}
+
+void
+WMHooksThread::run() {
+  UINT windowMsg = WM_Hooks_WindowChanged();
+  UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
+  UINT borderMsg = WM_Hooks_WindowBorderChanged();
+  UINT rectangleMsg = WM_Hooks_RectangleChanged();
+  UINT cursorMsg = WM_Hooks_CursorChanged();
+#ifdef _DEBUG
+  UINT diagnosticMsg = WM_Hooks_Diagnostic();
+#endif
+  MSG msg;
+  RECT wrect;
+  HWND hwnd;
+  int count = 0;
+
+  vlog.debug("starting hook thread");
+
+  while (active && GetMessage(&msg, NULL, 0, 0)) {
+    count++;
+    if (msg.message == windowMsg) {
+      hwnd = (HWND) msg.lParam;
+      if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+          GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+      {
+        NotifyHooksRegion(Rect(wrect.left, wrect.top,
+                               wrect.right, wrect.bottom));
+
+      }
+    } else if (msg.message == clientAreaMsg) {
+      hwnd = (HWND) msg.lParam;
+      if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+          GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+      {
+        POINT pt = {0,0};
+        if (ClientToScreen(hwnd, &pt)) {
+          NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
+                                 wrect.right+pt.x, wrect.bottom+pt.y));
+        }
+      }
+    } else if (msg.message == borderMsg) {
+      hwnd = (HWND) msg.lParam;
+      if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+          GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+      {
+        Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom));
+        RECT crect;
+        POINT pt = {0,0};
+        if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) &&
+            !IsRectEmpty(&crect))
+        {
+          changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
+                                       crect.right+pt.x, crect.bottom+pt.y));
+        }
+        NotifyHooksRegion(changed);
+      }
+    } else if (msg.message == rectangleMsg) {
+      Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
+                    LOWORD(msg.lParam), HIWORD(msg.lParam));
+      if (!r.is_empty()) {
+        NotifyHooksRegion(r);
+      }
+    } else if (msg.message == cursorMsg) {
+      NotifyHooksCursor((HCURSOR)msg.lParam);
+#ifdef _DEBUG
+    } else if (msg.message == diagnosticMsg) {
+      vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam);
+#endif
+    }
+  }
+
+  vlog.debug("stopping hook thread - processed %d events", count);
+  WM_Hooks_Remove(getThreadId());
+}
+
+Thread*
+WMHooksThread::join() {
+  vlog.debug("stopping WMHooks thread");
+  active = false;
+  PostThreadMessage(thread_id, WM_QUIT, 0, 0);
+  vlog.debug("joining WMHooks thread");
+  return Thread::join();
+}
+
+// -=- WMHooks class
+
+rfb::win32::WMHooks::WMHooks()
+  : clipper(0), new_changes(true), fg_window(0),
+  notified(false), MsgWindow(_T("WMHooks")) {
+}
+
+rfb::win32::WMHooks::~WMHooks() {
+  RemHook(this);
+  if (clipper) delete clipper;
+}
+
+LRESULT
+rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+  case WM_USER:
+    {
+      // *** Yield, to allow the triggering update event to be processed
+      //     BEFORE we try to grab the resulting changes.
+      // *** IMPROVES THINGS NOTICABLY ON WinXP
+      Sleep(0);
+      // ***
+
+      Lock l(hook_mgr_lock);
+      notified = false;
+      new_changes.get_update(*clipper);
+      new_changes.clear();
+    }
+    break;
+  }
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+bool
+rfb::win32::WMHooks::setClipRect(const Rect& r) {
+  clip_region = r;
+  if (clipper) clipper->set_clip_region(clip_region);
+  return true;
+}
+
+bool
+rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
+  if (clipper) delete clipper;
+  clipper = new ClippedUpdateTracker(*ut);
+  clipper->set_clip_region(clip_region);
+  return AddHook(this);
+}
+
+#ifdef _DEBUG
+void
+rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
+  WM_Hooks_SetDiagnosticRange(min, max);
+}
+#endif
+
+
+// -=- WMBlockInput class
+
+Mutex blockMutex;
+int blockCount = 0;
+
+rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
+}
+
+rfb::win32::WMBlockInput::~WMBlockInput() {
+  blockInputs(false);
+}
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+  if (on == active) return true;
+  vlog.debug("blockInput changed");
+  Lock l(blockMutex);
+  int newCount = blockCount;
+  if (on)
+    newCount++;
+  else
+    newCount--;
+  if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
+    vlog.debug("set blocking to %d", newCount);
+    blockCount = newCount;
+    active = on;
+    return true;
+  }
+  return false;
+}
+
+
+// -=- WMCursorHooks class
+
+rfb::win32::WMCursorHooks::WMCursorHooks() {
+}
+
+rfb::win32::WMCursorHooks::~WMCursorHooks() {
+  RemCursorHook(this);
+}
+
+bool
+rfb::win32::WMCursorHooks::start() {
+  return AddCursorHook(this);
+}
+
+HCURSOR
+rfb::win32::WMCursorHooks::getCursor() const {
+  return hook_cursor;
+}
diff --git a/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h
new file mode 100644
index 0000000..791df76
--- /dev/null
+++ b/rfb_win32/WMHooks.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMHooks.h
+
+#ifndef __RFB_WIN32_WM_HOOKS_H__
+#define __RFB_WIN32_WM_HOOKS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class WMHooks : public MsgWindow {
+    public:
+      WMHooks();
+      ~WMHooks();
+
+      bool setClipRect(const Rect& cr);
+      bool setUpdateTracker(UpdateTracker* ut);
+
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+#ifdef _DEBUG
+      // Get notifications of any messages in the given range, to any hooked window
+      void setDiagnosticRange(UINT min, UINT max);
+#endif
+
+    protected:
+      ClippedUpdateTracker* clipper;
+      Region clip_region;
+
+      void* fg_window;
+      Rect fg_window_rect;
+
+    public:
+      SimpleUpdateTracker new_changes;
+      bool notified;
+    };
+
+    class WMBlockInput {
+    public:
+      WMBlockInput();
+      ~WMBlockInput();
+      bool blockInputs(bool block);
+    protected:
+      bool active;
+    };
+
+    // - Legacy cursor handling support
+    class WMCursorHooks {
+    public:
+      WMCursorHooks();
+      ~WMCursorHooks();
+
+      bool start();
+
+      HCURSOR getCursor() const;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_WM_HOOKS_H__
diff --git a/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx
new file mode 100644
index 0000000..9773abf
--- /dev/null
+++ b/rfb_win32/WMNotifier.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMNotifier.cxx
+
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/MsgWindow.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMMonitor");
+
+
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) {
+}
+
+WMMonitor::~WMMonitor() {
+}
+
+
+LRESULT
+WMMonitor::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+  case WM_DISPLAYCHANGE:
+    if (notifier) {
+      notifier->notifyDisplayEvent(Notifier::DisplaySizeChanged);
+      notifier->notifyDisplayEvent(Notifier::DisplayPixelFormatChanged);
+    }
+    break;
+	case WM_SYSCOLORCHANGE:
+  case WM_PALETTECHANGED:
+    if (notifier) {
+      notifier->notifyDisplayEvent(Notifier::DisplayColourMapChanged);
+    }
+    break;
+  };
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
diff --git a/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h
new file mode 100644
index 0000000..564d176
--- /dev/null
+++ b/rfb_win32/WMNotifier.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMNotifier.h
+//
+// The WMNotifier is used to get callbacks indicating changes in the state
+// of the system, for instance in the size/format/palette of the display.
+// The WMNotifier contains a Win32 window, which receives notifications of
+// system events and stores them.  Whenever processEvent is called, any
+// incoming events are processed and the appropriate notifier called.
+
+#ifndef __RFB_WIN32_NOTIFIER_H__
+#define __RFB_WIN32_NOTIFIER_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    // -=- Window Message Monitor implementation
+
+    class WMMonitor : MsgWindow {
+    public:
+
+      class Notifier {
+      public:
+        typedef enum {DisplaySizeChanged, DisplayColourMapChanged,
+          DisplayPixelFormatChanged} DisplayEventType;
+        virtual void notifyDisplayEvent(DisplayEventType evt) = 0;
+      };
+
+      WMMonitor();
+      virtual ~WMMonitor();
+
+      void setNotifier(Notifier* wmn) {notifier=wmn;}
+
+    protected:
+      // - Internal MsgWindow callback
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      Notifier* notifier;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_WMNOTIFIER_H__
diff --git a/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx
new file mode 100644
index 0000000..f568b21
--- /dev/null
+++ b/rfb_win32/WMPoller.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMPoller.cxx
+
+#include <rfb_win32/WMPoller.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMPoller");
+
+BoolParameter rfb::win32::WMPoller::poll_console_windows("PollConsoleWindows",
+  "Server should poll console windows for updates", true);
+
+// -=- WMPoller class
+
+rfb::win32::WMPoller::WMPoller() : clipper(0) {
+}
+
+rfb::win32::WMPoller::~WMPoller() {
+  if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMPoller::processEvent() {
+  PollInfo info;
+  if (clipper && poll_console_windows) {
+    ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
+    clipper->add_changed(info.poll_include);
+  }
+  return false;
+}
+
+bool
+rfb::win32::WMPoller::setClipRect(const Rect& r) {
+  clip_region = r;
+  if (clipper) clipper->set_clip_region(clip_region);
+  return true;
+}
+
+bool
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) {
+  if (clipper) delete clipper;
+  clipper = new ClippedUpdateTracker(*ut);
+  clipper->set_clip_region(clip_region);
+  return true;
+}
+
+bool
+rfb::win32::WMPoller::checkPollWindow(HWND w) {
+  TCHAR buffer[128];
+  if (!GetClassName(w, buffer, 128))
+    throw rdr::SystemException("unable to get window class:%u", GetLastError());
+  if ((_tcscmp(buffer, _T("tty")) != 0) &&
+    (_tcscmp(buffer, _T("ConsoleWindowClass")) != 0)) {
+    return false;
+  }
+  return true;
+}
+
+void
+rfb::win32::WMPoller::pollWindow(HWND w, PollInfo* i) {
+  RECT r;
+  if (IsWindowVisible(w) && GetWindowRect(w, &r)) {
+    if (IsRectEmpty(&r)) return;
+    Region wrgn(Rect(r.left, r.top, r.right, r.bottom));
+    if (checkPollWindow(w)) {
+      wrgn.assign_subtract(i->poll_exclude);
+      i->poll_include.assign_union(wrgn);
+    } else {
+      i->poll_exclude.assign_union(wrgn);
+    }
+  }
+}
+
+BOOL CALLBACK
+rfb::win32::WMPoller::enumWindowProc(HWND w, LPARAM lp) {
+  pollWindow(w, (PollInfo*)lp);
+  return TRUE;
+}
diff --git a/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h
new file mode 100644
index 0000000..3f3f402
--- /dev/null
+++ b/rfb_win32/WMPoller.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMPoller.h
+//
+// Polls the foreground window.  If the pollOnlyConsoles flag is set,
+// then checks the window class of the foreground window first and
+// only polls it if it's a console.
+// If the pollAllWindows flag is set then iterates through visible
+// windows, and polls the visible bits.  If pollOnlyConsoles is also
+// set then only visible parts of console windows will be polled.
+
+#ifndef __RFB_WIN32_WM_POLLER_H__
+#define __RFB_WIN32_WM_POLLER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class WMPoller {
+    public:
+      WMPoller();
+      ~WMPoller();
+
+      bool processEvent();
+      bool setClipRect(const Rect& cr);
+      bool setUpdateTracker(UpdateTracker* ut);
+
+      static BoolParameter poll_console_windows;
+    protected:
+      struct PollInfo {
+        Region poll_include;
+        Region poll_exclude;
+      };
+      static bool checkPollWindow(HWND w);
+      static void pollWindow(HWND w, PollInfo* info);
+      static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp);
+
+      ClippedUpdateTracker* clipper;
+      Region clip_region;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_WM_POLLER_H__
diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx
new file mode 100644
index 0000000..f6a7484
--- /dev/null
+++ b/rfb_win32/WMShatter.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMShatter.cxx
+
+#include <rfb_win32/WMShatter.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMShatter");
+
+bool
+rfb::win32::IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+  bool result = true;
+  switch (msg) {
+    // - UNSAFE MESSAGES
+  case WM_TIMER:
+    result = lParam == 0;
+    break;
+  };
+  if (!result) {
+    vlog.info("IsSafeWM: 0x%x received 0x%x(%u, %lu) - not safe", window, msg, wParam, lParam);
+  }
+  return result;
+}
+
+LRESULT
+rfb::win32::SafeDefWindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (IsSafeWM(window, msg, wParam, lParam))
+    return DefWindowProc(window, msg, wParam, lParam);
+  return 0;
+}
+
+LRESULT
+rfb::win32::SafeDispatchMessage(const MSG* msg) {
+  if (IsSafeWM(msg->hwnd, msg->message, msg->wParam, msg->lParam))
+    return DispatchMessage(msg);
+  return 0;
+}
diff --git a/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h
new file mode 100644
index 0000000..7b81678
--- /dev/null
+++ b/rfb_win32/WMShatter.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMShatter.h
+//
+// WMShatter provides the IsSafeWM routine, which returns true iff the
+// supplied window message is safe to pass to DispatchMessage, or to
+// process in the window procedure.
+//
+// This is only required, of course, to avoid so-called "shatter" attacks
+// to be made against the VNC server, which take advantage of the noddy
+// design of the Win32 window messaging system.
+//
+// The API here is designed to hopefully be future proof, so that if they
+// ever come up with a proper way to determine whether a message is safe
+// or not then it can just be reimplemented here...
+
+#ifndef __RFB_WIN32_SHATTER_H__
+#define __RFB_WIN32_SHATTER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
+
+    LRESULT SafeDefWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+    LRESULT SafeDispatchMessage(const MSG* msg);
+
+  };
+
+};
+
+#endif // __RFB_WIN32_SHATTER_H__
diff --git a/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx
new file mode 100644
index 0000000..46d85ea
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMCopyRect.cxx
+
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb/LogWriter.h>
+#include <windows.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCopyRect");
+
+// -=- WMHooks class
+
+rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) {
+}
+
+rfb::win32::WMCopyRect::~WMCopyRect() {
+  if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMCopyRect::processEvent() {
+  if (clipper) {
+    // See if the foreground window has moved
+    HWND window = GetForegroundWindow();
+    if (window) {
+      RECT wrect;
+      if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
+        Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+        if (fg_window == window) {
+
+          if (!fg_window_rect.tl.equals(winrect.tl)) {
+            // Window has moved - send a copyrect event to the client
+            Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
+            Region copy_dest = winrect;
+            clipper->add_copied(copy_dest, delta);
+            clipper->add_changed(Region(fg_window_rect).subtract(copy_dest));
+          }
+        }
+        fg_window = window;
+        fg_window_rect = winrect;
+      } else {
+        fg_window = 0;
+      }
+    } else {
+      fg_window = 0;
+    }
+  }
+  return false;
+}
+
+bool
+rfb::win32::WMCopyRect::setClipRect(const Rect& r) {
+  clip_region = r;
+  if (clipper) clipper->set_clip_region(clip_region);
+  return true;
+}
+
+bool
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) {
+  if (clipper) delete clipper;
+  clipper = new ClippedUpdateTracker(*ut);
+  clipper->set_clip_region(clip_region);
+  return true;
+}
diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h
new file mode 100644
index 0000000..0750d86
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WMWindowCopyRect.h
+//
+// Helper class which produces copyRect actions by monitoring the location
+// of the current foreground window.
+// Whenever processEvent is called, the foreground window's position is
+// recalculated and a copy event flushed to the supplied UpdateTracker
+// if appropriate.
+
+#ifndef __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+#define __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class WMCopyRect {
+    public:
+      WMCopyRect();
+      ~WMCopyRect();
+
+      bool processEvent();
+      bool setClipRect(const Rect& cr);
+      bool setUpdateTracker(UpdateTracker* ut);
+
+    protected:
+      ClippedUpdateTracker* clipper;
+      Region clip_region;
+      void* fg_window;
+      Rect fg_window_rect;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__
diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx
new file mode 100644
index 0000000..e25f43a
--- /dev/null
+++ b/rfb_win32/Win32Util.cxx
@@ -0,0 +1,447 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// Win32Util.cxx
+
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rdr/HexOutStream.h>
+
+
+namespace rfb {
+namespace win32 {
+
+LogicalPalette::LogicalPalette() : palette(0), numEntries(0) {
+  BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+  LOGPALETTE* logpal = (LOGPALETTE*)buf;
+  logpal->palVersion = 0x300;
+  logpal->palNumEntries = 256;
+  for (int i=0; i<256;i++) {
+    logpal->palPalEntry[i].peRed = 0;
+    logpal->palPalEntry[i].peGreen = 0;
+    logpal->palPalEntry[i].peBlue = 0;
+    logpal->palPalEntry[i].peFlags = 0;
+  }
+  palette = CreatePalette(logpal);
+  if (!palette)
+    throw rdr::SystemException("failed to CreatePalette", GetLastError());
+}
+
+LogicalPalette::~LogicalPalette() {
+  if (palette)
+    if (!DeleteObject(palette))
+      throw rdr::SystemException("del palette failed", GetLastError());
+}
+
+void LogicalPalette::setEntries(int start, int count, const Colour* cols) {
+  if (numEntries < count) {
+    ResizePalette(palette, start+count);
+    numEntries = start+count;
+  }
+  PALETTEENTRY* logpal = new PALETTEENTRY[count];
+  for (int i=0; i<count; i++) {
+    logpal[i].peRed = cols[i].r >> 8;
+    logpal[i].peGreen = cols[i].g >> 8;
+    logpal[i].peBlue = cols[i].b >> 8;
+    logpal[i].peFlags = 0;
+  }
+  UnrealizeObject(palette);
+  SetPaletteEntries(palette, start, count, logpal);
+  delete [] logpal;
+}
+
+
+static LogWriter dcLog("DeviceContext");
+
+PixelFormat DeviceContext::getPF() const {
+  return getPF(dc);
+}
+
+PixelFormat DeviceContext::getPF(HDC dc) {
+  PixelFormat format;
+  CompatibleBitmap bitmap(dc, 1, 1);
+
+  // -=- Get the bitmap format information
+  BitmapInfo bi;
+  memset(&bi, 0, sizeof(bi));
+  bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bi.bmiHeader.biBitCount = 0;
+
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine device pixel format", GetLastError());
+  }
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
+  }
+
+  // -=- Munge the bitmap info here
+  switch (bi.bmiHeader.biBitCount) {
+  case 1:
+  case 4:
+    bi.bmiHeader.biBitCount = 8;
+    break;
+  case 24:
+    bi.bmiHeader.biBitCount = 32;
+    break;
+  }
+  bi.bmiHeader.biPlanes = 1;
+
+  format.trueColour = bi.bmiHeader.biBitCount > 8;
+  format.bigEndian = 0;
+  format.bpp = format.depth = bi.bmiHeader.biBitCount;
+
+  if (format.trueColour) {
+    DWORD rMask=0, gMask=0, bMask=0;
+
+    // Which true colour format is the DIB section using?
+    switch (bi.bmiHeader.biCompression) {
+    case BI_RGB:
+      // Default RGB layout
+      switch (bi.bmiHeader.biBitCount) {
+      case 16:
+        // RGB 555 - High Colour
+        dcLog.info("16-bit High Colour");
+        rMask = 0x7c00;
+        bMask = 0x001f;
+        gMask = 0x03e0;
+        format.depth = 15;
+        break;
+      case 24:
+      case 32:
+        // RGB 888 - True Colour
+        dcLog.info("24/32-bit High Colour");
+        rMask = 0xff0000;
+        gMask = 0x00ff00;
+        bMask = 0x0000ff;
+        format.depth = 24;
+        break;
+      default:
+        dcLog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
+        throw rdr::Exception("unknown bits per pixel specified");
+      };
+      break;
+    case BI_BITFIELDS:
+      // Custom RGB layout
+      rMask = bi.mask.red;
+      gMask = bi.mask.green;
+      bMask = bi.mask.blue;
+      dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)",
+        bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+      if (format.bpp == 32)
+        format.depth = 24; // ...probably
+      break;
+    };
+
+    // Convert the data we just retrieved
+    initMaxAndShift(rMask, &format.redMax, &format.redShift);
+    initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
+    initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
+  }
+
+  return format;
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+  dc = GetDC(wnd);
+  if (!dc)
+    throw rdr::SystemException("GetDC failed", GetLastError());
+}
+WindowDC::~WindowDC() {
+  if (dc)
+    ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+  dc = CreateCompatibleDC(existing);
+  if (!dc)
+    throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+CompatibleDC::~CompatibleDC() {
+  if (dc)
+    DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+  oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+  if (!oldBitmap)
+    throw rdr::SystemException("SelectObject to CompatibleDC failed",
+    GetLastError());
+}
+BitmapDC::~BitmapDC() {
+  SelectObject(dc, oldBitmap);
+}
+
+
+CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) {
+  hbmp = CreateCompatibleBitmap(hdc, width, height);
+  if (!hbmp)
+    throw rdr::SystemException("CreateCompatibleBitmap() failed", 
+    GetLastError());
+}
+CompatibleBitmap::~CompatibleBitmap() {
+  if (hbmp) DeleteObject(hbmp);
+}
+
+
+PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+  oldPal = SelectPalette(dc, pal, FALSE);
+  redrawRequired = RealizePalette(dc) > 0;
+}
+PaletteSelector::~PaletteSelector() {
+  if (oldPal) SelectPalette(device, oldPal, TRUE);
+}
+
+
+IconInfo::IconInfo(HICON icon) {
+  if (!GetIconInfo(icon, this))
+    throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+}
+IconInfo::~IconInfo() {
+  if (hbmColor)
+    DeleteObject(hbmColor);
+  if (hbmMask)
+    DeleteObject(hbmMask);
+}
+
+
+ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) {
+  if (!module) module = GetModuleHandle(0);
+  if (!GetModuleFileName(module, buf, MAX_PATH))
+    buf[0] = 0;
+}
+
+
+FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
+  // Get executable name
+  ModuleFileName exeName;
+  if (!filename) filename = exeName.buf;
+
+  // Get version info size
+  DWORD handle;
+  int size = GetFileVersionInfoSize((TCHAR*)filename, &handle);
+  if (!size)
+    throw rdr::SystemException("GetVersionInfoSize failed", GetLastError());
+
+  // Get version info
+  buf = new TCHAR[size];
+  if (!GetFileVersionInfo((TCHAR*)filename, handle, size, buf))
+    throw rdr::SystemException("GetVersionInfo failed", GetLastError());
+}
+
+const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) {
+  char langIdBuf[sizeof(langId)];
+  for (int i=sizeof(langIdBuf)-1; i>=0; i--) {
+    langIdBuf[i] = langId & 0xff;
+    langId = langId >> 8;
+  }
+
+  TCharArray langIdStr = rdr::HexOutStream::binToHexStr(langIdBuf, sizeof(langId));
+  TCharArray infoName(_tcslen(_T("StringFileInfo")) + 4 + _tcslen(name) + _tcslen(langIdStr.buf));
+  _stprintf(infoName.buf, _T("\\StringFileInfo\\%s\\%s"), langIdStr.buf, name);
+
+  // Locate the required version string within the version info
+  TCHAR* buffer = 0;
+  UINT length = 0;
+  if (!VerQueryValue(buf, infoName.buf, (void**)&buffer, &length)) {
+    printf("unable to find %s version string", CStr(infoName.buf));
+    throw rdr::Exception("VerQueryValue failed");
+  }
+  return buffer;
+}
+
+
+bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) {
+  return tstrSplit(path, '\\', dir, file, true);
+}
+
+
+static LogWriter dfbLog("DynamicFn");
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+  dllHandle = LoadLibrary(dllName);
+  if (!dllHandle) {
+    dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+    return;
+  }
+  fnPtr = GetProcAddress(dllHandle, fnName);
+  if (!fnPtr)
+    dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+  if (dllHandle)
+    FreeLibrary(dllHandle);
+}
+
+
+static LogWriter miLog("MonitorInfo");
+
+MonitorInfo::MonitorInfo(HWND window) {
+#if (WINVER >= 0x0500)
+  typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+  rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+  typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+  rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+  // Can we dynamically link to the monitor functions?
+  if (_MonitorFromWindow.isValid()) {
+    if (_GetMonitorInfo.isValid()) {
+      HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+      miLog.debug("monitor=%lx", monitor);
+      if (monitor) {
+        memset(this, 0, sizeof(MONITORINFOEXA));
+        cbSize = sizeof(MONITORINFOEXA);
+        if ((*_GetMonitorInfo)(monitor, this)) {
+          miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
+          miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
+          miLog.debug("device is \"%s\"", szDevice);
+          return;
+        }
+        miLog.error("failed to get monitor info: %ld", GetLastError());
+      }
+    } else {
+      miLog.debug("GetMonitorInfo not found");
+    }
+  } else {
+      miLog.debug("MonitorFromWindow not found");
+  }
+#else
+#pragma message ("not building in GetMonitorInfo")
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+#endif
+
+  // Legacy fallbacks - just return the desktop settings
+  miLog.debug("using legacy fall-backs");
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+
+#if (WINVER >= 0x0500)
+
+struct moveToMonitorData {
+  HWND window;
+  const char* monitorName;
+};
+
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor,
+                                    HDC dc,
+                                    LPRECT pos,
+                                    LPARAM d) {
+  moveToMonitorData* data = (moveToMonitorData*)d;
+  MONITORINFOEXA info;
+  memset(&info, 0, sizeof(info));
+  info.cbSize = sizeof(info);
+
+  if ((*_GetMonitorInfo)(monitor, &info)) {
+    if (stricmp(data->monitorName, info.szDevice) == 0) {
+      SetWindowPos(data->window, 0,
+        info.rcMonitor.left, info.rcMonitor.top,
+        info.rcMonitor.right, info.rcMonitor.bottom,
+        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+#endif
+
+void moveToMonitor(HWND handle, const char* device) {
+  miLog.debug("moveToMonitor %s", device);
+
+#if (WINVER >= 0x500)
+  typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+  rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+  if (!_EnumDisplayMonitors.isValid()) {
+    miLog.debug("EnumDisplayMonitors not found");
+    return;
+  }
+
+  moveToMonitorData data;
+  data.window = handle;
+  data.monitorName = device;
+
+  (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data);
+#endif
+}
+
+
+void centerWindow(HWND handle, HWND parent, bool clipToParent) {
+  RECT r;
+  if (parent && IsWindowVisible(parent)) {
+    if (!GetWindowRect(parent, &r)) return;
+  } else {
+    MonitorInfo mi(handle);
+    r=mi.rcWork;
+  }
+  centerWindow(handle, r, clipToParent);
+}
+
+void centerWindow(HWND handle, const RECT& r, bool clipToRect) {
+  RECT wr;
+  if (!GetWindowRect(handle, &wr)) return;
+  int w = wr.right-wr.left;
+  int h = wr.bottom-wr.top;
+  if (clipToRect) {
+    w = min(r.right-r.left, w);
+    h = min(r.bottom-r.top, h);
+  }
+  int x = (r.left + r.right - w)/2;
+  int y = (r.top + r.bottom - h)/2;
+  UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE);
+  SetWindowPos(handle, 0, x, y, w, h, flags);
+}
+
+
+int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
+  const TCHAR* msgType = 0;
+  UINT tflags = flags & 0x70;
+  if (tflags == MB_ICONHAND)
+    msgType = _T("Error");
+  else if (tflags == MB_ICONQUESTION)
+    msgType = _T("Question");
+  else if (tflags == MB_ICONEXCLAMATION)
+    msgType = _T("Warning");
+  else if (tflags == MB_ICONASTERISK)
+    msgType = _T("Information");
+  flags |= MB_TOPMOST | MB_SETFOREGROUND;
+  int len = _tcslen(AppName.buf) + 1;
+  if (msgType) len += _tcslen(msgType) + 3;
+  TCharArray title = new TCHAR[len];
+  _tcscpy(title.buf, AppName.buf);
+  if (msgType) {
+    _tcscat(title.buf, _T(" : "));
+    _tcscat(title.buf, msgType);
+  }
+  return MessageBox(parent, msg, title.buf, flags);
+}
+
+
+};
+};
diff --git a/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h
new file mode 100644
index 0000000..5f0ab5a
--- /dev/null
+++ b/rfb_win32/Win32Util.h
@@ -0,0 +1,217 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- Win32Util.h
+
+// Miscellaneous but useful Win32 API utility functions & classes.
+// In particular, a set of classes which wrap GDI objects,
+// and some to handle palettes.
+
+#ifndef __RFB_WIN32_GDIUTIL_H__
+#define __RFB_WIN32_GDIUTIL_H__
+
+#include <rfb/ColourMap.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class LogicalPalette {
+    public:
+      LogicalPalette();
+      ~LogicalPalette();
+      void setEntries(int start, int count, const Colour* cols);
+      HPALETTE getHandle() {return palette;}
+    protected:
+      HPALETTE palette;
+      int numEntries;
+    };
+
+    class DeviceContext {
+    public:
+      DeviceContext() : dc(0) {}
+      virtual ~DeviceContext() {}
+      operator HDC() const {return dc;}
+      PixelFormat getPF() const;
+      static PixelFormat getPF(HDC dc);
+    protected:
+      HDC dc;
+    };
+
+    class WindowDC : public DeviceContext {
+    public:
+      WindowDC(HWND wnd);
+      virtual ~WindowDC();
+    protected:
+      HWND hwnd;
+    };
+
+    class CompatibleDC : public DeviceContext {
+    public:
+      CompatibleDC(HDC existing);
+      virtual ~CompatibleDC();
+    };
+
+    class BitmapDC : public CompatibleDC {
+    public:
+      BitmapDC(HDC hdc, HBITMAP hbitmap);
+      ~BitmapDC();
+    protected:
+      HBITMAP oldBitmap;
+    };
+
+    class CompatibleBitmap {
+    public:
+      CompatibleBitmap(HDC hdc, int width, int height);
+      virtual ~CompatibleBitmap();
+      operator HBITMAP() const {return hbmp;}
+    protected:
+      HBITMAP hbmp;
+    };
+
+    struct BitmapInfo {
+      BITMAPINFOHEADER bmiHeader;
+      union {
+        struct {
+          DWORD red;
+          DWORD green;
+          DWORD blue;
+        } mask;
+        RGBQUAD color[256];
+      };
+    };
+
+    inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+      for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+        (*max) = (rdr::U16)mask;
+    }
+
+    class PaletteSelector {
+    public:
+      PaletteSelector(HDC dc, HPALETTE pal);
+      ~PaletteSelector();
+      bool isRedrawRequired() {return redrawRequired;}
+    protected:
+      HPALETTE oldPal;
+      HDC device;
+      bool redrawRequired;
+    };
+
+    struct IconInfo : public ICONINFO {
+      IconInfo(HICON icon);
+      ~IconInfo();
+    };
+
+    struct ModuleFileName : public TCharArray {
+      ModuleFileName(HMODULE module=0);
+    };
+
+    struct FileVersionInfo : public TCharArray {
+      FileVersionInfo(const TCHAR* filename=0);
+      const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0);
+    };
+
+    bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file);
+
+    class DynamicFnBase {
+    public:
+      DynamicFnBase(const TCHAR* dllName, const char* fnName);
+      ~DynamicFnBase();
+      bool isValid() const {return fnPtr != 0;}
+    protected:
+      void* fnPtr;
+      HMODULE dllHandle;
+    };
+
+    template<class T> class DynamicFn : public DynamicFnBase {
+    public:
+      DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+      T operator *() const {return (T)fnPtr;};
+    };
+
+    // Structure containing info on the monitor nearest the window.
+    // Copes with multi-monitor OSes and older ones.
+#if (WINVER >= 0x0500)
+    struct MonitorInfo : MONITORINFOEXA {
+      MonitorInfo(HWND hwnd);
+    };
+#else
+    struct MonitorInfo {
+      MonitorInfo(HWND hwnd);
+      DWORD cbSize;
+      RECT rcMonitor;
+      RECT rcWork;
+      DWORD dwFlags;
+      char szDevice[1]; // Always null...
+    };
+#endif
+    void moveToMonitor(HWND handle, const char* device);
+
+    class Handle {
+    public:
+      Handle(HANDLE h_=0) : h(h_) {}
+      ~Handle() {
+        if (h) CloseHandle(h);
+      }
+      operator HANDLE() {return h;}
+      HANDLE h;
+    };
+
+    // Center the window to a rectangle, or to a parent window.
+    // Optionally, resize the window to lay within the rect or parent window
+    // If the parent window is NULL then the working area if the window's
+    // current monitor is used instead.
+    void centerWindow(HWND handle, const RECT& r, bool clipToRect=false);
+    void centerWindow(HWND handle, HWND parent, bool clipToRect=false);
+
+    // MsgBox helper function.  Define rfb::win32::AppName somewhere in your
+    // code and MsgBox will use its value in informational messages.
+    extern TStr AppName;
+    int MsgBox(HWND parent, const TCHAR* message, UINT flags);
+
+    // Get the computer name
+    struct ComputerName : TCharArray {
+      ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
+        ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
+        if (!GetComputerName(buf, &namelength))
+          _tcscpy(buf, _T(""));
+      }
+    };
+
+    // Allocate and/or manage LocalAlloc memory.
+    struct LocalMem {
+      LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
+        if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
+      }
+      LocalMem(void* p) : ptr(p) {}
+      ~LocalMem() {LocalFree(ptr);}
+      operator void*() {return ptr;}
+      void* takePtr() {
+        void* t = ptr; ptr = 0; return t;
+      }
+      void* ptr;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/rfb_win32/keymap.h b/rfb_win32/keymap.h
new file mode 100644
index 0000000..69ce66f
--- /dev/null
+++ b/rfb_win32/keymap.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// keymap.h - this file is shared between SInput.cxx and CKeyboard.cxx
+//
+// Mapping of X keysyms to and from Windows VK codes.  Ordering here must be
+// such that when we look up a Windows VK code we get the preferred X keysym.
+// Going the other way there is no problem because an X keysym always maps to
+// exactly one Windows VK code.  This map only contain keys which are not the
+// normal keys for printable ASCII characters.  For example it does not contain
+// VK_SPACE (note that things like VK_ADD are for the plus key on the keypad,
+// not on the main keyboard).
+
+struct keymap_t {
+  rdr::U32 keysym;
+  rdr::U8 vk;
+  bool extended;
+};
+
+static keymap_t keymap[] = {
+
+  { XK_BackSpace,        VK_BACK, 0 },
+  { XK_Tab,              VK_TAB, 0 },
+  { XK_Clear,            VK_CLEAR, 0 },
+  { XK_Return,           VK_RETURN, 0 },
+  { XK_Pause,            VK_PAUSE, 0 },
+  { XK_Escape,           VK_ESCAPE, 0 },
+  { XK_Delete,           VK_DELETE, 1 },
+
+  // Cursor control & motion
+
+  { XK_Home,             VK_HOME, 1 },
+  { XK_Left,             VK_LEFT, 1 },
+  { XK_Up,               VK_UP, 1 },
+  { XK_Right,            VK_RIGHT, 1 },
+  { XK_Down,             VK_DOWN, 1 },
+  { XK_Page_Up,          VK_PRIOR, 1 },
+  { XK_Page_Down,        VK_NEXT, 1 },
+  { XK_End,              VK_END, 1 },
+
+  // Misc functions
+
+  { XK_Select,           VK_SELECT, 0 },
+  { XK_Print,            VK_SNAPSHOT, 0 },
+  { XK_Execute,          VK_EXECUTE, 0 },
+  { XK_Insert,           VK_INSERT, 1 },
+  { XK_Help,             VK_HELP, 0 },
+  { XK_Break,            VK_CANCEL, 1 },
+
+  // Auxilliary Functions - must come before XK_KP_F1, etc
+
+  { XK_F1,               VK_F1, 0 },
+  { XK_F2,               VK_F2, 0 },
+  { XK_F3,               VK_F3, 0 },
+  { XK_F4,               VK_F4, 0 },
+  { XK_F5,               VK_F5, 0 },
+  { XK_F6,               VK_F6, 0 },
+  { XK_F7,               VK_F7, 0 },
+  { XK_F8,               VK_F8, 0 },
+  { XK_F9,               VK_F9, 0 },
+  { XK_F10,              VK_F10, 0 },
+  { XK_F11,              VK_F11, 0 },
+  { XK_F12,              VK_F12, 0 },
+  { XK_F13,              VK_F13, 0 },
+  { XK_F14,              VK_F14, 0 },
+  { XK_F15,              VK_F15, 0 },
+  { XK_F16,              VK_F16, 0 },
+  { XK_F17,              VK_F17, 0 },
+  { XK_F18,              VK_F18, 0 },
+  { XK_F19,              VK_F19, 0 },
+  { XK_F20,              VK_F20, 0 },
+  { XK_F21,              VK_F21, 0 },
+  { XK_F22,              VK_F22, 0 },
+  { XK_F23,              VK_F23, 0 },
+  { XK_F24,              VK_F24, 0 },
+
+  // Keypad Functions, keypad numbers
+
+  { XK_KP_Tab,           VK_TAB, 0 },
+  { XK_KP_Enter,         VK_RETURN, 1 },
+  { XK_KP_F1,            VK_F1, 0 },
+  { XK_KP_F2,            VK_F2, 0 },
+  { XK_KP_F3,            VK_F3, 0 },
+  { XK_KP_F4,            VK_F4, 0 },
+  { XK_KP_Home,          VK_HOME, 0 },
+  { XK_KP_Left,          VK_LEFT, 0 },
+  { XK_KP_Up,            VK_UP, 0 },
+  { XK_KP_Right,         VK_RIGHT, 0 },
+  { XK_KP_Down,          VK_DOWN, 0 },
+  { XK_KP_End,           VK_END, 0 },
+  { XK_KP_Page_Up,       VK_PRIOR, 0 },
+  { XK_KP_Page_Down,     VK_NEXT, 0 },
+  { XK_KP_Begin,         VK_CLEAR, 0 },
+  { XK_KP_Insert,        VK_INSERT, 0 },
+  { XK_KP_Delete,        VK_DELETE, 0 },
+  { XK_KP_Multiply,      VK_MULTIPLY, 0 },
+  { XK_KP_Add,           VK_ADD, 0 },
+  { XK_KP_Separator,     VK_SEPARATOR, 0 },
+  { XK_KP_Subtract,      VK_SUBTRACT, 0 },
+  { XK_KP_Decimal,       VK_DECIMAL, 0 },
+  { XK_KP_Divide,        VK_DIVIDE, 1 },
+
+  { XK_KP_0,             VK_NUMPAD0, 0 },
+  { XK_KP_1,             VK_NUMPAD1, 0 },
+  { XK_KP_2,             VK_NUMPAD2, 0 },
+  { XK_KP_3,             VK_NUMPAD3, 0 },
+  { XK_KP_4,             VK_NUMPAD4, 0 },
+  { XK_KP_5,             VK_NUMPAD5, 0 },
+  { XK_KP_6,             VK_NUMPAD6, 0 },
+  { XK_KP_7,             VK_NUMPAD7, 0 },
+  { XK_KP_8,             VK_NUMPAD8, 0 },
+  { XK_KP_9,             VK_NUMPAD9, 0 },
+
+  // Modifiers
+    
+  { XK_Shift_L,          VK_SHIFT, 0 },
+  { XK_Shift_R,          VK_SHIFT, 0 },
+  { XK_Control_L,        VK_CONTROL, 0 },
+  { XK_Control_R,        VK_CONTROL, 1 },
+  { XK_Alt_L,            VK_MENU, 0 },
+  { XK_Alt_R,            VK_MENU, 1 },
+
+  // Left & Right Windows keys & Windows Menu Key
+
+  { XK_Super_L,          VK_LWIN, 0 },
+  { XK_Super_R,          VK_RWIN, 0 },
+  { XK_Menu,             VK_APPS, 0 },
+
+  // Japanese stuff - almost certainly wrong...
+
+  { XK_Kanji,            VK_KANJI, 0 },
+  { XK_Kana_Shift,       VK_KANA, 0 },
+
+};
diff --git a/rfb_win32/msvcwarning.h b/rfb_win32/msvcwarning.h
new file mode 100644
index 0000000..e50d9c1
--- /dev/null
+++ b/rfb_win32/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug info truncated
diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp
new file mode 100644
index 0000000..2118bcb
--- /dev/null
+++ b/rfb_win32/rfb_win32.dsp
@@ -0,0 +1,341 @@
+# Microsoft Developer Studio Project File - Name="rfb_win32" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb_win32 - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb_win32.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb_win32.mak" CFG="rfb_win32 - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "rfb_win32 - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "rfb_win32 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb_win32 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb_win32 - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "rfb_win32 - Win32 Release"
+# Name "rfb_win32 - Win32 Debug"
+# Name "rfb_win32 - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\AboutDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AboutDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IntervalTimer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keymap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/tx/Makefile.in b/tx/Makefile.in
new file mode 100644
index 0000000..b953325
--- /dev/null
+++ b/tx/Makefile.in
@@ -0,0 +1,18 @@
+
+SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx \
+       Timer.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+library = libtx.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/tx/TXButton.h b/tx/TXButton.h
new file mode 100644
index 0000000..3f0c57e
--- /dev/null
+++ b/tx/TXButton.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXButton.h
+//
+// A TXButton is a clickable button with some text in it.  The button must be
+// big enough to contain the text - if not then it will be resized
+// appropriately.
+//
+
+#ifndef __TXBUTTON_H__
+#define __TXBUTTON_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXButtonCallback's buttonActivate() method is called when a button is
+// activated.
+class TXButton;
+class TXButtonCallback {
+public:
+  virtual void buttonActivate(TXButton* button)=0;
+};
+
+
+class TXButton : public TXWindow, public TXEventHandler {
+public:
+
+  TXButton(Display* dpy_, const char* text_, TXButtonCallback* cb_=0,
+           TXWindow* parent_=0, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_), down(false),
+      disabled_(false)
+  {
+    setEventHandler(this);
+    setText(text_);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    XSetFont(dpy, gc, defaultFont);
+    addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask);
+  }
+
+  virtual ~TXButton() {
+    XFreeGC(dpy, gc);
+  }
+
+  // setText() changes the text in the button.
+  void setText(const char* text_) {
+    text.buf = rfb::strDup(text_);
+    int textWidth = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newWidth = max(width(), textWidth + xPad*2 + bevel*2);
+    int newHeight = max(height(), textHeight + yPad*2 + bevel*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+  }
+
+  // disabled() sets or queries the disabled state of the checkbox.  A disabled
+  // checkbox cannot be changed via the user interface.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+
+  void paint() {
+    int tw = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+    int startx = (width() - tw) / 2;
+    int starty = (height() + defaultFS->ascent - defaultFS->descent) / 2;
+    if (down || disabled_) {
+      drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, darkBg,lightBg);
+      startx++; starty++;
+    } else {
+      drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, lightBg,darkBg);
+    }
+
+    XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+    XDrawString(dpy, win(), gc, startx, starty, text.buf, strlen(text.buf));
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    case ButtonPress:
+      if (!disabled_) {
+        down = true;
+        paint();
+      }
+      break;
+    case ButtonRelease:
+      if (!down) break;
+      down = false;
+      paint();
+      if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+          ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+        if (cb) cb->buttonActivate(this);
+      }
+      break;
+    }
+  }
+
+  GC gc;
+  rfb::CharArray text;
+  TXButtonCallback* cb;
+  bool down;
+  bool disabled_;
+};
+
+#endif
diff --git a/tx/TXCheckbox.h b/tx/TXCheckbox.h
new file mode 100644
index 0000000..146091d
--- /dev/null
+++ b/tx/TXCheckbox.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXCheckbox.h
+//
+// A TXCheckbox has a box which may be "checked" with some text next to it.
+// The checkbox window must be big enough to contain the text - if not then it
+// will be resized appropriately.
+//
+// There are two styles of checkbox: the normal style which uses a tick in a
+// square box, and the radio style which uses a dot inside a circle.  The
+// default behaviour when clicking on the checkbox is to toggle it on or off,
+// but this behaviour can be changed by the callback object.  In particular to
+// get radiobutton behaviour, the callback must ensure that only one of a set
+// of radiobuttons is selected.
+//
+
+#ifndef __TXCHECKBOX_H__
+#define __TXCHECKBOX_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXCheckboxCallback's checkboxSelect() method is called when the state of a
+// checkbox changes.
+class TXCheckbox;
+class TXCheckboxCallback {
+public:
+  virtual void checkboxSelect(TXCheckbox* checkbox)=0;
+};
+
+
+class TXCheckbox : public TXWindow, public TXEventHandler {
+public:
+  TXCheckbox(Display* dpy_, const char* text_, TXCheckboxCallback* cb_,
+             bool radio_=false, TXWindow* parent_=0, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_), text(0),
+      boxSize(radio_ ? 12 : 13), boxPad(4),
+      checked_(false), disabled_(false), radio(radio_)
+  {
+    setEventHandler(this);
+    setText(text_);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    XSetFont(dpy, gc, defaultFont);
+    addEventMask(ExposureMask| ButtonPressMask | ButtonReleaseMask);
+  }
+
+  virtual ~TXCheckbox() {
+    XFreeGC(dpy, gc);
+    if (text) free(text);
+  }
+
+  // setText() changes the text in the checkbox.
+  void setText(const char* text_) {
+    if (text) free(text);
+    text = strdup(text_);
+    int textWidth = XTextWidth(defaultFS, text, strlen(text));
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newWidth = max(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
+    int newHeight = max(height(), textHeight + yPad*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+  }
+
+  // checked() sets or queries the state of the checkbox
+  void checked(bool b) { checked_ = b; paint(); }
+  bool checked() { return checked_; }
+
+  // disabled() sets or queries the disabled state of the checkbox.  A disabled
+  // checkbox cannot be changed via the user interface.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+  void paint() {
+    if (disabled_)
+      drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+                bevel, disabledBg, darkBg, lightBg, radio);
+    else
+      drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+                bevel, enabledBg, darkBg, lightBg, radio);
+    XSetBackground(dpy, gc, disabled_ ? disabledBg : enabledBg);
+    XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+    if (checked_) {
+      Pixmap icon = radio ? dot : tick;
+      int iconSize = radio ? dotSize : tickSize;
+      XCopyPlane(dpy, icon, win(), gc, 0, 0, iconSize, iconSize,
+                 xPad + boxPad + (boxSize - iconSize) / 2,
+                 (height() - iconSize) / 2, 1);
+    }
+    XDrawString(dpy, win(), gc, xPad + boxSize + boxPad*2,
+                (height() + defaultFS->ascent - defaultFS->descent) / 2,
+                text, strlen(text));
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    case ButtonPress:
+      break;
+    case ButtonRelease:
+      if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+          ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+        if (!disabled_) {
+          checked_ = !checked_;
+          if (cb) cb->checkboxSelect(this);
+          paint();
+        }
+      }
+      break;
+    }
+  }
+
+  TXCheckboxCallback* cb;
+  GC gc;
+  char* text;
+  int boxSize;
+  int boxPad;
+  bool checked_;
+  bool disabled_;
+  bool radio;
+};
+
+#endif
diff --git a/tx/TXDialog.h b/tx/TXDialog.h
new file mode 100644
index 0000000..01677a9
--- /dev/null
+++ b/tx/TXDialog.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXDialog.h
+//
+// A TXDialog is a pop-up dialog window.  The dialog can be made visible by
+// calling its show() method.  Dialogs can be modal or non-modal.  For a modal
+// dialog box, the show() method only returns when the dialog box has been
+// dismissed.  For a non-modal dialog box, the show() method returns
+// immediately.
+//
+
+#ifndef __TXDIALOG_H__
+#define __TXDIALOG_H__
+
+#include "TXWindow.h"
+#include <errno.h>
+
+class TXDialog : public TXWindow, public TXDeleteWindowCallback {
+public:
+  TXDialog(Display* dpy, int width, int height, const char* name,
+           bool modal_=false)
+    : TXWindow(dpy, width, height), done(false), ok(false), modal(modal_)
+  {
+    toplevel(name, this);
+    int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+    int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+    setUSPosition((dpyWidth - width - 10) / 2, (dpyHeight - height - 30) / 2);
+  }
+
+  virtual ~TXDialog() {}
+
+  // show() makes the dialog visible.  For a modal dialog box, this processes X
+  // events until the done flag has been set, after which it returns the value
+  // of the ok flag.  For a non-modal dialog box it always returns true
+  // immediately.
+  bool show() {
+    ok = false;
+    done = false;
+    initDialog();
+    raise();
+    map();
+    if (modal) {
+      while (true) {
+        TXWindow::handleXEvents(dpy);
+        if (done) {
+          return ok;
+        }
+        fd_set rfds;
+        FD_ZERO(&rfds);
+        FD_SET(ConnectionNumber(dpy), &rfds);
+        int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+        if (n < 0) throw rdr::SystemException("select",errno);
+      }
+    }
+    return true;
+  }
+
+  // initDialog() can be overridden in a derived class.  Typically it is used
+  // to make sure that checkboxes have the right state, etc.
+  virtual void initDialog() {}
+
+protected:
+  virtual void deleteWindow(TXWindow* w) {
+    ok = false;
+    done = true;
+    unmap();
+  }
+
+  bool done;
+  bool ok;
+  bool modal;
+};
+
+#endif
diff --git a/tx/TXEntry.h b/tx/TXEntry.h
new file mode 100644
index 0000000..b51946e
--- /dev/null
+++ b/tx/TXEntry.h
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXEntry.h
+//
+// A TXEntry allows you to enter a single line of text in a window.  The entry
+// must be tall enough to contain a line of text - if not then it will be
+// resized appropriately.  If the passwd argument to the constructor is true,
+// then the text in the entry will be replaced by asterisks on the screen.
+//
+
+#ifndef __TXENTRY_H__
+#define __TXENTRY_H__
+
+#include "TXWindow.h"
+#include <X11/keysym.h>
+
+#ifndef XK_ISO_Left_Tab
+#define	XK_ISO_Left_Tab					0xFE20
+#endif
+
+// TXEntryCallback's entryCallback() method is called when one of three special
+// key presses have happened: Enter/Return, forward tab, or backward tab.
+class TXEntry;
+class TXEntryCallback {
+public:
+  enum Detail { ENTER, NEXT_FOCUS, PREV_FOCUS };
+  virtual void entryCallback(TXEntry* entry, Detail detail, Time time)=0;
+};
+
+
+class TXEntry : public TXWindow, public TXEventHandler {
+public:
+
+  TXEntry(Display* dpy_, TXEntryCallback* cb_=0,
+          TXWindow* parent_=0, bool passwd_=false, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_),
+      passwd(passwd_), disabled_(false), gotFocus(false)
+  {
+    setEventHandler(this);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    addEventMask(ExposureMask | KeyPressMask | FocusChangeMask
+                 | ButtonPressMask);
+    text[0] = 0;
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newHeight = max(height(), textHeight + yPad*2 + bevel*2);
+    if (height() < newHeight) {
+      resize(width(), newHeight);
+    }
+  }
+
+  virtual ~TXEntry() {
+    XFreeGC(dpy, gc);
+    // overwrite memory used to store password - not critical, but can avoid
+    // accidental exposure of a password in uninitialised memory.
+    if (passwd)
+      memset(text, 0, maxLen);
+  }
+
+  // getText() gets the text in the entry.
+  const char* getText() { return text; }
+
+  // setText() sets the text in the entry.
+  void setText(const char* text_) {
+    strncpy(text, text_, maxLen-1);
+    text[maxLen-1] = 0;
+    paint();
+  }
+
+  // disabled() sets or queries the disabled state of the entry.  A disabled
+  // entry cannot have text entered into it.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+  void paint() {
+    if (disabled_)
+      drawBevel(gc, 0, 0, width(), height(), bevel, disabledBg,darkBg,lightBg);
+    else
+      drawBevel(gc, 0, 0, width(), height(), bevel, enabledBg, darkBg,lightBg);
+    char* str = text;
+    char stars[maxLen];
+    if (passwd) {
+      int i;
+      for (i = 0; i < (int)strlen(text); i++) stars[i] = '*';
+      stars[i] = 0;
+      str = stars;
+    }
+    int tw = XTextWidth(defaultFS, str, strlen(str));
+    int startx = bevel + xPad;
+    if (startx + tw > width() - 2*bevel) {
+      startx = width() - 2*bevel - tw;
+    }
+    XDrawString(dpy, win(), defaultGC, startx,
+                (height() + defaultFS->ascent - defaultFS->descent) / 2,
+                str, strlen(str));
+    if (!disabled_ && gotFocus)
+      XDrawLine(dpy, win(), defaultGC, startx+tw,
+                (height() - defaultFS->ascent - defaultFS->descent) / 2,
+                startx+tw,
+                (height() + defaultFS->ascent + defaultFS->descent) / 2);
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+
+    case FocusIn:
+      gotFocus = true;
+      paint();
+      break;
+
+    case FocusOut:
+      gotFocus = false;
+      paint();
+      break;
+
+    case ButtonPress:
+      if (!disabled_)
+        XSetInputFocus(dpy, win(), RevertToParent, ev->xbutton.time);
+      break;
+
+    case KeyPress:
+      {
+        if (disabled_ || !gotFocus) break;
+        KeySym keysym;
+        XComposeStatus compose;
+        char buf[10];
+        int count = XLookupString(&ev->xkey, buf, 10, &keysym, &compose);
+        if (count >= 1 && buf[0] >= ' ' && buf[0] <= '~') {
+          if (strlen(text) + count >= maxLen) {
+            XBell(dpy, 0);
+          } else {
+            strncat(text, buf, count);
+            paint();
+          }
+        } else if (keysym == XK_BackSpace || keysym == XK_Delete ||
+                   keysym == XK_KP_Delete) {
+          if (strlen(text) > 0) {
+            text[strlen(text)-1] = 0;
+            paint();
+          }
+        } else if (keysym == XK_Return || keysym == XK_KP_Enter ||
+                   keysym == XK_Linefeed) {
+          if (cb) cb->entryCallback(this, TXEntryCallback::ENTER,
+                                    ev->xkey.time);
+        } else if ((keysym == XK_Tab || keysym == XK_KP_Tab)
+                   && !(ev->xkey.state & ShiftMask))
+        {
+          if (cb) cb->entryCallback(this, TXEntryCallback::NEXT_FOCUS,
+                                    ev->xkey.time);
+        } else if (((keysym == XK_Tab || keysym == XK_KP_Tab)
+                    && (ev->xkey.state & ShiftMask))
+                   || keysym == XK_ISO_Left_Tab)
+        {
+          if (cb) cb->entryCallback(this, TXEntryCallback::PREV_FOCUS,
+                                    ev->xkey.time);
+        }
+      }
+    }
+  }
+  GC gc;
+  enum { maxLen = 256 };
+  char text[maxLen];
+  TXEntryCallback* cb;
+  bool passwd;
+  bool disabled_;
+  bool gotFocus;
+};
+
+#endif
diff --git a/tx/TXImage.cxx b/tx/TXImage.cxx
new file mode 100644
index 0000000..d5b0649
--- /dev/null
+++ b/tx/TXImage.cxx
@@ -0,0 +1,340 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXImage.cxx
+//
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <list>
+#include <rfb/TransImageGetter.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXImage.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("TXImage");
+
+TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
+  : xim(0), dpy(d), vis(vis_), depth(depth_), shminfo(0), tig(0), cube(0)
+{
+  width_ = width;
+  height_ = height;
+  for (int i = 0; i < 256; i++)
+    colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
+
+  if (!vis)
+    vis = DefaultVisual(dpy,DefaultScreen(dpy));
+  if (!depth)
+    depth = DefaultDepth(dpy,DefaultScreen(dpy));
+
+  createXImage();
+  getNativePixelFormat(vis, depth);
+  colourmap = this;
+  format.bpp = 0;  // just make it different to any valid format, so that...
+  setPF(nativePF); // ...setPF() always works
+}
+
+TXImage::~TXImage()
+{
+  if (data != (rdr::U8*)xim->data) delete [] data;
+  destroyXImage();
+  delete tig;
+  delete cube;
+}
+
+void TXImage::resize(int w, int h)
+{
+  if (w == width() && h == height()) return;
+
+  int oldStrideBytes = getStride() * (format.bpp/8);
+  int rowsToCopy = min(h, height());
+  int bytesPerRow = min(w, width()) * (format.bpp/8);
+  rdr::U8* oldData = 0;
+  bool allocData = false;
+
+  if (data != (rdr::U8*)xim->data) {
+    oldData = (rdr::U8*)data;
+    allocData = true;
+  } else {
+    oldData = new rdr::U8[xim->bytes_per_line * height()];
+    memcpy(oldData, xim->data, xim->bytes_per_line * height());
+  }
+
+  destroyXImage();
+  width_ = w;
+  height_ = h;
+  createXImage();
+
+  if (allocData)
+    data = new rdr::U8[width() * height() * (format.bpp/8)];
+  else
+    data = (rdr::U8*)xim->data;
+
+  int newStrideBytes = getStride() * (format.bpp/8);
+  for (int i = 0; i < rowsToCopy; i++)
+    memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
+           bytesPerRow);
+  delete [] oldData;
+}
+
+void TXImage::setPF(const PixelFormat& newPF)
+{
+  if (newPF.equal(format)) return;
+  format = newPF;
+
+  if (data != (rdr::U8*)xim->data) delete [] data;
+  delete tig;
+  tig = 0;
+
+  if (format.equal(nativePF) && format.trueColour) {
+    data = (rdr::U8*)xim->data;
+  } else {
+    data = new rdr::U8[width() * height() * (format.bpp/8)];
+    tig = new TransImageGetter();
+    tig->init(this, nativePF, 0, cube);
+  }
+}
+
+int TXImage::getStride() const
+{
+  if (data == (rdr::U8*)xim->data)
+    return xim->bytes_per_line / (xim->bits_per_pixel / 8);
+  else
+    return width();
+}
+
+void TXImage::put(Window win, GC gc, const rfb::Rect& r)
+{
+  if (r.is_empty()) return;
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  if (data != (rdr::U8*)xim->data) {
+    rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
+                             + x * (xim->bits_per_pixel / 8));
+    tig->getImage(ximDataStart, r,
+                  xim->bytes_per_line / (xim->bits_per_pixel / 8));
+  }
+  if (usingShm()) {
+    XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
+  } else {
+    XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
+  }
+}
+
+void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+  for (int i = 0; i < nColours; i++) {
+    colourMap[firstColour+i].r = rgbs[i*3];
+    colourMap[firstColour+i].g = rgbs[i*3+1];
+    colourMap[firstColour+i].b = rgbs[i*3+2];
+  }
+}
+
+void TXImage::updateColourMap()
+{
+  tig->setColourMapEntries(0, 0, 0);
+}
+
+void TXImage::lookup(int index, int* r, int* g, int* b)
+{
+  *r = colourMap[index].r;
+  *g = colourMap[index].g;
+  *b = colourMap[index].b;
+}
+
+
+static bool caughtError = false;
+
+static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
+{
+  caughtError = true;
+  return 0;
+}
+
+class TXImageCleanup {
+public:
+  std::list<TXImage*> images;
+  ~TXImageCleanup() {
+    while (!images.empty())
+      delete images.front();
+  }
+};
+
+static TXImageCleanup imageCleanup;
+
+void TXImage::createXImage()
+{
+  if (XShmQueryExtension(dpy)) {
+    shminfo = new XShmSegmentInfo;
+
+    xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
+                          0, shminfo, width(), height());
+
+    if (xim) {
+      shminfo->shmid = shmget(IPC_PRIVATE,
+                              xim->bytes_per_line * xim->height,
+                              IPC_CREAT|0777);
+
+      if (shminfo->shmid != -1) {
+        shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+        if (shminfo->shmaddr != (char *)-1) {
+
+          shminfo->readOnly = False;
+
+          XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
+          XShmAttach(dpy, shminfo);
+          XSync(dpy, False);
+          XSetErrorHandler(oldHdlr);
+
+          if (!caughtError) {
+            vlog.debug("Using shared memory XImage");
+            imageCleanup.images.push_back(this);
+            return;
+          }
+
+          shmdt(shminfo->shmaddr);
+        } else {
+          vlog.error("shmat failed");
+          perror("shmat");
+        }
+
+        shmctl(shminfo->shmid, IPC_RMID, 0);
+      } else {
+        vlog.error("shmget failed");
+        perror("shmget");
+      }
+
+      XDestroyImage(xim);
+      xim = 0;
+    } else {
+      vlog.error("XShmCreateImage failed");
+    }
+
+    delete shminfo;
+    shminfo = 0;
+  }
+
+  xim = XCreateImage(dpy, vis, depth, ZPixmap,
+                     0, 0, width(), height(), BitmapPad(dpy), 0);
+
+  xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+  if (!xim->data) {
+    vlog.error("malloc failed");
+    exit(1);
+  }
+}
+
+void TXImage::destroyXImage()
+{
+  if (shminfo) {
+    vlog.debug("Freeing shared memory XImage");
+    shmdt(shminfo->shmaddr);
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    delete shminfo;
+    shminfo = 0;
+    imageCleanup.images.remove(this);
+  }
+  // XDestroyImage() will free(xim->data) if appropriate
+  if (xim) XDestroyImage(xim);
+  xim = 0;
+}
+
+
+static bool supportedBPP(int bpp) {
+  return (bpp == 8 || bpp == 16 || bpp == 32);
+}
+
+static int depth2bpp(Display* dpy, int depth)
+{
+  int nformats;
+  XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
+
+  int i;
+  for (i = 0; i < nformats; i++)
+    if (format[i].depth == depth) break;
+
+  if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
+    throw rfb::Exception("Error: couldn't find suitable pixmap format");
+
+  int bpp = format[i].bits_per_pixel;
+  XFree(format);
+  return bpp;
+}
+
+void TXImage::getNativePixelFormat(Visual* vis, int depth)
+{
+  cube = 0;
+  nativePF.depth = depth;
+  nativePF.bpp = depth2bpp(dpy, depth);
+  nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+  nativePF.trueColour = (vis->c_class == TrueColor);
+
+  vlog.info("Using default colormap and visual, %sdepth %d.",
+            (vis->c_class == TrueColor) ? "TrueColor, " :
+            ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
+            depth);
+
+  if (nativePF.trueColour) {
+
+    nativePF.redShift   = ffs(vis->red_mask)   - 1;
+    nativePF.greenShift = ffs(vis->green_mask) - 1;
+    nativePF.blueShift  = ffs(vis->blue_mask)  - 1;
+    nativePF.redMax   = vis->red_mask   >> nativePF.redShift;
+    nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
+    nativePF.blueMax  = vis->blue_mask  >> nativePF.blueShift;
+
+  } else {
+
+    XColor xc[256];
+    cube = new rfb::ColourCube(6,6,6);
+    int r;
+    for (r = 0; r < cube->nRed; r++) {
+      for (int g = 0; g < cube->nGreen; g++) {
+        for (int b = 0; b < cube->nBlue; b++) {
+          int i = (r * cube->nGreen + g) * cube->nBlue + b;
+          xc[i].red =   r * 65535 / (cube->nRed-1);
+          xc[i].green = g * 65535 / (cube->nGreen-1);
+          xc[i].blue =  b * 65535 / (cube->nBlue-1);
+        }
+      }
+    }
+
+    TXWindow::getColours(dpy, xc, cube->size());
+
+    for (r = 0; r < cube->nRed; r++) {
+      for (int g = 0; g < cube->nGreen; g++) {
+        for (int b = 0; b < cube->nBlue; b++) {
+          int i = (r * cube->nGreen + g) * cube->nBlue + b;
+          cube->set(r, g, b, xc[i].pixel);
+        }
+      }
+    }
+  }
+}
diff --git a/tx/TXImage.h b/tx/TXImage.h
new file mode 100644
index 0000000..a90a694
--- /dev/null
+++ b/tx/TXImage.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXImage.h
+//
+// A TXImage represents a rectangular off-screen image in any RFB pixel format.
+// By default it will use the "native" pixel format for the screen, which will
+// be an 8-bit colourmap unless the X display is TrueColor.  The pixel format
+// can be changed via the setPF() method.  The pixel data is accessible via the
+// data member inherited from FullFramePixelBuffer, or can be set via the
+// fillRect(), imageRect(), copyRect() and maskRect() methods, also inherited
+// from PixelBuffer.  A rectangle of the image can be drawn into an X Window
+// via the put() method.  If using a colourmap, the setColourMapEntries() and
+// updateColourMap() methods must be called to set up the colourmap as
+// appropriate.
+
+
+#ifndef __TXIMAGE_H__
+#define __TXIMAGE_H__
+
+#include <X11/Xlib.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ColourCube.h>
+#include <X11/extensions/XShm.h>
+
+namespace rfb { class TransImageGetter; }
+
+class TXImage : public rfb::FullFramePixelBuffer, public rfb::ColourMap {
+public:
+  TXImage(Display* dpy, int width, int height, Visual* vis=0, int depth=0);
+  ~TXImage();
+
+  // resize() resizes the image, preserving the image data where possible.
+  void resize(int w, int h);
+
+  // put causes the given rectangle to be drawn onto the given window.
+  void put(Window win, GC gc, const rfb::Rect& r);
+
+  // setColourMapEntries() changes some of the entries in the colourmap.
+  // However these settings won't take effect until updateColourMap() is
+  // called.  This is because recalculating the internal translation table can
+  // be expensive.
+  void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+  void updateColourMap();
+
+  bool usingShm() { return shminfo; }
+
+  // PixelBuffer methods
+  // width(), height(), getPF() etc are inherited from PixelBuffer
+  virtual void setPF(const rfb::PixelFormat& pf);
+  virtual int getStride() const;
+
+private:
+
+  // ColourMap method
+  virtual void lookup(int index, int* r, int* g, int* b);
+
+  void createXImage();
+  void destroyXImage();
+  void getNativePixelFormat(Visual* vis, int depth);
+
+  XImage* xim;
+  Display* dpy;
+  Visual* vis;
+  int depth;
+  XShmSegmentInfo* shminfo;
+  rfb::TransImageGetter* tig;
+  rfb::Colour colourMap[256];
+  rfb::PixelFormat nativePF;
+  rfb::ColourCube* cube;
+};
+
+#endif
diff --git a/tx/TXLabel.h b/tx/TXLabel.h
new file mode 100644
index 0000000..44f7047
--- /dev/null
+++ b/tx/TXLabel.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXLabel.h
+//
+// An TXLabel allows you to put up multiline text in a window with various
+// alignments.  The label must be big enough to contain the text - if not then
+// it will be resized appropriately.
+//
+
+#ifndef __TXLABEL_H__
+#define __TXLABEL_H__
+
+#include <stdlib.h>
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+class TXLabel : public TXWindow, public TXEventHandler {
+public:
+  enum HAlign { left, centre, right };
+  enum VAlign { top, middle, bottom };
+
+  TXLabel(Display* dpy_, const char* text_, TXWindow* parent_=0,
+          int w=1, int h=1, HAlign ha=centre, VAlign va=middle)
+    : TXWindow(dpy_, w, h, parent_), lineSpacing(2), lines(0),
+      halign(ha), valign(va)
+  {
+    setEventHandler(this);
+    setText(text_);
+    addEventMask(ExposureMask);
+  }
+
+  // setText() changes the text in the label.
+  void setText(const char* text_) {
+    text.buf = rfb::strDup(text_);
+    lines = 0;
+    int lineStart = 0;
+    int textWidth = 0;
+    int i = -1;
+    do {
+      i++;
+      if (text.buf[i] == '\n' || text.buf[i] == 0) {
+        int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+        if (tw > textWidth) textWidth = tw;
+        lineStart = i+1;
+        lines++;
+      }
+    } while (text.buf[i] != 0);
+    int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lines);
+
+    int newWidth = max(width(), textWidth + xPad*2);
+    int newHeight = max(height(), textHeight + yPad*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+  }
+
+private:
+  int xOffset(int textWidth) {
+    switch (halign) {
+    case left:  return xPad;
+    case right: return width() - xPad - textWidth;
+    default:    return (width() - textWidth) / 2;
+    }
+  }
+
+  int yOffset(int lineNum) {
+    int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lines);
+    int lineOffset = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lineNum + defaultFS->ascent);
+    switch (valign) {
+    case top:    return yPad + lineOffset;
+    case bottom: return height() - yPad - textHeight + lineOffset;
+    default:     return (height() - textHeight) / 2 + lineOffset;
+    }
+  }
+
+  void paint() {
+    int lineNum = 0;
+    int lineStart = 0;
+    int i = -1;
+    do {
+      i++;
+      if (text.buf[i] == '\n' || text.buf[i] == 0) {
+        int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+        XDrawString(dpy, win(), defaultGC, xOffset(tw), yOffset(lineNum),
+                    &text.buf[lineStart], i-lineStart);
+        lineStart = i+1;
+        lineNum++;
+      }
+    } while (text.buf[i] != 0);
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    }
+  }
+
+  int lineSpacing;
+  rfb::CharArray text;
+  int lines;
+  HAlign halign;
+  VAlign valign;
+};
+
+#endif
diff --git a/tx/TXMenu.cxx b/tx/TXMenu.cxx
new file mode 100644
index 0000000..4069f2d
--- /dev/null
+++ b/tx/TXMenu.cxx
@@ -0,0 +1,186 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXMenu.cxx
+//
+
+#include "TXMenu.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <rfb/util.h>
+#include <X11/keysym.h>
+
+TXMenu::TXMenu(Display* dpy_, TXMenuCallback* cb_, int w, int h,
+               TXWindow* parent_)
+  : TXWindow(dpy_, w, h, parent_), cb(cb_), nEntries(0),
+    highlight(-1)
+{
+  setEventHandler(this);
+  gc = XCreateGC(dpy, win(), 0, 0);
+  addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+               PointerMotionMask | EnterWindowMask | LeaveWindowMask);
+}
+
+TXMenu::~TXMenu()
+{
+  XFreeGC(dpy, gc);
+  for (int i = 0; i < nEntries; i++)
+    delete [] text[i];
+}
+
+inline int TXMenu::entryHeight(int i)
+{
+  if (text[i])
+    return defaultFS->ascent + defaultFS->descent + bevel*2 + yPad*2;
+  else
+    return yPad*2 + 1;
+}
+
+void TXMenu::addEntry(const char* text_, long id_)
+{
+  assert(nEntries < maxEntries);
+  text[nEntries] = rfb::strDup(text_);
+  checked[nEntries] = false;
+  id[nEntries++] = id_;
+  int tw = 0;
+  if (text_)
+    tw = XTextWidth(defaultFS, text_, strlen(text_));
+  int newWidth = width();
+  if (tw + bevel*2 + xPad*5 + tickSize > width())
+    newWidth = tw + bevel*2 + xPad*5 + tickSize;
+  int newHeight = 0;
+  for (int i = 0; i < nEntries; i++)
+    newHeight += entryHeight(i);
+  resize(newWidth, newHeight);
+}
+
+void TXMenu::check(long id_, bool checked_)
+{
+  for (int i = 0; i < nEntries; i++) {
+    if (id[i] == id_) {
+      checked[i] = checked_;
+      break;
+    }
+  }
+}
+
+void TXMenu::paint()
+{
+  int y = 0;
+  for (int i = 0; i < nEntries; i++) {
+    if (text[i]) {
+      if (i == highlight)
+        drawBevel(gc, 0, y, width(), entryHeight(i), bevel,
+                  defaultBg, darkBg, lightBg);
+      else
+        XClearArea(dpy, win(), 0, y, width(), entryHeight(i), false);
+      if (checked[i])
+        XCopyPlane(dpy, tick, win(), defaultGC, 0, 0, tickSize, tickSize,
+                   bevel + xPad,
+                   y + bevel + yPad + defaultFS->ascent - tickSize, 1);
+
+      XDrawImageString(dpy, win(), defaultGC, bevel + xPad*2 + tickSize,
+                       y + bevel + yPad + defaultFS->ascent,
+                       text[i], strlen(text[i]));
+    } else {
+      XDrawLine(dpy, win(), defaultGC, bevel + xPad, y + entryHeight(i) / 2,
+                width() - bevel - xPad, y + entryHeight(i) / 2);
+    }
+    y += entryHeight(i);
+  }
+}
+
+void TXMenu::handleEvent(TXWindow* w, XEvent* ev)
+{
+  switch (ev->type) {
+  case Expose:
+    paint();
+    break;
+
+  case ButtonRelease:
+    {
+      int y = ev->xmotion.y;
+      int entryY = 0;
+      for (int i = 0; i < nEntries; i++) {
+        if (y >= entryY && y <= entryY + entryHeight(i)) {
+          if (cb && text[i])
+            cb->menuSelect(id[i], this);
+          break;
+        }
+        entryY += entryHeight(i);
+      }
+      highlight = -1;
+      paint();
+      break;
+    }
+
+  case ButtonPress:
+  case MotionNotify:
+    {
+      int y = ev->xmotion.y;
+      int entryY = 0;
+      for (int i = 0; i < nEntries; i++) {
+        if (y >= entryY && y <= entryY + entryHeight(i)) {
+          if (highlight != i) {
+            highlight = i;
+            paint();
+          }
+          break;
+        }
+        entryY += entryHeight(i);
+      }
+      break;
+    }
+
+  case KeyPress:
+    {
+      KeySym ks;
+      char str[256];
+      XLookupString(&ev->xkey, str, 256, &ks, NULL);
+      if (ks == XK_Escape) {
+        highlight = -1;
+        unmap();
+      } else if (ks == XK_Down || ks == XK_Up) {
+        if (nEntries < 1) break;
+        if (highlight < 0)
+          highlight = (ks == XK_Down ? nEntries-1 : 0);
+        int start = highlight;
+        int inc = (ks == XK_Down ? 1 : nEntries-1);
+        do {
+          highlight = (highlight + inc) % nEntries;
+        } while (highlight != start && !text[highlight]);
+        paint();
+      } else if (ks == XK_space || ks == XK_KP_Space ||
+                 ks == XK_Return || ks == XK_KP_Enter) {
+        if (cb && highlight >= 0 && text[highlight])
+          cb->menuSelect(id[highlight], this);
+        highlight = -1;
+        paint();
+      }
+      break;
+    }
+
+  case EnterNotify:
+  case LeaveNotify:
+    highlight = -1;
+    paint();
+    break;
+  }
+}
diff --git a/tx/TXMenu.h b/tx/TXMenu.h
new file mode 100644
index 0000000..82dafa5
--- /dev/null
+++ b/tx/TXMenu.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXMenu.h
+//
+// A TXMenu consists of multiple entries which can be added one at a time.
+// Each entry consists of some text, and has an associated integer identifier.
+// A callback is made when a menu entry is selected.
+//
+
+#ifndef __TXMENU_H__
+#define __TXMENU_H__
+
+#include "TXWindow.h"
+
+// TXMenuCallback's menuSelect() method is called when a particular menu entry
+// is selected.  The id argument identifies the menu entry.
+class TXMenu;
+class TXMenuCallback {
+public:
+  virtual void menuSelect(long id, TXMenu* menu)=0;
+};
+
+class TXMenu : public TXWindow, public TXEventHandler {
+public:
+  TXMenu(Display* dpy_, TXMenuCallback* cb=0, int width=1, int height=1,
+         TXWindow* parent_=0);
+  virtual ~TXMenu();
+
+  // addEntry() adds an entry to the end of the menu with the given text and
+  // identifier.
+  void addEntry(const char* text, long id);
+
+  // check() sets whether the given menu entry should have a tick next to it.
+  void check(long id, bool checked);
+
+private:
+  int entryHeight(int i);
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+  void paint();
+
+  GC gc;
+  TXMenuCallback* cb;
+  enum { maxEntries = 64 };
+  char* text[maxEntries];
+  long id[maxEntries];
+  bool checked[maxEntries];
+  int nEntries;
+  int highlight;
+};
+
+#endif
diff --git a/tx/TXMsgBox.h b/tx/TXMsgBox.h
new file mode 100644
index 0000000..00c4eb5
--- /dev/null
+++ b/tx/TXMsgBox.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXMsgBox.h
+//
+// A TXMsgBox is a specialised pop-up dialog window, designed to present
+// the user with a small amount of textual information, and potentially to
+// obtain their response.
+// TXMsgBoxes are always modal, and may have an Ok button, Ok+Cancel buttons,
+// or Yes+No buttons.
+// The MsgBox helper function creates a TXMsgBox on the fly, runs it, and
+// returns the result.
+//
+
+#ifndef __TXMSGBOX_H__
+#define __TXMSGBOX_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+enum TXMsgBoxFlags {
+  MB_OK = 0,
+  MB_OKCANCEL = 1,
+  MB_YESNO = 4,
+  MB_ICONERROR = 0x10,
+  MB_ICONQUESTION = 0x20,
+  MB_ICONWARNING = 0x30,
+  MB_ICONINFORMATION = 0x40,
+  MB_DEFBUTTON1 = 0,
+  MB_DEFBUTTON2 = 0x100
+};
+
+class TXMsgBox : public TXDialog, public TXButtonCallback {
+public:
+  TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0)
+    : TXDialog(dpy, 1, 1, "Message"),
+      textLabel(dpy, "", this),
+    okButton(dpy, "OK", this, this, 60),
+    cancelButton(dpy, "Cancel", this, this, 60)
+  {
+    textLabel.xPad = 8;
+    textLabel.move(0, yPad*4);
+    textLabel.setText(text);
+    resize(textLabel.width(),
+           textLabel.height() + okButton.height() + yPad*12);
+
+    switch (flags & 0x30) {
+    case MB_ICONERROR:
+      toplevel("Error", this); break;
+    case MB_ICONQUESTION:
+      toplevel("Question", this); break;
+    case MB_ICONWARNING:
+      toplevel("Warning", this); break;
+    case MB_ICONINFORMATION:
+      toplevel("Information", this); break;
+    default:
+      if (title)
+	toplevel(title, this);
+      break;
+    };
+
+    switch (flags & 0x7) {
+    default:
+      okButton.move((width() - okButton.width()) / 2,
+		    height() - yPad*4 - okButton.height());
+      cancelButton.unmap();
+      break;
+    case MB_OKCANCEL:
+    case MB_YESNO:
+      
+      okButton.move(((width()/2) - okButton.width()) / 2,
+		    height() - yPad*4 - okButton.height());
+      cancelButton.move(((width()*3/2) - cancelButton.width()) / 2,
+			height() - yPad*4 - cancelButton.height());
+      if ((flags & 0x7) == MB_YESNO) {
+	okButton.setText("Yes");
+	cancelButton.setText("No");
+      }
+      break;
+    };
+
+    setBorderWidth(1);
+  }
+
+  virtual void buttonActivate(TXButton* b) {
+    ok = (b == &okButton);   
+    unmap();
+  }
+
+  TXLabel textLabel;
+  TXButton okButton;
+  TXButton cancelButton;
+};
+
+#endif
diff --git a/tx/TXScrollbar.cxx b/tx/TXScrollbar.cxx
new file mode 100644
index 0000000..946ccff
--- /dev/null
+++ b/tx/TXScrollbar.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXScrollbar.cxx
+//
+
+#include "TXScrollbar.h"
+#include <stdio.h>
+#include <assert.h>
+
+TXScrollbar::TXScrollbar(Display* dpy_, int width, int height, bool vert,
+                         TXScrollbarCallback* cb_, TXWindow* parent_)
+  : TXWindow(dpy_, width, height, parent_), cb(cb_), vertical(vert),
+    clickedInThumb(false)
+{
+  setEventHandler(this);
+  gc = XCreateGC(dpy, win(), 0, 0);
+  addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+               ButtonMotionMask);
+  setBg(scrollbarBg);
+  limit[0] = len[0] = limit[1] = len[1] = 1;
+  start[0] = start[1] = 0;
+}
+
+TXScrollbar::~TXScrollbar()
+{
+  XFreeGC(dpy, gc);
+}
+
+void TXScrollbar::set(int limit_, int start_, int len_, bool vert)
+{
+  assert(limit_ > 0 && len_ >= 0 && len_ <= limit_);
+
+  if (start_ < 0) start_ = 0;
+  if (start_ > limit_ - len_) start_ = limit_ - len_;
+
+  if (limit[vert] != limit_ || start[vert] != start_ || len[vert] != len_) {
+    limit[vert] = limit_;
+    start[vert] = start_;
+    len[vert] = len_;
+    paint();
+  }
+}
+
+void TXScrollbar::paint()
+{
+  int x = scaleToBarX(start[0]);
+  int y = scaleToBarY(start[1]);
+  int w = scaleToBarX(len[0]);
+  int h = scaleToBarY(len[1]);
+  if (y > 0) XClearArea(dpy, win(), 0, 0, 0, y, false);
+  if (x > 0) XClearArea(dpy, win(), 0, y, x, y+h, false);
+  XClearArea(dpy, win(), x+w, y, 0, y+h, false);
+  XClearArea(dpy, win(), 0, y+h, 0, 0, false);
+  drawBevel(gc, x, y, w, h, bevel, defaultBg, lightBg, darkBg);
+}
+
+void TXScrollbar::handleEvent(TXWindow* w, XEvent* ev)
+{
+  switch (ev->type) {
+  case Expose:
+    paint();
+    break;
+
+  case ButtonPress:
+    {
+      xDown = ev->xbutton.x;
+      yDown = ev->xbutton.y;
+      xStart = start[0];
+      yStart = start[1];
+      bool clickedInThumbX = false;
+      if (xDown < scaleToBarX(start[0])) {
+        set(limit[0], start[0] - len[0], len[0], false);
+      } else if (xDown >= scaleToBarX(start[0]+len[0])) {
+        set(limit[0], start[0] + len[0], len[0], false);
+      } else {
+        clickedInThumbX = true;
+      }
+      bool clickedInThumbY = false;
+      if (yDown < scaleToBarY(start[1])) {
+        set(limit[1], start[1] - len[1], len[1], true);
+      } else if (yDown >= scaleToBarY(start[1]+len[1])) {
+        set(limit[1], start[1] + len[1], len[1], true);
+      } else {
+        clickedInThumbY = true;
+      }
+      clickedInThumb = clickedInThumbX && clickedInThumbY;
+      if (cb) cb->scrollbarPos(start[0], start[1], this);
+    }
+    break;
+
+  case ButtonRelease:
+  case MotionNotify:
+    while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+    if (clickedInThumb) {
+      int dx = ev->xmotion.x - xDown;
+      int dy = ev->xmotion.y - yDown;
+      set(limit[0], xStart + barToScaleX(dx), len[0], false);
+      set(limit[1], yStart + barToScaleY(dy), len[1], true);
+      if (cb) cb->scrollbarPos(start[0], start[1], this);
+    }
+    break;
+  }
+}
diff --git a/tx/TXScrollbar.h b/tx/TXScrollbar.h
new file mode 100644
index 0000000..4cc2afa
--- /dev/null
+++ b/tx/TXScrollbar.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXScrollbar.h
+//
+// A TXScrollbar represents a range of values starting at start, of length len,
+// between zero and limit.  The vertical argument to the constructor says
+// whether the scrollbar is horizontal or vertical.
+//
+// In fact it can represent a range in each dimension but usually one of the
+// dimensions is fixed, according to the vertical flag (for a vertical
+// scrollbar, the horizontal dimension is fixed, and vice-versa).
+//
+// The TXScrollbarCallback argument is an object which will be notified when
+// the user has attempted to move the scrollbar.  The x and y arguments to the
+// scrollbarPos() method give the start values in the respective dimensions.
+// They are guaranteed to be between 0 and limit-len.
+//
+
+#ifndef __TXSCROLLBAR_H__
+#define __TXSCROLLBAR_H__
+
+#include "TXWindow.h"
+
+class TXScrollbarCallback;
+
+class TXScrollbar : public TXWindow, public TXEventHandler {
+public:
+  TXScrollbar(Display* dpy_, int width=1, int height=1, bool vertical=false,
+              TXScrollbarCallback* cb=0, TXWindow* parent_=0);
+  virtual ~TXScrollbar();
+
+  // set() sets the limit, start and length of the range represented by the
+  // scrollbar.  The values of limit and len passed in must be valid
+  // (i.e. limit > 0 and 0 <= len <= limit).  Values of start are clipped to
+  // the range 0 to limit-len.
+  void set(int limit, int start, int len) { set(limit, start, len, vertical); }
+
+  // set() with an extra argument vert can be used to represent a range in both
+  // dimensions simultaneously.
+  void set(int limit, int start, int len, bool vert);
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+  int scaleToBarX(int x) { return (x * width() + limit[0]/2) / limit[0]; }
+  int scaleToBarY(int y) { return (y * height() + limit[1]/2) / limit[1]; }
+  int barToScaleX(int x) { return (x * limit[0] + width()/2) / width(); }
+  int barToScaleY(int y) { return (y * limit[1] + height()/2) / height(); }
+  void paint();
+
+  GC gc;
+  TXScrollbarCallback* cb;
+  int limit[2];
+  int start[2];
+  int len[2];
+  int xDown, yDown;
+  int xStart, yStart;
+  bool vertical;
+  bool clickedInThumb;
+};
+
+class TXScrollbarCallback {
+public:
+  virtual void scrollbarPos(int x, int y, TXScrollbar* sb)=0;
+};
+#endif
diff --git a/tx/TXViewport.cxx b/tx/TXViewport.cxx
new file mode 100644
index 0000000..67982e9
--- /dev/null
+++ b/tx/TXViewport.cxx
@@ -0,0 +1,157 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXViewport.cxx
+//
+
+#include "TXViewport.h"
+#include <stdio.h>
+
+TXViewport::TXViewport(Display* dpy_, int w, int h, TXWindow* parent_)
+  : TXWindow(dpy_, w, h, parent_), child(0), hScrollbar(0),
+    vScrollbar(0), scrollbarSize(15), xOff(0), yOff(0), bumpScrollTimer(this),
+    bumpScroll(false), needScrollbars(false), bumpScrollX(0), bumpScrollY(0)
+{
+  clipper = new TXWindow(dpy, width()-scrollbarSize, height()-scrollbarSize,
+                         this);
+  clipper->setBg(black);
+  hScrollbar = new TXScrollbar(dpy, width()-scrollbarSize, scrollbarSize,
+                               false, this, this);
+  vScrollbar = new TXScrollbar(dpy, scrollbarSize, height()-scrollbarSize,
+                               true, this, this);
+}
+
+TXViewport::~TXViewport()
+{
+  delete clipper;
+  delete hScrollbar;
+  delete vScrollbar;
+}
+
+void TXViewport::setChild(TXWindow* child_)
+{
+  child = child_;
+  XReparentWindow(dpy, child->win(), clipper->win(), 0, 0);
+  xOff = yOff = 0;
+  child->map();
+  resizeNotify();
+}
+
+bool TXViewport::setOffset(int x, int y)
+{
+  if (clipper->width() >= child->width()) {
+    x = (clipper->width() - child->width()) / 2;
+  } else {
+    if (x > 0) x = 0;
+    if (x + child->width() < clipper->width())
+      x = clipper->width() - child->width();
+  }
+
+  if (clipper->height() >= child->height()) {
+    y = (clipper->height() - child->height()) / 2;
+  } else {
+    if (y > 0) y = 0;
+    if (y + child->height() < clipper->height())
+      y = clipper->height() - child->height();
+  }
+
+  if (x != xOff || y != yOff) {
+    xOff = x;
+    yOff = y;
+    child->move(xOff, yOff);
+    return true;
+  }
+
+  return false;
+}
+
+void TXViewport::setBumpScroll(bool b)
+{
+  bumpScroll = b;
+  resizeNotify();
+}
+
+// Note: bumpScrollEvent() only works if the viewport is positioned at 0,0 and
+// is the same width and height as the screen.
+bool TXViewport::bumpScrollEvent(XMotionEvent* ev)
+{
+  if (!bumpScroll) return false;
+  int bumpScrollPixels = 20;
+  bumpScrollX = bumpScrollY = 0;
+
+  if (ev->x_root == width()-1)  bumpScrollX = -bumpScrollPixels;
+  else if (ev->x_root == 0)     bumpScrollX = bumpScrollPixels;
+  if (ev->y_root == height()-1) bumpScrollY = -bumpScrollPixels;
+  else if (ev->y_root == 0)     bumpScrollY = bumpScrollPixels;
+
+  if (bumpScrollX || bumpScrollY) {
+    if (bumpScrollTimer.isSet()) return true;
+    if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) {
+      bumpScrollTimer.reset(25);
+      return true;
+    }
+  }
+
+  bumpScrollTimer.cancel();
+  return false;
+}
+
+void TXViewport::timerCallback(Timer* timer)
+{
+  if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY))
+    bumpScrollTimer.reset(25);
+}
+
+void TXViewport::resizeNotify()
+{
+  needScrollbars = (!bumpScroll &&
+                    (width() < child->width() || height() < child->height()) &&
+                    (width() > scrollbarSize && height() > scrollbarSize));
+  if (needScrollbars) {
+    clipper->resize(width()-scrollbarSize, height()-scrollbarSize);
+    hScrollbar->map();
+    vScrollbar->map();
+  } else {
+    clipper->resize(width(), height());
+    hScrollbar->unmap();
+    vScrollbar->unmap();
+  }
+
+  setOffset(xOff, yOff);
+
+  if (needScrollbars) {
+    hScrollbar->move(0, height()-scrollbarSize);
+    hScrollbar->resize(width()-scrollbarSize, scrollbarSize);
+    hScrollbar->set(child->width(), -xOff, width()-scrollbarSize);
+    vScrollbar->move(width()-scrollbarSize, 0);
+    vScrollbar->resize(scrollbarSize, height()-scrollbarSize);
+    vScrollbar->set(child->height(), -yOff, height()-scrollbarSize);
+  }
+}
+
+void TXViewport::scrollbarPos(int x, int y, TXScrollbar* sb)
+{
+  if (sb == hScrollbar) {
+    x = -x;
+    y = yOff;
+  } else {
+    x = xOff;
+    y = -y;
+  }
+  setOffset(x, y);
+}
diff --git a/tx/TXViewport.h b/tx/TXViewport.h
new file mode 100644
index 0000000..9dddc2f
--- /dev/null
+++ b/tx/TXViewport.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXViewport.h
+//
+// A TXViewport allows a large window to be viewed by adding scrollbars to the
+// right and bottom if necessary.  It also has a bump-scroll mode where there
+// are no scrollbars, and scrolling is achieved by bumping up against the edge
+// of the screen instead.  Note that this only works when the viewport fills
+// the entire screen.  If the child window is smaller than the viewport, it is
+// always positioned centrally in the viewport.
+
+#ifndef __TXVIEWPORT_H__
+#define __TXVIEWPORT_H__
+
+#include "TXWindow.h"
+#include "TXScrollbar.h"
+#include "Timer.h"
+
+class TXViewport : public TXWindow, public TXScrollbarCallback,
+                   public TimerCallback {
+public:
+  TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0);
+  virtual ~TXViewport();
+
+  // setChild() sets the child window which is to be viewed in the viewport.
+  void setChild(TXWindow* child_);
+
+  // setOffset() sets the position of the child in the viewport.  Note that the
+  // offsets are negative.  For example when the offset is (-100,-30), position
+  // (100,30) in the child window is at the top-left of the viewport.  The
+  // offsets given are clipped to keep the child window filling the viewport
+  // (except where the child window is smaller than the viewport, in which case
+  // it is always positioned centrally in the viewport).  It returns true if
+  // the child was repositioned.
+  bool setOffset(int x, int y);
+
+  // setBumpScroll() puts the viewport in bump-scroll mode.
+  void setBumpScroll(bool b);
+
+  // bumpScrollEvent() can be called with a MotionNotify event which may
+  // potentially be against the edge of the screen.  It returns true if the
+  // event was used for bump-scrolling, false if it should be processed
+  // normally.
+  bool bumpScrollEvent(XMotionEvent* ev);
+
+private:
+  virtual void resizeNotify();
+  virtual void scrollbarPos(int x, int y, TXScrollbar* sb);
+  virtual void timerCallback(Timer* timer);
+  TXWindow* clipper;
+  TXWindow* child;
+  TXScrollbar* hScrollbar;
+  TXScrollbar* vScrollbar;
+  const int scrollbarSize;
+  int xOff, yOff;
+  Timer bumpScrollTimer;
+  bool bumpScroll;
+  bool needScrollbars;
+  int bumpScrollX, bumpScrollY;
+};
+#endif
diff --git a/tx/TXWindow.cxx b/tx/TXWindow.cxx
new file mode 100644
index 0000000..a649de4
--- /dev/null
+++ b/tx/TXWindow.cxx
@@ -0,0 +1,486 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXWindow.cxx
+//
+
+#include <X11/Xatom.h>
+#include "TXWindow.h"
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/util.h>
+
+std::list<TXWindow*> windows;
+
+Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+Atom xaCLIPBOARD;
+unsigned long TXWindow::black, TXWindow::white;
+unsigned long TXWindow::defaultFg, TXWindow::defaultBg;
+unsigned long TXWindow::lightBg, TXWindow::darkBg;
+unsigned long TXWindow::disabledFg, TXWindow::disabledBg;
+unsigned long TXWindow::enabledBg;
+unsigned long TXWindow::scrollbarBg;
+Colormap TXWindow::cmap = 0;
+GC TXWindow::defaultGC = 0;
+Font TXWindow::defaultFont = 0;
+XFontStruct* TXWindow::defaultFS = 0;
+Time TXWindow::cutBufferTime = 0;
+Pixmap TXWindow::dot = 0, TXWindow::tick = 0;
+const int TXWindow::dotSize = 4, TXWindow::tickSize = 8;
+char* TXWindow::defaultWindowClass;
+
+void TXWindow::init(Display* dpy, const char* defaultWindowClass_)
+{
+  cmap = DefaultColormap(dpy,DefaultScreen(dpy));
+  wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+  wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+  wmTakeFocus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+  xaTIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
+  xaTARGETS = XInternAtom(dpy, "TARGETS", False);
+  xaSELECTION_TIME = XInternAtom(dpy, "SELECTION_TIME", False);
+  xaSELECTION_STRING = XInternAtom(dpy, "SELECTION_STRING", False);
+  xaCLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
+  XColor cols[6];
+  cols[0].red = cols[0].green = cols[0].blue = 0x0000;
+  cols[1].red = cols[1].green = cols[1].blue = 0xbbbb;
+  cols[2].red = cols[2].green = cols[2].blue = 0xeeee;
+  cols[3].red = cols[3].green = cols[3].blue = 0x5555;
+  cols[4].red = cols[4].green = cols[4].blue = 0x8888;
+  cols[5].red = cols[5].green = cols[5].blue = 0xffff;
+  getColours(dpy, cols, 6);
+  black = defaultFg = cols[0].pixel;
+  defaultBg = disabledBg = cols[1].pixel;
+  lightBg = cols[2].pixel;
+  darkBg = disabledFg = cols[3].pixel;
+  scrollbarBg = cols[4].pixel;
+  white = enabledBg = cols[5].pixel;
+  defaultGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);
+  defaultFS
+    = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
+  if (!defaultFS) {
+    defaultFS = XLoadQueryFont(dpy, "fixed");
+    if (!defaultFS) {
+      fprintf(stderr,"Failed to load any font\n");
+      exit(1);
+    }
+  }
+  defaultFont = defaultFS->fid;
+  XSetForeground(dpy, defaultGC, defaultFg);
+  XSetBackground(dpy, defaultGC, defaultBg);
+  XSetFont(dpy, defaultGC, defaultFont);
+  XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
+
+  static char dotBits[] = { 0x06, 0x0f, 0x0f, 0x06};
+  dot = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), dotBits,
+                              dotSize, dotSize);
+  static char tickBits[] = { 0x80, 0xc0, 0xe2, 0x76, 0x3e, 0x1c, 0x08, 0x00};
+  tick = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), tickBits,
+                               tickSize, tickSize);
+  defaultWindowClass = rfb::strDup(defaultWindowClass_);
+}
+
+void TXWindow::handleXEvents(Display* dpy)
+{
+  while (XPending(dpy)) {
+    XEvent ev;
+    XNextEvent(dpy, &ev);
+    if (ev.type == MappingNotify) {
+      XRefreshKeyboardMapping(&ev.xmapping);
+    } else if (ev.type == PropertyNotify &&
+               ev.xproperty.window == DefaultRootWindow(dpy) &&
+               ev.xproperty.atom == XA_CUT_BUFFER0) {
+      cutBufferTime = ev.xproperty.time;
+    } else {
+      std::list<TXWindow*>::iterator i;
+      for (i = windows.begin(); i != windows.end(); i++) {
+        if ((*i)->win() == ev.xany.window)
+          (*i)->handleXEvent(&ev);
+      }
+    }
+  }
+}
+
+void TXWindow::getColours(Display* dpy, XColor* cols, int nCols)
+{
+  bool* got = new bool[nCols];
+  bool failed = false;
+  int i;
+  for (i = 0; i < nCols; i++) {
+    if (XAllocColor(dpy, cmap, &cols[i])) {
+      got[i] = true;
+    } else {
+      got[i] = false;
+      failed = true;
+    }
+  }
+
+  if (!failed) {
+    delete [] got;
+    return;
+  }
+
+  // AllocColor has failed.  This is because the colormap is full.  So the
+  // only thing we can do is use the "shared" pixels in the colormap.  The
+  // code below is designed to work even when the colormap isn't full so is
+  // more complex than it needs to be in this case.  However it would be
+  // useful one day to be able to restrict the number of colours allocated by
+  // an application so I'm leaving it in here.
+
+  // For each pixel in the colormap, try to allocate exactly its RGB values.
+  // If this returns a different pixel then it must be a private or
+  // unallocated pixel, so we can't use it.  If it returns us the same pixel
+  // again, it's almost certainly a shared colour, so we can use it (actually
+  // it is possible that it was an unallocated pixel which we've now
+  // allocated - by going through the pixels in reverse order we make this
+  // unlikely except for the lowest unallocated pixel - this works because of
+  // the way the X server allocates new pixels).
+
+  int cmapSize = DisplayCells(dpy,DefaultScreen(dpy));
+
+  XColor* cm = new XColor[cmapSize];
+  bool* shared = new bool[cmapSize];
+  bool* usedAsNearest = new bool[cmapSize];
+
+  for (i = 0; i < cmapSize; i++) {
+    cm[i].pixel = i;
+    shared[i] = usedAsNearest[i] = false;
+  }
+
+  XQueryColors(dpy, cmap, cm, cmapSize);
+
+  for (i = cmapSize-1; i >= 0; i--) {
+    if (XAllocColor(dpy, cmap, &cm[i])) {
+      if (cm[i].pixel == (unsigned long)i) {
+        shared[i] = true;
+      } else {
+        XFreeColors(dpy, cmap, &cm[i].pixel, 1, 0);
+      }
+    }
+  }
+
+  for (int j = 0; j < nCols; j++) {
+    unsigned long minDistance = ULONG_MAX;
+    unsigned long nearestPixel = 0;
+    if (!got[j]) {
+      for (i = 0; i < cmapSize; i++) {
+        if (shared[i]) {
+          unsigned long rd = (cm[i].red - cols[j].red)/2;
+          unsigned long gd = (cm[i].green - cols[j].green)/2;
+          unsigned long bd = (cm[i].blue - cols[j].blue)/2;
+          unsigned long distance = (rd*rd + gd*gd + bd*bd);
+
+          if (distance < minDistance) {
+            minDistance = distance;
+            nearestPixel = i;
+          }
+        }
+      }
+
+      cols[j].pixel = nearestPixel;
+      usedAsNearest[nearestPixel] = true;
+    }
+  }
+
+  for (i = 0; i < cmapSize; i++) {
+    if (shared[i] && !usedAsNearest[i]) {
+      unsigned long p = i;
+      XFreeColors(dpy, cmap, &p, 1, 0);
+    }
+  }
+}
+
+Window TXWindow::windowWithName(Display* dpy, Window top, const char* name)
+{
+  char* windowName;
+  if (XFetchName(dpy, top, &windowName)) {
+    if (strcmp(windowName, name) == 0) {
+      XFree(windowName);
+      return top;
+    }
+    XFree(windowName);
+  }
+
+  Window* children;
+  Window dummy;
+  unsigned int nchildren;
+  if (!XQueryTree(dpy, top, &dummy, &dummy, &children,&nchildren) || !children)
+    return 0;
+
+  for (int i = 0; i < (int)nchildren; i++) {
+    Window w = windowWithName(dpy, children[i], name);
+    if (w) {
+      XFree((char*)children);
+      return w;
+    }
+  }
+  XFree((char*)children);
+  return 0;
+}
+
+
+TXWindow::TXWindow(Display* dpy_, int w, int h, TXWindow* parent_,
+                   int borderWidth)
+  : dpy(dpy_), xPad(3), yPad(3), bevel(2), parent(parent_), width_(w),
+    height_(h), eventHandler(0), dwc(0), eventMask(0), toplevel_(false)
+{
+  sizeHints.flags = 0;
+  XSetWindowAttributes attr;
+  attr.background_pixel = defaultBg;
+  attr.border_pixel = 0;
+  Window par = parent ? parent->win() : DefaultRootWindow(dpy);
+  win_ = XCreateWindow(dpy, par, 0, 0, width_, height_, borderWidth,
+                      CopyFromParent, CopyFromParent, CopyFromParent,
+                      CWBackPixel | CWBorderPixel, &attr);
+  if (parent) map();
+
+  windows.push_back(this);
+}
+
+TXWindow::~TXWindow()
+{
+  windows.remove(this);
+  XDestroyWindow(dpy, win());
+}
+
+void TXWindow::toplevel(const char* name, TXDeleteWindowCallback* dwc_,
+                        int argc, char** argv, const char* windowClass,
+                        bool iconic)
+{
+  toplevel_ = true;
+  XWMHints wmHints;
+  wmHints.flags = InputHint|StateHint;
+  wmHints.input = True;
+  wmHints.initial_state = iconic ? IconicState : NormalState;
+  XClassHint classHint;
+  if (!windowClass) windowClass = defaultWindowClass;
+  classHint.res_name = (char*)name;
+  classHint.res_class = (char*)windowClass;
+  XSetWMProperties(dpy, win(), 0, 0, argv, argc,
+                   &sizeHints, &wmHints, &classHint);
+  XStoreName(dpy, win(), name);
+  XSetIconName(dpy, win(), name);
+  Atom protocols[10];
+  int nProtocols = 0;
+  protocols[nProtocols++] = wmTakeFocus;
+  dwc = dwc_;
+  if (dwc)
+    protocols[nProtocols++] = wmDeleteWindow;
+  XSetWMProtocols(dpy, win(), protocols, nProtocols);
+  addEventMask(StructureNotifyMask);
+}
+
+void TXWindow::setMaxSize(int w, int h)
+{
+  sizeHints.flags |= PMaxSize;
+  sizeHints.max_width = w;
+  sizeHints.max_height = h;
+  XSetWMNormalHints(dpy, win(), &sizeHints);
+}
+
+void TXWindow::setUSPosition(int x, int y)
+{
+  sizeHints.flags |= USPosition;
+  sizeHints.x = x;
+  sizeHints.y = y;
+  XSetWMNormalHints(dpy, win(), &sizeHints);
+  move(x, y);
+}
+
+void TXWindow::setGeometry(const char* geom, int x, int y, int w, int h)
+{
+  char defGeom[256];
+  sprintf(defGeom,"%dx%d+%d+%d",w,h,x,y);
+  XWMGeometry(dpy, DefaultScreen(dpy), strEmptyToNull((char*)geom), defGeom,
+              0, &sizeHints, &x, &y, &w, &h, &sizeHints.win_gravity);
+  sizeHints.flags |= PWinGravity;
+  setUSPosition(x, y);
+  resize(w, h);
+}
+
+TXEventHandler* TXWindow::setEventHandler(TXEventHandler* h)
+{
+  TXEventHandler* old = eventHandler;
+  eventHandler = h;
+  return old;
+}
+
+void TXWindow::addEventMask(long mask)
+{
+  eventMask |= mask;
+  XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::removeEventMask(long mask)
+{
+  eventMask &= ~mask;
+  XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::unmap()
+{
+  XUnmapWindow(dpy, win());
+  if (toplevel_) {
+    XUnmapEvent ue;
+    ue.type = UnmapNotify;
+    ue.display = dpy;
+    ue.event = DefaultRootWindow(dpy);
+    ue.window = win();
+    ue.from_configure = False;
+    XSendEvent(dpy, DefaultRootWindow(dpy), False,
+               (SubstructureRedirectMask|SubstructureNotifyMask),
+               (XEvent*)&ue);
+  }
+}
+
+void TXWindow::resize(int w, int h)
+{
+  //if (w == width_ && h == height_) return;
+  XResizeWindow(dpy, win(), w, h);
+  width_ = w;
+  height_ = h;
+  resizeNotify();
+}
+
+void TXWindow::setBorderWidth(int bw)
+{
+  XWindowChanges c;
+  c.border_width = bw;
+  XConfigureWindow(dpy, win(), CWBorderWidth, &c);
+}
+
+void TXWindow::ownSelection(Atom selection, Time time)
+{
+  XSetSelectionOwner(dpy, selection, win(), time);
+  if (XGetSelectionOwner(dpy, selection) == win()) {
+    selectionOwner_[selection] = true;
+    selectionOwnTime[selection] = time;
+  }
+}
+
+void TXWindow::handleXEvent(XEvent* ev)
+{
+  switch (ev->type) {
+
+  case ClientMessage:
+    if (ev->xclient.message_type == wmProtocols) {
+      if ((Atom)ev->xclient.data.l[0] == wmDeleteWindow) {
+        if (dwc) dwc->deleteWindow(this);
+      } else if ((Atom)ev->xclient.data.l[0] == wmTakeFocus) {
+        takeFocus(ev->xclient.data.l[1]);
+      }
+    }
+    break;
+
+  case ConfigureNotify:
+    if (ev->xconfigure.width != width_ || ev->xconfigure.height != height_) {
+      width_ = ev->xconfigure.width;
+      height_ = ev->xconfigure.height;
+      resizeNotify();
+    }
+    break;
+
+  case SelectionNotify:
+    if (ev->xselection.property != None) {
+      Atom type;
+      int format;
+      unsigned long nitems, after;
+      unsigned char *data;
+      XGetWindowProperty(dpy, win(), ev->xselection.property, 0, 16384, True,
+                         AnyPropertyType, &type, &format,
+                         &nitems, &after, &data);
+      if (type != None) {
+        selectionNotify(&ev->xselection, type, format, nitems, data);
+        XFree(data);
+        break;
+      }
+    }
+    selectionNotify(&ev->xselection, 0, 0, 0, 0);
+    break;
+
+  case SelectionRequest:
+    {
+      XSelectionEvent se;
+      se.type = SelectionNotify;
+      se.display = ev->xselectionrequest.display;
+      se.requestor = ev->xselectionrequest.requestor;
+      se.selection = ev->xselectionrequest.selection;
+      se.time = ev->xselectionrequest.time;
+      se.target = ev->xselectionrequest.target;
+      if (ev->xselectionrequest.property == None)
+        ev->xselectionrequest.property = ev->xselectionrequest.target;
+      if (!selectionOwner_[se.selection]) {
+        se.property = None;
+      } else {
+        se.property = ev->xselectionrequest.property;
+        if (se.target == xaTARGETS) {
+          Atom targets[2];
+          targets[0] = xaTIMESTAMP;
+          targets[1] = XA_STRING;
+          XChangeProperty(dpy, se.requestor, se.property, XA_ATOM, 32,
+                          PropModeReplace, (unsigned char*)targets, 2);
+        } else if (se.target == xaTIMESTAMP) {
+          rdr::U32 t = selectionOwnTime[se.selection];
+          XChangeProperty(dpy, se.requestor, se.property, XA_INTEGER, 32,
+                          PropModeReplace, (unsigned char*)&t, 1);
+        } else if (se.target == XA_STRING) {
+          if (!selectionRequest(se.requestor, se.selection, se.property))
+            se.property = None;
+        }
+      }
+      XSendEvent(dpy, se.requestor, False, 0, (XEvent*)&se);
+      break;
+    }
+
+  case SelectionClear:
+    selectionOwner_[ev->xselectionclear.selection] = false;
+    break;
+  }
+
+  if (eventHandler) eventHandler->handleEvent(this, ev);
+}
+
+void TXWindow::drawBevel(GC gc, int x, int y, int w, int h, int b,
+                         unsigned long middle, unsigned long tl,
+                         unsigned long br, bool round)
+{
+  if (round) {
+    XGCValues gcv;
+    gcv.line_width = b;
+    XChangeGC(dpy, gc, GCLineWidth, &gcv);
+    XSetForeground(dpy, gc, middle);
+    XFillArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 0, 360*64);
+    XSetForeground(dpy, gc, tl);
+    XDrawArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 45*64, 180*64);
+    XSetForeground(dpy, gc, br);
+    XDrawArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 225*64, 180*64);
+  } else {
+    XSetForeground(dpy, gc, middle);
+    if (w-2*b > 0 && h-2*b > 0)
+      XFillRectangle(dpy, win(), gc, x+b, y+b, w-2*b, h-2*b);
+    XSetForeground(dpy, gc, tl);
+    XFillRectangle(dpy, win(), gc, x, y, w, b);
+    XFillRectangle(dpy, win(), gc, x, y, b, h);
+    XSetForeground(dpy, gc, br);
+    for (int i = 0; i < b; i++) {
+      if (w-i > 0) XFillRectangle(dpy, win(), gc, x+i, y+h-1-i, w-i, 1); 
+      if (h-1-i > 0) XFillRectangle(dpy, win(), gc, x+w-1-i, y+i+1, 1, h-1-i);
+    }
+  }
+}
diff --git a/tx/TXWindow.h b/tx/TXWindow.h
new file mode 100644
index 0000000..20c31c9
--- /dev/null
+++ b/tx/TXWindow.h
@@ -0,0 +1,210 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// TXWindow.h
+//
+// A TXWindow is the base class for all tx windows (widgets).  In addition it
+// contains a number of static methods and members which are used throughout
+// tx.
+//
+// Before calling any other tx methods, TXWindow::init() must be called with
+// the X display to use.
+
+#ifndef __TXWINDOW_H__
+#define __TXWINDOW_H__
+
+#include <rdr/types.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <map>
+
+
+// TXDeleteWindowCallback's deleteWindow() method is called when a top-level
+// window is "deleted" (closed) by the user using the window manager.
+class TXWindow;
+class TXDeleteWindowCallback {
+public:
+  virtual void deleteWindow(TXWindow* w) = 0;
+};
+
+// TXEventHandler is an interface implemented by classes wanting to handle X
+// events on a window.  Most derived classes of window are their own event
+// handlers.
+class TXEventHandler {
+public:
+  virtual void handleEvent(TXWindow* w, XEvent* ev) = 0;
+};
+
+class TXWindow {
+public:
+
+  // Constructor - creates a window of the given size, with the default
+  // background (currently grey).  It is mapped by default if it has a parent.
+  // If no parent is specified its parent is the root window and it will remain
+  // unmapped.
+  TXWindow(Display* dpy_, int width=1, int height=1, TXWindow* parent_=0,
+           int borderWidth=0);
+  virtual ~TXWindow();
+
+  // toplevel() declares that this is a top-level window.  Various
+  // window-manager-related properties are set on the window.  The given
+  // TXDeleteWindowCallback is notified when the window is "deleted" (cloesd)
+  // by the user.
+  void toplevel(const char* name, TXDeleteWindowCallback* dwc=0,
+                int argc=0, char** argv=0, const char* windowClass=0,
+                bool iconic=false);
+
+  // setMaxSize() tells the window manager the maximum size to allow a
+  // top-level window.  It has no effect on a non-top-level window.
+  void setMaxSize(int w, int h);
+
+  // setUSPosition() tells the window manager the position which the "user" has
+  // asked for a top-level window.  Most window managers ignore requests by a
+  // program for position, so you have to tell it that the "user" asked for the
+  // position.  This has no effect on a non-top-level window.
+  void setUSPosition(int x, int y);
+
+  void setGeometry(const char* geom, int x, int y, int w, int h);
+
+  // setTransientFor() tells the window manager that this window is "owned" by
+  // the given window.  The window manager can use this information as it sees
+  // fit.
+  void setTransientFor(Window w) { XSetTransientForHint(dpy, win(), w); }
+
+  // setEventHandler() sets the TXEventHandler to handle X events for this
+  // window.  It returns the previous event handler, so that handlers can chain
+  // themselves.
+  TXEventHandler* setEventHandler(TXEventHandler* h);
+
+  // Accessor methods
+  Window win() { return win_; }
+  int width() { return width_; }
+  int height() { return height_; }
+
+  // selectionOwner() returns true if this window owns the given selection.
+  bool selectionOwner(Atom selection) { return selectionOwner_[selection]; }
+
+  // Wrappers around common Xlib calls
+  void addEventMask(long mask);
+  void removeEventMask(long mask);
+  void map()                   { XMapWindow(dpy, win()); }
+  void unmap();
+  void setBg(unsigned long bg) { XSetWindowBackground(dpy, win(), bg); }
+  void move(int x, int y)      { XMoveWindow(dpy, win(), x, y); }
+  void resize(int w, int h);
+  void raise()                 { XRaiseWindow(dpy, win()); }
+  void setBorderWidth(int bw);
+
+  // ownSelection requests that the window owns the given selection from the
+  // given time (the time should be taken from an X event).
+  void ownSelection(Atom selection, Time time);
+
+
+  // drawBevel draws a rectangular or circular bevel filling the given
+  // rectangle, using the given colours for the middle, the top/left and the
+  // bottom/right.
+  void drawBevel(GC gc, int x, int y, int w, int h, int b,
+                 unsigned long middle, unsigned long tl, unsigned long br,
+                 bool round=false);
+
+  // Methods to be overridden in a derived class
+
+  // resizeNotify() is called whenever the window's dimensions may have
+  // changed.
+  virtual void resizeNotify() {}
+
+  // takeFocus() is called when the window has received keyboard focus from the
+  // window manager.
+  virtual void takeFocus(Time time) {}
+
+  // selectionNotify() is called when the selection owner has replied to a
+  // request for information about a selection from the selection owner.
+  virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+                               int nitems, void* data) {}
+
+  // selectionRequest() is called when this window is the selection owner and
+  // another X client has requested the selection.  It should set the given
+  // property on the given window to the value of the given selection,
+  // returning true if successful, false otherwise.
+  virtual bool selectionRequest(Window requestor,
+                                Atom selection, Atom property) { return false;}
+
+  // Static methods
+
+  // init() must be called before any other tx methods.
+  static void init(Display* dpy, const char* defaultWindowClass);
+
+  // getColours() sets the pixel values in the cols array to the best available
+  // for the given rgb values, even in the case of a full colormap.
+  static void getColours(Display* dpy, XColor* cols, int nCols);
+
+  // handleXEvents() should be called whenever there are events to handle on
+  // the connection to the X display.  It process all available events, then
+  // returns when there are no more events to process.
+  static void handleXEvents(Display* dpy);
+
+  // windowWithName() locates a window with a given name on a display.
+  static Window windowWithName(Display* dpy, Window top, const char* name);
+
+  // strEmptyToNull() returns the string it's given but turns an empty string
+  // into null, which can be useful for passing rfb parameters to Xlib calls.
+  static char* strEmptyToNull(char* s) { return s && s[0] ? s : 0; }
+
+  // The following are default values for various things.
+  static unsigned long black, white;
+  static unsigned long defaultFg, defaultBg, lightBg, darkBg;
+  static unsigned long disabledFg, disabledBg, enabledBg;
+  static unsigned long scrollbarBg;
+  static GC defaultGC;
+  static Colormap cmap;
+  static Font defaultFont;
+  static XFontStruct* defaultFS;
+  static Time cutBufferTime;
+  static Pixmap dot, tick;
+  static const int dotSize, tickSize;
+  static char* defaultWindowClass;
+
+  Display* const dpy;
+
+  int xPad, yPad, bevel;
+
+private:
+
+  // handleXEvent() is called from handleXEvents() when an event for this
+  // window arrives.  It does general event processing before calling on to the
+  // event handler.
+  void handleXEvent(XEvent* ev);
+
+  TXWindow* parent;
+  Window win_;
+  int width_, height_;
+  TXEventHandler* eventHandler;
+  TXDeleteWindowCallback* dwc;
+  long eventMask;
+  XSizeHints sizeHints;
+  std::map<Atom,Time> selectionOwnTime;
+  std::map<Atom,bool> selectionOwner_;
+  bool toplevel_;
+};
+
+extern Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+extern Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+extern Atom xaCLIPBOARD;
+
+#endif
diff --git a/tx/Timer.cxx b/tx/Timer.cxx
new file mode 100644
index 0000000..78cff1c
--- /dev/null
+++ b/tx/Timer.cxx
@@ -0,0 +1,105 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Timer.cxx
+//
+
+#include "Timer.h"
+
+static Timer* timers;
+
+void Timer::callTimers()
+{
+  struct timeval now;
+  gettimeofday(&now, 0);
+  while (timers) {
+    Timer* timer = timers;
+    if (timer->before(now)) {
+      timers = timers->next;
+      timer->cb->timerCallback(timer);
+    } else {
+      break;
+    }
+  }
+}
+
+bool Timer::getTimeout(struct timeval* timeout)
+{
+  if (!timers) return false;
+  Timer* timer = timers;
+  struct timeval now;
+  gettimeofday(&now, 0);
+  if (timer->before(now)) {
+    timeout->tv_sec = 0;
+    timeout->tv_usec = 0;
+    return true;
+  }
+  timeout->tv_sec = timer->tv.tv_sec - now.tv_sec;
+  if (timer->tv.tv_usec < now.tv_usec) {
+    timeout->tv_usec = timer->tv.tv_usec + 1000000 - now.tv_usec;
+    timeout->tv_sec--;
+  } else {
+    timeout->tv_usec = timer->tv.tv_usec - now.tv_usec;
+  }
+  return true;
+}
+
+Timer::Timer(TimerCallback* cb_) : cb(cb_) {}
+Timer::~Timer()
+{
+  cancel();
+}
+
+void Timer::reset(int ms) {
+  cancel();
+  gettimeofday(&tv, 0);
+  tv.tv_sec += ms / 1000;
+  tv.tv_usec += (ms % 1000) * 1000;
+  if (tv.tv_usec > 1000000) {
+    tv.tv_sec += 1;
+    tv.tv_usec -= 1000000;
+  }
+
+  Timer** timerPtr;
+  for (timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
+    if (before((*timerPtr)->tv)) {
+      next = *timerPtr;
+      *timerPtr = this;
+      break;
+    }
+  }
+  if (!*timerPtr) {
+    next = 0;
+    *timerPtr = this;
+  }
+}
+
+void Timer::cancel() {
+  for (Timer** timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
+    if (*timerPtr == this) {
+      *timerPtr = (*timerPtr)->next;
+      break;
+    }
+  }
+}
+
+bool Timer::isSet() {
+  for (Timer* timer = timers; timer; timer = timer->next)
+    if (timer == this) return true;
+  return false;
+}
diff --git a/tx/Timer.h b/tx/Timer.h
new file mode 100644
index 0000000..dabe4cd
--- /dev/null
+++ b/tx/Timer.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#include <sys/time.h>
+#include <unistd.h>
+
+class TimerCallback;
+
+class Timer {
+public:
+  static void callTimers();
+  static bool getTimeout(struct timeval* timeout);
+
+  Timer(TimerCallback* cb_);
+  ~Timer();
+  void reset(int ms);
+  void cancel();
+  bool isSet();
+  bool before(struct timeval other) {
+    return (tv.tv_sec < other.tv_sec ||
+            (tv.tv_sec == other.tv_sec && tv.tv_usec < other.tv_usec));
+  }
+private:
+  struct timeval tv;
+  TimerCallback* cb;
+  Timer* next;
+};
+
+class TimerCallback {
+public:
+  virtual void timerCallback(Timer* timer) = 0;
+};
+
+#endif
diff --git a/vnc.dsw b/vnc.dsw
new file mode 100644
index 0000000..e6b377c
--- /dev/null
+++ b/vnc.dsw
@@ -0,0 +1,206 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Xregion"=.\Xregion\Xregion.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "logmessages"=.\logmessages\logmessages.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "network"=.\network\network.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rdr"=.\rdr\rdr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rfb"=.\rfb\rfb.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name rdr
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name Xregion
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "rfb_win32"=.\rfb_win32\rfb_win32.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name rfb
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "vncconfig"=.\vncconfig\vncconfig.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name rfb
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name rfb_win32
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name wm_hooks
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name network
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name Xregion
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "vncviewer"=.\vncviewer\vncviewer.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name rfb
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name zlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name rfb_win32
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name network
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name rdr
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name Xregion
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "winvnc"=.\winvnc\winvnc.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name network
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name rfb_win32
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name wm_hooks
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name zlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name Xregion
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "wm_hooks"=.\wm_hooks\wm_hooks.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "zlib"=.\zlib\zlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/vncconfig/Authentication.h b/vncconfig/Authentication.h
new file mode 100644
index 0000000..5923c2c
--- /dev/null
+++ b/vncconfig/Authentication.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_AUTHENTICATION
+#define WINVNCCONF_AUTHENTICATION
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+
+extern rfb::VncAuthPasswdConfigParameter vncAuthPasswd;
+
+namespace rfb {
+
+  namespace win32 {
+
+    class VncPasswdDialog : public Dialog {
+    public:
+      VncPasswdDialog(const RegKey& rk) : Dialog(GetModuleHandle(0)), regKey(rk), warnPasswdInsecure(false) {}
+      bool showDialog() {
+        return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD));
+      }
+      bool onOk() {
+        TCharArray password1 = getItemString(IDC_PASSWORD1);
+        TCharArray password2 = getItemString(IDC_PASSWORD2);;
+        if (_tcscmp(password1.buf, password2.buf) != 0) {
+          MsgBox(0, _T("The supplied passwords do not match"),
+                 MB_ICONEXCLAMATION | MB_OK);
+          return false;
+        }
+        if (warnPasswdInsecure &&
+          (MsgBox(0, _T("Please note that your VNC password cannot be stored securely on this system.  ")
+                     _T("Are you sure you wish to continue?"),
+                  MB_YESNO | MB_ICONWARNING) == IDNO))
+          return false;
+        char passwd[9];
+        memset(passwd, 0, sizeof(passwd));
+        strCopy(passwd, CStr(password1.buf), sizeof(passwd));
+        vncAuthObfuscatePasswd(passwd);
+        regKey.setBinary(_T("Password"), passwd, 8);
+        return true;
+      }
+      void setWarnPasswdInsecure(bool warn) {
+        warnPasswdInsecure = warn;
+      }
+    protected:
+      const RegKey& regKey;
+      bool warnPasswdInsecure;
+    };
+
+    class AuthenticationPage : public PropSheetPage {
+    public:
+      AuthenticationPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)),
+        passwd(rk), regKey(rk) {}
+      void initDialog() {
+        CharArray sec_types_str;
+        sec_types_str.buf = rfb::Server::sec_types.getData();
+        std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
+
+        useNone = useVNC = false;
+        std::list<int>::iterator i;
+        for (i=sec_types.begin(); i!=sec_types.end(); i++) {
+          if ((*i) == secTypeNone) useNone = true;
+          else if ((*i) == secTypeVncAuth) useVNC = true;
+        }
+
+        setItemChecked(IDC_AUTH_NONE, useNone);
+        setItemChecked(IDC_AUTH_VNC, useVNC);
+        setItemChecked(IDC_QUERY_CONNECT, rfb::Server::queryConnect);
+      }
+      bool onCommand(int id, int cmd) {
+        switch (id) {
+        case IDC_AUTH_VNC_PASSWD:
+          passwd.showDialog();
+          return true;
+        case IDC_AUTH_NONE:
+        case IDC_AUTH_VNC:
+        case IDC_QUERY_CONNECT:
+          setChanged((rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) ||
+                     (useNone != isItemChecked(IDC_AUTH_NONE)) ||
+                     (useVNC != isItemChecked(IDC_AUTH_VNC)));
+          return false;
+        };
+        return false;
+      }
+      bool onOk() {
+        useVNC = isItemChecked(IDC_AUTH_VNC);
+        useNone = isItemChecked(IDC_AUTH_NONE);
+        if (useVNC) {
+          CharArray password = vncAuthPasswd.getVncAuthPasswd();
+          if (!password.buf || strlen(password.buf) == 0) {
+            MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified!  ")
+                      _T("The password dialog will now be shown."), MB_ICONEXCLAMATION | MB_OK);
+            passwd.showDialog();
+          }
+          regKey.setString(_T("SecurityTypes"), _T("VncAuth"));
+        } else if (useNone) {
+          regKey.setString(_T("SecurityTypes"), _T("None"));
+        }
+        regKey.setString(_T("ReverseSecurityTypes"), _T("None"));
+        regKey.setBool(_T("QueryConnect"), isItemChecked(IDC_QUERY_CONNECT));
+        return true;
+      }
+      void setWarnPasswdInsecure(bool warn) {
+        passwd.setWarnPasswdInsecure(warn);
+      }
+    protected:
+      RegKey regKey;
+      VncPasswdDialog passwd;
+      bool useNone;
+      bool useVNC;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncconfig/Connections.h b/vncconfig/Connections.h
new file mode 100644
index 0000000..133e81c
--- /dev/null
+++ b/vncconfig/Connections.h
@@ -0,0 +1,278 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_CONNECTIONS
+#define WINVNCCONF_CONNECTIONS
+
+#include <vector>
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/Configuration.h>
+#include <rfb/Blacklist.h>
+#include <network/TcpSocket.h>
+
+static rfb::IntParameter http_port("HTTPPortNumber",
+  "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
+static rfb::IntParameter port_number("PortNumber",
+  "TCP/IP port on which the server will accept connections", 5900);
+static rfb::StringParameter hosts("Hosts",
+  "Filter describing which hosts are allowed access to this server", "+");
+static rfb::BoolParameter localHost("LocalHost",
+  "Only accept connections from via the local loop-back network interface", false);
+
+namespace rfb {
+
+  namespace win32 {
+
+    class ConnHostDialog : public Dialog {
+    public:
+      ConnHostDialog() : Dialog(GetModuleHandle(0)) {}
+      bool showDialog(const TCHAR* pat) {
+        delete [] pattern.buf;
+        pattern.buf = tstrDup(pat);
+        return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONN_HOST));
+      }
+      void initDialog() {
+        if (_tcslen(pattern.buf) == 0) {
+          delete [] pattern.buf;
+          pattern.buf = tstrDup(_T("+"));
+        }
+
+        if (pattern.buf[0] == _T('+'))
+          setItemChecked(IDC_ALLOW, true);
+        else
+          setItemChecked(IDC_DENY, true);
+
+        setItemString(IDC_HOST_PATTERN, &pattern.buf[1]);
+
+        delete [] pattern.buf;
+        pattern.buf = 0;
+      }
+      bool onOk() {
+        delete [] pattern.buf;
+        pattern.buf = 0;
+
+        TCharArray host = getItemString(IDC_HOST_PATTERN);
+
+        TCharArray newPat(_tcslen(host.buf)+2);
+        if (isItemChecked(IDC_ALLOW))
+          newPat.buf[0] = _T('+');
+        else
+          newPat.buf[0] = _T('-');
+        newPat.buf[1] = 0;
+        _tcscat(newPat.buf, host.buf);
+
+        network::TcpFilter::Pattern pat = network::TcpFilter::parsePattern(CStr(newPat.buf));
+        pattern.buf = TCharArray(network::TcpFilter::patternToStr(pat)).takeBuf();
+        return true;
+      }
+      const TCHAR* getPattern() {return pattern.buf;}
+    protected:
+      TCharArray pattern;
+    };
+
+    class ConnectionsPage : public PropSheetPage {
+    public:
+      ConnectionsPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_CONNECTIONS)), regKey(rk) {}
+      void initDialog() {
+        vlog.debug("set IDC_PORT %d", (int)port_number);
+        setItemInt(IDC_PORT, port_number);
+        setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout);
+        vlog.debug("set IDC_HTTP_PORT %d", (int)http_port);
+        setItemInt(IDC_HTTP_PORT, http_port);
+        setItemChecked(IDC_HTTP_ENABLE, http_port != 0);
+        enableItem(IDC_HTTP_PORT, http_port != 0);
+        setItemChecked(IDC_LOCALHOST, localHost);
+
+        HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+        while (SendMessage(listBox, LB_GETCOUNT, 0, 0))
+          SendMessage(listBox, LB_DELETESTRING, 0, 0);
+
+        CharArray tmp;
+        tmp.buf = hosts.getData();
+        while (tmp.buf) {
+          CharArray first;
+          strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
+          if (strlen(first.buf))
+            SendMessage(listBox, LB_ADDSTRING, 0, (LPARAM)(const TCHAR*)TStr(first.buf));
+        }
+
+        onCommand(IDC_HOSTS, EN_CHANGE);
+      }
+      bool onCommand(int id, int cmd) {
+        switch (id) {
+        case IDC_HOSTS:
+          {
+            DWORD selected = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCURSEL, 0, 0);
+            int count = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCOUNT, 0, 0);
+            bool enable = selected != LB_ERR;
+            enableItem(IDC_HOST_REMOVE, enable);
+            enableItem(IDC_HOST_UP, enable && (selected > 0));
+            enableItem(IDC_HOST_DOWN, enable && (selected < count-1));
+            enableItem(IDC_HOST_EDIT, enable);
+            setChanged(isChanged());
+          }
+          return true;
+
+        case IDC_PORT:
+          if (cmd == EN_CHANGE) {
+            try {
+              if (getItemInt(IDC_PORT) > 100)
+                setItemInt(IDC_HTTP_PORT, getItemInt(IDC_PORT)-100);
+            } catch (...) {
+            }
+          }
+        case IDC_HTTP_PORT:
+        case IDC_IDLE_TIMEOUT:
+          if (cmd == EN_CHANGE)
+            setChanged(isChanged());
+          return false;
+
+        case IDC_HTTP_ENABLE:
+          enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE));
+          setChanged(isChanged());
+          return false;
+
+        case IDC_LOCALHOST:
+          enableItem(IDC_HOSTS, !isItemChecked(IDC_LOCALHOST));
+          enableItem(IDC_HOST_REMOVE, !isItemChecked(IDC_LOCALHOST));
+          enableItem(IDC_HOST_UP, !isItemChecked(IDC_LOCALHOST));
+          enableItem(IDC_HOST_DOWN, !isItemChecked(IDC_LOCALHOST));
+          enableItem(IDC_HOST_EDIT, !isItemChecked(IDC_LOCALHOST));
+          enableItem(IDC_HOST_ADD, !isItemChecked(IDC_LOCALHOST));
+          setChanged(isChanged());
+          return false;
+
+        case IDC_HOST_ADD:
+          if (hostDialog.showDialog(_T("")))
+          {
+            const TCHAR* pattern = hostDialog.getPattern();
+            if (pattern)
+              SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_ADDSTRING, 0, (LPARAM)pattern);
+          }
+          return true;
+
+        case IDC_HOST_EDIT:
+          {
+            HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+            int item = SendMessage(listBox, LB_GETCURSEL, 0, 0);
+            TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1);
+            SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf);
+
+            if (hostDialog.showDialog(pattern.buf)) {
+              const TCHAR* newPat = hostDialog.getPattern();
+              if (newPat) {
+                item = SendMessage(listBox, LB_FINDSTRINGEXACT, item, (LPARAM)pattern.buf);
+                if (item != LB_ERR) {
+                  SendMessage(listBox, LB_DELETESTRING, item, 0); 
+                  SendMessage(listBox, LB_INSERTSTRING, item, (LPARAM)newPat);
+                  SendMessage(listBox, LB_SETCURSEL, item, 0);
+                  onCommand(IDC_HOSTS, EN_CHANGE);
+                }
+              }
+            }
+          }
+          return true;
+
+        case IDC_HOST_UP:
+          {
+            HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+            int item = SendMessage(listBox, LB_GETCURSEL, 0, 0);
+            TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1);
+            SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf);
+            SendMessage(listBox, LB_DELETESTRING, item, 0);
+            SendMessage(listBox, LB_INSERTSTRING, item-1, (LPARAM)pattern.buf);
+            SendMessage(listBox, LB_SETCURSEL, item-1, 0);
+            onCommand(IDC_HOSTS, EN_CHANGE);
+          }
+          return true;
+
+        case IDC_HOST_DOWN:
+          {
+            HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+            int item = SendMessage(listBox, LB_GETCURSEL, 0, 0);
+            TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1);
+            SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf);
+            SendMessage(listBox, LB_DELETESTRING, item, 0);
+            SendMessage(listBox, LB_INSERTSTRING, item+1, (LPARAM)pattern.buf);
+            SendMessage(listBox, LB_SETCURSEL, item+1, 0);
+            onCommand(IDC_HOSTS, EN_CHANGE);
+          }
+          return true;
+
+        case IDC_HOST_REMOVE:
+          {
+            HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+            int item = SendMessage(listBox, LB_GETCURSEL, 0, 0);
+            SendMessage(listBox, LB_DELETESTRING, item, 0);
+            onCommand(IDC_HOSTS, EN_CHANGE);
+          }
+
+        }
+        return false;
+      }
+      bool onOk() {
+        regKey.setInt(_T("PortNumber"), getItemInt(IDC_PORT));
+        regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST));
+        regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT));
+        regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) ? getItemInt(IDC_HTTP_PORT) : 0);
+
+        regKey.setString(_T("Hosts"), TCharArray(getHosts()).buf);
+        return true;
+      }
+      bool isChanged() {
+        try {
+          CharArray new_hosts = getHosts();
+          CharArray old_hosts = hosts.getData();
+          return (strcmp(new_hosts.buf, old_hosts.buf) != 0) ||
+              (localHost != isItemChecked(IDC_LOCALHOST)) ||
+              (port_number != getItemInt(IDC_PORT)) ||
+              (http_port != getItemInt(IDC_HTTP_PORT)) ||
+              ((http_port!=0) != (isItemChecked(IDC_HTTP_ENABLE)!=0)) ||
+              (rfb::Server::idleTimeout != getItemInt(IDC_IDLE_TIMEOUT));
+        } catch (rdr::Exception) {
+          return false;
+        }
+      }
+      char* getHosts() {
+        int bufLen = 1, i;
+        HWND listBox = GetDlgItem(handle, IDC_HOSTS);
+        for (i=0; i<SendMessage(listBox, LB_GETCOUNT, 0, 0); i++)
+          bufLen+=SendMessage(listBox, LB_GETTEXTLEN, i, 0)+1;
+        TCharArray hosts_str(bufLen);
+        hosts_str.buf[0] = 0;
+        TCHAR* outPos = hosts_str.buf;
+        for (i=0; i<SendMessage(listBox, LB_GETCOUNT, 0, 0); i++) {
+          outPos += SendMessage(listBox, LB_GETTEXT, i, (LPARAM)outPos);
+          outPos[0] = ',';
+          outPos[1] = 0;
+          outPos++;
+        }
+        return strDup(hosts_str.buf);
+      }
+    protected:
+      RegKey regKey;
+      ConnHostDialog hostDialog;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Desktop.h b/vncconfig/Desktop.h
new file mode 100644
index 0000000..a0a325a
--- /dev/null
+++ b/vncconfig/Desktop.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_DESKTOP
+#define WINVNCCONF_DESKTOP
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/SDisplay.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class DesktopPage : public PropSheetPage {
+    public:
+      DesktopPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DESKTOP)), regKey(rk) {}
+      void initDialog() {
+        CharArray action = rfb::win32::SDisplay::disconnectAction.getData();
+        bool disconnectLock = stricmp(action.buf, "Lock") == 0;
+        bool disconnectLogoff = stricmp(action.buf, "Logoff") == 0;
+        typedef BOOL (WINAPI *_LockWorkStation_proto)();
+        DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+        if (!_LockWorkStation.isValid()) {
+          enableItem(IDC_DISCONNECT_LOCK, false);
+          if (disconnectLock) {
+            disconnectLogoff = true;
+            disconnectLock = false;
+          }
+        }
+        setItemChecked(IDC_DISCONNECT_LOGOFF, disconnectLogoff);
+        setItemChecked(IDC_DISCONNECT_LOCK, disconnectLock);
+        setItemChecked(IDC_DISCONNECT_NONE, !disconnectLock && !disconnectLogoff);
+        setItemChecked(IDC_REMOVE_WALLPAPER, rfb::win32::SDisplay::removeWallpaper);
+        setItemChecked(IDC_REMOVE_PATTERN, rfb::win32::SDisplay::removePattern);
+        setItemChecked(IDC_DISABLE_EFFECTS, rfb::win32::SDisplay::disableEffects);
+      }
+      bool onCommand(int id, int cmd) {
+        switch (id) {
+        case IDC_DISCONNECT_LOGOFF:
+        case IDC_DISCONNECT_LOCK:
+        case IDC_DISCONNECT_NONE:
+        case IDC_REMOVE_WALLPAPER:
+        case IDC_REMOVE_PATTERN:
+        case IDC_DISABLE_EFFECTS:
+          CharArray action = rfb::win32::SDisplay::disconnectAction.getData();
+          bool disconnectLock = stricmp(action.buf, "Lock") == 0;
+          bool disconnectLogoff = stricmp(action.buf, "Logoff") == 0;
+          setChanged((disconnectLogoff != isItemChecked(IDC_DISCONNECT_LOGOFF)) ||
+                     (disconnectLock != isItemChecked(IDC_DISCONNECT_LOCK)) ||
+                     (isItemChecked(IDC_REMOVE_WALLPAPER) != rfb::win32::SDisplay::removeWallpaper) ||
+                     (isItemChecked(IDC_REMOVE_PATTERN) != rfb::win32::SDisplay::removePattern) ||
+                     (isItemChecked(IDC_DISABLE_EFFECTS) != rfb::win32::SDisplay::disableEffects));
+          break;
+        }
+        return false;
+      }
+      bool onOk() {
+        const TCHAR* action = _T("None");
+        if (isItemChecked(IDC_DISCONNECT_LOGOFF))
+          action = _T("Logoff");
+        else if (isItemChecked(IDC_DISCONNECT_LOCK))
+          action = _T("Lock");
+        regKey.setString(_T("DisconnectAction"), action);
+        regKey.setBool(_T("RemoveWallpaper"), isItemChecked(IDC_REMOVE_WALLPAPER));
+        regKey.setBool(_T("RemovePattern"), isItemChecked(IDC_REMOVE_PATTERN));
+        regKey.setBool(_T("DisableEffects"), isItemChecked(IDC_DISABLE_EFFECTS));
+        return true;
+      }
+    protected:
+      RegKey regKey;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Hooking.h b/vncconfig/Hooking.h
new file mode 100644
index 0000000..b9bec15
--- /dev/null
+++ b/vncconfig/Hooking.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_HOOKING
+#define WINVNCCONF_HOOKING
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/SDisplay.h>
+#include <rfb/ServerCore.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class HookingPage : public PropSheetPage {
+    public:
+      HookingPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_HOOKING)), regKey(rk) {}
+      void initDialog() {
+        setItemChecked(IDC_USEHOOKS, rfb::win32::SDisplay::use_hooks);
+        setItemChecked(IDC_POLLCONSOLES, rfb::win32::WMPoller::poll_console_windows);
+        setItemChecked(IDC_COMPAREFB, rfb::Server::compareFB);
+      }
+      bool onCommand(int id, int cmd) {
+        switch (id) {
+        case IDC_USEHOOKS:
+        case IDC_POLLCONSOLES:
+        case IDC_COMPAREFB:
+          setChanged((rfb::win32::SDisplay::use_hooks != isItemChecked(IDC_USEHOOKS)) ||
+            (rfb::win32::WMPoller::poll_console_windows != isItemChecked(IDC_POLLCONSOLES)) ||
+            (rfb::Server::compareFB != isItemChecked(IDC_COMPAREFB)));
+          break;
+        }
+        return false;
+      }
+      bool onOk() {
+        regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS));
+        regKey.setBool(_T("PollConsoleWindows"), isItemChecked(IDC_POLLCONSOLES));
+        regKey.setBool(_T("CompareFB"), isItemChecked(IDC_COMPAREFB));
+        return true;
+      }
+    protected:
+      RegKey regKey;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Inputs.h b/vncconfig/Inputs.h
new file mode 100644
index 0000000..2735d24
--- /dev/null
+++ b/vncconfig/Inputs.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_INPUTS
+#define WINVNCCONF_INPUTS
+
+#ifndef SPI_GETBLOCKSENDINPUTRESETS
+#define SPI_GETBLOCKSENDINPUTRESETS 0x1026
+#define SPI_SETBLOCKSENDINPUTRESETS 0x1027
+#endif
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/ServerCore.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class InputsPage : public PropSheetPage {
+    public:
+      InputsPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_INPUTS)),
+          regKey(rk), enableAffectSSaver(true) {}
+      void initDialog() {
+        setItemChecked(IDC_ACCEPT_KEYS, rfb::Server::acceptKeyEvents);
+        setItemChecked(IDC_ACCEPT_PTR, rfb::Server::acceptPointerEvents);
+        setItemChecked(IDC_ACCEPT_CUTTEXT, rfb::Server::acceptCutText);
+        setItemChecked(IDC_SEND_CUTTEXT, rfb::Server::sendCutText);
+        setItemChecked(IDC_DISABLE_LOCAL_INPUTS, SDisplay::disableLocalInputs);
+        BOOL blocked = FALSE;
+        if (SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &blocked, 0))
+          setItemChecked(IDC_AFFECT_SCREENSAVER, !blocked);
+        else
+          enableAffectSSaver = false;
+        enableItem(IDC_AFFECT_SCREENSAVER, enableAffectSSaver);
+      }
+      bool onCommand(int id, int cmd) {
+        BOOL inputResetsBlocked;
+        SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &inputResetsBlocked, 0);
+        setChanged((rfb::Server::acceptKeyEvents != isItemChecked(IDC_ACCEPT_KEYS)) ||
+          (rfb::Server::acceptPointerEvents != isItemChecked(IDC_ACCEPT_PTR)) ||
+          (rfb::Server::acceptCutText != isItemChecked(IDC_ACCEPT_CUTTEXT)) ||
+          (rfb::Server::sendCutText != isItemChecked(IDC_SEND_CUTTEXT)) ||
+          (SDisplay::disableLocalInputs != isItemChecked(IDC_DISABLE_LOCAL_INPUTS)) ||
+          (enableAffectSSaver && (!inputResetsBlocked != isItemChecked(IDC_AFFECT_SCREENSAVER))));
+        return false;
+      }
+      bool onOk() {
+        regKey.setBool(_T("AcceptKeyEvents"), isItemChecked(IDC_ACCEPT_KEYS));
+        regKey.setBool(_T("AcceptPointerEvents"), isItemChecked(IDC_ACCEPT_PTR));
+        regKey.setBool(_T("AcceptCutText"), isItemChecked(IDC_ACCEPT_CUTTEXT));
+        regKey.setBool(_T("SendCutText"), isItemChecked(IDC_SEND_CUTTEXT));
+        regKey.setBool(_T("DisableLocalInputs"), isItemChecked(IDC_DISABLE_LOCAL_INPUTS));
+        if (enableAffectSSaver) {
+          BOOL blocked = !isItemChecked(IDC_AFFECT_SCREENSAVER);
+          SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, blocked, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+        }
+        return true;
+      }
+    protected:
+      RegKey regKey;
+      bool enableAffectSSaver;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Legacy.cxx b/vncconfig/Legacy.cxx
new file mode 100644
index 0000000..5926651
--- /dev/null
+++ b/vncconfig/Legacy.cxx
@@ -0,0 +1,248 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <vncconfig/Legacy.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("Legacy");
+
+
+void LegacyPage::LoadPrefs()
+      {
+        // VNC 3.3.3R3 Preferences Algorithm, as described by vncProperties.cpp
+        // - Load user-specific settings, based on logged-on user name,
+        //   from HKLM/Software/ORL/WinVNC3/<user>.  If they don't exist,
+        //   try again with username "Default".
+        // - Load system-wide settings from HKLM/Software/ORL/WinVNC3.
+        // - If AllowProperties is non-zero then load the user's own
+        //   settings from HKCU/Software/ORL/WinVNC3.
+
+        // Get the name of the current user
+        TCharArray username;
+        try {
+          UserName name;
+          username.buf = name.takeBuf();
+        } catch (rdr::SystemException& e) {
+          if (e.err != ERROR_NOT_LOGGED_ON)
+            throw;
+        }
+
+        // Open and read the WinVNC3 registry key
+        allowProperties = true;
+        RegKey winvnc3;
+        try {
+          winvnc3.openKey(HKEY_LOCAL_MACHINE, _T("Software\\ORL\\WinVNC3"));
+          int debugMode = winvnc3.getInt(_T("DebugMode"), 0);
+          const char* debugTarget = 0; 
+          if (debugMode & 2) debugTarget = "file";
+          if (debugMode & 4) debugTarget = "stderr";
+          if (debugTarget) {
+            char logSetting[32];
+            sprintf(logSetting, "*:%s:%d", debugTarget, winvnc3.getInt(_T("DebugLevel"), 0));
+            regKey.setString(_T("Log"), TStr(logSetting));
+          }
+ 
+          TCharArray authHosts;
+          authHosts.buf = winvnc3.getString(_T("AuthHosts"), 0);
+          if (authHosts.buf) {
+            CharArray newHosts;
+            newHosts.buf = strDup("");
+
+            // Reformat AuthHosts to Hosts.  Wish I'd left the format the same. :( :( :(
+            try {
+              CharArray tmp = strDup(authHosts.buf);
+              while (tmp.buf) {
+
+                // Split the AuthHosts string into patterns to match
+                CharArray first;
+                rfb::strSplit(tmp.buf, ':', &first.buf, &tmp.buf);
+                if (strlen(first.buf)) {
+                  int bits = 0;
+                  CharArray pattern(1+4*4+4);
+                  pattern.buf[0] = first.buf[0];
+                  pattern.buf[1] = 0;
+
+                  // Split the pattern into IP address parts and process
+                  rfb::CharArray address;
+                  address.buf = rfb::strDup(&first.buf[1]);
+                  while (address.buf) {
+                    rfb::CharArray part;
+                    rfb::strSplit(address.buf, '.', &part.buf, &address.buf);
+                    if (bits)
+                      strcat(pattern.buf, ".");
+                    if (strlen(part.buf) > 3)
+                      throw rdr::Exception("Invalid IP address part");
+                    if (strlen(part.buf) > 0) {
+                      strcat(pattern.buf, part.buf);
+                      bits += 8;
+                    }
+                  }
+
+                  // Pad out the address specification if required
+                  int addrBits = bits;
+                  while (addrBits < 32) {
+                    if (addrBits) strcat(pattern.buf, ".");
+                    strcat(pattern.buf, "0");
+                    addrBits += 8;
+                  }
+
+                  // Append the number of bits to match
+                  char buf[4];
+                  sprintf(buf, "/%d", bits);
+                  strcat(pattern.buf, buf);
+
+                  // Append this pattern to the Hosts value
+                  int length = strlen(newHosts.buf) + strlen(pattern.buf) + 2;
+                  CharArray tmpHosts(length);
+                  strcpy(tmpHosts.buf, pattern.buf);
+                  if (strlen(newHosts.buf)) {
+                    strcat(tmpHosts.buf, ",");
+                    strcat(tmpHosts.buf, newHosts.buf);
+                  }
+                  delete [] newHosts.buf;
+                  newHosts.buf = tmpHosts.takeBuf();
+                }
+              }
+
+              // Finally, save the Hosts value
+              regKey.setString(_T("Hosts"), TStr(newHosts.buf));
+            } catch (rdr::Exception) {
+              MsgBox(0, _T("Unable to convert AuthHosts setting to Hosts format."),
+                     MB_ICONWARNING | MB_OK);
+            }
+          } else {
+            regKey.setString(_T("Hosts"), _T("+"));
+          }
+
+          regKey.setBool(_T("LocalHost"), winvnc3.getBool(_T("LoopbackOnly"), false));
+          // *** check AllowLoopback?
+
+          if (winvnc3.getBool(_T("AuthRequired"), true))
+            regKey.setString(_T("SecurityTypes"), _T("VncAuth"));
+          else
+            regKey.setString(_T("SecurityTypes"), _T("None"));
+
+          int connectPriority = winvnc3.getInt(_T("ConnectPriority"), 0);
+          regKey.setBool(_T("DisconnectClients"), connectPriority == 0);
+          regKey.setBool(_T("AlwaysShared"), connectPriority == 1);
+          regKey.setBool(_T("NeverShared"), connectPriority == 2);
+
+        } catch(rdr::Exception) {
+        }
+
+        // Open the local, default-user settings
+        allowProperties = true;
+        try {
+          RegKey userKey;
+          userKey.openKey(winvnc3, _T("Default"));
+          vlog.info("loading Default prefs");
+          LoadUserPrefs(userKey);
+        } catch(rdr::Exception& e) {
+          vlog.error("error reading Default settings:%s", e.str());
+        }
+
+        // Open the local, user-specific settings
+        if (userSettings && username.buf) {
+          try {
+            RegKey userKey;
+            userKey.openKey(winvnc3, username.buf);
+            vlog.info("loading local User prefs");
+            LoadUserPrefs(userKey);
+          } catch(rdr::Exception& e) {
+            vlog.error("error reading local User settings:%s", e.str());
+          }
+
+          // Open the user's own settings
+          if (allowProperties) {
+            try {
+              RegKey userKey;
+              userKey.openKey(HKEY_CURRENT_USER, _T("Software\\ORL\\WinVNC3"));
+              vlog.info("loading global User prefs");
+              LoadUserPrefs(userKey);
+            } catch(rdr::Exception& e) {
+              vlog.error("error reading global User settings:%s", e.str());
+            }
+          }
+        }
+
+        // Disable the Options menu item if appropriate
+        regKey.setBool(_T("DisableOptions"), !allowProperties);
+      }
+
+      void LegacyPage::LoadUserPrefs(const RegKey& key)
+      {
+        if (key.getBool(_T("HTTPConnect"), true))
+          regKey.setInt(_T("HTTPPortNumber"), key.getInt(_T("PortNumber"), 5900)-100);
+        else
+          regKey.setInt(_T("HTTPPortNumber"), 0);
+        regKey.setInt(_T("PortNumber"), key.getBool(_T("SocketConnect")) ? key.getInt(_T("PortNumber"), 5900) : 0);
+        if (key.getBool(_T("AutoPortSelect"), false)) {
+          MsgBox(0, _T("The AutoPortSelect setting is not supported by this release.")
+                    _T("The port number will default to 5900."),
+                    MB_ICONWARNING | MB_OK);
+          regKey.setInt(_T("PortNumber"), 5900);
+        }
+        regKey.setInt(_T("IdleTimeout"), key.getInt(_T("IdleTimeout"), 0));
+
+        regKey.setBool(_T("RemoveWallpaper"), key.getBool(_T("RemoveWallpaper")));
+        regKey.setBool(_T("RemovePattern"), key.getBool(_T("RemoveWallpaper")));
+        regKey.setBool(_T("DisableEffects"), key.getBool(_T("RemoveWallpaper")));
+
+        if (key.getInt(_T("QuerySetting"), 2) != 2) {
+          regKey.setBool(_T("QueryConnect"), key.getInt(_T("QuerySetting")) > 2);
+          MsgBox(0, _T("The QuerySetting option has been replaced by QueryConnect.")
+                 _T("Please see the documentation for details of the QueryConnect option."),
+                 MB_ICONWARNING | MB_OK);
+        }
+        regKey.setInt(_T("QueryTimeout"), key.getInt(_T("QueryTimeout"), 10));
+
+        rfb::CharArray passwd;
+        int length;
+        key.getBinary(_T("Password"), (void**)&passwd.buf, &length, 0, 0);
+        regKey.setBinary(_T("Password"), passwd.buf, length);
+
+        bool enableInputs = key.getBool(_T("InputsEnabled"), true);
+        regKey.setBool(_T("AcceptKeyEvents"), enableInputs);
+        regKey.setBool(_T("AcceptPointerEvents"), enableInputs);
+        regKey.setBool(_T("AcceptCutText"), enableInputs);
+        regKey.setBool(_T("SendCutText"), enableInputs);
+
+        switch (key.getInt(_T("LockSetting"), 0)) {
+        case 0: regKey.setString(_T("DisconnectAction"), _T("None")); break;
+        case 1: regKey.setString(_T("DisconnectAction"), _T("Lock")); break;
+        case 2: regKey.setString(_T("DisconnectAction"), _T("Logoff")); break;
+        };
+
+        regKey.setBool(_T("DisableLocalInputs"), key.getBool(_T("LocalInputsDisabled"), false));
+
+        // *** ignore polling preferences
+        // PollUnderCursor, PollForeground, OnlyPollConsole, OnlyPollOnEvent
+        regKey.setBool(_T("UseHooks"), !key.getBool(_T("PollFullScreen"), false));
+
+        if (key.isValue(_T("AllowShutdown")))
+          MsgBox(0, _T("The AllowShutdown option is not supported by this release."), MB_ICONWARNING | MB_OK);
+        if (key.isValue(_T("AllowEditClients")))
+          MsgBox(0, _T("The AllowEditClients option is not supported by this release."), MB_ICONWARNING | MB_OK);
+
+        allowProperties = key.getBool(_T("AllowProperties"), allowProperties);
+      }
diff --git a/vncconfig/Legacy.h b/vncconfig/Legacy.h
new file mode 100644
index 0000000..e66dc56
--- /dev/null
+++ b/vncconfig/Legacy.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef WINVNCCONF_LEGACY
+#define WINVNCCONF_LEGACY
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <lmcons.h>
+
+#include <vncconfig/resource.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/ServerCore.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class LegacyPage : public PropSheetPage {
+    public:
+      LegacyPage(const RegKey& rk, bool userSettings_)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_LEGACY)), regKey(rk), userSettings(userSettings_) {}
+      void initDialog() {
+        setItemChecked(IDC_PROTOCOL_3_3, rfb::Server::protocol3_3);
+      }
+      bool onCommand(int id, int cmd) {
+        switch (id) {
+        case IDC_LEGACY_IMPORT:
+          {
+            DWORD result = MsgBox(0,
+              _T("Importing your legacy VNC 3.3 settings will overwrite your existing settings.\n")
+              _T("Are you sure you wish to continue?"),
+              MB_ICONWARNING | MB_YESNO);
+            if (result == IDYES) {
+              LoadPrefs();
+              MsgBox(0, _T("Imported VNC 3.3 settings successfully."),
+                     MB_ICONINFORMATION | MB_OK);
+
+              // Sleep to allow RegConfig thread to reload settings
+              Sleep(1000);
+              propSheet->reInitPages();
+            }
+          }
+          return true;
+        case IDC_PROTOCOL_3_3:
+          setChanged(isItemChecked(IDC_PROTOCOL_3_3) != rfb::Server::protocol3_3);
+          return false;
+        };
+        return false;
+      }
+      bool onOk() {
+        regKey.setBool(_T("Protocol3.3"), isItemChecked(IDC_PROTOCOL_3_3));
+        return true;
+      }
+
+      void LoadPrefs();
+      void LoadUserPrefs(const RegKey& key);
+
+    protected:
+      bool allowProperties;
+      RegKey regKey;
+      bool userSettings;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Sharing.h b/vncconfig/Sharing.h
new file mode 100644
index 0000000..b63b39a
--- /dev/null
+++ b/vncconfig/Sharing.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef WINVNCCONF_SHARING
+#define WINVNCCONF_SHARING
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/ServerCore.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class SharingPage : public PropSheetPage {
+    public:
+      SharingPage(const RegKey& rk)
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_SHARING)), regKey(rk) {}
+      void initDialog() {
+        setItemChecked(IDC_DISCONNECT_CLIENTS, rfb::Server::disconnectClients);
+        setItemChecked(IDC_SHARE_NEVER, rfb::Server::neverShared);
+        setItemChecked(IDC_SHARE_ALWAYS, rfb::Server::alwaysShared);
+        setItemChecked(IDC_SHARE_CLIENT, !(rfb::Server::neverShared || rfb::Server::alwaysShared));
+      }
+      bool onCommand(int id, int cmd) {
+        setChanged((isItemChecked(IDC_DISCONNECT_CLIENTS) != rfb::Server::disconnectClients) ||
+          (isItemChecked(IDC_SHARE_NEVER) != rfb::Server::neverShared) ||
+          (isItemChecked(IDC_SHARE_ALWAYS) != rfb::Server::alwaysShared));
+        return true;
+      }
+      bool onOk() {
+        regKey.setBool(_T("DisconnectClients"), isItemChecked(IDC_DISCONNECT_CLIENTS));
+        regKey.setBool(_T("AlwaysShared"), isItemChecked(IDC_SHARE_ALWAYS));
+        regKey.setBool(_T("NeverShared"), isItemChecked(IDC_SHARE_NEVER));
+       return true;
+      }
+    protected:
+      RegKey regKey;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/resource.h b/vncconfig/resource.h
new file mode 100644
index 0000000..99e2cea
--- /dev/null
+++ b/vncconfig/resource.h
@@ -0,0 +1,86 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by vncconfig.rc
+//
+#define IDR_MANIFEST                    1
+#define IDI_ICON                        101
+#define IDD_DIALOG1                     102
+#define IDD_DIALOG2                     103
+#define IDD_SECURITY                    104
+#define IDD_AUTHENTICATION              104
+#define IDD_CONNECTIONS                 105
+#define IDD_HOOKING                     106
+#define IDD_VNC_PASSWD                  107
+#define IDD_AUTH_VNC_PASSWD             107
+#define IDD_LEGACY                      108
+#define IDD_CONN_HOST                   109
+#define IDD_SHARING                     110
+#define IDD_INPUTS                      111
+#define IDR_TRAY                        112
+#define IDD_ABOUT                       113
+#define IDI_CONNECTED                   115
+#define IDD_DESKTOP                     116
+#define IDC_EDIT1                       1000
+#define IDC_PORT                        1000
+#define IDC_PASSWORD1                   1000
+#define IDC_HOST_PATTERN                1000
+#define IDC_AUTH_NONE                   1002
+#define IDC_AUTH_VNC                    1003
+#define IDC_AUTH_VNC_PASSWD             1009
+#define IDC_USEHOOKS                    1011
+#define IDC_POLLCONSOLES                1012
+#define IDC_COMPAREFB                   1013
+#define IDC_IDLE_TIMEOUT                1015
+#define IDC_HOSTS                       1016
+#define IDC_HOST_ADD                    1017
+#define IDC_HOST_REMOVE                 1018
+#define IDC_HOST_UP                     1019
+#define IDC_BUTTON4                     1020
+#define IDC_HOST_DOWN                   1020
+#define IDC_HOST_EDIT                   1021
+#define IDC_PASSWORD2                   1022
+#define IDC_LEGACY_IMPORT               1023
+#define IDC_ALLOW                       1024
+#define IDC_DENY                        1025
+#define IDC_SHARE_ALWAYS                1030
+#define IDC_SHARE_NEVER                 1031
+#define IDC_SHARE_CLIENT                1032
+#define IDC_DISCONNECT_CLIENTS          1033
+#define IDC_ACCEPT_KEYS                 1034
+#define IDC_ACCEPT_PTR                  1035
+#define IDC_ACCEPT_CUTTEXT              1036
+#define IDC_SEND_CUTTEXT                1037
+#define IDC_PROTOCOL_3_3                1038
+#define IDC_DESCRIPTION                 1039
+#define IDC_BUILDTIME                   1040
+#define IDC_VERSION                     1041
+#define IDC_COPYRIGHT                   1042
+#define IDC_HTTP_ENABLE                 1043
+#define IDC_HTTP_PORT                   1044
+#define A                               1045
+#define IDC_BL_THRESHOLD                1046
+#define IDC_BL_TIMEOUT                  1047
+#define IDC_AFFECT_SCREENSAVER          1048
+#define IDC_LOCALHOST                   1049
+#define IDC_DISABLE_LOCAL_INPUTS        1050
+#define IDC_QUERY_CONNECT               1051
+#define IDC_DISCONNECT_NONE             1056
+#define IDC_DISCONNECT_LOCK             1057
+#define IDC_DISCONNECT_LOGOFF           1058
+#define IDC_REMOVE_WALLPAPER            1059
+#define IDC_REMOVE_PATTERN              1060
+#define IDC_DISABLE_EFFECTS             1061
+#define ID_OPTIONS                      40001
+#define ID_CLOSE                        40002
+#define ID_ABOUT                        40003
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        117
+#define _APS_NEXT_COMMAND_VALUE         40004
+#define _APS_NEXT_CONTROL_VALUE         1062
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/vncconfig/vncconfig.cxx b/vncconfig/vncconfig.cxx
new file mode 100644
index 0000000..2a98dbc
--- /dev/null
+++ b/vncconfig/vncconfig.cxx
@@ -0,0 +1,193 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <commctrl.h>
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include "resource.h"
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/CurrentUser.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("main");
+
+
+#include <vncconfig/Authentication.h>
+#include <vncconfig/Connections.h>
+#include <vncconfig/Sharing.h>
+#include <vncconfig/Hooking.h>
+#include <vncconfig/Inputs.h>
+#include <vncconfig/Legacy.h>
+#include <vncconfig/Desktop.h>
+
+
+TStr rfb::win32::AppName("VNC Config");
+
+
+VncAuthPasswdConfigParameter vncAuthPasswd;
+
+#ifdef _DEBUG
+BoolParameter captureDialogs("CaptureDialogs", "", false);
+#endif
+
+HKEY configKey = HKEY_CURRENT_USER;
+
+
+void
+processParams(int argc, char* argv[]) {
+  for (int i=1; i<argc; i++) {
+    if (strcasecmp(argv[i], "-service") == 0) {
+      configKey = HKEY_LOCAL_MACHINE;
+    } else if (strcasecmp(argv[i], "-user") == 0) {
+      configKey = HKEY_CURRENT_USER;
+    } else {
+      // Try to process <option>=<value>, or -<bool>
+      if (Configuration::setParam(argv[i], true))
+        continue;
+      // Try to process -<option> <value>
+      if ((argv[i][0] == '-') && (i+1 < argc)) {
+        if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+          i++;
+          continue;
+        }
+      }
+    }
+  }
+}
+
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, char* cmdLine, int cmdShow) {
+
+  // Configure debugging output
+#ifdef _DEBUG
+  AllocConsole();
+  freopen("CONIN$","rb",stdin);
+  freopen("CONOUT$","wb",stdout);
+  freopen("CONOUT$","wb",stderr);
+  setbuf(stderr, 0);
+  initStdIOLoggers();
+  LogWriter vlog("main");
+  logParams.setParam("*:stderr:100");
+  vlog.info("Starting vncconfig applet");
+#endif
+
+  try {
+    try {
+      // Process command-line args
+      int argc = __argc;
+      char** argv = __argv;
+      processParams(argc, argv);
+
+      /* *** Required if we wish to use IP address control
+      INITCOMMONCONTROLSEX icce;
+      icce.dwSize = sizeof(icce);
+      icce.dwICC = ICC_INTERNET_CLASSES;
+      InitCommonControlsEx(&icce);
+      */
+
+      // Create the required configuration registry key
+      RegKey rootKey;
+      rootKey.createKey(configKey, _T("Software\\RealVNC\\WinVNC4"));
+  
+      // Override whatever security it already had (NT only)
+      bool warnOnChangePassword = false;
+      try {
+        AccessEntries access;
+        Sid adminSID = Sid::Administrators();
+        Sid systemSID = Sid::SYSTEM();
+        access.addEntry(adminSID, KEY_ALL_ACCESS, GRANT_ACCESS);
+        access.addEntry(systemSID, KEY_ALL_ACCESS, GRANT_ACCESS);
+        UserName user;
+        if (configKey == HKEY_CURRENT_USER)
+          access.addEntry(user.buf, KEY_ALL_ACCESS, GRANT_ACCESS);
+        AccessControlList acl = CreateACL(access);
+
+        // Set the DACL, and don't allow the key to inherit its parent's DACL
+        rootKey.setDACL(acl, false);
+      } catch (rdr::SystemException& e) {
+        // Something weird happens on NT 4.0 SP5 but I can't reproduce it on other
+        // NT 4.0 service pack revisions.
+        if (e.err == ERROR_INVALID_PARAMETER) {
+          MsgBox(0, _T("Windows reported an error trying to secure the VNC Server settings for this user.  ")
+                    _T("Your settings may not be secure!"), MB_ICONWARNING | MB_OK);
+        } else if (e.err != ERROR_CALL_NOT_IMPLEMENTED &&
+                   e.err != ERROR_NOT_LOGGED_ON) {
+          // If the call is not implemented, ignore the error and continue
+          // If we are on Win9x and no user is logged on, ignore error and continue
+          throw;
+        }
+        warnOnChangePassword = true;
+      }
+
+      // Start the RegConfig reader, to load in existing settings
+      RegistryReader config;
+      config.setKey(configKey, _T("Software\\RealVNC\\WinVNC4"));
+
+      // Build the dialog
+      std::list<PropSheetPage*> pages;
+      AuthenticationPage auth(rootKey); pages.push_back(&auth);
+      auth.setWarnPasswdInsecure(warnOnChangePassword);
+      ConnectionsPage conn(rootKey); pages.push_back(&conn);
+      InputsPage inputs(rootKey); pages.push_back(&inputs);
+      SharingPage sharing(rootKey); pages.push_back(&sharing);
+      DesktopPage desktop(rootKey); pages.push_back(&desktop);
+      HookingPage hooks(rootKey); pages.push_back(&hooks);
+      LegacyPage legacy(rootKey, configKey == HKEY_CURRENT_USER); pages.push_back(&legacy);
+
+      // Load the default icon to use
+      HICON icon = (HICON)LoadImage(inst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
+
+      // Create the PropertySheet handler
+      TCHAR* propSheetTitle = _T("VNC Server Properties (Service-Mode)");
+      if (configKey == HKEY_CURRENT_USER)
+        propSheetTitle = _T("VNC Server Properties (User-Mode)");
+      PropSheet sheet(inst, propSheetTitle, pages, icon);
+
+#ifdef _DEBUG
+      vlog.debug("capture dialogs=%s", captureDialogs ? "true" : "false");
+      sheet.showPropSheet(0, true, false, captureDialogs);
+#else
+      sheet.showPropSheet(0, true, false);
+#endif
+
+      return 0;
+    } catch (rdr::SystemException& e) {
+      switch (e.err) {
+      case ERROR_ACCESS_DENIED:
+        MsgBox(0, _T("You do not have sufficient access rights to run the VNC Configuration applet"),
+               MB_ICONSTOP | MB_OK);
+        return 1;
+      };
+      throw;
+    }
+  } catch (rdr::Exception& e) {
+    MsgBox(NULL, TStr(e.str()), MB_ICONEXCLAMATION | MB_OK);
+    return 1;
+  }
+}
diff --git a/vncconfig/vncconfig.dsp b/vncconfig/vncconfig.dsp
new file mode 100644
index 0000000..b557dfa
--- /dev/null
+++ b/vncconfig/vncconfig.dsp
@@ -0,0 +1,188 @@
+# Microsoft Developer Studio Project File - Name="vncconfig" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vncconfig - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "vncconfig.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "vncconfig.mak" CFG="vncconfig - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "vncconfig - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vncconfig - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "vncconfig - Win32 Debug Unicode" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "vncconfig - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF  "$(CFG)" == "vncconfig - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncconfig___Win32_Debug"
+# PROP BASE Intermediate_Dir "vncconfig___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+
+!ELSEIF  "$(CFG)" == "vncconfig - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncconfig___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "vncconfig___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "vncconfig - Win32 Release"
+# Name "vncconfig - Win32 Debug"
+# Name "vncconfig - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Legacy.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Authentication.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Connections.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Desktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hooking.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Inputs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Legacy.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sharing.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\connected.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.rc
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\vncconfig.exe.manifest
+# End Source File
+# End Target
+# End Project
diff --git a/vncconfig/vncconfig.exe.manifest b/vncconfig/vncconfig.exe.manifest
new file mode 100644
index 0000000..35a4164
--- /dev/null
+++ b/vncconfig/vncconfig.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+   version="4.0.0.26"
+   processorArchitecture="X86"
+   name="RealVNC.vncconfig.exe"
+   type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+   <dependentAssembly>
+     <assemblyIdentity
+       type="win32"
+       name="Microsoft.Windows.Common-Controls"
+       version="6.0.0.0"
+       processorArchitecture="X86"
+       publicKeyToken="6595b64144ccf1df"
+       language="*"
+     />
+   </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/vncconfig/vncconfig.ico b/vncconfig/vncconfig.ico
new file mode 100644
index 0000000..5b555dd
--- /dev/null
+++ b/vncconfig/vncconfig.ico
Binary files differ
diff --git a/vncconfig/vncconfig.rc b/vncconfig/vncconfig.rc
new file mode 100644
index 0000000..e74c8f1
--- /dev/null
+++ b/vncconfig/vncconfig.rc
@@ -0,0 +1,479 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON                ICON    DISCARDABLE     "vncconfig.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_AUTHENTICATION DIALOG DISCARDABLE  0, 0, 225, 86
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Authentication"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "No Authentication or Encryption",IDC_AUTH_NONE,"Button",
+                    BS_AUTORADIOBUTTON | WS_GROUP,7,10,143,15
+    CONTROL         "VNC 3.3 Authentication, no Encryption",IDC_AUTH_VNC,
+                    "Button",BS_AUTORADIOBUTTON,7,30,143,15
+    PUSHBUTTON      "Set Password",IDC_AUTH_VNC_PASSWD,155,30,55,15
+    CONTROL         "Prompt local user to accept incoming connections",
+                    IDC_QUERY_CONNECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    7,50,211,15
+END
+
+IDD_CONNECTIONS DIALOG DISCARDABLE  0, 0, 218, 198
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Connections"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT           "Accept connections on port:",IDC_STATIC,7,10,138,15,
+                    SS_CENTERIMAGE
+    LTEXT           "Disconnect idle clients after (seconds):",IDC_STATIC,7,
+                    25,138,15,SS_CENTERIMAGE
+    EDITTEXT        IDC_PORT,150,10,61,15,ES_AUTOHSCROLL | ES_NUMBER
+    EDITTEXT        IDC_IDLE_TIMEOUT,150,25,61,15,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "Serve Java viewer via HTTP on port:",IDC_HTTP_ENABLE,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,138,15
+    EDITTEXT        IDC_HTTP_PORT,150,40,61,15,ES_AUTOHSCROLL | ES_NUMBER
+    GROUPBOX        "Access Control",IDC_STATIC,7,55,204,135
+    CONTROL         "Only accept connections from the local machine",
+                    IDC_LOCALHOST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,
+                    70,190,15
+    LISTBOX         IDC_HOSTS,15,90,130,95,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | 
+                    WS_TABSTOP
+    PUSHBUTTON      "&Add",IDC_HOST_ADD,150,90,55,15
+    PUSHBUTTON      "&Remove",IDC_HOST_REMOVE,150,110,55,15
+    PUSHBUTTON      "Move Up",IDC_HOST_UP,150,130,55,15
+    PUSHBUTTON      "Move Down",IDC_HOST_DOWN,150,150,55,15
+    PUSHBUTTON      "&Edit",IDC_HOST_EDIT,150,170,55,15
+END
+
+IDD_HOOKING DIALOG DISCARDABLE  0, 0, 198, 95
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Hooks"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Use VNC hooks to track graphical updates",IDC_USEHOOKS,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,184,15
+    CONTROL         "Poll console windows for updates",IDC_POLLCONSOLES,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,184,15
+    CONTROL         "Filter out updates that have no effect (recommended)",
+                    IDC_COMPAREFB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,
+                    184,15
+END
+
+IDD_AUTH_VNC_PASSWD DIALOG DISCARDABLE  0, 0, 212, 70
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "WinVNC Password"
+FONT 8, "MS Sans Serif"
+BEGIN
+    EDITTEXT        IDC_PASSWORD1,75,10,130,15,ES_PASSWORD | ES_AUTOHSCROLL
+    EDITTEXT        IDC_PASSWORD2,75,30,130,14,ES_PASSWORD | ES_AUTOHSCROLL
+    LTEXT           "New Password:",IDC_STATIC,7,10,63,15
+    DEFPUSHBUTTON   "OK",IDOK,100,50,50,15
+    PUSHBUTTON      "Cancel",IDCANCEL,155,50,50,15
+    LTEXT           "Confirm Password:",IDC_STATIC,7,30,63,14
+END
+
+IDD_LEGACY DIALOG DISCARDABLE  0, 0, 166, 92
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Legacy"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "&Import VNC 3.3 Settings",IDC_LEGACY_IMPORT,7,10,92,20
+    CONTROL         "Only use protocol version 3.3",IDC_PROTOCOL_3_3,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,35,152,15
+END
+
+IDD_CONN_HOST DIALOG DISCARDABLE  0, 0, 225, 47
+STYLE DS_SYSMODAL | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "Specify Host IP Address Pattern"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,168,7,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,168,24,50,14
+    CONTROL         "&Allow",IDC_ALLOW,"Button",BS_AUTORADIOBUTTON | 
+                    WS_GROUP,7,7,53,16
+    CONTROL         "&Deny",IDC_DENY,"Button",BS_AUTORADIOBUTTON,7,23,53,15
+    EDITTEXT        IDC_HOST_PATTERN,65,7,100,16,ES_AUTOHSCROLL
+    LTEXT           "e.g. 192.168.0.0/255.255.0.0",IDC_STATIC,65,25,100,15
+END
+
+IDD_SHARING DIALOG DISCARDABLE  0, 0, 186, 95
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Sharing"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Always treat new connections as shared",
+                    IDC_SHARE_ALWAYS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,
+                    7,10,172,15
+    CONTROL         "Never treat new connections as shared",IDC_SHARE_NEVER,
+                    "Button",BS_AUTORADIOBUTTON,7,25,172,15
+    CONTROL         "Use client's preferred sharing setting",
+                    IDC_SHARE_CLIENT,"Button",BS_AUTORADIOBUTTON,7,40,172,15
+    CONTROL         "Non-shared connections replace existing ones",
+                    IDC_DISCONNECT_CLIENTS,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,7,55,172,15
+END
+
+IDD_INPUTS DIALOG DISCARDABLE  0, 0, 186, 119
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Inputs"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Accept keyboard events from clients",IDC_ACCEPT_KEYS,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
+    CONTROL         "Accept pointer events from clients",IDC_ACCEPT_PTR,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
+    CONTROL         "Accept clipboard updates from clients",
+                    IDC_ACCEPT_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    7,40,172,15
+    CONTROL         "Send clipboard updates to clients",IDC_SEND_CUTTEXT,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,172,15
+    CONTROL         "Allow input events to affect the screen-saver",
+                    IDC_AFFECT_SCREENSAVER,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,7,70,172,15
+    CONTROL         "Disable local inputs while server is in use",
+                    IDC_DISABLE_LOCAL_INPUTS,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,7,95,172,15
+END
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "About VNC Config for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
+    ICON            IDI_ICON,IDC_STATIC,7,7,20,20
+    LTEXT           ">appname<",IDC_DESCRIPTION,40,7,125,18
+    LTEXT           ">version<",IDC_VERSION,165,7,77,18
+    LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
+    LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,202,15
+    LTEXT           "See http://www.realvnc.com for more information on VNC.",
+                    IDC_STATIC,40,55,202,15
+END
+
+IDD_DESKTOP DIALOG DISCARDABLE  0, 0, 185, 137
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CONTROL | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Desktop"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Do nothing",IDC_DISCONNECT_NONE,"Button",
+                    BS_AUTORADIOBUTTON,15,80,155,15
+    CONTROL         "Lock workstation",IDC_DISCONNECT_LOCK,"Button",
+                    BS_AUTORADIOBUTTON,15,95,155,15
+    CONTROL         "Logoff user",IDC_DISCONNECT_LOGOFF,"Button",
+                    BS_AUTORADIOBUTTON,15,110,155,15
+    GROUPBOX        "When last client disconnects",IDC_STATIC,7,70,171,60
+    GROUPBOX        "While connected",IDC_STATIC,7,5,171,60
+    CONTROL         "Remove wallpaper",IDC_REMOVE_WALLPAPER,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,15,15,155,15
+    CONTROL         "Remove background pattern",IDC_REMOVE_PATTERN,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,15,30,155,15
+    CONTROL         "Disable user interface effects",IDC_DISABLE_EFFECTS,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,46,155,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_AUTHENTICATION, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 218
+        VERTGUIDE, 150
+        VERTGUIDE, 155
+        VERTGUIDE, 210
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 79
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+    END
+
+    IDD_CONNECTIONS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 211
+        VERTGUIDE, 15
+        VERTGUIDE, 145
+        VERTGUIDE, 150
+        VERTGUIDE, 205
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 191
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 90
+        HORZGUIDE, 105
+        HORZGUIDE, 110
+        HORZGUIDE, 125
+        HORZGUIDE, 130
+        HORZGUIDE, 145
+        HORZGUIDE, 150
+        HORZGUIDE, 165
+        HORZGUIDE, 170
+        HORZGUIDE, 185
+        HORZGUIDE, 190
+    END
+
+    IDD_HOOKING, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 191
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 88
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+    END
+
+    IDD_AUTH_VNC_PASSWD, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 205
+        VERTGUIDE, 70
+        VERTGUIDE, 75
+        VERTGUIDE, 100
+        VERTGUIDE, 150
+        VERTGUIDE, 155
+        VERTGUIDE, 205
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 65
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 44
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+    END
+
+    IDD_LEGACY, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 159
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 85
+        HORZGUIDE, 10
+        HORZGUIDE, 30
+        HORZGUIDE, 35
+        HORZGUIDE, 50
+    END
+
+    IDD_CONN_HOST, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 218
+        VERTGUIDE, 60
+        VERTGUIDE, 65
+        VERTGUIDE, 165
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 40
+    END
+
+    IDD_SHARING, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 88
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+    END
+
+    IDD_INPUTS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 112
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 95
+        HORZGUIDE, 110
+    END
+
+    IDD_ABOUT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 242
+        VERTGUIDE, 40
+        VERTGUIDE, 165
+        VERTGUIDE, 195
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 85
+        HORZGUIDE, 7
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+    END
+
+    IDD_DESKTOP, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 178
+        VERTGUIDE, 15
+        VERTGUIDE, 170
+        TOPMARGIN, 5
+        BOTTOMMARGIN, 130
+        HORZGUIDE, 15
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 60
+        HORZGUIDE, 65
+        HORZGUIDE, 70
+        HORZGUIDE, 80
+        HORZGUIDE, 95
+        HORZGUIDE, 110
+        HORZGUIDE, 125
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "RealVNC Ltd.\0"
+            VALUE "FileDescription", "VNC Server Configuration Applet for Win32\0"
+            VALUE "FileVersion", "4.0\0"
+            VALUE "InternalName", "VNC Config 4.0\0"
+            VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+            VALUE "LegalTrademarks", "RealVNC\0"
+            VALUE "OriginalFilename", "vncconfig.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "VNC Config 4.0\0"
+            VALUE "ProductVersion", "4.0\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST            24      DISCARDABLE     "vncconfig.exe.manifest"
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/vncconfig_unix/Makefile.in b/vncconfig_unix/Makefile.in
new file mode 100644
index 0000000..58277c3
--- /dev/null
+++ b/vncconfig_unix/Makefile.in
@@ -0,0 +1,23 @@
+
+SRCS = vncExt.c vncconfig.cxx
+
+OBJS = vncExt.o vncconfig.o
+
+program = vncconfig
+
+DEP_LIBS = ../tx/libtx.a ../rfb/librfb.a ../network/libnetwork.a \
+           ../rdr/librdr.a
+
+EXTRA_LIBS = @X_PRE_LIBS@ @X_LIBS@ -lX11 -lXext @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+	rm -f $(program)
+	$(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncconfig_unix/buildtime.c b/vncconfig_unix/buildtime.c
new file mode 100644
index 0000000..a96031c
--- /dev/null
+++ b/vncconfig_unix/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/vncconfig_unix/vncExt.c b/vncconfig_unix/vncExt.c
new file mode 100644
index 0000000..ac1dab3
--- /dev/null
+++ b/vncconfig_unix/vncExt.c
@@ -0,0 +1,316 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+
+#define NEED_REPLIES
+#include <X11/Xlibint.h>
+#define _VNCEXT_PROTO_
+#include "vncExt.h"
+
+static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                  xEvent* w);
+static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                    xEvent* w);
+
+static Bool extensionInited = False;
+static XExtCodes* codes = 0;
+
+static Bool checkExtension(Display* dpy)
+{
+  if (!extensionInited) {
+    extensionInited = True;
+    codes = XInitExtension(dpy, VNCEXTNAME);
+    if (!codes) return False;
+    XESetWireToEvent(dpy, codes->first_event + VncExtClientCutTextNotify,
+                     XVncExtClientCutTextNotifyWireToEvent);
+    XESetWireToEvent(dpy, codes->first_event + VncExtSelectionChangeNotify,
+                     XVncExtSelectionChangeNotifyWireToEvent);
+  }
+  return codes != 0;
+}
+
+Bool XVncExtQueryExtension(Display* dpy, int* event_basep, int* error_basep)
+{
+  if (!checkExtension(dpy)) return False;
+  *event_basep = codes->first_event;
+  *error_basep = codes->first_error;
+  return True;
+}
+
+Bool XVncExtSetParam(Display* dpy, const char* param)
+{
+  xVncExtSetParamReq* req;
+  xVncExtSetParamReply rep;
+
+  int paramLen = strlen(param);
+  if (paramLen > 255) return False;
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtSetParam, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtSetParam;
+  req->length += (paramLen + 3) >> 2;
+  req->paramLen = paramLen;
+  Data(dpy, param, paramLen);
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return rep.success;
+}
+
+Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len)
+{
+  xVncExtGetParamReq* req;
+  xVncExtGetParamReply rep;
+
+  int paramLen = strlen(param);
+  *value = 0;
+  *len = 0;
+  if (paramLen > 255) return False;
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtGetParam, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtGetParam;
+  req->length += (paramLen + 3) >> 2;
+  req->paramLen = paramLen;
+  Data(dpy, param, paramLen);
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  if (rep.success) {
+    *len = rep.valueLen;
+    *value = (char*) Xmalloc (*len+1);
+    _XReadPad(dpy, *value, *len);
+    (*value)[*len] = 0;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return rep.success;
+}
+
+char* XVncExtGetParamDesc(Display* dpy, const char* param)
+{
+  xVncExtGetParamDescReq* req;
+  xVncExtGetParamDescReply rep;
+  char* desc = 0;
+
+  int paramLen = strlen(param);
+  if (paramLen > 255) return False;
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtGetParamDesc, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtGetParamDesc;
+  req->length += (paramLen + 3) >> 2;
+  req->paramLen = paramLen;
+  Data(dpy, param, paramLen);
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  if (rep.success) {
+    desc = (char*)Xmalloc(rep.descLen+1);
+    _XReadPad(dpy, desc, rep.descLen);
+    desc[rep.descLen] = 0;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return desc;
+}
+
+char** XVncExtListParams(Display* dpy, int* nParams)
+{
+  xVncExtListParamsReq* req;
+  xVncExtListParamsReply rep;
+  char** list = 0;
+  char* ch;
+  int rlen, paramLen, i;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtListParams, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtListParams;
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  if (rep.nParams) {
+    list = (char**)Xmalloc(rep.nParams * sizeof(char*));
+    rlen = rep.length << 2;
+    ch = (char*)Xmalloc(rlen + 1);
+    if (!list || !ch) {
+      if (list) Xfree((char*)list);
+      if (ch) Xfree(ch);
+      _XEatData(dpy, rlen);
+      UnlockDisplay(dpy);
+      SyncHandle();
+      return 0;
+    }
+    _XReadPad(dpy, ch, rlen);
+    paramLen = *ch++;
+    for (i = 0; i < rep.nParams; i++) {
+      list[i] = ch;
+      ch += paramLen;
+      paramLen = *ch;
+      *ch++ = 0;
+    }
+  }
+  *nParams = rep.nParams;
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return list;
+}
+
+void XVncExtFreeParamList(char** list)
+{
+  if (list) {
+    Xfree(list[0]-1);
+    Xfree((char*)list);
+  }
+}
+
+Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len)
+{
+  xVncExtSetServerCutTextReq* req;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtSetServerCutText, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtSetServerCutText;
+  req->length += (len + 3) >> 2;
+  req->textLen = len;
+  Data(dpy, str, len);
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return True;
+}
+
+Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len)
+{
+  xVncExtGetClientCutTextReq* req;
+  xVncExtGetClientCutTextReply rep;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtGetClientCutText, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtGetClientCutText;
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  *len = rep.textLen;
+  *str = (char*) Xmalloc (*len+1);
+  _XReadPad(dpy, *str, *len);
+  (*str)[*len] = 0;
+  return True;
+}
+
+Bool XVncExtSelectInput(Display* dpy, Window w, int mask)
+{
+  xVncExtSelectInputReq* req;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtSelectInput, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtSelectInput;
+  req->window = w;
+  req->mask = mask;
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return True;
+}
+
+Bool XVncExtConnect(Display* dpy, char* hostAndPort)
+{
+  xVncExtConnectReq* req;
+  xVncExtConnectReply rep;
+
+  int strLen = strlen(hostAndPort);
+  if (strLen > 255) return False;
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtConnect, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtConnect;
+  req->length += (strLen + 3) >> 2;
+  req->strLen = strLen;
+  Data(dpy, hostAndPort, strLen);
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return rep.success;
+}
+
+static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                  xEvent* w)
+{
+  XVncExtClientCutTextEvent* ev = (XVncExtClientCutTextEvent*)e;
+  xVncExtClientCutTextNotifyEvent* wire = (xVncExtClientCutTextNotifyEvent*)w;
+  ev->type = wire->type & 0x7f;
+  ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+  ev->send_event = (wire->type & 0x80) != 0;
+  ev->display = dpy;
+  ev->window = wire->window;
+  ev->time = wire->time;
+  return True;
+}
+
+static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                    xEvent* w)
+{
+  XVncExtSelectionChangeEvent* ev = (XVncExtSelectionChangeEvent*)e;
+  xVncExtSelectionChangeNotifyEvent* wire
+    = (xVncExtSelectionChangeNotifyEvent*)w;
+  ev->type = wire->type & 0x7f;
+  ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+  ev->send_event = (wire->type & 0x80) != 0;
+  ev->display = dpy;
+  ev->window = wire->window;
+  ev->selection = wire->selection;
+  return True;
+}
diff --git a/vncconfig_unix/vncExt.h b/vncconfig_unix/vncExt.h
new file mode 100644
index 0000000..de69f4e
--- /dev/null
+++ b/vncconfig_unix/vncExt.h
@@ -0,0 +1,279 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef _VNCEXT_H_
+#define _VNCEXT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define X_VncExtSetParam 0
+#define X_VncExtGetParam 1
+#define X_VncExtGetParamDesc 2
+#define X_VncExtListParams 3
+#define X_VncExtSetServerCutText 4
+#define X_VncExtGetClientCutText 5
+#define X_VncExtSelectInput 6
+#define X_VncExtConnect 7
+
+#define VncExtClientCutTextNotify 0
+#define VncExtSelectionChangeNotify 1
+#define VncExtClientCutTextMask (1 << VncExtClientCutTextNotify)
+#define VncExtSelectionChangeMask (1 << VncExtSelectionChangeNotify)
+
+#define VncExtNumberEvents 2
+#define VncExtNumberErrors 0
+
+#ifndef _VNCEXT_SERVER_
+
+Bool XVncExtQueryExtension(Display* dpy, int* event_basep, int* error_basep);
+Bool XVncExtSetParam(Display* dpy, const char* param);
+Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len);
+char* XVncExtGetParamDesc(Display* dpy, const char* param);
+char** XVncExtListParams(Display* dpy, int* nParams);
+void XVncExtFreeParamList(char** list);
+Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len);
+Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len);
+Bool XVncExtSelectInput(Display* dpy, Window w, int mask);
+Bool XVncExtConnect(Display* dpy, char* hostAndPort);
+
+typedef struct {
+  int type;
+  unsigned long serial;
+  Bool send_event;
+  Display *display;
+  Window window;
+  Time time;
+} XVncExtClientCutTextEvent;
+
+typedef struct {
+  int type;
+  unsigned long serial;
+  Bool send_event;
+  Display *display;
+  Window window;
+  Atom selection;
+} XVncExtSelectionChangeEvent;
+
+#endif
+
+#ifdef _VNCEXT_PROTO_
+
+#define VNCEXTNAME "VNC-EXTENSION"
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtSetParam */
+  CARD16 length B16;
+  CARD8 paramLen;
+  CARD8 pad0;
+  CARD16 pad1 B16;
+} xVncExtSetParamReq;
+#define sz_xVncExtSetParamReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 pad0 B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtSetParamReply;
+#define sz_xVncExtSetParamReply 32
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtGetParam */
+  CARD16 length B16;
+  CARD8 paramLen;
+  CARD8 pad0;
+  CARD16 pad1 B16;
+} xVncExtGetParamReq;
+#define sz_xVncExtGetParamReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 valueLen B16;
+ CARD16 pad0 B16;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetParamReply;
+#define sz_xVncExtGetParamReply 32
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtGetParamDesc */
+  CARD16 length B16;
+  CARD8 paramLen;
+  CARD8 pad0;
+  CARD16 pad1 B16;
+} xVncExtGetParamDescReq;
+#define sz_xVncExtGetParamDescReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 descLen B16;
+ CARD16 pad0 B16;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetParamDescReply;
+#define sz_xVncExtGetParamDescReply 32
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtListParams */
+  CARD16 length B16;
+} xVncExtListParamsReq;
+#define sz_xVncExtListParamsReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 nParams B16;
+ CARD16 pad1 B16;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+} xVncExtListParamsReply;
+#define sz_xVncExtListParamsReply 32
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtSetServerCutText */
+  CARD16 length B16;
+  CARD32 textLen B32;
+} xVncExtSetServerCutTextReq;
+#define sz_xVncExtSetServerCutTextReq 8
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtGetClientCutText */
+  CARD16 length B16;
+} xVncExtGetClientCutTextReq;
+#define sz_xVncExtGetClientCutTextReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 textLen B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetClientCutTextReply;
+#define sz_xVncExtGetClientCutTextReply 32
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtSelectInput */
+  CARD16 length B16;
+  CARD32 window B32;
+  CARD32 mask B32;
+} xVncExtSelectInputReq;
+#define sz_xVncExtSelectInputReq 12
+
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtConnect */
+  CARD16 length B16;
+  CARD8 strLen;
+  CARD8 pad0;
+  CARD16 pad1 B16;
+} xVncExtConnectReq;
+#define sz_xVncExtConnectReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 pad0 B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtConnectReply;
+#define sz_xVncExtConnectReply 32
+
+
+typedef struct {
+  BYTE type;    /* always eventBase + VncExtClientCutTextNotify */
+  BYTE pad0;
+  CARD16 sequenceNumber B16;
+  CARD32 window B32;
+  CARD32 time B32;
+  CARD32 pad1 B32;
+  CARD32 pad2 B32;
+  CARD32 pad3 B32;
+  CARD32 pad4 B32;
+  CARD32 pad5 B32;
+} xVncExtClientCutTextNotifyEvent;
+#define sz_xVncExtClientCutTextNotifyEvent 32
+
+typedef struct {
+  BYTE type;    /* always eventBase + VncExtSelectionChangeNotify */
+  BYTE pad0;
+  CARD16 sequenceNumber B16;
+  CARD32 window B32;
+  CARD32 selection B32;
+  CARD32 pad1 B32;
+  CARD32 pad2 B32;
+  CARD32 pad3 B32;
+  CARD32 pad4 B32;
+  CARD32 pad5 B32;
+} xVncExtSelectionChangeNotifyEvent;
+#define sz_xVncExtSelectionChangeNotifyEvent 32
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vncconfig_unix/vncconfig.cxx b/vncconfig_unix/vncconfig.cxx
new file mode 100644
index 0000000..e707ffb
--- /dev/null
+++ b/vncconfig_unix/vncconfig.cxx
@@ -0,0 +1,313 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// VNC server configuration utility
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include "vncExt.h"
+#include <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXCheckbox.h"
+
+using namespace rfb;
+
+LogWriter vlog("vncconfig");
+
+StringParameter displayname("display", "The X display", "");
+BoolParameter noWindow("nowin", "Don't display a window", 0);
+BoolParameter iconic("iconic", "Start with window iconified", 0);
+
+#define ACCEPT_CUT_TEXT "AcceptCutText"
+#define SEND_CUT_TEXT "SendCutText"
+
+char* programName = 0;
+Display* dpy;
+int vncExtEventBase, vncExtErrorBase;
+
+static bool getBoolParam(Display* dpy, const char* param) {
+  char* data;
+  int len;
+  if (XVncExtGetParam(dpy, param, &data, &len)) {
+    if (strcmp(data,"1") == 0) return true;
+  }
+  return false;
+}
+
+class VncConfigWindow : public TXWindow, public TXEventHandler,
+                        public TXDeleteWindowCallback,
+                        public TXCheckboxCallback {
+public:
+  VncConfigWindow(Display* dpy)
+    : TXWindow(dpy, 300, 100), clientCutText(0), clientCutTextLen(0),
+      acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
+      sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
+      sendPrimary(dpy, "Send primary selection to viewers", this, false, this)
+  {
+    int y = yPad;
+    acceptClipboard.move(xPad, y);
+    acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
+    y += acceptClipboard.height();
+    sendClipboard.move(xPad, y);
+    sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
+    y += sendClipboard.height();
+    sendPrimary.move(xPad, y);
+    sendPrimary.checked(true);
+    sendPrimary.disabled(!sendClipboard.checked());
+    y += sendPrimary.height();
+    setEventHandler(this);
+    toplevel("VNC config", this, 0, 0, 0, iconic);
+    XVncExtSelectInput(dpy, win(),
+                       VncExtClientCutTextMask|VncExtSelectionChangeMask);
+  }
+
+  // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
+  // primary and clipboard selections to the clientCutText. If we get a
+  // SelectionChangeNotify event from Xvnc, set the serverCutText to the value
+  // of the new selection.
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    if (acceptClipboard.checked()) {
+      if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
+        XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
+        if (clientCutText)
+          XFree(clientCutText);
+        clientCutText = 0;
+        if (XVncExtGetClientCutText(dpy, &clientCutText, &clientCutTextLen)) {
+          vlog.debug("Got client cut text");
+          XStoreBytes(dpy, clientCutText, clientCutTextLen);
+          ownSelection(XA_PRIMARY, cutEv->time);
+          ownSelection(xaCLIPBOARD, cutEv->time);
+        }
+      }
+    }
+    if (sendClipboard.checked()) {
+      if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
+        XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
+        if (selEv->selection == xaCLIPBOARD ||
+            (selEv->selection == XA_PRIMARY && sendPrimary.checked())) {
+          if (!selectionOwner(selEv->selection))
+            XConvertSelection(dpy, selEv->selection, XA_STRING,
+                              selEv->selection, win(), CurrentTime);
+        }
+      }
+    }
+  }
+  
+
+  // selectionRequest() is called when we are the selection owner and another X
+  // client has requested the selection.  We simply put the server's cut text
+  // into the requested property.  TXWindow will handle the rest.
+  bool selectionRequest(Window requestor, Atom selection, Atom property)
+  {
+    if (clientCutText)
+      XChangeProperty(dpy, requestor, property, XA_STRING, 8,
+                      PropModeReplace, (unsigned char*)clientCutText,
+                      clientCutTextLen);
+    return clientCutText;
+  }
+
+  // selectionNotify() is called when we have requested the selection from the
+  // selection owner.
+  void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+                       int nitems, void* data)
+  {
+    if (ev->requestor != win() || ev->target != XA_STRING)
+      return;
+
+    if (data && format == 8) {
+      vlog.debug("setting selection as server cut text");
+      XVncExtSetServerCutText(dpy, (char*)data, nitems);
+    }
+  }
+
+  // TXDeleteWindowCallback method
+  virtual void deleteWindow(TXWindow* w) {
+    exit(1);
+  }
+
+  // TXCheckboxCallback method
+  virtual void checkboxSelect(TXCheckbox* checkbox) {
+    if (checkbox == &acceptClipboard) {
+      XVncExtSetParam(dpy, (acceptClipboard.checked()
+                            ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
+    } else if (checkbox == &sendClipboard) {
+      XVncExtSetParam(dpy, (sendClipboard.checked()
+                            ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
+      sendPrimary.disabled(!sendClipboard.checked());
+    }
+  }
+
+private:
+  char* clientCutText;
+  int clientCutTextLen;
+  TXCheckbox acceptClipboard, sendClipboard, sendPrimary;
+};
+
+static void usage()
+{
+  fprintf(stderr,"usage: %s [-display <display>] [-nowin] [-iconic]\n",
+          programName);
+  fprintf(stderr,"       %s [-display <display>] -connect <host>[:<port>]\n",
+          programName);
+  fprintf(stderr,"       %s [-display <display>] -disconnect\n", programName);
+  fprintf(stderr,"       %s [-display <display>] [-set] <param>=<value> ...\n",
+          programName);
+  fprintf(stderr,"       %s [-display <display>] -list\n", programName);
+  fprintf(stderr,"       %s [-display <display>] -get <param>\n", programName);
+  fprintf(stderr,"       %s [-display <display>] -desc <param>\n",programName);
+  exit(1);
+}
+
+void removeArgs(int* argc, char** argv, int first, int n)
+{
+  if (first + n > *argc) return;
+  for (int i = first + n; i < *argc; i++)
+    argv[i-n] = argv[i];
+  *argc -= n;
+}
+
+int main(int argc, char** argv)
+{
+  programName = argv[0];
+  rfb::initStdIOLoggers();
+  rfb::LogWriter::setLogParams("*:stderr:30");
+
+  // Process vncconfig's own parameters first, then we process the
+  // other arguments when we have the X display.
+  int i;
+  for (i = 1; i < argc; i++) {
+    if (Configuration::setParam(argv[i]))
+      continue;
+
+    if (argv[i][0] == '-' && i+1 < argc &&
+        Configuration::setParam(&argv[i][1], argv[i+1])) {
+      i++;
+      continue;
+    }
+    break;
+  }
+
+  CharArray displaynameStr(displayname.getData());
+  if (!(dpy = XOpenDisplay(displaynameStr.buf))) {
+    fprintf(stderr,"%s: unable to open display \"%s\"\n",
+            programName, XDisplayName(displaynameStr.buf));
+    exit(1);
+  }
+
+  if (!XVncExtQueryExtension(dpy, &vncExtEventBase, &vncExtErrorBase)) {
+    fprintf(stderr,"No VNC extension on display %s\n",
+            XDisplayName(displaynameStr.buf));
+    exit(1);
+  }
+
+  if (i < argc) {
+    for (; i < argc; i++) {
+      if (strcmp(argv[i], "-connect") == 0) {
+        i++;
+        if (i >= argc) usage();
+        if (!XVncExtConnect(dpy, argv[i])) {
+          fprintf(stderr,"connecting to %s failed\n",argv[i]);
+        }
+      } else if (strcmp(argv[i], "-disconnect") == 0) {
+        if (!XVncExtConnect(dpy, "")) {
+          fprintf(stderr,"disconnecting all clients failed\n");
+        }
+      } else if (strcmp(argv[i], "-get") == 0) {
+        i++;
+        if (i >= argc) usage();
+        char* data;
+        int len;
+        if (XVncExtGetParam(dpy, argv[i], &data, &len)) {
+          printf("%.*s\n",len,data);
+        } else {
+          fprintf(stderr,"getting param %s failed\n",argv[i]);
+        }
+        XFree(data);
+      } else if (strcmp(argv[i], "-desc") == 0) {
+        i++;
+        if (i >= argc) usage();
+        char* desc = XVncExtGetParamDesc(dpy, argv[i]);
+        if (desc) {
+          printf("%s\n",desc);
+        } else {
+          fprintf(stderr,"getting description for param %s failed\n",argv[i]);
+        }
+        XFree(desc);
+      } else if (strcmp(argv[i], "-list") == 0) {
+        int nParams;
+        char** list = XVncExtListParams(dpy, &nParams);
+        for (int i = 0; i < nParams; i++) {
+          printf("%s\n",list[i]);
+        }
+        XVncExtFreeParamList(list);
+      } else if (strcmp(argv[i], "-set") == 0) {
+        i++;
+        if (i >= argc) usage();
+        if (!XVncExtSetParam(dpy, argv[i])) {
+          fprintf(stderr,"setting param %s failed\n",argv[i]);
+        }
+      } else if (XVncExtSetParam(dpy, argv[i])) {
+        fprintf(stderr,"set parameter %s\n",argv[i]);
+      } else {
+        usage();
+      }
+    }
+
+    return 0;
+  }
+
+  try {
+    TXWindow::init(dpy,"Vncconfig");
+
+    VncConfigWindow w(dpy);
+    if (!noWindow) w.map();
+
+    while (true) {
+      TXWindow::handleXEvents(dpy);
+      fd_set rfds;
+      FD_ZERO(&rfds);
+      FD_SET(ConnectionNumber(dpy), &rfds);
+      int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+      if (n < 0) throw rdr::SystemException("select",errno);
+    }
+
+    XCloseDisplay(dpy);
+
+  } catch (rdr::Exception &e) {
+    vlog.error(e.str());
+  }
+
+  return 0;
+}
diff --git a/vncconfig_unix/vncconfig.man b/vncconfig_unix/vncconfig.man
new file mode 100644
index 0000000..9a7d8ce
--- /dev/null
+++ b/vncconfig_unix/vncconfig.man
@@ -0,0 +1,115 @@
+.TH vncconfig 1 "3 June 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncconfig \- configure and control a VNC server
+.SH SYNOPSIS
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP] [\fB\-nowin\fP] [\fB\-iconic\fP]
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-connect
+.IR host [: port ]
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-disconnect
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.IR param = value " ..."
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-list
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+\fB\-get\fP \fIparam\fP
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+\fB\-desc\fP \fIparam\fP
+.SH DESCRIPTION
+.B vncconfig
+is used to configure and control a running instance of Xvnc, or any other X
+server with the VNC extension.  Note that it cannot be used to control VNC
+servers prior to version 4.
+
+When run with no options, it runs as a kind of "helper" application for Xvnc.
+Its main purpose when run in this mode is to support clipboard transfer to and
+from the VNC viewer(s).  Note that without a running instance of
+\fBvncconfig\fP there will be no clipboard support.  It puts up a window with
+some checkboxes which can be used to disable clipboard transfers if required
+(in the future there may be more functions available from this window).  The
+\fB-nowin\fP flag can be used if you always want clipboard support but don't
+wish to clutter the desktop with this window - alternatively the \fB-iconic\fP
+option can be used to make it iconified by default.
+
+When run in any other mode, \fBvncconfig\fP is a one-shot program used to
+configure or control Xvnc as appropriate.  It can be used to tell Xvnc to
+connect or disconnect from listening viewers, and to set and retrieve Xvnc's
+parameters.
+
+Note that the DISPLAY environment variable or the \fB\-display\fP option
+must be set as appropriate to control Xvnc.  If you run it on an ordinary X
+server (or on a version 3 Xvnc) you will get an error message saying that there
+is no VNC extension.
+
+.SH OPTIONS
+.TP
+.B \-display \fIXdisplay\fP
+Specifies the Xvnc server to control.
+
+.TP
+.B \-nowin
+When run as a "helper" app, don't put up a window.
+
+.TP
+.B \-iconic
+When run as a "helper" app, make the window iconified at startup.
+
+.TP
+.B \-connect \fIhost\fP[:\fIport\fP]
+Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer
+(normally connections are made the other way round - the viewer connects to the
+server). \fIhost\fP is the host where the listening viewer is running. If it's
+not listening on the default port of 5500, you can specify \fIhost:port\fP
+instead.
+
+.TP
+.B \-disconnect
+This causes Xvnc to disconnect from all viewers so that the VNC desktop is not
+displayed anywhere.
+
+.TP
+.IR param = value
+Sets an Xvnc parameter to the given value.  Note that some of Xvnc's parameters
+are read only once at startup so that changing them in this way may not have
+any effect.
+
+.TP
+.B \-list
+Lists all the parameters supported by Xvnc.
+
+.TP
+.B \-get \fIparam\fP
+Prints the current value of the given Xvnc parameter.
+
+.TP
+.B \-desc \fIparam\fP
+Prints a short description of the given Xvnc parameter.
+
+.SH SEE ALSO
+.BR vncpasswd (1),
+.BR vncviewer (1),
+.BR vncserver (1),
+.BR Xvnc (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/vncinstall b/vncinstall
new file mode 100755
index 0000000..e5ab017
--- /dev/null
+++ b/vncinstall
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+#  Copyright (C) 2002-2003 RealVNC Ltd.
+#
+#  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; either 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.
+#
+
+#
+# vncinstall - copy the VNC programs to an installation directory.
+# Also tries to install the manual pages somewhere sensible.
+#
+
+if [ $# -lt 1 -o $# -gt 3 -o ! -d "$1" ]; then 
+  echo "usage: $0 <installation-directory> [<man-page-directory>] [<module-directory>]"
+  echo "e.g.   $0 /usr/local/bin"
+  exit 1
+fi
+
+dst=$1
+shift
+if [ $# -gt 0 ]; then
+  mandst="$1/man1"
+  shift
+  if [ $# -gt 0 ]; then
+    moduledst=$1
+    shift
+  else
+    moduledst=/usr/X11R6/lib/modules/extensions
+  fi
+else
+  if [ "`basename $dst`" = bin ]; then
+    mandst="`dirname $dst`/man/man1"
+    if [ ! -d "$mandst" -a "$dst" = /usr/bin ]; then
+      mandst=/usr/share/man/man1
+    fi
+  fi
+fi
+
+if [ "$mandst" != "" ]; then
+  if [ ! -d "$mandst" -o ! -w "$mandst" ]; then
+    echo "Can't install manual pages to $mandst"
+    mandst=""
+  fi
+fi
+
+for f in xc/programs/Xserver/Xvnc vncviewer/vncviewer vncpasswd/vncpasswd \
+	 vncconfig/vncconfig vncserver x0vncserver/x0vncserver; do
+
+  if [ ! -f $f ]; then
+    echo "Couldn't find $f"
+  else
+    if cmp -s $f $dst/`basename $f`; then
+      echo "`basename $f` hasn't changed"
+    else
+      echo "Copying $f to $dst"
+      cp -pf $f $dst
+      chmod 0555 $dst/`basename $f`
+    fi
+
+
+    if [ -f $f.man ]; then
+      if [ "$mandst" != "" -a -d "$mandst" ]; then
+        if cmp -s $f.man $mandst/`basename $f.1`; then
+          echo "`basename $f.man` hasn't changed"
+        else
+          echo "Copying $f.man to $mandst/`basename $f.1`"
+          cp -pf $f.man $mandst/`basename $f.1`
+          chmod 0444 $mandst/`basename $f.1`
+        fi
+      fi
+    fi
+  fi
+
+done
+
+vncModule=xc/programs/Xserver/vnc/module/vnc.so
+if [ -f "$vncModule" -a -d "$moduledst" ]; then
+  if cmp -s $vncModule $moduledst/`basename $vncModule`; then
+    echo "`basename $vncModule` hasn't changed"
+  else
+    echo "Copying $vncModule to $moduledst"
+    cp -pf $vncModule $moduledst
+    chmod 0555 $moduledst/`basename $vncModule`
+  fi
+fi
diff --git a/vncmkdepend/Makefile b/vncmkdepend/Makefile
new file mode 100644
index 0000000..3278368
--- /dev/null
+++ b/vncmkdepend/Makefile
@@ -0,0 +1,4 @@
+SRCS = include.c main.c parse.c pr.c cppsetup.c ifparser.c
+OBJS = $(SRCS:.c=.o)
+vncmkdepend: $(OBJS)
+	$(CC) $^ -o $@
diff --git a/vncmkdepend/README b/vncmkdepend/README
new file mode 100644
index 0000000..338b62c
--- /dev/null
+++ b/vncmkdepend/README
@@ -0,0 +1,39 @@
+vncmkdepend
+===========
+
+This code is taken from the X11R6 makedepend sources.  All I have done is:
+
+ * Remove all predefined symbols, include directories and other
+   platform-specific behaviour.  Its behaviour should be the same on all
+   platforms.  All symbols and include directories must be explicitly given on
+   the command line with -D and -I flags.
+ * Fix processing of numbers in #if expressions.
+ * Fix processing of -D flag.
+ * Fix C++ comment handling.
+ * Improve #error warnings.
+ * Change the output so that processing <name>.<suffix> produces <name>.d
+
+The copyright on the code is:
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/vncmkdepend/cppsetup.c b/vncmkdepend/cppsetup.c
new file mode 100644
index 0000000..bf1c4fc
--- /dev/null
+++ b/vncmkdepend/cppsetup.c
@@ -0,0 +1,242 @@
+/* $XConsortium: cppsetup.c,v 1.13 94/04/17 20:10:32 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+#ifdef	CPP
+/*
+ * This file is strictly for the sake of cpy.y and yylex.c (if
+ * you indeed have the source for cpp).
+ */
+#define IB 1
+#define SB 2
+#define NB 4
+#define CB 8
+#define QB 16
+#define WB 32
+#define SALT '#'
+#if pdp11 | vax | ns16000 | mc68000 | ibm032
+#define COFF 128
+#else
+#define COFF 0
+#endif
+/*
+ * These variables used by cpy.y and yylex.c
+ */
+extern char	*outp, *inp, *newp, *pend;
+extern char	*ptrtab;
+extern char	fastab[];
+extern char	slotab[];
+
+/*
+ * cppsetup
+ */
+struct filepointer	*currentfile;
+struct inclist		*currentinc;
+
+cppsetup(line, filep, inc)
+	register char	*line;
+	register struct filepointer	*filep;
+	register struct inclist		*inc;
+{
+	register char *p, savec;
+	static boolean setupdone = FALSE;
+	boolean	value;
+
+	if (!setupdone) {
+		cpp_varsetup();
+		setupdone = TRUE;
+	}
+
+	currentfile = filep;
+	currentinc = inc;
+	inp = newp = line;
+	for (p=newp; *p; p++)
+		;
+
+	/*
+	 * put a newline back on the end, and set up pend, etc.
+	 */
+	*p++ = '\n';
+	savec = *p;
+	*p = '\0';
+	pend = p;
+
+	ptrtab = slotab+COFF;
+	*--inp = SALT; 
+	outp=inp; 
+	value = yyparse();
+	*p = savec;
+	return(value);
+}
+
+struct symtab *lookup(symbol)
+	char	*symbol;
+{
+	static struct symtab    undefined;
+	struct symtab   *sp;
+
+	sp = isdefined(symbol, currentinc, NULL);
+	if (sp == NULL) {
+		sp = &undefined;
+		sp->s_value = NULL;
+	}
+	return (sp);
+}
+
+pperror(tag, x0,x1,x2,x3,x4)
+	int	tag,x0,x1,x2,x3,x4;
+{
+	warning("\"%s\", line %d: ", currentinc->i_file, currentfile->f_line);
+	warning(x0,x1,x2,x3,x4);
+}
+
+
+yyerror(s)
+	register char	*s;
+{
+	fatalerr("Fatal error: %s\n", s);
+}
+#else /* not CPP */
+
+#include "ifparser.h"
+struct _parse_data {
+    struct filepointer *filep;
+    struct inclist *inc;
+    const char *line;
+};
+
+static const char *
+_my_if_errors (ip, cp, expecting)
+    IfParser *ip;
+    const char *cp;
+    const char *expecting;
+{
+    struct _parse_data *pd = (struct _parse_data *) ip->data;
+    int lineno = pd->filep->f_line;
+    char *filename = pd->inc->i_file;
+    char prefix[300];
+    int prefixlen;
+    int i;
+
+    sprintf (prefix, "\"%s\":%d", filename, lineno);
+    prefixlen = strlen(prefix);
+    fprintf (stderr, "%s:  %s", prefix, pd->line);
+    i = cp - pd->line;
+    if (i > 0 && pd->line[i-1] != '\n') {
+	putc ('\n', stderr);
+    }
+    for (i += prefixlen + 3; i > 0; i--) {
+	putc (' ', stderr);
+    }
+    fprintf (stderr, "^--- expecting %s\n", expecting);
+    return NULL;
+}
+
+
+#define MAXNAMELEN 256
+
+static struct symtab *
+_lookup_variable (ip, var, len)
+    IfParser *ip;
+    const char *var;
+    int len;
+{
+    char tmpbuf[MAXNAMELEN + 1];
+    struct _parse_data *pd = (struct _parse_data *) ip->data;
+
+    if (len > MAXNAMELEN)
+	return 0;
+
+    strncpy (tmpbuf, var, len);
+    tmpbuf[len] = '\0';
+    return isdefined (tmpbuf, pd->inc, NULL);
+}
+
+
+static int
+_my_eval_defined (ip, var, len)
+    IfParser *ip;
+    const char *var;
+    int len;
+{
+    if (_lookup_variable (ip, var, len))
+	return 1;
+    else
+	return 0;
+}
+
+#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
+
+static int
+_my_eval_variable (ip, var, len)
+    IfParser *ip;
+    const char *var;
+    int len;
+{
+    struct symtab *s;
+
+    s = _lookup_variable (ip, var, len);
+    if (!s)
+	return 0;
+    do {
+	var = s->s_value;
+	if (!isvarfirstletter(*var))
+	    break;
+	s = _lookup_variable (ip, var, strlen(var));
+    } while (s);
+
+    return atoi(var);
+}
+
+
+cppsetup(line, filep, inc)
+	register char	*line;
+	register struct filepointer	*filep;
+	register struct inclist		*inc;
+{
+    IfParser ip;
+    struct _parse_data pd;
+    int val = 0;
+
+    pd.filep = filep;
+    pd.inc = inc;
+    pd.line = line;
+    ip.funcs.handle_error = _my_if_errors;
+    ip.funcs.eval_defined = _my_eval_defined;
+    ip.funcs.eval_variable = _my_eval_variable;
+    ip.data = (char *) &pd;
+
+    (void) ParseIfExpression (&ip, line, &val);
+    if (val)
+	return IF;
+    else
+	return IFFALSE;
+}
+#endif /* CPP */
+
diff --git a/vncmkdepend/def.h b/vncmkdepend/def.h
new file mode 100644
index 0000000..8fb4c23
--- /dev/null
+++ b/vncmkdepend/def.h
@@ -0,0 +1,140 @@
+/* $XConsortium: def.h,v 1.25 94/04/17 20:10:33 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef X_NOT_POSIX
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define MAXDEFINES	512
+#define MAXFILES	512
+#define MAXDIRS		64
+#define SYMTABINC	10	/* must be > 1 for define() to work right */
+#define	TRUE		1
+#define	FALSE		0
+
+/* the following must match the directives table in main.c */
+#define	IF		0
+#define	IFDEF		1
+#define	IFNDEF		2
+#define	ELSE		3
+#define	ENDIF		4
+#define	DEFINE		5
+#define	UNDEF		6
+#define	INCLUDE		7
+#define	LINE		8
+#define	PRAGMA		9
+#define ERROR           10
+#define IDENT           11
+#define SCCS            12
+#define ELIF            13
+#define EJECT           14
+#define IFFALSE         15     /* pseudo value --- never matched */
+#define ELIFFALSE       16     /* pseudo value --- never matched */
+#define INCLUDEDOT      17     /* pseudo value --- never matched */
+#define IFGUESSFALSE    18     /* pseudo value --- never matched */
+#define ELIFGUESSFALSE  19     /* pseudo value --- never matched */
+
+#ifdef DEBUG
+extern int	_debugmask;
+/*
+ * debug levels are:
+ * 
+ *     0	show ifn*(def)*,endif
+ *     1	trace defined/!defined
+ *     2	show #include
+ *     3	show #include SYMBOL
+ *     4-6	unused
+ */
+#define debug(level,arg) { if (_debugmask & (1 << level)) warning arg; }
+#else
+#define	debug(level,arg) /**/
+#endif /* DEBUG */
+
+typedef	unsigned char boolean;
+
+struct symtab {
+	char	*s_name;
+	char	*s_value;
+};
+
+struct	inclist {
+	char		*i_incstring;	/* string from #include line */
+	char		*i_file;	/* path name of the include file */
+	struct inclist	**i_list;	/* list of files it itself includes */
+	int		i_listlen;	/* length of i_list */
+	struct symtab	*i_defs;	/* symbol table for this file */
+	int		i_ndefs;	/* current # defines */
+	int		i_deflen;	/* amount of space in table */
+	boolean		i_defchecked;	/* whether defines have been checked */
+	boolean		i_notified;	/* whether we have revealed includes */
+	boolean		i_marked;	/* whether it's in the makefile */
+	boolean		i_searched;	/* whether we have read this */
+	boolean         i_included_sym; /* whether #include SYMBOL was found */
+					/* Can't use i_list if TRUE */
+};
+
+struct filepointer {
+	char	*f_p;
+	char	*f_base;
+	char	*f_end;
+	long	f_len;
+	long	f_line;
+};
+
+#ifndef X_NOT_STDC_ENV
+#include <stdlib.h>
+#if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
+char *malloc(), *realloc();
+#endif /* macII */
+#else
+char			*malloc();
+char			*realloc();
+#endif
+
+char			*copy();
+char			*base_name();
+char			*getline();
+struct symtab		*slookup();
+struct symtab		*isdefined();
+struct symtab		*fdefined();
+struct filepointer	*getfile();
+struct inclist		*newinclude();
+struct inclist		*inc_path();
+
+#if NeedVarargsPrototypes
+extern fatalerr(char *, ...);
+extern warning(char *, ...);
+extern warning1(char *, ...);
+#endif
diff --git a/vncmkdepend/ifparser.c b/vncmkdepend/ifparser.c
new file mode 100644
index 0000000..60740bc
--- /dev/null
+++ b/vncmkdepend/ifparser.c
@@ -0,0 +1,445 @@
+/*
+ * $XConsortium: ifparser.c,v 1.7 94/01/18 21:30:50 rws Exp $
+ *
+ * Copyright 1992 Network Computing Devices, Inc.
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Network Computing Devices may not be
+ * used in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Network Computing Devices makes
+ * no representations about the suitability of this software for any purpose.
+ * It is provided ``as is'' without express or implied warranty.
+ * 
+ * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * 
+ * Author:  Jim Fulton
+ *          Network Computing Devices, Inc.
+ * 
+ * Simple if statement processor
+ *
+ * This module can be used to evaluate string representations of C language
+ * if constructs.  It accepts the following grammar:
+ * 
+ *     EXPRESSION	:=	VALUE
+ * 			 |	VALUE  BINOP	EXPRESSION
+ * 
+ *     VALUE		:=	'('  EXPRESSION  ')'
+ * 			 |	'!'  VALUE
+ * 			 |	'-'  VALUE
+ * 			 |	'defined'  '('  variable  ')'
+ * 			 |	'defined'  variable
+ *			 |	# variable '(' variable-list ')'
+ * 			 |	variable
+ * 			 |	number
+ * 
+ *     BINOP		:=	'*'	|  '/'	|  '%'
+ * 			 |	'+'	|  '-'
+ * 			 |	'<<'	|  '>>'
+ * 			 |	'<'	|  '>'	|  '<='  |  '>='
+ * 			 |	'=='	|  '!='
+ * 			 |	'&'	|  '|'
+ * 			 |	'&&'	|  '||'
+ * 
+ * The normal C order of precidence is supported.
+ * 
+ * 
+ * External Entry Points:
+ * 
+ *     ParseIfExpression		parse a string for #if
+ */
+
+#include "ifparser.h"
+#include <ctype.h>
+
+/****************************************************************************
+		   Internal Macros and Utilities for Parser
+ ****************************************************************************/
+
+#define DO(val) if (!(val)) return NULL
+#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
+#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
+#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
+
+
+static const char *
+parse_variable (g, cp, varp)
+    IfParser *g;
+    const char *cp;
+    const char **varp;
+{
+    SKIPSPACE (cp);
+
+    if (!isvarfirstletter (*cp))
+	return CALLFUNC(g, handle_error) (g, cp, "variable name");
+
+    *varp = cp;
+    /* EMPTY */
+    for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
+    return cp;
+}
+
+
+static const char *
+parse_number (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    SKIPSPACE (cp);
+
+    if (!isdigit(*cp))
+	return CALLFUNC(g, handle_error) (g, cp, "number");
+
+    *valp = strtol(cp, &cp, 0);
+    return cp;
+}
+
+
+static const char *
+parse_value (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    const char *var;
+
+    *valp = 0;
+
+    SKIPSPACE (cp);
+    if (!*cp)
+	return cp;
+
+    switch (*cp) {
+      case '(':
+	DO (cp = ParseIfExpression (g, cp + 1, valp));
+	SKIPSPACE (cp);
+	if (*cp != ')') 
+	    return CALLFUNC(g, handle_error) (g, cp, ")");
+
+	return cp + 1;			/* skip the right paren */
+
+      case '!':
+	DO (cp = parse_value (g, cp + 1, valp));
+	*valp = !(*valp);
+	return cp;
+
+      case '-':
+	DO (cp = parse_value (g, cp + 1, valp));
+	*valp = -(*valp);
+	return cp;
+
+      case '#':
+	DO (cp = parse_variable (g, cp + 1, &var));
+	SKIPSPACE (cp);
+	if (*cp != '(')
+	    return CALLFUNC(g, handle_error) (g, cp, "(");
+	do {
+	    DO (cp = parse_variable (g, cp + 1, &var));
+	    SKIPSPACE (cp);
+	} while (*cp && *cp != ')');
+	if (*cp != ')')
+	    return CALLFUNC(g, handle_error) (g, cp, ")");
+	*valp = 1; /* XXX */
+	return cp + 1;
+
+      case 'd':
+	if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
+	    int paren = 0;
+	    cp += 7;
+	    SKIPSPACE (cp);
+	    if (*cp == '(') {
+		paren = 1;
+		cp++;
+	    }
+	    DO (cp = parse_variable (g, cp, &var));
+	    SKIPSPACE (cp);
+	    if (paren && *cp != ')')
+		return CALLFUNC(g, handle_error) (g, cp, ")");
+	    *valp = (*(g->funcs.eval_defined)) (g, var, cp - var);
+	    return cp + paren;		/* skip the right paren */
+	}
+	/* fall out */
+    }
+
+    if (isdigit(*cp)) {
+	DO (cp = parse_number (g, cp, valp));
+    } else if (!isvarfirstletter(*cp))
+	return CALLFUNC(g, handle_error) (g, cp, "variable or number");
+    else {
+	DO (cp = parse_variable (g, cp, &var));
+	*valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
+    }
+    
+    return cp;
+}
+
+
+
+static const char *
+parse_product (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_value (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '*':
+	DO (cp = parse_product (g, cp + 1, &rightval));
+	*valp = (*valp * rightval);
+	break;
+
+      case '/':
+	DO (cp = parse_product (g, cp + 1, &rightval));
+	*valp = (*valp / rightval);
+	break;
+
+      case '%':
+	DO (cp = parse_product (g, cp + 1, &rightval));
+	*valp = (*valp % rightval);
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_sum (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_product (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '+':
+	DO (cp = parse_sum (g, cp + 1, &rightval));
+	*valp = (*valp + rightval);
+	break;
+
+      case '-':
+	DO (cp = parse_sum (g, cp + 1, &rightval));
+	*valp = (*valp - rightval);
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_shift (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_sum (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '<':
+	if (cp[1] == '<') {
+	    DO (cp = parse_shift (g, cp + 2, &rightval));
+	    *valp = (*valp << rightval);
+	}
+	break;
+
+      case '>':
+	if (cp[1] == '>') {
+	    DO (cp = parse_shift (g, cp + 2, &rightval));
+	    *valp = (*valp >> rightval);
+	}
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_inequality (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_shift (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '<':
+	if (cp[1] == '=') {
+	    DO (cp = parse_inequality (g, cp + 2, &rightval));
+	    *valp = (*valp <= rightval);
+	} else {
+	    DO (cp = parse_inequality (g, cp + 1, &rightval));
+	    *valp = (*valp < rightval);
+	}
+	break;
+
+      case '>':
+	if (cp[1] == '=') {
+	    DO (cp = parse_inequality (g, cp + 2, &rightval));
+	    *valp = (*valp >= rightval);
+	} else {
+	    DO (cp = parse_inequality (g, cp + 1, &rightval));
+	    *valp = (*valp > rightval);
+	}
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_equality (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_inequality (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '=':
+	if (cp[1] == '=')
+	    cp++;
+	DO (cp = parse_equality (g, cp + 1, &rightval));
+	*valp = (*valp == rightval);
+	break;
+
+      case '!':
+	if (cp[1] != '=')
+	    break;
+	DO (cp = parse_equality (g, cp + 2, &rightval));
+	*valp = (*valp != rightval);
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_band (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_equality (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '&':
+	if (cp[1] != '&') {
+	    DO (cp = parse_band (g, cp + 1, &rightval));
+	    *valp = (*valp & rightval);
+	}
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_bor (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_band (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '|':
+	if (cp[1] != '|') {
+	    DO (cp = parse_bor (g, cp + 1, &rightval));
+	    *valp = (*valp | rightval);
+	}
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_land (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_bor (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '&':
+	if (cp[1] != '&')
+	    return CALLFUNC(g, handle_error) (g, cp, "&&");
+	DO (cp = parse_land (g, cp + 2, &rightval));
+	*valp = (*valp && rightval);
+	break;
+    }
+    return cp;
+}
+
+
+static const char *
+parse_lor (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    int rightval;
+
+    DO (cp = parse_land (g, cp, valp));
+    SKIPSPACE (cp);
+
+    switch (*cp) {
+      case '|':
+	if (cp[1] != '|')
+	    return CALLFUNC(g, handle_error) (g, cp, "||");
+	DO (cp = parse_lor (g, cp + 2, &rightval));
+	*valp = (*valp || rightval);
+	break;
+    }
+    return cp;
+}
+
+
+/****************************************************************************
+			     External Entry Points
+ ****************************************************************************/
+
+const char *
+ParseIfExpression (g, cp, valp)
+    IfParser *g;
+    const char *cp;
+    int *valp;
+{
+    return parse_lor (g, cp, valp);
+}
+
+
diff --git a/vncmkdepend/ifparser.h b/vncmkdepend/ifparser.h
new file mode 100644
index 0000000..c333798
--- /dev/null
+++ b/vncmkdepend/ifparser.h
@@ -0,0 +1,76 @@
+/*
+ * $XConsortium: ifparser.h,v 1.1 92/08/22 13:05:39 rws Exp $
+ *
+ * Copyright 1992 Network Computing Devices, Inc.
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Network Computing Devices may not be
+ * used in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Network Computing Devices makes
+ * no representations about the suitability of this software for any purpose.
+ * It is provided ``as is'' without express or implied warranty.
+ * 
+ * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * 
+ * Author:  Jim Fulton
+ *          Network Computing Devices, Inc.
+ * 
+ * Simple if statement processor
+ *
+ * This module can be used to evaluate string representations of C language
+ * if constructs.  It accepts the following grammar:
+ * 
+ *     EXPRESSION	:=	VALUE
+ * 			 |	VALUE  BINOP	EXPRESSION
+ * 
+ *     VALUE		:=	'('  EXPRESSION  ')'
+ * 			 |	'!'  VALUE
+ * 			 |	'-'  VALUE
+ * 			 |	'defined'  '('  variable  ')'
+ * 			 |	variable
+ * 			 |	number
+ * 
+ *     BINOP		:=	'*'	|  '/'	|  '%'
+ * 			 |	'+'	|  '-'
+ * 			 |	'<<'	|  '>>'
+ * 			 |	'<'	|  '>'	|  '<='  |  '>='
+ * 			 |	'=='	|  '!='
+ * 			 |	'&'	|  '|'
+ * 			 |	'&&'	|  '||'
+ * 
+ * The normal C order of precidence is supported.
+ * 
+ * 
+ * External Entry Points:
+ * 
+ *     ParseIfExpression		parse a string for #if
+ */
+
+#include <stdio.h>
+
+#define const /**/
+typedef int Bool;
+#define False 0
+#define True 1
+
+typedef struct _if_parser {
+    struct {				/* functions */
+	char *(*handle_error) (/* struct _if_parser *, const char *,
+				 const char * */);
+	int (*eval_variable) (/* struct _if_parser *, const char *, int */);
+	int (*eval_defined) (/* struct _if_parser *, const char *, int */);
+    } funcs;
+    char *data;
+} IfParser;
+
+char *ParseIfExpression (/* IfParser *, const char *, int * */);
+
diff --git a/vncmkdepend/include.c b/vncmkdepend/include.c
new file mode 100644
index 0000000..d6cd139
--- /dev/null
+++ b/vncmkdepend/include.c
@@ -0,0 +1,312 @@
+/* $XConsortium: include.c,v 1.17 94/12/05 19:33:08 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+
+#include "def.h"
+
+extern struct	inclist	inclist[ MAXFILES ],
+			*inclistp;
+extern char	*includedirs[ ];
+extern char	*notdotdot[ ];
+extern boolean show_where_not;
+extern boolean warn_multiple;
+
+struct inclist *inc_path(file, include, dot)
+	register char	*file,
+			*include;
+	boolean	dot;
+{
+	static char	path[ BUFSIZ ];
+	register char		**pp, *p;
+	register struct inclist	*ip;
+	struct stat	st;
+	boolean	found = FALSE;
+
+	/*
+	 * Check all previously found include files for a path that
+	 * has already been expanded.
+	 */
+	for (ip = inclist; ip->i_file; ip++)
+	    if ((strcmp(ip->i_incstring, include) == 0) && !ip->i_included_sym)
+	    {
+		found = TRUE;
+		break;
+	    }
+
+	/*
+	 * If the path was surrounded by "" or is an absolute path,
+	 * then check the exact path provided.
+	 */
+	if (!found && (dot || *include == '/')) {
+		if (stat(include, &st) == 0) {
+			ip = newinclude(include, include);
+			found = TRUE;
+		}
+		else if (show_where_not)
+			warning1("\tnot in %s\n", include);
+	}
+
+	/*
+	 * See if this include file is in the directory of the
+	 * file being compiled.
+	 */
+	if (!found) {
+		for (p=file+strlen(file); p>file; p--)
+			if (*p == '/')
+				break;
+		if (p == file)
+			strcpy(path, include);
+		else {
+			strncpy(path, file, (p-file) + 1);
+			path[ (p-file) + 1 ] = '\0';
+			strcpy(path + (p-file) + 1, include);
+		}
+		remove_dotdot(path);
+		if (stat(path, &st) == 0) {
+			ip = newinclude(path, include);
+			found = TRUE;
+		}
+		else if (show_where_not)
+			warning1("\tnot in %s\n", path);
+	}
+
+	/*
+	 * Check the include directories specified. (standard include dir
+	 * should be at the end.)
+	 */
+	if (!found)
+		for (pp = includedirs; *pp; pp++) {
+#ifdef WIN32
+			sprintf(path, "%s\\%s", *pp, include);
+#else
+			sprintf(path, "%s/%s", *pp, include);
+#endif
+			remove_dotdot(path);
+			if (stat(path, &st) == 0) {
+				ip = newinclude(path, include);
+				found = TRUE;
+				break;
+			}
+			else if (show_where_not)
+				warning1("\tnot in %s\n", path);
+		}
+
+	if (!found)
+		ip = NULL;
+	return(ip);
+}
+
+/*
+ * Occasionally, pathnames are created that look like .../x/../y
+ * Any of the 'x/..' sequences within the name can be eliminated.
+ * (but only if 'x' is not a symbolic link!!)
+ */
+remove_dotdot(path)
+	char	*path;
+{
+	register char	*end, *from, *to, **cp;
+	char		*components[ MAXFILES ],
+			newpath[ BUFSIZ ];
+	boolean		component_copied;
+
+	/*
+	 * slice path up into components.
+	 */
+	to = newpath;
+	if (*path == '/')
+		*to++ = '/';
+	*to = '\0';
+	cp = components;
+	for (from=end=path; *end; end++)
+		if (*end == '/') {
+			while (*end == '/')
+				*end++ = '\0';
+			if (*from)
+				*cp++ = from;
+			from = end;
+		}
+	*cp++ = from;
+	*cp = NULL;
+
+	/*
+	 * Recursively remove all 'x/..' component pairs.
+	 */
+	cp = components;
+	while(*cp) {
+		if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1))
+		    && !issymbolic(newpath, *cp))
+		{
+		    char **fp = cp + 2;
+		    char **tp = cp;
+
+		    do 
+			*tp++ = *fp; /* move all the pointers down */
+		    while (*fp++);
+		    if (cp != components)
+			cp--;	/* go back and check for nested ".." */
+		} else {
+		    cp++;
+		}
+	}
+	/*
+	 * Concatenate the remaining path elements.
+	 */
+	cp = components;
+	component_copied = FALSE;
+	while(*cp) {
+		if (component_copied)
+			*to++ = '/';
+		component_copied = TRUE;
+		for (from = *cp; *from; )
+			*to++ = *from++;
+		*to = '\0';
+		cp++;
+	}
+	*to++ = '\0';
+
+	/*
+	 * copy the reconstituted path back to our pointer.
+	 */
+	strcpy(path, newpath);
+}
+
+isdot(p)
+	register char	*p;
+{
+	if(p && *p++ == '.' && *p++ == '\0')
+		return(TRUE);
+	return(FALSE);
+}
+
+isdotdot(p)
+	register char	*p;
+{
+	if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
+		return(TRUE);
+	return(FALSE);
+}
+
+issymbolic(dir, component)
+	register char	*dir, *component;
+{
+#ifdef S_IFLNK
+	struct stat	st;
+	char	buf[ BUFSIZ ], **pp;
+
+	sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component);
+	for (pp=notdotdot; *pp; pp++)
+		if (strcmp(*pp, buf) == 0)
+			return (TRUE);
+	if (lstat(buf, &st) == 0
+	&& (st.st_mode & S_IFMT) == S_IFLNK) {
+		*pp++ = copy(buf);
+		if (pp >= &notdotdot[ MAXDIRS ])
+			fatalerr("out of .. dirs, increase MAXDIRS\n");
+		return(TRUE);
+	}
+#endif
+	return(FALSE);
+}
+
+/*
+ * Add an include file to the list of those included by 'file'.
+ */
+struct inclist *newinclude(newfile, incstring)
+	register char	*newfile, *incstring;
+{
+	register struct inclist	*ip;
+
+	/*
+	 * First, put this file on the global list of include files.
+	 */
+	ip = inclistp++;
+	if (inclistp == inclist + MAXFILES - 1)
+		fatalerr("out of space: increase MAXFILES\n");
+	ip->i_file = copy(newfile);
+	ip->i_included_sym = FALSE;
+	if (incstring == NULL)
+		ip->i_incstring = ip->i_file;
+	else
+		ip->i_incstring = copy(incstring);
+
+	return(ip);
+}
+
+included_by(ip, newfile)
+	register struct inclist	*ip, *newfile;
+{
+	register i;
+
+	if (ip == NULL)
+		return;
+	/*
+	 * Put this include file (newfile) on the list of files included
+	 * by 'file'.  If 'file' is NULL, then it is not an include
+	 * file itself (i.e. was probably mentioned on the command line).
+	 * If it is already on the list, don't stick it on again.
+	 */
+	if (ip->i_list == NULL)
+		ip->i_list = (struct inclist **)
+			malloc(sizeof(struct inclist *) * ++ip->i_listlen);
+	else {
+		for (i=0; i<ip->i_listlen; i++)
+			if (ip->i_list[ i ] == newfile) {
+			    i = strlen(newfile->i_file);
+			    if (!ip->i_included_sym &&
+				!(i > 2 &&
+				  newfile->i_file[i-1] == 'c' &&
+				  newfile->i_file[i-2] == '.'))
+			    {
+				/* only bitch if ip has */
+				/* no #include SYMBOL lines  */
+				/* and is not a .c file */
+				if (warn_multiple)
+				{
+					warning("%s includes %s more than once!\n",
+						ip->i_file, newfile->i_file);
+					warning1("Already have\n");
+					for (i=0; i<ip->i_listlen; i++)
+						warning1("\t%s\n", ip->i_list[i]->i_file);
+				}
+			    }
+			    return;
+			}
+		ip->i_list = (struct inclist **) realloc(ip->i_list,
+			sizeof(struct inclist *) * ++ip->i_listlen);
+	}
+	ip->i_list[ ip->i_listlen-1 ] = newfile;
+}
+
+inc_clean ()
+{
+	register struct inclist *ip;
+
+	for (ip = inclist; ip < inclistp; ip++) {
+		ip->i_marked = FALSE;
+	}
+}
diff --git a/vncmkdepend/main.c b/vncmkdepend/main.c
new file mode 100644
index 0000000..73057ae
--- /dev/null
+++ b/vncmkdepend/main.c
@@ -0,0 +1,593 @@
+/* $XConsortium: main.c,v 1.84 94/11/30 16:10:44 kaleb Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+#ifdef hpux
+#define sigvec sigvector
+#endif /* hpux */
+
+#ifdef X_POSIX_C_SOURCE
+#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
+#include <signal.h>
+#undef _POSIX_C_SOURCE
+#else
+#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
+#include <signal.h>
+#else
+#define _POSIX_SOURCE
+#include <signal.h>
+#undef _POSIX_SOURCE
+#endif
+#endif
+
+#if NeedVarargsPrototypes
+#include <stdarg.h>
+#endif
+
+#ifdef DEBUG
+int	_debugmask;
+#endif
+
+char *ProgramName;
+
+char	*directives[] = {
+	"if",
+	"ifdef",
+	"ifndef",
+	"else",
+	"endif",
+	"define",
+	"undef",
+	"include",
+	"line",
+	"pragma",
+	"error",
+	"ident",
+	"sccs",
+	"elif",
+	"eject",
+	NULL
+};
+
+struct symtab predefs[] = { {NULL, NULL} };
+
+struct	inclist inclist[ MAXFILES ],
+		*inclistp = inclist,
+		maininclist;
+
+char	*filelist[ MAXFILES ];
+char	*includedirs[ MAXDIRS + 1 ];
+char	*notdotdot[ MAXDIRS ];
+char	*objprefix = "";
+char	*objsuffix = ".o";
+int	width = 78;
+boolean	append = FALSE;
+boolean	printed = FALSE;
+boolean	verbose = FALSE;
+boolean	show_where_not = FALSE;
+boolean warn_multiple = FALSE;	/* Warn on multiple includes of same file */
+
+static
+#ifdef SIGNALRETURNSINT
+int
+#else
+void
+#endif
+catch (sig)
+    int sig;
+{
+	fflush (stdout);
+	fatalerr ("got signal %d\n", sig);
+}
+
+#if defined(USG) || (defined(i386) && defined(SYSV)) || defined(WIN32) || defined(__nextstep__)
+#define USGISH
+#endif
+
+#ifndef USGISH
+#ifndef _POSIX_SOURCE
+#define sigaction sigvec
+#define sa_handler sv_handler
+#define sa_mask sv_mask
+#define sa_flags sv_flags
+#endif
+struct sigaction sig_act;
+#endif /* USGISH */
+
+main(argc, argv)
+	int	argc;
+	char	**argv;
+{
+	register char	**fp = filelist;
+	register char	**incp = includedirs;
+	register char	*p;
+	register struct inclist	*ip;
+	char	*makefile = NULL;
+	struct filepointer	*filecontent;
+	struct symtab *psymp = predefs;
+	char *endmarker = NULL;
+	char *defincdir = NULL;
+
+	ProgramName = argv[0];
+
+	while (psymp->s_name)
+	{
+	    define2(psymp->s_name, psymp->s_value, &maininclist);
+	    psymp++;
+	}
+	if (argc == 2 && argv[1][0] == '@') {
+	    struct stat ast;
+	    int afd;
+	    char *args;
+	    char **nargv;
+	    int nargc;
+	    char quotechar = '\0';
+
+	    nargc = 1;
+	    if ((afd = open(argv[1]+1, O_RDONLY)) < 0)
+		fatalerr("cannot open \"%s\"\n", argv[1]+1);
+	    fstat(afd, &ast);
+	    args = (char *)malloc(ast.st_size + 1);
+	    if ((ast.st_size = read(afd, args, ast.st_size)) < 0)
+		fatalerr("failed to read %s\n", argv[1]+1);
+	    args[ast.st_size] = '\0';
+	    close(afd);
+	    for (p = args; *p; p++) {
+		if (quotechar) {
+		    if (quotechar == '\\' ||
+			(*p == quotechar && p[-1] != '\\'))
+			quotechar = '\0';
+		    continue;
+		}
+		switch (*p) {
+		case '\\':
+		case '"':
+		case '\'':
+		    quotechar = *p;
+		    break;
+		case ' ':
+		case '\n':
+		    *p = '\0';
+		    if (p > args && p[-1])
+			nargc++;
+		    break;
+		}
+	    }
+	    if (p[-1])
+		nargc++;
+	    nargv = (char **)malloc(nargc * sizeof(char *));
+	    nargv[0] = argv[0];
+	    argc = 1;
+	    for (p = args; argc < nargc; p += strlen(p) + 1)
+		if (*p) nargv[argc++] = p;
+	    argv = nargv;
+	}
+	for(argc--, argv++; argc; argc--, argv++) {
+	    	/* if looking for endmarker then check before parsing */
+		if (endmarker && strcmp (endmarker, *argv) == 0) {
+		    endmarker = NULL;
+		    continue;
+		}
+		if (**argv != '-') {
+			/* treat +thing as an option for C++ */
+			if (endmarker && **argv == '+')
+				continue;
+			*fp++ = argv[0];
+			continue;
+		}
+		switch(argv[0][1]) {
+		case '-':
+			endmarker = &argv[0][2];
+			if (endmarker[0] == '\0') endmarker = "--";
+			break;
+		case 'D':
+		    {
+			int offset = 2;
+			if (argv[0][2] == '\0') {
+				argv++;
+				argc--;
+				offset = 0;
+			}
+			for (p=argv[0] + offset; *p ; p++)
+				if (*p == '=') {
+					*p = ' ';
+					break;
+				}
+			define(argv[0] + offset, &maininclist);
+			break;
+		    }
+		case 'I':
+			if (incp >= includedirs + MAXDIRS)
+			    fatalerr("Too many -I flags.\n");
+			*incp++ = argv[0]+2;
+			if (**(incp-1) == '\0') {
+				*(incp-1) = *(++argv);
+				argc--;
+			}
+			break;
+		case 'Y':
+			defincdir = argv[0]+2;
+			break;
+		/* do not use if endmarker processing */
+		case 'a':
+			if (endmarker) break;
+			append = TRUE;
+			break;
+		case 'w':
+			if (endmarker) break;
+			if (argv[0][2] == '\0') {
+				argv++;
+				argc--;
+				width = atoi(argv[0]);
+			} else
+				width = atoi(argv[0]+2);
+			break;
+		case 'o':
+			if (endmarker) break;
+			if (argv[0][2] == '\0') {
+				argv++;
+				argc--;
+				objsuffix = argv[0];
+			} else
+				objsuffix = argv[0]+2;
+			break;
+		case 'p':
+			if (endmarker) break;
+			if (argv[0][2] == '\0') {
+				argv++;
+				argc--;
+				objprefix = argv[0];
+			} else
+				objprefix = argv[0]+2;
+			break;
+		case 'v':
+			if (endmarker) break;
+			verbose = TRUE;
+#ifdef DEBUG
+			if (argv[0][2])
+				_debugmask = atoi(argv[0]+2);
+#endif
+			break;
+		case 'm':
+			warn_multiple = TRUE;
+			break;
+			
+		/* Ignore -O, -g so we can just pass ${CFLAGS} to
+		   makedepend
+		 */
+		case 'O':
+		case 'g':
+			break;
+		default:
+			if (endmarker) break;
+	/*		fatalerr("unknown opt = %s\n", argv[0]); */
+			warning("ignoring option %s\n", argv[0]);
+		}
+	}
+	if (!defincdir) {
+	} else if (*defincdir) {
+	    if (incp >= includedirs + MAXDIRS)
+		fatalerr("Too many -I flags.\n");
+	    *incp++ = defincdir;
+	}
+
+	/*
+	 * catch signals.
+	 */
+#ifdef USGISH
+/*  should really reset SIGINT to SIG_IGN if it was.  */
+#ifdef SIGHUP
+	signal (SIGHUP, catch);
+#endif
+	signal (SIGINT, catch);
+#ifdef SIGQUIT
+	signal (SIGQUIT, catch);
+#endif
+	signal (SIGILL, catch);
+#ifdef SIGBUS
+	signal (SIGBUS, catch);
+#endif
+	signal (SIGSEGV, catch);
+#ifdef SIGSYS
+	signal (SIGSYS, catch);
+#endif
+#else
+	sig_act.sa_handler = catch;
+#ifdef _POSIX_SOURCE
+	sigemptyset(&sig_act.sa_mask);
+	sigaddset(&sig_act.sa_mask, SIGINT);
+	sigaddset(&sig_act.sa_mask, SIGQUIT);
+#ifdef SIGBUS
+	sigaddset(&sig_act.sa_mask, SIGBUS);
+#endif
+	sigaddset(&sig_act.sa_mask, SIGILL);
+	sigaddset(&sig_act.sa_mask, SIGSEGV);
+	sigaddset(&sig_act.sa_mask, SIGHUP);
+	sigaddset(&sig_act.sa_mask, SIGPIPE);
+#ifdef SIGSYS
+	sigaddset(&sig_act.sa_mask, SIGSYS);
+#endif
+#else
+	sig_act.sa_mask = ((1<<(SIGINT -1))
+			   |(1<<(SIGQUIT-1))
+#ifdef SIGBUS
+			   |(1<<(SIGBUS-1))
+#endif
+			   |(1<<(SIGILL-1))
+			   |(1<<(SIGSEGV-1))
+			   |(1<<(SIGHUP-1))
+			   |(1<<(SIGPIPE-1))
+#ifdef SIGSYS
+			   |(1<<(SIGSYS-1))
+#endif
+			   );
+#endif /* _POSIX_SOURCE */
+	sig_act.sa_flags = 0;
+	sigaction(SIGHUP, &sig_act, (struct sigaction *)0);
+	sigaction(SIGINT, &sig_act, (struct sigaction *)0);
+	sigaction(SIGQUIT, &sig_act, (struct sigaction *)0);
+	sigaction(SIGILL, &sig_act, (struct sigaction *)0);
+#ifdef SIGBUS
+	sigaction(SIGBUS, &sig_act, (struct sigaction *)0);
+#endif
+	sigaction(SIGSEGV, &sig_act, (struct sigaction *)0);
+#ifdef SIGSYS
+	sigaction(SIGSYS, &sig_act, (struct sigaction *)0);
+#endif
+#endif /* USGISH */
+
+	/*
+	 * now peruse through the list of files.
+	 */
+	for(fp=filelist; *fp; fp++) {
+		char *base = base_name(*fp);
+		char *depfile = (char *)malloc(strlen(base) + 3);
+		sprintf(depfile,"%s.d",base);
+		if (!freopen(depfile, "wb", stdout))
+		    fatalerr("cannot open \"%s\"\n", depfile);
+		free(depfile);
+		free(base);
+		printed = FALSE;
+		filecontent = getfile(*fp);
+		ip = newinclude(*fp, (char *)NULL);
+
+		find_includes(filecontent, ip, ip, 0, TRUE);
+		freefile(filecontent);
+		recursive_pr_include(ip, ip->i_file, base_name(*fp));
+		inc_clean();
+		if (printed)
+			printf("\n");
+	}
+	exit(0);
+}
+
+struct filepointer *getfile(file)
+	char	*file;
+{
+	register int	fd;
+	struct filepointer	*content;
+	struct stat	st;
+
+	content = (struct filepointer *)malloc(sizeof(struct filepointer));
+	if ((fd = open(file, O_RDONLY)) < 0) {
+		warning("cannot open \"%s\"\n", file);
+		content->f_p = content->f_base = content->f_end = (char *)malloc(1);
+		*content->f_p = '\0';
+		return(content);
+	}
+	fstat(fd, &st);
+	content->f_base = (char *)malloc(st.st_size+1);
+	if (content->f_base == NULL)
+		fatalerr("cannot allocate mem\n");
+	if ((st.st_size = read(fd, content->f_base, st.st_size)) < 0)
+		fatalerr("failed to read %s\n", file);
+	close(fd);
+	content->f_len = st.st_size+1;
+	content->f_p = content->f_base;
+	content->f_end = content->f_base + st.st_size;
+	*content->f_end = '\0';
+	content->f_line = 0;
+	return(content);
+}
+
+freefile(fp)
+	struct filepointer	*fp;
+{
+	free(fp->f_base);
+	free(fp);
+}
+
+char *copy(str)
+	register char	*str;
+{
+	register char	*p = (char *)malloc(strlen(str) + 1);
+
+	strcpy(p, str);
+	return(p);
+}
+
+match(str, list)
+	register char	*str, **list;
+{
+	register int	i;
+
+	for (i=0; *list; i++, list++)
+		if (strcmp(str, *list) == 0)
+			return(i);
+	return(-1);
+}
+
+/*
+ * Get the next line.  We only return lines beginning with '#' since that
+ * is all this program is ever interested in.
+ */
+char *getline(filep)
+	register struct filepointer	*filep;
+{
+	register char	*p,	/* walking pointer */
+			*eof,	/* end of file pointer */
+			*bol;	/* beginning of line pointer */
+	register	lineno;	/* line number */
+
+	p = filep->f_p;
+	eof = filep->f_end;
+	if (p >= eof)
+		return((char *)NULL);
+	lineno = filep->f_line;
+
+	for(bol = p--; ++p < eof; ) {
+		if (*p == '/' && *(p+1) == '*') { /* consume comments */
+			*p++ = ' ', *p++ = ' ';
+			while (*p) {
+				if (*p == '*' && *(p+1) == '/') {
+					*p++ = ' ', *p = ' ';
+					break;
+				}
+				else if (*p == '\n')
+					lineno++;
+				*p++ = ' ';
+			}
+			continue;
+		}
+		else if (*p == '/' && *(p+1) == '/') { /* consume comments */
+			*p++ = ' ', *p++ = ' ';
+			while (*p && *p != '\n')
+				*p++ = ' ';
+			p--;	/* go back to before newline */
+			lineno++;
+			continue;
+		}
+		else if (*p == '\\') {
+			if (*(p+1) == '\n') {
+				*p = ' ';
+				*(p+1) = ' ';
+				lineno++;
+			}
+		}
+		else if (*p == '\n') {
+			lineno++;
+			if (*bol == '#') {
+				register char *cp;
+
+				*p++ = '\0';
+				/* punt lines with just # (yacc generated) */
+				for (cp = bol+1; 
+				     *cp && (*cp == ' ' || *cp == '\t'); cp++);
+				if (*cp) goto done;
+			}
+			bol = p+1;
+		}
+	}
+	if (*bol != '#')
+		bol = NULL;
+done:
+	filep->f_p = p;
+	filep->f_line = lineno;
+	return(bol);
+}
+
+/*
+ * Strip the file name down to what we want to see in the Makefile.
+ * It will have objprefix and objsuffix around it.
+ */
+char *base_name(file)
+	register char	*file;
+{
+	register char	*p;
+
+	for(p=file+strlen(file); p>file && *p != '/' && *p != '\\'; p--) ;
+	if (p>file) p++;
+	file = copy(p);
+	for(p=file+strlen(file); p>file && *p != '.'; p--) ;
+
+	if (*p == '.')
+		*p = '\0';
+	return(file);
+}
+
+
+#if NeedVarargsPrototypes
+fatalerr(char *msg, ...)
+#else
+/*VARARGS*/
+fatalerr(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+    char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+	va_list args;
+#endif
+	fprintf(stderr, "%s: error:  ", ProgramName);
+#if NeedVarargsPrototypes
+	va_start(args, msg);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+#else
+	fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+	exit (1);
+}
+
+#if NeedVarargsPrototypes
+warning(char *msg, ...)
+#else
+/*VARARGS0*/
+warning(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+    char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+	va_list args;
+#endif
+	fprintf(stderr, "%s: warning:  ", ProgramName);
+#if NeedVarargsPrototypes
+	va_start(args, msg);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+#else
+	fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+}
+
+#if NeedVarargsPrototypes
+warning1(char *msg, ...)
+#else
+/*VARARGS0*/
+warning1(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+    char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+	va_list args;
+	va_start(args, msg);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+#else
+	fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+}
diff --git a/vncmkdepend/parse.c b/vncmkdepend/parse.c
new file mode 100644
index 0000000..3f9b2fe
--- /dev/null
+++ b/vncmkdepend/parse.c
@@ -0,0 +1,568 @@
+/* $XConsortium: parse.c,v 1.30 94/04/17 20:10:38 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+extern char	*directives[];
+extern struct inclist	maininclist;
+
+find_includes(filep, file, file_red, recursion, failOK)
+	struct filepointer	*filep;
+	struct inclist		*file, *file_red;
+	int			recursion;
+	boolean			failOK;
+{
+	register char	*line;
+	register int	type;
+	boolean recfailOK;
+
+	while (line = getline(filep)) {
+		switch(type = deftype(line, filep, file_red, file, TRUE)) {
+		case IF:
+		doif:
+			type = find_includes(filep, file,
+				file_red, recursion+1, failOK);
+			while ((type == ELIF) || (type == ELIFFALSE) ||
+			       (type == ELIFGUESSFALSE))
+				type = gobble(filep, file, file_red);
+			if (type == ELSE)
+				gobble(filep, file, file_red);
+			break;
+		case IFFALSE:
+		case IFGUESSFALSE:
+		    doiffalse:
+			if (type == IFGUESSFALSE || type == ELIFGUESSFALSE)
+			    recfailOK = TRUE;
+			else
+			    recfailOK = failOK;
+			type = gobble(filep, file, file_red);
+			if (type == ELSE)
+			    find_includes(filep, file,
+					  file_red, recursion+1, recfailOK);
+			else
+			if (type == ELIF)
+			    goto doif;
+			else
+			if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE))
+			    goto doiffalse;
+			break;
+		case IFDEF:
+		case IFNDEF:
+			if ((type == IFDEF && isdefined(line, file_red, NULL))
+			 || (type == IFNDEF && !isdefined(line, file_red, NULL))) {
+				debug(1,(type == IFNDEF ?
+				    "line %d: %s !def'd in %s via %s%s\n" : "",
+				    filep->f_line, line,
+				    file->i_file, file_red->i_file, ": doit"));
+				type = find_includes(filep, file,
+					file_red, recursion+1, failOK);
+				while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE)
+					type = gobble(filep, file, file_red);
+				if (type == ELSE)
+					gobble(filep, file, file_red);
+			}
+			else {
+				debug(1,(type == IFDEF ?
+				    "line %d: %s !def'd in %s via %s%s\n" : "",
+				    filep->f_line, line,
+				    file->i_file, file_red->i_file, ": gobble"));
+				type = gobble(filep, file, file_red);
+				if (type == ELSE)
+					find_includes(filep, file,
+						file_red, recursion+1, failOK);
+				else if (type == ELIF)
+				    	goto doif;
+				else if (type == ELIFFALSE || type == ELIFGUESSFALSE)
+				    	goto doiffalse;
+			}
+			break;
+		case ELSE:
+		case ELIFFALSE:
+		case ELIFGUESSFALSE:
+		case ELIF:
+			if (!recursion)
+				gobble(filep, file, file_red);
+		case ENDIF:
+			if (recursion)
+				return(type);
+		case DEFINE:
+			define(line, file);
+			break;
+		case UNDEF:
+			if (!*line) {
+			    warning("%s, line %d: incomplete undef == \"%s\"\n",
+				file_red->i_file, filep->f_line, line);
+			    break;
+			}
+			undefine(line, file_red);
+			break;
+		case INCLUDE:
+			add_include(filep, file, file_red, line, FALSE, failOK);
+			break;
+		case INCLUDEDOT:
+			add_include(filep, file, file_red, line, TRUE, failOK);
+			break;
+		case ERROR:
+		    	warning("(from %s) %s: %d: %s\n",
+				file_red->i_file, file->i_file,
+				 filep->f_line, line);
+		    	break;
+		    
+		case PRAGMA:
+		case IDENT:
+		case SCCS:
+		case EJECT:
+			break;
+		case -1:
+			warning("%s", file_red->i_file);
+			if (file_red != file)
+			    warning1(" (reading %s)", file->i_file);
+			warning1(", line %d: unknown directive == \"%s\"\n",
+				 filep->f_line, line);
+			break;
+		case -2:
+			warning("%s", file_red->i_file);
+			if (file_red != file)
+			    warning1(" (reading %s)", file->i_file);
+			warning1(", line %d: incomplete include == \"%s\"\n",
+				 filep->f_line, line);
+			break;
+		}
+	}
+	return(-1);
+}
+
+gobble(filep, file, file_red)
+	register struct filepointer *filep;
+	struct inclist		*file, *file_red;
+{
+	register char	*line;
+	register int	type;
+
+	while (line = getline(filep)) {
+		switch(type = deftype(line, filep, file_red, file, FALSE)) {
+		case IF:
+		case IFFALSE:
+		case IFGUESSFALSE:
+		case IFDEF:
+		case IFNDEF:
+			type = gobble(filep, file, file_red);
+			while ((type == ELIF) || (type == ELIFFALSE) ||
+			       (type == ELIFGUESSFALSE))
+			    type = gobble(filep, file, file_red);
+			if (type == ELSE)
+			        (void)gobble(filep, file, file_red);
+			break;
+		case ELSE:
+		case ENDIF:
+			debug(0,("%s, line %d: #%s\n",
+				file->i_file, filep->f_line,
+				directives[type]));
+			return(type);
+		case DEFINE:
+		case UNDEF:
+		case INCLUDE:
+		case INCLUDEDOT:
+		case PRAGMA:
+		case ERROR:
+		case IDENT:
+		case SCCS:
+		case EJECT:
+			break;
+		case ELIF:
+		case ELIFFALSE:
+		case ELIFGUESSFALSE:
+			return(type);
+		case -1:
+			warning("%s, line %d: unknown directive == \"%s\"\n",
+				file_red->i_file, filep->f_line, line);
+			break;
+		}
+	}
+	return(-1);
+}
+
+/*
+ * Decide what type of # directive this line is.
+ */
+int deftype (line, filep, file_red, file, parse_it)
+	register char	*line;
+	register struct filepointer *filep;
+	register struct inclist *file_red, *file;
+	int	parse_it;
+{
+	register char	*p;
+	char	*directive, savechar;
+	register int	ret;
+
+	/*
+	 * Parse the directive...
+	 */
+	directive=line+1;
+	while (*directive == ' ' || *directive == '\t')
+		directive++;
+
+	p = directive;
+	while (*p >= 'a' && *p <= 'z')
+		p++;
+	savechar = *p;
+	*p = '\0';
+	ret = match(directive, directives);
+	*p = savechar;
+
+	/* If we don't recognize this compiler directive or we happen to just
+	 * be gobbling up text while waiting for an #endif or #elif or #else
+	 * in the case of an #elif we must check the zero_value and return an
+	 * ELIF or an ELIFFALSE.
+	 */
+
+	if (ret == ELIF && !parse_it)
+	{
+	    while (*p == ' ' || *p == '\t')
+		p++;
+	    /*
+	     * parse an expression.
+	     */
+	    debug(0,("%s, line %d: #elif %s ",
+		   file->i_file, filep->f_line, p));
+	    ret = zero_value(p, filep, file_red);
+	    if (ret != IF)
+	    {
+		debug(0,("false...\n"));
+		if (ret == IFFALSE)
+		    return(ELIFFALSE);
+		else
+		    return(ELIFGUESSFALSE);
+	    }
+	    else
+	    {
+		debug(0,("true...\n"));
+		return(ELIF);
+	    }
+	}
+
+	if (ret < 0 || ! parse_it)
+		return(ret);
+
+	/*
+	 * now decide how to parse the directive, and do it.
+	 */
+	while (*p == ' ' || *p == '\t')
+		p++;
+	switch (ret) {
+	case IF:
+		/*
+		 * parse an expression.
+		 */
+		ret = zero_value(p, filep, file_red);
+		debug(0,("%s, line %d: %s #if %s\n",
+			 file->i_file, filep->f_line, ret?"false":"true", p));
+		break;
+	case IFDEF:
+	case IFNDEF:
+		debug(0,("%s, line %d: #%s %s\n",
+			file->i_file, filep->f_line, directives[ret], p));
+	case UNDEF:
+		/*
+		 * separate the name of a single symbol.
+		 */
+		while (isalnum(*p) || *p == '_')
+			*line++ = *p++;
+		*line = '\0';
+		break;
+	case INCLUDE:
+		debug(2,("%s, line %d: #include %s\n",
+			file->i_file, filep->f_line, p));
+
+		/* Support ANSI macro substitution */
+		{
+		    struct symtab *sym = isdefined(p, file_red, NULL);
+		    while (sym) {
+			p = sym->s_value;
+			debug(3,("%s : #includes SYMBOL %s = %s\n",
+			       file->i_incstring,
+			       sym -> s_name,
+			       sym -> s_value));
+			/* mark file as having included a 'soft include' */
+			file->i_included_sym = TRUE; 
+			sym = isdefined(p, file_red, NULL);
+		    }
+		}
+
+		/*
+		 * Separate the name of the include file.
+		 */
+		while (*p && *p != '"' && *p != '<')
+			p++;
+		if (! *p)
+			return(-2);
+		if (*p++ == '"') {
+			ret = INCLUDEDOT;
+			while (*p && *p != '"')
+				*line++ = *p++;
+		} else
+			while (*p && *p != '>')
+				*line++ = *p++;
+		*line = '\0';
+		break;
+	case DEFINE:
+		/*
+		 * copy the definition back to the beginning of the line.
+		 */
+		strcpy (line, p);
+		break;
+	case ELSE:
+	case ENDIF:
+	case ELIF:
+	case PRAGMA:
+	case ERROR:
+	case IDENT:
+	case SCCS:
+	case EJECT:
+		debug(0,("%s, line %d: #%s\n",
+			file->i_file, filep->f_line, directives[ret]));
+		/*
+		 * nothing to do.
+		 */
+		break;
+	}
+	return(ret);
+}
+
+struct symtab *isdefined(symbol, file, srcfile)
+	register char	*symbol;
+	struct inclist	*file;
+	struct inclist	**srcfile;
+{
+	register struct symtab	*val;
+
+	if (val = slookup(symbol, &maininclist)) {
+		debug(1,("%s defined on command line\n", symbol));
+		if (srcfile != NULL) *srcfile = &maininclist;
+		return(val);
+	}
+	if (val = fdefined(symbol, file, srcfile))
+		return(val);
+	debug(1,("%s not defined in %s\n", symbol, file->i_file));
+	return(NULL);
+}
+
+struct symtab *fdefined(symbol, file, srcfile)
+	register char	*symbol;
+	struct inclist	*file;
+	struct inclist	**srcfile;
+{
+	register struct inclist	**ip;
+	register struct symtab	*val;
+	register int	i;
+	static int	recurse_lvl = 0;
+
+	if (file->i_defchecked)
+		return(NULL);
+	file->i_defchecked = TRUE;
+	if (val = slookup(symbol, file))
+		debug(1,("%s defined in %s as %s\n", symbol, file->i_file, val->s_value));
+	if (val == NULL && file->i_list)
+		{
+		for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++)
+			if (val = fdefined(symbol, *ip, srcfile)) {
+				break;
+			}
+		}
+	else if (val != NULL && srcfile != NULL) *srcfile = file;
+	recurse_lvl--;
+	file->i_defchecked = FALSE;
+
+	return(val);
+}
+
+/*
+ * Return type based on if the #if expression evaluates to 0
+ */
+zero_value(exp, filep, file_red)
+	register char	*exp;
+	register struct filepointer *filep;
+	register struct inclist *file_red;
+{
+	if (cppsetup(exp, filep, file_red))
+	    return(IFFALSE);
+	else
+	    return(IF);
+}
+
+define(def, file)
+	char	*def;
+	struct inclist	*file;
+{
+    char *val;
+
+    /* Separate symbol name and its value */
+    val = def;
+    while (isalnum(*val) || *val == '_')
+	val++;
+    if (*val)
+	*val++ = '\0';
+    while (*val == ' ' || *val == '\t')
+	val++;
+
+    if (!*val)
+	val = "1";
+    define2(def, val, file);
+}
+
+define2(name, val, file)
+	char	*name, *val;
+	struct inclist	*file;
+{
+    int first, last, below;
+    register struct symtab *sp = NULL, *dest;
+
+    /* Make space if it's needed */
+    if (file->i_defs == NULL)
+    {
+	file->i_defs = (struct symtab *)
+			malloc(sizeof (struct symtab) * SYMTABINC);
+	file->i_deflen = SYMTABINC;
+	file->i_ndefs = 0;
+    }
+    else if (file->i_ndefs == file->i_deflen)
+	file->i_defs = (struct symtab *)
+			realloc(file->i_defs,
+			    sizeof(struct symtab)*(file->i_deflen+=SYMTABINC));
+
+    if (file->i_defs == NULL)
+	fatalerr("malloc()/realloc() failure in insert_defn()\n");
+
+    below = first = 0;
+    last = file->i_ndefs - 1;
+    while (last >= first)
+    {
+	/* Fast inline binary search */
+	register char *s1;
+	register char *s2;
+	register int middle = (first + last) / 2;
+
+	/* Fast inline strchr() */
+	s1 = name;
+	s2 = file->i_defs[middle].s_name;
+	while (*s1++ == *s2++)
+	    if (s2[-1] == '\0') break;
+
+	/* If exact match, set sp and break */
+	if (*--s1 == *--s2) 
+	{
+	    sp = file->i_defs + middle;
+	    break;
+	}
+
+	/* If name > i_defs[middle] ... */
+	if (*s1 > *s2) 
+	{
+	    below = first;
+	    first = middle + 1;
+	}
+	/* else ... */
+	else
+	{
+	    below = last = middle - 1;
+	}
+    }
+
+    /* Search is done.  If we found an exact match to the symbol name,
+       just replace its s_value */
+    if (sp != NULL)
+    {
+	free(sp->s_value);
+	sp->s_value = copy(val);
+	return;
+    }
+
+    sp = file->i_defs + file->i_ndefs++;
+    dest = file->i_defs + below + 1;
+    while (sp > dest)
+    {
+	*sp = sp[-1];
+	sp--;
+    }
+    sp->s_name = copy(name);
+    sp->s_value = copy(val);
+}
+
+struct symtab *slookup(symbol, file)
+	register char	*symbol;
+	register struct inclist	*file;
+{
+	register int first = 0;
+	register int last = file->i_ndefs - 1;
+
+	if (file) while (last >= first)
+	{
+	    /* Fast inline binary search */
+	    register char *s1;
+	    register char *s2;
+	    register int middle = (first + last) / 2;
+
+	    /* Fast inline strchr() */
+	    s1 = symbol;
+	    s2 = file->i_defs[middle].s_name;
+	    while (*s1++ == *s2++)
+	        if (s2[-1] == '\0') break;
+
+	    /* If exact match, we're done */
+	    if (*--s1 == *--s2) 
+	    {
+	        return file->i_defs + middle;
+	    }
+
+	    /* If symbol > i_defs[middle] ... */
+	    if (*s1 > *s2) 
+	    {
+	        first = middle + 1;
+	    }
+	    /* else ... */
+	    else
+	    {
+	        last = middle - 1;
+	    }
+	}
+	return(NULL);
+}
+
+undefine(symbol, file)
+	char	*symbol;
+	register struct inclist	*file;
+{
+	register struct symtab *ptr;
+	struct inclist *srcfile;
+	while ((ptr = isdefined(symbol, file, &srcfile)) != NULL)
+	{
+	    srcfile->i_ndefs--;
+	    for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++)
+		*ptr = ptr[1];
+	}
+}
diff --git a/vncmkdepend/pr.c b/vncmkdepend/pr.c
new file mode 100644
index 0000000..318046d
--- /dev/null
+++ b/vncmkdepend/pr.c
@@ -0,0 +1,130 @@
+/* $XConsortium: pr.c,v 1.17 94/04/17 20:10:38 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+extern struct	inclist	inclist[ MAXFILES ],
+			*inclistp;
+extern char	*objprefix;
+extern char	*objsuffix;
+extern int	width;
+extern boolean	printed;
+extern boolean	verbose;
+extern boolean	show_where_not;
+
+add_include(filep, file, file_red, include, dot, failOK)
+	struct filepointer	*filep;
+	struct inclist	*file, *file_red;
+	char	*include;
+	boolean	dot;
+{
+	register struct inclist	*newfile;
+	register struct filepointer	*content;
+
+	/*
+	 * First decide what the pathname of this include file really is.
+	 */
+	newfile = inc_path(file->i_file, include, dot);
+	if (newfile == NULL) {
+		if (failOK)
+		    return;
+		if (file != file_red)
+			warning("%s (reading %s, line %d): ",
+				file_red->i_file, file->i_file, filep->f_line);
+		else
+			warning("%s, line %d: ", file->i_file, filep->f_line);
+		warning1("cannot find include file \"%s\"\n", include);
+		show_where_not = TRUE;
+		newfile = inc_path(file->i_file, include, dot);
+		show_where_not = FALSE;
+	}
+
+	if (newfile) {
+		included_by(file, newfile);
+		if (!newfile->i_searched) {
+			newfile->i_searched = TRUE;
+			content = getfile(newfile->i_file);
+			find_includes(content, newfile, file_red, 0, failOK);
+			freefile(content);
+		}
+	}
+}
+
+recursive_pr_include(head, file, base)
+	register struct inclist	*head;
+	register char	*file, *base;
+{
+	register int	i;
+
+	if (head->i_marked)
+		return;
+	head->i_marked = TRUE;
+	if (head->i_file != file)
+		pr(head, file, base);
+	for (i=0; i<head->i_listlen; i++)
+		recursive_pr_include(head->i_list[ i ], file, base);
+}
+
+pr(ip, file, base)
+	register struct inclist  *ip;
+	char	*file, *base;
+{
+	static char	*lastfile;
+	register int	len, i;
+	char	buf[ BUFSIZ ];
+#ifdef WIN32
+	char *transfile = TranslateFileNameD2U(ip->i_file,0);
+#else
+	char *transfile = ip->i_file;
+#endif
+
+	printed = TRUE;
+	if (file != lastfile) {
+		lastfile = file;
+		sprintf(buf, "%s%s%s %s.d: %s", objprefix, base, objsuffix,
+			base, transfile);
+	}
+	else {
+		sprintf(buf, " \\\n %s", transfile);
+	}
+	fwrite(buf, strlen(buf), 1, stdout);
+
+	/*
+	 * If verbose is set, then print out what this file includes.
+	 */
+	if (! verbose || ip->i_list == NULL || ip->i_notified)
+		return;
+	ip->i_notified = TRUE;
+	lastfile = NULL;
+	printf("\n# %s includes:", transfile);
+	for (i=0; i<ip->i_listlen; i++)
+		printf("\n#\t%s", ip->i_list[ i ]->i_incstring);
+#ifdef WIN32
+	free(transfile);
+#endif
+}
diff --git a/vncpasswd/Makefile.in b/vncpasswd/Makefile.in
new file mode 100644
index 0000000..fb625e0
--- /dev/null
+++ b/vncpasswd/Makefile.in
@@ -0,0 +1,18 @@
+
+SRCS = vncpasswd.cxx
+
+OBJS = vncpasswd.o
+
+program = vncpasswd
+
+DEP_LIBS = ../rfb/librfb.a # ../network/libnetwork.a ../rdr/librdr.a
+
+DIR_CPPFLAGS = -I$(top_srcdir)
+
+all:: $(program)
+
+$(program): $(OBJS) $(DEP_LIBS)
+	rm -f $(program)
+	$(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(DEP_LIBS) $(LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncpasswd/vncpasswd.cxx b/vncpasswd/vncpasswd.cxx
new file mode 100644
index 0000000..c8dd777
--- /dev/null
+++ b/vncpasswd/vncpasswd.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <rfb/vncAuth.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+char* prog;
+
+static void usage()
+{
+  fprintf(stderr,"usage: %s [file]\n",prog);
+  exit(1);
+}
+
+int main(int argc, char** argv)
+{
+  prog = argv[0];
+
+  char* fname = 0;
+
+  for (int i = 1; i < argc; i++) {
+    if (strcmp(argv[i], "-q") == 0) { // allowed for backwards compatibility
+    } else if (argv[i][0] == '-') {
+      usage();
+    } else if (!fname) {
+      fname = argv[i];
+    } else {
+      usage();
+    }
+  }
+
+  if (!fname) {
+    if (!getenv("HOME")) {
+      fprintf(stderr,"HOME is not set\n");
+      exit(1);
+    }
+    fname = new char[strlen(getenv("HOME")) + 20];
+    sprintf(fname, "%s/.vnc", getenv("HOME"));
+    mkdir(fname, 0777);
+    sprintf(fname, "%s/.vnc/passwd", getenv("HOME"));
+  }
+
+  while (true) {
+    char* passwd = getpass("Password: ");
+    if (!passwd) {
+      perror("getpass error");
+      exit(1);
+    }   
+    if (strlen(passwd) < 6) {
+      if (strlen(passwd) == 0) {
+        fprintf(stderr,"Password not changed\n");
+        exit(1);
+      }
+      fprintf(stderr,"Password must be at least 6 characters - try again\n");
+      continue;
+    }
+
+    if (strlen(passwd) > 8)
+      passwd[8] = '\0';
+
+    CharArray passwdCopy(strDup(passwd));
+
+    passwd = getpass("Verify: ");
+    if (!passwd) {
+      perror("getpass error");
+      exit(1);
+    }   
+    if (strlen(passwd) > 8)
+      passwd[8] = '\0';
+
+    if (strcmp(passwdCopy.buf, passwd) != 0) {
+      fprintf(stderr,"Passwords don't match - try again\n");
+      continue;
+    }
+
+    FILE* fp = fopen(fname,"w");
+    if (!fp) {
+      fprintf(stderr,"Couldn't open %s for writing\n",fname);
+      exit(1);
+    }
+    chmod(fname, S_IRUSR|S_IWUSR);
+
+    vncAuthObfuscatePasswd(passwd);
+
+    if (fwrite(passwd, 8, 1, fp) != 1) {
+      fprintf(stderr,"Writing to %s failed\n",fname);
+      exit(1);
+    }
+
+    fclose(fp);
+
+    for (unsigned int i = 0; i < strlen(passwd); i++)
+      passwd[i] = passwdCopy.buf[i] = 0;
+
+    return 0;
+  }
+}
diff --git a/vncpasswd/vncpasswd.man b/vncpasswd/vncpasswd.man
new file mode 100644
index 0000000..ab5e761
--- /dev/null
+++ b/vncpasswd/vncpasswd.man
@@ -0,0 +1,42 @@
+.TH vncpasswd 1 "29 July 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncpasswd \- change a VNC password
+.SH SYNOPSIS
+.B vncpasswd
+.RI [ passwd-file ]
+.SH DESCRIPTION
+.B vncpasswd
+allows you to set the password used to access VNC desktops.  It stores an
+obfuscated version of the password in the given file (default
+$HOME/.vnc/passwd).  The \fBvncserver\fP script runs \fBvncpasswd\fP the first
+time you start a VNC desktop, and invokes \fBXvnc\fP with the appropriate
+\fB\-rfbauth\fP option.  \fBvncviewer\fP can also be given a password file to
+use via the \fB\-passwd\fP option.
+
+The password must be at least six characters long, and only the first eight
+characters are significant.  Note that the stored password is \fBnot\fP
+encrypted securely - anyone who has access to this file can trivially find out
+the plaintext password, so \fBvncpasswd\fP always sets appropriate permissions
+(read and write only by the owner).  However, when accessing a VNC desktop a
+challenge-response mechanism is used over the wire making it hard for anyone to
+crack the password simply by snooping on the network.
+
+.SH FILES
+.TP
+$HOME/.vnc/passwd
+Default location of the VNC password file.
+
+.SH SEE ALSO
+.BR vncviewer (1),
+.BR vncserver (1),
+.BR Xvnc (1)
+.BR vncconfig (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/vncserver b/vncserver
new file mode 100755
index 0000000..19333cb
--- /dev/null
+++ b/vncserver
@@ -0,0 +1,570 @@
+#!/usr/bin/perl
+#
+#  Copyright (C) 2002-2003 RealVNC Ltd.
+#  Copyright (C) 1999 AT&T Laboratories Cambridge.  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; either 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.
+#
+
+#
+# vncserver - wrapper script to start an X VNC server.
+#
+
+#
+# First make sure we're operating in a sane environment.
+#
+
+&SanityCheck();
+
+#
+# Global variables.  You may want to configure some of these for your site.
+#
+
+$geometry = "1024x768";
+$depth = 16;
+$vncJavaFiles = (((-d "/usr/share/vnc/classes") && "/usr/share/vnc/classes") ||
+                 ((-d "/usr/local/vnc/classes") && "/usr/local/vnc/classes"));
+$vncUserDir = "$ENV{HOME}/.vnc";
+$xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
+
+$defaultXStartup
+    = ("#!/bin/sh\n\n".
+       "[ -r \$HOME/.Xresources ] && xrdb \$HOME/.Xresources\n".
+       "xsetroot -solid grey\n".
+       "vncconfig -iconic &\n".
+       "xterm -geometry 80x24+10+10 -ls -title \"\$VNCDESKTOP Desktop\" &\n".
+       "twm &\n");
+
+chop($host = `uname -n`);
+
+
+# Check command line options
+
+&ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
+	      "-help",0,"-h",0,"--help",0);
+
+&Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
+
+&Kill() if ($opt{'-kill'});
+
+# Uncomment this line if you want default geometry, depth and pixelformat
+# to match the current X display:
+# &GetXDisplayDefaults();
+
+if ($opt{'-geometry'}) {
+    $geometry = $opt{'-geometry'};
+}
+if ($opt{'-depth'}) {
+    $depth = $opt{'-depth'};
+    $pixelformat = "";
+}
+if ($opt{'-pixelformat'}) {
+    $pixelformat = $opt{'-pixelformat'};
+}
+
+&CheckGeometryAndDepth();
+
+
+# Create the user's vnc directory if necessary.
+
+if (!(-e $vncUserDir)) {
+    if (!mkdir($vncUserDir,0755)) {
+	die "$prog: Could not create $vncUserDir.\n";
+    }
+}
+    
+# Make sure the user has a password.
+
+($z,$z,$mode) = stat("$vncUserDir/passwd");
+if (!(-e "$vncUserDir/passwd") || ($mode & 077)) {
+    warn "\nYou will require a password to access your desktops.\n\n";
+    system("vncpasswd -q $vncUserDir/passwd"); 
+    if (($? >> 8) != 0) {
+	exit 1;
+    }
+}
+
+# Find display number.
+
+if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) {
+    $displayNumber = $1;
+    shift(@ARGV);
+    if (!&CheckDisplayNumber($displayNumber)) {
+	die "A VNC server is already running as :$displayNumber\n";
+    }
+} elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/)) {
+    &Usage();
+} else {
+    $displayNumber = &GetDisplayNumber();
+}
+
+$vncPort = 5900 + $displayNumber;
+
+$desktopLog = "$vncUserDir/$host:$displayNumber.log";
+unlink($desktopLog);
+
+# Make an X server cookie - use as the seed the sum of the current time, our
+# PID and part of the encrypted form of the password.  Ideally we'd use
+# /dev/urandom, but that's only available on Linux.
+
+srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
+$cookie = "";
+for (1..16) {
+    $cookie .= sprintf("%02x", int(rand(256)) % 256);
+}
+    
+system("xauth -f $xauthorityFile add $host:$displayNumber . $cookie");
+system("xauth -f $xauthorityFile add $host/unix:$displayNumber . $cookie"); 
+
+if ($opt{'-name'}) {
+    $desktopName = $opt{'-name'};
+} else {
+    $desktopName = "$host:$displayNumber ($ENV{USER})";
+}
+
+# Now start the X VNC Server
+
+$cmd = "Xvnc :$displayNumber";
+$cmd .= " -desktop " . &quotedString($desktopName);
+$cmd .= " -httpd $vncJavaFiles" if ($vncJavaFiles);
+$cmd .= " -auth $xauthorityFile";
+$cmd .= " -geometry $geometry" if ($geometry);
+$cmd .= " -depth $depth" if ($depth);
+$cmd .= " -pixelformat $pixelformat" if ($pixelformat);
+$cmd .= " -rfbwait 30000";
+$cmd .= " -rfbauth $vncUserDir/passwd";
+$cmd .= " -rfbport $vncPort";
+$cmd .= " -pn";
+
+# Add font path and color database stuff here, e.g.:
+#
+# $cmd .= " -fp /usr/lib/X11/fonts/misc/,/usr/lib/X11/fonts/75dpi/";
+# $cmd .= " -co /usr/lib/X11/rgb";
+#
+
+foreach $arg (@ARGV) {
+    $cmd .= " " . &quotedString($arg);
+}
+$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
+
+# Run $cmd and record the process ID.
+
+$pidFile = "$vncUserDir/$host:$displayNumber.pid";
+system("$cmd & echo \$! >$pidFile");
+
+# Give Xvnc a chance to start up
+
+sleep(3); 
+
+warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
+
+# Create the user's xstartup script if necessary.
+
+if (!(-e "$vncUserDir/xstartup")) {
+    warn "Creating default startup script $vncUserDir/xstartup\n";
+    open(XSTARTUP, ">$vncUserDir/xstartup");
+    print XSTARTUP $defaultXStartup;
+    close(XSTARTUP);
+    chmod 0755, "$vncUserDir/xstartup";
+}
+
+# Run the X startup script.
+
+warn "Starting applications specified in $vncUserDir/xstartup\n";
+warn "Log file is $desktopLog\n\n";
+
+# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
+# TCP (DISPLAY=host:n)
+
+if (-e "/tmp/.X11-unix/X$displayNumber" ||
+    -e "/usr/spool/sockets/X11/$displayNumber")
+{
+    $ENV{DISPLAY}= ":$displayNumber";
+} else {
+    $ENV{DISPLAY}= "$host:$displayNumber";
+}
+$ENV{VNCDESKTOP}= $desktopName;
+
+system("$vncUserDir/xstartup >> " . &quotedString($desktopLog) . " 2>&1 &");
+
+exit;
+
+
+###############################################################################
+#
+# CheckGeometryAndDepth simply makes sure that the geometry and depth values
+# are sensible.
+#
+
+sub CheckGeometryAndDepth
+{
+    if ($geometry =~ /^(\d+)x(\d+)$/) {
+	$width = $1; $height = $2;
+
+	if (($width<1) || ($height<1)) {
+	    die "$prog: geometry $geometry is invalid\n";
+	}
+
+	while (($width % 4)!=0) {
+	    $width = $width + 1;
+	}
+
+	while (($height % 2)!=0) {
+	    $height = $height + 1;
+	}
+
+	$geometry = "${width}x$height";
+    } else {
+	die "$prog: geometry $geometry is invalid\n";
+    }
+
+    if (($depth < 8) || ($depth > 32)) {
+	die "Depth must be between 8 and 32\n";
+    }
+}
+
+
+#
+# GetDisplayNumber gets the lowest available display number.  A display number
+# n is taken if something is listening on the VNC server port (5900+n) or the
+# X server port (6000+n).
+#
+
+sub GetDisplayNumber
+{
+    foreach $n (1..99) {
+	if (&CheckDisplayNumber($n)) {
+	    return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
+	}
+    }
+    
+    die "$prog: no free display number on $host.\n";
+}
+
+
+#
+# CheckDisplayNumber checks if the given display number is available.  A
+# display number n is taken if something is listening on the VNC server port
+# (5900+n) or the X server port (6000+n).
+#
+
+sub CheckDisplayNumber
+{
+    local ($n) = @_;
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    if (-e "/tmp/.X$n-lock") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    if (-e "/tmp/.X11-unix/X$n") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    if (-e "/usr/spool/sockets/X11/$n") {
+	warn("\nWarning: $host:$n is taken because of ".
+             "/usr/spool/sockets/X11/$n\n");
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    return 1;
+}
+
+
+#
+# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
+# format of the current X display being used.  If successful, it sets the
+# options as appropriate so that the X VNC server will use the same settings
+# (minus an allowance for window manager decorations on the geometry).  Using
+# the same depth and pixel format means that the VNC server won't have to
+# translate pixels when the desktop is being viewed on this X display (for
+# TrueColor displays anyway).
+#
+
+sub GetXDisplayDefaults
+{
+    local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
+	   $red, $green, $blue);
+
+    $wmDecorationWidth = 4;	# a guess at typical size for window manager
+    $wmDecorationHeight = 24;	# decoration size
+
+    return if (!defined($ENV{DISPLAY}));
+
+    @lines = `xdpyinfo 2>/dev/null`;
+
+    return if ($? != 0);
+
+    @matchlines = grep(/dimensions/, @lines);
+    if (@matchlines) {
+	($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
+
+	$width -= $wmDecorationWidth;
+	$height -= $wmDecorationHeight;
+
+	$geometry = "${width}x$height";
+    }
+
+    @matchlines = grep(/default visual id/, @lines);
+    if (@matchlines) {
+	($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
+
+	for ($i = 0; $i < @lines; $i++) {
+	    if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
+		if (($lines[$i+1] !~ /TrueColor/) ||
+		    ($lines[$i+2] !~ /depth/) ||
+		    ($lines[$i+4] !~ /red, green, blue masks/))
+		{
+		    return;
+		}
+		last;
+	    }
+	}
+
+	return if ($i >= @lines);
+
+	($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
+	($red,$green,$blue)
+	    = ($lines[$i+4]
+	       =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
+
+	$red = hex($red);
+	$green = hex($green);
+	$blue = hex($blue);
+
+	if ($red > $blue) {
+	    $red = int(log($red) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($blue) / log(2));
+	    $blue = int(log($blue) / log(2)) + 1;
+	    $pixelformat = "rgb$red$green$blue";
+	} else {
+	    $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($red) / log(2));
+	    $red = int(log($red) / log(2)) + 1;
+	    $pixelformat = "bgr$blue$green$red";
+	}
+    }
+}
+
+
+#
+# quotedString returns a string which yields the original string when parsed
+# by a shell.
+#
+
+sub quotedString
+{
+    local ($in) = @_;
+
+    $in =~ s/\'/\'\"\'\"\'/g;
+
+    return "'$in'";
+}
+
+
+#
+# removeSlashes turns slashes into underscores for use as a file name.
+#
+
+sub removeSlashes
+{
+    local ($in) = @_;
+
+    $in =~ s|/|_|g;
+
+    return "$in";
+}
+
+
+#
+# Usage
+#
+
+sub Usage
+{
+    die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
+	"                 [-geometry <width>x<height>]\n".
+	"                 [-pixelformat rgbNNN|bgrNNN]\n".
+	"                 <Xvnc-options>...\n\n".
+	"       $prog -kill <X-display>\n\n");
+}
+
+
+#
+# Kill
+#
+
+sub Kill
+{
+    $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
+
+    if ($opt{'-kill'} =~ /^:\d+$/) {
+	$pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
+    } else {
+	if ($opt{'-kill'} !~ /^$host:/) {
+	    die "\nCan't tell if $opt{'-kill'} is on $host\n".
+		"Use -kill :<number> instead\n\n";
+	}
+	$pidFile = "$vncUserDir/$opt{'-kill'}.pid";
+    }
+
+    if (! -r $pidFile) {
+	die "\nCan't find file $pidFile\n".
+	    "You'll have to kill the Xvnc process manually\n\n";
+    }
+
+    $SIG{'HUP'} = 'IGNORE';
+    chop($pid = `cat $pidFile`);
+    warn "Killing Xvnc process ID $pid\n";
+    system("kill $pid");
+    unlink $pidFile;
+    exit;
+}
+
+
+#
+# ParseOptions takes a list of possible options and a boolean indicating
+# whether the option has a value following, and sets up an associative array
+# %opt of the values of the options given on the command line. It removes all
+# the arguments it uses from @ARGV and returns them in @optArgs.
+#
+
+sub ParseOptions
+{
+    local (@optval) = @_;
+    local ($opt, @opts, %valFollows, @newargs);
+
+    while (@optval) {
+	$opt = shift(@optval);
+	push(@opts,$opt);
+	$valFollows{$opt} = shift(@optval);
+    }
+
+    @optArgs = ();
+    %opt = ();
+
+    arg: while (defined($arg = shift(@ARGV))) {
+	foreach $opt (@opts) {
+	    if ($arg eq $opt) {
+		push(@optArgs, $arg);
+		if ($valFollows{$opt}) {
+		    if (@ARGV == 0) {
+			&Usage();
+		    }
+		    $opt{$opt} = shift(@ARGV);
+		    push(@optArgs, $opt{$opt});
+		} else {
+		    $opt{$opt} = 1;
+		}
+		next arg;
+	    }
+	}
+	push(@newargs,$arg);
+    }
+
+    @ARGV = @newargs;
+}
+
+
+#
+# Routine to make sure we're operating in a sane environment.
+#
+
+sub SanityCheck
+{
+    local ($cmd);
+
+    #
+    # Get the program name
+    #
+
+    ($prog) = ($0 =~ m|([^/]+)$|);
+
+    #
+    # Check we have all the commands we'll need on the path.
+    #
+
+ cmd:
+    foreach $cmd ("uname","xauth","Xvnc","vncpasswd") {
+	for (split(/:/,$ENV{PATH})) {
+	    if (-x "$_/$cmd") {
+		next cmd;
+	    }
+	}
+	die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+    }
+
+    #
+    # Check the HOME environment variable is set
+    #
+
+    if (!defined($ENV{HOME})) {
+	die "$prog: The HOME environment variable is not set.\n";
+    }
+    chdir($ENV{HOME});
+
+    #
+    # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
+    # eval, and if it fails we try 'require "sys/socket.ph"'.  If this fails,
+    # we just guess at the values.  If you find perl moaning here, just
+    # hard-code the values of AF_INET and SOCK_STREAM.  You can find these out
+    # for your platform by looking in /usr/include/sys/socket.h and related
+    # files.
+    #
+
+    chop($os = `uname`);
+    chop($osrev = `uname -r`);
+
+    eval 'use Socket';
+    if ($@) {
+	eval 'require "sys/socket.ph"';
+	if ($@) {
+	    if (($os eq "SunOS") && ($osrev !~ /^4/)) {
+		$AF_INET = 2;
+		$SOCK_STREAM = 2;
+	    } else {
+		$AF_INET = 2;
+		$SOCK_STREAM = 1;
+	    }
+	} else {
+	    $AF_INET = &AF_INET;
+	    $SOCK_STREAM = &SOCK_STREAM;
+	}
+    } else {
+	$AF_INET = &AF_INET;
+	$SOCK_STREAM = &SOCK_STREAM;
+    }
+}
diff --git a/vncserver.man b/vncserver.man
new file mode 100644
index 0000000..cd243ff
--- /dev/null
+++ b/vncserver.man
@@ -0,0 +1,120 @@
+.TH vncserver 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncserver \- start or stop a VNC server
+.SH SYNOPSIS
+.B vncserver
+.RI [: display# ]
+.RB [ \-name
+.IR desktop-name ]
+.RB [ \-geometry
+.IR width x height ]
+.RB [ \-depth
+.IR depth ]
+.RB [ \-pixelformat
+.IR format ]
+.RI [ Xvnc-options... ]
+.br
+.BI "vncserver \-kill :" display#
+.SH DESCRIPTION
+.B vncserver
+is used to start a VNC (Virtual Network Computing) desktop.
+.B vncserver
+is a Perl script which simplifies the process of starting an Xvnc server.  It
+runs Xvnc with appropriate options and starts some X applications to be
+displayed in the VNC desktop.
+
+.B vncserver
+can be run with no options at all. In this case it will choose the first
+available display number (usually :1), start Xvnc as that display, and run a
+couple of basic applications to get you started. You can also specify the
+display number, in which case it will use that number if it is available and
+exit if not, eg:
+
+.RS
+vncserver :13
+.RE
+
+Editing the file $HOME/.vnc/xstartup allows you to change the applications run
+at startup (but note that this will not affect an existing desktop).
+
+.SH OPTIONS
+You can get a list of options by giving \fB\-h\fP as an option to vncserver.
+In addition to the options listed below, any unrecognised options will be
+passed to Xvnc - see the Xvnc man page, or "Xvnc \-help" for details.
+
+.TP
+.B \-name \fIdesktop-name\fP
+Each desktop has a name which may be displayed by the viewer. It defaults to
+"\fIhost\fP:\fIdisplay#\fP (\fIusername\fP)" but you can change it with this
+option.  It is passed in to the xstartup script via the $VNCDESKTOP environment
+variable, allowing you to run a different set of applications according to the
+name of the desktop.
+
+.TP
+.B \-geometry \fIwidth\fPx\fIheight\fP
+Specify the size of the desktop to be created. Default is 1024x768. 
+
+.TP
+.B \-depth \fIdepth\fP
+Specify the pixel depth in bits of the desktop to be created. Default is 16,
+other possible values are 8, 15 and 24 - anything else is likely to cause
+strange behaviour by applications.
+
+.TP
+.B \-pixelformat \fIformat\fP
+Specify pixel format for server to use (BGRnnn or RGBnnn).  The default for
+depth 8 is BGR233 (meaning the most significant two bits represent blue, the
+next three green, and the least significant three represent red), the default
+for depth 16 is RGB565 and for depth 24 is RGB888.
+
+.TP
+.B \-cc 3
+As an alternative to the default TrueColor visual, this allows you to run an
+Xvnc server with a PseudoColor visual (i.e. one which uses a colour map or
+palette), which can be useful for running some old X applications which only
+work on such a display.  Values other than 3 (PseudoColor) and 4 (TrueColor)
+for the \-cc option may result in strange behaviour, and PseudoColor desktops
+must be 8 bits deep.
+
+.TP
+.B \-kill :\fIdisplay#\fP
+This kills a VNC desktop previously started with vncserver.  It does this by
+killing the Xvnc process, whose process ID is stored in the file
+"$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid".  It actually ignores anything
+preceding a ":" in its argument.  This can be useful so you can write
+"vncserver \-kill $DISPLAY", for example at the end of your xstartup file after
+a particular application exits.
+
+.SH FILES
+Several VNC-related files are found in the directory $HOME/.vnc:
+.TP
+$HOME/.vnc/xstartup
+A shell script specifying X applications to be run when a VNC desktop is
+started.  If it doesn't exist, vncserver will create a new one which runs a
+couple of basic applications.
+.TP
+$HOME/.vnc/passwd
+The VNC password file.
+.TP
+$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log
+The log file for Xvnc and applications started in xstartup.
+.TP
+$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid
+Identifies the Xvnc process ID, used by the
+.B \-kill
+option.
+
+.SH SEE ALSO
+.BR vncviewer (1),
+.BR vncpasswd (1),
+.BR vncconfig (1),
+.BR Xvnc (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx
new file mode 100644
index 0000000..09ed1fe
--- /dev/null
+++ b/vncviewer/CViewManager.cxx
@@ -0,0 +1,252 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <winsock2.h>
+#include <vncviewer/CViewManager.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/ConnectionDialog.h>
+#include <vncviewer/ConnectingDialog.h>
+#include <rfb/Hostname.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+#include <rfb/vncAuth.h>
+#include <rdr/HexInStream.h>
+#include <network/TcpSocket.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CViewManager");
+
+
+// -=- Custom thread class used internally
+
+class CViewThread : public Thread {
+public:
+  CViewThread(network::Socket* s, CViewManager& cvm);
+  CViewThread(const char* conninfo, CViewManager& cvm, bool infoIsConfigFile);
+  virtual ~CViewThread();
+
+  virtual void run();
+protected:
+  void setSocket(network::Socket* sock);
+
+  network::Socket* sock;
+  CharArray hostname;
+  CViewManager& manager;
+
+  bool useConfigFile;
+};
+
+
+CViewThread::CViewThread(network::Socket* s, CViewManager& cvm)
+: Thread("CView"), sock(s), manager(cvm) {
+  setDeleteAfterRun();
+}
+
+CViewThread::CViewThread(const char* h, CViewManager& cvm, bool hIsConfigFile)
+: Thread("CView"), sock(0), manager(cvm), useConfigFile(hIsConfigFile) {
+  setDeleteAfterRun();
+  if (h) hostname.buf = strDup(h);
+}
+
+
+CViewThread::~CViewThread() {
+  vlog.debug("~CViewThread");
+  manager.remThread(this);
+  delete sock;
+}
+
+
+void CViewThread::run() {
+  try {
+    CView view;
+    view.setManager(&manager);
+
+    if (!sock) {
+      try {
+        // If the hostname is actually a config filename then read it
+        if (useConfigFile) {
+          CharArray filename = hostname.takeBuf();
+          CViewOptions options;
+          options.readFromFile(filename.buf);
+          if (options.host.buf)
+            hostname.buf = strDup(options.host.buf);
+          view.applyOptions(options);
+        }
+
+        // If there is no hostname then present the connection
+        // dialog
+        if (!hostname.buf) {
+          ConnectionDialog conn(&view);
+          if (!conn.showDialog())
+            return;
+          hostname.buf = strDup(conn.hostname.buf);
+
+          // *** hack - Tell the view object the hostname
+          CViewOptions opt(view.getOptions());
+          opt.setHost(hostname.buf);
+          view.applyOptions(opt);
+        }
+
+        // Parse the host name & port
+        CharArray host;
+        int port;
+        getHostAndPort(hostname.buf, &host.buf, &port);
+
+        // Attempt to connect
+        ConnectingDialog dlg;
+        // this is a nasty hack to get round a Win2K and later "feature" which
+        // puts your second window in the background unless the first window
+        // you put up actually gets some input.  Just generate a fake shift
+        // event, which seems to do the trick.
+        keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), 0, 0);
+        keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), KEYEVENTF_KEYUP, 0);
+        sock = new network::TcpSocket(host.buf, port);
+      } catch(rdr::Exception& e) {
+        vlog.error("unable to connect to %s (%s)", hostname, e.str());
+        MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
+        return;
+      }
+
+      // Try to add the caller to the MRU
+      MRU::addToMRU(hostname.buf);
+    }
+
+    view.initialise(sock);
+    try {
+      view.postQuitOnDestroy(true);
+      while (true) {
+        // - processMsg is designed to be callable in response to select().
+        //   As a result, it can be called when FdInStream data is available,
+        //   BUT there may be no actual RFB data available.  This is the case
+        //   for example when reading data over an encrypted stream - an
+        //   entire block must be read from the FdInStream before any data
+        //   becomes available through the top-level encrypted stream.
+        //   Since we are using blockCallback and not doing a select() here,
+        //   we simply check() for some data on the top-level RFB stream.
+        //   This ensures that processMsg will only be called when there is
+        //   actually something to do.  In the meantime, blockCallback()
+        //   will be called, keeping the user interface responsive.
+        view.getInStream()->check(1,1);
+        view.processMsg();
+      }
+    } catch (CView::QuitMessage& e) {
+      // - Cope silently with WM_QUIT messages
+      vlog.debug("QuitMessage received (wParam=%d)", e.wParam);
+    } catch (rdr::EndOfStream& e) {
+      // - Copy silently with disconnection if in NORMAL state
+      if (view.state() == CConnection::RFBSTATE_NORMAL)
+        vlog.debug(e.str());
+      else {
+        view.postQuitOnDestroy(false);
+        throw rfb::Exception("server closed connection unexpectedly");
+      }
+    } catch (rdr::Exception&) {
+      // - We MUST do this, otherwise ~CView will cause a
+      //   PostQuitMessage and any MessageBox call will quit immediately.
+      view.postQuitOnDestroy(false);
+      throw;
+    }
+  } catch(rdr::Exception& e) {
+    // - Something went wrong - display the error
+    vlog.error("error: %s", e.str());
+    MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
+  }
+}
+
+
+// -=- CViewManager itself
+
+CViewManager::CViewManager()
+: MsgWindow(_T("CViewManager")), threadsSig(threadsMutex),
+  mainThread(Thread::self()) {
+}
+
+CViewManager::~CViewManager() {
+  while (!socks.empty()) {
+    network::SocketListener* sock = socks.front();
+    delete sock;
+    socks.pop_front();
+  }
+  awaitEmpty();
+}
+
+
+void CViewManager::awaitEmpty() {
+  Lock l(threadsMutex);
+  while (!threads.empty()) {
+    threadsSig.wait(true);
+  }
+}
+
+
+void CViewManager::addThread(Thread* t) {
+  Lock l(threadsMutex);
+  threads.push_front(t);
+}
+
+void CViewManager::remThread(Thread* t) {
+  Lock l(threadsMutex);
+  threads.remove(t);
+  threadsSig.signal();
+
+  // If there are no listening sockets then post a quit message when the
+  // last client disconnects
+  if (socks.empty())
+    PostThreadMessage(mainThread->getThreadId(), WM_QUIT, 0, 0);
+}
+
+
+bool CViewManager::addClient(const char* hostinfo, bool isConfigFile) {
+  CViewThread* thread = new CViewThread(hostinfo, *this, isConfigFile);
+  addThread(thread);
+  thread->start();
+  return true;
+}
+
+bool CViewManager::addListener(network::SocketListener* sock) {
+  socks.push_back(sock);
+  WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_ACCEPT);
+  return true;
+}
+
+bool CViewManager::addDefaultTCPListener(int port) {
+  return addListener(new network::TcpListener(port));
+}
+
+
+LRESULT CViewManager::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+  case WM_USER:
+    std::list<network::SocketListener*>::iterator i;
+    for (i=socks.begin(); i!=socks.end(); i++) {
+      if (wParam == (*i)->getFd()) {
+        network::Socket* new_sock = (*i)->accept();
+        CharArray connname;
+        connname.buf = new_sock->getPeerEndpoint();
+        vlog.debug("accepted connection: %s", connname);
+        CViewThread* thread = new CViewThread(new_sock, *this);
+        addThread(thread);
+        thread->start();
+        break;
+      }
+    }
+    break;
+  }
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
diff --git a/vncviewer/CViewManager.h b/vncviewer/CViewManager.h
new file mode 100644
index 0000000..3d11dd9
--- /dev/null
+++ b/vncviewer/CViewManager.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CViewManager.h
+
+// Creates and manages threads to run CView instances.
+
+#ifndef __RFB_WIN32_CVIEW_MANAGER_H__
+#define __RFB_WIN32_CVIEW_MANAGER_H__
+
+#include <list>
+#include <network/Socket.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CViewManager : public MsgWindow {
+    public:
+      CViewManager();
+      ~CViewManager();
+
+      void awaitEmpty();
+
+      void addThread(Thread* t);
+      void remThread(Thread* t);
+
+      bool addClient(const char* hostinfo, bool isConfigFile=false);
+
+      bool addListener(network::SocketListener* sock);
+      bool addDefaultTCPListener(int port);
+
+      LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+    protected:
+      std::list<network::SocketListener*> socks;
+      std::list<Thread*> threads;
+      Mutex threadsMutex;
+      Condition threadsSig;
+      Thread* mainThread;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/CViewOptions.cxx b/vncviewer/CViewOptions.cxx
new file mode 100644
index 0000000..089c4c4
--- /dev/null
+++ b/vncviewer/CViewOptions.cxx
@@ -0,0 +1,364 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <vncviewer/CViewOptions.h>
+#include <rfb/Configuration.h>
+#include <rfb/encodings.h>
+#include <rfb/vncAuth.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Registry.h>
+#include <rdr/HexInStream.h>
+#include <rdr/HexOutStream.h>
+#include <stdlib.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true);
+static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true);
+
+static BoolParameter fullColour("FullColour",
+                         "Use full colour (default is to use low colour "
+                         "unless auto select decides the link is fast enough)",
+                         false);
+static IntParameter lowColourLevel("LowColourLevel",
+                         "Colour level to use on slow connections. "
+                         "0 = Very Low (8 colours), 1 = Low (64 colours), 2 = Medium (256 colours)",
+                         1);
+static BoolParameter fullScreen("FullScreen",
+                         "Use the whole display to show the remote desktop."
+                         "(Press F8 to access the viewer menu)",
+                         false);
+static StringParameter preferredEncoding("PreferredEncoding",
+                         "Preferred graphical encoding to use - overridden by AutoSelect if set. "
+                         "(ZRLE, Hextile or Raw)", "ZRLE");
+
+static BoolParameter autoSelect("AutoSelect", "Auto select pixel format and encoding", true);
+static BoolParameter sharedConnection("Shared",
+                         "Allow existing connections to the server to continue."
+                         "(Default is to disconnect all other clients)",
+                         false);
+
+static BoolParameter sendPtrEvents("SendPointerEvents",
+                         "Send pointer (mouse) events to the server.", true);
+static BoolParameter sendKeyEvents("SendKeyEvents",
+                         "Send key presses (and releases) to the server.", true);
+
+static BoolParameter clientCutText("ClientCutText",
+                         "Send clipboard changes to the server.", true);
+static BoolParameter serverCutText("ServerCutText",
+                         "Accept clipboard changes from the server.", true);
+
+static BoolParameter protocol3_3("Protocol3.3",
+                         "Only use protocol version 3.3", false);
+
+static IntParameter ptrEventInterval("PointerEventInterval",
+                         "The interval to delay between sending one pointer event "
+                         "and the next.", 0);
+static BoolParameter emulate3("Emulate3",
+                         "Emulate middle mouse button when left and right buttons "
+                         "are used simulatenously.", false);
+
+static BoolParameter acceptBell("AcceptBell",
+                         "Produce a system beep when requested to by the server.",
+                         true);
+
+static StringParameter monitor("Monitor", "The monitor to open the VNC Viewer window on, if available.", "");
+static StringParameter menuKey("MenuKey", "The key which brings up the popup menu", "F8");
+
+
+CViewOptions::CViewOptions()
+: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
+autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
+shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents),
+preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
+protocol3_3(::protocol3_3), acceptBell(::acceptBell), lowColourLevel(::lowColourLevel),
+pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData())
+{
+  CharArray encodingName(::preferredEncoding.getData());
+  preferredEncoding = encodingNum(encodingName.buf);
+  setMenuKey(CharArray(::menuKey.getData()).buf);
+}
+
+
+void CViewOptions::readFromFile(const char* filename) {
+  FILE* f = fopen(filename, "r");
+  if (!f)
+    throw rdr::Exception("Failed to read configuration file");
+
+  try { 
+    char line[4096];
+    CharArray section;
+
+    CharArray hostTmp;
+    int portTmp = 0;
+
+    while (!feof(f)) {
+      // Read the next line
+      if (!fgets(line, sizeof(line), f)) {
+        if (feof(f))
+          break;
+        throw rdr::SystemException("fgets", ferror(f));
+      }
+      int len=strlen(line);
+      if (line[len-1] == '\n') {
+        line[len-1] = 0;
+        len--;
+      }
+
+      // Process the line
+      if (line[0] == ';') {
+        // Comment
+      } else if (line[0] == '[') {
+        // Entering a new section
+        if (!strSplit(&line[1], ']', &section.buf, 0))
+          throw rdr::Exception("bad Section");
+      } else {
+        // Reading an option
+        CharArray name;
+        CharArray value;
+        if (!strSplit(line, '=', &name.buf, &value.buf))
+          throw rdr::Exception("bad Name/Value pair");
+
+        if (stricmp(section.buf, "Connection") == 0) {
+          if (stricmp(name.buf, "Host") == 0) {
+            hostTmp.replaceBuf(value.takeBuf());
+          } else if (stricmp(name.buf, "Port") == 0) {
+            portTmp = atoi(value.buf);
+          } else if (stricmp(name.buf, "UserName") == 0) {
+            userName.replaceBuf(value.takeBuf());
+          } else if (stricmp(name.buf, "Password") == 0) {
+            int len = 0;
+            CharArray obfuscated;
+            rdr::HexInStream::hexStrToBin(value.buf, &obfuscated.buf, &len);
+            if (len == 8) {
+              password.replaceBuf(new char[9]);
+              memcpy(password.buf, obfuscated.buf, 8);
+              vncAuthUnobfuscatePasswd(password.buf);
+              password.buf[8] = 0;
+            }
+          }
+        } else if (stricmp(section.buf, "Options") == 0) {
+            // V4 options
+          if (stricmp(name.buf, "UseLocalCursor") == 0) {
+            useLocalCursor = atoi(value.buf);
+          } else if (stricmp(name.buf, "UseDesktopResize") == 0) {
+            useDesktopResize = atoi(value.buf);
+          } else if (stricmp(name.buf, "FullScreen") == 0) {
+            fullScreen = atoi(value.buf);
+          } else if (stricmp(name.buf, "FullColour") == 0) {
+            fullColour = atoi(value.buf);
+          } else if (stricmp(name.buf, "LowColourLevel") == 0) {
+            lowColourLevel = atoi(value.buf);
+          } else if (stricmp(name.buf, "PreferredEncoding") == 0) {
+            preferredEncoding = encodingNum(value.buf);
+          } else if ((stricmp(name.buf, "AutoDetect") == 0) ||
+                     (stricmp(name.buf, "AutoSelect") == 0)) {
+            autoSelect = atoi(value.buf);
+          } else if (stricmp(name.buf, "Shared") == 0) {
+            shared = atoi(value.buf);
+          } else if (stricmp(name.buf, "SendPtrEvents") == 0) {
+            sendPtrEvents = atoi(value.buf);
+          } else if (stricmp(name.buf, "SendKeyEvents") == 0) {
+            sendKeyEvents = atoi(value.buf);
+          } else if (stricmp(name.buf, "SendCutText") == 0) {
+            clientCutText = atoi(value.buf);
+          } else if (stricmp(name.buf, "AcceptCutText") == 0) {
+            serverCutText = atoi(value.buf);
+          } else if (stricmp(name.buf, "Emulate3") == 0) {
+            emulate3 = atoi(value.buf);
+          } else if (stricmp(name.buf, "PointerEventInterval") == 0) {
+            pointerEventInterval = atoi(value.buf);
+          } else if (stricmp(name.buf, "Monitor") == 0) {
+            monitor.replaceBuf(value.takeBuf());
+          } else if (stricmp(name.buf, "MenuKey") == 0) {
+            setMenuKey(value.buf);
+
+            // Legacy options
+          } else if (stricmp(name.buf, "Preferred_Encoding") == 0) {
+            preferredEncoding = atoi(value.buf);
+          } else if (stricmp(name.buf, "8bit") == 0) {
+            fullColour = !atoi(value.buf);
+          } else if (stricmp(name.buf, "FullScreen") == 0) {
+            fullScreen = atoi(value.buf);
+          } else if (stricmp(name.buf, "ViewOnly") == 0) {
+            sendPtrEvents = sendKeyEvents = !atoi(value.buf);
+          } else if (stricmp(name.buf, "DisableClipboard") == 0) {
+            clientCutText = serverCutText = !atoi(value.buf);
+          }
+        }
+      }
+    }
+    fclose(f); f=0;
+
+    // Process the Host and Port
+    if (hostTmp.buf) {
+      int hostLen = strlen(hostTmp.buf) + 2 + 17;
+      host.replaceBuf(new char[hostLen]);
+      strCopy(host.buf, hostTmp.buf, hostLen);
+      if (portTmp) {
+        strncat(host.buf, "::", hostLen-1);
+        char tmp[16];
+        sprintf(tmp, "%d", portTmp);
+        strncat(host.buf, tmp, hostLen-1);
+      }
+    }
+
+    setConfigFileName(filename);
+  } catch (rdr::Exception&) {
+    if (f) fclose(f);
+    throw;
+  }
+}
+
+void CViewOptions::writeToFile(const char* filename) {
+  FILE* f = fopen(filename, "w");
+  if (!f)
+    throw rdr::Exception("Failed to write configuration file");
+
+  try {
+    // - Split server into host and port and save
+    fprintf(f, "[Connection]\n");
+
+    fprintf(f, "Host=%s\n", host.buf);
+    if (userName.buf)
+      fprintf(f, "UserName=%s\n", userName.buf);
+    if (password.buf) {
+      // - Warn the user before saving the password
+      if (MsgBox(0, _T("Do you want to include the VNC Password in this configuration file?\n")
+                    _T("Storing the password is more convenient but poses a security risk."),
+                    MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES) {
+        char obfuscated[9];
+        memset(obfuscated, 0, sizeof(obfuscated));
+        strCopy(obfuscated, password.buf, sizeof(obfuscated));
+        vncAuthObfuscatePasswd(obfuscated);
+        CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfuscated, 8);
+        fprintf(f, "Password=%s\n", obfuscatedHex.buf);
+      }
+    }
+
+    // - Save the other options
+    fprintf(f, "[Options]\n");
+
+    fprintf(f, "UseLocalCursor=%d\n", (int)useLocalCursor);
+    fprintf(f, "UseDesktopResize=%d\n", (int)useDesktopResize);
+    fprintf(f, "FullScreen=%d\n", (int)fullScreen);
+    fprintf(f, "FullColour=%d\n", (int)fullColour);
+    fprintf(f, "LowColourLevel=%d\n", lowColourLevel);
+    fprintf(f, "PreferredEncoding=%s\n", encodingName(preferredEncoding));
+    fprintf(f, "AutoSelect=%d\n", (int)autoSelect);
+    fprintf(f, "Shared=%d\n", (int)shared);
+    fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
+    fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
+    fprintf(f, "SendCutText=%d\n", (int)clientCutText);
+    fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
+    fprintf(f, "Emulate3=%d\n", (int)emulate3);
+    fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval);
+    if (monitor.buf)
+      fprintf(f, "Monitor=%s\n", monitor.buf);
+    fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf);
+    fclose(f); f=0;
+
+    setConfigFileName(filename);
+  } catch (rdr::Exception&) {
+    if (f) fclose(f);
+    throw;
+  }
+}
+
+
+void CViewOptions::writeDefaults() {
+  RegKey key;
+  key.createKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCviewer4"));
+  key.setBool(_T("UseLocalCursor"), useLocalCursor);
+  key.setBool(_T("UseDesktopResize"), useDesktopResize);
+  key.setBool(_T("FullScreen"), fullScreen);
+  key.setBool(_T("FullColour"), fullColour);
+  key.setInt(_T("LowColourLevel"), lowColourLevel);
+  key.setString(_T("PreferredEncoding"), TStr(encodingName(preferredEncoding)));
+  key.setBool(_T("AutoSelect"), autoSelect);
+  key.setBool(_T("Shared"), shared);
+  key.setBool(_T("SendPointerEvents"), sendPtrEvents);
+  key.setBool(_T("SendKeyEvents"), sendKeyEvents);
+  key.setBool(_T("ClientCutText"), clientCutText);
+  key.setBool(_T("ServerCutText"), serverCutText);
+  key.setBool(_T("Protocol3.3"), protocol3_3);
+  key.setBool(_T("AcceptBell"), acceptBell);
+  key.setBool(_T("Emulate3"), emulate3);
+  key.setInt(_T("PointerEventInterval"), pointerEventInterval);
+  if (monitor.buf)
+    key.setString(_T("Monitor"), TStr(monitor.buf));
+  key.setString(_T("MenuKey"), TCharArray(menuKeyName()).buf);
+}
+
+
+void CViewOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));}
+void CViewOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));}
+void CViewOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));}
+void CViewOptions::setHost(const char* h) {host.replaceBuf(strDup(h));}
+void CViewOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));}
+
+void CViewOptions::setMenuKey(const char* keyName) {
+  if (!keyName[0]) {
+    menuKey = 0;
+  } else {
+    menuKey = VK_F8;
+    if (keyName[0] == 'F') {
+      UINT fKey = atoi(&keyName[1]);
+      if (fKey >= 1 && fKey <= 12)
+        menuKey = fKey-1 + VK_F1;
+    }
+  }
+}
+char* CViewOptions::menuKeyName() {
+  int fNum = (menuKey-VK_F1)+1;
+  if (fNum<1 || fNum>12)
+    return strDup("");
+  CharArray menuKeyStr(4);
+  sprintf(menuKeyStr.buf, "F%d", fNum);
+  return menuKeyStr.takeBuf();
+}
+
+
+CViewOptions& CViewOptions::operator=(const CViewOptions& o) {
+  useLocalCursor = o.useLocalCursor;
+  useDesktopResize = o.useDesktopResize;
+  fullScreen = o.fullScreen;
+  fullColour = o.fullColour;
+  lowColourLevel = o.lowColourLevel;
+  preferredEncoding = o.preferredEncoding;
+  autoSelect = o.autoSelect;
+  shared = o.shared;
+  sendPtrEvents = o.sendPtrEvents;
+  sendKeyEvents = o.sendKeyEvents;
+  clientCutText = o.clientCutText;
+  serverCutText = o.serverCutText;
+  emulate3 = o.emulate3;
+  pointerEventInterval = o.pointerEventInterval;
+  protocol3_3 = o.protocol3_3;
+  acceptBell = o.acceptBell;
+  setUserName(o.userName.buf);
+  setPassword(o.password.buf);
+  setConfigFileName(o.configFileName.buf);
+  setHost(o.host.buf);
+  setMonitor(o.monitor.buf);
+  menuKey = o.menuKey;
+  return *this;
+}
\ No newline at end of file
diff --git a/vncviewer/CViewOptions.h b/vncviewer/CViewOptions.h
new file mode 100644
index 0000000..9120bde
--- /dev/null
+++ b/vncviewer/CViewOptions.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CViewOptions.h
+
+// Definition of the CViewOptions class, responsible for storing the
+// current & requested VNCviewer options.
+
+#ifndef __RFB_WIN32_CVIEW_OPTIONS_H__
+#define __RFB_WIN32_CVIEW_OPTIONS_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    //
+    // -=- Options structure.  Each viewer option has a corresponding
+    //     entry in CViewOptions.  The viewer options are set by calling
+    //     CView::applyOptions(...)
+    //     The CViewOptions structure automatically picks up the default
+    //     value of each option from the Configuration system
+    //     The readFromFile and writeFromFile methods can be used to load
+    //     and save VNC configuration files.  readFromFile is backwards
+    //     compatible with 3.3 releases, while writeToFile is not.
+
+    class CViewOptions {
+    public:
+      CViewOptions();
+      CViewOptions(const CViewOptions& o) {operator=(o);}
+      CViewOptions& operator=(const CViewOptions& o);
+      void readFromFile(const char* filename_);
+      void writeToFile(const char* filename_);
+      void writeDefaults();
+      bool useLocalCursor;
+      bool useDesktopResize;
+      bool fullScreen;
+      bool fullColour;
+      int lowColourLevel;
+      int preferredEncoding;
+      bool autoSelect;
+      bool shared;
+      bool sendPtrEvents;
+      bool sendKeyEvents;
+      bool clientCutText;
+      bool serverCutText;
+      bool emulate3;
+      int pointerEventInterval;
+      bool protocol3_3;
+      bool acceptBell;
+      CharArray userName;
+      void setUserName(const char* user);
+      CharArray password;
+      void setPassword(const char* pwd);
+      CharArray configFileName;
+      void setConfigFileName(const char* cfn);
+      CharArray host;
+      void setHost(const char* h);
+      CharArray monitor;
+      void setMonitor(const char* m);
+      unsigned int menuKey;
+      void setMenuKey(const char* keyName);
+      char* menuKeyName();
+    };
+
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/ConnectingDialog.h b/vncviewer/ConnectingDialog.h
new file mode 100644
index 0000000..b146ced
--- /dev/null
+++ b/vncviewer/ConnectingDialog.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- ConnectingDialog.h
+
+// Dialog to indicate to the user that the viewer is attempting to make an
+// outgoing connection.
+
+#ifndef __RFB_WIN32_CONNECTING_DLG_H__
+#define __RFB_WIN32_CONNECTING_DLG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/Threading.h>
+#include <vncviewer/resource.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
+	    switch (uMsg) {
+	    case WM_INITDIALOG:
+		    {
+			    SetWindowLong(hwnd, GWL_USERDATA, lParam);
+			    return TRUE;
+		    }
+	    case WM_COMMAND:
+		    switch (LOWORD(wParam)) {
+		    case IDCANCEL:
+          network::Socket* sock = (network::Socket*) GetWindowLong(hwnd, GWL_USERDATA);
+          sock->shutdown();
+			    EndDialog(hwnd, FALSE);
+			    return TRUE;
+		    }
+		    break;
+	    case WM_DESTROY:
+		    EndDialog(hwnd, TRUE);
+		    return TRUE;
+	    }
+	    return 0;
+    }
+
+    // *** hacky bit - should use async connect so dialog behaves properly
+    class ConnectingDialog : public Thread {
+    public:
+      ConnectingDialog() : Thread("ConnectingDialog") {
+        dialog = 0;
+        active = true;
+        start();
+      }
+      virtual ~ConnectingDialog() {
+        // *** join() required here because otherwise ~Thread calls Thread::join()
+        join();
+      }
+      virtual void run() {
+        dialog = CreateDialogParam(GetModuleHandle(0),
+          MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, 0);
+        ShowWindow(dialog, SW_SHOW);
+        MSG msg;
+        while (active && GetMessage(&msg, dialog, 0, 0)) {
+          DispatchMessage(&msg);
+        }
+        DestroyWindow(dialog);
+      }
+      virtual Thread* join() {
+        active = false;
+        if (dialog)
+          PostMessage(dialog, WM_QUIT, 0, 0);
+        return Thread::join();
+      }
+    protected:
+      HWND dialog;
+      bool active;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncviewer/ConnectionDialog.cxx b/vncviewer/ConnectionDialog.cxx
new file mode 100644
index 0000000..c083444
--- /dev/null
+++ b/vncviewer/ConnectionDialog.cxx
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#include <vncviewer/ConnectionDialog.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/resource.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+ConnectionDialog::ConnectionDialog(CView* view_) : Dialog(GetModuleHandle(0)), view(view_) {
+}
+
+
+bool ConnectionDialog::showDialog() {
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_DLG));
+}
+
+void ConnectionDialog::initDialog() {
+  HWND box = GetDlgItem(handle, IDC_SERVER_EDIT);
+
+  std::list<char*> mru = MRU::getEntries();
+  std::list<char*>::iterator i;
+
+  // Locate the combo-box
+  // NB: TCharArray converts the supplied char* and assumes ownership!
+  for (i=mru.begin(); i!=mru.end(); i++) {
+    int index = SendMessage(box, CB_ADDSTRING, 0, (LPARAM)TCharArray(*i).buf);
+  }
+
+  // Select the first item in the list
+  SendMessage(box, CB_SETCURSEL, 0, 0);
+}
+
+
+bool ConnectionDialog::onOk() {
+  delete [] hostname.buf;
+  hostname.buf = 0;
+  hostname.buf = getItemString(IDC_SERVER_EDIT);
+  return hostname.buf[0] != 0;
+}
+
+bool ConnectionDialog::onCommand(int id, int cmd) {
+  switch (id) {
+  case IDC_ABOUT:
+    AboutDialog::instance.showDialog();
+    return true;
+  case IDC_OPTIONS:
+    view->optionsDialog.showDialog(view);
+    return true;
+  };
+  return false;
+}
diff --git a/vncviewer/ConnectionDialog.h b/vncviewer/ConnectionDialog.h
new file mode 100644
index 0000000..554c86f
--- /dev/null
+++ b/vncviewer/ConnectionDialog.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- ConnectionDialog.h
+
+// Connection dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_CONN_DIALOG_H__
+#define __RFB_WIN32_CONN_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <vncviewer/MRU.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView;
+
+    class ConnectionDialog : Dialog {
+    public:
+      ConnectionDialog(CView* view);
+      virtual bool showDialog();
+      virtual void initDialog();
+      virtual bool onOk();
+      virtual bool onCommand(int id, int cmd);
+      TCharArray hostname;
+    protected:
+      CView* view;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/InfoDialog.cxx b/vncviewer/InfoDialog.cxx
new file mode 100644
index 0000000..0d2313a
--- /dev/null
+++ b/vncviewer/InfoDialog.cxx
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <vncviewer/InfoDialog.h>
+#include <vncviewer/resource.h>
+#include <vncviewer/CView.h>
+#include <rfb/secTypes.h>
+#include <rfb/encodings.h>
+#include <rfb/CSecurity.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Info");
+
+
+bool InfoDialog::showDialog(CView* vw) {
+  view = vw;
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO));
+}
+
+void InfoDialog::initDialog() {
+  char buf[256];
+
+  setItemString(IDC_INFO_NAME, TStr(view->cp.name()));
+
+  setItemString(IDC_INFO_HOST, TCharArray(view->sock->getPeerAddress()).buf);
+
+  Rect bufRect = view->buffer->getRect();
+  sprintf(buf, "%dx%d", bufRect.width(), bufRect.height());
+  setItemString(IDC_INFO_SIZE, TStr(buf));
+
+  view->cp.pf().print(buf, 256);
+  setItemString(IDC_INFO_PF, TStr(buf));
+
+  view->serverDefaultPF.print(buf, 256);
+  setItemString(IDC_INFO_DEF_PF, TStr(buf));
+
+  setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(view->getOptions().preferredEncoding)));
+  setItemString(IDC_LAST_ENCODING, TStr(encodingName(view->lastUsedEncoding())));
+
+  sprintf(buf, "%d kbits/s", view->sock->inStream().kbitsPerSecond());
+  setItemString(IDC_INFO_LINESPEED, TStr(buf));
+
+  sprintf(buf, "%d.%d", view->cp.majorVersion, view->cp.minorVersion);
+  setItemString(IDC_INFO_VERSION, TStr(buf));
+
+  int secType = view->getCurrentCSecurity()->getType();
+  setItemString(IDC_INFO_SECURITY, TStr(secTypeName(secType)));
+}
diff --git a/vncviewer/InfoDialog.h b/vncviewer/InfoDialog.h
new file mode 100644
index 0000000..7a64d38
--- /dev/null
+++ b/vncviewer/InfoDialog.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- InfoDialog.h
+
+// Info dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_INFO_DIALOG_H__
+#define __RFB_WIN32_INFO_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView;
+
+    class InfoDialog : Dialog {
+    public:
+      InfoDialog() : Dialog(GetModuleHandle(0)), view(0) {}
+      virtual bool showDialog(CView* vw);
+      virtual void initDialog();
+    protected:
+      CView* view;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/MRU.h b/vncviewer/MRU.h
new file mode 100644
index 0000000..9e99395
--- /dev/null
+++ b/vncviewer/MRU.h
@@ -0,0 +1,140 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __VIEWER_MRU_H__
+#define __VIEWER_MRU_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <list>
+#include <set>
+
+#include <rfb_win32/Registry.h>
+
+#include <rfb/util.h>
+#include <rdr/HexOutStream.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    namespace MRU {
+
+      static const RegKey RegRoot = HKEY_CURRENT_USER;
+      static const TCHAR* RegPath = _T("Software\\RealVNC\\VNCViewer4\\MRU");
+      static const int MaxMRUEntries = 256;
+      static const int MRUEntries = 10;
+
+      static std::list<char*> getEntries() {
+        std::list<char*> mru;
+
+        try {
+          RegKey key;
+          key.openKey(RegRoot, RegPath);
+
+          CharArray order;
+          int length;
+          key.getBinary(_T("Order"), (void**)&order.buf, &length);
+
+          for (int i=0; i<length; i++) {
+            TCharArray keyname = rdr::HexOutStream::binToHexStr(&order.buf[i], 1);
+            try {
+              TCharArray entry = key.getString(keyname.buf);
+              mru.push_back(strDup(entry.buf));
+            } catch (rdr::Exception) {
+            }
+          }
+        } catch (rdr::Exception) {
+        }
+
+        return mru;
+      }
+
+      static void addToMRU(const char* name) {
+        RegKey key;
+        key.createKey(RegRoot, RegPath);
+
+        BYTE keycode;
+        CharArray old_order;
+        char order[MaxMRUEntries];
+        int orderlen;
+        
+        try {
+          key.getBinary(_T("Order"), (void**)&old_order.buf, &orderlen);
+          if (orderlen)
+            memcpy(order, old_order.buf, orderlen);
+
+          std::set<int> ordercodes;
+          keycode = 0;
+          bool found = false;
+          for (int i=0; i<orderlen; i++) {
+            TCharArray keyname = rdr::HexOutStream::binToHexStr(&order[i], 1);
+            try {
+              TCharArray hostname = key.getString(keyname.buf);
+              if (strcmp(name, CStr(hostname.buf)) == 0) {
+                keycode = order[i];
+                found = true;
+                break;
+              }
+            } catch (rdr::Exception) {
+            }
+            ordercodes.insert(order[i]);
+          }
+
+          if (!found) {
+            if (orderlen <= MRUEntries) {
+              while (ordercodes.find(keycode) != ordercodes.end()) keycode++;
+            } else {
+              keycode = order[orderlen-1];
+              orderlen--;
+            }
+          }
+
+        } catch (rdr::Exception) {
+          keycode = 0;
+          orderlen = 0;
+        }
+
+        printf("keycode=%d\n", (int)keycode);
+
+        orderlen++;
+        int i, j=orderlen-1;
+        for (i=0; i<orderlen-1; i++) {
+          if (order[i] == keycode) {
+            j = i;
+            orderlen--;
+            break;
+          }
+        }
+        for (i=j; i>0; i--)
+          order[i] = order[i-1];
+        order[0] = keycode;
+
+        printf("selected %d\n", (int)keycode);
+
+        TCharArray keyname = rdr::HexOutStream::binToHexStr((char*)&keycode, 1);
+        key.setString(keyname.buf, TStr(name));
+        key.setBinary(_T("Order"), order, orderlen);
+      }
+
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
new file mode 100644
index 0000000..ab45f8c
--- /dev/null
+++ b/vncviewer/OptionsDialog.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#if (_WIN32_WINNT < 0x0400)
+#define _WIN32_WINNT 0x0400
+#endif
+#include <windows.h>
+#include <commdlg.h>
+
+#include <vncviewer/OptionsDialog.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/resource.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rfb/encodings.h>
+#include <rfb/CConnection.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Options");
+
+
+struct OptionsInfo {
+  CView* view;
+  CViewOptions options;
+};
+
+
+OptionsDialog rfb::win32::OptionsDialog::global;
+
+
+class VNCviewerOptions : public PropSheet {
+public:
+  VNCviewerOptions(OptionsInfo& info_, std::list<PropSheetPage*> pages)
+    : PropSheet(GetModuleHandle(0), 
+    info_.view ? _T("VNC Viewer Options") : _T("VNC Viewer Defaults"), pages),
+    info(info_), changed(false) {
+  }
+  ~VNCviewerOptions() {
+    if (changed) {
+      if (info.view)
+        // Apply the settings to the supplied session object
+        info.view->applyOptions(info.options);
+      else {
+        // Commit the settings to the user's registry area
+        info.options.writeDefaults();
+      }
+    }
+  }
+
+  void setChanged() {changed = true;}
+
+  bool changed;
+  OptionsInfo& info;
+};
+      
+
+class FormatPage : public PropSheetPage {
+public:
+  FormatPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FORMAT)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_ENCODING_AUTO, dlg->options.autoSelect);
+    setItemChecked(IDC_FORMAT_FULLCOLOUR, dlg->options.fullColour);
+    if (!dlg->options.fullColour) {
+      switch (dlg->options.lowColourLevel) {
+      case 0: setItemChecked(IDC_FORMAT_VERYLOWCOLOUR, true); break;
+      case 1: setItemChecked(IDC_FORMAT_LOWCOLOUR, true); break;
+      case 2: setItemChecked(IDC_FORMAT_MEDIUMCOLOUR, true); break;
+      }
+    }
+    switch (dlg->options.preferredEncoding) {
+    case encodingZRLE: setItemChecked(IDC_ENCODING_ZRLE, true); break;
+    case encodingHextile: setItemChecked(IDC_ENCODING_HEXTILE, true); break;
+    case encodingRaw: setItemChecked(IDC_ENCODING_RAW, true); break;
+    }
+    onCommand(IDC_ENCODING_AUTO, 0 /* ? */); // Force enableItem status to refresh
+  }
+  virtual bool onOk() {
+    dlg->options.autoSelect = isItemChecked(IDC_ENCODING_AUTO);
+    dlg->options.fullColour = isItemChecked(IDC_FORMAT_FULLCOLOUR);
+    if (isItemChecked(IDC_FORMAT_VERYLOWCOLOUR))
+      dlg->options.lowColourLevel = 0;
+    if (isItemChecked(IDC_FORMAT_LOWCOLOUR))
+      dlg->options.lowColourLevel = 1;
+    if (isItemChecked(IDC_FORMAT_MEDIUMCOLOUR))
+      dlg->options.lowColourLevel = 2;
+    dlg->options.preferredEncoding = encodingZRLE;
+    if (isItemChecked(IDC_ENCODING_HEXTILE))
+      dlg->options.preferredEncoding = encodingHextile;
+    if (isItemChecked(IDC_ENCODING_RAW))
+      dlg->options.preferredEncoding = encodingRaw;
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+  virtual bool onCommand(int id, int cmd) {
+    if (id == IDC_ENCODING_AUTO) {
+      bool ok = !isItemChecked(IDC_ENCODING_AUTO);
+      enableItem(IDC_ENCODING_ZRLE, ok);
+      enableItem(IDC_ENCODING_HEXTILE, ok);
+      enableItem(IDC_ENCODING_RAW, ok);
+      return true;
+    }
+    return false;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+class MiscPage : public PropSheetPage {
+public:
+  MiscPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MISC)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_CONN_SHARED, dlg->options.shared);
+    enableItem(IDC_CONN_SHARED, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
+    setItemChecked(IDC_FULL_SCREEN, dlg->options.fullScreen);
+    setItemChecked(IDC_LOCAL_CURSOR, dlg->options.useLocalCursor);
+    setItemChecked(IDC_DESKTOP_RESIZE, dlg->options.useDesktopResize);
+    enableItem(IDC_PROTOCOL_3_3, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
+    setItemChecked(IDC_PROTOCOL_3_3, dlg->options.protocol3_3);
+    setItemChecked(IDC_ACCEPT_BELL, dlg->options.acceptBell);
+  }
+  virtual bool onOk() {
+    dlg->options.shared = isItemChecked(IDC_CONN_SHARED);
+    dlg->options.fullScreen = isItemChecked(IDC_FULL_SCREEN);
+    dlg->options.useLocalCursor = isItemChecked(IDC_LOCAL_CURSOR);
+    dlg->options.useDesktopResize = isItemChecked(IDC_DESKTOP_RESIZE);
+    dlg->options.protocol3_3 = isItemChecked(IDC_PROTOCOL_3_3);
+    dlg->options.acceptBell = isItemChecked(IDC_ACCEPT_BELL);
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+class InputsPage : public PropSheetPage {
+public:
+  InputsPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_INPUTS)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_SEND_POINTER, dlg->options.sendPtrEvents);
+    setItemChecked(IDC_SEND_KEYS, dlg->options.sendKeyEvents);
+    setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText);
+    setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText);
+    setItemChecked(IDC_EMULATE3, dlg->options.emulate3);
+    setItemChecked(IDC_POINTER_INTERVAL, dlg->options.pointerEventInterval != 0);
+
+    // Populate the Menu Key tab
+    HWND menuKey = GetDlgItem(handle, IDC_MENU_KEY);
+    SendMessage(menuKey, CB_RESETCONTENT, 0, 0);
+    SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)_T("none"));
+    if (!dlg->options.menuKey)
+      SendMessage(menuKey, CB_SETCURSEL, 0, 0);
+    for (int i=0; i<12; i++) {
+      TCHAR buf[4];
+      _stprintf(buf, _T("F%d"), i+1);
+      int index = SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)buf);
+      if (i == (dlg->options.menuKey - VK_F1))
+        SendMessage(menuKey, CB_SETCURSEL, index, 0);
+    }
+  }
+  virtual bool onOk() {
+    dlg->options.sendPtrEvents = isItemChecked(IDC_SEND_POINTER);
+    dlg->options.sendKeyEvents = isItemChecked(IDC_SEND_KEYS);
+    dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT);
+    dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT);
+    dlg->options.emulate3 = isItemChecked(IDC_EMULATE3);
+    dlg->options.pointerEventInterval = 
+      isItemChecked(IDC_POINTER_INTERVAL) ? 200 : 0;
+
+    HWND mkHwnd = GetDlgItem(handle, IDC_MENU_KEY);
+    int index = SendMessage(mkHwnd, CB_GETCURSEL, 0, 0);
+    TCharArray keyName(SendMessage(mkHwnd, CB_GETLBTEXTLEN, index, 0)+1);
+    SendMessage(mkHwnd, CB_GETLBTEXT, index, (LPARAM)keyName.buf);
+    if (_tcscmp(keyName.buf, _T("none")) == 0)
+      dlg->options.setMenuKey("");
+    else
+      dlg->options.setMenuKey(CStr(keyName.buf));
+
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+
+class DefaultsPage : public PropSheetPage {
+public:
+  DefaultsPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DEFAULTS)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    enableItem(IDC_LOAD_CONFIG, dlg->options.configFileName.buf);
+    enableItem(IDC_SAVE_CONFIG, dlg->options.configFileName.buf);
+  }
+  virtual bool onCommand(int id, int cmd) {
+    HWND hwnd = dlg->view ? dlg->view->getHandle() : 0;
+    switch (id) {
+    case IDC_LOAD_DEFAULTS:
+      dlg->options = CViewOptions();
+      break;
+    case IDC_SAVE_DEFAULTS:
+      propSheet->commitPages();
+      dlg->options.writeDefaults();
+      break;
+    case IDC_LOAD_CONFIG:
+      dlg->options.readFromFile(dlg->options.configFileName.buf);
+      break;
+    case IDC_SAVE_CONFIG:
+      propSheet->commitPages();
+      dlg->options.writeToFile(dlg->options.configFileName.buf);
+      MsgBox(hwnd, _T("Options saved successfully"),
+             MB_OK | MB_ICONINFORMATION);
+      return 0;
+    case IDC_SAVE_CONFIG_AS:
+      propSheet->commitPages();
+      // Get a filename to save to
+      TCHAR newFilename[4096];
+      TCHAR currentDir[4096];
+      if (dlg->options.configFileName.buf)
+        _tcscpy(newFilename, TStr(dlg->options.configFileName.buf));
+      else
+        newFilename[0] = 0;
+      OPENFILENAME ofn;
+      memset(&ofn, 0, sizeof(ofn));
+#ifdef OPENFILENAME_SIZE_VERSION_400
+      ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+      ofn.lStructSize = sizeof(ofn);
+#endif
+      ofn.hwndOwner = hwnd;
+      ofn.lpstrFilter = _T("VNC Connection Options\000*.vnc\000");
+      ofn.lpstrFile = newFilename;
+      currentDir[0] = 0;
+      GetCurrentDirectory(4096, currentDir);
+      ofn.lpstrInitialDir = currentDir;
+      ofn.nMaxFile = 4096;
+      ofn.lpstrDefExt = _T(".vnc");
+      ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+      if (!GetSaveFileName(&ofn)) {
+        if (CommDlgExtendedError())
+          throw rdr::Exception("GetSaveFileName failed");
+        return 0;
+      }
+
+      // Save the Options
+      dlg->options.writeToFile(CStr(newFilename));
+      MsgBox(hwnd, _T("Options saved successfully"),
+             MB_OK | MB_ICONINFORMATION);
+      return 0;
+    };
+    propSheet->reInitPages();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+
+OptionsDialog::OptionsDialog() : visible(false) {
+}
+
+bool OptionsDialog::showDialog(CView* view, bool capture) {
+  if (visible) return false;
+  visible = true;
+
+  // Grab the current properties
+  OptionsInfo info;
+  if (view)
+    info.options = view->getOptions();
+  info.view = view;
+
+  // Build a list of pages to display
+  std::list<PropSheetPage*> pages;
+  FormatPage formatPage(&info); pages.push_back(&formatPage);
+  InputsPage inputsPage(&info); pages.push_back(&inputsPage);
+  MiscPage miscPage(&info); pages.push_back(&miscPage);
+  DefaultsPage defPage(&info); if (view) pages.push_back(&defPage);
+
+  // Show the property sheet
+  VNCviewerOptions dialog(info, pages);
+  dialog.showPropSheet(view ? view->getHandle() : 0, false, false, capture);
+
+  visible = false;
+  return dialog.changed;
+}
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
new file mode 100644
index 0000000..eec9b96
--- /dev/null
+++ b/vncviewer/OptionsDialog.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- OptionsDialog.h
+
+// Options dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_OPTIONS_DIALOG_H__
+#define __RFB_WIN32_OPTIONS_DIALOG_H__
+
+#include <vncviewer/CViewOptions.h>
+#include <rfb_win32/Dialog.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView;
+
+    class OptionsDialog {
+    public:
+      OptionsDialog();
+      virtual bool showDialog(CView* cfg, bool capture=false);
+
+      static OptionsDialog global;
+    protected:
+      bool visible;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/UserPasswdDialog.cxx b/vncviewer/UserPasswdDialog.cxx
new file mode 100644
index 0000000..8ab4ba4
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.cxx
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/resource.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), showUsername(false) {
+}
+
+
+void UserPasswdDialog::setCSecurity(const CSecurity* cs) {
+  description.replaceBuf(tstrDup(cs->description()));
+}
+
+bool UserPasswdDialog::showDialog() {
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_VNC_AUTH_DLG));
+}
+
+void UserPasswdDialog::initDialog() {
+  if (username.buf) {
+    setItemString(IDC_USERNAME, username.buf);
+    tstrFree(username.takeBuf());
+  }
+  if (password.buf) {
+    setItemString(IDC_PASSWORD, password.buf);
+    tstrFree(password.takeBuf());
+  }
+  if (!showUsername) {
+    setItemString(IDC_USERNAME, _T(""));
+    enableItem(IDC_USERNAME, false);
+  }
+  if (description.buf) {
+    TCharArray title(128);
+    GetWindowText(handle, title.buf, 128);
+    _tcsncat(title.buf, _T(" ["), 128);
+    _tcsncat(title.buf, description.buf, 128);
+    _tcsncat(title.buf, _T("]"), 128);
+    SetWindowText(handle, title.buf);
+  }
+}
+
+bool UserPasswdDialog::onOk() {
+	username.buf = getItemString(IDC_USERNAME);
+	password.buf = getItemString(IDC_PASSWORD);
+  return true;
+}
+
+
+bool UserPasswdDialog::getUserPasswd(char** user, char** passwd) {
+  bool result = false;
+  showUsername = user != 0;
+  if (user && *user)
+    username.buf = tstrDup(*user);
+  if (passwd && *passwd)
+    password.buf = tstrDup(*passwd);
+  if (showDialog()) {
+    if (user)
+      *user = strDup(username.buf);
+    *passwd = strDup(password.buf);
+    result = true;
+  }
+  tstrFree(username.takeBuf());
+  tstrFree(password.takeBuf());
+  return result;
+}
diff --git a/vncviewer/UserPasswdDialog.h b/vncviewer/UserPasswdDialog.h
new file mode 100644
index 0000000..998a49f
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- UserPasswdDialog.h
+
+// Username and password dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_USERPASSWD_DIALOG_H__
+#define __RFB_WIN32_USERPASSWD_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/CSecurity.h>
+#include <rfb/UserPasswdGetter.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class UserPasswdDialog : Dialog, public UserPasswdGetter {
+    public:
+      UserPasswdDialog();
+      virtual bool showDialog();
+      virtual void initDialog();
+      virtual bool onOk();
+      virtual bool getUserPasswd(char** user, char** passwd);
+      void setCSecurity(const CSecurity* cs);
+    protected:
+      TCharArray username;
+      TCharArray password;
+      bool showUsername;
+      TCharArray description;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/buildTime.cxx b/vncviewer/buildTime.cxx
new file mode 100644
index 0000000..bab2e13
--- /dev/null
+++ b/vncviewer/buildTime.cxx
@@ -0,0 +1 @@
+const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file
diff --git a/vncviewer/cursor1.cur b/vncviewer/cursor1.cur
new file mode 100644
index 0000000..20a713f
--- /dev/null
+++ b/vncviewer/cursor1.cur
Binary files differ
diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx
new file mode 100644
index 0000000..7d5653d
--- /dev/null
+++ b/vncviewer/cview.cxx
@@ -0,0 +1,1468 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#define WIN32_LEAN_AND_MEAN
+#if (_WIN32_WINNT < 0x0400)
+#define _WIN32_WINNT 0x0400
+#endif
+#include <windows.h>
+#include <winsock2.h>
+#include <tchar.h>
+#include <commctrl.h>
+
+#include <network/TcpSocket.h>
+
+#include <vncviewer/CView.h>
+#include <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/resource.h>
+
+#include <rfb/encodings.h>
+#include <rfb/secTypes.h>
+#include <rfb/CSecurityNone.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+
+#include <rfb_win32/WMShatter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+
+// - Statics & consts
+
+static LogWriter vlog("CView");
+
+const int IDM_FULLSCREEN = 1;
+const int IDM_SEND_MENU_KEY = 2;
+const int IDM_SEND_CAD = 3;
+const int IDM_ABOUT = 4;
+const int IDM_OPTIONS = 5;
+const int IDM_INFO = 6;
+const int IDM_NEWCONN = 7;
+const int IDM_REQUEST_REFRESH = 9;
+const int IDM_CTRL_KEY = 10;
+const int IDM_ALT_KEY = 11;
+
+const int TIMER_BUMPSCROLL = 1;
+const int TIMER_POINTER_INTERVAL = 2;
+const int TIMER_POINTER_3BUTTON = 3;
+
+
+IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
+                        "pixel data - a debugging feature", 0);
+
+
+//
+// -=- CViewClass
+
+//
+// Window class used as the basis for all CView instances
+//
+
+class CViewClass {
+public:
+  CViewClass();
+  ~CViewClass();
+  ATOM classAtom;
+  HINSTANCE instance;
+};
+
+LRESULT CALLBACK CViewProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+  LRESULT result;
+
+  // *** vlog.debug("CViewMsg %x->(%x, %x, %x)", wnd, msg, wParam, lParam);
+
+  if (msg == WM_CREATE)
+    SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+  else if (msg == WM_DESTROY)
+    SetWindowLong(wnd, GWL_USERDATA, 0);
+  CView* _this = (CView*) GetWindowLong(wnd, GWL_USERDATA);
+  if (!_this) {
+    vlog.info("null _this in %x, message %u", wnd, msg);
+    return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
+  }
+
+  try {
+    result = _this->processMessage(msg, wParam, lParam);
+  } catch (rdr::Exception& e) {
+    vlog.error("untrapped: %s", e.str());
+  }
+
+  return result;
+};
+
+HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
+HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); 
+
+CViewClass::CViewClass() : classAtom(0) {
+  WNDCLASS wndClass;
+  wndClass.style = 0;
+  wndClass.lpfnWndProc = CViewProc;
+  wndClass.cbClsExtra = 0;
+  wndClass.cbWndExtra = 0;
+  wndClass.hInstance = instance = GetModuleHandle(0);
+  wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
+  if (!wndClass.hIcon)
+    printf("unable to load icon:%ld", GetLastError());
+  wndClass.hCursor = NULL;
+  wndClass.hbrBackground = NULL;
+  wndClass.lpszMenuName = 0;
+  wndClass.lpszClassName = _T("rfb::win32::CViewClass");
+  classAtom = RegisterClass(&wndClass);
+  if (!classAtom) {
+    throw rdr::SystemException("unable to register CView window class", GetLastError());
+  }
+}
+
+CViewClass::~CViewClass() {
+  if (classAtom) {
+    UnregisterClass((const TCHAR*)classAtom, instance);
+  }
+}
+
+CViewClass baseClass;
+
+
+//
+// -=- CView instance implementation
+//
+
+RegKey CView::userConfigKey;
+
+
+CView::CView() 
+  : quit_on_destroy(false), buffer(0), sock(0), readyToRead(false),
+    client_size(0, 0, 16, 16), window_size(0, 0, 32, 32),
+    cursorVisible(false), cursorAvailable(false), cursorInBuffer(false),
+    systemCursorVisible(true), trackingMouseLeave(false),
+    hwnd(0), requestUpdate(false), has_focus(false), palette_changed(false),
+    sameMachine(false), encodingChange(false), formatChange(false),
+    lastUsedEncoding_(encodingRaw), fullScreenActive(false),
+    bumpScroll(false), manager(0) {
+
+  // Create the window
+  const TCHAR* name = _T("VNC Viewer 4.0b");
+  hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW,
+    0, 0, 10, 10, 0, 0, baseClass.instance, this);
+  if (!hwnd) {
+    throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+  }
+  vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), hwnd);
+
+  // Initialise the CPointer pointer handler
+  ptr.setHWND(getHandle());
+  ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
+  ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
+
+  // Initialise the bumpscroll timer
+  bumpScrollTimer.setHWND(getHandle());
+  bumpScrollTimer.setId(TIMER_BUMPSCROLL);
+
+  // Hook the clipboard
+  clipboard.setNotifier(this);
+
+  // Create the backing buffer
+  buffer = new win32::DIBSectionBuffer(getHandle());
+}
+
+CView::~CView() {
+  vlog.debug("~CView");
+  showSystemCursor();
+  if (hwnd) {
+    setVisible(false);
+    DestroyWindow(hwnd);
+    hwnd = 0;
+  }
+  delete buffer;
+  vlog.debug("~CView done");
+}
+
+bool CView::initialise(network::Socket* s) {
+  // Update the window menu
+  HMENU wndmenu = GetSystemMenu(hwnd, FALSE);
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
+  AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
+  AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
+  AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  if (manager) AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
+
+  // Set the server's name for MRU purposes
+  CharArray endpoint(s->getPeerEndpoint());
+  setServerName(endpoint.buf);
+  if (!options.host.buf)
+    options.setHost(endpoint.buf);
+
+  // Initialise the underlying CConnection
+  setStreams(&s->inStream(), &s->outStream());
+
+  // Enable processing of window messages while blocked on I/O
+  s->inStream().setBlockCallback(this);
+
+  // Initialise the viewer options
+  applyOptions(options);
+
+  // - Set which auth schemes we support
+  addSecType(secTypeNone);
+  addSecType(secTypeVncAuth);
+
+  initialiseProtocol();
+  WSAAsyncSelect(s->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
+  sock = s;
+
+  return true;
+}
+
+
+void
+CView::applyOptions(CViewOptions& opt) {
+  // *** CHANGE THIS TO USE CViewOptions::operator= ***
+
+  // - Take the username, password, config filename, and host spec
+  options.setUserName(opt.userName.buf);
+  options.setPassword(opt.password.buf);
+  options.setHost(opt.host.buf);
+  options.setConfigFileName(opt.configFileName.buf);
+  options.setMonitor(opt.monitor.buf);
+
+  // - Set optional features in ConnParams
+  encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
+    (options.useDesktopResize != opt.useDesktopResize));
+  cp.supportsLocalCursor = options.useLocalCursor = opt.useLocalCursor;
+  cp.supportsDesktopResize = options.useDesktopResize = opt.useDesktopResize;
+  if (cursorAvailable)
+    hideLocalCursor();
+  cursorAvailable = cursorAvailable && options.useLocalCursor;
+
+  // - Switch full-screen mode on/off
+  options.fullScreen = opt.fullScreen;
+  setFullscreen(options.fullScreen);
+
+  // - Handle format/encoding options
+  encodingChange |= (options.preferredEncoding != opt.preferredEncoding);
+  options.preferredEncoding = opt.preferredEncoding;
+
+  formatChange |= (options.fullColour != opt.fullColour);
+  options.fullColour = opt.fullColour;
+
+  if (!options.fullColour)
+    formatChange |= (options.lowColourLevel != opt.lowColourLevel);
+  options.lowColourLevel = opt.lowColourLevel;
+
+  options.autoSelect = opt.autoSelect;
+
+  // - Sharing
+  options.shared = opt.shared;
+  setShared(options.shared);
+
+  // - Inputs
+  options.sendPtrEvents = opt.sendPtrEvents;
+  options.sendKeyEvents = opt.sendKeyEvents;
+  options.clientCutText = opt.clientCutText;
+  options.serverCutText = opt.serverCutText;
+  options.emulate3 = opt.emulate3;
+  ptr.enableEmulate3(opt.emulate3);
+  options.pointerEventInterval = opt.pointerEventInterval;
+  ptr.enableInterval(opt.pointerEventInterval);
+  options.menuKey = opt.menuKey;
+
+  // - Protocol version override
+  options.protocol3_3 = opt.protocol3_3;
+  setProtocol3_3(options.protocol3_3);
+
+  // - Bell
+  options.acceptBell = opt.acceptBell;
+}
+
+void
+CView::setFullscreen(bool fs) {
+  // Set the menu fullscreen option tick
+  CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_FULLSCREEN,
+    (options.fullScreen ? MF_CHECKED : 0) | MF_BYCOMMAND);
+
+  // If the window is not visible then we ignore the request.
+  // setVisible() will call us to correct the full-screen state when
+  // the window is visible, to keep things consistent.
+  if (!IsWindowVisible(getHandle()))
+    return;
+
+  if (fs && !fullScreenActive) {
+    fullScreenActive = bumpScroll = true;
+
+    // Un-minimize the window if required
+    if (GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE)
+      ShowWindow(getHandle(), SW_RESTORE);
+
+    // Save the non-fullscreen window position
+    RECT wrect;
+    GetWindowRect(getHandle(), &wrect);
+    fullScreenOldRect = Rect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+
+    // Find the size of the display the window is on
+    MonitorInfo mi(getHandle());
+
+    // Set the window full-screen
+    DWORD flags = GetWindowLong(getHandle(), GWL_STYLE);
+    fullScreenOldFlags = flags;
+    flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
+    vlog.debug("flags=%x", flags);
+
+    SetWindowLong(getHandle(), GWL_STYLE, flags);
+    SetWindowPos(getHandle(), HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
+      mi.rcMonitor.right-mi.rcMonitor.left,
+      mi.rcMonitor.bottom-mi.rcMonitor.top,
+      SWP_FRAMECHANGED);
+  } else if (!fs && fullScreenActive) {
+    fullScreenActive = bumpScroll = false;
+
+    // Set the window non-fullscreen
+    SetWindowLong(getHandle(), GWL_STYLE, fullScreenOldFlags);
+    SetWindowPos(getHandle(), HWND_NOTOPMOST,
+      fullScreenOldRect.tl.x, fullScreenOldRect.tl.y,
+      fullScreenOldRect.width(), fullScreenOldRect.height(),
+      SWP_FRAMECHANGED);
+  }
+
+  // Adjust the viewport offset to cope with change in size between FS
+  // and previous window state.
+  setViewportOffset(scrolloffset);
+}
+
+
+bool CView::setViewportOffset(const Point& tl) {
+/* ***
+  Point np = Point(max(0, min(maxscrolloffset.x, tl.x)),
+    max(0, min(maxscrolloffset.y, tl.y)));
+    */
+  Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())),
+    max(0, min(tl.y, buffer->height()-client_size.height())));
+  Point delta = np.translate(scrolloffset.negate());
+  if (!np.equals(scrolloffset)) {
+    scrolloffset = np;
+    ScrollWindowEx(getHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
+    UpdateWindow(getHandle());
+    return true;
+  }
+  return false;
+}
+
+
+bool CView::processBumpScroll(const Point& pos)
+{
+  if (!bumpScroll) return false;
+  int bumpScrollPixels = 20;
+  bumpScrollDelta = Point();
+
+  if (pos.x == client_size.width()-1)
+    bumpScrollDelta.x = bumpScrollPixels;
+  else if (pos.x == 0)
+    bumpScrollDelta.x = -bumpScrollPixels;
+  if (pos.y == client_size.height()-1)
+    bumpScrollDelta.y = bumpScrollPixels;
+  else if (pos.y == 0)
+    bumpScrollDelta.y = -bumpScrollPixels;
+
+  if (bumpScrollDelta.x || bumpScrollDelta.y) {
+    if (bumpScrollTimer.isActive()) return true;
+    if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
+      bumpScrollTimer.start(25);
+      return true;
+    }
+  }
+
+  bumpScrollTimer.stop();
+  return false;
+}
+
+
+LRESULT
+CView::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+
+    // -=- Process standard window messages
+
+  case WM_DISPLAYCHANGE:
+    // Display has changed - use new pixel format
+    calculateFullColourPF();
+    break;
+
+  case WM_PAINT:
+    {
+      PAINTSTRUCT ps;
+      HDC paintDC = BeginPaint(getHandle(), &ps);
+      if (!paintDC)
+        throw SystemException("unable to BeginPaint", GetLastError());
+      Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
+
+      if (!pr.is_empty()) {
+
+        // Draw using the correct palette
+        PaletteSelector pSel(paintDC, windowPalette.getHandle());
+
+        if (buffer->bitmap) {
+          // Update the bitmap's palette
+          if (palette_changed) {
+            palette_changed = false;
+            buffer->refreshPalette();
+          }
+
+          // Get device context
+          BitmapDC bitmapDC(paintDC, buffer->bitmap);
+
+          // Blit the border if required
+          Rect bufpos = bufferToClient(buffer->getRect());
+          if (!pr.enclosed_by(bufpos)) {
+            vlog.debug("draw border");
+            HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
+            RECT r;
+            SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
+          }
+
+          // Do the blit
+          Point buf_pos = clientToBuffer(pr.tl);
+          if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
+            bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
+            throw SystemException("unable to BitBlt to window", GetLastError());
+
+        } else {
+          // Blit a load of black
+          if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
+            0, 0, 0, BLACKNESS))
+            throw SystemException("unable to BitBlt to blank window", GetLastError());
+        }
+      }
+
+      EndPaint(getHandle(), &ps);
+
+      // - Request the next update from the server, if required
+      requestNewUpdate();
+    }
+    return 0;
+
+    // -=- Palette management
+
+  case WM_PALETTECHANGED:
+    vlog.debug("WM_PALETTECHANGED");
+    if ((HWND)wParam == getHandle()) {
+      vlog.debug("ignoring");
+      break;
+    }
+  case WM_QUERYNEWPALETTE:
+    vlog.debug("re-selecting palette");
+    {
+      WindowDC wdc(getHandle());
+      PaletteSelector pSel(wdc, windowPalette.getHandle());
+      if (pSel.isRedrawRequired()) {
+        InvalidateRect(getHandle(), 0, FALSE);
+        UpdateWindow(getHandle());
+      }
+    }
+    return TRUE;
+
+    // -=- Window position
+
+    // Prevent the window from being resized to be too large if in normal mode.
+    // If maximized or fullscreen the allow oversized windows.
+
+  case WM_WINDOWPOSCHANGING:
+    {
+      WINDOWPOS* wpos = (WINDOWPOS*)lParam;
+      if (wpos->flags &  SWP_NOSIZE)
+        break;
+
+      // Work out how big the window should ideally be
+      DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
+      DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+      RECT r;
+      SetRect(&r, 0, 0, buffer->width(), buffer->height());
+      AdjustWindowRect(&r, style, FALSE);
+      Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+      if (current_style & WS_VSCROLL)
+        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
+      if (current_style & WS_HSCROLL)
+        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
+      RECT current;
+      GetWindowRect(getHandle(), &current);
+
+      // Ensure that the window isn't resized too large
+      // If the window is maximized or full-screen then any size is allowed
+      if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
+        if (wpos->cx > reqd_size.width()) {
+          wpos->cx = reqd_size.width();
+          wpos->x = current.left;
+        }
+        if (wpos->cy > reqd_size.height()) {
+          wpos->cy = reqd_size.height();
+          wpos->y = current.top;
+        }
+      }
+
+    }
+    break;
+
+    // Add scrollbars if required and update window size info we have cached.
+
+  case WM_SIZE:
+    {
+      Point old_offset = bufferToClient(Point(0, 0));
+
+      // Update the cached sizing information
+      RECT r;
+      GetWindowRect(getHandle(), &r);
+      window_size = Rect(r.left, r.top, r.right, r.bottom);
+      GetClientRect(getHandle(), &r);
+      client_size = Rect(r.left, r.top, r.right, r.bottom);
+
+      // Determine whether scrollbars are required
+      calculateScrollBars();
+
+      // Redraw if required
+      if (!old_offset.equals(bufferToClient(Point(0, 0))))
+        InvalidateRect(getHandle(), 0, TRUE);
+    }
+    break;
+
+  case WM_VSCROLL:
+  case WM_HSCROLL: 
+    {
+      Point delta;
+      int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
+
+      switch (LOWORD(wParam)) {
+      case SB_PAGEUP: newpos -= 50; break;
+      case SB_PAGEDOWN: newpos += 50; break;
+      case SB_LINEUP: newpos -= 5; break;
+      case SB_LINEDOWN: newpos += 5; break;
+      case SB_THUMBTRACK:
+      case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
+      default: vlog.info("received unknown scroll message");
+      };
+
+      if (msg == WM_HSCROLL)
+        setViewportOffset(Point(newpos, scrolloffset.y));
+      else
+        setViewportOffset(Point(scrolloffset.x, newpos));
+  
+      SCROLLINFO si;
+      si.cbSize = sizeof(si); 
+      si.fMask  = SIF_POS; 
+      si.nPos   = newpos; 
+      SetScrollInfo(getHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); 
+    }
+    break;
+
+    // -=- Bump-scrolling
+
+  case WM_TIMER:
+    switch (wParam) {
+    case TIMER_BUMPSCROLL:
+      if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
+        bumpScrollTimer.stop();
+      break;
+    case TIMER_POINTER_INTERVAL:
+    case TIMER_POINTER_3BUTTON:
+      try {
+        ptr.handleTimer(writer(), wParam);
+      } catch (rdr::Exception& e) {
+        close(e.str());
+      }
+      break;
+    }
+    break;
+
+    // -=- Cursor shape/visibility handling
+
+  case WM_SETCURSOR:
+    if (LOWORD(lParam) != HTCLIENT)
+      break;
+    SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
+    return TRUE;
+
+  case WM_MOUSELEAVE:
+    trackingMouseLeave = false;
+    cursorOutsideBuffer();
+    return 0;
+
+    // -=- Mouse input handling
+
+  case WM_MOUSEMOVE:
+  case WM_LBUTTONUP:
+  case WM_MBUTTONUP:
+  case WM_RBUTTONUP:
+  case WM_LBUTTONDOWN:
+  case WM_MBUTTONDOWN:
+  case WM_RBUTTONDOWN:
+  case WM_MOUSEWHEEL:
+    if (has_focus)
+    {
+      if (!trackingMouseLeave) {
+        TRACKMOUSEEVENT tme;
+        tme.cbSize = sizeof(TRACKMOUSEEVENT);
+        tme.dwFlags = TME_LEAVE;
+        tme.hwndTrack = hwnd;
+        _TrackMouseEvent(&tme);
+        trackingMouseLeave = true;
+      }
+      int mask = 0;
+      if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
+      if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
+      if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
+
+      if (msg == WM_MOUSEWHEEL) {
+        int delta = (short)HIWORD(wParam);
+        int repeats = (abs(delta)+119) / 120;
+        int wheelMask = (delta > 0) ? 8 : 16;
+        vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
+        for (int i=0; i<repeats; i++) {
+          writePointerEvent(oldpos.x, oldpos.y, mask | wheelMask);
+          writePointerEvent(oldpos.x, oldpos.y, mask);
+        }
+      } else {
+        Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
+        Point p = clientToBuffer(clientPos);
+
+        // If the mouse is not within the server buffer area, do nothing
+        cursorInBuffer = buffer->getRect().contains(p);
+        if (!cursorInBuffer) {
+          cursorOutsideBuffer();
+          break;
+        }
+
+        // If we're locally rendering the cursor then redraw it
+        if (cursorAvailable) {
+          // - Render the cursor!
+          if (!p.equals(cursorPos)) {
+            hideLocalCursor();
+            cursorPos = p;
+            showLocalCursor();
+            if (cursorVisible)
+              hideSystemCursor();
+          }
+        }
+
+        // If we are doing bump-scrolling then try that first...
+        if (processBumpScroll(clientPos))
+          break;
+
+        // Send a pointer event to the server
+        writePointerEvent(p.x, p.y, mask);
+        oldpos = p;
+      }
+    } else {
+      cursorOutsideBuffer();
+    }
+    break;
+
+    // -=- Track whether or not the window has focus
+
+  case WM_SETFOCUS:
+    has_focus = true;
+    break;
+  case WM_KILLFOCUS:
+    has_focus = false;
+    cursorOutsideBuffer();
+    // Restore the remote keys to consistent states
+    try {
+      kbd.releaseAllKeys(writer());
+    } catch (rdr::Exception& e) {
+      close(e.str());
+    }
+    break;
+
+    // -=- Handle the extra window menu items
+
+    // Process the items added to the system menu
+  case WM_SYSCOMMAND:
+
+    // - First check whether it's one of our messages
+    switch (wParam) {
+    case IDM_FULLSCREEN:
+      options.fullScreen = !options.fullScreen;
+      setFullscreen(options.fullScreen);
+      return 0;
+    case IDM_CTRL_KEY:
+      writeKeyEvent(VK_CONTROL, 0, !kbd.keyPressed(VK_CONTROL));
+      return 0;
+    case IDM_ALT_KEY:
+      writeKeyEvent(VK_MENU, 0, !kbd.keyPressed(VK_MENU));
+      return 0;
+    case IDM_SEND_MENU_KEY:
+      writeKeyEvent(options.menuKey, 0, true);
+      writeKeyEvent(options.menuKey, 0, false);
+      return 0;
+    case IDM_SEND_CAD:
+      writeKeyEvent(VK_CONTROL, 0, true);
+      writeKeyEvent(VK_MENU, 0, true);
+      writeKeyEvent(VK_DELETE, 0, true);
+      writeKeyEvent(VK_DELETE, 0, false);
+      writeKeyEvent(VK_MENU, 0, false);
+      writeKeyEvent(VK_CONTROL, 0, false);
+      return 0;
+    case IDM_REQUEST_REFRESH:
+      try {
+        writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
+        requestUpdate = false;
+      } catch (rdr::Exception& e) {
+        close(e.str());
+      }
+      return 0;
+    case IDM_NEWCONN:
+      manager->addClient(0);
+      return 0;
+    case IDM_OPTIONS:
+      // Update the monitor device name in the CViewOptions instance
+      {
+        MonitorInfo mi(getHandle());
+        options.setMonitor(mi.szDevice);
+        optionsDialog.showDialog(this);
+        return 0;
+      }
+    case IDM_INFO:
+      infoDialog.showDialog(this);
+      return 0;
+    case IDM_ABOUT:
+      AboutDialog::instance.showDialog();
+      return 0;
+    };
+
+    // - Not one of our messages, so process it as a system message
+    switch (wParam & 0xfff0) {
+
+      // When restored, ensure that full-screen mode is re-enabled if required.
+    case SC_RESTORE:
+      rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+      setFullscreen(options.fullScreen);
+      return 0;
+
+      // If we are maximized or minimized then that cancels full-screen mode.
+    case SC_MINIMIZE:
+    case SC_MAXIMIZE:
+      setFullscreen(false);
+      break;
+
+      // If the system menu is shown then make sure it's up to date
+    case SC_KEYMENU:
+    case SC_MOUSEMENU:
+      updateF8Menu(false);
+      break;
+
+    };
+    break;
+
+    // Treat all menu commands as system menu commands
+  case WM_COMMAND:
+    SendMessage(getHandle(), WM_SYSCOMMAND, wParam, lParam);
+    return 0;
+
+  case WM_MENUCHAR:
+    vlog.debug("menuchar");
+    break;
+
+    // -=- Handle keyboard input
+
+  case WM_KEYUP:
+  case WM_KEYDOWN:
+    // Hook the MenuKey to pop-up the window menu
+    if (options.menuKey && (wParam == options.menuKey)) {
+
+      bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
+      bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
+      bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
+      if (!(ctrlDown || altDown || shiftDown)) {
+
+        // If MenuKey is being released then pop-up the menu
+        if ((msg == WM_KEYDOWN)) {
+          // Make sure it's up to date
+          updateF8Menu(true);
+
+          // Show it under the pointer
+          POINT pt;
+          GetCursorPos(&pt);
+          cursorInBuffer = false;
+          TrackPopupMenu(GetSystemMenu(getHandle(), FALSE),
+            TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0);
+        }
+
+        // Ignore the MenuKey keypress for both press & release events
+        return 0;
+      }
+    }
+	case WM_SYSKEYDOWN:
+	case WM_SYSKEYUP:
+    writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
+    return 0;
+
+    // -=- Handle the window closing
+
+  case WM_CLOSE:
+    vlog.debug("WM_CLOSE %x", getHandle());
+    if (quit_on_destroy) {
+      vlog.debug("posting WM_QUIT");
+      PostQuitMessage(0);
+    } else {
+      vlog.debug("not posting WM_QUIT");
+    }
+    break;
+
+    // -=- Process incoming socket data
+
+  case WM_USER:
+    readyToRead = true;
+    break;
+
+  }
+
+  return rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+}
+
+void CView::blockCallback() {
+  // - An InStream has blocked on I/O while processing an RFB message
+  //   We re-enable socket event notifications, so we'll know when more
+  //   data is available, then we sit and dispatch window events until
+  //   the notification arrives.
+  readyToRead = false;
+  WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
+  MSG msg;
+  while (true) {
+    if (readyToRead) {
+      // - Network event notification.  Return control to I/O routine.
+      WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, 0);
+      return;
+    }
+
+    DWORD result = GetMessage(&msg, NULL, 0, 0);
+    if (result == 0) {
+      vlog.debug("WM_QUIT");
+      throw QuitMessage(msg.wParam);
+    } else if (result < 0) {
+      throw rdr::SystemException("GetMessage error", GetLastError());
+    }
+
+    // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
+    // call ToAscii() in CKeyboard::keyEvent().  ToAscii() stores dead key
+    // state from one call to the next, which would be messed up by calls to
+    // TranslateMessage() (actually it looks like TranslateMessage() calls
+    // ToAscii() internally).
+    DispatchMessage(&msg);
+  }
+}
+
+
+void
+CView::hideLocalCursor() {
+  // - Blit the cursor backing store over the cursor
+  // *** ALWAYS call this BEFORE changing buffer PF!!!
+  if (cursorVisible) {
+    cursorVisible = false;
+    buffer->imageRect(cursorBackingRect, cursorBacking.data);
+    invalidateBufferRect(cursorBackingRect);
+  }
+}
+
+void
+CView::showLocalCursor() {
+  if (cursorAvailable && !cursorVisible && cursorInBuffer) {
+    if (!cp.pf().equal(cursor.getPF()) ||
+      cursor.getRect().is_empty()) {
+      vlog.info("attempting to render invalid local cursor");
+      cursorAvailable = false;
+      showSystemCursor();
+      return;
+    }
+    cursorVisible = true;
+    
+    cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
+    cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
+    buffer->getImage(cursorBacking.data, cursorBackingRect);
+
+    renderLocalCursor();
+
+    invalidateBufferRect(cursorBackingRect);
+  }
+}
+
+void CView::cursorOutsideBuffer()
+{
+  cursorInBuffer = false;
+  hideLocalCursor();
+  showSystemCursor();
+}
+
+void
+CView::renderLocalCursor()
+{
+  Rect r = cursor.getRect();
+  r = r.translate(cursorPos).translate(cursor.hotspot.negate());
+  buffer->maskRect(r, cursor.data, cursor.mask.buf);
+}
+
+void
+CView::hideSystemCursor() {
+  if (systemCursorVisible) {
+    vlog.debug("hide system cursor");
+    systemCursorVisible = false;
+    ShowCursor(FALSE);
+  }
+}
+
+void
+CView::showSystemCursor() {
+  if (!systemCursorVisible) {
+    vlog.debug("show system cursor");
+    systemCursorVisible = true;
+    ShowCursor(TRUE);
+  }
+}
+
+
+bool
+CView::invalidateBufferRect(const Rect& crect) {
+  Rect rect = bufferToClient(crect);
+  if (rect.intersect(client_size).is_empty()) return false;
+  RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
+  InvalidateRect(getHandle(), &invalid, FALSE);
+  return true;
+}
+
+
+void
+CView::notifyClipboardChanged(const char* text, int len) {
+  if (!options.clientCutText) return;
+  if (state() != RFBSTATE_NORMAL) return;
+  try {
+    writer()->writeClientCutText(text, len);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+CSecurity* CView::getCSecurity(int secType)
+{
+  switch (secType) {
+  case secTypeNone:
+    return new CSecurityNone();
+  case secTypeVncAuth:
+    return new CSecurityVncAuth(this);
+  default:
+    throw Exception("Unsupported secType?");
+  }
+}
+
+
+void
+CView::setColourMapEntries(int first, int count, U16* rgbs) {
+  vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
+  int i;
+  for (i=0;i<count;i++) {
+    buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
+  }
+  // *** change to 0, 256?
+  refreshWindowPalette(first, count);
+  palette_changed = true;
+  InvalidateRect(getHandle(), 0, FALSE);
+}
+
+void
+CView::bell() {
+  if (options.acceptBell)
+    MessageBeep(-1);
+}
+
+
+void
+CView::setDesktopSize(int w, int h) {
+  vlog.debug("setDesktopSize %dx%d", w, h);
+
+  // If the locally-rendered cursor is visible then remove it
+  hideLocalCursor();
+
+  // Resize the backing buffer
+  buffer->setSize(w, h);
+
+  // If the window is not maximised or full-screen then resize it
+  if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
+    // Resize the window to the required size
+    RECT r = {0, 0, w, h};
+    AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE);
+    SetWindowPos(getHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top,
+      SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+
+    // Move the window to the desired monitor
+    if (options.monitor.buf)
+      moveToMonitor(getHandle(), options.monitor.buf);
+
+    // Clip to the system work area
+    centerWindow(getHandle(), 0, true);
+  } else {
+    // Ensure the screen contents are consistent
+    InvalidateRect(getHandle(), 0, FALSE);
+  }
+
+  // Tell the underlying CConnection
+  CConnection::setDesktopSize(w, h);
+
+  // Enable/disable scrollbars as appropriate
+  calculateScrollBars();
+}
+
+void
+CView::setCursor(const Point& hotspot, const Point& size, void* data, void* mask) {
+  if (!options.useLocalCursor) return;
+  hideLocalCursor();
+
+  cursor.hotspot = hotspot;
+
+  cursor.setSize(size.x, size.y);
+  cursor.setPF(cp.pf());
+  cursor.imageRect(cursor.getRect(), data);
+  memcpy(cursor.mask.buf, mask, cursor.maskLen());
+  cursor.crop();
+
+  cursorBacking.setSize(size.x, size.y);
+  cursorBacking.setPF(cp.pf());
+
+  cursorAvailable = true;
+
+  showLocalCursor();
+}
+
+PixelFormat
+CView::getNativePF() const {
+  vlog.debug("getNativePF()");
+  return WindowDC(getHandle()).getPF();
+}
+
+void
+CView::setVisible(bool visible) {
+  ShowWindow(getHandle(), visible ? SW_SHOW : SW_HIDE);
+  if (visible) {
+    // When the window becomes visible, make it active
+    SetForegroundWindow(getHandle());
+    SetActiveWindow(getHandle());
+    // If the window should be full-screen, then do so
+    setFullscreen(options.fullScreen);
+  } else {
+    // Disable full-screen mode
+    setFullscreen(false);
+  }
+}
+
+void
+CView::close(const char* reason) {
+  setVisible(false);
+  if (reason) {
+    vlog.info("closing - %s", reason);
+    MsgBox(NULL, TStr(reason), MB_ICONINFORMATION | MB_OK);
+  }
+  SendMessage(getHandle(), WM_CLOSE, 0, 0);
+}
+
+
+void
+CView::framebufferUpdateEnd() {
+  if (debugDelay != 0) {
+    vlog.debug("debug delay %d",(int)debugDelay);
+    UpdateWindow(getHandle());
+    Sleep(debugDelay);
+    std::list<rfb::Rect>::iterator i;
+    for (i = debugRects.begin(); i != debugRects.end(); i++) {
+      invertRect(*i);
+    }
+    debugRects.clear();
+  }
+  if (options.autoSelect)
+    autoSelectFormatAndEncoding();
+
+  // Always request the next update
+  requestUpdate = true;
+
+  // Check that at least part of the window has changed
+  if (!GetUpdateRect(getHandle(), 0, FALSE)) {
+    if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE))
+      requestNewUpdate();
+  }
+
+  showLocalCursor();
+}
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+//   Above 16Mbps (timing for at least a second), same machine, switch to raw
+//   Above 3Mbps, switch to hextile
+//   Below 1.5Mbps, switch to ZRLE
+//   Above 1Mbps, switch to full colour mode
+void
+CView::autoSelectFormatAndEncoding() {
+  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+  unsigned int newEncoding = options.preferredEncoding;
+
+  if (kbitsPerSecond > 16000 && sameMachine &&
+      sock->inStream().timeWaited() >= 10000) {
+    newEncoding = encodingRaw;
+  } else if (kbitsPerSecond > 3000) {
+    newEncoding = encodingHextile;
+  } else if (kbitsPerSecond < 1500) {
+    newEncoding = encodingZRLE;
+  }
+
+  if (newEncoding != options.preferredEncoding) {
+    vlog.info("Throughput %d kbit/s - changing to %s encoding",
+            kbitsPerSecond, encodingName(newEncoding));
+    options.preferredEncoding = newEncoding;
+    encodingChange = true;
+  }
+
+  if (kbitsPerSecond > 1000) {
+    if (!options.fullColour) {
+      vlog.info("Throughput %d kbit/s - changing to full colour",
+                kbitsPerSecond);
+      options.fullColour = true;
+      formatChange = true;
+    }
+  }
+}
+
+void
+CView::requestNewUpdate() {
+  if (!requestUpdate) return;
+
+  if (formatChange) {
+    // Hide the rendered cursor, if any, to prevent
+    // the backing buffer being used in the wrong format
+    hideLocalCursor();
+
+    // Select the required pixel format
+    if (options.fullColour) {
+      buffer->setPF(fullColourPF);
+    } else {
+      switch (options.lowColourLevel) {
+      case 0:
+        buffer->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
+        break;
+      case 1:
+        buffer->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
+        break;
+      case 2:
+        buffer->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
+        break;
+      }
+    }
+
+    // Print the current pixel format
+    char str[256];
+    buffer->getPF().print(str, 256);
+    vlog.info("Using pixel format %s",str);
+
+    // Save the connection pixel format and tell server to use it
+    cp.setPF(buffer->getPF());
+    writer()->writeSetPixelFormat(cp.pf());
+
+    // Correct the local window's palette
+    if (!getNativePF().trueColour)
+      refreshWindowPalette(0, 1 << cp.pf().depth);
+  }
+
+  if (encodingChange) {
+    vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
+    writer()->writeSetEncodings(options.preferredEncoding, true);
+  }
+
+  writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+                                          !formatChange);
+
+  encodingChange = formatChange = requestUpdate = false;
+}
+
+
+void
+CView::writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down) {
+  if (!options.sendKeyEvents) return;
+  try {
+    kbd.keyEvent(writer(), vkey, flags, down);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void
+CView::writePointerEvent(int x, int y, int buttonMask) {
+  if (!options.sendPtrEvents) return;
+  try {
+    ptr.pointerEvent(writer(), x, y, buttonMask);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+void
+CView::refreshWindowPalette(int start, int count) {
+  vlog.debug("refreshWindowPalette(%d, %d)", start, count);
+
+  Colour colours[256];
+  if (count > 256) {
+    vlog.debug("%d palette entries", count);
+    throw rdr::Exception("too many palette entries");
+  }
+
+  // Copy the palette from the DIBSectionBuffer
+  ColourMap* cm = buffer->getColourMap();
+  if (!cm) return;
+  for (int i=0; i<count; i++) {
+    int r, g, b;
+    cm->lookup(i, &r, &g, &b);
+    colours[i].r = r;
+    colours[i].g = g;
+    colours[i].b = b;
+  }
+
+  // Set the window palette
+  windowPalette.setEntries(start, count, colours);
+
+  // Cause the window to be redrawn
+  InvalidateRect(getHandle(), 0, 0);
+}
+
+
+void CView::calculateScrollBars() {
+  // Calculate the required size of window
+  DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
+  DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+  DWORD old_style;
+  RECT r;
+  SetRect(&r, 0, 0, buffer->width(), buffer->height());
+  AdjustWindowRect(&r, style, FALSE);
+  Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+
+  if (!bumpScroll) {
+    // We only enable scrollbars if bump-scrolling is not active.
+    // Effectively, this means if full-screen is not active,
+    // but I think it's better to make these things explicit.
+    
+    // Work out whether scroll bars are required
+    do {
+      old_style = style;
+
+      if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
+        style |= WS_HSCROLL;
+        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
+      }
+      if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
+        style |= WS_VSCROLL;
+        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
+      }
+    } while (style != old_style);
+  }
+
+  // Tell Windows to update the window style & cached settings
+  if (style != current_style) {
+    SetWindowLong(getHandle(), GWL_STYLE, style);
+    SetWindowPos(getHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+  }
+
+  // Update the scroll settings
+  SCROLLINFO si;
+  if (style & WS_VSCROLL) {
+    si.cbSize = sizeof(si); 
+    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
+    si.nMin   = 0; 
+    si.nMax   = buffer->height(); 
+    si.nPage  = buffer->height() - (reqd_size.height() - window_size.height()); 
+    maxscrolloffset.y = max(0, si.nMax-si.nPage);
+    scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y);
+    si.nPos   = scrolloffset.y;
+    SetScrollInfo(getHandle(), SB_VERT, &si, TRUE);
+  }
+  if (style & WS_HSCROLL) {
+    si.cbSize = sizeof(si); 
+    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
+    si.nMin   = 0;
+    si.nMax   = buffer->width(); 
+    si.nPage  = buffer->width() - (reqd_size.width() - window_size.width()); 
+    maxscrolloffset.x = max(0, si.nMax-si.nPage);
+    scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x);
+    si.nPos   = scrolloffset.x;
+    SetScrollInfo(getHandle(), SB_HORZ, &si, TRUE);
+  }
+}
+
+
+void
+CView::calculateFullColourPF() {
+  // If the server is palette based then use palette locally
+  // Also, don't bother doing bgr222
+  if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
+    fullColourPF = serverDefaultPF;
+    options.fullColour = true;
+  } else {
+    // If server is trueColour, use lowest depth PF
+    PixelFormat native = getNativePF();
+    if ((serverDefaultPF.bpp < native.bpp) ||
+      ((serverDefaultPF.bpp == native.bpp) &&
+      (serverDefaultPF.depth < native.depth)))
+      fullColourPF = serverDefaultPF;
+    else
+      fullColourPF = getNativePF();
+  }
+  formatChange = true;
+}
+
+
+void
+CView::updateF8Menu(bool hideSystemCommands) {
+  HMENU menu = GetSystemMenu(getHandle(), FALSE);
+
+  if (hideSystemCommands) {  
+    // Gray out menu items that might cause a World Of Pain
+    HMENU menu = GetSystemMenu(getHandle(), FALSE);
+    EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
+    EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
+    EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
+    EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
+    EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
+  }
+
+  // Update the modifier key menu items
+  UINT ctrlCheckFlags = kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
+  UINT altCheckFlags = kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
+  CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
+  CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
+
+  // Ensure that the Send <MenuKey> menu item has the correct text
+  if (options.menuKey) {
+    TCharArray menuKeyStr(options.menuKeyName());
+    TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
+    _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
+    if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
+      InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
+  } else {
+    RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
+  }
+}
+
+
+void
+CView::setName(const char* name) {
+  vlog.debug("setName %s", name);
+  ::SetWindowText(getHandle(), TStr(name));
+  CConnection::setName(name);
+}
+
+
+void CView::serverInit() {
+  CConnection::serverInit();
+
+  // Save the server's current format
+  serverDefaultPF = cp.pf();
+
+  // Calculate the full-colour format to use
+  calculateFullColourPF();
+
+  // Request the initial update
+  vlog.info("requesting initial update");
+  formatChange = encodingChange = requestUpdate = true;
+  requestNewUpdate();
+
+  // Show the window
+  setVisible(true);
+}
+
+void
+CView::serverCutText(const char* str, int len) {
+  if (!options.serverCutText) return;
+  CharArray t(len+1);
+  memcpy(t.buf, str, len);
+  t.buf[len] = 0;
+  clipboard.setClipText(t.buf);
+}
+
+
+void CView::beginRect(const Rect& r, unsigned int encoding) {
+  sock->inStream().startTiming();
+}
+
+void CView::endRect(const Rect& r, unsigned int encoding) {
+  sock->inStream().stopTiming();
+  lastUsedEncoding_ = encoding;
+  if (debugDelay != 0) {
+    invertRect(r);
+    debugRects.push_back(r);
+  }
+}
+
+void CView::fillRect(const Rect& r, Pixel pix) {
+  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+  buffer->fillRect(r, pix);
+  invalidateBufferRect(r);
+}
+void CView::imageRect(const Rect& r, void* pixels) {
+  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+  buffer->imageRect(r, pixels);
+  invalidateBufferRect(r);
+}
+void CView::copyRect(const Rect& r, int srcX, int srcY) {
+  if (cursorBackingRect.overlaps(r) ||
+      cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height())))
+    hideLocalCursor();
+  buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
+  invalidateBufferRect(r);
+}
+
+void CView::invertRect(const Rect& r) {
+  int stride;
+  rdr::U8* p = buffer->getPixelsRW(r, &stride);
+  for (int y = 0; y < r.height(); y++) {
+    for (int x = 0; x < r.width(); x++) {
+      switch (buffer->getPF().bpp) {
+      case 8:  ((rdr::U8* )p)[x+y*stride] ^= 0xff;       break;
+      case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff;     break;
+      case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
+      }
+    }
+  }
+  invalidateBufferRect(r);
+}
+
+bool CView::getUserPasswd(char** user, char** password) {
+  if (user && options.userName.buf)
+    *user = strDup(options.userName.buf);
+  if (password && options.password.buf)
+    *password = strDup(options.password.buf);
+  if ((user && !*user) || (password && !*password)) {
+    // Missing username or password - prompt the user
+    UserPasswdDialog userPasswdDialog;
+    userPasswdDialog.setCSecurity(getCurrentCSecurity());
+    if (!userPasswdDialog.getUserPasswd(user, password))
+      return false;
+  }
+  if (user) options.setUserName(*user);
+  if (password) options.setPassword(*password);
+  return true;
+}
+
diff --git a/vncviewer/cview.h b/vncviewer/cview.h
new file mode 100644
index 0000000..2bee1c4
--- /dev/null
+++ b/vncviewer/cview.h
@@ -0,0 +1,296 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- CView.h
+
+// An instance of the CView class is created for each VNC Viewer connection.
+
+#ifndef __RFB_WIN32_CVIEW_H__
+#define __RFB_WIN32_CVIEW_H__
+
+#include <network/Socket.h>
+
+#include <rfb/CConnection.h>
+#include <rfb/Cursor.h>
+#include <rfb/UserPasswdGetter.h>
+
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/CKeyboard.h>
+#include <rfb_win32/CPointer.h>
+
+#include <vncviewer/InfoDialog.h>
+#include <vncviewer/OptionsDialog.h>
+#include <vncviewer/CViewOptions.h>
+#include <vncviewer/CViewManager.h>
+#include <list>
+
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView : public CConnection,
+                  public UserPasswdGetter,
+                  rfb::win32::Clipboard::Notifier,
+                  rdr::FdInStreamBlockCallback
+    {
+    public:
+      CView();
+      virtual ~CView();
+
+      bool initialise(network::Socket* s);
+
+      void setManager(CViewManager* m) {manager = m;}
+
+      void applyOptions(CViewOptions& opt);
+      const CViewOptions& getOptions() const {return options;};
+
+      // -=- Window Message handling
+
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // -=- Socket blocking handling
+      //     blockCallback will throw QuitMessage(result) when
+      //     it processes a WM_QUIT message.
+      //     The caller may catch that to cope gracefully with
+      //     a request to quit.
+
+      class QuitMessage : public rdr::Exception {
+      public:
+        QuitMessage(WPARAM wp) : rdr::Exception("QuitMessage") {}
+        WPARAM wParam;
+      };
+      virtual void blockCallback();
+
+      // -=- Window interface
+
+      void postQuitOnDestroy(bool qod) {quit_on_destroy = qod;}
+      PixelFormat getNativePF() const;
+      void setVisible(bool visible);
+      void close(const char* reason=0);
+      HWND getHandle() const {return hwnd;}
+
+      void notifyClipboardChanged(const char* text, int len);
+
+      // -=- Coordinate conversions
+
+      inline Point bufferToClient(const Point& p) {
+        Point pos = p;
+        if (client_size.width() > buffer->width())
+          pos.x += (client_size.width() - buffer->width()) / 2;
+        else if (client_size.width() < buffer->width())
+          pos.x -= scrolloffset.x;
+        if (client_size.height() > buffer->height())
+          pos.y += (client_size.height() - buffer->height()) / 2;
+        else if (client_size.height() < buffer->height())
+          pos.y -= scrolloffset.y;
+        return pos;
+      }
+      inline Rect bufferToClient(const Rect& r) {
+        return Rect(bufferToClient(r.tl), bufferToClient(r.br));
+      }
+
+      inline Point clientToBuffer(const Point& p) {
+        Point pos = p;
+        if (client_size.width() > buffer->width())
+          pos.x -= (client_size.width() - buffer->width()) / 2;
+        else if (client_size.width() < buffer->width())
+          pos.x += scrolloffset.x;
+        if (client_size.height() > buffer->height())
+          pos.y -= (client_size.height() - buffer->height()) / 2;
+        else if (client_size.height() < buffer->height())
+          pos.y += scrolloffset.y;
+        return pos;
+      }
+      inline Rect clientToBuffer(const Rect& r) {
+        return Rect(clientToBuffer(r.tl), clientToBuffer(r.br));
+      }
+
+      void setFullscreen(bool fs);
+
+      bool setViewportOffset(const Point& tl);
+
+      bool processBumpScroll(const Point& cursorPos);
+      void setBumpScroll(bool on);
+
+      int lastUsedEncoding() const { return lastUsedEncoding_; }
+
+      // -=- CConnection interface overrides
+
+      virtual CSecurity* getCSecurity(int secType);
+
+      virtual void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+      virtual void bell();
+
+      virtual void framebufferUpdateEnd();
+
+      virtual void setDesktopSize(int w, int h);
+      virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+      virtual void setName(const char* name);
+      virtual void serverInit();
+
+      virtual void serverCutText(const char* str, int len);
+
+      virtual void beginRect(const Rect& r, unsigned int encoding);
+      virtual void endRect(const Rect& r, unsigned int encoding);
+
+      virtual void fillRect(const Rect& r, Pixel pix);
+      virtual void imageRect(const Rect& r, void* pixels);
+      virtual void copyRect(const Rect& r, int srcX, int srcY);
+
+      void invertRect(const Rect& r);
+
+      // VNCviewer dialog objects
+
+      OptionsDialog optionsDialog;
+
+      friend class InfoDialog;
+      InfoDialog infoDialog;
+
+      // UserPasswdGetter overrides, used to support a pre-supplied VNC password
+      virtual bool getUserPasswd(char** user, char** password);
+
+      // Global user-config registry key
+      static RegKey userConfigKey;
+
+    protected:
+
+      // Locally-rendered VNC cursor
+      void hideLocalCursor();
+      void showLocalCursor();
+      void renderLocalCursor();
+
+      // The system-rendered cursor
+      void hideSystemCursor();
+      void showSystemCursor();
+
+      // cursorOutsideBuffer() is called whenever we detect that the mouse has
+      // moved outside the desktop.  It restores the system arrow cursor.
+      void cursorOutsideBuffer();
+
+      // Returns true if part of the supplied rect is visible, false otherwise
+      bool invalidateBufferRect(const Rect& crect);
+
+      // Auto-encoding selector
+      void autoSelectFormatAndEncoding();
+
+      // Request an update with appropriate setPixelFormat and setEncodings calls
+      void requestNewUpdate();
+
+      // Update the window palette if the display is palette-based.
+      // Colours are pulled from the DIBSectionBuffer's ColourMap.
+      // Only the specified range of indexes is dealt with.
+      // After the update, the entire window is redrawn.
+      void refreshWindowPalette(int start, int count);
+
+      // Determine whether or not we need to enable/disable scrollbars and set the
+      // window style accordingly
+      void calculateScrollBars();
+
+      // Recalculate the most suitable full-colour pixel format
+      void calculateFullColourPF();
+
+      // Enable/disable/check/uncheck the F8 menu items as appropriate.
+      void updateF8Menu(bool hideSystemCommands);
+
+      // VNCviewer options
+
+      CViewOptions options;
+
+      // Input handling
+      void writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down);
+      void writePointerEvent(int x, int y, int buttonMask);
+      rfb::win32::CKeyboard kbd;
+      rfb::win32::CPointer ptr;
+      Point oldpos;
+
+      // Clipboard handling
+      rfb::win32::Clipboard clipboard;
+
+      // Pixel format and encoding
+      PixelFormat serverDefaultPF;
+      PixelFormat fullColourPF;
+      bool sameMachine;
+      bool encodingChange;
+      bool formatChange;
+      int lastUsedEncoding_;
+
+      // Networking and RFB protocol
+      network::Socket* sock;
+      bool readyToRead;
+      bool requestUpdate;
+
+      // Palette handling
+      LogicalPalette windowPalette;
+      bool palette_changed;
+
+      // - Full-screen mode
+      Rect fullScreenOldRect;
+      DWORD fullScreenOldFlags;
+      bool fullScreenActive;
+
+      // Bump-scrolling (used in full-screen mode)
+      bool bumpScroll;
+      Point bumpScrollDelta;
+      IntervalTimer bumpScrollTimer;
+
+      // Cursor handling
+      Cursor cursor;
+      bool systemCursorVisible;  // Should system-cursor be drawn?
+      bool trackingMouseLeave;
+      bool cursorInBuffer;    // Is cursor position within server buffer? (ONLY for LocalCursor)
+      bool cursorVisible;     // Is cursor currently rendered?
+      bool cursorAvailable;   // Is cursor available for rendering?
+      Point cursorPos;
+      ManagedPixelBuffer cursorBacking;
+      Rect cursorBackingRect;
+
+      // ** Debugging/logging
+      /*
+      int update_rect_count;
+      int update_pixel_count;
+      Rect update_extent;
+      */
+      std::list<Rect> debugRects;
+
+      // Local window state
+      win32::DIBSectionBuffer* buffer;
+      bool has_focus;
+      bool quit_on_destroy;
+      Rect window_size;
+      Rect client_size;
+      Point scrolloffset;
+      Point maxscrolloffset;
+      HWND hwnd;
+
+      // Handle back to CViewManager instance, if any
+      CViewManager* manager;
+
+    };
+
+  };
+
+};
+
+#endif
+
+
diff --git a/vncviewer/msvcwarning.h b/vncviewer/msvcwarning.h
new file mode 100644
index 0000000..53a0678
--- /dev/null
+++ b/vncviewer/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/vncviewer/resource.h b/vncviewer/resource.h
new file mode 100644
index 0000000..351a2b0
--- /dev/null
+++ b/vncviewer/resource.h
@@ -0,0 +1,80 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by vncviewer.rc
+//
+#define IDR_MANIFEST                    1
+#define IDI_ICON                        101
+#define IDD_VNC_AUTH_DLG                102
+#define IDD_CONNECTING_DLG              103
+#define IDD_CONNECTION_DLG              104
+#define IDC_DOT_CURSOR                  105
+#define IDD_ABOUT                       107
+#define IDD_FORMAT                      108
+#define IDD_MISC                        109
+#define IDD_INPUTS                      110
+#define IDD_SERVER_KEYS                 111
+#define IDR_TRAY                        112
+#define IDD_CONNECTION_INFO             113
+#define IDD_DEFAULTS                    116
+#define IDC_PASSWORD                    1000
+#define IDC_CONNECTING_TEXT             1001
+#define IDC_SERVER_EDIT                 1002
+#define IDC_USERNAME                    1005
+#define IDC_VERSION                     1008
+#define IDC_BUILDTIME                   1009
+#define IDC_ENCODING_AUTO               1010
+#define IDC_FORMAT_FULLCOLOUR           1011
+#define IDC_ENCODING_ZRLE               1012
+#define IDC_ENCODING_HEXTILE            1013
+#define IDC_CONN_SHARED                 1013
+#define IDC_ENCODING_RAW                1014
+#define IDC_FULL_SCREEN                 1014
+#define IDC_SEND_POINTER                1015
+#define IDC_SEND_KEYS                   1016
+#define IDC_CLIENT_CUTTEXT              1017
+#define IDC_SERVER_CUTTEXT              1018
+#define IDC_LOCAL_CURSOR                1019
+#define IDC_DESKTOP_RESIZE              1020
+#define IDC_COPYRIGHT                   1021
+#define IDC_DESCRIPTION                 1022
+#define IDC_OPTIONS                     1023
+#define IDC_ABOUT                       1024
+#define IDC_LIST1                       1025
+#define IDC_INFO_NAME                   1026
+#define IDC_INFO_HOST                   1027
+#define IDC_INFO_SIZE                   1028
+#define IDC_INFO_PF                     1029
+#define IDC_INFO_DEF_PF                 1030
+#define IDC_INFO_LINESPEED              1031
+#define IDC_INFO_VERSION                1032
+#define IDC_PROTOCOL_3_3                1034
+#define IDC_ACCEPT_BELL                 1035
+#define IDC_FORMAT_VERYLOWCOLOUR        1036
+#define IDC_FORMAT_LOWCOLOUR            1037
+#define IDC_FORMAT_MEDIUMCOLOUR         1038
+#define IDC_LOAD_DEFAULTS               1040
+#define IDC_SAVE_DEFAULTS               1041
+#define IDC_LOAD_CONFIG                 1042
+#define IDC_EMULATE3                    1043
+#define IDC_POINTER_INTERVAL            1044
+#define IDC_SAVE_CONFIG                 1045
+#define IDC_INFO_SECURITY               1046
+#define IDC_SAVE_CONFIG_AS              1048
+#define IDC_MENU_KEY                    1051
+#define IDC_REQUESTED_ENCODING          1052
+#define IDC_LAST_ENCODING               1053
+#define ID_CLOSE                        40002
+#define ID_OPTIONS                      40003
+#define ID_NEW_CONNECTION               40004
+#define ID_ABOUT                        40005
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        120
+#define _APS_NEXT_COMMAND_VALUE         40006
+#define _APS_NEXT_CONTROL_VALUE         1054
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
new file mode 100644
index 0000000..59e2d00
--- /dev/null
+++ b/vncviewer/vncviewer.cxx
@@ -0,0 +1,370 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- VNC Viewer for Win32
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+#include <list>
+
+#include <vncviewer/resource.h>
+#include <vncviewer/CViewManager.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/OptionsDialog.h>
+
+#include <rfb/Logger_stdio.h>
+#include <rfb/Logger_file.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/TrayIcon.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/AboutDialog.h>
+
+#include <network/TcpSocket.h>
+
+#ifdef _DIALOG_CAPTURE
+#include <extra/LoadBMP.h>
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+using namespace network;
+
+static LogWriter vlog("main");
+
+TStr rfb::win32::AppName("VNC Viewer");
+
+
+#ifdef _DIALOG_CAPTURE
+BoolParameter captureDialogs("CaptureDialogs", "", false);
+#endif
+
+//
+// -=- Listener
+//     Class to handle listening on a particular port for incoming connections
+//     from servers, and spawning of clients
+//
+
+static BoolParameter acceptIncoming("Listen", "Accept incoming connections from VNC servers.", false);
+
+
+//
+// -=- AboutDialog global values
+//
+
+const WORD rfb::win32::AboutDialog::DialogId = IDD_ABOUT;
+const WORD rfb::win32::AboutDialog::Copyright = IDC_COPYRIGHT;
+const WORD rfb::win32::AboutDialog::Version = IDC_VERSION;
+const WORD rfb::win32::AboutDialog::BuildTime = IDC_BUILDTIME;
+const WORD rfb::win32::AboutDialog::Description = IDC_DESCRIPTION;
+
+
+//
+// -=- VNCviewer Tray Icon
+//
+
+class CViewTrayIcon : public TrayIcon {
+public:
+  CViewTrayIcon(CViewManager& mgr) : manager(mgr) {
+    setIcon(IDI_ICON);
+    setToolTip(_T("VNC Viewer"));
+  }
+  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+    switch(msg) {
+
+    case WM_USER:
+      switch (lParam) {
+      case WM_LBUTTONDBLCLK:
+        SendMessage(getHandle(), WM_COMMAND, ID_NEW_CONNECTION, 0);
+        break;
+      case WM_RBUTTONUP:
+        HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(IDR_TRAY));
+        HMENU trayMenu = GetSubMenu(menu, 0);
+
+        // First item is New Connection, the default
+        SetMenuDefaultItem(trayMenu, ID_NEW_CONNECTION, FALSE);
+
+        // SetForegroundWindow is required, otherwise Windows ignores the
+        // TrackPopupMenu because the window isn't the foreground one, on
+        // some older Windows versions...
+        SetForegroundWindow(getHandle());
+
+        // Display the menu
+        POINT pos;
+        GetCursorPos(&pos);
+        TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
+        break;
+			} 
+			return 0;
+
+    case WM_COMMAND:
+      switch (LOWORD(wParam)) {
+      case ID_NEW_CONNECTION:
+        manager.addClient(0);
+        break;
+      case ID_OPTIONS:
+        OptionsDialog::global.showDialog(0);
+        break;
+      case ID_ABOUT:
+        AboutDialog::instance.showDialog();
+        break;
+      case ID_CLOSE:
+        SendMessage(getHandle(), WM_CLOSE, 0, 0);
+        break;
+      }
+      return 0;
+
+    case WM_CLOSE:
+      PostQuitMessage(0);
+      return 0;
+    }
+
+    return TrayIcon::processMessage(msg, wParam, lParam);
+  }
+protected:
+  CViewManager& manager;
+};
+
+//
+// -=- processParams
+//     Read in the command-line parameters and interpret them.
+//
+
+void
+programInfo() {
+  win32::FileVersionInfo inf;
+  _tprintf(_T("%s - %s, Version %s\n"),
+    inf.getVerString(_T("ProductName")),
+    inf.getVerString(_T("FileDescription")),
+    inf.getVerString(_T("FileVersion")));
+  printf("%s\n", buildTime);
+  _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright")));
+}
+
+void
+programUsage() {
+  printf("usage: vncviewer <options> <hostname>[:<display>]\n");
+  printf("Command-line options:\n");
+  printf("  -help                                - Provide usage information.\n");
+  printf("  -config <file>                       - Load connection settings from VNCViewer 3.3 settings file\n");
+  printf("  -console                             - Run with a console window visible.\n");
+  printf("  <setting>=<value>                    - Set the named configuration parameter.\n");
+  printf("    (Parameter values specified on the command-line override those specified by other configuration methods.)\n");
+  printf("\nLog names:\n");
+  LogWriter::listLogWriters();
+  printf("\nLog destinations:\n");
+  Logger::listLoggers();
+  printf("\nParameters:\n");
+  Configuration::listParams();
+}
+
+
+bool print_usage = false;
+bool close_console = true;
+std::list<char*> hosts;
+std::list<char*> configFiles;
+
+void
+processParams(int argc, char* argv[]) {
+  for (int i=1; i<argc; i++) {
+    try {
+
+      if (strcasecmp(argv[i], "-console") == 0) {
+        close_console = false;
+
+      } else if (((strcasecmp(argv[i], "-config") == 0) ||
+                  (strcasecmp(argv[i], "/config") == 0)) && (i < argc-1)) {
+        configFiles.push_back(strDup(argv[i+1]));
+        i++;
+
+      } else if ((strcasecmp(argv[i], "-help") == 0) ||
+                 (strcasecmp(argv[i], "--help") == 0) ||
+                 (strcasecmp(argv[i], "-h") == 0) ||
+                 (strcasecmp(argv[i], "/?") == 0)) {
+        print_usage = true;
+        close_console = false;
+        break;
+
+      } else {
+        // Try to process <option>=<value>, or -<bool>
+        if (Configuration::setParam(argv[i], true))
+          continue;
+        // Try to process -<option> <value>
+        if ((argv[i][0] == '-') && (i+1 < argc)) {
+          if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+            i++;
+            continue;
+          }
+        }
+        // If it's -<option> then it's not recognised - error
+        // If it's <host> then add it to the list to connect to.
+        if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
+          const char* fmt = "The option %s was not recognized.  Use -help to see VNC Viewer usage";
+          CharArray tmp(strlen(argv[i])+strlen(fmt)+1);
+          sprintf(tmp.buf, fmt, argv[i]);
+          MsgBox(0, TStr(tmp.buf), MB_ICONSTOP | MB_OK);
+          exit(1);
+        } else {
+          hosts.push_back(strDup(argv[i]));
+        }
+      }
+
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+    }
+  }
+}
+
+
+//
+// -=- main
+//
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) {
+
+  try {
+
+    // - Initialise the available loggers
+    initStdIOLoggers();
+    initFileLogger("C:\\temp\\vncviewer4.log");
+
+    // - By default, just log errors to stderr
+    logParams.setDefault("*:stderr:0");
+
+    // - Process the command-line
+    int argc = __argc;
+    char** argv = __argv;
+    processParams(argc, argv);
+
+    // - By default the console will be closed
+    if (close_console) {
+      if (!FreeConsole())
+        vlog.info("unable to close console:%u", GetLastError());
+    } else {
+      AllocConsole();
+      freopen("CONIN$","rb",stdin);
+      freopen("CONOUT$","wb",stdout);
+      freopen("CONOUT$","wb",stderr);
+      setbuf(stderr, 0);
+    }
+
+#ifdef _DIALOG_CAPTURE
+    if (captureDialogs) {
+      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+      OptionsDialog::global.showDialog(0, true);
+      return 0;
+    }
+#endif
+
+    // - If no clients are specified, bring up a connection dialog
+    if (configFiles.empty() && hosts.empty() && !acceptIncoming && !print_usage)
+      hosts.push_back(0);
+
+    programInfo();
+
+    // - Connect to the clients
+    if (!configFiles.empty() || !hosts.empty() || acceptIncoming) {
+      // - Configure the registry configuration reader
+      win32::RegistryReader reg_reader;
+      reg_reader.setKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+
+      // - Tell the rest of VNC Viewer where to write config data to
+      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+
+      // - Start the Socket subsystem for TCP
+      TcpSocket::initTcpSockets();
+
+      // Create the client connection manager
+      CViewManager view_manager;
+
+      if (acceptIncoming) {
+        int port = 5500;
+
+        // Listening viewer
+        if (hosts.size() > 1) {
+          programUsage();
+          exit(2);
+        }
+        if (!hosts.empty()) {
+          port = atoi(hosts.front());  
+        }
+
+        vlog.debug("opening listener");
+
+        CViewTrayIcon tray(view_manager);
+
+        view_manager.addDefaultTCPListener(port);
+
+        // Run the view manager
+        // Also processes the tray icon if necessary
+        MSG msg;
+        while (GetMessage(&msg, NULL, 0, 0) > 0) {
+          TranslateMessage(&msg);
+          DispatchMessage(&msg);
+        }
+
+        vlog.debug("quitting viewer");
+      } else {
+        // Read each config file in turn
+        while (!configFiles.empty()) {
+          char* filename = configFiles.front();
+          view_manager.addClient(filename, true);
+          strFree(filename);
+          configFiles.pop_front();
+        }
+
+        // Connect to each client in turn
+        while (!hosts.empty()) {
+          char* hostinfo = hosts.front();
+          view_manager.addClient(hostinfo);
+          strFree(hostinfo);
+          hosts.pop_front();
+        }
+
+        // Run the view manager
+        MSG msg;
+        while (GetMessage(&msg, NULL, 0, 0) > 0) {
+          TranslateMessage(&msg);
+          DispatchMessage(&msg);
+        }
+
+        vlog.debug("quitting viewer");
+      }
+
+    }
+
+    // - If necessary, print the program's usage info
+    if (print_usage)
+      programUsage();
+
+    if (!close_console) {
+      printf("Press Enter/Return key to continue\n");
+      char c = getchar();
+    }
+
+  } catch (rdr::Exception& e) {
+    MsgBox(0, TStr(e.str()), MB_ICONSTOP | MB_OK);
+  }
+
+  return 0;
+}
diff --git a/vncviewer/vncviewer.dsp b/vncviewer/vncviewer.dsp
new file mode 100644
index 0000000..25d358f
--- /dev/null
+++ b/vncviewer/vncviewer.dsp
@@ -0,0 +1,229 @@
+# Microsoft Developer Studio Project File - Name="vncviewer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vncviewer - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "vncviewer.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "vncviewer.mak" CFG="vncviewer - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "vncviewer - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug Unicode" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "vncviewer - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /machine:I386
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "vncviewer - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "vncviewer - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug_Unicode\ /FdDebug_Unicode\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "vncviewer - Win32 Release"
+# Name "vncviewer - Win32 Debug"
+# Name "vncviewer - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\buildTime.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\cview.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewOptions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ConnectingDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\cview.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewOptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MRU.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\cursor1.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.exe.manifest
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/vncviewer/vncviewer.exe.manifest b/vncviewer/vncviewer.exe.manifest
new file mode 100644
index 0000000..557e456
--- /dev/null
+++ b/vncviewer/vncviewer.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+   version="4.0.0.26"
+   processorArchitecture="X86"
+   name="RealVNC.vncviewer.exe"
+   type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+   <dependentAssembly>
+     <assemblyIdentity
+       type="win32"
+       name="Microsoft.Windows.Common-Controls"
+       version="6.0.0.0"
+       processorArchitecture="X86"
+       publicKeyToken="6595b64144ccf1df"
+       language="*"
+     />
+   </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/vncviewer/vncviewer.ico b/vncviewer/vncviewer.ico
new file mode 100644
index 0000000..98e5578
--- /dev/null
+++ b/vncviewer/vncviewer.ico
Binary files differ
diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc
new file mode 100644
index 0000000..bd9ab6d
--- /dev/null
+++ b/vncviewer/vncviewer.rc
@@ -0,0 +1,500 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON                ICON    DISCARDABLE     "vncviewer.ico"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "RealVNC Ltd.\0"
+            VALUE "FileDescription", "VNC Viewer for Win32\0"
+            VALUE "FileVersion", "4.0\0"
+            VALUE "InternalName", "VNCViewer 4.0\0"
+            VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+            VALUE "LegalTrademarks", "RealVNC\0"
+            VALUE "OriginalFilename", "vncviewer.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "VNC Viewer 4.0\0"
+            VALUE "ProductVersion", "4.0\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_VNC_AUTH_DLG DIALOG DISCARDABLE  0, 0, 241, 46
+STYLE DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "VNC Viewer : Authentication"
+FONT 8, "MS Sans Serif"
+BEGIN
+    EDITTEXT        IDC_USERNAME,75,6,95,14,ES_AUTOHSCROLL
+    EDITTEXT        IDC_PASSWORD,75,25,95,15,ES_PASSWORD | ES_AUTOHSCROLL | 
+                    ES_WANTRETURN
+    DEFPUSHBUTTON   "OK",IDOK,181,6,53,14
+    PUSHBUTTON      "Cancel",IDCANCEL,181,25,53,15
+    ICON            IDI_ICON,IDI_ICON,7,6,20,20
+    LTEXT           "Username:",IDC_STATIC,35,6,35,14
+    LTEXT           "Password:",IDC_STATIC,35,25,35,15
+END
+
+IDD_CONNECTING_DLG DIALOG DISCARDABLE  0, 0, 185, 47
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "VNC Viewer : Connecting"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "Cancel",IDCANCEL,128,26,50,14,WS_DISABLED
+    CTEXT           "Attempting to connect to host...",IDC_CONNECTING_TEXT,7,
+                    7,171,14,SS_CENTERIMAGE
+END
+
+IDD_CONNECTION_DLG DIALOG DISCARDABLE  0, 0, 241, 54
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "VNC Viewer : Connection Details"
+FONT 8, "MS Sans Serif"
+BEGIN
+    COMBOBOX        IDC_SERVER_EDIT,70,6,110,234,CBS_DROPDOWN | 
+                    CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "&About...",IDC_ABOUT,15,30,50,17
+    PUSHBUTTON      "&Options...",IDC_OPTIONS,70,30,50,17
+    DEFPUSHBUTTON   "OK",IDOK,130,30,50,17
+    PUSHBUTTON      "Cancel",IDCANCEL,185,30,50,17
+    ICON            IDI_ICON,IDI_ICON,5,6,20,20
+    LTEXT           "Server:",IDC_STATIC,35,6,30,14
+END
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "About VNC Viewer for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
+    ICON            IDI_ICON,IDC_STATIC,7,10,20,20
+    LTEXT           ">appname<",IDC_DESCRIPTION,40,10,125,15
+    LTEXT           ">version<",IDC_VERSION,165,10,77,15
+    LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
+    LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,202,15
+    LTEXT           "See http://www.realvnc.com for more information on VNC.",
+                    IDC_STATIC,40,55,202,15
+END
+
+IDD_FORMAT DIALOG DISCARDABLE  0, 0, 201, 101
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Colour/Encoding"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "&Auto select",IDC_ENCODING_AUTO,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,7,88,13
+    GROUPBOX        "Preferred encoding",IDC_STATIC,7,25,83,60
+    CONTROL         "ZRLE",IDC_ENCODING_ZRLE,"Button",BS_AUTORADIOBUTTON | 
+                    WS_GROUP,10,35,75,14
+    CONTROL         "Hextile",IDC_ENCODING_HEXTILE,"Button",
+                    BS_AUTORADIOBUTTON,10,49,75,16
+    CONTROL         "Raw",IDC_ENCODING_RAW,"Button",BS_AUTORADIOBUTTON,10,65,
+                    75,15
+    GROUPBOX        "Colour level",IDC_STATIC,95,10,99,75
+    CONTROL         "&Full (all available colours)",IDC_FORMAT_FULLCOLOUR,
+                    "Button",BS_AUTORADIOBUTTON | WS_GROUP,100,20,90,15
+    CONTROL         "&Medium (256 colours)",IDC_FORMAT_MEDIUMCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,35,90,14
+    CONTROL         "&Low (64 colours)",IDC_FORMAT_LOWCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,49,90,16
+    CONTROL         "&Very low (8 colours)",IDC_FORMAT_VERYLOWCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,65,90,15
+END
+
+IDD_MISC DIALOG DISCARDABLE  0, 0, 213, 137
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Misc"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Shared connection (do not disconnect other viewers)",
+                    IDC_CONN_SHARED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,
+                    10,199,15
+    CONTROL         "Full-screen mode",IDC_FULL_SCREEN,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,25,199,15
+    CONTROL         "Render cursor locally",IDC_LOCAL_CURSOR,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,40,199,15
+    CONTROL         "Allow dynamic desktop resizing",IDC_DESKTOP_RESIZE,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,199,15
+    CONTROL         "Only use protocol version 3.3",IDC_PROTOCOL_3_3,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,70,199,15
+    CONTROL         "Beep when requested to by the server",IDC_ACCEPT_BELL,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,199,15
+END
+
+IDD_INPUTS DIALOG DISCARDABLE  0, 0, 186, 138
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Inputs"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Send pointer events to server",IDC_SEND_POINTER,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
+    CONTROL         "Send keyboard events to server",IDC_SEND_KEYS,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
+    CONTROL         "Send clipboard changes to server",IDC_CLIENT_CUTTEXT,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
+    CONTROL         "Accept clipboard changes from server",
+                    IDC_SERVER_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    7,55,172,15
+    CONTROL         "Enable 3-button mouse emulation",IDC_EMULATE3,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
+    CONTROL         "Rate-limit mouse move events",IDC_POINTER_INTERVAL,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,172,14
+    LTEXT           "Menu key",IDC_STATIC,7,100,98,15,SS_CENTERIMAGE
+    COMBOBOX        IDC_MENU_KEY,105,100,74,105,CBS_DROPDOWNLIST | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_CONNECTION_INFO DIALOG DISCARDABLE  0, 0, 239, 186
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "VNC Connection Info"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,182,165,50,14
+    LTEXT           "Desktop Name:",IDC_STATIC,7,10,73,15
+    LTEXT           "Host:",IDC_STATIC,7,25,73,15
+    LTEXT           "Size:",IDC_STATIC,7,40,73,15
+    LTEXT           "Pixel Format:",IDC_STATIC,7,55,73,15
+    LTEXT           "Server Default:",IDC_STATIC,7,70,73,15
+    LTEXT           "Line Speed Estimate:",IDC_STATIC,7,115,73,15
+    LTEXT           "Protocol Version:",IDC_STATIC,7,130,73,15
+    LTEXT           "",IDC_INFO_NAME,80,10,152,15
+    LTEXT           "",IDC_INFO_HOST,80,25,152,15
+    LTEXT           "",IDC_INFO_SIZE,80,40,152,15
+    LTEXT           "",IDC_INFO_PF,80,55,152,15
+    LTEXT           "",IDC_INFO_DEF_PF,80,70,152,15
+    LTEXT           "",IDC_INFO_LINESPEED,80,115,152,15
+    LTEXT           "",IDC_INFO_VERSION,80,130,152,15
+    LTEXT           "Security Method:",IDC_STATIC,7,145,73,15
+    LTEXT           "",IDC_INFO_SECURITY,80,145,152,15
+    LTEXT           "Requested Encoding:",IDC_STATIC,7,85,73,15
+    LTEXT           "Last Used Encoding:",IDC_STATIC,7,100,73,15
+    LTEXT           "",IDC_REQUESTED_ENCODING,80,86,152,15
+    LTEXT           "",IDC_LAST_ENCODING,80,100,152,15
+END
+
+IDD_DEFAULTS DIALOG DISCARDABLE  0, 0, 131, 113
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Defaults"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "Reload &Defaults",IDC_LOAD_DEFAULTS,7,10,117,15
+    PUSHBUTTON      "&Save As Defaults",IDC_SAVE_DEFAULTS,7,30,117,15
+    PUSHBUTTON      "Reload Configuration &File",IDC_LOAD_CONFIG,7,50,117,15
+    PUSHBUTTON      "Save &Configuration File",IDC_SAVE_CONFIG,7,70,117,15
+    PUSHBUTTON      "Save Configuration File &As ...",IDC_SAVE_CONFIG_AS,7,
+                    90,117,15
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_VNC_AUTH_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 234
+        VERTGUIDE, 35
+        VERTGUIDE, 70
+        VERTGUIDE, 75
+        TOPMARGIN, 6
+        BOTTOMMARGIN, 40
+        HORZGUIDE, 20
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+    END
+
+    IDD_CONNECTING_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 178
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 40
+        HORZGUIDE, 21
+        HORZGUIDE, 26
+    END
+
+    IDD_CONNECTION_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 5
+        RIGHTMARGIN, 235
+        VERTGUIDE, 15
+        VERTGUIDE, 35
+        VERTGUIDE, 65
+        VERTGUIDE, 70
+        VERTGUIDE, 120
+        VERTGUIDE, 130
+        VERTGUIDE, 180
+        VERTGUIDE, 185
+        TOPMARGIN, 6
+        BOTTOMMARGIN, 47
+        HORZGUIDE, 20
+        HORZGUIDE, 30
+        HORZGUIDE, 40
+    END
+
+    IDD_ABOUT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 242
+        VERTGUIDE, 40
+        VERTGUIDE, 165
+        VERTGUIDE, 195
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 85
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+    END
+
+    IDD_FORMAT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 194
+        VERTGUIDE, 10
+        VERTGUIDE, 85
+        VERTGUIDE, 90
+        VERTGUIDE, 95
+        VERTGUIDE, 100
+        VERTGUIDE, 105
+        VERTGUIDE, 190
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 94
+        HORZGUIDE, 10
+        HORZGUIDE, 20
+        HORZGUIDE, 25
+        HORZGUIDE, 35
+        HORZGUIDE, 49
+        HORZGUIDE, 65
+        HORZGUIDE, 80
+        HORZGUIDE, 85
+    END
+
+    IDD_MISC, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 206
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 130
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+    END
+
+    IDD_INPUTS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        VERTGUIDE, 105
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 131
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+        HORZGUIDE, 115
+    END
+
+    IDD_CONNECTION_INFO, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 232
+        VERTGUIDE, 80
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 179
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+        HORZGUIDE, 115
+        HORZGUIDE, 130
+        HORZGUIDE, 145
+        HORZGUIDE, 160
+    END
+
+    IDD_DEFAULTS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 124
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 106
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 90
+        HORZGUIDE, 105
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+IDC_DOT_CURSOR          CURSOR  DISCARDABLE     "cursor1.cur"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_CONNECTION_DLG DLGINIT
+BEGIN
+    IDC_SERVER_EDIT, 0x403, 16, 0
+0x796d, 0x616d, 0x6863, 0x6e69, 0x2e65, 0x726f, 0x3a67, 0x0031, 
+    0
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TRAY MENU DISCARDABLE 
+BEGIN
+    POPUP "Tray Menu"
+    BEGIN
+        MENUITEM "&New Connection...",          ID_NEW_CONNECTION
+        MENUITEM SEPARATOR
+        MENUITEM "Default &Options...",         ID_OPTIONS
+        MENUITEM SEPARATOR
+        MENUITEM "&Close Daemon",               ID_CLOSE
+        MENUITEM "&About...",                   ID_ABOUT
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST            24      DISCARDABLE     "vncviewer.exe.manifest"
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/vncviewer_unix/AboutDialog.h b/vncviewer_unix/AboutDialog.h
new file mode 100644
index 0000000..2f54f66
--- /dev/null
+++ b/vncviewer_unix/AboutDialog.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// AboutDialog.h
+//
+
+#ifndef __ABOUTDIALOG_H__
+#define __ABOUTDIALOG_H__
+
+#include "TXMsgBox.h"
+#include "parameters.h"
+
+extern char buildtime[];
+
+class AboutDialog : public TXMsgBox {
+public:
+  AboutDialog(Display* dpy)
+    : TXMsgBox(dpy, aboutText, MB_OK, "About VNC Viewer") {
+  }
+};
+
+#endif
diff --git a/vncviewer_unix/CConn.cxx b/vncviewer_unix/CConn.cxx
new file mode 100644
index 0000000..0b6431b
--- /dev/null
+++ b/vncviewer_unix/CConn.cxx
@@ -0,0 +1,669 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// CConn.cxx
+//
+
+#include <unistd.h>
+#include "CConn.h"
+#include <rfb/CMsgWriter.h>
+#include <rfb/encodings.h>
+#include <rfb/secTypes.h>
+#include <rfb/CSecurityNone.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/Hostname.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <network/TcpSocket.h>
+
+#include "TXViewport.h"
+#include "DesktopWindow.h"
+#include "ServerDialog.h"
+#include "PasswdDialog.h"
+#include "parameters.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("CConn");
+
+IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
+                        "pixel data - a debugging feature", 0);
+
+StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
+                        "F8");
+StringParameter windowName("name", "The X window name", "");
+
+CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
+             char* vncServerName)
+  : dpy(dpy_), argc(argc_),
+    argv(argv_), serverHost(0), serverPort(0), sock(sock_), viewport(0),
+    desktop(0), desktopEventHandler(0),
+    currentEncoding(encodingZRLE), lastServerEncoding((unsigned int)-1),
+    fullColour(::fullColour),
+    autoSelect(::autoSelect), shared(::shared), formatChange(false),
+    encodingChange(false), sameMachine(false), fullScreen(::fullScreen),
+    ctrlDown(false), altDown(false),
+    menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy)
+{
+  CharArray menuKeyStr(menuKey.getData());
+  menuKeysym = XStringToKeysym(menuKeyStr.buf);
+
+  setShared(shared);
+  addSecType(secTypeNone);
+  addSecType(secTypeVncAuth);
+  CharArray encStr(preferredEncoding.getData());
+  int encNum = encodingNum(encStr.buf);
+  if (encNum != -1) {
+    currentEncoding = encNum;
+    autoSelect = false;
+  }
+  cp.supportsDesktopResize = true;
+  cp.supportsLocalCursor = useLocalCursor;
+  initMenu();
+
+  if (sock) {
+    char* name = sock->getPeerEndpoint();
+    vlog.info("Accepted connection from %s", name);
+    if (name) free(name);
+  } else {
+    if (vncServerName) {
+      getHostAndPort(vncServerName, &serverHost, &serverPort);
+    } else {
+      ServerDialog dlg(dpy, &options, &about);
+      if (!dlg.show() || dlg.entry.getText()[0] == 0) {
+        exit(1);
+      }
+      getHostAndPort(dlg.entry.getText(), &serverHost, &serverPort);
+    }
+
+    sock = new network::TcpSocket(serverHost, serverPort);
+    vlog.info("connected to host %s port %d", serverHost, serverPort);
+  }
+
+  sameMachine = sock->sameMachine();
+  sock->inStream().setBlockCallback(this);
+  setServerName(sock->getPeerEndpoint());
+  setStreams(&sock->inStream(), &sock->outStream());
+  initialiseProtocol();
+}
+
+CConn::~CConn() {
+  free(serverHost);
+  delete desktop;
+  delete viewport;
+  delete sock;
+}
+
+// deleteWindow() is called when the user closes the desktop or menu windows.
+
+void CConn::deleteWindow(TXWindow* w) {
+  if (w == &menu) {
+    menu.unmap();
+  } else if (w == viewport) {
+    exit(1);
+  }
+}
+
+// handleEvent() filters all events on the desktop and menu.  Most are passed
+// straight through.  The exception is the F8 key.  When pressed on the
+// desktop, it is used to bring up the menu.  An F8 press or release on the
+// menu is passed through as if it were on the desktop.
+
+void CConn::handleEvent(TXWindow* w, XEvent* ev)
+{
+  KeySym ks;
+  char str[256];
+
+  switch (ev->type) {
+  case KeyPress:
+  case KeyRelease:
+    XLookupString(&ev->xkey, str, 256, &ks, NULL);
+    if (ks == menuKeysym && (ev->xkey.state & (ShiftMask|ControlMask)) == 0) {
+      if (w == desktop && ev->type == KeyPress) {
+        showMenu(ev->xkey.x_root, ev->xkey.y_root);
+        break;
+      } else if (w == &menu) {
+        if (ev->type == KeyPress) menu.unmap();
+        desktopEventHandler->handleEvent(w, ev);
+        break;
+      }
+    }
+    // drop through
+
+  default:
+    if (w == desktop) desktopEventHandler->handleEvent(w, ev);
+    else if (w == &menu) menuEventHandler->handleEvent(w, ev);
+  }
+}
+
+// blockCallback() is called when reading from the socket would block.  We
+// process X events until the socket is ready for reading again.
+
+void CConn::blockCallback() {
+  fd_set rfds;
+  do {
+    TXWindow::handleXEvents(dpy);
+    struct timeval tv;
+    struct timeval* tvp;
+    if (Timer::getTimeout(&tv))
+      tvp = &tv;
+    else
+      tvp = 0;
+    FD_ZERO(&rfds);
+    FD_SET(ConnectionNumber(dpy), &rfds);
+    FD_SET(sock->getFd(), &rfds);
+    int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
+    if (n < 0) throw rdr::SystemException("select",errno);
+    Timer::callTimers();
+  } while (!(FD_ISSET(sock->getFd(), &rfds)));
+}
+
+
+// getPasswd() is called by the CSecurity object when it needs us to read a
+// password from the user.
+
+bool CConn::getUserPasswd(char** user, char** password)
+{
+  CharArray passwordFileStr(passwordFile.getData());
+  if (!user && passwordFileStr.buf[0]) {
+    FILE* fp = fopen(passwordFileStr.buf, "r");
+    if (!fp) return false;
+    char data[256];
+    int datalen = fread(data, 1, 256, fp);
+    fclose(fp);
+    if (datalen != 8) return false;
+    vncAuthUnobfuscatePasswd(data);
+    *password = strDup(data);
+    memset(data, 0, strlen(data));
+    return true;
+  }
+
+  const char* secType = secTypeName(getCurrentCSecurity()->getType());
+  const char* titlePrefix = "VNC Authentication";
+  CharArray title(strlen(titlePrefix) + strlen(secType) + 4);
+  sprintf(title.buf, "%s [%s]", titlePrefix, secType);
+  PasswdDialog dlg(dpy, title.buf, !user);
+  if (!dlg.show()) return false;
+  if (user)
+    *user = strDup(dlg.userEntry.getText());
+  *password = strDup(dlg.passwdEntry.getText());
+  return true;
+}
+
+
+// CConnection callback methods
+
+// getCSecurity() gets the appropriate CSecurity object for the security
+// types which we support.
+CSecurity* CConn::getCSecurity(int secType) {
+  switch (secType) {
+  case secTypeNone:
+    return new CSecurityNone();
+  case secTypeVncAuth:
+    return new CSecurityVncAuth(this);
+  default:
+    throw rfb::Exception("Unsupported secType?");
+  }
+}
+
+// serverInit() is called when the serverInit message has been received.  At
+// this point we create the desktop window and display it.  We also tell the
+// server the pixel format and encodings to use and request the first update.
+void CConn::serverInit() {
+  CConnection::serverInit();
+  serverPF = cp.pf();
+  desktop = new DesktopWindow(dpy, cp.width, cp.height, serverPF, this);
+  desktopEventHandler = desktop->setEventHandler(this);
+  desktop->addEventMask(KeyPressMask | KeyReleaseMask);
+  fullColourPF = desktop->getPF();
+  if (!serverPF.trueColour)
+    fullColour = true;
+  recreateViewport();
+  formatChange = encodingChange = true;
+  requestNewUpdate();
+}
+
+// setDesktopSize() is called when the desktop size changes (including when
+// it is set initially).
+void CConn::setDesktopSize(int w, int h) {
+  CConnection::setDesktopSize(w,h);
+  if (desktop) {
+    desktop->resize(w, h);
+    recreateViewport();
+  }
+}
+
+// framebufferUpdateEnd() is called at the end of an update.
+// For each rectangle, the FdInStream will have timed the speed
+// of the connection, allowing us to select format and encoding
+// appropriately, and then request another incremental update.
+void CConn::framebufferUpdateEnd() {
+  if (debugDelay != 0) {
+    XSync(dpy, False);
+    struct timeval tv;
+    tv.tv_sec = debugDelay / 1000;
+    tv.tv_usec = (debugDelay % 1000) * 1000;
+    select(0, 0, 0, 0, &tv);
+    std::list<rfb::Rect>::iterator i;
+    for (i = debugRects.begin(); i != debugRects.end(); i++) {
+      desktop->invertRect(*i);
+    }
+    debugRects.clear();
+  }
+  desktop->framebufferUpdateEnd();
+  if (autoSelect)
+    autoSelectFormatAndEncoding();
+  requestNewUpdate();
+}
+
+// The rest of the callbacks are fairly self-explanatory...
+
+void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+  desktop->setColourMapEntries(firstColour, nColours, rgbs);
+}
+
+void CConn::bell() { XBell(dpy, 0); }
+
+void CConn::serverCutText(const char* str, int len) {
+  desktop->serverCutText(str,len);
+}
+
+// We start timing on beginRect and stop timing on endRect, to
+// avoid skewing the bandwidth estimation as a result of the server
+// being slow or the network having high latency
+void CConn::beginRect(const Rect& r, unsigned int encoding)
+{
+  sock->inStream().startTiming();
+  if (encoding != encodingCopyRect) {
+    lastServerEncoding = encoding;
+  }
+}
+
+void CConn::endRect(const Rect& r, unsigned int encoding)
+{
+  sock->inStream().stopTiming();
+  if (debugDelay != 0) {
+    desktop->invertRect(r);
+    debugRects.push_back(r);
+  }
+}
+
+void CConn::fillRect(const rfb::Rect& r, rfb::Pixel p) {
+  desktop->fillRect(r,p);
+}
+void CConn::imageRect(const rfb::Rect& r, void* p) {
+  desktop->imageRect(r,p);
+}
+void CConn::copyRect(const rfb::Rect& r, int sx, int sy) {
+  desktop->copyRect(r,sx,sy);
+}
+void CConn::setCursor(const Point& hotspot, const Point& size,
+                      void* data, void* mask) {
+  desktop->setCursor(hotspot, size, data, mask);
+}
+
+
+// Menu stuff - menuSelect() is called when the user selects a menu option.
+
+enum { ID_OPTIONS, ID_INFO, ID_FULLSCREEN, ID_REFRESH, ID_F8, ID_CTRLALTDEL,
+       ID_ABOUT, ID_DISMISS, ID_EXIT, ID_NEWCONN, ID_CTRL, ID_ALT };
+
+void CConn::initMenu() {
+  menuEventHandler = menu.setEventHandler(this);
+  menu.addEventMask(KeyPressMask | KeyReleaseMask);
+  menu.addEntry("Exit viewer", ID_EXIT);
+  menu.addEntry(0, 0);
+  menu.addEntry("Full screen", ID_FULLSCREEN);
+  menu.check(ID_FULLSCREEN, fullScreen);
+  menu.addEntry(0, 0);
+  menu.addEntry("Ctrl", ID_CTRL);
+  menu.addEntry("Alt", ID_ALT);
+  CharArray menuKeyStr(menuKey.getData());
+  CharArray sendMenuKey(6+strlen(menuKeyStr.buf));
+  sprintf(sendMenuKey.buf, "Send %s", menuKeyStr.buf);
+  menu.addEntry(sendMenuKey.buf, ID_F8);
+  menu.addEntry("Send Ctrl-Alt-Del", ID_CTRLALTDEL);
+  menu.addEntry(0, 0);
+  menu.addEntry("Refresh screen", ID_REFRESH);
+  menu.addEntry(0, 0);
+  menu.addEntry("New connection...", ID_NEWCONN);
+  menu.addEntry("Options...", ID_OPTIONS);
+  menu.addEntry("Connection info...", ID_INFO);
+  menu.addEntry("About VNCviewer...", ID_ABOUT);
+  menu.addEntry(0, 0);
+  menu.addEntry("Dismiss menu", ID_DISMISS);
+  menu.toplevel("VNC Menu", this);
+  menu.setBorderWidth(1);
+}
+
+void CConn::showMenu(int x, int y) {
+  menu.check(ID_FULLSCREEN, fullScreen);
+  menu.move(x, y);
+  menu.raise();
+  menu.map();
+}
+
+void CConn::menuSelect(long id, TXMenu* m) {
+  switch (id) {
+  case ID_NEWCONN:
+    {
+      menu.unmap();
+      if (fullScreen) {
+        fullScreen = false;
+        if (viewport) recreateViewport();
+      }
+      int pid = fork();
+      if (pid < 0) { perror("fork"); exit(1); }
+      if (pid == 0) {
+        delete sock;
+        close(ConnectionNumber(dpy));
+        struct timeval tv;
+        tv.tv_sec = 0;
+        tv.tv_usec = 200*1000;
+        select(0, 0, 0, 0, &tv);
+        execlp(programName, programName, 0);
+        perror("execlp"); exit(1);
+      }
+      break;
+    }
+  case ID_OPTIONS:
+    menu.unmap();
+    options.show();
+    break;
+  case ID_INFO:
+    {
+      menu.unmap();
+      char pfStr[100];
+      char spfStr[100];
+      cp.pf().print(pfStr, 100);
+      serverPF.print(spfStr, 100);
+      int secType = getCurrentCSecurity()->getType();
+      char infoText[1024];
+      sprintf(infoText,
+              "Desktop name: %.80s\n"
+              "Host: %.80s port: %d\n"
+              "Size: %d x %d\n"
+              "Pixel format: %s\n"
+              "(server default %s)\n"
+              "Requested encoding: %s\n"
+              "Last used encoding: %s\n"
+              "Line speed estimate: %d kbit/s\n"
+              "Protocol version: %d.%d\n"
+              "Security method: %s\n",
+              cp.name(), serverHost, serverPort, cp.width, cp.height,
+              pfStr, spfStr, encodingName(currentEncoding),
+              encodingName(lastServerEncoding),
+              sock->inStream().kbitsPerSecond(),
+              cp.majorVersion, cp.minorVersion,
+              secTypeName(secType));
+      info.setText(infoText);
+      info.show();
+      break;
+    }
+  case ID_FULLSCREEN:
+    menu.unmap();
+    fullScreen = !fullScreen;
+    if (viewport) recreateViewport();
+    break;
+  case ID_REFRESH:
+    menu.unmap();
+    writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+                                            false);
+    break;
+  case ID_F8:
+    menu.unmap();
+    if (!viewOnly) {
+      writer()->writeKeyEvent(menuKeysym, true);
+      writer()->writeKeyEvent(menuKeysym, false);
+    }
+    break;
+  case ID_CTRLALTDEL:
+    menu.unmap();
+    if (!viewOnly) {
+      writer()->writeKeyEvent(XK_Control_L, true);
+      writer()->writeKeyEvent(XK_Alt_L, true);
+      writer()->writeKeyEvent(XK_Delete, true);
+      writer()->writeKeyEvent(XK_Delete, false);
+      writer()->writeKeyEvent(XK_Alt_L, false);
+      writer()->writeKeyEvent(XK_Control_L, false);
+    }
+    break;
+  case ID_CTRL:
+    menu.unmap();
+    if (!viewOnly) {
+      ctrlDown = !ctrlDown;
+      writer()->writeKeyEvent(XK_Control_L, ctrlDown);
+      menu.check(ID_CTRL, ctrlDown);
+    }
+    break;
+  case ID_ALT:
+    menu.unmap();
+    if (!viewOnly) {
+      altDown = !altDown;
+      writer()->writeKeyEvent(XK_Alt_L, altDown);
+      menu.check(ID_ALT, altDown);
+    }
+    break;
+  case ID_ABOUT:
+    menu.unmap();
+    about.show();
+    break;
+  case ID_DISMISS:
+    menu.unmap();
+    break;
+  case ID_EXIT:
+    exit(1);
+    break;
+  }
+}
+
+
+// OptionsDialogCallback.  setOptions() sets the options dialog's checkboxes
+// etc to reflect our flags.  getOptions() sets our flags according to the
+// options dialog's checkboxes.
+
+void CConn::setOptions() {
+  options.autoSelect.checked(autoSelect);
+  options.fullColour.checked(fullColour);
+  options.veryLowColour.checked(!fullColour && lowColourLevel == 0);
+  options.lowColour.checked(!fullColour && lowColourLevel == 1);
+  options.mediumColour.checked(!fullColour && lowColourLevel == 2);
+  options.zrle.checked(currentEncoding == encodingZRLE);
+  options.hextile.checked(currentEncoding == encodingHextile);
+  options.raw.checked(currentEncoding == encodingRaw);
+  options.viewOnly.checked(viewOnly);
+  options.acceptClipboard.checked(acceptClipboard);
+  options.sendClipboard.checked(sendClipboard);
+  options.sendPrimary.checked(sendPrimary);
+  if (state() == RFBSTATE_NORMAL)
+    options.shared.disabled(true);
+  else
+    options.shared.checked(shared);
+  options.fullScreen.checked(fullScreen);
+  options.useLocalCursor.checked(useLocalCursor);
+  options.dotWhenNoCursor.checked(dotWhenNoCursor);
+}
+
+void CConn::getOptions() {
+  autoSelect = options.autoSelect.checked();
+  if (fullColour != options.fullColour.checked())
+    formatChange = true;
+  fullColour = options.fullColour.checked();
+  if (!fullColour) {
+    int newLowColourLevel = (options.veryLowColour.checked() ? 0 :
+                             options.lowColour.checked() ? 1 : 2);
+    if (newLowColourLevel != lowColourLevel) {
+      lowColourLevel.setParam(newLowColourLevel);
+      formatChange = true;
+    }
+  }
+  unsigned int newEncoding = (options.zrle.checked() ? encodingZRLE :
+                              options.hextile.checked() ? encodingHextile :
+                              encodingRaw);
+  if (newEncoding != currentEncoding) {
+    currentEncoding = newEncoding;
+    encodingChange = true;
+  }
+  viewOnly.setParam(options.viewOnly.checked());
+  acceptClipboard.setParam(options.acceptClipboard.checked());
+  sendClipboard.setParam(options.sendClipboard.checked());
+  sendPrimary.setParam(options.sendPrimary.checked());
+  shared = options.shared.checked();
+  setShared(shared);
+  if (fullScreen != options.fullScreen.checked()) {
+    fullScreen = options.fullScreen.checked();
+    if (viewport) recreateViewport();
+  }
+  useLocalCursor.setParam(options.useLocalCursor.checked());
+  if (cp.supportsLocalCursor != useLocalCursor) {
+    cp.supportsLocalCursor = useLocalCursor;
+    encodingChange = true;
+    if (desktop)
+      desktop->resetLocalCursor();
+  }
+  dotWhenNoCursor.setParam(options.dotWhenNoCursor.checked());
+  checkEncodings();
+}
+
+void CConn::recreateViewport()
+{
+  TXViewport* oldViewport = viewport;
+  viewport = new TXViewport(dpy, cp.width, cp.height);
+  desktop->setViewport(viewport);
+  CharArray windowNameStr(windowName.getData());
+  if (!windowNameStr.buf[0]) {
+    windowNameStr.replaceBuf(new char[256]);
+    sprintf(windowNameStr.buf,"VNC: %.240s",cp.name());
+  }
+  viewport->toplevel(windowNameStr.buf, this, argc, argv);
+  viewport->setBumpScroll(fullScreen);
+  XSetWindowAttributes attr;
+  attr.override_redirect = fullScreen;
+  XChangeWindowAttributes(dpy, viewport->win(), CWOverrideRedirect, &attr);
+  XChangeWindowAttributes(dpy, menu.win(), CWOverrideRedirect, &attr);
+  XChangeWindowAttributes(dpy, options.win(), CWOverrideRedirect, &attr);
+  XChangeWindowAttributes(dpy, about.win(), CWOverrideRedirect, &attr);
+  XChangeWindowAttributes(dpy, info.win(), CWOverrideRedirect, &attr);
+  reconfigureViewport();
+  menu.setTransientFor(viewport->win());
+  viewport->map();
+  if (fullScreen) {
+    XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync,
+                  CurrentTime);
+  } else {
+    XUngrabKeyboard(dpy, CurrentTime);
+  }
+  if (oldViewport) delete oldViewport;
+}
+
+void CConn::reconfigureViewport()
+{
+  viewport->setMaxSize(cp.width, cp.height);
+  if (fullScreen) {
+    viewport->resize(DisplayWidth(dpy,DefaultScreen(dpy)),
+                     DisplayHeight(dpy,DefaultScreen(dpy)));
+  } else {
+    int w = cp.width;
+    int h = cp.height;
+    if (w + wmDecorationWidth >= DisplayWidth(dpy,DefaultScreen(dpy)))
+      w = DisplayWidth(dpy,DefaultScreen(dpy)) - wmDecorationWidth;
+    if (h + wmDecorationHeight >= DisplayHeight(dpy,DefaultScreen(dpy)))
+      h = DisplayHeight(dpy,DefaultScreen(dpy)) - wmDecorationHeight;
+
+    int x = (DisplayWidth(dpy,DefaultScreen(dpy)) - w - wmDecorationWidth) / 2;
+    int y = (DisplayHeight(dpy,DefaultScreen(dpy)) - h - wmDecorationHeight)/2;
+
+    CharArray geometryStr(geometry.getData());
+    viewport->setGeometry(geometryStr.buf, x, y, w, h);
+  }
+}
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+//   Above 16Mbps (timing for at least a second), same machine, switch to raw
+//   Above 3Mbps, switch to hextile
+//   Below 1.5Mbps, switch to ZRLE
+//   Above 1Mbps, switch to full colour mode
+void CConn::autoSelectFormatAndEncoding()
+{
+  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+  unsigned int newEncoding = currentEncoding;
+
+  if (kbitsPerSecond > 16000 && sameMachine &&
+      sock->inStream().timeWaited() >= 10000) {
+    newEncoding = encodingRaw;
+  } else if (kbitsPerSecond > 3000) {
+    newEncoding = encodingHextile;
+  } else if (kbitsPerSecond < 1500) {
+    newEncoding = encodingZRLE;
+  }
+
+  if (newEncoding != currentEncoding) {
+    vlog.info("Throughput %d kbit/s - changing to %s encoding",
+              kbitsPerSecond, encodingName(newEncoding));
+    currentEncoding = newEncoding;
+    encodingChange = true;
+  }
+
+  if (kbitsPerSecond > 1000) {
+    if (!fullColour) {
+      vlog.info("Throughput %d kbit/s - changing to full colour",
+                kbitsPerSecond);
+      fullColour = true;
+      formatChange = true;
+    }
+  }
+}
+
+// checkEncodings() sends a setEncodings message if one is needed.
+void CConn::checkEncodings()
+{
+  if (encodingChange && writer()) {
+    vlog.info("Using %s encoding",encodingName(currentEncoding));
+    writer()->writeSetEncodings(currentEncoding, true);
+    encodingChange = false;
+  }
+}
+
+// requestNewUpdate() requests an update from the server, having set the
+// format and encoding appropriately.
+void CConn::requestNewUpdate()
+{
+  if (formatChange) {
+    if (fullColour) {
+      desktop->setPF(fullColourPF);
+    } else {
+      if (lowColourLevel == 0)
+        desktop->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
+      else if (lowColourLevel == 1)
+        desktop->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
+      else
+        desktop->setPF(PixelFormat(8,8,0,0));
+    }
+    char str[256];
+    desktop->getPF().print(str, 256);
+    vlog.info("Using pixel format %s",str);
+    cp.setPF(desktop->getPF());
+    writer()->writeSetPixelFormat(cp.pf());
+  }
+  checkEncodings();
+  writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+                                          !formatChange);
+  formatChange = false;
+}
diff --git a/vncviewer_unix/CConn.h b/vncviewer_unix/CConn.h
new file mode 100644
index 0000000..c95951b
--- /dev/null
+++ b/vncviewer_unix/CConn.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// CConn represents a client connection to a VNC server.
+//
+
+#ifndef __CCONN_H__
+#define __CCONN_H__
+
+#include <rfb/CConnection.h>
+#include <rfb/Exception.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rdr/FdInStream.h>
+#include <list>
+
+#include "TXWindow.h"
+#include "AboutDialog.h"
+#include "InfoDialog.h"
+#include "TXMenu.h"
+#include "OptionsDialog.h"
+
+class TXWindow;
+class TXViewport;
+class DesktopWindow;
+namespace network { class Socket; }
+
+class CConn : public rfb::CConnection, public rfb::UserPasswdGetter,
+              public TXDeleteWindowCallback,
+              public rdr::FdInStreamBlockCallback,
+              public TXMenuCallback , public OptionsDialogCallback,
+              public TXEventHandler
+{
+public:
+
+  CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
+        char* vncServerName);
+  ~CConn();
+
+  // TXDeleteWindowCallback methods
+  void deleteWindow(TXWindow* w);
+
+  // FdInStreamBlockCallback methods
+  void blockCallback();
+
+  // UserPasswdGetter methods
+  virtual bool getUserPasswd(char** user, char** password);
+
+  // TXMenuCallback methods
+  void menuSelect(long id, TXMenu* m);
+
+  // OptionsDialogCallback methods
+  virtual void setOptions();
+  virtual void getOptions();
+
+  // TXEventHandler callback method
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+  // CConnection callback methods
+  rfb::CSecurity* getCSecurity(int secType);
+  void serverInit();
+  void setDesktopSize(int w, int h);
+  void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+  void bell();
+  void serverCutText(const char* str, int len);
+  void framebufferUpdateEnd();
+  void beginRect(const rfb::Rect& r, unsigned int encoding);
+  void endRect(const rfb::Rect& r, unsigned int encoding);
+  void fillRect(const rfb::Rect& r, rfb::Pixel p);
+  void imageRect(const rfb::Rect& r, void* p);
+  void copyRect(const rfb::Rect& r, int sx, int sy);
+  void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+                 void* data, void* mask);
+
+private:
+
+  void recreateViewport();
+  void reconfigureViewport();
+  void initMenu();
+  void showMenu(int x, int y);
+  void autoSelectFormatAndEncoding();
+  void checkEncodings();
+  void requestNewUpdate();
+
+  Display* dpy;
+  int argc;
+  char** argv;
+  char* serverHost;
+  int serverPort;
+  network::Socket* sock;
+  rfb::PixelFormat serverPF;
+  TXViewport* viewport;
+  DesktopWindow* desktop;
+  TXEventHandler* desktopEventHandler;
+  rfb::PixelFormat fullColourPF;
+  std::list<rfb::Rect> debugRects;
+  unsigned int currentEncoding, lastServerEncoding;
+  bool fullColour;
+  bool autoSelect;
+  bool shared;
+  bool formatChange;
+  bool encodingChange;
+  bool sameMachine;
+  bool fullScreen;
+  bool ctrlDown;
+  bool altDown;
+  KeySym menuKeysym;
+  TXMenu menu;
+  TXEventHandler* menuEventHandler;
+  OptionsDialog options;
+  AboutDialog about;
+  InfoDialog info;
+};
+
+#endif
diff --git a/vncviewer_unix/DesktopWindow.cxx b/vncviewer_unix/DesktopWindow.cxx
new file mode 100644
index 0000000..d0e4cb5
--- /dev/null
+++ b/vncviewer_unix/DesktopWindow.cxx
@@ -0,0 +1,573 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// DesktopWindow.cxx
+//
+
+#include "DesktopWindow.h"
+#include "CConn.h"
+#include <rfb/CMsgWriter.h>
+#include <rfb/LogWriter.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+#include <string.h>
+#include "parameters.h"
+
+#ifndef XK_ISO_Left_Tab
+#define	XK_ISO_Left_Tab					0xFE20
+#endif
+
+static rdr::U8 reverseBits[] = {
+  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+  0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+  0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+  0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+  0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+  0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+  0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+  0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+  0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+  0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+  0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+  0x3f, 0xbf, 0x7f, 0xff
+};
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("DesktopWindow");
+
+DesktopWindow::DesktopWindow(Display* dpy, int w, int h,
+                             const rfb::PixelFormat& serverPF,
+                             CConn* cc_, TXWindow* parent)
+  : TXWindow(dpy, w, h, parent), cc(cc_), im(0),
+    cursorVisible(false), cursorAvailable(false), currentSelectionTime(0),
+    newSelection(0), gettingInitialSelectionTime(true),
+    newServerCutText(false), serverCutText_(0),
+    setColourMapEntriesTimer(this), viewport(0),
+    pointerEventTimer(this), lastPointerX(0), lastPointerY(0),
+    lastButtonMask(0)
+{
+  setEventHandler(this);
+  gc = XCreateGC(dpy, win(), 0, 0);
+  addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+               PointerMotionMask | KeyPressMask | KeyReleaseMask |
+               EnterWindowMask | LeaveWindowMask);
+  createXCursors();
+  XDefineCursor(dpy, win(), dotCursor);
+  im = new TXImage(dpy, width(), height());
+  if (!serverPF.trueColour)
+    im->setPF(serverPF);
+  XConvertSelection(dpy, sendPrimary ? XA_PRIMARY : xaCLIPBOARD, xaTIMESTAMP,
+                    xaSELECTION_TIME, win(), CurrentTime);
+  memset(downKeysym, 0, 256*4);
+}
+
+DesktopWindow::~DesktopWindow()
+{
+  XFreeGC(dpy, gc);
+  XFreeCursor(dpy, dotCursor);
+  XFreeCursor(dpy, noCursor);
+  if (localXCursor)
+    XFreeCursor(dpy, localXCursor);
+  delete im;
+}
+
+void DesktopWindow::setViewport(TXViewport* viewport_)
+{
+  viewport = viewport_;
+  viewport->setChild(this);
+}
+
+// Cursor stuff
+
+void DesktopWindow::createXCursors()
+{
+  static char dotSource[] = { 0x00, 0x0e, 0x0e, 0x0e, 0x00 };
+  static char dotMask[]   = { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+  Pixmap source = XCreateBitmapFromData(dpy, win(), dotSource, 5, 5);
+  Pixmap mask = XCreateBitmapFromData(dpy, win(), dotMask, 5, 5);
+  XColor fg, bg;
+  fg.red = fg.green = fg.blue = 0;
+  bg.red = bg.green = bg.blue = 0xffff;
+  dotCursor = XCreatePixmapCursor(dpy, source, mask, &fg, &bg, 2, 2);
+  XFreePixmap(dpy, source);
+  XFreePixmap(dpy, mask);
+  char zero = 0;
+  Pixmap empty = XCreateBitmapFromData(dpy, win(), &zero, 1, 1);
+  noCursor = XCreatePixmapCursor(dpy, empty, empty, &fg, &bg, 0, 0);
+  XFreePixmap(dpy, empty);
+  localXCursor = 0;
+}
+
+void DesktopWindow::setCursor(const Point& hotspot, const Point& size,
+                              void* data, void* mask)
+{
+  if (!useLocalCursor) return;
+
+  hideLocalCursor();
+
+  int mask_len = ((size.x+7)/8) * size.y;
+
+  int i;
+  for (i = 0; i < mask_len; i++)
+    if (((rdr::U8*)mask)[i]) break;
+
+  if (i == mask_len) {
+    if (dotWhenNoCursor) {
+      vlog.debug("cursor is empty - using dot");
+      XDefineCursor(dpy, win(), dotCursor);
+    } else {
+      XDefineCursor(dpy, win(), noCursor);
+    }
+    cursorAvailable = false;
+    return;
+  }
+
+  cursor.hotspot = hotspot;
+
+  cursor.setSize(size.x, size.y);
+  cursor.setPF(getPF());
+  cursor.imageRect(cursor.getRect(), data);
+
+  cursorBacking.setSize(size.x, size.y);
+  cursorBacking.setPF(getPF());
+
+  delete [] cursor.mask.buf;
+  cursor.mask.buf = new rdr::U8[mask_len];
+  memcpy(cursor.mask.buf, mask, mask_len);
+
+  Pixel pix0, pix1;
+  rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
+  if (bitmap.buf && cursor.getRect().contains(cursor.hotspot)) {
+    int bytesPerRow = (cursor.width() + 7) / 8;
+    for (int j = 0; j < cursor.height(); j++) {
+      for (int i = 0; i < bytesPerRow; i++) {
+        bitmap.buf[j * bytesPerRow + i]
+          = reverseBits[bitmap.buf[j * bytesPerRow + i]];
+        cursor.mask.buf[j * bytesPerRow + i]
+          = reverseBits[cursor.mask.buf[j * bytesPerRow + i]];
+      }
+    }
+    Pixmap source = XCreateBitmapFromData(dpy, win(), (char*)bitmap.buf,
+                                          cursor.width(), cursor.height());
+    Pixmap mask = XCreateBitmapFromData(dpy, win(), (char*)cursor.mask.buf,
+                                        cursor.width(), cursor.height());
+    Colour rgb;
+    XColor fg, bg;
+    getPF().rgbFromPixel(pix1, im->getColourMap(), &rgb);
+    fg.red = rgb.r; fg.green = rgb.g; fg.blue = rgb.b;
+    getPF().rgbFromPixel(pix0, im->getColourMap(), &rgb);
+    bg.red = rgb.r; bg.green = rgb.g; bg.blue = rgb.b;
+    if (localXCursor)
+      XFreeCursor(dpy, localXCursor);
+    localXCursor = XCreatePixmapCursor(dpy, source, mask, &fg, &bg,
+                                       cursor.hotspot.x, cursor.hotspot.y);
+    XDefineCursor(dpy, win(), localXCursor);
+    XFreePixmap(dpy, source);
+    XFreePixmap(dpy, mask);
+    cursorAvailable = false;
+    return;
+  }
+
+  if (!cursorAvailable) {
+    XDefineCursor(dpy, win(), noCursor);
+    cursorAvailable = true;
+  }
+
+  showLocalCursor();
+}
+
+void DesktopWindow::resetLocalCursor()
+{
+  hideLocalCursor();
+  XDefineCursor(dpy, win(), dotCursor);
+  cursorAvailable = false;
+}
+
+void DesktopWindow::hideLocalCursor()
+{
+  // - Blit the cursor backing store over the cursor
+  if (cursorVisible) {
+    cursorVisible = false;
+    im->imageRect(cursorBackingRect, cursorBacking.data);
+    im->put(win(), gc, cursorBackingRect);
+  }
+}
+
+void DesktopWindow::showLocalCursor()
+{
+  if (cursorAvailable && !cursorVisible) {
+    if (!getPF().equal(cursor.getPF()) ||
+        cursor.getRect().is_empty()) {
+      vlog.error("attempting to render invalid local cursor");
+      XDefineCursor(dpy, win(), dotCursor);
+      cursorAvailable = false;
+      return;
+    }
+    cursorVisible = true;
+
+    rfb::Rect cursorRect = (cursor.getRect().translate(cursorPos).
+                            translate(cursor.hotspot.negate()));
+    cursorBackingRect = cursorRect.intersect(im->getRect());
+    im->getImage(cursorBacking.data, cursorBackingRect);
+
+    im->maskRect(cursorRect, cursor.data, cursor.mask.buf);
+    im->put(win(), gc, cursorBackingRect);
+  }
+}
+
+// setColourMapEntries() changes some of the entries in the colourmap.
+// Unfortunately these messages are often sent one at a time, so we delay the
+// settings taking effect by 100ms.  This is because recalculating the internal
+// translation table can be expensive.
+void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
+                                        rdr::U16* rgbs)
+{
+  im->setColourMapEntries(firstColour, nColours, rgbs);
+  if (!setColourMapEntriesTimer.isSet())
+    setColourMapEntriesTimer.reset(100);
+}
+
+void DesktopWindow::serverCutText(const char* str, int len)
+{
+  if (acceptClipboard) {
+    newServerCutText = true;
+    delete [] serverCutText_;
+    serverCutText_ = new char[len+1];
+    memcpy(serverCutText_, str, len);
+    serverCutText_[len] = 0;
+  }
+}
+
+
+// Call XSync() at the end of an update.  We do this because we'd like to
+// ensure that the current update has actually been drawn by the X server
+// before the next update arrives - this is necessary for copyRect to
+// behave correctly.  In particular, if part of the source of a copyRect is
+// not actually displayed in the window, then XCopyArea results in
+// GraphicsExpose events, which require us to draw from the off-screen
+// image.  By the time XSync returns, the GraphicsExpose events will be in
+// Xlib's queue, so hopefully will be processed before the next update.
+// Possibly we should process the GraphicsExpose events here explicitly?
+
+void DesktopWindow::framebufferUpdateEnd()
+{
+  XSync(dpy, False);
+}
+
+
+// invertRect() flips all the bits in every pixel in the given rectangle
+
+void DesktopWindow::invertRect(const Rect& r)
+{
+  int stride;
+  rdr::U8* p = im->getPixelsRW(r, &stride);
+  for (int y = 0; y < r.height(); y++) {
+    for (int x = 0; x < r.width(); x++) {
+      switch (getPF().bpp) {
+      case 8:  ((rdr::U8* )p)[x+y*stride] ^= 0xff;       break;
+      case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff;     break;
+      case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
+      }
+    }
+  }
+  im->put(win(), gc, r);
+}
+
+
+// resize() - resize the window and the image, taking care to remove the local
+// cursor first.
+void DesktopWindow::resize(int w, int h)
+{
+  hideLocalCursor();
+  TXWindow::resize(w, h);
+  im->resize(w, h);
+}
+
+
+void DesktopWindow::timerCallback(Timer* timer)
+{
+  if (timer == &setColourMapEntriesTimer) {
+    im->updateColourMap();
+    im->put(win(), gc, im->getRect());
+  } else if (timer == &pointerEventTimer) {
+    if (!viewOnly) {
+      cc->writer()->writePointerEvent(lastPointerX, lastPointerY,
+                                      lastButtonMask);
+    }
+  }
+}
+
+
+void DesktopWindow::handlePointerEvent(int x, int y, int buttonMask)
+{
+  if (!viewOnly) {
+    if (pointerEventInterval == 0 || buttonMask != lastButtonMask) {
+      cc->writer()->writePointerEvent(x, y, buttonMask);
+    } else {
+      if (!pointerEventTimer.isSet())
+        pointerEventTimer.reset(pointerEventInterval);
+    }
+    lastPointerX = x;
+    lastPointerY = y;
+    lastButtonMask = buttonMask;
+  }
+  // - If local cursor rendering is enabled then use it
+  if (cursorAvailable) {
+    // - Render the cursor!
+    Point p(x, y);
+    if (!p.equals(cursorPos)) {
+      hideLocalCursor();
+      if (im->getRect().contains(p)) {
+        cursorPos = p;
+        showLocalCursor();
+      }
+    }
+  }
+}
+
+
+// handleXEvent() handles the various X events on the window
+void DesktopWindow::handleEvent(TXWindow* w, XEvent* ev)
+{
+  switch (ev->type) {
+  case GraphicsExpose:
+  case Expose:
+    im->put(win(), gc, Rect(ev->xexpose.x, ev->xexpose.y,
+                            ev->xexpose.x + ev->xexpose.width,
+                            ev->xexpose.y + ev->xexpose.height));
+    break;
+
+  case MotionNotify:
+    while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+    if (viewport && viewport->bumpScrollEvent(&ev->xmotion)) break;
+    handlePointerEvent(ev->xmotion.x, ev->xmotion.y,
+                       (ev->xmotion.state & 0x1f00) >> 8);
+    break;
+
+  case ButtonPress:
+    handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+                       (((ev->xbutton.state & 0x1f00) >> 8) |
+                        (1 << (ev->xbutton.button-1))));
+    break;
+
+  case ButtonRelease:
+    handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+                       (((ev->xbutton.state & 0x1f00) >> 8) &
+                        ~(1 << (ev->xbutton.button-1))));
+    break;
+
+  case KeyPress:
+    if (!viewOnly) {
+      KeySym ks;
+      char keyname[256];
+      XLookupString(&ev->xkey, keyname, 256, &ks, NULL);
+      bool fakeShiftPress = false;
+
+      // Turn ISO_Left_Tab into shifted Tab
+      if (ks == XK_ISO_Left_Tab) {
+        fakeShiftPress = !(ev->xkey.state & ShiftMask);
+        ks = XK_Tab;
+      }
+
+      if (fakeShiftPress)
+        cc->writer()->writeKeyEvent(XK_Shift_L, true);
+
+      downKeysym[ev->xkey.keycode] = ks;
+      cc->writer()->writeKeyEvent(ks, true);
+
+      if (fakeShiftPress)
+        cc->writer()->writeKeyEvent(XK_Shift_L, false);
+      break;
+    }
+
+  case KeyRelease:
+    if (!viewOnly) {
+      if (downKeysym[ev->xkey.keycode]) {
+        cc->writer()->writeKeyEvent(downKeysym[ev->xkey.keycode], false);
+        downKeysym[ev->xkey.keycode] = 0;
+      }
+    }
+    break;
+
+  case EnterNotify:
+    newSelection = 0;
+    if (sendPrimary && !selectionOwner(XA_PRIMARY)) {
+      XConvertSelection(dpy, XA_PRIMARY, xaTIMESTAMP, xaSELECTION_TIME,
+                        win(), ev->xcrossing.time);
+    } else if (!selectionOwner(xaCLIPBOARD)) {
+      XConvertSelection(dpy, xaCLIPBOARD, xaTIMESTAMP, xaSELECTION_TIME,
+                        win(), ev->xcrossing.time);
+    }
+    break;
+
+  case LeaveNotify:
+    if (serverCutText_ && newServerCutText) {
+      newServerCutText = false;
+      vlog.debug("acquiring primary and clipboard selections");
+      XStoreBytes(dpy, serverCutText_, strlen(serverCutText_));
+      ownSelection(XA_PRIMARY, ev->xcrossing.time);
+      ownSelection(xaCLIPBOARD, ev->xcrossing.time);
+      currentSelectionTime = ev->xcrossing.time;
+    }
+    // Release all keys - this should probably done on a FocusOut event, but
+    // LeaveNotify is near enough...
+    for (int i = 8; i < 256; i++) {
+      if (downKeysym[i]) {
+        cc->writer()->writeKeyEvent(downKeysym[i], false);
+        downKeysym[i] = 0;
+      }
+    }
+    break;
+  }
+}
+
+// selectionRequest() is called when we are the selection owner and another X
+// client has requested the selection.  We simply put the server's cut text
+// into the requested property.  TXWindow will handle the rest.
+bool DesktopWindow::selectionRequest(Window requestor,
+                                     Atom selection, Atom property)
+{
+  XChangeProperty(dpy, requestor, property, XA_STRING, 8,
+                  PropModeReplace, (unsigned char*)serverCutText_,
+                  strlen(serverCutText_));
+  return true;
+}
+
+
+// selectionNotify() is called when we have requested any information about a
+// selection from the selection owner.  Note that there are two selections,
+// PRIMARY and CLIPBOARD, plus the cut buffer, and we try to use whichever is
+// the most recent of the three.
+//
+// There are two different "targets" for which selectionNotify() is called, the
+// timestamp and the actual string value of the selection.  We always use the
+// timestamp to decide which selection to retrieve.
+//
+// The first time selectionNotify() is called is when we are trying to find the
+// timestamp of the selections at initialisation.  This should be called first
+// for PRIMARY, then we call XConvertSelection() for CLIPBOARD.  The second
+// time should be the result for CLIPBOARD.  At this stage we've got the
+// "currentSelectionTime" so we return.
+//
+// Subsequently selectionNotify() is called whenever the mouse enters the
+// viewer window.  Again, the first time it is called should be the timestamp
+// for PRIMARY, and we then request the timestamp for CLIPBOARD.  When
+// selectionNotify() is called again with the timestamp for CLIPBOARD, we now
+// know if either selection is "new" i.e. later than the previous value of
+// currentSelectionTime.  The last thing to check is the timestamp on the cut
+// buffer.  If the cut buffer is newest we send that to the server, otherwise
+// if one of the selections was newer, we request the string value of that
+// selection.
+//
+// Finally, if we get selectionNotify() called for the string value of a
+// selection, we sent that to the server.
+//
+// As a final minor complication, when one of the selections is actually owned
+// by us, we don't request the details for it.
+
+// TIME_LATER treats 0 as meaning a long time ago, so a==0 means a cannot be
+// later than b.  This is different to the usual meaning of CurrentTime.
+#define TIME_LATER(a, b) ((a) != 0 && ((b) == 0 || (long)((a) - (b)) > 0))
+
+
+void DesktopWindow::selectionNotify(XSelectionEvent* ev, Atom type, int format,
+                                    int nitems, void* data)
+{
+  if (ev->requestor != win())
+    return;
+
+  if (ev->target == xaTIMESTAMP) {
+    if (ev->property == xaSELECTION_TIME) {
+      if (data && format == 32 && nitems == 1) {
+        Time t = *(rdr::U32 *)data;
+        vlog.debug("selection (%d) time is %d, later %d",
+                   ev->selection, t, TIME_LATER(t, currentSelectionTime));
+        if (TIME_LATER(t, currentSelectionTime)) {
+          currentSelectionTime = t;
+          newSelection = ev->selection;
+        }
+      }
+    } else {
+      vlog.debug("no selection (%d)",ev->selection);
+    }
+
+    if (ev->selection == XA_PRIMARY) {
+      if (!selectionOwner(xaCLIPBOARD)) {
+        XConvertSelection(dpy, xaCLIPBOARD, xaTIMESTAMP, xaSELECTION_TIME,
+                          win(), ev->time);
+        return;
+      }
+    } else if (ev->selection != xaCLIPBOARD) {
+      vlog.error("unknown selection %d",ev->selection);
+      return;
+    }
+
+    if (gettingInitialSelectionTime) {
+      gettingInitialSelectionTime = false;
+      return;
+    }
+
+    if (!sendClipboard) return;
+    if (sendPrimary) {
+      vlog.debug("cut buffer time is %d, later %d", cutBufferTime,
+                 TIME_LATER(cutBufferTime, currentSelectionTime));
+      if (TIME_LATER(cutBufferTime, currentSelectionTime)) {
+        currentSelectionTime = cutBufferTime;
+        int len;
+        char* str = XFetchBytes(dpy, &len);
+        if (str) {
+          if (!viewOnly) {
+            vlog.debug("sending cut buffer to server");
+            cc->writer()->writeClientCutText(str, len);
+          }
+          XFree(str);
+          return;
+        }
+      }
+    }
+    if (newSelection) {
+      XConvertSelection(dpy, newSelection, XA_STRING, xaSELECTION_STRING,
+                        win(), CurrentTime);
+    }
+
+  } else if (ev->target == XA_STRING) {
+    if (!sendClipboard) return;
+    if (ev->property == xaSELECTION_STRING) {
+      if (data && format == 8) {
+        if (!viewOnly) {
+          vlog.debug("sending %s selection to server",
+                     ev->selection == XA_PRIMARY ? "primary" :
+                     ev->selection == xaCLIPBOARD ? "clipboard" : "unknown" );
+          cc->writer()->writeClientCutText((char*)data, nitems);
+        }
+      }
+    }
+  }
+}
diff --git a/vncviewer_unix/DesktopWindow.h b/vncviewer_unix/DesktopWindow.h
new file mode 100644
index 0000000..9274f9d
--- /dev/null
+++ b/vncviewer_unix/DesktopWindow.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// DesktopWindow is a TXWindow representing a VNC desktop.
+//
+
+#ifndef __DESKTOPWINDOW_H__
+#define __DESKTOPWINDOW_H__
+
+#include <rfb/Cursor.h>
+#include <rfb/Rect.h>
+#include "TXWindow.h"
+#include "TXViewport.h"
+#include "TXImage.h"
+#include "Timer.h"
+
+class CConn;
+
+class DesktopWindow : public TXWindow, public TXEventHandler,
+                      public TimerCallback {
+public:
+
+  DesktopWindow(Display* dpy, int w, int h,
+                const rfb::PixelFormat& serverPF, CConn* cc_,
+                TXWindow* parent=0);
+  ~DesktopWindow();
+
+  void setViewport(TXViewport* viewport);
+
+  // getPF() and setPF() get and set the TXImage's pixel format
+  const rfb::PixelFormat& getPF() { return im->getPF(); }
+  void setPF(const rfb::PixelFormat& pf) { im->setPF(pf); }
+
+  // setCursor() sets the shape of the local cursor
+  void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+                 void* data, void* mask);
+
+  // resetLocalCursor() stops the rendering of the local cursor
+  void resetLocalCursor();
+
+  // Methods forwarded from CConn
+  void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+  void serverCutText(const char* str, int len);
+  void framebufferUpdateEnd();
+
+  void fillRect(const rfb::Rect& r, rfb::Pixel pix) {
+    if (r.overlaps(cursorBackingRect)) hideLocalCursor();
+    im->fillRect(r, pix);
+    im->put(win(), gc, r);
+    showLocalCursor();
+  }
+  void imageRect(const rfb::Rect& r, void* pixels) {
+    if (r.overlaps(cursorBackingRect)) hideLocalCursor();
+    im->imageRect(r, pixels);
+    im->put(win(), gc, r);
+    showLocalCursor();
+  }
+  void copyRect(const rfb::Rect& r, int srcX, int srcY) {
+    if (r.overlaps(cursorBackingRect) ||
+        cursorBackingRect.overlaps(rfb::Rect(srcX, srcY,
+                                             srcX+r.width(), srcY+r.height())))
+      hideLocalCursor();
+    if (im->usingShm())
+      XSync(dpy, False);
+    im->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY));
+    XCopyArea(dpy, win(), win(), gc, srcX, srcY,
+              r.width(), r.height(), r.tl.x, r.tl.y);
+    showLocalCursor();
+  }
+  void invertRect(const rfb::Rect& r);
+
+  // TXWindow methods
+  virtual void resize(int w, int h);
+  virtual bool selectionRequest(Window requestor,
+                                Atom selection, Atom property);
+  virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+                               int nitems, void* data);
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+
+  void createXCursors();
+  void hideLocalCursor();
+  void showLocalCursor();
+  void timerCallback(Timer* timer);
+  void handlePointerEvent(int x, int y, int buttonMask);
+
+  CConn* cc;
+  TXImage* im;
+  GC gc;
+  ::Cursor dotCursor, noCursor, localXCursor;
+
+  rfb::Cursor cursor;
+  bool cursorVisible;     // Is cursor currently rendered?
+  bool cursorAvailable;   // Is cursor available for rendering?
+  rfb::Point cursorPos;
+  rfb::ManagedPixelBuffer cursorBacking;
+  rfb::Rect cursorBackingRect;
+
+  Time currentSelectionTime;
+  Atom newSelection;
+  bool gettingInitialSelectionTime;
+  bool newServerCutText;
+  char* serverCutText_;
+
+  Timer setColourMapEntriesTimer;
+  TXViewport* viewport;
+  Timer pointerEventTimer;
+  int lastPointerX, lastPointerY, lastButtonMask;
+  rdr::U32 downKeysym[256];
+};
+
+#endif
diff --git a/vncviewer_unix/InfoDialog.h b/vncviewer_unix/InfoDialog.h
new file mode 100644
index 0000000..b228bfc
--- /dev/null
+++ b/vncviewer_unix/InfoDialog.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// InfoDialog.h
+//
+
+#ifndef __INFODIALOG_H__
+#define __INFODIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+extern char buildtime[];
+
+class InfoDialog : public TXDialog, public TXButtonCallback {
+public:
+  InfoDialog(Display* dpy)
+    : TXDialog(dpy, 1, 1, "VNC connection info"),
+      infoLabel(dpy, "", this, 1, 1, TXLabel::left),
+      okButton(dpy, "OK", this, this, 60)
+  {
+    infoLabel.xPad = 8;
+    infoLabel.move(0, yPad*4);
+    setBorderWidth(1);
+  }
+
+  void setText(char* infoText) {
+    infoLabel.setText(infoText);
+    resize(infoLabel.width(),
+           infoLabel.height() + okButton.height() + yPad*12);
+
+    okButton.move((width() - okButton.width()) / 2,
+                  height() - yPad*4 - okButton.height());
+  }
+
+  virtual void buttonActivate(TXButton* b) {
+    unmap();
+  }
+
+  TXLabel infoLabel;
+  TXButton okButton;
+};
+
+#endif
diff --git a/vncviewer_unix/Makefile.in b/vncviewer_unix/Makefile.in
new file mode 100644
index 0000000..a49dacc
--- /dev/null
+++ b/vncviewer_unix/Makefile.in
@@ -0,0 +1,23 @@
+
+SRCS = DesktopWindow.cxx CConn.cxx vncviewer.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+program = vncviewer
+
+DEP_LIBS = ../tx/libtx.a ../rfb/librfb.a ../network/libnetwork.a \
+           ../rdr/librdr.a
+
+EXTRA_LIBS = @ZLIB_LIB@ @X_PRE_LIBS@ @X_LIBS@ -lXext -lX11 @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+	rm -f $(program)
+	$(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncviewer_unix/OptionsDialog.h b/vncviewer_unix/OptionsDialog.h
new file mode 100644
index 0000000..96268cc
--- /dev/null
+++ b/vncviewer_unix/OptionsDialog.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// OptionsDialog.h
+//
+
+#ifndef __OPTIONSDIALOG_H__
+#define __OPTIONSDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+#include "TXButton.h"
+#include "TXCheckbox.h"
+#include "parameters.h"
+
+class OptionsDialogCallback {
+public:
+  virtual void setOptions() = 0;
+  virtual void getOptions() = 0;
+};
+
+class OptionsDialog : public TXDialog, public TXButtonCallback,
+                      public TXCheckboxCallback {
+public:
+  OptionsDialog(Display* dpy, OptionsDialogCallback* cb_)
+    : TXDialog(dpy, 400, 400, "VNC Viewer: Connection Options"), cb(cb_),
+      formatAndEnc(dpy, "Encoding and Colour Level:", this),
+      inputs(dpy, "Inputs:", this),
+      misc(dpy, "Misc:", this),
+      autoSelect(dpy, "Auto select", this, false, this),
+      fullColour(dpy, "Full (all available colours)", this, true, this),
+      mediumColour(dpy, "Medium (256 colours)", this, true, this),
+      lowColour(dpy, "Low (64 colours)", this, true, this),
+      veryLowColour(dpy, "Very low (8 colours)", this, true, this),
+      zrle(dpy, "ZRLE", this, true, this),
+      hextile(dpy, "Hextile", this, true, this),
+      raw(dpy, "Raw", this, true, this),
+      viewOnly(dpy, "View only (ignore mouse & keyboard)", this, false, this),
+      acceptClipboard(dpy, "Accept clipboard from server", this, false, this),
+      sendClipboard(dpy, "Send clipboard to server", this, false, this),
+      sendPrimary(dpy, "Send primary selection & cut buffer as clipboard",
+                  this, false, this),
+      shared(dpy, "Shared (don't disconnect other viewers)", this, false,this),
+      fullScreen(dpy, "Full-screen mode", this, false, this),
+      useLocalCursor(dpy, "Render cursor locally", this, false, this),
+      dotWhenNoCursor(dpy, "Show dot when no cursor", this, false, this),
+      okButton(dpy, "OK", this, this, 60),
+      cancelButton(dpy, "Cancel", this, this, 60)
+  {
+    int y = yPad;
+    formatAndEnc.move(xPad, y);
+    y += formatAndEnc.height();
+    autoSelect.move(xPad, y);
+    int x2 = xPad + autoSelect.width() + xPad*5;
+    fullColour.move(x2, y);
+    y += autoSelect.height();
+    zrle.move(xPad, y);
+    mediumColour.move(x2, y);
+    y += zrle.height();
+    hextile.move(xPad, y);
+    lowColour.move(x2, y);
+    y += hextile.height();
+    raw.move(xPad, y);
+    veryLowColour.move(x2, y);
+    y += raw.height();
+
+    y += yPad*4;
+    inputs.move(xPad, y);
+    y += inputs.height();
+    viewOnly.move(xPad, y);
+    y += viewOnly.height();
+    acceptClipboard.move(xPad, y);
+    y += acceptClipboard.height();
+    sendClipboard.move(xPad, y);
+    y += sendClipboard.height();
+    sendPrimary.move(xPad, y);
+    y += sendPrimary.height();
+
+    y += yPad*4;
+    misc.move(xPad, y);
+    y += misc.height();
+    shared.move(xPad, y);
+    y += shared.height();
+    fullScreen.move(xPad, y);
+    y += fullScreen.height();
+    useLocalCursor.move(xPad, y);
+    y += useLocalCursor.height();
+    dotWhenNoCursor.move(xPad, y);
+    y += dotWhenNoCursor.height();
+
+    okButton.move(width() - xPad*12 - cancelButton.width() - okButton.width(),
+                  height() - yPad*4 - okButton.height());
+    cancelButton.move(width() - xPad*6 - cancelButton.width(),
+                      height() - yPad*4 - cancelButton.height());
+    setBorderWidth(1);
+  }
+
+  virtual void initDialog() {
+    if (cb) cb->setOptions();
+    zrle.disabled(autoSelect.checked());
+    hextile.disabled(autoSelect.checked());
+    raw.disabled(autoSelect.checked());
+    sendPrimary.disabled(!sendClipboard.checked());
+    dotWhenNoCursor.disabled(!useLocalCursor.checked());
+  }
+
+  virtual void takeFocus(Time time) {
+    //XSetInputFocus(dpy, entry.win, RevertToParent, time);
+  }
+
+  virtual void buttonActivate(TXButton* b) {
+    if (b == &okButton) {
+      if (cb) cb->getOptions();
+      unmap();
+    } else if (b == &cancelButton) {
+      unmap();
+    }
+  }
+
+  virtual void checkboxSelect(TXCheckbox* checkbox) {
+    if (checkbox == &autoSelect) {
+      zrle.disabled(autoSelect.checked());
+      hextile.disabled(autoSelect.checked());
+      raw.disabled(autoSelect.checked());
+    } else if (checkbox == &fullColour || checkbox == &mediumColour ||
+               checkbox == &lowColour || checkbox == &veryLowColour) {
+      fullColour.checked(checkbox == &fullColour);
+      mediumColour.checked(checkbox == &mediumColour);
+      lowColour.checked(checkbox == &lowColour);
+      veryLowColour.checked(checkbox == &veryLowColour);
+    } else if (checkbox == &zrle || checkbox == &hextile || checkbox == &raw) {
+      zrle.checked(checkbox == &zrle);
+      hextile.checked(checkbox == &hextile);
+      raw.checked(checkbox == &raw);
+    } else if (checkbox == &sendClipboard) {
+      sendPrimary.disabled(!sendClipboard.checked());
+    } else if (checkbox == &useLocalCursor) {
+      dotWhenNoCursor.disabled(!useLocalCursor.checked());
+    }
+  }
+
+  OptionsDialogCallback* cb;
+  TXLabel formatAndEnc, inputs, misc;
+  TXCheckbox autoSelect;
+  TXCheckbox fullColour, mediumColour, lowColour, veryLowColour;
+  TXCheckbox zrle, hextile, raw;
+  TXCheckbox viewOnly, acceptClipboard, sendClipboard, sendPrimary;
+  TXCheckbox shared, fullScreen, useLocalCursor, dotWhenNoCursor;
+  TXButton okButton, cancelButton;
+};
+
+#endif
diff --git a/vncviewer_unix/PasswdDialog.h b/vncviewer_unix/PasswdDialog.h
new file mode 100644
index 0000000..3c1ee8d
--- /dev/null
+++ b/vncviewer_unix/PasswdDialog.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// PasswdDialog.h
+//
+
+#ifndef __PASSWDDIALOG_H__
+#define __PASSWDDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+
+class PasswdDialog : public TXDialog, public TXEntryCallback {
+public:
+  PasswdDialog(Display* dpy, const char* title, bool userDisabled)
+    : TXDialog(dpy, 320, 100, title, true),
+      userLabel(dpy, "Username:", this, 120),
+      userEntry(dpy, this, this, false, 180),
+      passwdLabel(dpy, "Password:", this, 120),
+      passwdEntry(dpy, this, this, true, 180)
+  {
+    userLabel.move(0, 20);
+    userEntry.move(userLabel.width(), 18);
+    userEntry.disabled(userDisabled);
+    passwdLabel.move(0, 60);
+    passwdEntry.move(passwdLabel.width(), 58);
+  }
+
+  void takeFocus(Time time) {
+    if (!userEntry.disabled())
+      XSetInputFocus(dpy, userEntry.win(), RevertToParent, time);
+    else
+      XSetInputFocus(dpy, passwdEntry.win(), RevertToParent, time);
+  }
+
+  void entryCallback(TXEntry* e, Detail detail, Time time) {
+    if (e == &userEntry) {
+      if (detail == ENTER || detail == NEXT_FOCUS || detail == PREV_FOCUS)
+        XSetInputFocus(dpy, passwdEntry.win(), RevertToParent, time);
+    } else if (e == &passwdEntry) {
+      if (detail == ENTER) {
+        ok = true;
+        done = true;
+      } else if (detail == NEXT_FOCUS || detail == PREV_FOCUS) {
+        XSetInputFocus(dpy, userEntry.win(), RevertToParent, time);
+      }
+    }
+  }
+
+  TXLabel userLabel;
+  TXEntry userEntry;
+  TXLabel passwdLabel;
+  TXEntry passwdEntry;
+};
+
+#endif
diff --git a/vncviewer_unix/ServerDialog.h b/vncviewer_unix/ServerDialog.h
new file mode 100644
index 0000000..245743f
--- /dev/null
+++ b/vncviewer_unix/ServerDialog.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// ServerDialog.h
+//
+
+#ifndef __SERVERDIALOG_H__
+#define __SERVERDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+#include "TXButton.h"
+#include "OptionsDialog.h"
+#include "AboutDialog.h"
+
+class ServerDialog : public TXDialog, public TXEntryCallback,
+                     public TXButtonCallback {
+public:
+  ServerDialog(Display* dpy, OptionsDialog* options_, AboutDialog* about_)
+    : TXDialog(dpy, 332, 120, "VNC Viewer: Connection Details", true),
+      label(dpy, "VNC server:", this, 100),
+      entry(dpy, this, this, false, 180),
+      aboutButton(dpy, "About...", this, this, 60),
+      optionsButton(dpy, "Options...", this, this, 60),
+      okButton(dpy, "OK", this, this, 60),
+      cancelButton(dpy, "Cancel", this, this, 60),
+      options(options_), about(about_)
+  {
+    label.move(0, 30);
+    entry.move(label.width(), 28);
+    int x = width();
+    int y = height() - yPad*4 - cancelButton.height();
+    x -= cancelButton.width() + xPad*6;
+    cancelButton.move(x, y);
+    x -= okButton.width() + xPad*6;
+    okButton.move(x, y);
+    x -= optionsButton.width() + xPad*6;
+    optionsButton.move(x, y);
+    x -= aboutButton.width() + xPad*6;
+    aboutButton.move(x, y);
+  }
+
+  virtual void takeFocus(Time time) {
+    XSetInputFocus(dpy, entry.win(), RevertToParent, time);
+  }
+
+  virtual void entryCallback(TXEntry* e, Detail detail, Time time) {
+    if (detail == ENTER) {
+      ok = true;
+      done = true;
+    }
+  }
+
+  virtual void buttonActivate(TXButton* b) {
+    if (b == &okButton) {
+      ok = true;
+      done = true;
+    } else if (b == &cancelButton) {
+      ok = false;
+      done = true;
+    } else if (b == &optionsButton) {
+      options->show();
+    } else if (b == &aboutButton) {
+      about->show();
+    }
+  }
+
+  TXLabel label;
+  TXEntry entry;
+  TXButton aboutButton, optionsButton, okButton, cancelButton;
+  OptionsDialog* options;
+  AboutDialog* about;
+};
+
+#endif
diff --git a/vncviewer_unix/buildtime.c b/vncviewer_unix/buildtime.c
new file mode 100644
index 0000000..a96031c
--- /dev/null
+++ b/vncviewer_unix/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/vncviewer_unix/parameters.h b/vncviewer_unix/parameters.h
new file mode 100644
index 0000000..815f5f1
--- /dev/null
+++ b/vncviewer_unix/parameters.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __PARAMETERS_H__
+#define __PARAMETERS_H__
+
+#include <rfb/Configuration.h>
+
+extern rfb::IntParameter pointerEventInterval;
+extern rfb::IntParameter wmDecorationWidth;
+extern rfb::IntParameter wmDecorationHeight;
+extern rfb::StringParameter passwordFile;
+extern rfb::BoolParameter useLocalCursor;
+extern rfb::BoolParameter dotWhenNoCursor;
+extern rfb::BoolParameter autoSelect;
+extern rfb::BoolParameter fullColour;
+extern rfb::IntParameter lowColourLevel;
+extern rfb::StringParameter preferredEncoding;
+extern rfb::BoolParameter viewOnly;
+extern rfb::BoolParameter shared;
+extern rfb::BoolParameter acceptClipboard;
+extern rfb::BoolParameter sendClipboard;
+extern rfb::BoolParameter sendPrimary;
+extern rfb::BoolParameter fullScreen;
+extern rfb::StringParameter geometry;
+
+extern char aboutText[];
+extern char* programName;
+
+#endif
diff --git a/vncviewer_unix/vncviewer.cxx b/vncviewer_unix/vncviewer.cxx
new file mode 100644
index 0000000..7b9e37d
--- /dev/null
+++ b/vncviewer_unix/vncviewer.cxx
@@ -0,0 +1,244 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// All-new VNC viewer for X.
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <network/TcpSocket.h>
+#include "TXWindow.h"
+#include "CConn.h"
+
+rfb::LogWriter vlog("main");
+
+using namespace network;
+using namespace rfb;
+
+IntParameter pointerEventInterval("PointerEventInterval",
+                                  "Time in milliseconds to rate-limit"
+                                  " successive pointer events", 0);
+IntParameter wmDecorationWidth("WMDecorationWidth", "Width of window manager "
+                               "decoration around a window", 6);
+IntParameter wmDecorationHeight("WMDecorationHeight", "Height of window "
+                                "manager decoration around a window", 24);
+StringParameter passwordFile("PasswordFile",
+                             "Password file for VNC authentication", "");
+AliasParameter rfbauth("passwd", "Alias for PasswordFile", &passwordFile);
+
+BoolParameter useLocalCursor("UseLocalCursor",
+                             "Render the mouse cursor locally", true);
+BoolParameter dotWhenNoCursor("DotWhenNoCursor",
+                              "Show the dot cursor when the server sends an "
+                              "invisible cursor", true);
+BoolParameter autoSelect("AutoSelect",
+                         "Auto select pixel format and encoding", true);
+BoolParameter fullColour("FullColour",
+                         "Use full colour - otherwise low colour level is used"
+                         " until AutoSelect decides the link is fast enough",
+                         false);
+AliasParameter fullColor("FullColor", "Alias for FullColour", &fullColour);
+IntParameter lowColourLevel("LowColourLevel",
+                            "Colour level to use on slow connections. "
+                            "0 = Very Low (8 colours), 1 = Low (64 colours), "
+                            "2 = Medium (256 colours)", 1);
+StringParameter preferredEncoding("PreferredEncoding",
+                                  "Preferred encoding to use (ZRLE, hextile or"
+                                  " raw) - implies AutoSelect=0", "");
+BoolParameter fullScreen("FullScreen", "Full screen mode", false);
+BoolParameter viewOnly("ViewOnly",
+                       "Don't send any mouse or keyboard events to the server",
+                       false);
+BoolParameter shared("Shared",
+                     "Don't disconnect other viewers upon connection - "
+                     "share the desktop instead",
+                     false);
+BoolParameter acceptClipboard("AcceptClipboard",
+                              "Accept clipboard changes from the server",
+                              true);
+BoolParameter sendClipboard("SendClipboard",
+                            "Send clipboard changes to the server", true);
+BoolParameter sendPrimary("SendPrimary",
+                          "Send the primary selection and cut buffer to the "
+                          "server as well as the clipboard selection",
+                          true);
+
+BoolParameter listenMode("listen", "Listen for connections from VNC servers",
+                         false);
+StringParameter geometry("geometry", "X geometry specification", "");
+StringParameter displayname("display", "The X display", "");
+
+char aboutText[256];
+char* programName;
+extern char buildtime[];
+
+static void CleanupSignalHandler(int sig)
+{
+  // CleanupSignalHandler allows C++ object cleanup to happen because it calls
+  // exit() rather than the default which is to abort.
+  vlog.info("CleanupSignalHandler called");
+  exit(1);
+}
+
+// XLoginIconifier is a class which iconifies the XDM login window when it has
+// grabbed the keyboard, thus releasing the grab, allowing the viewer to use
+// the keyboard.  It remaps the xlogin window on exit.
+class XLoginIconifier {
+public:
+  Display* dpy;
+  Window xlogin;
+  XLoginIconifier() : dpy(0), xlogin(0) {}
+  void iconify(Display* dpy_) {
+    dpy = dpy_;
+    if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync,
+                      GrabModeSync, CurrentTime) == GrabSuccess) {
+      XUngrabKeyboard(dpy, CurrentTime);
+    } else {
+      xlogin = TXWindow::windowWithName(dpy, DefaultRootWindow(dpy), "xlogin");
+      if (xlogin) {
+        XIconifyWindow(dpy, xlogin, DefaultScreen(dpy));
+        XSync(dpy, False);
+      }
+    }
+  }
+  ~XLoginIconifier() {
+    if (xlogin) {
+      fprintf(stderr,"~XLoginIconifier remapping xlogin\n");
+      XMapWindow(dpy, xlogin);
+      XFlush(dpy);
+      sleep(1);
+    }
+  }
+};
+
+static XLoginIconifier xloginIconifier;
+
+static void usage()
+{
+  fprintf(stderr,
+          "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
+          "       %s [parameters] -listen [port] [parameters]\n",
+          programName,programName);
+  fprintf(stderr,"\n"
+          "Parameters can be turned on with -<param> or off with -<param>=0\n"
+          "Parameters which take a value can be specified as "
+          "-<param> <value>\n"
+          "Other valid forms are <param>=<value> -<param>=<value> "
+          "--<param>=<value>\n"
+          "Parameter names are case-insensitive.  The parameters are:\n\n");
+  Configuration::listParams(79, 14);
+  exit(1);
+}
+
+int main(int argc, char** argv)
+{
+  sprintf(aboutText, "VNC viewer for X version 4.0 - built %s\n"
+          "Copyright (C) 2002-2004 RealVNC Ltd.\n"
+          "See http://www.realvnc.com for information on VNC.",
+          buildtime);
+  fprintf(stderr,"\n%s\n", aboutText);
+
+  rfb::initStdIOLoggers();
+  rfb::LogWriter::setLogParams("*:stderr:30");
+
+  signal(SIGHUP, CleanupSignalHandler);
+  signal(SIGINT, CleanupSignalHandler);
+  signal(SIGTERM, CleanupSignalHandler);
+
+  programName = argv[0];
+  char* vncServerName = 0;
+  Display* dpy;
+
+  for (int i = 1; i < argc; i++) {
+    if (Configuration::setParam(argv[i]))
+      continue;
+
+    if (argv[i][0] == '-') {
+      if (i+1 < argc) {
+        if (Configuration::setParam(&argv[i][1], argv[i+1])) {
+          i++;
+          continue;
+        }
+      }
+      usage();
+    }
+
+    if (vncServerName)
+      usage();
+    vncServerName = argv[i];
+  }
+
+  try {
+    TcpSocket::initTcpSockets();
+
+    Socket* sock = 0;
+
+    if (listenMode) {
+      int port = 5500;
+      if (vncServerName && isdigit(vncServerName[0]))
+        port = atoi(vncServerName);
+
+      TcpListener listener(port);
+
+      vlog.info("Listening on port %d\n",port);
+
+      while (true) {
+        sock = listener.accept();
+        int pid = fork();
+        if (pid < 0) { perror("fork"); exit(1); }
+        if (pid == 0) break; // child
+        delete sock;
+        int status;
+        while (wait3(&status, WNOHANG, 0) > 0) ;
+      }
+    }
+
+    CharArray displaynameStr(displayname.getData());
+    if (!(dpy = XOpenDisplay(TXWindow::strEmptyToNull(displaynameStr.buf)))) {
+      fprintf(stderr,"%s: unable to open display \"%s\"\n",
+              programName, XDisplayName(displaynameStr.buf));
+      exit(1);
+    }
+
+    TXWindow::init(dpy, "Vncviewer");
+    xloginIconifier.iconify(dpy);
+    CConn cc(dpy, argc, argv, sock, vncServerName);
+
+    // X events are processed whenever reading from the socket would block.
+
+    while (true) {
+      cc.getInStream()->check(1);
+      cc.processMsg();
+    }
+
+  } catch (rdr::Exception &e) {
+    vlog.error(e.str());
+  }
+
+  return 0;
+}
diff --git a/vncviewer_unix/vncviewer.man b/vncviewer_unix/vncviewer.man
new file mode 100644
index 0000000..a0db436
--- /dev/null
+++ b/vncviewer_unix/vncviewer.man
@@ -0,0 +1,189 @@
+.TH vncviewer 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncviewer \- VNC viewer for X
+.SH SYNOPSIS
+.B vncviewer
+.RI [ options ] 
+.RI [ host ][: display# ]
+.br
+.B vncviewer
+.RI [ options ] 
+.B \-listen
+.RI [ port ]
+.SH DESCRIPTION
+.B vncviewer
+is a viewer (client) for Virtual Network Computing.  This manual page documents
+version 4 for the X window system.
+
+If you run the viewer with no arguments it will prompt you for a VNC server to
+connect to.  Alternatively, specify the VNC server as an argument, e.g.:
+
+.RS
+vncviewer snoopy:2
+.RE
+
+where 'snoopy' is the name of the machine, and '2' is the display number of the
+VNC server on that machine.  Either the machine name or display number can be
+omitted.  So for example ":1" means display number 1 on the same machine, and
+"snoopy" means "snoopy:0" i.e. display 0 on machine "snoopy".
+
+If the VNC server is successfully contacted, you will be prompted for a
+password to authenticate you.  If the password is correct, a window will appear
+showing the desktop of the VNC server.
+
+.SH AUTOMATIC PROTOCOL SELECTION
+
+The viewer tests the speed of the connection to the server and chooses the
+encoding and pixel format (colour level) appropriately.  This makes it much
+easier to use than previous versions where the user had to specify arcane
+command line arguments.
+
+The viewer normally starts out assuming the link is slow, using a low colour
+level and using the encoding with the best compression.  If it turns out that
+the link is fast enough it switches to full-colour mode and will use an
+encoding which compresses less but is faster to generate, thus improving the
+interactive feel.  Automatic selection can be turned off by setting the
+\fBAutoSelect\fP parameter to false, or from the options dialog.
+
+.SH POPUP MENU
+The viewer has a popup menu containing entries which perform various actions.
+It is usually brought up by pressing F8, but this can be configured with the
+MenuKey parameter.  Actions which the popup menu can perform include:
+.RS 2
+.IP * 2
+switching in and out of full-screen mode
+.IP *
+quitting the viewer
+.IP *
+generating key events, e.g. sending ctrl-alt-del
+.IP *
+accessing the options dialog and various other dialogs
+.RE
+.PP
+By default, key presses in the popup menu get sent to the VNC server and
+dismiss the popup.  So to get an F8 through to the VNC server simply press it
+twice.
+
+.SH FULL SCREEN MODE
+A full-screen mode is supported.  This is particularly useful when connecting
+to a remote screen which is the same size as your local one. If the remote
+screen is bigger, you can scroll by bumping the mouse against the edge of the
+screen.
+
+Unfortunately this mode doesn't work completely with all window managers, since
+it breaks the X window management conventions.
+
+.SH OPTIONS (PARAMETERS)
+You can get a list of parameters by giving \fB\-h\fP as a command-line option
+to vncviewer.  Parameters can be turned on with -\fIparam\fP or off with
+-\fIparam\fP=0.  Parameters which take a value can be specified as
+-\fIparam\fP \fIvalue\fP.  Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP
+-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP.  Parameter names are
+case-insensitive.
+
+Many of the parameters can also be set graphically via the options dialog box.
+This can be accessed from the popup menu or from the "Connection details"
+dialog box.
+
+.TP
+.B \-display \fIXdisplay\fP
+Specifies the X display on which the VNC viewer window should appear.
+
+.TP
+.B \-geometry \fIgeometry\fP
+Standard X position and sizing specification.
+
+.TP
+.B \-listen \fI[port]\fP
+Causes vncviewer to listen on the given port (default 5500) for reverse
+connections from a VNC server.  WinVNC supports reverse connections initiated
+using the 'Add New Client' menu option or the '\-connect' command-line option.
+Xvnc supports reverse connections with a helper program called
+.B vncconfig.
+
+.TP
+.B \-passwd \fIpassword-file\fP
+If you are on a filesystem which gives you access to the password file used by
+the server, you can specify it here to avoid typing it in.  It will usually be
+"~/.vnc/passwd".
+
+.TP
+.B \-Shared
+When you make a connection to a VNC server, all other existing connections are
+normally closed.  This option requests that they be left open, allowing you to
+share the desktop with someone already using it.
+
+.TP
+.B \-ViewOnly
+Specifies that no keyboard or mouse events should be sent to the server.
+Useful if you want to view a desktop without interfering; often needs to be
+combined with
+.B \-Shared.
+
+.TP
+.B \-FullScreen
+Start in full-screen mode.
+
+.TP
+.B \-AutoSelect
+Use automatic selection of encoding and pixel format (default is on).  Normally
+the viewer tests the speed of the connection to the server and chooses the
+encoding and pixel format appropriately.  Turn it off with \fB-AutoSelect=0\fP.
+
+.TP
+.B \-FullColour, \-FullColor
+Tells the VNC server to send full-colour pixels in the best format for this
+display.  By default a low colour mode is used until AutoSelect decides the
+link is fast enough.  However if the server's native pixel format is
+colourmapped (as opposed to truecolour) then the server's format is used by
+default.
+
+.TP
+.B \-LowColourLevel \fIlevel\fP
+Selects the reduced colour level to use on slow links.  \fIlevel\fP can range
+from 0 to 2, 0 meaning 8 colours, 1 meaning 64 colours (the default), 2 meaning
+256 colours.
+
+.TP
+.B \-PreferredEncoding \fIencoding\fP
+This option specifies the preferred encoding to use from one of "ZRLE",
+"hextile" or "raw".
+
+.TP
+.B -UseLocalCursor
+Render the mouse cursor locally if the server supports it (default is on).
+This can make the interactive performance feel much better over slow links.
+
+.TP
+.B \-WMDecorationWidth \fIw\fP, \-WMDecorationHeight \fIh\fP
+The total width and height taken up by window manager decorations.  This is
+used to calculate the maximum size of the VNC viewer window.  Default is
+width 6, height 24.
+
+.TP
+.B \-log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
+Configures the debug log settings.  \fIdest\fP can currently be \fBstderr\fP or
+\fBstdout\fP, and \fIlevel\fP is between 0 and 100, 100 meaning most verbose
+output.  \fIlogname\fP is usually \fB*\fP meaning all, but you can target a
+specific source file if you know the name of its "LogWriter".  Default is
+\fB*:stderr:30\fP.
+
+.TP
+.B \-MenuKey \fIkeysym-name\fP
+This option specifies the key which brings up the popup menu.  The key is
+specified as an X11 keysym name (these can be obtained by removing the XK_
+prefix from the entries in "/usr/include/X11/keysymdef.h").  Default is F8.
+
+.SH SEE ALSO
+.BR Xvnc (1)
+.BR vncconfig (1),
+.BR vncserver (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/winvnc/AddNewClientDialog.h b/winvnc/AddNewClientDialog.h
new file mode 100644
index 0000000..d8e0af3
--- /dev/null
+++ b/winvnc/AddNewClientDialog.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- AddnewClientDialog.h
+
+#ifndef __WINVNC_ADD_NEW_CLIENT_DIALOG_H__
+#define __WINVNC_ADD_NEW_CLIENT_DIALOG_H__
+
+#include <winvnc/resource.h>
+#include <rfb_win32/Dialog.h>
+//#include <rfb_win32/TCharArray.h>
+
+namespace winvnc {
+
+  class AddNewClientDialog : public rfb::win32::Dialog {
+  public:
+    AddNewClientDialog() : Dialog(GetModuleHandle(0)) {}
+    // - Show the dialog and return true if OK was clicked,
+    //   false in case of error or Cancel
+    virtual bool showDialog() {
+      return Dialog::showDialog(MAKEINTRESOURCE(IDD_ADD_NEW_CLIENT));
+    }
+    const char* getHostName() const {return hostName.buf;}
+  protected:
+
+    // Dialog methods (protected)
+    virtual void initDialog() {
+      if (hostName.buf)
+        setItemString(IDC_HOST, rfb::TStr(hostName.buf));
+    }
+    virtual bool onOk() {
+      hostName.replaceBuf(rfb::strDup(rfb::CStr(getItemString(IDC_HOST))));
+      return true;
+    }
+
+    rfb::CharArray hostName;
+  };
+
+};
+
+#endif
diff --git a/winvnc/JavaViewer.cxx b/winvnc/JavaViewer.cxx
new file mode 100644
index 0000000..fecbcee
--- /dev/null
+++ b/winvnc/JavaViewer.cxx
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winvnc/JavaViewer.h>
+#include <winvnc/resource.h>
+#include <rdr/MemInStream.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCserverST.h>
+#include <rfb_win32/TCharArray.h>
+
+#define strcasecmp _stricmp
+
+using namespace winvnc;
+using namespace rfb;
+
+
+static rfb::LogWriter vlog("JavaViewerServer");
+
+JavaViewerServer::JavaViewerServer(rfb::VNCServerST* svr) : server(svr) {
+}
+
+JavaViewerServer::~JavaViewerServer() {
+}
+
+rdr::InStream* JavaViewerServer::getFile(const char* name, const char** contentType) {
+  if (strcmp(name, "/") == 0)
+    name = "/index.vnc";
+
+  HRSRC resource = FindResource(0, TStr(name), _T("HTTPFILE"));
+  if (!resource) return 0;
+  HGLOBAL handle = LoadResource(0, resource);
+  if (!handle) return 0;
+  void* buf = LockResource(handle);
+  int len = SizeofResource(0, resource);
+
+  rdr::InStream* is = new rdr::MemInStream(buf, len);
+  if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
+    is = new rdr::SubstitutingInStream(is, this, 20);
+    *contentType = "text/html";
+  }
+  return is;
+}
+
+char* JavaViewerServer::substitute(const char* varName)
+{
+  if (strcmp(varName, "$$") == 0) {
+    return rfb::strDup("$");
+  }
+  if (strcmp(varName, "$PORT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", rfbPort);
+    return str;
+  }
+  if (strcmp(varName, "$WIDTH") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", server->getDesktopSize().x);
+    return str;
+  }
+  if (strcmp(varName, "$HEIGHT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", server->getDesktopSize().y);
+    return str;
+  }
+  if (strcmp(varName, "$APPLETWIDTH") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", server->getDesktopSize().x);
+    return str;
+  }
+  if (strcmp(varName, "$APPLETHEIGHT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", server->getDesktopSize().y + 32);
+    return str;
+  }
+  if (strcmp(varName, "$DESKTOP") == 0) {
+    return rfb::strDup(server->getName());
+  }
+  return 0;
+}
diff --git a/winvnc/JavaViewer.h b/winvnc/JavaViewer.h
new file mode 100644
index 0000000..20af786
--- /dev/null
+++ b/winvnc/JavaViewer.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- JavaViewer.h
+
+// Custom HTTPServer-derived class which serves the Java VNC Viewer
+// to clients, using resource files compiled in to the WinVNC executable.
+
+#ifndef WINVNC_JAVA_VIEWER
+#define WINVNC_JAVA_VIEWER
+
+#include <rfb/HTTPServer.h>
+#include <rfb/VNCServerST.h>
+#include <rdr/SubstitutingInStream.h>
+
+namespace winvnc {
+
+  class JavaViewerServer : public rfb::HTTPServer, public rdr::Substitutor {
+  public:
+    JavaViewerServer(rfb::VNCServerST* desktop);
+    virtual ~JavaViewerServer();
+
+    virtual rdr::InStream* getFile(const char* name, const char** contentType);
+
+    // rdr::Substitutor callback
+    virtual char* substitute(const char* varName);
+
+    void setRFBport(int port) {
+      rfbPort = port;
+    }
+  protected:
+    int rfbPort;
+    rfb::VNCServerST* server;
+  };
+
+};
+
+#endif
+
diff --git a/winvnc/QueryConnectDialog.cxx b/winvnc/QueryConnectDialog.cxx
new file mode 100644
index 0000000..52d7249
--- /dev/null
+++ b/winvnc/QueryConnectDialog.cxx
@@ -0,0 +1,100 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <winvnc/QueryConnectDialog.h>
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/resource.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb_win32/Service.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+
+static LogWriter vlog("QueryConnectDialog");
+
+static IntParameter timeout("QueryConnectTimeout",
+                            "Number of seconds to show the Accept Connection dialog before "
+                            "rejecting the connection",
+                            10);
+
+
+// - Visible methods
+
+QueryConnectDialog::QueryConnectDialog(network::Socket* sock_,
+                                       const char* userName_,
+                                       VNCServerWin32* s)
+: Thread("QueryConnectDialog"), Dialog(GetModuleHandle(0)),
+  sock(sock_), approve(false), server(s) {
+  peerIp.buf = sock->getPeerAddress();
+  userName.buf = strDup(userName_);
+}
+
+void QueryConnectDialog::startDialog() {
+  start();
+}
+
+
+// - Thread overrides
+
+void QueryConnectDialog::run() {
+  countdown = timeout;
+  try {
+    if (desktopChangeRequired() && !changeDesktop())
+      throw rdr::Exception("changeDesktop failed");
+    approve = Dialog::showDialog(MAKEINTRESOURCE(IDD_QUERY_CONNECT));
+    server->queryConnectionComplete();
+  } catch (...) {
+    server->queryConnectionComplete();
+    throw;
+  }
+}
+
+
+// - Dialog overrides
+
+void QueryConnectDialog::initDialog() {
+  if (!SetTimer(handle, 1, 1000, 0))
+    throw rdr::SystemException("SetTimer", GetLastError());
+  setItemString(IDC_QUERY_HOST, TStr(peerIp.buf));
+  if (!userName.buf)
+    userName.buf = strDup("(anonymous)");
+  setItemString(IDC_QUERY_USER, TStr(userName.buf));
+  setCountdownLabel();
+}
+
+void QueryConnectDialog::setCountdownLabel() {
+  TCHAR buf[16];
+  _stprintf(buf, _T("%d"), countdown);
+  setItemString(IDC_QUERY_COUNTDOWN, buf);
+}
+
+BOOL QueryConnectDialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (msg == WM_TIMER) {
+    if (--countdown == 0 || desktopChangeRequired()) {
+      DestroyWindow(hwnd);
+    } else {
+      setCountdownLabel();
+    }
+    return TRUE;
+  } else {
+    return Dialog::dialogProc(hwnd, msg, wParam, lParam);
+  }
+}
diff --git a/winvnc/QueryConnectDialog.h b/winvnc/QueryConnectDialog.h
new file mode 100644
index 0000000..30dd270
--- /dev/null
+++ b/winvnc/QueryConnectDialog.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- QueryConnectDialog.h
+
+#ifndef __WINVNC_QUERY_CONNECT_DIALOG_H__
+#define __WINVNC_QUERY_CONNECT_DIALOG_H__
+
+#include <rfb/Threading.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+namespace network { class Socket; }
+
+namespace winvnc {
+
+  class VNCServerWin32;
+
+  class QueryConnectDialog : public rfb::Thread, rfb::win32::Dialog {
+  public:
+    QueryConnectDialog(network::Socket* sock, const char* userName, VNCServerWin32* s);
+    virtual void startDialog();
+    virtual void run();
+    network::Socket* getSock() {return sock;}
+    bool isAccepted() const {return approve;}
+  protected:
+
+    // Dialog methods (protected)
+    virtual void initDialog();
+    virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+    // Custom internal methods
+    void setCountdownLabel();
+
+    int countdown;
+    network::Socket* sock;
+    rfb::CharArray peerIp;
+    rfb::CharArray userName;
+    bool approve;
+    VNCServerWin32* server;
+  };
+
+};
+
+#endif
diff --git a/winvnc/STrayIcon.cxx b/winvnc/STrayIcon.cxx
new file mode 100644
index 0000000..7cfea3c
--- /dev/null
+++ b/winvnc/STrayIcon.cxx
@@ -0,0 +1,234 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WinVNC Version 4.0 Tray Icon implementation
+
+#include <winvnc/STrayIcon.h>
+#include <winvnc/resource.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/TrayIcon.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/CurrentUser.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+
+static LogWriter vlog("STrayIcon");
+
+BoolParameter STrayIconThread::disableOptions("DisableOptions", "Disable the Options entry in the VNC Server tray menu.", false);
+
+
+//
+// -=- AboutDialog global values
+//
+
+const WORD rfb::win32::AboutDialog::DialogId = IDD_ABOUT;
+const WORD rfb::win32::AboutDialog::Copyright = IDC_COPYRIGHT;
+const WORD rfb::win32::AboutDialog::Version = IDC_VERSION;
+const WORD rfb::win32::AboutDialog::BuildTime = IDC_BUILDTIME;
+const WORD rfb::win32::AboutDialog::Description = IDC_DESCRIPTION;
+
+
+//
+// -=- Internal tray icon class
+//
+
+const UINT WM_SET_TOOLTIP = WM_USER + 1;
+
+
+class winvnc::STrayIcon : public TrayIcon {
+public:
+  STrayIcon(STrayIconThread& t) : thread(t),
+    vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),
+    vncConnect(_T("winvnc4.exe"), _T("-connect")) {
+
+    // ***
+    SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));
+    // ***
+
+    SetTimer(getHandle(), 1, 3000, 0);
+    PostMessage(getHandle(), WM_TIMER, 1, 0);
+    PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);
+  }
+
+  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+    switch(msg) {
+
+    case WM_USER:
+      {
+        bool userKnown = CurrentUserToken().isValid();
+        bool allowOptions = !STrayIconThread::disableOptions && userKnown;
+
+        switch (lParam) {
+        case WM_LBUTTONDBLCLK:
+          SendMessage(getHandle(), WM_COMMAND, allowOptions ? ID_OPTIONS : ID_ABOUT, 0);
+          break;
+        case WM_RBUTTONUP:
+          HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(thread.menu));
+          HMENU trayMenu = GetSubMenu(menu, 0);
+
+
+          // Default item is Options, if available, or About if not
+          SetMenuDefaultItem(trayMenu, allowOptions ? ID_OPTIONS : ID_ABOUT, FALSE);
+          
+          // Enable/disable options as required
+          EnableMenuItem(trayMenu, ID_OPTIONS, (!allowOptions ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+          EnableMenuItem(trayMenu, ID_CONNECT, (!userKnown ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+          EnableMenuItem(trayMenu, ID_CLOSE, (isServiceProcess() ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+
+          // SetForegroundWindow is required, otherwise Windows ignores the
+          // TrackPopupMenu because the window isn't the foreground one, on
+          // some older Windows versions...
+          SetForegroundWindow(getHandle());
+
+          // Display the menu
+          POINT pos;
+          GetCursorPos(&pos);
+          TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
+          break;
+			  } 
+			  return 0;
+      }
+    
+      // Handle tray icon menu commands
+    case WM_COMMAND:
+      switch (LOWORD(wParam)) {
+      case ID_OPTIONS:
+        {
+          CurrentUserToken token;
+          if (token.isValid())
+            vncConfig.start(isServiceProcess() ? (HANDLE)token : 0);
+          else
+            vlog.error("Options: unknown current user");
+        }
+        break;
+      case ID_CONNECT:
+        {
+          CurrentUserToken token;
+          if (token.isValid())
+            vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);
+          else
+            vlog.error("Options: unknown current user");
+        }
+        break;
+      case ID_DISCONNECT:
+        thread.server.disconnectClients("tray menu disconnect");
+        break;
+      case ID_CLOSE:
+        if (!isServiceProcess())
+          thread.server.stop();
+        break;
+      case ID_ABOUT:
+        AboutDialog::instance.showDialog();
+        break;
+      }
+      return 0;
+
+      // Handle commands send by other processes
+    case WM_COPYDATA:
+      {
+        COPYDATASTRUCT* command = (COPYDATASTRUCT*)lParam;
+        switch (command->dwData) {
+        case 1:
+          {
+            CharArray viewer = new char[command->cbData + 1];
+            memcpy(viewer.buf, command->lpData, command->cbData);
+            viewer.buf[command->cbData] = 0;
+            thread.server.addNewClient(viewer.buf);
+          }
+          break;
+        case 2:
+          thread.server.disconnectClients("IPC disconnect");
+          break;
+        };
+      };
+      break;
+
+    case WM_CLOSE:
+      PostQuitMessage(0);
+      break;
+
+    case WM_TIMER:
+      if (rfb::win32::desktopChangeRequired()) {
+        SendMessage(getHandle(), WM_CLOSE, 0, 0);
+        return 0;
+      }
+      setIcon(thread.server.isServerInUse() ? thread.activeIcon : thread.inactiveIcon);
+      return 0;
+
+    case WM_SET_TOOLTIP:
+      {
+        rfb::Lock l(thread.lock);
+        if (thread.toolTip.buf)
+          setToolTip(thread.toolTip.buf);
+      }
+      return 0;
+
+    }
+
+    return TrayIcon::processMessage(msg, wParam, lParam);
+  }
+
+protected:
+  LaunchProcess vncConfig;
+  LaunchProcess vncConnect;
+  STrayIconThread& thread;
+};
+
+
+STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, UINT menu_)
+: server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), menu(menu_),
+  windowHandle(0), runTrayIcon(true) {
+  start();
+}
+
+
+void STrayIconThread::run() {
+  while (runTrayIcon) {
+    if (rfb::win32::desktopChangeRequired() && 
+      !rfb::win32::changeDesktop())
+      Sleep(2000);
+
+    STrayIcon icon(*this);
+    windowHandle = icon.getHandle();
+
+    MSG msg;
+    while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+    }
+
+    windowHandle = 0;
+  }
+}
+
+void STrayIconThread::setToolTip(const TCHAR* text) {
+  if (!windowHandle) return;
+  Lock l(lock);
+  delete [] toolTip.buf;
+  toolTip.buf = tstrDup(text);
+  PostMessage(windowHandle, WM_SET_TOOLTIP, 0, 0);
+}
+
+
diff --git a/winvnc/STrayIcon.h b/winvnc/STrayIcon.h
new file mode 100644
index 0000000..cfd5ec0
--- /dev/null
+++ b/winvnc/STrayIcon.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef WINVNC_TRAYICON_H
+#define WINVNC_TRAYICON_H
+
+#include <winvnc/VNCServerWin32.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/Configuration.h>
+#include <rfb/Threading.h>
+
+namespace winvnc {
+
+  class STrayIconThread : rfb::Thread {
+  public:
+    STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon,
+      UINT activeIcon, UINT menu);
+    virtual ~STrayIconThread() {
+      runTrayIcon = false;
+      PostThreadMessage(getThreadId(), WM_QUIT, 0, 0);
+    }
+
+    virtual void run();
+
+    void setToolTip(const TCHAR* text);
+
+    static rfb::BoolParameter disableOptions;
+
+    friend class STrayIcon;
+  protected:
+    rfb::Mutex lock;
+    HWND windowHandle;
+    rfb::TCharArray toolTip;
+    VNCServerWin32& server;
+    UINT inactiveIcon;
+    UINT activeIcon;
+    UINT menu;
+    bool runTrayIcon;
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/winvnc/VNCServerService.cxx b/winvnc/VNCServerService.cxx
new file mode 100644
index 0000000..c967a5a
--- /dev/null
+++ b/winvnc/VNCServerService.cxx
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WinVNC Version 4.0 Service-Mode implementation
+
+#include <winvnc/VNCServerService.h>
+#include <rfb_win32/OSVersion.h>
+
+using namespace winvnc;
+using namespace rfb;
+using namespace win32;
+
+const TCHAR* winvnc::VNCServerService::Name = _T("WinVNC4");
+
+
+VNCServerService::VNCServerService(VNCServerWin32& s)
+  : Service(Name), server(s) {
+  // - Set the service-mode logging defaults
+  //   These will be overridden by the Log option in the
+  //   registry, if present.
+  if (osVersion.isPlatformNT)
+    logParams.setParam("*:EventLog:0,Connections:EventLog:100");
+  else
+    logParams.setParam("*:file:0,Connections:file:100");
+}
+
+
+DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[]) {
+  setStatus(SERVICE_RUNNING);
+  int result = server.run();
+  setStatus(SERVICE_STOP_PENDING);
+  return result;
+}
+
+void VNCServerService::stop() {
+  server.stop();
+}
diff --git a/winvnc/VNCServerService.h b/winvnc/VNCServerService.h
new file mode 100644
index 0000000..378ad0c
--- /dev/null
+++ b/winvnc/VNCServerService.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __WINVNC_SERVICEMODE_H__
+#define __WINVNC_SERVICEMODE_H__
+
+#include <winvnc/VNCServerWin32.h>
+#include <rfb_win32/Service.h>
+
+namespace winvnc {
+
+  class VNCServerService : public rfb::win32::Service {
+  public:
+    VNCServerService(VNCServerWin32& s);
+
+    DWORD serviceMain(int argc, TCHAR* argv[]);
+    void stop();
+
+    void osShuttingDown() {}
+    void readParams() {}
+
+    static const TCHAR* Name;
+  protected:
+    VNCServerWin32& server;
+  };
+
+};
+
+#endif
diff --git a/winvnc/VNCServerWin32.cxx b/winvnc/VNCServerWin32.cxx
new file mode 100644
index 0000000..a870cb1
--- /dev/null
+++ b/winvnc/VNCServerWin32.cxx
@@ -0,0 +1,341 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- WinVNC Version 4.0 Main Routine
+
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/resource.h>
+#include <winvnc/STrayIcon.h>
+
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Service.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/Hostname.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+using namespace network;
+
+static LogWriter vlog("VNCServerWin32");
+
+
+const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\RealVNC\\WinVNC4");
+
+const UINT VNCM_REG_CHANGED = WM_USER;
+const UINT VNCM_COMMAND = WM_USER + 1;
+
+
+static IntParameter http_port("HTTPPortNumber",
+  "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
+static IntParameter port_number("PortNumber",
+  "TCP/IP port on which the server will accept connections", 5900);
+static StringParameter hosts("Hosts",
+  "Filter describing which hosts are allowed access to this server", "+");
+static VncAuthPasswdConfigParameter vncAuthPasswd;
+static BoolParameter localHost("LocalHost",
+  "Only accept connections from via the local loop-back network interface", false);
+
+
+// -=- ManagedListener
+//     Wrapper class which simplifies the management of a listening socket
+//     on a specified port, attached to a SocketManager and SocketServer.
+//     Ensures that socket and filter are deleted and updated appropriately.
+
+class ManagedListener {
+public:
+  ManagedListener(win32::SocketManager* mgr, SocketServer* svr)
+    : sock(0), filter(0), port(0), manager(mgr),
+      server(svr), localOnly(0) {}
+  ~ManagedListener() {setPort(0);}
+  void setPort(int port, bool localOnly=false);
+  void setFilter(const char* filter);
+  TcpListener* sock;
+protected:
+  TcpFilter* filter;
+  win32::SocketManager* manager;
+  SocketServer* server;
+  int port;
+  bool localOnly;
+};
+
+// - If the port number/localHost setting has changed then tell the
+//   SocketManager to shutdown and delete it.  Also remove &
+//   delete the filter.  Then try to open a socket on the new port.
+void ManagedListener::setPort(int newPort, bool newLocalOnly) {
+  if ((port == newPort) && (localOnly == newLocalOnly) && sock) return;
+  if (sock) {
+    vlog.info("Closed TcpListener on port %d", port);
+    sock->setFilter(0);
+    delete filter;
+    manager->remListener(sock);
+    sock = 0;
+    filter = 0;
+  }
+  port = newPort;
+  localOnly = newLocalOnly;
+  if (port != 0) {
+    try {
+      sock = new TcpListener(port, localOnly);
+      vlog.info("Created TcpListener on port %d%s", port,
+                localOnly ? "(localhost)" : "(any)");
+    } catch (rdr::Exception& e) {
+      vlog.error("TcpListener on port %d failed (%s)", port, e.str());
+    }
+  }
+  if (sock)
+    manager->addListener(sock, server);
+}
+
+void ManagedListener::setFilter(const char* newFilter) {
+  if (!sock) return;
+  vlog.info("Updating TcpListener filter");
+  sock->setFilter(0);
+  delete filter;
+  filter = new TcpFilter(newFilter);
+  sock->setFilter(filter);
+}
+
+
+VNCServerWin32::VNCServerWin32()
+  : vncServer(CStr(ComputerName().buf), &desktop),
+    httpServer(0), runServer(false),
+    isDesktopStarted(false),
+    command(NoCommand), commandSig(commandLock),
+    queryConnectDialog(0) {
+  // Create the Java-viewer HTTP server
+  httpServer = new JavaViewerServer(&vncServer);
+
+  // Initialise the desktop
+  desktop.setStatusLocation(&isDesktopStarted);
+
+  // Initialise the VNC server
+  vncServer.setQueryConnectionHandler(this);
+
+  // Register the desktop's event to be handled
+  sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
+}
+
+VNCServerWin32::~VNCServerWin32() {
+  // Stop the SDisplay from updating our state
+  desktop.setStatusLocation(0);
+
+  // Destroy the HTTP server
+  delete httpServer;
+}
+
+
+int VNCServerWin32::run() {
+  { Lock l(runLock);
+    hostThread = Thread::self();
+    runServer = true;
+  }
+
+  // - Register for notification of configuration changes
+  if (isServiceProcess())
+    config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
+  else
+    config.setKey(HKEY_CURRENT_USER, RegConfigPath);
+  config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
+
+  // - Create the tray icon if possible
+  STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
+
+  DWORD result = 0;
+  try {
+    // - Create some managed listening sockets
+    ManagedListener rfb(&sockMgr, &vncServer);
+    ManagedListener http(&sockMgr, httpServer);
+
+    // - Continue to operate until WM_QUIT is processed
+    MSG msg;
+    do {
+      // -=- Make sure we're listening on the right ports.
+      rfb.setPort(port_number, localHost);
+      http.setPort(http_port, localHost);
+
+      // -=- Update the Java viewer's web page port number.
+      httpServer->setRFBport(rfb.sock ? port_number : 0);
+
+      // -=- Update the TCP address filter for both ports, if open.
+      CharArray pattern;
+      pattern.buf = hosts.getData();
+      if (!localHost) {
+        rfb.setFilter(pattern.buf);
+        http.setFilter(pattern.buf);
+      }
+
+      // - If there is a listening port then add the address to the
+      //   tray icon's tool-tip text.
+      {
+        const TCHAR* prefix = isServiceProcess() ?
+          _T("VNC Server (Service):") : _T("VNC Server (User):");
+
+        std::list<char*> addrs;
+        if (rfb.sock)
+          rfb.sock->getMyAddresses(&addrs);
+        else
+          addrs.push_front(strDup("Not accepting connections"));
+
+        std::list<char*>::iterator i, next_i;
+        int length = _tcslen(prefix)+1;
+        for (i=addrs.begin(); i!= addrs.end(); i++)
+          length += strlen(*i) + 1;
+
+        TCharArray toolTip(length);
+        _tcscpy(toolTip.buf, prefix);
+        for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
+          next_i = i; next_i ++;
+          TCharArray addr = *i;    // Assumes ownership of string
+          _tcscat(toolTip.buf, addr.buf);
+          if (next_i != addrs.end())
+            _tcscat(toolTip.buf, _T(","));
+        }
+        trayIcon.setToolTip(toolTip.buf);
+      }
+
+      vlog.debug("Entering message loop");
+
+      // - Run the server until the registry changes, or we're told to quit
+      while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
+        if (msg.hwnd == 0) {
+          if (msg.message == VNCM_REG_CHANGED)
+            break;
+          if (msg.message == VNCM_COMMAND)
+            doCommand();
+        }
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+      }
+
+    } while ((msg.message != WM_QUIT) || runServer);
+
+    vlog.debug("Server exited cleanly");
+  } catch (rdr::SystemException &s) {
+    vlog.error(s.str());
+    result = s.err;
+  } catch (rdr::Exception &e) {
+    vlog.error(e.str());
+  }
+
+  { Lock l(runLock);
+    runServer = false;
+    hostThread = 0;
+  }
+
+  return result;
+}
+
+void VNCServerWin32::stop() {
+  Lock l(runLock);
+  runServer = false;
+  PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
+}
+
+
+bool VNCServerWin32::disconnectClients(const char* reason) {
+  return queueCommand(DisconnectClients, reason, 0);
+}
+
+bool VNCServerWin32::addNewClient(const char* client) {
+  TcpSocket* sock = 0;
+  try {
+    CharArray hostname;
+    int port;
+    getHostAndPort(client, &hostname.buf, &port, 5500);
+    vlog.error("port=%d", port);
+    sock = new TcpSocket(hostname.buf, port);
+    if (queueCommand(AddClient, sock, 0))
+      return true;
+    delete sock;
+  } catch (...) {
+    delete sock;
+  }
+  return false;
+}
+
+
+VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
+                                            const char* userName,
+                                            char** reason)
+{
+  if (queryConnectDialog) {
+    *reason = rfb::strDup("Another connection is currently being queried.");
+    return VNCServerST::REJECT;
+  }
+  queryConnectDialog = new QueryConnectDialog(sock, userName, this);
+  queryConnectDialog->startDialog();
+  return VNCServerST::PENDING;
+}
+
+void VNCServerWin32::queryConnectionComplete() {
+  Thread* qcd = queryConnectDialog;
+  queueCommand(QueryConnectionComplete, 0, 0);
+  delete qcd->join();
+}
+
+
+bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len) {
+  Lock l(commandLock);
+  while (command != NoCommand) commandSig.wait();
+  command = cmd;
+  commandData = data;
+  commandDataLen = len;
+  if (PostThreadMessage(hostThread->getThreadId(), VNCM_COMMAND, 0, 0))
+    while (command != NoCommand) commandSig.wait();
+  else
+    return false;
+  return true;
+}
+
+void VNCServerWin32::doCommand() {
+  Lock l(commandLock);
+  if (command == NoCommand) return;
+
+  // Perform the required command
+  switch (command) {
+
+  case DisconnectClients:
+    // Disconnect all currently active VNC Viewers
+    vncServer.closeClients((const char*)commandData);
+    break;
+
+  case AddClient:
+    // Make a reverse connection to a VNC Viewer
+    vncServer.addClient((network::Socket*)commandData, true);
+    sockMgr.addSocket((network::Socket*)commandData, &vncServer);
+    break;
+
+  case QueryConnectionComplete:
+    // The Accept/Reject dialog has completed
+    // Get the result, then clean it up
+    vncServer.approveConnection(queryConnectDialog->getSock(),
+                                queryConnectDialog->isAccepted(),
+                                "Connection rejected by user");
+    queryConnectDialog = 0;
+    break;
+
+  default:
+    vlog.error("unknown command %d queued", command);
+  };
+
+  // Clear the command and signal completion
+  command = NoCommand;
+  commandSig.signal();
+}
diff --git a/winvnc/VNCServerWin32.h b/winvnc/VNCServerWin32.h
new file mode 100644
index 0000000..c824d54
--- /dev/null
+++ b/winvnc/VNCServerWin32.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#ifndef __VNCSERVER_WIN32_H__
+#define __VNCSERVER_WIN32_H__
+
+#include <winsock2.h>
+#include <network/TcpSocket.h>
+#include <rfb/VNCServerST.h>
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/SocketManager.h>
+#include <rfb_win32/TCharArray.h>
+#include <winvnc/QueryConnectDialog.h>
+#include <winvnc/JavaViewer.h>
+
+namespace winvnc {
+
+  class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler {
+  public:
+    VNCServerWin32();
+    virtual ~VNCServerWin32();
+
+    // Run the server in the current thread
+    int run();
+
+    // Cause the run() call to return
+    // THREAD-SAFE
+    void stop();
+
+    // Determine whether a viewer is active
+    // THREAD-SAFE
+    bool isServerInUse() const {return isDesktopStarted;}
+
+    // Connect out to the specified VNC Viewer
+    // THREAD-SAFE
+    bool addNewClient(const char* client);
+
+    // Disconnect all connected clients
+    // THREAD-SAFE
+    bool disconnectClients(const char* reason=0);
+
+    // Call used to notify VNCServerST of user accept/reject query completion
+    // CALLED FROM AcceptConnectDialog THREAD
+    void queryConnectionComplete();
+
+    // Overridden VNCServerST::QueryConnectionHandler callback,
+    // used to prompt user to accept or reject a connection.
+    // CALLBACK IN VNCServerST "HOST" THREAD
+    virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
+                                                          const char* userName,
+                                                          char** reason);
+
+    // Where to read the configuration settings from
+    static const TCHAR* RegConfigPath;
+
+  protected:
+
+    // Perform a particular internal function in the server thread
+    typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete} Command;
+    bool queueCommand(Command cmd, const void* data, int len);
+    void doCommand();
+    Command command;
+    const void* commandData;
+    int commandDataLen;
+    rfb::Mutex commandLock;
+    rfb::Condition commandSig;
+
+    // VNCServerWin32 Server-internal state
+    rfb::win32::SDisplay desktop;
+    rfb::VNCServerST vncServer;
+    rfb::Mutex runLock;
+    rfb::Thread* hostThread;
+    bool runServer;
+    bool isDesktopStarted;
+    JavaViewerServer* httpServer;
+    rfb::win32::RegistryReader config;
+    rfb::win32::SocketManager sockMgr;
+    QueryConnectDialog* queryConnectDialog;
+  };
+
+};
+
+#endif
diff --git a/winvnc/buildTime.cxx b/winvnc/buildTime.cxx
new file mode 100644
index 0000000..bab2e13
--- /dev/null
+++ b/winvnc/buildTime.cxx
@@ -0,0 +1 @@
+const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file
diff --git a/winvnc/connected.ico b/winvnc/connected.ico
new file mode 100644
index 0000000..d996bcd
--- /dev/null
+++ b/winvnc/connected.ico
Binary files differ
diff --git a/winvnc/java/index.vnc b/winvnc/java/index.vnc
new file mode 100644
index 0000000..aecb613
--- /dev/null
+++ b/winvnc/java/index.vnc
@@ -0,0 +1,13 @@
+<HTML>
+<HEAD>
+<TITLE>
+VNC viewer for Java
+</TITLE>
+</HEAD>
+<BODY>
+<APPLET CODE=vncviewer/VNCViewer.class ARCHIVE=vncviewer.jar
+        WIDTH=400 HEIGHT=250>
+<PARAM name="port" value="$PORT">
+</APPLET>
+</BODY>
+</HTML>
diff --git a/winvnc/java/logo150x150.gif b/winvnc/java/logo150x150.gif
new file mode 100644
index 0000000..f1699ba
--- /dev/null
+++ b/winvnc/java/logo150x150.gif
Binary files differ
diff --git a/winvnc/java/vncviewer.jar b/winvnc/java/vncviewer.jar
new file mode 100644
index 0000000..3c92b5b
--- /dev/null
+++ b/winvnc/java/vncviewer.jar
Binary files differ
diff --git a/winvnc/msvcwarning.h b/winvnc/msvcwarning.h
new file mode 100644
index 0000000..53a0678
--- /dev/null
+++ b/winvnc/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/winvnc/resource.h b/winvnc/resource.h
new file mode 100644
index 0000000..81c89e2
--- /dev/null
+++ b/winvnc/resource.h
@@ -0,0 +1,37 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by winvnc.rc
+//
+#define IDR_MANIFEST                    1
+#define IDI_ICON                        101
+#define IDR_TRAY                        102
+#define IDD_DIALOG1                     103
+#define IDD_ABOUT                       104
+#define IDI_CONNECTED                   105
+#define IDR_VNCVIEWER_JAR               106
+#define IDD_QUERY_CONNECT               107
+#define IDD_ADD_NEW_CLIENT              108
+#define IDC_DESCRIPTION                 1000
+#define IDC_BUILDTIME                   1001
+#define IDC_VERSION                     1002
+#define IDC_COPYRIGHT                   1003
+#define IDC_QUERY_COUNTDOWN             1008
+#define IDC_QUERY_USER                  1009
+#define IDC_QUERY_HOST                  1010
+#define IDC_HOST                        1011
+#define ID_OPTIONS                      40001
+#define ID_CLOSE                        40002
+#define ID_ABOUT                        40003
+#define ID_DISCONNECT                   40004
+#define ID_CONNECT                      40005
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        109
+#define _APS_NEXT_COMMAND_VALUE         40006
+#define _APS_NEXT_CONTROL_VALUE         1012
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/winvnc/winvnc.cxx b/winvnc/winvnc.cxx
new file mode 100644
index 0000000..5ba6ebc
--- /dev/null
+++ b/winvnc/winvnc.cxx
@@ -0,0 +1,249 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- VNC Server 4.0 for Windows (WinVNC4)
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/VNCServerService.h>
+#include <winvnc/AddNewClientDialog.h>
+
+#include <rfb/Logger_stdio.h>
+#include <rfb/Logger_file.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <network/TcpSocket.h>
+
+using namespace winvnc;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("main");
+
+TStr rfb::win32::AppName("VNC Server");
+
+
+static bool runAsService = false;
+static bool runServer = true;
+static bool close_console = false;
+
+
+//
+// -=- processParams
+//     Read in the command-line parameters and interpret them.
+//
+
+void
+programInfo() {
+  win32::FileVersionInfo inf;
+  _tprintf(_T("%s - %s, Version %s\n"),
+    inf.getVerString(_T("ProductName")),
+    inf.getVerString(_T("FileDescription")),
+    inf.getVerString(_T("FileVersion")));
+  printf("%s\n", buildTime);
+  _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright")));
+}
+
+void
+programUsage() {
+  printf("Command-line options:\n");
+  printf("  -connect [<host[::port]>]            - Connect an existing WinVNC server to a listening viewer.\n");
+  printf("  -disconnect                          - Disconnect all clients from an existing WinVNC server.\n");
+  printf("  -register <options...>               - Register WinVNC server as a system service.\n");
+  printf("  -unregister                          - Remove WinVNC server from the list of system services.\n");
+  printf("  -start                               - Start the WinVNC server system service.\n");
+  printf("  -stop                                - Stop the WinVNC server system service.\n");
+  printf("  -status                              - Query the WinVNC service status.\n");
+  printf("  -help                                - Provide usage information.\n");
+  printf("  -noconsole                           - Run without a console (i.e. no stderr/stdout)\n");
+  printf("  <setting>=<value>                    - Set the named configuration parameter.\n");
+  printf("    (Parameter values specified on the command-line override those specified by other configuration methods.)\n");
+  printf("\nLog names:\n");
+  LogWriter::listLogWriters();
+  printf("\nLog destinations:\n");
+  Logger::listLoggers();
+  printf("\nAvailable configuration parameters:\n");
+  Configuration::listParams();
+}
+
+void
+processParams(int argc, const char* argv[]) {
+  for (int i=1; i<argc; i++) {
+    try {
+
+      if (strcasecmp(argv[i], "-connect") == 0) {
+        runServer = false;
+        CharArray host;
+        if (i+1 < argc) {
+          host.buf = strDup(argv[i+1]);
+        } else {
+          AddNewClientDialog ancd;
+          if (ancd.showDialog())
+            host.buf = strDup(ancd.getHostName());
+        }
+        if (host.buf) {
+          HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+          COPYDATASTRUCT copyData;
+          copyData.dwData = 1; // *** AddNewClient
+          copyData.cbData = strlen(host.buf);
+          copyData.lpData = (void*)host.buf;
+          i++;
+          SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
+          printf("Sent connect request to VNC Server...\n");
+        }
+      } else if (strcasecmp(argv[i], "-disconnect") == 0) {
+        HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+        COPYDATASTRUCT copyData;
+        copyData.dwData = 2; // *** DisconnectClients
+        copyData.lpData = 0;
+        copyData.cbData = 0;
+        SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
+        printf("Sent disconnect request to VNC Server...\n");
+        runServer = false;
+      } else if (strcasecmp(argv[i], "-start") == 0) {
+        printf("Attempting to start service...\n");
+        runServer = false;
+        if (rfb::win32::startService(VNCServerService::Name))
+          printf("Started service successfully\n");
+      } else if (strcasecmp(argv[i], "-stop") == 0) {
+        printf("Attempting to stop service...\n");
+        runServer = false;
+        if (rfb::win32::stopService(VNCServerService::Name))
+          printf("Stopped service successfully\n");
+      } else if (strcasecmp(argv[i], "-status") == 0) {
+        printf("Querying service status...\n");
+        runServer = false;
+        rfb::win32::printServiceStatus(VNCServerService::Name);
+
+      } else if (strcasecmp(argv[i], "-service") == 0) {
+        printf("Run in service mode\n");
+        runAsService = true;
+
+      } else if (strcasecmp(argv[i], "-register") == 0) {
+        printf("Attempting to register service...\n");
+        runServer = false;
+        int j = i;
+        i = argc;
+        if (rfb::win32::registerService(VNCServerService::Name,
+                                        _T("VNC Server Version 4"),
+                                        argc-(j+1), &argv[j+1]))
+          printf("Registered service successfully\n");
+      } else if (strcasecmp(argv[i], "-unregister") == 0) {
+        printf("Attempting to unregister service...\n");
+        runServer = false;
+        if (rfb::win32::unregisterService(VNCServerService::Name))
+          printf("Unregistered service successfully\n");
+
+      } else if (strcasecmp(argv[i], "-noconsole") == 0) {
+        close_console = true;
+
+      } else if ((strcasecmp(argv[i], "-help") == 0) ||
+        (strcasecmp(argv[i], "--help") == 0) ||
+        (strcasecmp(argv[i], "-h") == 0) ||
+        (strcasecmp(argv[i], "/?") == 0)) {
+        runServer = false;
+        programUsage();
+        break;
+
+      } else {
+        // Try to process <option>=<value>, or -<bool>
+        if (Configuration::setParam(argv[i], true))
+          continue;
+        // Try to process -<option> <value>
+        if ((argv[i][0] == '-') && (i+1 < argc)) {
+          if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+            i++;
+            continue;
+          }
+        }
+        // Nope.  Show them usage and don't run the server
+        runServer = false;
+        programUsage();
+        break;
+      }
+
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+    }
+  }
+}
+
+
+//
+// -=- main
+//
+
+int main(int argc, const char* argv[]) {
+  int result = 0;
+
+  try {
+    // - Initialise the available loggers
+    //freopen("\\\\drupe\\tjr\\WinVNC4.log","ab",stderr);
+    //setbuf(stderr, 0);
+    initStdIOLoggers();
+    initFileLogger("C:\\temp\\WinVNC4.log");
+    rfb::win32::initEventLogLogger(VNCServerService::Name);
+
+    // - By default, just log errors to stderr
+    logParams.setParam("*:stderr:0");
+
+    // - Print program details and process the command line
+    programInfo();
+    processParams(argc, argv);
+
+    // - Run the server if required
+    if (runServer) {
+      if (close_console) {
+        vlog.info("closing console");
+        if (!FreeConsole())
+          vlog.info("unable to close console:%u", GetLastError());
+      }
+
+      network::TcpSocket::initTcpSockets();
+      VNCServerWin32 server;
+
+      if (runAsService) {
+        printf("Starting Service-Mode VNC Server.\n");
+        VNCServerService service(server);
+        service.start();
+        result = service.getStatus().dwWin32ExitCode;
+      } else {
+        printf("Starting User-Mode VNC Server.\n");
+        result = server.run();
+      }
+    }
+
+    vlog.debug("WinVNC service destroyed");
+  } catch (rdr::Exception& e) {
+    try {
+      vlog.error("Fatal Error: %s", e.str());
+    } catch (...) {
+      fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str());
+    }
+    if (!runAsService)
+      MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
+  }
+
+  vlog.debug("WinVNC process quitting");
+  return result;
+}
diff --git a/winvnc/winvnc.dsp b/winvnc/winvnc.dsp
new file mode 100644
index 0000000..aa9580a
--- /dev/null
+++ b/winvnc/winvnc.dsp
@@ -0,0 +1,228 @@
+# Microsoft Developer Studio Project File - Name="winvnc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=winvnc - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "winvnc.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "winvnc.mak" CFG="winvnc - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "winvnc - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "winvnc - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "winvnc - Win32 Debug Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "winvnc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /machine:I386 /out:"../Release/winvnc4.exe"
+# SUBTRACT LINK32 /profile
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "winvnc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"../Debug/winvnc4.exe" /fixed:no
+# SUBTRACT LINK32 /profile
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "winvnc - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "winvnc___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "winvnc___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_CONSOLE" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"../Debug/winvnc4.exe" /fixed:no
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\Debug_Unicode/winvnc4.exe" /fixed:no
+# SUBTRACT LINK32 /pdb:none
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "winvnc - Win32 Release"
+# Name "winvnc - Win32 Debug"
+# Name "winvnc - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\buildTime.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\JavaViewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\QueryConnectDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\STrayIcon.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerService.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AddNewClientDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\JavaViewer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\QueryConnectDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\STrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerService.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerWin32.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\connected.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\java\logo150x150.gif
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.ico
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\java\index.vnc
+# End Source File
+# Begin Source File
+
+SOURCE=.\java\vncviewer.jar
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.jar
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc4.exe.manifest
+# End Source File
+# End Target
+# End Project
diff --git a/winvnc/winvnc.ico b/winvnc/winvnc.ico
new file mode 100644
index 0000000..55b16bd
--- /dev/null
+++ b/winvnc/winvnc.ico
Binary files differ
diff --git a/winvnc/winvnc.rc b/winvnc/winvnc.rc
new file mode 100644
index 0000000..22d0ba9
--- /dev/null
+++ b/winvnc/winvnc.rc
@@ -0,0 +1,254 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "RealVNC Ltd.\0"
+            VALUE "FileDescription", "VNC Server for Win32\0"
+            VALUE "FileVersion", "4.0\0"
+            VALUE "InternalName", "WinVNC 4.0\0"
+            VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+            VALUE "LegalTrademarks", "RealVNC\0"
+            VALUE "OriginalFilename", "winvnc4.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "VNC Server 4.0\0"
+            VALUE "ProductVersion", "4.0\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON                ICON    DISCARDABLE     "winvnc.ico"
+IDI_CONNECTED           ICON    DISCARDABLE     "connected.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TRAY MENU DISCARDABLE 
+BEGIN
+    POPUP "Tray Menu"
+    BEGIN
+        MENUITEM "&Options...",                 ID_OPTIONS
+        MENUITEM SEPARATOR
+        MENUITEM "Add &New Client",             ID_CONNECT
+        MENUITEM "&Disconnect Clients",         ID_DISCONNECT
+        MENUITEM SEPARATOR
+        MENUITEM "&Close VNC Server",           ID_CLOSE
+        MENUITEM "&About...",                   ID_ABOUT
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "About VNC Server for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
+    ICON            IDI_ICON,IDC_STATIC,7,7,20,20
+    LTEXT           ">appname<",IDC_DESCRIPTION,40,7,125,18
+    LTEXT           ">version<",IDC_VERSION,165,7,77,18
+    LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
+    LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,202,15
+    LTEXT           "See http://www.realvnc.com for more information on VNC.",
+                    IDC_STATIC,40,55,202,15
+END
+
+IDD_QUERY_CONNECT DIALOG DISCARDABLE  0, 0, 164, 93
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "VNC Server : Accept Connection?"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "&Reject",IDCANCEL,105,72,52,14
+    PUSHBUTTON      "&Accept",IDOK,7,72,53,14
+    RTEXT           "User:",IDC_STATIC,7,10,28,15,SS_CENTERIMAGE
+    RTEXT           "Host:",IDC_STATIC,7,30,28,15,SS_CENTERIMAGE
+    CTEXT           "Seconds until automatic reject:",IDC_STATIC,7,50,113,15,
+                    SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_COUNTDOWN,125,50,32,15,SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_USER,40,10,117,15,SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_HOST,40,30,117,15,SS_CENTERIMAGE
+END
+
+IDD_ADD_NEW_CLIENT DIALOG DISCARDABLE  0, 0, 183, 53
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | 
+    WS_CAPTION | WS_SYSMENU
+CAPTION "VNC Server : Add New Client"
+FONT 8, "MS Sans Serif"
+BEGIN
+    EDITTEXT        IDC_HOST,70,10,105,15,ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "OK",IDOK,70,32,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,125,32,50,14
+    ICON            IDI_ICON,IDC_STATIC,7,10,21,20,SS_REALSIZEIMAGE
+    CONTROL         "Viewer:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | 
+                    SS_CENTERIMAGE | WS_GROUP,35,10,30,15
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTTPFILE
+//
+
+/VNCVIEWER.JAR          HTTPFILE DISCARDABLE    "java\\vncviewer.jar"
+/LOGO150X150.GIF        HTTPFILE DISCARDABLE    "java\\logo150x150.gif"
+/INDEX.VNC              HTTPFILE DISCARDABLE    "java\\index.vnc"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST            24      DISCARDABLE     "winvnc4.exe.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_QUERY_CONNECT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 157
+        VERTGUIDE, 35
+        VERTGUIDE, 40
+        VERTGUIDE, 60
+        VERTGUIDE, 120
+        VERTGUIDE, 125
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 86
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+    END
+
+    IDD_ADD_NEW_CLIENT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 176
+        VERTGUIDE, 35
+        VERTGUIDE, 65
+        VERTGUIDE, 70
+        VERTGUIDE, 120
+        VERTGUIDE, 125
+        VERTGUIDE, 175
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 46
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/winvnc/winvnc4.exe.manifest b/winvnc/winvnc4.exe.manifest
new file mode 100644
index 0000000..d5a2b87
--- /dev/null
+++ b/winvnc/winvnc4.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+   version="4.0.0.26"
+   processorArchitecture="X86"
+   name="RealVNC.winvnc4.exe"
+   type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+   <dependentAssembly>
+     <assemblyIdentity
+       type="win32"
+       name="Microsoft.Windows.Common-Controls"
+       version="6.0.0.0"
+       processorArchitecture="X86"
+       publicKeyToken="6595b64144ccf1df"
+       language="*"
+     />
+   </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/wm_hooks/msvcwarning.h b/wm_hooks/msvcwarning.h
new file mode 100644
index 0000000..d0c98c3
--- /dev/null
+++ b/wm_hooks/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/wm_hooks/resource.h b/wm_hooks/resource.h
new file mode 100644
index 0000000..0d8f646
--- /dev/null
+++ b/wm_hooks/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by wm_hooks.rc
+//
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/wm_hooks/wm_hooks.cxx b/wm_hooks/wm_hooks.cxx
new file mode 100644
index 0000000..6923db7
--- /dev/null
+++ b/wm_hooks/wm_hooks.cxx
@@ -0,0 +1,462 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- wm_hooks.cxx
+//
+// Window Message Hooks Dynamic Link library
+
+#define _WIN32_WINNT 0x0400
+#include <tchar.h>
+
+#include <wm_hooks/wm_hooks.h>
+
+UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
+
+UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
+UINT WM_Hooks_WindowChanged() {
+  return WM_HK_WindowChanged;
+}
+
+UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
+UINT WM_Hooks_WindowClientAreaChanged() {
+  return WM_HK_WindowClientAreaChanged;
+}
+
+UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
+UINT WM_Hooks_WindowBorderChanged() {
+  return WM_HK_WindowBorderChanged;
+}
+
+UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
+UINT WM_Hooks_RectangleChanged() {
+  return WM_HK_RectangleChanged;
+}
+
+UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
+UINT WM_Hooks_CursorChanged() {
+  return WM_HK_CursorChanged;
+}
+
+#ifdef _DEBUG
+UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
+UINT WM_Hooks_Diagnostic() {
+  return WM_HK_Diagnostic;
+}
+#endif
+
+ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
+
+//
+// -=- DLL entry point
+//
+
+HINSTANCE dll_instance = 0;
+
+BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID reserved) {
+  switch (reason) {
+  case DLL_PROCESS_ATTACH:
+    dll_instance = (HINSTANCE)instance;
+    return TRUE;
+  case DLL_PROCESS_DETACH:
+    return TRUE;
+  case DLL_THREAD_DETACH:
+    WM_Hooks_Remove(GetCurrentThreadId());
+    return TRUE;
+  default:
+    return TRUE;
+  };
+}
+
+//
+// -=- Display update hooks
+//
+
+#pragma data_seg(".WM_Hooks_Shared")
+DWORD hook_owner = 0;
+DWORD hook_target = 0;
+HHOOK hook_CallWndProc = 0;
+HHOOK hook_CallWndProcRet = 0;
+HHOOK hook_GetMessage = 0;
+HHOOK hook_DialogMessage = 0;
+BOOL enable_cursor_shape = FALSE;
+HCURSOR cursor = 0;
+#ifdef _DEBUG
+UINT diagnostic_min=1;
+UINT diagnostic_max=0;
+#endif
+#pragma data_seg()
+
+#ifdef _DEBUG
+DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
+  diagnostic_min = min; diagnostic_max=max;
+}
+#endif
+
+bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
+  if (hook_owner) {
+    return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
+    /*
+    if (last_event)
+      return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
+    last_event = event;
+    last_wParam = wParam;
+    last_lParam = lParam;
+    return true;
+    */
+  }
+  return false;
+}
+
+bool NotifyWindow(HWND hwnd, UINT msg) {
+  return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyWindowBorder(HWND hwnd, UINT msg) {
+  return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
+  return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyRectangle(RECT* rect) {
+  WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
+  LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
+  return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
+}
+bool NotifyCursor(HCURSOR cursor) {
+  return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
+}
+
+void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM lParam) {
+#ifdef _DEBUG
+  if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
+    PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
+#endif
+  if (!IsWindowVisible(wnd)) return;
+  switch (msg) {
+
+    // -=- Border update events
+	case WM_NCPAINT:
+	case WM_NCACTIVATE:
+    NotifyWindowBorder(wnd, msg);
+		break;
+
+    // -=- Client area update events
+	case BM_SETCHECK:
+	case BM_SETSTATE:
+	case EM_SETSEL:
+	case WM_CHAR:
+	case WM_ENABLE:
+	case WM_KEYUP:
+	case WM_LBUTTONUP:
+	case WM_MBUTTONUP:
+	case WM_PALETTECHANGED:
+	case WM_RBUTTONUP:
+	case WM_SYSCOLORCHANGE:
+	case WM_SETTEXT:
+  case WM_SETFOCUS:
+	//case WM_TIMER:
+    NotifyWindowClientArea(wnd, msg);
+    break;
+	case WM_HSCROLL:
+	case WM_VSCROLL:
+		if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
+			NotifyWindow(wnd, msg);
+		break;
+
+	case WM_WINDOWPOSCHANGING:
+  case WM_DESTROY:
+    {
+      RECT wrect;
+      if (GetWindowRect(wnd, &wrect)) {
+        NotifyRectangle(&wrect);
+      }
+    }
+    break;
+
+	case WM_PAINT:
+    // *** could improve this
+    NotifyWindowClientArea(wnd, msg);
+    break;
+
+    // Handle pop-up menus appearing
+  case 482:
+    NotifyWindow(wnd, 482);
+    break;
+
+    // Handle pop-up menus having items selected
+	case 485:
+		{
+			HANDLE prop = GetProp(wnd, (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0));
+      if (prop != (HANDLE) wParam) {
+        NotifyWindow(wnd, 485);
+				SetProp(wnd,
+					(LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0),
+					(HANDLE) wParam);
+			}
+		}
+		break;
+
+  case WM_NCMOUSEMOVE:
+  case WM_MOUSEMOVE:
+    if (enable_cursor_shape) {
+      HCURSOR new_cursor = GetCursor();
+      if (new_cursor != cursor) {
+        cursor = new_cursor;
+        NotifyCursor(cursor);
+      }
+    }
+    break;
+
+    /* ***
+		if (prf_use_GetUpdateRect)
+		{
+			HRGN region;
+			region = CreateRectRgn(0, 0, 0, 0);
+
+			// Get the affected region
+			if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
+			{
+				int buffsize;
+				UINT x;
+				RGNDATA *buff;
+				POINT TopLeft;
+
+				// Get the top-left point of the client area
+				TopLeft.x = 0;
+				TopLeft.y = 0;
+				if (!ClientToScreen(hWnd, &TopLeft))
+					break;
+
+				// Get the size of buffer required
+				buffsize = GetRegionData(region, 0, 0);
+				if (buffsize != 0)
+				{
+					buff = (RGNDATA *) new BYTE [buffsize];
+					if (buff == NULL)
+						break;
+
+					// Now get the region data
+					if(GetRegionData(region, buffsize, buff))
+					{
+						for (x=0; x<(buff->rdh.nCount); x++)
+						{
+							// Obtain the rectangles from the list
+							RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
+							SendDeferredUpdateRect(
+								hWnd,
+								(SHORT) (TopLeft.x + urect->left),
+								(SHORT) (TopLeft.y + urect->top),
+								(SHORT) (TopLeft.x + urect->right),
+								(SHORT) (TopLeft.y + urect->bottom)
+								);
+						}
+					}
+
+					delete [] buff;
+				}
+			}
+
+			// Now free the region
+			if (region != NULL)
+				DeleteObject(region);
+		}
+    */
+  };
+}
+
+LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode == HC_ACTION) {
+    CWPSTRUCT* info = (CWPSTRUCT*) lParam;
+    ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
+  }
+  return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode == HC_ACTION) {
+    CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
+    ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
+  }
+  return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode == HC_ACTION) {
+    if (wParam & PM_REMOVE) {
+      MSG* msg = (MSG*) lParam;
+      ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
+    }
+  }
+  return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode == HC_ACTION) {
+    MSG* msg = (MSG*) lParam;
+    ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
+  }
+  return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
+}
+
+// - WM_Hooks_Install
+
+BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
+  // - Are there already hooks set?
+  if (hook_owner) {
+    if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
+      WM_Hooks_Remove(hook_owner);
+    } else {
+      return FALSE;
+    }
+  }
+
+  // - Initialise the hooks
+  hook_owner = owner;
+  hook_target = thread;
+
+  hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
+  //hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
+  hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
+  hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
+
+  if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
+    WM_Hooks_Remove(owner);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+// - WM_Hooks_Remove
+
+BOOL WM_Hooks_Remove(DWORD owner) {
+  if (owner != hook_owner) return FALSE;
+  if (hook_CallWndProc) {
+    UnhookWindowsHookEx(hook_CallWndProc);
+    hook_CallWndProc = 0;
+  }
+  if (hook_CallWndProcRet) {
+    UnhookWindowsHookEx(hook_CallWndProcRet);
+    hook_CallWndProcRet = 0;
+  }
+  if (hook_GetMessage) {
+    UnhookWindowsHookEx(hook_GetMessage);
+    hook_GetMessage = 0;
+  }
+  if (hook_DialogMessage) {
+    UnhookWindowsHookEx(hook_DialogMessage);
+    hook_DialogMessage = 0;
+  }
+  hook_owner = 0;
+  hook_target = 0;
+  return TRUE;
+}
+
+//
+// -=- User input hooks
+//
+
+#pragma data_seg(".WM_Hooks_Shared")
+HHOOK hook_keyboard = 0;
+HHOOK hook_pointer = 0;
+bool enable_real_ptr = true;
+bool enable_synth_ptr = true;
+bool enable_real_kbd = true;
+bool enable_synth_kbd = true;
+#pragma data_seg()
+
+#ifdef WH_KEYBOARD_LL
+LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode >= 0) {
+    KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
+    bool real_event = (info->flags & LLKHF_INJECTED) == 0;
+    if ((real_event && !enable_real_kbd) ||
+      (!real_event && !enable_synth_kbd)) {
+      return 1;
+    }
+  }
+  return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
+  if (nCode >= 0) {
+    MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
+    bool real_event = (info->flags & LLMHF_INJECTED) == 0;
+    if ((real_event && !enable_real_ptr) ||
+      (!real_event && !enable_synth_ptr)) {
+      return 1;
+    }
+  }
+  return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
+}
+
+bool RefreshInputHooks() {
+  bool success = true;
+  bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
+  bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
+  if (hook_keyboard && !set_kbd_hook) {
+    UnhookWindowsHookEx(hook_keyboard);
+    hook_keyboard = 0;
+  }
+  if (hook_pointer && !set_ptr_hook) {
+    UnhookWindowsHookEx(hook_pointer);
+    hook_pointer = 0;
+  }
+  if (!hook_keyboard && set_kbd_hook) {
+    hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
+    if (!hook_keyboard) success = false;
+  }
+  if (!hook_pointer && set_ptr_hook) {
+    hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
+    if (!hook_pointer) success = false;
+  }
+  return success;
+}
+#else
+#pragma message("WARNING: low-level mouse and keyboard hooks not supported")
+#endif
+
+// - WM_Hooks_EnableRealInputs
+
+BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
+#ifdef WH_KEYBOARD_LL
+  enable_real_ptr = pointer!=0;
+  enable_real_kbd = keyboard!=0;
+  return RefreshInputHooks();
+#else
+  return FALSE;
+#endif
+}
+
+// - WM_Hooks_EnableSynthInputs
+
+BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
+#ifdef WH_KEYBOARD_LL
+  enable_synth_ptr = pointer!=0;
+  enable_synth_kbd = keyboard!=0;
+  return RefreshInputHooks();
+#else
+  return FALSE;
+#endif
+}
+
+// - WM_Hooks_EnableCursorShape
+
+BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
+  enable_cursor_shape = enable;
+  return FALSE;
+}
diff --git a/wm_hooks/wm_hooks.def b/wm_hooks/wm_hooks.def
new file mode 100644
index 0000000..b9198ab
--- /dev/null
+++ b/wm_hooks/wm_hooks.def
@@ -0,0 +1,5 @@
+LIBRARY      "wm_hooks"
+DESCRIPTION  'Window Message Hooks Dynamic Link Library'
+
+SECTIONS
+	.WM_Hooks_Shared read write shared
diff --git a/wm_hooks/wm_hooks.dsp b/wm_hooks/wm_hooks.dsp
new file mode 100644
index 0000000..3c5c1fd
--- /dev/null
+++ b/wm_hooks/wm_hooks.dsp
@@ -0,0 +1,149 @@
+# Microsoft Developer Studio Project File - Name="wm_hooks" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=wm_hooks - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "wm_hooks.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "wm_hooks.mak" CFG="wm_hooks - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "wm_hooks - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wm_hooks - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wm_hooks - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "wm_hooks - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "../Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "wm_hooks - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "../Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /pdbtype:sept
+# SUBTRACT LINK32 /profile
+
+!ELSEIF  "$(CFG)" == "wm_hooks - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "wm_hooks___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "wm_hooks___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "wm_hooks - Win32 Release"
+# Name "wm_hooks - Win32 Debug"
+# Name "wm_hooks - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\wm_hooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\wm_hooks.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\wm_hooks.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\wm_hooks.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/wm_hooks/wm_hooks.h b/wm_hooks/wm_hooks.h
new file mode 100644
index 0000000..afff4be
--- /dev/null
+++ b/wm_hooks/wm_hooks.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+// -=- wm_hooks.h
+//
+// Window Message Hooks Dynamic Link library
+//
+// This interface is used by the WMHooks class in rfb_win32 to hook the
+// windows on the desktop and receive notifications of changes in their
+// state.
+
+#ifndef __WM_HOOKS_H__
+#define __WM_HOOKS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define DLLEXPORT __declspec(dllexport)
+
+extern "C"
+{
+  //
+  // -=- Display hook message types
+  //
+
+  DLLEXPORT UINT WM_Hooks_WindowChanged();
+  DLLEXPORT UINT WM_Hooks_WindowBorderChanged();
+  DLLEXPORT UINT WM_Hooks_WindowClientAreaChanged();
+  DLLEXPORT UINT WM_Hooks_RectangleChanged();
+  DLLEXPORT UINT WM_Hooks_CursorChanged();
+
+  //
+  // -=- Display update hooks
+  //
+
+  // - WM_Hooks_Install
+  // Add the current thread to the list of threads that will receive
+  // notifications of changes to the display.
+  // If thread is NULL then the entire display will be hooked.
+  // If thread is !NULL and then the specified
+  // thread will be hooked.
+  // Each thread may only register one hook at a time.
+  // The call will fail (return FALSE) if the thread already has hooks
+  // set, or if the hooks cannot be set, or some other error occurs.
+
+  DLLEXPORT BOOL WM_Hooks_Install(DWORD owner, DWORD thread);
+
+  // - WM_Hooks_Remove
+  // Removes any hook set by the current thread.
+  // The return indicates whether anything went wrong removing the hooks,
+  // that might cause problems later.
+
+  DLLEXPORT BOOL WM_Hooks_Remove(DWORD owner);
+
+  //
+  // -=- User input hooks
+  //
+
+  // - WM_Hooks_EnableRealInputs
+  // If TRUE is passed, then "real" input is enabled, otherwise it is disabled.
+
+  DLLEXPORT BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard);
+
+  // - WM_Hooks_EnableSynthInputs
+  // If TRUE is passed, then synthetic inputs are enabled, otherwise disabled.
+
+  DLLEXPORT BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard);
+
+  //
+  // -=- Cursor shape hooking
+  //
+
+  // - WM_Hooks_EnableCursorShape
+  // If TRUE is passed, then hooks will produce notifications when cursor shape
+  // changes.
+
+  DLLEXPORT BOOL WM_Hooks_EnableCursorShape(BOOL enable);
+
+#ifdef _DEBUG
+  // - WM_Hooks_SetDiagnosticRange
+  // Select a range of messages that will be reported while hooks are active
+  DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max);
+  DLLEXPORT UINT WM_Hooks_Diagnostic();
+#endif
+
+}
+
+#endif // __WM_HOOKS_H__
diff --git a/wm_hooks/wm_hooks.rc b/wm_hooks/wm_hooks.rc
new file mode 100644
index 0000000..87d282b
--- /dev/null
+++ b/wm_hooks/wm_hooks.rc
@@ -0,0 +1,109 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "RealVNC Ltd.\0"
+            VALUE "FileDescription", "VNC Server for Win32 Hooking DLL\0"
+            VALUE "FileVersion", "4.0\0"
+            VALUE "InternalName", "WMHooks 4.0\0"
+            VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+            VALUE "LegalTrademarks", "RealVNC\0"
+            VALUE "OriginalFilename", "wm_hooks.dll\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "VNC Server 4.0\0"
+            VALUE "ProductVersion", "4.0\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
+
+#endif    // !_MAC
+
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/x0vncserver/Image.cxx b/x0vncserver/Image.cxx
new file mode 100644
index 0000000..56a197e
--- /dev/null
+++ b/x0vncserver/Image.cxx
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Image.cxx
+//
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+#include "Image.h"
+#include <list>
+
+class ImageCleanup {
+public:
+  std::list<Image*> images;
+
+  ~ImageCleanup()
+  {
+    fprintf(stderr,"~ImageCleanup called\n");
+
+    while (!images.empty()) {
+      delete images.front();
+    }
+  }
+};
+
+ImageCleanup imageCleanup;
+
+static bool caughtShmError = false;
+
+static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
+{
+  caughtShmError = true;
+  return 0;
+}
+
+Image::Image(Display* d, int width, int height)
+  : xim(0), dpy(d), shminfo(0), usingShm(false)
+{
+  if (createShmImage(width, height)) return;
+
+  xim = XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
+                     DefaultDepth(dpy,DefaultScreen(dpy)), ZPixmap,
+                     0, 0, width, height, BitmapPad(dpy), 0);
+
+  xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+  if (!xim->data) {
+    fprintf(stderr,"malloc failed\n");
+    exit(1);
+  }
+}
+
+Image::~Image()
+{
+  fprintf(stderr,"~Image called - usingShm %d\n",usingShm);
+  if (usingShm) {
+    usingShm = false;
+    shmdt(shminfo->shmaddr);
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    imageCleanup.images.remove(this);
+  }
+  delete shminfo;
+  if (xim) XDestroyImage(xim);
+}
+
+void Image::get(Window w)
+{
+  if (usingShm) {
+    XShmGetImage(dpy, w, xim, 0, 0, AllPlanes);
+  } else {
+    XGetSubImage(dpy, w, 0, 0, xim->width, xim->height,
+                 AllPlanes, ZPixmap, xim, 0, 0);
+  }
+}
+
+bool Image::createShmImage(int width, int height)
+{
+  if (XShmQueryExtension(dpy)) {
+    shminfo = new XShmSegmentInfo;
+
+    xim = XShmCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
+                          DefaultDepth(dpy,DefaultScreen(dpy)), ZPixmap,
+                          0, shminfo, width, height);
+
+    if (xim) {
+      shminfo->shmid = shmget(IPC_PRIVATE,
+                              xim->bytes_per_line * xim->height,
+                              IPC_CREAT|0777);
+
+      if (shminfo->shmid != -1) {
+        shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+        if (shminfo->shmaddr != (char *)-1) {
+
+          shminfo->readOnly = False;
+
+          XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
+          XShmAttach(dpy, shminfo);
+          XSync(dpy, False);
+          XSetErrorHandler(oldHdlr);
+
+          if (!caughtShmError) {
+            fprintf(stderr,"Using shared memory XImage\n");
+            usingShm = true;
+            imageCleanup.images.push_back(this);
+            return true;
+          }
+
+          shmdt(shminfo->shmaddr);
+        } else {
+          fprintf(stderr,"shmat failed\n");
+          perror("shmat");
+        }
+
+        shmctl(shminfo->shmid, IPC_RMID, 0);
+      } else {
+        fprintf(stderr,"shmget failed\n");
+        perror("shmget");
+      }
+
+      XDestroyImage(xim);
+      xim = 0;
+    } else {
+      fprintf(stderr,"XShmCreateImage failed\n");
+    }
+  }
+
+  return false;
+}
diff --git a/x0vncserver/Image.h b/x0vncserver/Image.h
new file mode 100644
index 0000000..1a9ff1c
--- /dev/null
+++ b/x0vncserver/Image.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+//
+// Image.h
+//
+
+#ifndef __IMAGE_H__
+#define __IMAGE_H__
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+class Image {
+
+public:
+
+  Image(Display* dpy, int width, int height);
+  ~Image();
+
+  void get(Window w);
+
+  XImage* xim;
+
+private:
+
+  bool createShmImage(int width, int height);
+
+  Display* dpy;
+  XShmSegmentInfo* shminfo;
+  bool usingShm;
+};
+
+#endif
diff --git a/x0vncserver/Makefile.in b/x0vncserver/Makefile.in
new file mode 100644
index 0000000..e0b06cb
--- /dev/null
+++ b/x0vncserver/Makefile.in
@@ -0,0 +1,22 @@
+
+SRCS = Image.cxx x0vncserver.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+program = x0vncserver
+
+DEP_LIBS = ../rfb/librfb.a ../network/libnetwork.a ../rdr/librdr.a
+
+EXTRA_LIBS = @ZLIB_LIB@ @X_PRE_LIBS@ @X_LIBS@ -lXtst -lXext -lX11 @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+	rm -f $(program)
+	$(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/x0vncserver/buildtime.c b/x0vncserver/buildtime.c
new file mode 100644
index 0000000..a96031c
--- /dev/null
+++ b/x0vncserver/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/x0vncserver/x0vncserver.cxx b/x0vncserver/x0vncserver.cxx
new file mode 100644
index 0000000..df40c33
--- /dev/null
+++ b/x0vncserver/x0vncserver.cxx
@@ -0,0 +1,277 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+#include <strings.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/Configuration.h>
+#include <rfb/SSecurityFactoryStandard.h>
+
+#include <network/TcpSocket.h>
+
+#include "Image.h"
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XTest.h>
+
+
+#include <rfb/Encoder.h>
+
+using namespace rfb;
+using namespace rdr;
+using namespace network;
+
+LogWriter vlog("main");
+
+StringParameter displayname("display", "The X display", "");
+IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
+VncAuthPasswdFileParameter vncAuthPasswdFile;
+
+static void CleanupSignalHandler(int sig)
+{
+  // CleanupSignalHandler allows C++ object cleanup to happen because it calls
+  // exit() rather than the default which is to abort.
+  fprintf(stderr,"CleanupSignalHandler called\n");
+  exit(1);
+}
+
+
+class XDesktop : public SDesktop, public rfb::ColourMap
+{
+public:
+  XDesktop(Display* dpy_)
+    : dpy(dpy_), pb(0), server(0), oldButtonMask(0), haveXtest(false)
+  {
+    int xtestEventBase;
+    int xtestErrorBase;
+    int major, minor;
+
+    if (XTestQueryExtension(dpy, &xtestEventBase,
+                            &xtestErrorBase, &major, &minor)) {
+      XTestGrabControl(dpy, True);
+      vlog.info("XTest extension present - version %d.%d",major,minor);
+      haveXtest = true;
+    } else {
+      vlog.info("XTest extension not present");
+      vlog.info("unable to inject events or display while server is grabbed");
+    }
+
+    int dpyWidth = DisplayWidth(dpy, DefaultScreen(dpy));
+    int dpyHeight = DisplayHeight(dpy, DefaultScreen(dpy));
+    Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
+
+    image = new Image(dpy, dpyWidth, dpyHeight);
+    image->get(DefaultRootWindow(dpy));
+
+    pf.bpp = image->xim->bits_per_pixel;
+    pf.depth = image->xim->depth;
+    pf.bigEndian = (image->xim->byte_order == MSBFirst);
+    pf.trueColour = (vis->c_class == TrueColor);
+    pf.redShift   = ffs(vis->red_mask) - 1;
+    pf.greenShift = ffs(vis->green_mask) - 1;
+    pf.blueShift  = ffs(vis->blue_mask) - 1;
+    pf.redMax     = vis->red_mask   >> pf.redShift;
+    pf.greenMax   = vis->green_mask >> pf.greenShift;
+    pf.blueMax    = vis->blue_mask  >> pf.blueShift;
+
+    pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
+                                  (rdr::U8*)image->xim->data, this);
+  }
+  virtual ~XDesktop() {
+    delete pb;
+  }
+
+  void setVNCServer(VNCServer* s) {
+    server = s;
+    server->setPixelBuffer(pb);
+  }
+
+  // -=- SDesktop interface
+
+  virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
+    if (!haveXtest) return;
+    XTestFakeMotionEvent(dpy, DefaultScreen(dpy), pos.x, pos.y, CurrentTime);
+    if (buttonMask != oldButtonMask) {
+      for (int i = 0; i < 5; i++) {
+	if ((buttonMask ^ oldButtonMask) & (1<<i)) {
+          if (buttonMask & (1<<i)) {
+            XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
+          } else {
+            XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
+          }
+        }
+      }
+    }
+    oldButtonMask = buttonMask;
+  }
+
+  virtual void keyEvent(rdr::U32 key, bool down) {
+    if (!haveXtest) return;
+    int keycode = XKeysymToKeycode(dpy, key);
+    if (keycode)
+      XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
+  }
+
+  virtual void clientCutText(const char* str, int len) {
+  }
+
+  virtual Point getFbSize() {
+    return Point(pb->width(), pb->height());
+  }
+
+  // rfb::ColourMap callbacks
+  virtual void lookup(int index, int* r, int* g, int* b) {
+    XColor xc;
+    xc.pixel = index;
+    if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
+      XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
+    } else {
+      xc.red = xc.green = xc.blue = 0;
+    }
+    *r = xc.red;
+    *g = xc.green;
+    *b = xc.blue;
+  }
+
+  virtual void poll() {
+    if (server && server->clientsReadyForUpdate()) {
+      image->get(DefaultRootWindow(dpy));
+      server->add_changed(pb->getRect());
+      server->tryUpdate();
+    }
+  }
+
+protected:
+  Display* dpy;
+  PixelFormat pf;
+  PixelBuffer* pb;
+  VNCServer* server;
+  Image* image;
+  int oldButtonMask;
+  bool haveXtest;
+};
+
+char* programName;
+
+static void usage()
+{
+  fprintf(stderr, "\nusage: %s [<parameters>]\n", programName);
+  fprintf(stderr,"\n"
+          "Parameters can be turned on with -<param> or off with -<param>=0\n"
+          "Parameters which take a value can be specified as "
+          "-<param> <value>\n"
+          "Other valid forms are <param>=<value> -<param>=<value> "
+          "--<param>=<value>\n"
+          "Parameter names are case-insensitive.  The parameters are:\n\n");
+  Configuration::listParams(79, 14);
+  exit(1);
+}
+
+int main(int argc, char** argv)
+{
+  initStdIOLoggers();
+  LogWriter::setLogParams("*:stderr:30");
+
+  programName = argv[0];
+  Display* dpy;
+
+  for (int i = 1; i < argc; i++) {
+    if (Configuration::setParam(argv[i]))
+      continue;
+
+    if (argv[i][0] == '-') {
+      if (i+1 < argc) {
+        if (Configuration::setParam(&argv[i][1], argv[i+1])) {
+          i++;
+          continue;
+        }
+      }
+      usage();
+    }
+
+    usage();
+  }
+
+  CharArray dpyStr(displayname.getData());
+  if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
+    fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
+            programName, XDisplayName(displayname.getData()));
+    exit(1);
+  }
+
+  signal(SIGHUP, CleanupSignalHandler);
+  signal(SIGINT, CleanupSignalHandler);
+  signal(SIGTERM, CleanupSignalHandler);
+
+  try {
+    XDesktop desktop(dpy);
+    VNCServerST server("x0vncserver", &desktop);
+    desktop.setVNCServer(&server);
+
+    TcpSocket::initTcpSockets();
+    TcpListener listener((int)rfbport);
+    vlog.info("Listening on port %d", (int)rfbport);
+
+    while (true) {
+      fd_set rfds;
+      struct timeval tv;
+
+      tv.tv_sec = 0;
+      tv.tv_usec = 50*1000;
+
+      FD_ZERO(&rfds);
+      FD_SET(listener.getFd(), &rfds);
+
+      std::list<Socket*> sockets;
+      server.getSockets(&sockets);
+      std::list<Socket*>::iterator i;
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        FD_SET((*i)->getFd(), &rfds);
+      }
+
+      int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
+      if (n < 0) throw rdr::SystemException("select",errno);
+
+      if (FD_ISSET(listener.getFd(), &rfds)) {
+        Socket* sock = listener.accept();
+        server.addClient(sock);
+      }
+
+      server.getSockets(&sockets);
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        if (FD_ISSET((*i)->getFd(), &rfds)) {
+          server.processSocketEvent(*i);
+        }
+      }
+
+      server.checkTimeouts();
+      desktop.poll();
+    }
+
+  } catch (rdr::Exception &e) {
+    vlog.error(e.str());
+  };
+
+  return 0;
+}
diff --git a/x0vncserver/x0vncserver.man b/x0vncserver/x0vncserver.man
new file mode 100644
index 0000000..9f81396
--- /dev/null
+++ b/x0vncserver/x0vncserver.man
@@ -0,0 +1,32 @@
+.TH x0vncserver 1 "19 September 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+x0vncserver \- VNC server which continuously polls an X display
+.SH SYNOPSIS
+.B x0vncserver
+[\fIparameters\fP]
+.SH DESCRIPTION
+.B x0vncserver
+is a VNC server which continuously polls any X display, allowing it to be
+controlled via VNC.  How usable it will be depends a lot on the machine it's
+running on, and what you're expecting.  It won't be as fast as Xvnc or a native
+X server with VNC support compiled in, but in many cases it is the best option
+since it is just an ordinary X application requiring no special installation.
+
+It has many of the same parameters as Xvnc.  Running \fBx0vncserver -h\fP will
+give a list of parameters with descriptions.  Note that you need to explicitly
+specify an appropriate password file using the PasswordFile parameter.
+
+.SH SEE ALSO
+.BR Xvnc (1)
+.BR vncpasswd (1),
+.BR vncviewer (1),
+.BR vncserver (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/xc.patch b/xc.patch
new file mode 100644
index 0000000..fb9baf6
--- /dev/null
+++ b/xc.patch
@@ -0,0 +1,191 @@
+*** xc/programs/Xserver/Imakefile.orig	Fri Jun  6 11:02:36 2003
+--- xc/programs/Xserver/Imakefile	Fri Jun  6 11:14:39 2003
+***************
+*** 409,412 ****
+--- 409,429 ----
+  #endif
+  #endif /* XsunServer */
++ XCOMM
++ XCOMM X VNC server
++ XCOMM
++ MFBDIR = mfb
++ CFB8DIR = cfb
++ CFB16DIR = cfb16
++ CFB24DIR = cfb24
++ CFB32DIR = cfb32
++ XVNCDDXDIR = vnc/Xvnc
++ XVNCDIRS = $(STDDIRS) $(MFBDIR) \
++ 	    $(CFB8DIR) $(CFB16DIR) $(CFB24DIR) $(CFB32DIR) \
++ 	    $(XVNCDDXDIR) $(DEPDIRS)
++ XVNCOBJS = $(XVNCDDXDIR)/stubs.o $(XVNCDDXDIR)/miinitext.o
++ XVNCLIBS = PreFbLibs vnc/Xvnc/LibraryTargetName(xvnc) CFBLibs PostFbLibs
++ XVNCSYSLIBS = $(FONTLIBS) $(SYSLIBS)
++ ServerTarget(Xvnc,$(XVNCDIRS),$(XVNCOBJS), \
++ 	$(XVNCLIBS) $(LOADABLEEXTS) $(LIBCWRAPPER),$(XVNCSYSLIBS))
+  
+  
+*** xc/programs/Xserver/mi/miinitext.c.orig	Fri Jun  6 11:02:59 2003
+--- xc/programs/Xserver/mi/miinitext.c	Fri Jun  6 11:17:15 2003
+***************
+*** 150,153 ****
+--- 150,156 ----
+  extern void MITMiscExtensionInit(INITARGS);
+  #endif
++ #ifdef VNCEXT
++ extern void vncExtensionInit(INITARGS);
++ #endif
+  #ifdef XIDLE
+  extern void XIdleExtensionInit(INITARGS);
+***************
+*** 285,288 ****
+--- 288,294 ----
+      MITMiscExtensionInit();
+  #endif
++ #ifdef VNCEXT
++     vncExtensionInit();
++ #endif
+  #ifdef XIDLE
+      XIdleExtensionInit();
+*** xc/programs/Xserver/cfb/cfb8line.c.orig	Wed Sep 18 18:11:47 2002
+--- xc/programs/Xserver/cfb/cfb8line.c	Thu Jun  5 18:32:04 2003
+***************
+*** 688,707 ****
+  	    y1_or_e1 = xOffset & 3;
+  # else
+! #  if PGSZ == 64 /* PIM value from <cfbmskbits.h> is not it! (for 16/32 PSZ)*/
+! 	    y1_or_e1 = ((long) addrp) & 0x7;
+! 	    addrp = (PixelType *) (((unsigned char *) addrp) - y1_or_e1);
+! #  else
+! 	    y1_or_e1 = ((long) addrp) & PIM;
+! 	    addrp = (PixelType *) (((unsigned char *) addrp) - y1_or_e1);
+! #  endif
+! #if PGSZ == 32
+! #  if PWSH != 2
+! 	    y1_or_e1 >>= (2 - PWSH);
+! #  endif
+! #else /* PGSZ == 64 */
+! #  if PWSH != 3
+! 	    y1_or_e1 >>= (3 - PWSH);
+! #  endif
+! #endif /* PGSZ */
+  # endif /* PSZ == 24 */
+  #if PSZ == 24
+--- 688,696 ----
+  	    y1_or_e1 = xOffset & 3;
+  # else
+! 	    /* Round addrp down to the next PixelGroup boundary, and
+! 	     * set y1_or_e1 to the excess (in pixels)
+! 	     * (assumes PGSZB is a power of 2). */
+! 	    y1_or_e1 = (((unsigned long) addrp) & (PGSZB - 1)) / (PSZ / 8);
+! 	    addrp -= y1_or_e1;
+  # endif /* PSZ == 24 */
+  #if PSZ == 24
+*** xc/programs/Xserver/cfb/cfbtile32.c.orig	Fri Dec 14 19:59:25 2001
+--- xc/programs/Xserver/cfb/cfbtile32.c	Thu Jun  5 18:16:48 2003
+***************
+*** 73,77 ****
+                        (p)++,(*(p) = MROP_PREBUILT_SOLID(((srcpix<<8)|(srcpix>>16)),*(p))))
+  
+! #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+  # define Expand(left,right) {\
+      int part = nlwMiddle & ((PGSZB*2)-1); \
+--- 73,83 ----
+                        (p)++,(*(p) = MROP_PREBUILT_SOLID(((srcpix<<8)|(srcpix>>16)),*(p))))
+  
+! 
+! 
+! /* XXX TJR: I doubt that this optimised case works (because the non-24 bit case
+!    was broken), so I've added the #if 0 below.  Someone who knows what they're
+!    doing can re-enable it if they fix it */
+! 
+! #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE) && 0
+  # define Expand(left,right) {\
+      int part = nlwMiddle & ((PGSZB*2)-1); \
+***************
+*** 145,150 ****
+  #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+  # define Expand(left,right) {\
+!     int part = nlwMiddle & ((PGSZB*2)-1); \
+!     nlwMiddle >>= PWSH + 1; \
+      while (h--) { \
+  	srcpix = psrc[srcy]; \
+--- 151,156 ----
+  #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+  # define Expand(left,right) {\
+!     int part = nlwMiddle & 7; \
+!     nlwMiddle >>= 3; \
+      while (h--) { \
+  	srcpix = psrc[srcy]; \
+*** xc/programs/Xserver/cfb/cfbglblt8.c.orig	Fri Dec 14 19:59:23 2001
+--- xc/programs/Xserver/cfb/cfbglblt8.c	Tue Aug 12 10:05:57 2003
+***************
+*** 284,288 ****
+      register glyphPointer   glyphBits;
+      register int	xoff;
+! #if defined(USE_LEFT_BITS) || (!defined(STIPPLE) && !defined(USE_STIPPLE_CODE))
+      register CfbBits	*dst;
+  #endif
+--- 284,288 ----
+      register glyphPointer   glyphBits;
+      register int	xoff;
+! #if defined(USE_LEFTBITS) || (!defined(STIPPLE) && !defined(USE_STIPPLE_CODE))
+      register CfbBits	*dst;
+  #endif
+***************
+*** 292,296 ****
+      CfbBits		*dstLine;
+      CfbBits		*pdstBase;
+! #ifdef USE_LEFT_BITS
+      CARD32		*cTmp;
+  #endif
+--- 292,296 ----
+      CfbBits		*dstLine;
+      CfbBits		*pdstBase;
+! #ifdef USE_LEFTBITS
+      CARD32		*cTmp;
+  #endif
+***************
+*** 399,403 ****
+  	    	} while (--hTmp);
+  	    	break;
+! #else /* !USE_LEFT_BITS */
+  	    	{
+  		    int h;
+--- 399,403 ----
+  	    	} while (--hTmp);
+  	    	break;
+! #else /* !USE_LEFTBITS */
+  	    	{
+  		    int h;
+***************
+*** 412,416 ****
+  	    	glyphBits = clips;
+  	    	/* fall through */
+! #endif /* USE_LEFT_BITS */
+  	    case rgnIN:
+  #ifdef STIPPLE
+--- 412,416 ----
+  	    	glyphBits = clips;
+  	    	/* fall through */
+! #endif /* USE_LEFTBITS */
+  	    case rgnIN:
+  #ifdef STIPPLE
+*** xc/programs/Xserver/cfb/cfbcppl.c.orig	Fri Dec 14 19:59:22 2001
+--- xc/programs/Xserver/cfb/cfbcppl.c	Sun Apr 18 12:53:36 2004
+***************
+*** 383,389 ****
+  	psrcLine = (unsigned int *)psrcBase + srcy * widthSrc + srcx;
+  #endif
+  	pdstLine = (unsigned int *)pdstBase + dsty * widthDst + (dstx >> 5);
+! 	if (dstx + width <= 32)
+  	{
+  	    mfbmaskpartialbits(dstx, width, startmask);
+  	    nlMiddle = 0;
+--- 383,389 ----
+  	psrcLine = (unsigned int *)psrcBase + srcy * widthSrc + srcx;
+  #endif
+  	pdstLine = (unsigned int *)pdstBase + dsty * widthDst + (dstx >> 5);
+! 	if ((dstx & 0x1f) + width <= 32)
+  	{
+  	    mfbmaskpartialbits(dstx, width, startmask);
+  	    nlMiddle = 0;
diff --git a/xc/config/cf/host.def b/xc/config/cf/host.def
new file mode 100644
index 0000000..2de89af
--- /dev/null
+++ b/xc/config/cf/host.def
@@ -0,0 +1 @@
+#include <vnc.def>
diff --git a/xc/config/cf/vnc.def b/xc/config/cf/vnc.def
new file mode 100644
index 0000000..08b6496
--- /dev/null
+++ b/xc/config/cf/vnc.def
@@ -0,0 +1,32 @@
+#define BuildServersOnly YES
+#define BuildFonts NO
+#define BuildClients NO
+#define BuildDocs NO
+#define BuildPexExt NO
+#define BuildNls NO
+#define BuildXIE NO
+#define BuildGlxExt NO
+#define XnestServer NO
+#define XprtServer NO
+
+#ifdef SunArchitecture
+#define ProjectRoot /usr/openwin
+#define HasGcc2 YES
+#define BuildXKB NO
+#endif
+
+#define BuildVNCExt YES
+#define VNCExtDefines -DVNCEXT
+#define SiteExtensionDefines VNCExtDefines
+#define SiteExtensionDirs vnc
+
+#define VncExtLibs $(TOP)/../rfb/librfb.a \
+                   $(TOP)/../Xregion/libXregion.a \
+                   $(TOP)/../network/libnetwork.a \
+                   $(TOP)/../rdr/librdr.a
+
+#define SiteExtensionLibs vnc/LibraryTargetName(vnc) VncExtLibs
+
+#define	ServerTarget(server,subdirs,objects,libs,syslibs)		@@\
+CCLINK = $(CXXENVSETUP) $(CXX) @@\
+ServerTargetWithFlags(server,subdirs,objects,libs,syslibs,$(_NOOP_))
diff --git a/xc/programs/Xserver/Xvnc.man b/xc/programs/Xserver/Xvnc.man
new file mode 100644
index 0000000..1852f4d
--- /dev/null
+++ b/xc/programs/Xserver/Xvnc.man
@@ -0,0 +1,258 @@
+.TH Xvnc 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+Xvnc \- the X VNC server 
+.SH SYNOPSIS
+.B Xvnc
+.RI [ options ] 
+.RI : display#
+.SH DESCRIPTION
+.B Xvnc
+is the X VNC (Virtual Network Computing) server.  It is based on a standard X
+server, but it has a "virtual" screen rather than a physical one.  X
+applications display themselves on it as if it were a normal X display, but
+they can only be accessed via a VNC viewer - see \fBvncviewer\fP(1).
+
+So Xvnc is really two servers in one. To the applications it is an X server,
+and to the remote VNC users it is a VNC server. By convention we have arranged
+that the VNC server display number will be the same as the X server display
+number, which means you can use eg. snoopy:2 to refer to display 2 on machine
+"snoopy" in both the X world and the VNC world.
+
+The best way of starting \fBXvnc\fP is via the \fBvncserver\fP script.  This
+sets up the environment appropriately and runs some X applications to get you
+going.  See the manual page for \fBvncserver\fP(1) for more information.
+
+.SH OPTIONS
+.B Xvnc
+takes lots of options - running \fBXvnc -help\fP gives a list.  Many of these
+are standard X server options, which are described in the \fBXserver\fP(1)
+manual page.  In addition to options which can only be set via the
+command-line, there are also "parameters" which can be set both via the
+command-line and through the \fBvncconfig\fP(1) program.
+
+.TP
+.B \-geometry \fIwidth\fPx\fIheight\fP
+Specify the size of the desktop to be created. Default is 1024x768.
+
+.TP
+.B \-depth \fIdepth\fP
+Specify the pixel depth in bits of the desktop to be created. Default is 16,
+other possible values are 8, 15, and 24 - anything else is likely to cause
+strange behaviour by applications.
+
+.TP
+.B \-pixelformat \fIformat\fP
+Specify pixel format for server to use (BGRnnn or RGBnnn).  The default for
+depth 8 is BGR233 (meaning the most significant two bits represent blue, the
+next three green, and the least significant three represent red), the default
+for depth 16 is RGB565 and for depth 24 is RGB888.
+
+.TP
+.B \-cc 3
+As an alternative to the default TrueColor visual, this allows you to run an
+Xvnc server with a PseudoColor visual (i.e. one which uses a colour map or
+palette), which can be useful for running some old X applications which only
+work on such a display.  Values other than 3 (PseudoColor) and 4 (TrueColor)
+for the \-cc option may result in strange behaviour, and PseudoColor desktops
+must be 8 bits deep (i.e. \fB-depth 8\fP).
+
+.TP
+.B \-inetd 
+This significantly changes Xvnc's behaviour so that it can be launched from
+inetd.  See the section below on usage with inetd.
+
+.TP
+.B \-help
+List all the options and parameters
+
+.SH PARAMETERS
+VNC parameters can be set both via the command-line and through the
+\fBvncconfig\fP(1) program, and with a VNC-enabled XFree86 server via Options
+entries in the XF86Config file.
+
+Parameters can be turned on with -\fIparam\fP or off with
+-\fIparam\fP=0.  Parameters which take a value can be specified as
+-\fIparam\fP \fIvalue\fP.  Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP
+-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP.  Parameter names are
+case-insensitive.
+
+.TP
+.B \-desktop \fIdesktop-name\fP
+Each desktop has a name which may be displayed by the viewer. It defaults to
+"x11".
+
+.TP
+.B \-rfbport \fIport\fP
+Specifies the TCP port on which Xvnc listens for connections from viewers (the
+protocol used in VNC is called RFB - "remote framebuffer").  The default is
+5900 plus the display number.
+
+.TP
+.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP
+
+Time in milliseconds to wait for a viewer which is blocking Xvnc.  This is
+necessary because Xvnc is single-threaded and sometimes blocks until the viewer
+has finished sending or receiving a message - note that this does not mean an
+update will be aborted after this time.  Default is 20000 (20 seconds).
+
+.TP
+.B \-httpd \fIdirectory\fP
+Run a mini-HTTP server which serves files from the given directory.  Normally
+the directory will contain the classes for the Java viewer.  In addition, files
+with a .vnc extension will have certain substitutions made so that a single
+installation of the Java VNC viewer can be served by separate instances of
+Xvnc.
+
+.TP
+.B \-httpPort \fIport\fP
+Specifies the port on which the mini-HTTP server runs.  Default is 5800 plus
+the display number.
+
+.TP
+.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
+Specifies the file containing the password used to authenticate viewers.  The
+file is accessed each time a connection comes in, so it can be changed on the
+fly via \fBvncpasswd\fP(1).
+
+.TP
+.B \-deferUpdate \fItime\fP
+Xvnc uses a "deferred update" mechanism which enhances performance in many
+cases. After any change to the framebuffer, Xvnc waits for this number of
+milliseconds (default 40) before sending an update to any waiting clients. This
+means that more changes tend to get coalesced together in a single
+update. Setting it to 0 results in the same behaviour as earlier versions of
+Xvnc, where the first change to the framebuffer causes an immediate update to
+any waiting clients.
+
+.TP
+.B \-SendCutText
+Send clipboard changes to clients (default is on).  Note that you must also run
+\fBvncconfig\fP(1) to get the clipboard to work.
+
+.TP
+.B \-AcceptCutText
+Accept clipboard updates from clients (default is on).  Note that you must also
+run \fBvncconfig\fP(1) to get the clipboard to work.
+
+.TP
+.B \-AcceptPointerEvents
+Accept pointer press and release events from clients (default is on).
+
+.TP
+.B \-AcceptKeyEvents
+Accept key press and release events from clients (default is on).
+
+.TP
+.B \-DisconnectClients
+Disconnect existing clients if an incoming connection is non-shared (default is
+on). If \fBDisconnectClients\fP is false, then a new non-shared connection will
+be refused while there is a client active.  When combined with
+\fBNeverShared\fP this means only one client is allowed at a time.
+
+.TP
+.B \-NeverShared
+Never treat incoming connections as shared, regardless of the client-specified
+setting (default is off).
+
+.TP
+.B \-AlwaysShared
+Always treat incoming connections as shared, regardless of the client-specified
+setting (default is off).
+
+.TP
+.B \-Protocol3.3
+Always use protocol version 3.3 for backwards compatibility with badly-behaved
+clients (default is off).
+
+.TP
+.B \-CompareFB
+Perform pixel comparison on framebuffer to reduce unnecessary updates (default
+is on).
+
+.TP
+.B \-SecurityTypes \fIsec-types\fP
+Specify which security schemes to use separated by commas.  At present only
+"None" and "VncAuth" are supported.  The default is "VncAuth" - note that if
+you want a server which does not require a password, you must set this
+parameter to "None".
+
+.TP
+.B \-IdleTimeout \fIseconds\fP
+The number of seconds after which an idle VNC connection will be dropped
+(default is 3600 i.e. an hour).
+
+.TP
+.B \-localhost
+Only allow connections from the same machine. Useful if you use SSH and want to
+stop non-SSH connections from any other hosts. See the guide to using VNC with
+SSH on the web site.
+
+.TP
+.B \-log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
+Configures the debug log settings.  \fIdest\fP can currently be \fBstderr\fP or
+\fBstdout\fP, and \fIlevel\fP is between 0 and 100, 100 meaning most verbose
+output.  \fIlogname\fP is usually \fB*\fP meaning all, but you can target a
+specific source file if you know the name of its "LogWriter".  Default is
+\fB*:stderr:30\fP.
+
+.SH USAGE WITH INETD
+By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched
+on demand when a connection comes in, rather than having to be started
+manually.  When given the \fB-inetd\fP option, instead of listening for TCP
+connections on a given port it uses its standard input and standard output.
+There are two modes controlled by the wait/nowait entry in the inetd.conf file.
+
+In the nowait mode, Xvnc uses its standard input and output directly as the
+connection to a viewer.  It never has a listening socket, so cannot accept
+further connections from viewers (it can however connect out to listening
+viewers by use of the vncconfig program).  Further viewer connections to the
+same TCP port result in inetd spawning off a new Xvnc to deal with each
+connection.  When the connection to the viewer dies, the Xvnc and any
+associated X clients die.  This behaviour is most useful when combined with the
+XDMCP options -query and -once.  An typical example in inetd.conf might be (all
+on one line):
+
+5950   stream   tcp nowait nobody  /usr/local/bin/Xvnc Xvnc -inetd -query
+localhost -once securitytypes=none
+
+In this example a viewer connection to :50 will result in a new Xvnc for that
+connection which should display the standard XDM login screen on that machine.
+Because the user needs to login via XDM, it is usually OK to accept connections
+without a VNC password in this case.
+
+In the wait mode, when the first connection comes in, inetd gives the listening
+socket to Xvnc.  This means that for a given TCP port, there is only ever one
+Xvnc at a time.  Further viewer connections to the same port are accepted by
+the same Xvnc in the normal way.  Even when the original connection is broken,
+the Xvnc will continue to run.  If this is used with the XDMCP options -query
+and -once, the Xvnc and associated X clients will die when the user logs out of
+the X session in the normal way.  It is important to use a VNC password in this
+case.  A typical entry in inetd.conf might be:
+
+5951   stream   tcp wait   james     /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.vnc/passwd
+
+In fact typically, you would have one entry for each user who uses VNC
+regularly, each of whom has their own dedicated TCP port which they use.  In
+this example, when user "james" connects to :51, he enters his VNC password,
+then gets the XDM login screen where he logs in in the normal way.  However,
+unlike the previous example, if he disconnects, the session remains persistent,
+and when he reconnects he will get the same session back again.  When he logs
+out of the X session, the Xvnc will die, but of course a new one will be
+created automatically the next time he connects.
+
+.SH SEE ALSO
+.BR vncconfig (1),
+.BR vncpasswd (1),
+.BR vncserver (1),
+.BR vncviewer (1),
+.BR Xserver (1),
+.BR inetd (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge.  It is now being maintained by RealVNC Ltd.  See
+http://www.realvnc.com for details.
diff --git a/xc/programs/Xserver/vnc/Imakefile b/xc/programs/Xserver/vnc/Imakefile
new file mode 100644
index 0000000..982233a
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Imakefile
@@ -0,0 +1,44 @@
+XCOMM CDEBUGFLAGS = -g
+XCOMM CXXDEBUGFLAGS = -g
+
+       VNCTOP = $(TOP)/..
+   VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#if DoLoadableServer
+#define IHaveSubdirs
+#endif
+
+#include <Server.tmpl>
+
+#if DoLoadableServer
+       MODULE_SUBDIRS = module
+#endif
+         SRCS = vncExtInit.cc vncHooks.cc XserverDesktop.cc
+         OBJS = vncExtInit.o vncHooks.o XserverDesktop.o
+     INCLUDES = -I../include -I$(EXTINCSRC) -I$(XINCLUDESRC) -I$(FONTINCSRC) \
+                -I../mfb -I../mi $(VNCINCLUDE)
+#if defined(XFree86Version) && XFree86Version >= 4000
+   VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP
+#endif
+#if defined(ProjectX) && (ProjectX >= 604)
+   VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP
+#endif
+      DEFINES = $(STD_DEFINES) $(VNCDEFINES) -UXFree86LOADER
+
+#define IHaveSubdirs
+SUBDIRS = Xvnc $(MODULE_SUBDIRS)
+
+NormalLibraryTarget(vnc,$(OBJS))
+LintLibraryTarget(vnc,$(SRCS))
+NormalLintTarget($(SRCS))
+
+NormalLibraryObjectRule()
+NormalCplusplusObjectRule()
+
+
+MakeSubdirs($(SUBDIRS))
+DependSubdirs($(SUBDIRS))
+
+DependTarget()
diff --git a/xc/programs/Xserver/vnc/RegionHelper.h b/xc/programs/Xserver/vnc/RegionHelper.h
new file mode 100644
index 0000000..640d558
--- /dev/null
+++ b/xc/programs/Xserver/vnc/RegionHelper.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __REGIONHELPER_H__
+#define __REGIONHELPER_H__
+
+// RegionHelper is a class which helps in using X server regions by
+// automatically freeing them in the destructor.  It also fixes a problem with
+// REGION_INIT when given an empty rectangle.
+
+class RegionHelper {
+public:
+
+  // constructor from a single rect
+  RegionHelper(ScreenPtr pScreen_, BoxPtr rect, int size)
+    : pScreen(pScreen_), reg(0)
+  {
+    init(rect, size);
+  }
+
+  // constructor from an existing X server region
+  RegionHelper(ScreenPtr pScreen_, RegionPtr pRegion)
+    : pScreen(pScreen_), reg(&regRec)
+  {
+    REGION_INIT(pScreen, reg, NullBox, 0);
+    REGION_COPY(pScreen, reg, pRegion);
+  }
+
+  // constructor from an array of rectangles
+  RegionHelper(ScreenPtr pScreen_, int nrects, xRectanglePtr rects,
+               int ctype=CT_NONE)
+    : pScreen(pScreen_)
+  {
+    reg = RECTS_TO_REGION(pScreen, nrects, rects, ctype);
+  }
+
+  // constructor for calling init() later
+  RegionHelper(ScreenPtr pScreen_) : pScreen(pScreen_), reg(0) {
+  }
+
+  void init(BoxPtr rect, int size) {
+    reg = &regRec;
+    if (rect && (rect->x2 == rect->x1 || rect->y2 == rect->y1)) {
+      REGION_INIT(pScreen, reg, NullBox, 0);
+    } else {
+      REGION_INIT(pScreen, reg, rect, size);
+    }
+  }
+
+  // destructor frees as appropriate
+  ~RegionHelper() {
+    if (reg == &regRec) {
+      REGION_UNINIT(pScreen, reg);
+    } else if (reg) {
+      REGION_DESTROY(pScreen, reg);
+    }
+  }
+  ScreenPtr pScreen;
+  RegionRec regRec;
+  RegionPtr reg;
+};
+
+#endif
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.cc b/xc/programs/Xserver/vnc/XserverDesktop.cc
new file mode 100644
index 0000000..9b5294e
--- /dev/null
+++ b/xc/programs/Xserver/vnc/XserverDesktop.cc
@@ -0,0 +1,1079 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// XserverDesktop.cxx
+//
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include <network/TcpSocket.h>
+#include <rfb/Exception.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/HTTPServer.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include "XserverDesktop.h"
+#include "vncExtInit.h"
+
+extern "C" {
+#define public c_public
+#define class c_class
+
+  // windowTable is in globals.h in XFree 4, but not in XFree 3 unfortunately
+extern WindowPtr *WindowTable;
+extern char *display;
+
+#include "inputstr.h"
+#include "servermd.h"
+#include "colormapst.h"
+#include "resource.h"
+#include "cursorstr.h"
+#include "windowstr.h"
+#define XK_CYRILLIC
+#include "keysym.h"
+#undef public
+#undef class
+}
+
+using namespace rfb;
+using namespace network;
+
+static LogWriter vlog("XserverDesktop");
+
+rfb::IntParameter deferUpdateTime("DeferUpdate",
+                                  "Time in milliseconds to defer updates",40);
+
+rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
+                  "Always reset the defer update timer on every change",false);
+
+static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col);
+
+static rdr::U8 reverseBits[] = {
+  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+  0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+  0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+  0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+  0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+  0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+  0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+  0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+  0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+  0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+  0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+  0x3f, 0xbf, 0x7f, 0xff
+};
+
+
+class MyHTTPServer : public rfb::HTTPServer {
+public:
+  MyHTTPServer(XserverDesktop* d) : desktop(d) {}
+  virtual ~MyHTTPServer() {}
+
+  virtual rdr::InStream* getFile(const char* name, const char** contentType) {
+    if (name[0] != '/' || strstr(name, "..") != 0) {
+      vlog.info("http request was for invalid file name");
+      return 0;
+    }
+
+    if (strcmp(name, "/") == 0) name = "/index.vnc";
+
+    CharArray httpDirStr(httpDir.getData());
+    CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
+    sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
+    int fd = open(fname.buf, O_RDONLY);
+    if (fd < 0) return 0;
+
+    rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
+    *contentType = guessContentType(name, *contentType);
+    if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
+      is = new rdr::SubstitutingInStream(is, desktop, 20);
+      *contentType = "text/html";
+    }
+    return is;
+  }
+
+  XserverDesktop* desktop;
+};
+
+
+XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
+                               network::TcpListener* listener_,
+                               network::TcpListener* httpListener_,
+                               const char* name, void* fbptr)
+  : pScreen(pScreen_), deferredUpdateTimer(0), dummyTimer(0),
+    server(0), httpServer(0),
+    listener(listener_), httpListener(httpListener_),
+    cmap(0), deferredUpdateTimerSet(false),
+    grabbing(false), ignoreHooks_(false), directFbptr(fbptr != 0),
+    oldButtonMask(0), cursorX(0), cursorY(0),
+    oldCursorX(0), oldCursorY(0)
+{
+  int i;
+  format.depth = pScreen->rootDepth;
+  for (i = 0; i < screenInfo.numPixmapFormats; i++) {
+    if (screenInfo.formats[i].depth == format.depth) {
+      format.bpp = screenInfo.formats[i].bitsPerPixel;
+      break;
+    }
+  }
+  if (i == screenInfo.numPixmapFormats) {
+    fprintf(stderr,"no pixmap format for root depth???\n");
+    abort();
+  }
+  format.bigEndian = (screenInfo.imageByteOrder == MSBFirst);
+
+  VisualPtr vis;
+  for (i = 0; i < pScreen->numVisuals; i++) {
+    if (pScreen->visuals[i].vid == pScreen->rootVisual) {
+      vis = &pScreen->visuals[i];
+      break;
+    }
+  }
+  if (i == pScreen->numVisuals) {
+    fprintf(stderr,"no visual rec for root visual???\n");
+    abort();
+  }
+  format.trueColour = (vis->c_class == TrueColor);
+  if (!format.trueColour && format.bpp != 8)
+    throw rfb::Exception("X server uses unsupported visual");
+  format.redShift   = ffs(vis->redMask) - 1;
+  format.greenShift = ffs(vis->greenMask) - 1;
+  format.blueShift  = ffs(vis->blueMask) - 1;
+  format.redMax     = vis->redMask   >> format.redShift;
+  format.greenMax   = vis->greenMask >> format.greenShift;
+  format.blueMax    = vis->blueMask  >> format.blueShift;
+
+  width_ = pScreen->width;
+  height_ = pScreen->height;
+  if (fbptr)
+    data = (rdr::U8*)fbptr;
+  else
+    data = new rdr::U8[pScreen->width * pScreen->height * (format.bpp/8)];
+  colourmap = this;
+
+  serverReset(pScreen);
+
+  server = new VNCServerST(name, this);
+  server->setPixelBuffer(this);
+
+  if (httpListener)
+    httpServer = new MyHTTPServer(this);
+}
+
+XserverDesktop::~XserverDesktop()
+{
+  if (!directFbptr)
+    delete [] data;
+  TimerFree(deferredUpdateTimer);
+  TimerFree(dummyTimer);
+  delete httpServer;
+  delete server;
+}
+
+void XserverDesktop::serverReset(ScreenPtr pScreen_)
+{
+  pScreen = pScreen_;
+  XID* ids = new XID[pScreen->maxInstalledCmaps];
+  int nmaps = (*pScreen->ListInstalledColormaps)(pScreen, ids);
+  cmap = (ColormapPtr)LookupIDByType(ids[0], RT_COLORMAP);
+  delete [] ids;
+}
+
+char* XserverDesktop::substitute(const char* varName)
+{
+  if (strcmp(varName, "$$") == 0) {
+    return rfb::strDup("$");
+  }
+  if (strcmp(varName, "$PORT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", listener ? listener->getMyPort() : 0);
+    return str;
+  }
+  if (strcmp(varName, "$WIDTH") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", width());
+    return str;
+  }
+  if (strcmp(varName, "$HEIGHT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", height());
+    return str;
+  }
+  if (strcmp(varName, "$APPLETWIDTH") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", width());
+    return str;
+  }
+  if (strcmp(varName, "$APPLETHEIGHT") == 0) {
+    char* str = new char[10];
+    sprintf(str, "%d", height() + 32);
+    return str;
+  }
+  if (strcmp(varName, "$DESKTOP") == 0) {
+    return rfb::strDup(server->getName());
+  }
+  if (strcmp(varName, "$DISPLAY") == 0) {
+    struct utsname uts;
+    uname(&uts);
+    char* str = new char[256];
+    strncat(str, uts.nodename, 240);
+    strcat(str, ":");
+    strncat(str, display, 10);
+    return str;
+  }
+  if (strcmp(varName, "$USER") == 0) {
+    struct passwd* user = getpwuid(getuid());
+    return rfb::strDup(user ? user->pw_name : "?");
+  }
+  return 0;
+}
+
+void XserverDesktop::setColormap(ColormapPtr cmap_)
+{
+  if (cmap != cmap_) {
+    cmap = cmap_;
+    setColourMapEntries(0, 0);
+  }
+}
+
+void XserverDesktop::setColourMapEntries(ColormapPtr pColormap, int ndef,
+                                         xColorItem* pdef)
+{
+  if (cmap != pColormap || ndef <= 0) return;
+
+  int first = pdef[0].pixel;
+  int n = 1;
+
+  for (int i = 1; i < ndef; i++) {
+    if (first + n == pdef[i].pixel) {
+      n++;
+    } else {
+      setColourMapEntries(first, n);
+      first = pdef[i].pixel;
+      n = 1;
+    }
+  }
+  setColourMapEntries(first, n);
+}
+
+void XserverDesktop::setColourMapEntries(int firstColour, int nColours)
+{
+  try {
+    server->setColourMapEntries(firstColour, nColours);
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::setColourMapEntries: %s",e.str());
+  }
+}
+
+void XserverDesktop::bell()
+{
+  server->bell();
+}
+
+void XserverDesktop::serverCutText(const char* str, int len)
+{
+  try {
+    server->serverCutText(str, len);
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::serverCutText: %s",e.str());
+  }
+}
+
+void XserverDesktop::setCursor(CursorPtr cursor)
+{
+  try {
+    int w = cursor->bits->width;
+    int h = cursor->bits->height;
+    rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)];
+
+    xColorItem fg, bg;
+    fg.red   = cursor->foreRed;
+    fg.green = cursor->foreGreen;
+    fg.blue  = cursor->foreBlue;
+    FakeAllocColor(cmap, &fg);
+    bg.red   = cursor->backRed;
+    bg.green = cursor->backGreen;
+    bg.blue  = cursor->backBlue;
+    FakeAllocColor(cmap, &bg);
+    FakeFreeColor(cmap, fg.pixel);
+    FakeFreeColor(cmap, bg.pixel);
+
+    int xMaskBytesPerRow = BitmapBytePad(w);
+
+    for (int y = 0; y < h; y++) {
+      for (int x = 0; x < w; x++) {
+        int byte = y * xMaskBytesPerRow + x / 8;
+#if (BITMAP_BIT_ORDER == MSBFirst)
+        int bit = 7 - x % 8;
+#else
+        int bit = x % 8;
+#endif
+        switch (getPF().bpp) {
+        case 8:
+          ((rdr::U8*)cursorData)[y * w + x]
+            = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+          break;
+        case 16:
+          ((rdr::U16*)cursorData)[y * w + x]
+            = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+          break;
+        case 32:
+          ((rdr::U32*)cursorData)[y * w + x]
+            = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+          break;
+        }
+      }
+    }
+
+    int rfbMaskBytesPerRow = (w + 7) / 8;
+
+    rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h];
+
+    for (int j = 0; j < h; j++) {
+      for (int i = 0; i < rfbMaskBytesPerRow; i++)
+#if (BITMAP_BIT_ORDER == MSBFirst)
+        cursorMask[j * rfbMaskBytesPerRow + i]
+          = cursor->bits->mask[j * xMaskBytesPerRow + i];
+#else
+        cursorMask[j * rfbMaskBytesPerRow + i]
+          = reverseBits[cursor->bits->mask[j * xMaskBytesPerRow + i]];
+#endif
+    }
+
+    server->setCursor(cursor->bits->width, cursor->bits->height,
+                      cursor->bits->xhot, cursor->bits->yhot,
+                      cursorData, cursorMask);
+    server->tryUpdate();
+    delete [] cursorData;
+    delete [] cursorMask;
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::setCursor: %s",e.str());
+  }
+}
+
+static void printRegion(RegionPtr reg)
+{
+  int nrects = REGION_NUM_RECTS(reg);
+
+  fprintf(stderr,"Region num rects %2d extents %3d,%3d %3dx%3d\n",nrects,
+          (REGION_EXTENTS(pScreen,reg))->x1,
+          (REGION_EXTENTS(pScreen,reg))->y1,
+          (REGION_EXTENTS(pScreen,reg))->x2-(REGION_EXTENTS(pScreen,reg))->x1,
+          (REGION_EXTENTS(pScreen,reg))->y2-(REGION_EXTENTS(pScreen,reg))->y1);
+
+  for (int i = 0; i < nrects; i++) {
+    fprintf(stderr,"    rect %3d,%3d %3dx%3d\n",
+            REGION_RECTS(reg)[i].x1,
+            REGION_RECTS(reg)[i].y1,
+            REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1,
+            REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1);
+  }
+}
+
+CARD32 XserverDesktop::deferredUpdateTimerCallback(OsTimerPtr timer,
+                                                   CARD32 now, pointer arg)
+{
+  XserverDesktop* desktop = (XserverDesktop*)arg;
+  desktop->deferredUpdateTimerSet = false;
+  try {
+    desktop->server->tryUpdate();
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::deferredUpdateTimerCallback: %s",e.str());
+  }
+  return 0;
+}
+
+void XserverDesktop::add_changed(RegionPtr reg)
+{
+  if (ignoreHooks_) return;
+  if (grabbing) return;
+  try {
+    rfb::Region rfbReg;
+    rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, reg),
+                                     REGION_NUM_RECTS(reg),
+                                     (ShortRect*)REGION_RECTS(reg));
+    server->add_changed(rfbReg);
+    if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
+      deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
+                                     deferUpdateTime,
+                                     deferredUpdateTimerCallback, this);
+      deferredUpdateTimerSet = true;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::add_changed: %s",e.str());
+  }
+}
+
+void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy)
+{
+  if (ignoreHooks_) return;
+  if (grabbing) return;
+  try {
+    rfb::Region rfbReg;
+    rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, dst),
+                                     REGION_NUM_RECTS(dst),
+                                     (ShortRect*)REGION_RECTS(dst));
+    server->add_copied(rfbReg, rfb::Point(dx, dy));
+    if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
+      deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
+                                     deferUpdateTime,
+                                     deferredUpdateTimerCallback, this);
+      deferredUpdateTimerSet = true;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::add_copied: %s",e.str());
+  }
+}
+
+void XserverDesktop::positionCursor()
+{
+  if (cursorX != oldCursorX || cursorY != oldCursorY) {
+    oldCursorX = cursorX;
+    oldCursorY = cursorY;
+    (*pScreen->SetCursorPosition) (pScreen, cursorX, cursorY, FALSE);
+    server->setCursorPos(cursorX, cursorY);
+    server->tryUpdate();
+  }
+}
+
+void XserverDesktop::blockHandler(fd_set* fds)
+{
+  try {
+    ScreenPtr screenWithCursor = GetCurrentRootWindow()->drawable.pScreen;
+    if (screenWithCursor == pScreen) {
+      int x, y;
+      GetSpritePosition(&x, &y);
+      if (x != cursorX || y != cursorY) {
+        cursorX = oldCursorX = x;
+        cursorY = oldCursorY = y;
+        server->setCursorPos(x, y);
+        server->tryUpdate();
+      }
+    }
+
+    if (listener)
+      FD_SET(listener->getFd(), fds);
+    if (httpListener)
+      FD_SET(httpListener->getFd(), fds);
+
+    std::list<Socket*> sockets;
+    server->getSockets(&sockets);
+    std::list<Socket*>::iterator i;
+    for (i = sockets.begin(); i != sockets.end(); i++) {
+      FD_SET((*i)->getFd(), fds);
+    }
+    if (httpServer) {
+      httpServer->getSockets(&sockets);
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        FD_SET((*i)->getFd(), fds);
+      }
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::blockHandler: %s",e.str());
+  }
+}
+
+static CARD32 dummyTimerCallback(OsTimerPtr timer, CARD32 now, pointer arg) {
+  return 0;
+}
+
+void XserverDesktop::wakeupHandler(fd_set* fds, int nfds)
+{
+  try {
+    if (nfds >= 1) {
+
+      if (listener) {
+        if (FD_ISSET(listener->getFd(), fds)) {
+          FD_CLR(listener->getFd(), fds);
+          Socket* sock = listener->accept();
+          server->addClient(sock);
+          vlog.debug("new client, sock %d",sock->getFd());
+        }
+      }
+
+      if (httpListener) {
+        if (FD_ISSET(httpListener->getFd(), fds)) {
+          FD_CLR(httpListener->getFd(), fds);
+          Socket* sock = httpListener->accept();
+          httpServer->addClient(sock);
+          vlog.debug("new http client, sock %d",sock->getFd());
+        }
+      }
+
+      std::list<Socket*> sockets;
+      server->getSockets(&sockets);
+      std::list<Socket*>::iterator i;
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        int fd = (*i)->getFd();
+        if (FD_ISSET(fd, fds)) {
+          FD_CLR(fd, fds);
+          if (!server->processSocketEvent(*i)) {
+            vlog.debug("client gone, sock %d",fd);
+            vncClientGone(fd);
+          }
+        }
+      }
+
+      if (httpServer) {
+        httpServer->getSockets(&sockets);
+        for (i = sockets.begin(); i != sockets.end(); i++) {
+          int fd = (*i)->getFd();
+          if (FD_ISSET(fd, fds)) {
+            FD_CLR(fd, fds);
+            if (!httpServer->processSocketEvent(*i)) {
+              vlog.debug("http client gone, sock %d",fd);
+            }
+          }
+        }
+      }
+
+      positionCursor();
+    }
+
+    int timeout = server->checkTimeouts();
+    if (timeout > 0) {
+      // set a dummy timer just so we are guaranteed be called again next time.
+      dummyTimer = TimerSet(dummyTimer, 0, timeout,
+                            dummyTimerCallback, 0);
+    }
+
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::wakeupHandler: %s",e.str());
+  }
+}
+
+void XserverDesktop::addClient(Socket* sock, bool reverse)
+{
+  vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
+  server->addClient(sock, reverse);
+}
+
+void XserverDesktop::disconnectClients()
+{
+  vlog.debug("disconnecting all clients");
+  return server->closeClients("Disconnection from server end");
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// SDesktop callbacks
+
+
+void XserverDesktop::pointerEvent(const Point& pos, rdr::U8 buttonMask)
+{
+  xEvent ev;
+  DevicePtr dev = LookupPointerDevice();
+
+  // SetCursorPosition seems to be very expensive (at least on XFree86 3.3.6
+  // for S3), so we delay calling it until positionCursor() is called at the
+  // end of processing a load of RFB.
+  //(*pScreen->SetCursorPosition) (pScreen, pos.x, pos.y, FALSE);
+
+  NewCurrentScreen(pScreen, pos.x, pos.y);
+
+  ev.u.u.type = MotionNotify;
+  ev.u.u.detail = 0;
+  ev.u.keyButtonPointer.rootX = pos.x;
+  ev.u.keyButtonPointer.rootY = pos.y;
+  ev.u.keyButtonPointer.time = GetTimeInMillis();
+
+  if (pos.x != cursorX || pos.y != cursorY)
+    (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
+
+  for (int i = 0; i < 5; i++) {
+    if ((buttonMask ^ oldButtonMask) & (1<<i)) {
+#ifdef XINPUT
+      // God knows why but some idiot decided to conditionally move the pointer
+      // mapping out of DIX, so we guess here that if XINPUT is defined we have
+      // to do it ourselves...
+      ev.u.u.detail = ((DeviceIntPtr)dev)->button->map[i + 1];
+#else
+      ev.u.u.detail = i + 1;
+#endif
+      ev.u.u.type = (buttonMask & (1<<i)) ? ButtonPress : ButtonRelease;
+      (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
+    }
+  }
+
+  cursorX = pos.x;
+  cursorY = pos.y;
+  oldButtonMask = buttonMask;
+}
+
+void XserverDesktop::clientCutText(const char* str, int len)
+{
+  vncClientCutText(str, len);
+}
+
+void XserverDesktop::grabRegion(const rfb::Region& region)
+{
+  if (directFbptr) return;
+  if (!pScreen->GetImage) {
+    vlog.error("VNC error: pScreen->GetImage == 0");
+    return;
+  }
+
+  grabbing = true;
+
+  int bytesPerPixel = format.bpp/8;
+  int bytesPerRow = pScreen->width * bytesPerPixel;
+
+  std::vector<rfb::Rect> rects;
+  std::vector<rfb::Rect>::iterator i;
+  region.get_rects(&rects);
+  for (i = rects.begin(); i != rects.end(); i++) {
+    for (int y = i->tl.y; y < i->br.y; y++) {
+      (*pScreen->GetImage) ((DrawablePtr)WindowTable[pScreen->myNum],
+                            i->tl.x, y, i->width(), 1,
+                            ZPixmap, (unsigned long)~0L,
+                            ((char*)data
+                             + y * bytesPerRow + i->tl.x * bytesPerPixel));
+    }
+  }
+  grabbing = false;
+}
+
+void XserverDesktop::lookup(int index, int* r, int* g, int* b)
+{
+  EntryPtr pent;
+  pent = (EntryPtr)&cmap->red[index];
+  if (pent->fShared) {
+    *r = pent->co.shco.red->color;
+    *g = pent->co.shco.green->color;
+    *b = pent->co.shco.blue->color;
+  } else {
+    *r = pent->co.local.red;
+    *g = pent->co.local.green;
+    *b = pent->co.local.blue;
+  }
+}
+
+//
+// Keyboard handling
+//
+
+#define IS_PRESSED(keyc, keycode) \
+  ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
+
+// ModifierState is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc.  An instance of the class is created
+// for every modifier which may need to be pressed or released.  Then either
+// press() or release() may be called to make sure that the corresponding keys
+// are in the right state.  The destructor of the class automatically reverts
+// to the previous state.  Each modifier may have multiple keys associated with
+// it, so in the case of a fake release, this may involve releasing more than
+// one key.
+
+class ModifierState {
+public:
+  ModifierState(DeviceIntPtr dev_, int modIndex_)
+    : dev(dev_), modIndex(modIndex_), nKeys(0), keys(0), pressed(false)
+  {
+  }
+  ~ModifierState() {
+    for (int i = 0; i < nKeys; i++)
+      generateXKeyEvent(keys[i], !pressed);
+    delete [] keys;
+  }
+  void press() {
+    KeyClassPtr keyc = dev->key;
+    if (!(keyc->state & (1<<modIndex))) {
+      tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier],
+                   true);
+      pressed = true;
+    }
+  }
+  void release() {
+    KeyClassPtr keyc = dev->key;
+    if (keyc->state & (1<<modIndex)) {
+      for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+        int keycode
+          = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k];
+        if (keycode && IS_PRESSED(keyc, keycode))
+          tempKeyEvent(keycode, false);
+      }
+    }
+  }
+private:
+  void tempKeyEvent(int keycode, bool down) {
+    if (keycode) {
+      if (!keys) keys = new int[dev->key->maxKeysPerModifier];
+      keys[nKeys++] = keycode;
+      generateXKeyEvent(keycode, down);
+    }
+  }
+  void generateXKeyEvent(int keycode, bool down) {
+    xEvent ev;
+    ev.u.u.type = down ? KeyPress : KeyRelease;
+    ev.u.u.detail = keycode;
+    ev.u.keyButtonPointer.time = GetTimeInMillis();
+    (*dev->c_public.processInputProc)(&ev, dev, 1);
+    vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up");
+  }
+  DeviceIntPtr dev;
+  int modIndex;
+  int nKeys;
+  int* keys;
+  bool pressed;
+};
+
+
+// altKeysym is a table of alternative keysyms which have the same meaning.
+
+struct altKeysym_t {
+  KeySym a, b;
+};
+
+altKeysym_t altKeysym[] = {
+  { XK_Shift_L,        XK_Shift_R },
+  { XK_Control_L,      XK_Control_R },
+  { XK_Meta_L,         XK_Meta_R },
+  { XK_Alt_L,          XK_Alt_R },
+  { XK_Super_L,        XK_Super_R },
+  { XK_Hyper_L,        XK_Hyper_R },
+  { XK_KP_Space,       XK_space },
+  { XK_KP_Tab,         XK_Tab },
+  { XK_KP_Enter,       XK_Return },
+  { XK_KP_F1,          XK_F1 },
+  { XK_KP_F2,          XK_F2 },
+  { XK_KP_F3,          XK_F3 },
+  { XK_KP_F4,          XK_F4 },
+  { XK_KP_Home,        XK_Home },
+  { XK_KP_Left,        XK_Left },
+  { XK_KP_Up,          XK_Up },
+  { XK_KP_Right,       XK_Right },
+  { XK_KP_Down,        XK_Down },
+  { XK_KP_Page_Up,     XK_Page_Up },
+  { XK_KP_Page_Down,   XK_Page_Down },
+  { XK_KP_End,         XK_End },
+  { XK_KP_Begin,       XK_Begin },
+  { XK_KP_Insert,      XK_Insert },
+  { XK_KP_Delete,      XK_Delete },
+  { XK_KP_Equal,       XK_equal },
+  { XK_KP_Multiply,    XK_asterisk },
+  { XK_KP_Add,         XK_plus },
+  { XK_KP_Separator,   XK_comma },
+  { XK_KP_Subtract,    XK_minus },
+  { XK_KP_Decimal,     XK_period },
+  { XK_KP_Divide,      XK_slash },
+  { XK_KP_0,           XK_0 },
+  { XK_KP_1,           XK_1 },
+  { XK_KP_2,           XK_2 },
+  { XK_KP_3,           XK_3 },
+  { XK_KP_4,           XK_4 },
+  { XK_KP_5,           XK_5 },
+  { XK_KP_6,           XK_6 },
+  { XK_KP_7,           XK_7 },
+  { XK_KP_8,           XK_8 },
+  { XK_KP_9,           XK_9 },
+};
+
+// keyEvent() - work out the best keycode corresponding to the keysym sent by
+// the viewer.  This is non-trivial because we can't assume much about the
+// local keyboard layout.  We must also find out which column of the keyboard
+// mapping the keysym is in, and alter the shift state appropriately.  Column 0
+// means both shift and "mode_switch" (AltGr) must be released, column 1 means
+// shift must be pressed and mode_switch released, column 2 means shift must be
+// released and mode_switch pressed, and column 3 means both shift and
+// mode_switch must be pressed.
+
+void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
+{
+  if (keysym == XK_Caps_Lock) {
+    vlog.debug("Ignoring caps lock");
+    return;
+  }
+  DeviceIntPtr dev = (DeviceIntPtr)LookupKeyboardDevice();
+  KeyClassPtr keyc = dev->key;
+  KeySymsPtr keymap = &keyc->curKeySyms;
+
+  // find which modifier Mode_switch is on.
+  int modeSwitchMapIndex = 0;
+  for (int i = 3; i < 8; i++) {
+    for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+      int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k];
+      for (int j = 0; j < keymap->mapWidth; j++) {
+        if (keycode != 0 &&
+            keymap->map[(keycode - keymap->minKeyCode)
+                        * keymap->mapWidth + j] == XK_Mode_switch)
+        {
+          modeSwitchMapIndex = i;
+          break;
+        }
+      }
+    }
+  }
+
+  int col = 0;
+  if (keyc->state & (1<<ShiftMapIndex)) col |= 1;
+  if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) col |= 2;
+
+  int kc = KeysymToKeycode(keymap, keysym, &col);
+
+  // Sort out the "shifted Tab" mess.  If we are sent a shifted Tab, generate a
+  // local shifted Tab regardless of what the "shifted Tab" keysym is on the
+  // local keyboard (it might be Tab, ISO_Left_Tab or HP's private BackTab
+  // keysym, and quite possibly some others too).  We never get ISO_Left_Tab
+  // here because it's already been translated in VNCSConnectionST.
+  if (keysym == XK_Tab && (keyc->state & (1<<ShiftMapIndex)))
+    col |= 1;
+
+  if (kc == 0) {
+    // Not a direct match in the local keyboard mapping.  Check for alternative
+    // keysyms with the same meaning.
+    for (int i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) {
+      if (keysym == altKeysym[i].a)
+        kc = KeysymToKeycode(keymap, altKeysym[i].b, &col);
+      else if (keysym == altKeysym[i].b)
+        kc = KeysymToKeycode(keymap, altKeysym[i].a, &col);
+      if (kc) break;
+    }
+  }
+
+  if (kc == 0) {
+    // Last resort - dynamically add a new key to the keyboard mapping.
+    for (kc = keymap->maxKeyCode; kc >= keymap->minKeyCode; kc--) {
+      if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) {
+        keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym;
+        col = 0;
+        SendMappingNotify(MappingKeyboard, kc, 1, serverClient);
+        vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc);
+        break;
+      }
+    }
+    if (kc < keymap->minKeyCode) {
+      vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym);
+      return;
+    }
+  }
+
+  // See if it's a modifier key.  If so, then don't do any auto-repeat, because
+  // the X server will translate each press into a release followed by a press.
+  for (int i = 0; i < 8; i++) {
+    for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+      if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] &&
+          IS_PRESSED(keyc,kc) && down)
+        return;
+    }
+  }
+
+  ModifierState shift(dev, ShiftMapIndex);
+  ModifierState modeSwitch(dev, modeSwitchMapIndex);
+  if (down) {
+    if (col & 1)
+      shift.press();
+    else
+      shift.release();
+    if (modeSwitchMapIndex) {
+      if (col & 2)
+        modeSwitch.press();
+      else
+        modeSwitch.release();
+    }
+  }
+  vlog.debug("keycode %d %s", kc, down ? "down" : "up");
+  xEvent ev;
+  ev.u.u.type = down ? KeyPress : KeyRelease;
+  ev.u.u.detail = kc;
+  ev.u.keyButtonPointer.time = GetTimeInMillis();
+  (*dev->c_public.processInputProc)(&ev, dev, 1);
+}
+
+
+void XConvertCase(KeySym sym, KeySym *lower, KeySym *upper)
+{
+    *lower = sym;
+    *upper = sym;
+    switch(sym >> 8) {
+    case 0: /* Latin 1 */
+	if ((sym >= XK_A) && (sym <= XK_Z))
+	    *lower += (XK_a - XK_A);
+	else if ((sym >= XK_a) && (sym <= XK_z))
+	    *upper -= (XK_a - XK_A);
+	else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
+	    *lower += (XK_agrave - XK_Agrave);
+	else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
+	    *upper -= (XK_agrave - XK_Agrave);
+	else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
+	    *lower += (XK_oslash - XK_Ooblique);
+	else if ((sym >= XK_oslash) && (sym <= XK_thorn))
+	    *upper -= (XK_oslash - XK_Ooblique);
+	break;
+    case 1: /* Latin 2 */
+	/* Assume the KeySym is a legal value (ignore discontinuities) */
+	if (sym == XK_Aogonek)
+	    *lower = XK_aogonek;
+	else if (sym >= XK_Lstroke && sym <= XK_Sacute)
+	    *lower += (XK_lstroke - XK_Lstroke);
+	else if (sym >= XK_Scaron && sym <= XK_Zacute)
+	    *lower += (XK_scaron - XK_Scaron);
+	else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
+	    *lower += (XK_zcaron - XK_Zcaron);
+	else if (sym == XK_aogonek)
+	    *upper = XK_Aogonek;
+	else if (sym >= XK_lstroke && sym <= XK_sacute)
+	    *upper -= (XK_lstroke - XK_Lstroke);
+	else if (sym >= XK_scaron && sym <= XK_zacute)
+	    *upper -= (XK_scaron - XK_Scaron);
+	else if (sym >= XK_zcaron && sym <= XK_zabovedot)
+	    *upper -= (XK_zcaron - XK_Zcaron);
+	else if (sym >= XK_Racute && sym <= XK_Tcedilla)
+	    *lower += (XK_racute - XK_Racute);
+	else if (sym >= XK_racute && sym <= XK_tcedilla)
+	    *upper -= (XK_racute - XK_Racute);
+	break;
+    case 2: /* Latin 3 */
+	/* Assume the KeySym is a legal value (ignore discontinuities) */
+	if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
+	    *lower += (XK_hstroke - XK_Hstroke);
+	else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
+	    *lower += (XK_gbreve - XK_Gbreve);
+	else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
+	    *upper -= (XK_hstroke - XK_Hstroke);
+	else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
+	    *upper -= (XK_gbreve - XK_Gbreve);
+	else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
+	    *lower += (XK_cabovedot - XK_Cabovedot);
+	else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
+	    *upper -= (XK_cabovedot - XK_Cabovedot);
+	break;
+    case 3: /* Latin 4 */
+	/* Assume the KeySym is a legal value (ignore discontinuities) */
+	if (sym >= XK_Rcedilla && sym <= XK_Tslash)
+	    *lower += (XK_rcedilla - XK_Rcedilla);
+	else if (sym >= XK_rcedilla && sym <= XK_tslash)
+	    *upper -= (XK_rcedilla - XK_Rcedilla);
+	else if (sym == XK_ENG)
+	    *lower = XK_eng;
+	else if (sym == XK_eng)
+	    *upper = XK_ENG;
+	else if (sym >= XK_Amacron && sym <= XK_Umacron)
+	    *lower += (XK_amacron - XK_Amacron);
+	else if (sym >= XK_amacron && sym <= XK_umacron)
+	    *upper -= (XK_amacron - XK_Amacron);
+	break;
+    case 6: /* Cyrillic */
+	/* Assume the KeySym is a legal value (ignore discontinuities) */
+	if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
+	    *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
+	else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
+	    *upper += (XK_Serbian_DJE - XK_Serbian_dje);
+	else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
+	    *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
+	else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
+	    *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
+        break;
+    case 7: /* Greek */
+	/* Assume the KeySym is a legal value (ignore discontinuities) */
+	if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
+	    *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+	else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
+		 sym != XK_Greek_iotaaccentdieresis &&
+		 sym != XK_Greek_upsilonaccentdieresis)
+	    *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+	else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
+	    *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
+	else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
+		 sym != XK_Greek_finalsmallsigma)
+	    *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
+        break;
+    }
+}
+
+static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
+{
+  register int per = keymap->mapWidth;
+  register KeySym *syms;
+  KeySym lsym, usym;
+
+  if ((col < 0) || ((col >= per) && (col > 3)) ||
+      (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
+    return NoSymbol;
+
+  syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
+  if (col < 4) {
+    if (col > 1) {
+      while ((per > 2) && (syms[per - 1] == NoSymbol))
+        per--;
+      if (per < 3)
+        col -= 2;
+    }
+    if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
+      XConvertCase(syms[col&~1], &lsym, &usym);
+      if (!(col & 1))
+        return lsym;
+      // I'm commenting out this logic because it's incorrect even though it
+      // was copied from the Xlib sources.  The X protocol book quite clearly
+      // states that where a group consists of element 1 being a non-alphabetic
+      // keysym and element 2 being NoSymbol that you treat the second element
+      // as being the same as the first.  This also tallies with the behaviour
+      // produced by the installed Xlib on my linux box (I believe this is
+      // because it uses some XKB code rather than the original Xlib code -
+      // compare XKBBind.c with KeyBind.c in lib/X11).
+      // else if (usym == lsym)
+      //   return NoSymbol;
+      else
+        return usym;
+    }
+  }
+  return syms[col];
+}
+
+// KeysymToKeycode() - find the keycode and column corresponding to the given
+// keysym.  The value of col passed in should be the column determined from the
+// current shift state.  If the keysym can be found in that column we prefer
+// that to finding it in a different column (which would require fake events to
+// alter the shift state).
+
+static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col)
+{
+  register int i, j;
+
+  j = *col;
+  for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+    if (KeyCodetoKeySym(keymap, i, j) == ks)
+      return i;
+  }
+
+  for (j = 0; j < keymap->mapWidth; j++) {
+    for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+      if (KeyCodetoKeySym(keymap, i, j) == ks) {
+        *col = j;
+        return i;
+      }
+    }
+  }
+  return 0;
+}
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.h b/xc/programs/Xserver/vnc/XserverDesktop.h
new file mode 100644
index 0000000..c983ece
--- /dev/null
+++ b/xc/programs/Xserver/vnc/XserverDesktop.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+//
+// XserverDesktop.h
+//
+
+#ifndef __XSERVERDESKTOP_H__
+#define __XSERVERDESKTOP_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/Configuration.h>
+#include <rdr/SubstitutingInStream.h>
+
+extern "C" {
+#define class c_class;
+#include <scrnintstr.h>
+#include <os.h>
+#undef class
+}
+
+namespace rfb {
+  class VNCServerST;
+}
+
+namespace network { class TcpListener; class Socket; }
+class MyHTTPServer;
+
+class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
+                       public rfb::ColourMap, public rdr::Substitutor {
+public:
+
+  XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener,
+                 network::TcpListener* httpListener_,
+                 const char* name, void* fbptr);
+  virtual ~XserverDesktop();
+
+  // methods called from X server code
+  void serverReset(ScreenPtr pScreen);
+  void setColormap(ColormapPtr cmap);
+  void setColourMapEntries(ColormapPtr pColormap, int ndef, xColorItem* pdef);
+  void bell();
+  void serverCutText(const char* str, int len);
+  void setCursor(CursorPtr cursor);
+  void add_changed(RegionPtr reg);
+  void add_copied(RegionPtr dst, int dx, int dy);
+  void positionCursor();
+  void ignoreHooks(bool b) { ignoreHooks_ = b; }
+  void blockHandler(fd_set* fds);
+  void wakeupHandler(fd_set* fds, int nfds);
+  void addClient(network::Socket* sock, bool reverse);
+  void disconnectClients();
+
+  // rfb::SDesktop callbacks
+  virtual void pointerEvent(const rfb::Point& pos, rdr::U8 buttonMask);
+  virtual void keyEvent(rdr::U32 key, bool down);
+  virtual void clientCutText(const char* str, int len);
+  virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); }
+
+  // rfb::PixelBuffer callbacks
+  virtual void grabRegion(const rfb::Region& r);
+
+  // rfb::ColourMap callbacks
+  virtual void lookup(int index, int* r, int* g, int* b);
+
+  // rdr::Substitutor callback
+  virtual char* substitute(const char* varName);
+
+private:
+  void setColourMapEntries(int firstColour, int nColours);
+  static CARD32 deferredUpdateTimerCallback(OsTimerPtr timer, CARD32 now,
+                                            pointer arg);
+  ScreenPtr pScreen;
+  OsTimerPtr deferredUpdateTimer, dummyTimer;
+  rfb::VNCServerST* server;
+  MyHTTPServer* httpServer;
+  network::TcpListener* listener;
+  network::TcpListener* httpListener;
+  ColormapPtr cmap;
+  bool deferredUpdateTimerSet;
+  bool grabbing;
+  bool ignoreHooks_;
+  bool directFbptr;
+  int oldButtonMask;
+  int cursorX, cursorY, oldCursorX, oldCursorY;
+};
+#endif
diff --git a/xc/programs/Xserver/vnc/Xvnc/Imakefile b/xc/programs/Xserver/vnc/Xvnc/Imakefile
new file mode 100644
index 0000000..d948887
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/Imakefile
@@ -0,0 +1,74 @@
+
+       VNCTOP = $(TOP)/..
+      VNCLIBS = VncExtLibs
+   VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#include <Server.tmpl>
+
+#ifdef XVendorString
+VENDORSTRING = XVendorString
+#else
+VENDORSTRING = "unknown"
+#endif
+
+#ifdef XVendorRelease
+VENDORRELEASE = XVendorRelease
+#else
+VENDORRELEASE = 0
+#endif
+
+   VENDOR_STRING = -DVENDOR_STRING=\"$(VENDORSTRING)\"
+   VENDOR_RELEASE = -DVENDOR_RELEASE="$(VENDORRELEASE)"
+
+#ifdef OS2Architecture
+SRCS1 = os2_stubs.c
+OBJS1 = os2_stubs.o
+#endif
+
+SRCSA =	xvnc.cc stubs.c $(SRCS1) miinitext.c $(SRCS2)
+
+OBJSA =	xvnc.o stubs.o $(OBJS1) miinitext.o $(OBJS2)
+
+INCLUDES = -I. -I.. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
+	   -I../../cfb -I../../mfb -I../../mi -I../../include -I../../os  \
+           -I$(EXTINCSRC) -I$(XINCLUDESRC)  -I$(SERVERSRC)/render $(VNCINCLUDE)
+
+DEFINES = $(OS_DEFINES) $(SHMDEF) $(MMAPDEF) \
+          $(VENDOR_STRING) $(VENDOR_RELEASE) $(STD_DEFINES) ServerOSDefines \
+          -UXFree86LOADER
+
+#ifdef XFree86Version
+/* 
+ * Make sure XINPUT, XF86VidTune, etc arent defined for the miinitext.o 
+ * used by Xvnc 
+ */
+EXT_DEFINES = ExtensionDefines -UXINPUT -UXF86VIDMODE -UXFreeXDGA -UXF86MISC
+#endif
+
+
+SRCS =	$(SRCSA) $(SRCSB) $(SRCSC)
+OBJS =	$(OBJSA) $(OBJSB) $(OBJSC)
+
+NormalLibraryObjectRule()
+NormalLibraryTarget(xvnc,$(OBJS) buildtime.o)
+
+#ifdef OS2Architecture
+LinkSourceFile(os2_stubs.c,../xfree86/os-support/os2)
+SpecialCObjectRule(os2_stubs,$(ICONFIGFILES),-DOS2NULLSELECT)
+#endif
+
+#ifdef HasGcc
+NO_OPERATOR_NAMES = -fno-operator-names
+#endif
+LinkSourceFile(stubs.c,../../Xi)
+SpecialCplusplusObjectRule(xvnc,$(ICONFIGFILES) xvnc,$(EXT_DEFINES) $(NO_OPERATOR_NAMES))
+
+LinkSourceFile(miinitext.c,$(SERVERSRC)/mi)
+SpecialCObjectRule(miinitext,$(ICONFIGFILES),$(EXT_DEFINES) $(PAN_DEFINES) -DNO_HW_ONLY_EXTS -DNO_MODULE_EXTS $(EXT_MODULE_DEFINES) -UXFree86LOADER)
+
+/* InstallManPage(Xvfb,$(MANDIR)) */
+DependTarget()
+
+buildtime.o: $(OBJS) ../LibraryTargetName(vnc) $(VNCLIBS)
diff --git a/xc/programs/Xserver/vnc/Xvnc/buildtime.c b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
new file mode 100644
index 0000000..a96031c
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/xc/programs/Xserver/vnc/Xvnc/xvnc.cc b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
new file mode 100644
index 0000000..cf8fdf9
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
@@ -0,0 +1,1221 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+/*
+
+Copyright (c) 1993  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <network/TcpSocket.h>
+#include "vncExtInit.h"
+
+extern "C" {
+#define class c_class
+#define public c_public
+#define xor c_xor
+#define and c_and
+#ifdef WIN32
+#include <X11/Xwinsock.h>
+#endif
+#include <stdio.h>
+#include "X11/X.h"
+#define NEED_EVENTS
+#include "X11/Xproto.h"
+#include "X11/Xos.h"
+#include "scrnintstr.h"
+#include "servermd.h"
+#define PSZ 8
+#include "cfb.h"
+#include "mi.h"
+#include "mibstore.h"
+#include "colormapst.h"
+#include "gcstruct.h"
+#include "input.h"
+#include "mipointer.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifndef WIN32
+#include <sys/param.h>
+#endif
+#include <X11/XWDFile.h>
+#include "dix.h"
+#include "miline.h"
+#include "inputstr.h"
+#include "keysym.h"
+  extern int defaultColorVisualClass;
+  extern char buildtime[];
+#undef class
+#undef public
+#undef xor
+#undef and
+  extern Bool cfb16ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
+  extern Bool cfb32ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
+  extern Bool cfb16CreateGC(GCPtr);
+  extern Bool cfb32CreateGC(GCPtr);
+  extern void cfb16GetSpans(DrawablePtr, int, DDXPointPtr, int*, int, char*);
+  extern void cfb32GetSpans(DrawablePtr, int, DDXPointPtr, int*, int, char*);
+  extern void cfb16GetImage(DrawablePtr, int, int, int, int, unsigned int,
+                            unsigned long, char*);
+  extern void cfb32GetImage(DrawablePtr, int, int, int, int, unsigned int,
+                            unsigned long, char*);
+}
+
+#define XVNCVERSION "4.0"
+
+extern char *display;
+extern int monitorResolution;
+
+#define VFB_DEFAULT_WIDTH  1024
+#define VFB_DEFAULT_HEIGHT 768
+#define VFB_DEFAULT_DEPTH  16
+#define VFB_DEFAULT_WHITEPIXEL 0xffff
+#define VFB_DEFAULT_BLACKPIXEL 0
+#define VFB_DEFAULT_LINEBIAS 0
+#define XWD_WINDOW_NAME_LEN 60
+
+typedef struct
+{
+  int scrnum;
+  int width;
+  int paddedWidth;
+  int paddedWidthInBytes;
+  int height;
+  int depth;
+  int bitsPerPixel;
+  int sizeInBytes;
+  int ncolors;
+  char *pfbMemory;
+  XWDColor *pXWDCmap;
+  XWDFileHeader *pXWDHeader;
+  Pixel blackPixel;
+  Pixel whitePixel;
+  unsigned int lineBias;
+  Bool pixelFormatDefined;
+  Bool rgbNotBgr;
+  int redBits, greenBits, blueBits;
+
+} vfbScreenInfo, *vfbScreenInfoPtr;
+
+static int vfbNumScreens;
+static vfbScreenInfo vfbScreens[MAXSCREENS];
+static Bool vfbPixmapDepths[33];
+static char needswap = 0;
+static int lastScreen = -1;
+
+static bool displaySpecified = false;
+static bool wellKnownSocketsCreated = false;
+static char displayNumStr[16];
+
+#define swapcopy16(_dst, _src) \
+    if (needswap) { CARD16 _s = _src; cpswaps(_s, _dst); } \
+    else _dst = _src;
+
+#define swapcopy32(_dst, _src) \
+    if (needswap) { CARD32 _s = _src; cpswapl(_s, _dst); } \
+    else _dst = _src;
+
+
+static void vfbInitializePixmapDepths()
+{
+  int i;
+  vfbPixmapDepths[1] = TRUE; /* always need bitmaps */
+  for (i = 2; i <= 32; i++)
+    vfbPixmapDepths[i] = FALSE;
+}
+
+static void vfbInitializeDefaultScreens()
+{
+  int i;
+
+  for (i = 0; i < MAXSCREENS; i++)
+  {
+    vfbScreens[i].scrnum = i;
+    vfbScreens[i].width  = VFB_DEFAULT_WIDTH;
+    vfbScreens[i].height = VFB_DEFAULT_HEIGHT;
+    vfbScreens[i].depth  = VFB_DEFAULT_DEPTH;
+    vfbScreens[i].blackPixel = VFB_DEFAULT_BLACKPIXEL;
+    vfbScreens[i].whitePixel = VFB_DEFAULT_WHITEPIXEL;
+    vfbScreens[i].lineBias = VFB_DEFAULT_LINEBIAS;
+    vfbScreens[i].pixelFormatDefined = FALSE;
+    vfbScreens[i].pfbMemory = NULL;
+  }
+  vfbNumScreens = 1;
+}
+
+static int vfbBitsPerPixel(int depth)
+{
+  if (depth == 1) return 1;
+  else if (depth <= 8) return 8;
+  else if (depth <= 16) return 16;
+  else return 32;
+}
+
+extern "C" {
+  void ddxGiveUp()
+  {
+    int i;
+
+    /* clean up the framebuffers */
+
+    for (i = 0; i < vfbNumScreens; i++)
+    {
+      Xfree(vfbScreens[i].pXWDHeader);
+    }
+
+    // Remove any unix domain sockets left behind.  I think these should
+    // already have been cleaned up but it doesn't hurt to try again.
+    if (wellKnownSocketsCreated) {
+      char sockName[64];
+      sprintf(sockName,"/tmp/.X11-unix/X%s",display);
+      unlink(sockName);
+      sprintf(sockName,"/usr/spool/sockets/X11/%s",display);
+      unlink(sockName);
+    }
+  }
+
+  void AbortDDX() { ddxGiveUp(); }
+  void OsVendorInit() {}
+  void OsVendorFatalError() {}
+
+  void ddxUseMsg()
+  {
+    ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
+    ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
+           VENDOR_STRING);
+    ErrorF("-screen scrn WxHxD     set screen's width, height, depth\n");
+    ErrorF("-pixdepths list-of-int support given pixmap depths\n");
+    ErrorF("-linebias n            adjust thin line pixelization\n");
+    ErrorF("-blackpixel n          pixel value for black\n");
+    ErrorF("-whitepixel n          pixel value for white\n");
+    ErrorF("-geometry WxH          set screen 0's width, height\n");
+    ErrorF("-depth D               set screen 0's depth\n");
+    ErrorF("-pixelformat fmt       set pixel format (rgbNNN or bgrNNN)\n");
+    ErrorF("-inetd                 has been launched from inetd\n");
+    ErrorF("\nVNC parameters:\n");
+
+    fprintf(stderr,"\n"
+            "Parameters can be turned on with -<param> or off with -<param>=0\n"
+            "Parameters which take a value can be specified as "
+            "-<param> <value>\n"
+            "Other valid forms are <param>=<value> -<param>=<value> "
+            "--<param>=<value>\n"
+            "Parameter names are case-insensitive.  The parameters are:\n\n");
+    rfb::Configuration::listParams(79, 14);
+  }
+}
+
+static bool displayNumFree(int num)
+{
+  try {
+    network::TcpListener l(6000+num);
+  } catch (rdr::Exception& e) {
+    return false;
+  }
+  char file[256];
+  sprintf(file, "/tmp/.X%d-lock", num);
+  if (access(file, F_OK) == 0) return false;
+  sprintf(file, "/tmp/.X11-unix/X%d", num);
+  if (access(file, F_OK) == 0) return false;
+  sprintf(file, "/usr/spool/sockets/X11/%d", num);
+  if (access(file, F_OK) == 0) return false;
+  return true;
+}
+
+int ddxProcessArgument(int argc, char *argv[], int i)
+{
+  static Bool firstTime = TRUE;
+
+  if (firstTime)
+  {
+    vfbInitializeDefaultScreens();
+    vfbInitializePixmapDepths();
+    firstTime = FALSE;
+    rfb::initStdIOLoggers();
+    rfb::LogWriter::setLogParams("*:stderr:30");
+  }
+
+  if (argv[i][0] ==  ':')
+    displaySpecified = true;
+
+  if (strcmp (argv[i], "-screen") == 0)	/* -screen n WxHxD */
+  {
+    int screenNum;
+    if (i + 2 >= argc) UseMsg();
+    screenNum = atoi(argv[i+1]);
+    if (screenNum < 0 || screenNum >= MAXSCREENS)
+    {
+      ErrorF("Invalid screen number %d\n", screenNum);
+      UseMsg();
+    }
+    if (3 != sscanf(argv[i+2], "%dx%dx%d",
+                    &vfbScreens[screenNum].width,
+                    &vfbScreens[screenNum].height,
+                    &vfbScreens[screenNum].depth))
+    {
+      ErrorF("Invalid screen configuration %s\n", argv[i+2]);
+      UseMsg();
+    }
+
+    if (screenNum >= vfbNumScreens)
+      vfbNumScreens = screenNum + 1;
+    lastScreen = screenNum;
+    return 3;
+  }
+
+  if (strcmp (argv[i], "-pixdepths") == 0)	/* -pixdepths list-of-depth */
+  {
+    int depth, ret = 1;
+
+    if (++i >= argc) UseMsg();
+    while ((i < argc) && (depth = atoi(argv[i++])) != 0)
+    {
+      if (depth < 0 || depth > 32)
+      {
+        ErrorF("Invalid pixmap depth %d\n", depth);
+        UseMsg();
+      }
+      vfbPixmapDepths[depth] = TRUE;
+      ret++;
+    }
+    return ret;
+  }
+
+  if (strcmp (argv[i], "-blackpixel") == 0)	/* -blackpixel n */
+  {
+    Pixel pix;
+    if (++i >= argc) UseMsg();
+    pix = atoi(argv[i]);
+    if (-1 == lastScreen)
+    {
+      int i;
+      for (i = 0; i < MAXSCREENS; i++)
+      {
+        vfbScreens[i].blackPixel = pix;
+      }
+    }
+    else
+    {
+      vfbScreens[lastScreen].blackPixel = pix;
+    }
+    return 2;
+  }
+
+  if (strcmp (argv[i], "-whitepixel") == 0)	/* -whitepixel n */
+  {
+    Pixel pix;
+    if (++i >= argc) UseMsg();
+    pix = atoi(argv[i]);
+    if (-1 == lastScreen)
+    {
+      int i;
+      for (i = 0; i < MAXSCREENS; i++)
+      {
+        vfbScreens[i].whitePixel = pix;
+      }
+    }
+    else
+    {
+      vfbScreens[lastScreen].whitePixel = pix;
+    }
+    return 2;
+  }
+
+  if (strcmp (argv[i], "-linebias") == 0)	/* -linebias n */
+  {
+    unsigned int linebias;
+    if (++i >= argc) UseMsg();
+    linebias = atoi(argv[i]);
+    if (-1 == lastScreen)
+    {
+      int i;
+      for (i = 0; i < MAXSCREENS; i++)
+      {
+        vfbScreens[i].lineBias = linebias;
+      }
+    }
+    else
+    {
+      vfbScreens[lastScreen].lineBias = linebias;
+    }
+    return 2;
+  }
+
+  if (strcmp(argv[i], "-geometry") == 0)
+  {
+    if (++i >= argc) UseMsg();
+    if (sscanf(argv[i],"%dx%d",&vfbScreens[0].width,
+               &vfbScreens[0].height) != 2) {
+      ErrorF("Invalid geometry %s\n", argv[i]);
+      UseMsg();
+    }
+    return 2;
+  }
+
+  if (strcmp(argv[i], "-depth") == 0)
+  {
+    if (++i >= argc) UseMsg();
+    vfbScreens[0].depth = atoi(argv[i]);
+    return 2;
+  }
+
+  if (strcmp(argv[i], "-pixelformat") == 0)
+  {
+    char rgbbgr[4];
+    int bits1, bits2, bits3;
+    if (++i >= argc) UseMsg();
+    if (sscanf(argv[i], "%3s%1d%1d%1d", rgbbgr,&bits1,&bits2,&bits3) < 4) {
+      ErrorF("Invalid pixel format %s\n", argv[i]);
+      UseMsg();
+    }
+
+#define SET_PIXEL_FORMAT(vfbScreen)                     \
+    (vfbScreen).pixelFormatDefined = TRUE;              \
+    (vfbScreen).depth = bits1 + bits2 + bits3;          \
+    (vfbScreen).greenBits = bits2;                      \
+    if (strcasecmp(rgbbgr, "bgr") == 0) {               \
+        (vfbScreen).rgbNotBgr = FALSE;                  \
+        (vfbScreen).redBits = bits3;                    \
+        (vfbScreen).blueBits = bits1;                   \
+    } else if (strcasecmp(rgbbgr, "rgb") == 0) {        \
+        (vfbScreen).rgbNotBgr = TRUE;                   \
+        (vfbScreen).redBits = bits1;                    \
+        (vfbScreen).blueBits = bits3;                   \
+    } else {                                            \
+        ErrorF("Invalid pixel format %s\n", argv[i]);   \
+        UseMsg();                                       \
+    }
+
+    if (-1 == lastScreen)
+    {
+      int i;
+      for (i = 0; i < MAXSCREENS; i++)
+      {
+        SET_PIXEL_FORMAT(vfbScreens[i]);
+      }
+    }
+    else
+    {
+      SET_PIXEL_FORMAT(vfbScreens[lastScreen]);
+    }
+
+    return 2;
+  }
+
+  if (strcmp(argv[i], "-inetd") == 0)
+  {
+    dup2(0,3);
+    vncInetdSock = 3;
+    close(2);
+
+    if (!displaySpecified) {
+      int port = network::TcpSocket::getSockPort(vncInetdSock);
+      int displayNum = port - 5900;
+      if (displayNum < 0 || displayNum > 99 || !displayNumFree(displayNum)) {
+        for (displayNum = 1; displayNum < 100; displayNum++)
+          if (displayNumFree(displayNum)) break;
+
+        if (displayNum == 100)
+          FatalError("Xvnc error: no free display number for -inetd");
+      }
+
+      display = displayNumStr;
+      sprintf(displayNumStr, "%d", displayNum);
+    }
+
+    return 1;
+  }
+
+  if (rfb::Configuration::setParam(argv[i]))
+    return 1;
+
+  if (argv[i][0] == '-' && i+1 < argc) {
+    if (rfb::Configuration::setParam(&argv[i][1], argv[i+1]))
+      return 2;
+  }
+
+  return 0;
+}
+
+#ifdef DDXTIME /* from ServerOSDefines */
+CARD32 GetTimeInMillis()
+{
+  struct timeval  tp;
+
+  X_GETTIMEOFDAY(&tp);
+  return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+#endif
+
+
+static Bool vfbMultiDepthCreateGC(GCPtr   pGC)
+{
+  switch (vfbBitsPerPixel(pGC->depth))
+  {
+  case 1:  return mfbCreateGC (pGC);
+  case 8:  return cfbCreateGC (pGC);
+  case 16: return cfb16CreateGC (pGC);
+  case 32: return cfb32CreateGC (pGC);
+  default: return FALSE;
+  }
+}
+
+static void vfbMultiDepthGetSpans(
+                                  DrawablePtr		pDrawable,	/* drawable from which to get bits */
+                                  int			wMax,		/* largest value of all *pwidths */
+                                  register DDXPointPtr ppt,		/* points to start copying from */
+                                  int			*pwidth,	/* list of number of bits to copy */
+                                  int			nspans,		/* number of scanlines to copy */
+                                  char		*pdstStart)	/* where to put the bits */
+{
+  switch (pDrawable->bitsPerPixel) {
+  case 1:
+    mfbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+    break;
+  case 8:
+    cfbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+    break;
+  case 16:
+    cfb16GetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+    break;
+  case 32:
+    cfb32GetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+    break;
+  }
+  return;
+}
+
+static void
+vfbMultiDepthGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h,
+                      unsigned int format, unsigned long planeMask,
+                      char *pdstLine)
+{
+  switch (pDrawable->bitsPerPixel)
+  {
+  case 1:
+    mfbGetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+    break;
+  case 8:
+    cfbGetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+    break;
+  case 16:
+    cfb16GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+    break;
+  case 32:
+    cfb32GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+    break;
+  }
+}
+
+static ColormapPtr InstalledMaps[MAXSCREENS];
+
+static int vfbListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps)
+{
+  /* By the time we are processing requests, we can guarantee that there
+   * is always a colormap installed */
+  *pmaps = InstalledMaps[pScreen->myNum]->mid;
+  return (1);
+}
+
+
+static void vfbInstallColormap(ColormapPtr pmap)
+{
+  int index = pmap->pScreen->myNum;
+  ColormapPtr oldpmap = InstalledMaps[index];
+
+  if (pmap != oldpmap)
+  {
+    int entries;
+    XWDFileHeader *pXWDHeader;
+    XWDColor *pXWDCmap;
+    VisualPtr pVisual;
+    Pixel *     ppix;
+    xrgb *      prgb;
+    xColorItem *defs;
+    int i;
+
+    if(oldpmap != (ColormapPtr)None)
+      WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid);
+    /* Install pmap */
+    InstalledMaps[index] = pmap;
+    WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid);
+
+    entries = pmap->pVisual->ColormapEntries;
+    pXWDHeader = vfbScreens[pmap->pScreen->myNum].pXWDHeader;
+    pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap;
+    pVisual = pmap->pVisual;
+
+    swapcopy32(pXWDHeader->visual_class, pVisual->c_class);
+    swapcopy32(pXWDHeader->red_mask, pVisual->redMask);
+    swapcopy32(pXWDHeader->green_mask, pVisual->greenMask);
+    swapcopy32(pXWDHeader->blue_mask, pVisual->blueMask);
+    swapcopy32(pXWDHeader->bits_per_rgb, pVisual->bitsPerRGBValue);
+    swapcopy32(pXWDHeader->colormap_entries, pVisual->ColormapEntries);
+
+    ppix = (Pixel *)ALLOCATE_LOCAL(entries * sizeof(Pixel));
+    prgb = (xrgb *)ALLOCATE_LOCAL(entries * sizeof(xrgb));
+    defs = (xColorItem *)ALLOCATE_LOCAL(entries * sizeof(xColorItem));
+
+    for (i = 0; i < entries; i++)  ppix[i] = i;
+    /* XXX truecolor */
+    QueryColors(pmap, entries, ppix, prgb);
+
+    for (i = 0; i < entries; i++) { /* convert xrgbs to xColorItems */
+      defs[i].pixel = ppix[i] & 0xff; /* change pixel to index */
+      defs[i].red = prgb[i].red;
+      defs[i].green = prgb[i].green;
+      defs[i].blue = prgb[i].blue;
+      defs[i].flags =  DoRed|DoGreen|DoBlue;
+    }
+    (*pmap->pScreen->StoreColors)(pmap, entries, defs);
+
+    DEALLOCATE_LOCAL(ppix);
+    DEALLOCATE_LOCAL(prgb);
+    DEALLOCATE_LOCAL(defs);
+  }
+}
+
+static void vfbUninstallColormap(ColormapPtr pmap)
+{
+  ColormapPtr curpmap = InstalledMaps[pmap->pScreen->myNum];
+
+  if(pmap == curpmap)
+  {
+    if (pmap->mid != pmap->pScreen->defColormap)
+    {
+      curpmap = (ColormapPtr) LookupIDByType(pmap->pScreen->defColormap,
+                                             RT_COLORMAP);
+      (*pmap->pScreen->InstallColormap)(curpmap);
+    }
+  }
+}
+
+static void vfbStoreColors(ColormapPtr pmap, int ndef, xColorItem *pdefs)
+{
+  XWDColor *pXWDCmap;
+  int i;
+
+  if (pmap != InstalledMaps[pmap->pScreen->myNum]) return;
+
+  pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap;
+
+  if ((pmap->pVisual->c_class | DynamicClass) == DirectColor)
+    return;
+
+  for (i = 0; i < ndef; i++)
+  {
+    if (pdefs[i].flags & DoRed) {
+      swapcopy16(pXWDCmap[pdefs[i].pixel].red, pdefs[i].red);
+    }
+    if (pdefs[i].flags & DoGreen) {
+      swapcopy16(pXWDCmap[pdefs[i].pixel].green, pdefs[i].green);
+    }
+    if (pdefs[i].flags & DoBlue) {
+      swapcopy16(pXWDCmap[pdefs[i].pixel].blue, pdefs[i].blue);
+    }
+  }
+}
+
+static Bool vfbSaveScreen(ScreenPtr pScreen, int on)
+{
+  return TRUE;
+}
+
+static char* vfbAllocateFramebufferMemory(vfbScreenInfoPtr pvfb)
+{
+  if (pvfb->pfbMemory) return pvfb->pfbMemory; /* already done */
+
+  pvfb->sizeInBytes = pvfb->paddedWidthInBytes * pvfb->height;
+
+  /* Calculate how many entries in colormap.  This is rather bogus, because
+   * the visuals haven't even been set up yet, but we need to know because we
+   * have to allocate space in the file for the colormap.  The number 10
+   * below comes from the MAX_PSEUDO_DEPTH define in cfbcmap.c.
+   */
+
+  if (pvfb->depth <= 10)
+  { /* single index colormaps */
+    pvfb->ncolors = 1 << pvfb->depth;
+  }
+  else
+  { /* decomposed colormaps */
+    int nplanes_per_color_component = pvfb->depth / 3;
+    if (pvfb->depth % 3) nplanes_per_color_component++;
+    pvfb->ncolors = 1 << nplanes_per_color_component;
+  }
+
+  /* add extra bytes for XWDFileHeader, window name, and colormap */
+
+  pvfb->sizeInBytes += SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN +
+    pvfb->ncolors * SIZEOF(XWDColor);
+
+  pvfb->pXWDHeader = NULL; 
+  pvfb->pXWDHeader = (XWDFileHeader *)Xalloc(pvfb->sizeInBytes);
+
+  if (pvfb->pXWDHeader)
+  {
+    pvfb->pXWDCmap = (XWDColor *)((char *)pvfb->pXWDHeader
+                                  + SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN);
+    pvfb->pfbMemory = (char *)(pvfb->pXWDCmap + pvfb->ncolors);
+    memset(pvfb->pfbMemory, 0, pvfb->paddedWidthInBytes * pvfb->height);
+    return pvfb->pfbMemory;
+  }
+  else
+    return NULL;
+}
+
+
+static void vfbWriteXWDFileHeader(ScreenPtr pScreen)
+{
+  vfbScreenInfoPtr pvfb = &vfbScreens[pScreen->myNum];
+  XWDFileHeader *pXWDHeader = pvfb->pXWDHeader;
+  char hostname[XWD_WINDOW_NAME_LEN];
+  VisualPtr	pVisual;
+  unsigned long swaptest = 1;
+  int i;
+
+  needswap = *(char *) &swaptest;
+
+  pXWDHeader->header_size = (char *)pvfb->pXWDCmap - (char *)pvfb->pXWDHeader;
+  pXWDHeader->file_version = XWD_FILE_VERSION;
+
+  pXWDHeader->pixmap_format = ZPixmap;
+  pXWDHeader->pixmap_depth = pvfb->depth;
+  pXWDHeader->pixmap_height = pXWDHeader->window_height = pvfb->height;
+  pXWDHeader->xoffset = 0;
+  pXWDHeader->byte_order = IMAGE_BYTE_ORDER;
+  pXWDHeader->bitmap_bit_order = BITMAP_BIT_ORDER;
+#ifndef INTERNAL_VS_EXTERNAL_PADDING
+  pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->width;
+  pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT;
+  pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD;
+#else
+  pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->paddedWidth;
+  pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT_PROTO;
+  pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD_PROTO;
+#endif
+  pXWDHeader->bits_per_pixel = pvfb->bitsPerPixel;
+  pXWDHeader->bytes_per_line = pvfb->paddedWidthInBytes;
+  pXWDHeader->ncolors = pvfb->ncolors;
+
+  /* visual related fields are written when colormap is installed */
+
+  pXWDHeader->window_x = pXWDHeader->window_y = 0;
+  pXWDHeader->window_bdrwidth = 0;
+
+  /* write xwd "window" name: Xvfb hostname:server.screen */
+
+  hostname[0] = 0;
+  sprintf((char *)(pXWDHeader+1), "Xvfb %s:%s.%d", hostname, display,
+          pScreen->myNum);
+
+  /* write colormap pixel slot values */
+
+  for (i = 0; i < pvfb->ncolors; i++)
+  {
+    pvfb->pXWDCmap[i].pixel = i;
+  }
+
+  /* byte swap to most significant byte first */
+
+  if (needswap)
+  {
+    SwapLongs((CARD32 *)pXWDHeader, SIZEOF(XWDheader)/4);
+    for (i = 0; i < pvfb->ncolors; i++)
+    {
+      register char n;
+      swapl(&pvfb->pXWDCmap[i].pixel, n);
+    }
+  }
+}
+
+
+static Bool vfbCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y) {
+  return FALSE;
+}
+static void vfbCrossScreen (ScreenPtr pScreen, Bool entering) {}
+static Bool vfbRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) {
+  return TRUE;
+}
+static Bool vfbUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) {
+  return TRUE;
+}
+static void vfbSetCursor(ScreenPtr pScreen, CursorPtr pCursor,
+                               int x, int y) {}
+static void vfbMoveCursor(ScreenPtr pScreen, int x, int y) {}
+
+static miPointerSpriteFuncRec vfbPointerSpriteFuncs = {
+  vfbRealizeCursor,
+  vfbUnrealizeCursor,
+  vfbSetCursor,
+  vfbMoveCursor
+};
+
+static miPointerScreenFuncRec vfbPointerScreenFuncs = {
+  vfbCursorOffScreen,
+  vfbCrossScreen,
+  miPointerWarpCursor
+};
+
+static Bool vfbScreenInit(int index, ScreenPtr pScreen, int argc, char** argv)
+{
+  vfbScreenInfoPtr pvfb = &vfbScreens[index];
+  int dpi = 100;
+  int ret;
+  char *pbits;
+
+  if (monitorResolution) dpi = monitorResolution;
+
+  pvfb->paddedWidthInBytes = PixmapBytePad(pvfb->width, pvfb->depth);
+  pvfb->bitsPerPixel = vfbBitsPerPixel(pvfb->depth);
+  pvfb->paddedWidth = pvfb->paddedWidthInBytes * 8 / pvfb->bitsPerPixel;
+  pbits = vfbAllocateFramebufferMemory(pvfb);
+  if (!pbits) return FALSE;
+  vncFbptr[index] = pbits;
+
+  defaultColorVisualClass
+    = (pvfb->bitsPerPixel > 8) ? TrueColor : PseudoColor;
+
+  switch (pvfb->bitsPerPixel)
+  {
+  case 1:
+    ret = mfbScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+                        dpi, dpi, pvfb->paddedWidth);
+    break;
+  case 8:
+    ret = cfbScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+                        dpi, dpi, pvfb->paddedWidth);
+    break;
+  case 16:
+    ret = cfb16ScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+                          dpi, dpi, pvfb->paddedWidth);
+    break;
+  case 32:
+    ret = cfb32ScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+                          dpi, dpi, pvfb->paddedWidth);
+    break;
+  default:
+    return FALSE;
+  }
+
+  if (!ret) return FALSE;
+
+  pScreen->CreateGC = vfbMultiDepthCreateGC;
+  pScreen->GetImage = vfbMultiDepthGetImage;
+  pScreen->GetSpans = vfbMultiDepthGetSpans;
+
+  pScreen->InstallColormap = vfbInstallColormap;
+  pScreen->UninstallColormap = vfbUninstallColormap;
+  pScreen->ListInstalledColormaps = vfbListInstalledColormaps;
+
+  pScreen->SaveScreen = vfbSaveScreen;
+  pScreen->StoreColors = vfbStoreColors;
+
+  miPointerInitialize(pScreen, &vfbPointerSpriteFuncs, &vfbPointerScreenFuncs,
+                      FALSE);
+
+  vfbWriteXWDFileHeader(pScreen);
+
+  pScreen->blackPixel = pvfb->blackPixel;
+  pScreen->whitePixel = pvfb->whitePixel;
+
+  if (!pvfb->pixelFormatDefined && pvfb->depth == 16) {
+    pvfb->pixelFormatDefined = TRUE;
+    pvfb->rgbNotBgr = TRUE;
+    pvfb->blueBits = pvfb->redBits = 5;
+    pvfb->greenBits = 6;
+  }
+
+  if (pvfb->pixelFormatDefined) {
+    VisualPtr vis;
+    for (vis = pScreen->visuals; vis->vid != pScreen->rootVisual; vis++)
+      ;
+
+    if (pvfb->rgbNotBgr) {
+      vis->offsetBlue = 0;
+      vis->blueMask = (1 << pvfb->blueBits) - 1;
+      vis->offsetGreen = pvfb->blueBits;
+      vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen;
+      vis->offsetRed = vis->offsetGreen + pvfb->greenBits;
+      vis->redMask = ((1 << pvfb->redBits) - 1) << vis->offsetRed;
+    } else {
+      vis->offsetRed = 0;
+      vis->redMask = (1 << pvfb->redBits) - 1;
+      vis->offsetGreen = pvfb->redBits;
+      vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen;
+      vis->offsetBlue = vis->offsetGreen + pvfb->greenBits;
+      vis->blueMask = ((1 << pvfb->blueBits) - 1) << vis->offsetBlue;
+    }
+  }
+
+  if (pvfb->bitsPerPixel == 1)
+  {
+    ret = mfbCreateDefColormap(pScreen);
+  }
+  else
+  {
+    ret = cfbCreateDefColormap(pScreen);
+  }
+
+  miSetZeroLineBias(pScreen, pvfb->lineBias);
+
+  return ret;
+
+} /* end vfbScreenInit */
+
+
+static void vfbClientStateChange(CallbackListPtr*, pointer, pointer) {
+  dispatchException &= ~DE_RESET;
+}
+
+void InitOutput(ScreenInfo *screenInfo, int argc, char **argv)
+{
+  ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
+  ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
+         VENDOR_STRING);
+  wellKnownSocketsCreated = true;
+
+  int i;
+  int NumFormats = 0;
+
+  /* initialize pixmap formats */
+
+  /* must have a pixmap depth to match every screen depth */
+  for (i = 0; i < vfbNumScreens; i++)
+  {
+    vfbPixmapDepths[vfbScreens[i].depth] = TRUE;
+  }
+
+  for (i = 1; i <= 32; i++)
+  {
+    if (vfbPixmapDepths[i])
+    {
+      if (NumFormats >= MAXFORMATS)
+        FatalError ("MAXFORMATS is too small for this server\n");
+      screenInfo->formats[NumFormats].depth = i;
+      screenInfo->formats[NumFormats].bitsPerPixel = vfbBitsPerPixel(i);
+      screenInfo->formats[NumFormats].scanlinePad = BITMAP_SCANLINE_PAD;
+      NumFormats++;
+    }
+  }
+
+  screenInfo->imageByteOrder = IMAGE_BYTE_ORDER;
+  screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT;
+  screenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD;
+  screenInfo->bitmapBitOrder = BITMAP_BIT_ORDER;
+  screenInfo->numPixmapFormats = NumFormats;
+
+  /* initialize screens */
+
+  for (i = 0; i < vfbNumScreens; i++)
+  {
+    if (-1 == AddScreen(vfbScreenInit, argc, argv))
+    {
+      FatalError("Couldn't add screen %d", i);
+    }
+  }
+
+  if (!AddCallback(&ClientStateCallback, vfbClientStateChange, 0)) {
+    FatalError("AddCallback failed\n");
+  }
+
+} /* end InitOutput */
+
+#ifdef DPMSExtension
+extern "C" {
+#if NeedFunctionPrototypes
+  void DPMSSet(CARD16 level)
+#else
+    void DPMSSet(level)
+    CARD16 level;
+#endif
+  {
+    return;
+  }
+
+  Bool DPMSSupported()
+  {
+    return FALSE;
+  }
+}
+#endif
+
+/* this is just to get the server to link on AIX */
+#ifdef AIXV3
+int SelectWaitTime = 10000; /* usec */
+#endif
+
+Bool LegalModifier(unsigned int key, DevicePtr pDev)
+{
+  return TRUE;
+}
+
+void ProcessInputEvents()
+{
+  mieqProcessInputEvents();
+  miPointerUpdate();
+}
+
+/* Fairly standard US PC Keyboard */
+
+#define VFB_MIN_KEY 8
+#define VFB_MAX_KEY 255
+#define VFB_MAP_LEN (VFB_MAX_KEY - VFB_MIN_KEY + 1)
+#define KEYSYMS_PER_KEY 2
+KeySym keyboardMap[VFB_MAP_LEN * KEYSYMS_PER_KEY] = {
+  NoSymbol, NoSymbol,
+  XK_Escape, NoSymbol,
+  XK_1, XK_exclam,
+  XK_2, XK_at,
+  XK_3, XK_numbersign,
+  XK_4, XK_dollar,
+  XK_5, XK_percent,
+  XK_6, XK_asciicircum,
+  XK_7, XK_ampersand,
+  XK_8, XK_asterisk,
+  XK_9, XK_parenleft,
+  XK_0, XK_parenright,
+  XK_minus, XK_underscore,
+  XK_equal, XK_plus,
+  XK_BackSpace, NoSymbol,
+  XK_Tab, NoSymbol,
+  XK_q, XK_Q,
+  XK_w, XK_W,
+  XK_e, XK_E,
+  XK_r, XK_R,
+  XK_t, XK_T,
+  XK_y, XK_Y,
+  XK_u, XK_U,
+  XK_i, XK_I,
+  XK_o, XK_O,
+  XK_p, XK_P,
+  XK_bracketleft, XK_braceleft,
+  XK_bracketright, XK_braceright,
+  XK_Return, NoSymbol,
+  XK_Control_L, NoSymbol,
+  XK_a, XK_A,
+  XK_s, XK_S,
+  XK_d, XK_D,
+  XK_f, XK_F,
+  XK_g, XK_G,
+  XK_h, XK_H,
+  XK_j, XK_J,
+  XK_k, XK_K,
+  XK_l, XK_L,
+  XK_semicolon, XK_colon,
+  XK_apostrophe, XK_quotedbl,
+  XK_grave, XK_asciitilde,
+  XK_Shift_L, NoSymbol,
+  XK_backslash, XK_bar,
+  XK_z, XK_Z,
+  XK_x, XK_X,
+  XK_c, XK_C,
+  XK_v, XK_V,
+  XK_b, XK_B,
+  XK_n, XK_N,
+  XK_m, XK_M,
+  XK_comma, XK_less,
+  XK_period, XK_greater,
+  XK_slash, XK_question,
+  XK_Shift_R, NoSymbol,
+  XK_KP_Multiply, NoSymbol,
+  XK_Alt_L, XK_Meta_L,
+  XK_space, NoSymbol,
+  /*XK_Caps_Lock*/ NoSymbol, NoSymbol,
+  XK_F1, NoSymbol,
+  XK_F2, NoSymbol,
+  XK_F3, NoSymbol,
+  XK_F4, NoSymbol,
+  XK_F5, NoSymbol,
+  XK_F6, NoSymbol,
+  XK_F7, NoSymbol,
+  XK_F8, NoSymbol,
+  XK_F9, NoSymbol,
+  XK_F10, NoSymbol,
+  XK_Num_Lock, XK_Pointer_EnableKeys,
+  XK_Scroll_Lock, NoSymbol,
+  XK_KP_Home, XK_KP_7,
+  XK_KP_Up, XK_KP_8,
+  XK_KP_Prior, XK_KP_9,
+  XK_KP_Subtract, NoSymbol,
+  XK_KP_Left, XK_KP_4,
+  XK_KP_Begin, XK_KP_5,
+  XK_KP_Right, XK_KP_6,
+  XK_KP_Add, NoSymbol,
+  XK_KP_End, XK_KP_1,
+  XK_KP_Down, XK_KP_2,
+  XK_KP_Next, XK_KP_3,
+  XK_KP_Insert, XK_KP_0,
+  XK_KP_Delete, XK_KP_Decimal,
+  NoSymbol, NoSymbol,
+  NoSymbol, NoSymbol,
+  NoSymbol, NoSymbol,
+  XK_F11, NoSymbol,
+  XK_F12, NoSymbol,
+  XK_Home, NoSymbol,
+  XK_Up, NoSymbol,
+  XK_Prior, NoSymbol,
+  XK_Left, NoSymbol,
+  NoSymbol, NoSymbol,
+  XK_Right, NoSymbol,
+  XK_End, NoSymbol,
+  XK_Down, NoSymbol,
+  XK_Next, NoSymbol,
+  XK_Insert, NoSymbol,
+  XK_Delete, NoSymbol,
+  XK_KP_Enter, NoSymbol,
+  XK_Control_R, NoSymbol,
+  XK_Pause, XK_Break,
+  XK_Print, XK_Execute,
+  XK_KP_Divide, NoSymbol,
+  XK_Alt_R, XK_Meta_R,
+};
+
+static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap)
+{
+  int i;
+
+  for (i = 0; i < MAP_LENGTH; i++)
+    pModMap[i] = NoSymbol;
+
+  for (i = 0; i < VFB_MAP_LEN; i++) {
+    if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Caps_Lock)
+      pModMap[i + VFB_MIN_KEY] = LockMask;
+    else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_L ||
+             keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_R)
+      pModMap[i + VFB_MIN_KEY] = ShiftMask;
+    else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_L ||
+             keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_R) {
+      pModMap[i + VFB_MIN_KEY] = ControlMask;
+    }
+    else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_L ||
+             keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_R)
+      pModMap[i + VFB_MIN_KEY] = Mod1Mask;
+  }
+
+  pKeySyms->minKeyCode = VFB_MIN_KEY;
+  pKeySyms->maxKeyCode = VFB_MAX_KEY;
+  pKeySyms->mapWidth = KEYSYMS_PER_KEY;
+  pKeySyms->map = keyboardMap;
+
+  return TRUE;
+}
+
+static void vfbBell(int percent, DeviceIntPtr device, pointer ctrl, int class_)
+{
+  if (percent > 0)
+    vncBell();
+}
+
+static int vfbKeybdProc(DeviceIntPtr pDevice, int onoff)
+{
+  KeySymsRec		keySyms;
+  CARD8 		modMap[MAP_LENGTH];
+  DevicePtr pDev = (DevicePtr)pDevice;
+
+  switch (onoff)
+  {
+  case DEVICE_INIT: 
+    GetMappings(&keySyms, modMap);
+    InitKeyboardDeviceStruct(pDev, &keySyms, modMap,
+                             (BellProcPtr)vfbBell, (KbdCtrlProcPtr)NoopDDA);
+    break;
+  case DEVICE_ON: 
+    pDev->on = TRUE;
+    break;
+  case DEVICE_OFF: 
+    pDev->on = FALSE;
+    break;
+  case DEVICE_CLOSE:
+    break;
+  }
+  return Success;
+}
+
+static int vfbMouseProc(DeviceIntPtr pDevice, int onoff)
+{
+  BYTE map[6];
+  DevicePtr pDev = (DevicePtr)pDevice;
+
+  switch (onoff)
+  {
+  case DEVICE_INIT:
+    map[1] = 1;
+    map[2] = 2;
+    map[3] = 3;
+    map[4] = 4;
+    map[5] = 5;
+    InitPointerDeviceStruct(pDev, map, 5, miPointerGetMotionEvents,
+                            (PtrCtrlProcPtr)NoopDDA, miPointerGetMotionBufferSize());
+    break;
+
+  case DEVICE_ON:
+    pDev->on = TRUE;
+    break;
+
+  case DEVICE_OFF:
+    pDev->on = FALSE;
+    break;
+
+  case DEVICE_CLOSE:
+    break;
+  }
+  return Success;
+}
+
+// InitInput is called after InitExtensions, so we're guaranteed that
+// vncExtensionInit() has already been called.
+
+void InitInput(int argc, char *argv[])
+{
+  DeviceIntPtr p, k;
+  p = AddInputDevice(vfbMouseProc, TRUE);
+  k = AddInputDevice(vfbKeybdProc, TRUE);
+  RegisterPointerDevice(p);
+  RegisterKeyboardDevice(k);
+  miRegisterPointerDevice(screenInfo.screens[0], p);
+  (void)mieqInit ((DevicePtr)k, (DevicePtr)p);
+}
diff --git a/xc/programs/Xserver/vnc/module/Imakefile b/xc/programs/Xserver/vnc/module/Imakefile
new file mode 100644
index 0000000..46e548f
--- /dev/null
+++ b/xc/programs/Xserver/vnc/module/Imakefile
@@ -0,0 +1,54 @@
+
+       VNCTOP = $(TOP)/..
+      VNCLIBS = VncExtLibs
+   VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#define IHaveModules
+#include <Server.tmpl>
+
+    SRCS = vncExtInit.cc vncHooks.cc xf86vncModule.cc XserverDesktop.cc
+    OBJS = vncExtInit.o vncHooks.o xf86vncModule.o XserverDesktop.o
+INCLUDES = -I.. -I../../include -I$(EXTINCSRC) -I$(XINCLUDESRC) \
+           -I$(FONTINCSRC) -I$(XF86COMSRC) \
+           $(VNCINCLUDE)
+ DEFINES = $(STD_DEFINES) -DGC_HAS_COMPOSITE_CLIP -DXFree86LOADER
+
+LinkSourceFile(vncExtInit.cc,..)
+LinkSourceFile(vncHooks.cc,..)
+LinkSourceFile(xf86vncModule.cc,..)
+LinkSourceFile(XserverDesktop.cc,..)
+
+ModuleObjectRule()
+/*
+ LibraryModuleTarget(vnc,$(OBJS) $(VNCLIBS))
+ InstallLibraryModule(vnc,$(MODULEDIR),extensions)
+*/
+
+
+/*
+ * CplusplusDynamicModuleTarget - build a module to be dynamically loaded
+ */
+#ifndef CplusplusDynamicModuleTarget
+#define CplusplusDynamicModuleTarget(module,modlist)			@@\
+AllTarget(module)							@@\
+									@@\
+module: modlist								@@\
+	RemoveFile($@)							@@\
+	$(CXX) -o $@ $(SHLIBLDFLAGS) modlist				@@\
+									@@\
+clean::									@@\
+	RemoveFile(module)
+#endif /* CplusplusDynamicModuleTarget */
+
+
+
+CplusplusDynamicModuleTarget(vnc.so,$(OBJS) $(VNCLIBS))
+InstallDynamicModule(vnc.so,$(MODULEDIR),extensions)
+
+DependTarget()
+
+/*
+ InstallDriverSDKLibraryModule(vnc,$(DRIVERSDKMODULEDIR),extensions)
+*/
diff --git a/xc/programs/Xserver/vnc/vncExtInit.cc b/xc/programs/Xserver/vnc/vncExtInit.cc
new file mode 100644
index 0000000..ccaf5b8
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncExtInit.cc
@@ -0,0 +1,714 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  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; either 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.
+ */
+
+#include <stdio.h>
+
+extern "C" {
+#define class c_class
+#define NEED_EVENTS
+#include "X.h"
+#include "Xproto.h"
+#include "misc.h"
+#include "os.h"
+#include "dixstruct.h"
+#include "extnsionst.h"
+#include "scrnintstr.h"
+#include "selection.h"
+#define _VNCEXT_SERVER_
+#define _VNCEXT_PROTO_
+#include "vncExt.h"
+#undef class
+#undef xalloc
+}
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rdr/HexOutStream.h>
+#include <rfb/LogWriter.h>
+#undef max
+#undef min
+#include <network/TcpSocket.h>
+
+#include "XserverDesktop.h"
+#include "vncHooks.h"
+#include "vncExtInit.h"
+
+extern "C" {
+
+  extern void vncExtensionInit();
+  static void vncResetProc(ExtensionEntry* extEntry);
+  static void vncBlockHandler(pointer data, OSTimePtr t, pointer readmask);
+  static void vncWakeupHandler(pointer data, int nfds, pointer readmask);
+  static void vncClientStateChange(CallbackListPtr*, pointer, pointer);
+  static void SendSelectionChangeEvent(Atom selection);
+  static int ProcVncExtDispatch(ClientPtr client);
+  static int SProcVncExtDispatch(ClientPtr client);
+
+  extern char *display;
+
+  extern Selection *CurrentSelections;
+  extern int NumCurrentSelections;
+}
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("vncext");
+
+static unsigned long vncExtGeneration = 0;
+static bool initialised = false;
+static XserverDesktop* desktop[MAXSCREENS] = { 0, };
+void* vncFbptr[MAXSCREENS] = { 0, };
+
+static char* clientCutText = 0;
+static int clientCutTextLen = 0;
+
+static struct VncInputSelect* vncInputSelectHead = 0;
+struct VncInputSelect {
+  VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m)
+  {
+    next = vncInputSelectHead;
+    vncInputSelectHead = this;
+  }
+  ClientPtr client;
+  Window window;
+  int mask;
+  VncInputSelect* next;
+};
+
+static int nPrevSelections = 0;
+static TimeStamp* prevSelectionTimes = 0;
+
+static int vncErrorBase = 0;
+static int vncEventBase = 0;
+static char* vncPasswdFile = 0;
+int vncInetdSock = -1;
+
+rfb::VncAuthPasswdFileParameter vncAuthPasswdFile;
+rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile",
+                            &vncAuthPasswdFile.param);
+rfb::StringParameter httpDir("httpd",
+                             "Directory containing files to serve via HTTP",
+                             "");
+rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0);
+rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
+                            &rfb::Server::clientWaitTimeMillis);
+rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
+rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
+rfb::BoolParameter localhostOnly("localhost",
+                                 "Only allow connections from localhost",
+                                 false);
+
+void vncExtensionInit()
+{
+  if (vncExtGeneration == serverGeneration) {
+    vlog.error("vncExtensionInit: called twice in same generation?");
+    return;
+  }
+  vncExtGeneration = serverGeneration;
+
+  ExtensionEntry* extEntry
+    = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors,
+                   ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc,
+                   StandardMinorOpcode);
+  if (!extEntry) {
+    ErrorF("vncExtInit: AddExtension failed\n");
+    return;
+  }
+
+  vncErrorBase = extEntry->errorBase;
+  vncEventBase = extEntry->eventBase;
+
+  vlog.info("VNC extension running!");
+
+  if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) {
+    FatalError("AddCallback failed\n");
+  }
+
+  try {
+    if (!initialised) {
+      rfb::initStdIOLoggers();
+      initialised = true;
+    }
+
+    for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+
+      if (!desktop[scr]) {
+        network::TcpListener* listener = 0;
+        network::TcpListener* httpListener = 0;
+        if (scr == 0 && vncInetdSock != -1) {
+          if (network::TcpSocket::isSocket(vncInetdSock) &&
+              !network::TcpSocket::isConnected(vncInetdSock))
+          {
+            listener = new network::TcpListener(0, 0, vncInetdSock, true);
+            vlog.info("inetd wait");
+          }
+        } else {
+          int port = rfbport;
+          if (port == 0) port = 5900 + atoi(display);
+          port += 1000 * scr;
+          listener = new network::TcpListener(port, localhostOnly);
+          vlog.info("Listening for VNC connections on port %d",port);
+          CharArray httpDirStr(httpDir.getData());
+          if (httpDirStr.buf[0]) {
+            port = httpPort;
+            if (port == 0) port = 5800 + atoi(display);
+            port += 1000 * scr;
+            httpListener = new network::TcpListener(port, localhostOnly);
+            vlog.info("Listening for HTTP connections on port %d",port);
+          }
+        }
+
+        CharArray desktopNameStr(desktopName.getData());
+        desktop[scr] = new XserverDesktop(screenInfo.screens[scr], listener,
+                                          httpListener,
+                                          desktopNameStr.buf,
+                                          vncFbptr[scr]);
+        vlog.info("created VNC server for screen %d", scr);
+
+        if (scr == 0 && vncInetdSock != -1 && !listener) {
+          network::Socket* sock = new network::TcpSocket(vncInetdSock);
+          desktop[scr]->addClient(sock, false);
+          vlog.info("added inetd sock");
+        }
+
+      } else {
+        desktop[scr]->serverReset(screenInfo.screens[scr]);
+      }
+
+      vncHooksInit(screenInfo.screens[scr], desktop[scr]);
+    }
+
+    RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0);
+
+  } catch (rdr::Exception& e) {
+    vlog.error("vncExtInit: %s",e.str());
+  }
+}
+
+static void vncResetProc(ExtensionEntry* extEntry)
+{
+}
+
+//
+// vncBlockHandler - called just before the X server goes into select().  Call
+// on to the block handler for each desktop.  Then check whether any of the
+// selections have changed, and if so, notify any interested X clients.
+//
+
+static void vncBlockHandler(pointer data, OSTimePtr timeout, pointer readmask)
+{
+  fd_set* fds = (fd_set*)readmask;
+
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    if (desktop[scr]) {
+      desktop[scr]->blockHandler(fds);
+    }
+  }
+
+  if (nPrevSelections != NumCurrentSelections) {
+    prevSelectionTimes
+      = (TimeStamp*)xnfrealloc(prevSelectionTimes,
+                               NumCurrentSelections * sizeof(TimeStamp));
+    for (int i = nPrevSelections; i < NumCurrentSelections; i++) {
+      prevSelectionTimes[i].months = 0;
+      prevSelectionTimes[i].milliseconds = 0;
+    }
+    nPrevSelections = NumCurrentSelections;
+  }
+  for (int i = 0; i < NumCurrentSelections; i++) {
+    if (CurrentSelections[i].lastTimeChanged.months
+        != prevSelectionTimes[i].months ||
+        CurrentSelections[i].lastTimeChanged.milliseconds
+        != prevSelectionTimes[i].milliseconds)
+    {
+      SendSelectionChangeEvent(CurrentSelections[i].selection);
+      prevSelectionTimes[i] = CurrentSelections[i].lastTimeChanged;
+    }
+  }
+}
+
+static void vncWakeupHandler(pointer data, int nfds, pointer readmask)
+{
+  fd_set* fds = (fd_set*)readmask;
+
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    if (desktop[scr]) {
+      desktop[scr]->wakeupHandler(fds, nfds);
+    }
+  }
+}
+
+static void vncClientStateChange(CallbackListPtr*, pointer, pointer p)
+{
+  ClientPtr client = ((NewClientInfoRec*)p)->client;
+  if (client->clientState == ClientStateGone) {
+    VncInputSelect** nextPtr = &vncInputSelectHead;
+    for (VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) {
+      if (cur->client == client) {
+        *nextPtr = cur->next;
+        delete cur;
+        continue;
+      }
+      nextPtr = &cur->next;
+    }
+  }
+}
+
+void vncBell()
+{
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    if (desktop[scr]) {
+      desktop[scr]->bell();
+    }
+  }
+}
+
+void vncClientGone(int fd)
+{
+  if (fd == vncInetdSock) {
+    fprintf(stderr,"inetdSock client gone\n");
+    GiveUp(0);
+  }
+}
+
+void vncClientCutText(const char* str, int len)
+{
+  delete [] clientCutText;
+  clientCutText = new char[len];
+  memcpy(clientCutText, str, len);
+  clientCutTextLen = len;
+  xVncExtClientCutTextNotifyEvent ev;
+  ev.type = vncEventBase + VncExtClientCutTextNotify;
+  for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
+    if (cur->mask & VncExtClientCutTextMask) {
+      ev.sequenceNumber = cur->client->sequence;
+      ev.window = cur->window;
+      ev.time = GetTimeInMillis();
+      if (cur->client->swapped) {
+        int n;
+        swaps(&ev.sequenceNumber, n);
+        swapl(&ev.window, n);
+        swapl(&ev.time, n);
+      }
+      WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent),
+                    (char *)&ev);
+    }
+  }
+}
+
+static void SendSelectionChangeEvent(Atom selection)
+{
+  xVncExtSelectionChangeNotifyEvent ev;
+  ev.type = vncEventBase + VncExtSelectionChangeNotify;
+  for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
+    if (cur->mask & VncExtSelectionChangeMask) {
+      ev.sequenceNumber = cur->client->sequence;
+      ev.window = cur->window;
+      ev.selection = selection;
+      if (cur->client->swapped) {
+        int n;
+        swaps(&ev.sequenceNumber, n);
+        swapl(&ev.window, n);
+        swapl(&ev.selection, n);
+      }
+      WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent),
+                    (char *)&ev);
+    }
+  }
+}
+
+static int ProcVncExtSetParam(ClientPtr client)
+{
+  REQUEST(xVncExtSetParamReq);
+  REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen);
+  CharArray param(stuff->paramLen+1);
+  strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+  param.buf[stuff->paramLen] = 0;
+
+  xVncExtSetParamReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.length = 0;
+  rep.sequenceNumber = client->sequence;
+  rep.success = rfb::Configuration::setParam(param.buf);
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+  }
+  WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep);
+  return (client->noClientException);
+}
+
+static int SProcVncExtSetParam(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtSetParamReq);
+  swaps(&stuff->length, n);
+  REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq);
+  return ProcVncExtSetParam(client);
+}
+
+static int ProcVncExtGetParam(ClientPtr client)
+{
+  REQUEST(xVncExtGetParamReq);
+  REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen);
+  CharArray param(stuff->paramLen+1);
+  strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+  param.buf[stuff->paramLen] = 0;
+
+  xVncExtGetParamReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.sequenceNumber = client->sequence;
+  rep.success = 0;
+  int len = 0;
+  char* value = 0;
+  rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
+  // Hack to avoid exposing password!
+  if (strcasecmp(param.buf, "Password") == 0)
+    p = 0;
+  if (p) {
+    value = p->getValueStr();
+    rep.success = 1;
+    len = value ? strlen(value) : 0;
+  }
+  rep.length = (len + 3) >> 2;
+  rep.valueLen = len;
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+    swaps(&rep.valueLen, n);
+  }
+  WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep);
+  if (value)
+    WriteToClient(client, len, value);
+  delete [] value;
+  return (client->noClientException);
+}
+
+static int SProcVncExtGetParam(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtGetParamReq);
+  swaps(&stuff->length, n);
+  REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq);
+  return ProcVncExtGetParam(client);
+}
+
+static int ProcVncExtGetParamDesc(ClientPtr client)
+{
+  REQUEST(xVncExtGetParamDescReq);
+  REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen);
+  CharArray param(stuff->paramLen+1);
+  strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+  param.buf[stuff->paramLen] = 0;
+
+  xVncExtGetParamDescReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.sequenceNumber = client->sequence;
+  rep.success = 0;
+  int len = 0;
+  const char* desc = 0;
+  rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
+  if (p) {
+    desc = p->getDescription();
+    rep.success = 1;
+    len = desc ? strlen(desc) : 0;
+  }
+  rep.length = (len + 3) >> 2;
+  rep.descLen = len;
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+    swaps(&rep.descLen, n);
+  }
+  WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep);
+  if (desc)
+    WriteToClient(client, len, (char*)desc);
+  return (client->noClientException);
+}
+
+static int SProcVncExtGetParamDesc(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtGetParamDescReq);
+  swaps(&stuff->length, n);
+  REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq);
+  return ProcVncExtGetParamDesc(client);
+}
+
+static int ProcVncExtListParams(ClientPtr client)
+{
+  REQUEST(xVncExtListParamsReq);
+  REQUEST_SIZE_MATCH(xVncExtListParamsReq);
+
+  xVncExtListParamsReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.sequenceNumber = client->sequence;
+
+  int nParams = 0;
+  int len = 0;
+  rfb::VoidParameter* current = rfb::Configuration::head;
+  while (current) {
+    int l = strlen(current->getName());
+    if (l <= 255) {
+      nParams++;
+      len += l + 1;
+    }
+    current = current->_next;
+  }
+  rep.length = (len + 3) >> 2;
+  rep.nParams = nParams;
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+    swaps(&rep.nParams, n);
+  }
+  WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep);
+  rdr::U8* data = new rdr::U8[len];
+  rdr::U8* ptr = data;
+  current = rfb::Configuration::head;
+  while (current) {
+    int l = strlen(current->getName());
+    if (l <= 255) {
+      *ptr++ = l;
+      memcpy(ptr, current->getName(), l);
+      ptr += l;
+    }
+    current = current->_next;
+  }
+  WriteToClient(client, len, (char*)data);
+  delete [] data;
+  return (client->noClientException);
+}
+
+static int SProcVncExtListParams(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtListParamsReq);
+  swaps(&stuff->length, n);
+  REQUEST_SIZE_MATCH(xVncExtListParamsReq);
+  return ProcVncExtListParams(client);
+}
+
+static int ProcVncExtSetServerCutText(ClientPtr client)
+{
+  REQUEST(xVncExtSetServerCutTextReq);
+  REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen);
+  char* str = new char[stuff->textLen+1];
+  strncpy(str, (char*)&stuff[1], stuff->textLen);
+  str[stuff->textLen] = 0;
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    if (desktop[scr]) {
+      desktop[scr]->serverCutText(str, stuff->textLen);
+    }
+  }
+  delete [] str;
+  return (client->noClientException);
+}
+
+static int SProcVncExtSetServerCutText(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtSetServerCutTextReq);
+  swaps(&stuff->length, n);
+  REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq);
+  swapl(&stuff->textLen, n);
+  return ProcVncExtSetServerCutText(client);
+}
+
+static int ProcVncExtGetClientCutText(ClientPtr client)
+{
+  REQUEST(xVncExtGetClientCutTextReq);
+  REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
+
+  xVncExtGetClientCutTextReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.length = (clientCutTextLen + 3) >> 2;
+  rep.sequenceNumber = client->sequence;
+  rep.textLen = clientCutTextLen;
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+    swapl(&rep.textLen, n);
+  }
+  WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep);
+  if (clientCutText)
+    WriteToClient(client, clientCutTextLen, clientCutText);
+  return (client->noClientException);
+}
+
+static int SProcVncExtGetClientCutText(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtGetClientCutTextReq);
+  swaps(&stuff->length, n);
+  REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
+  return ProcVncExtGetClientCutText(client);
+}
+
+static int ProcVncExtSelectInput(ClientPtr client)
+{
+  REQUEST(xVncExtSelectInputReq);
+  REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
+  VncInputSelect** nextPtr = &vncInputSelectHead;
+  VncInputSelect* cur;
+  for (cur = vncInputSelectHead; cur; cur = *nextPtr) {
+    if (cur->client == client && cur->window == stuff->window) {
+      cur->mask = stuff->mask;
+      if (!cur->mask) {
+        *nextPtr = cur->next;
+        delete cur;
+      }
+      break;
+    }
+    nextPtr = &cur->next;
+  }
+  if (!cur) {
+    cur = new VncInputSelect(client, stuff->window, stuff->mask);
+  }
+  return (client->noClientException);
+}
+
+static int SProcVncExtSelectInput(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtSelectInputReq);
+  swaps(&stuff->length, n);
+  REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
+  swapl(&stuff->window, n);
+  swapl(&stuff->mask, n);
+  return ProcVncExtSelectInput(client);
+}
+
+static int ProcVncExtConnect(ClientPtr client)
+{
+  REQUEST(xVncExtConnectReq);
+  REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen);
+  CharArray str(stuff->strLen+1);
+  strncpy(str.buf, (char*)&stuff[1], stuff->strLen);
+  str.buf[stuff->strLen] = 0;
+
+  xVncExtConnectReply rep;
+  rep.success = 0;
+  if (desktop[0]) {
+    if (stuff->strLen == 0) {
+      try {
+        desktop[0]->disconnectClients();
+        rep.success = 1;
+      } catch (rdr::Exception& e) {
+        vlog.error("Disconnecting all clients: %s",e.str());
+      }
+    } else {
+      int port = 5500;
+      for (int i = 0; i < stuff->strLen; i++) {
+        if (str.buf[i] == ':') {
+          port = atoi(&str.buf[i+1]);
+          str.buf[i] = 0;
+          break;
+        }
+      }
+
+      try {
+        network::Socket* sock = new network::TcpSocket(str.buf, port);
+        desktop[0]->addClient(sock, true);
+	rep.success = 1;
+      } catch (rdr::Exception& e) {
+        vlog.error("Reverse connection: %s",e.str());
+      }
+    }
+  }
+
+  rep.type = X_Reply;
+  rep.length = 0;
+  rep.sequenceNumber = client->sequence;
+  if (client->swapped) {
+    int n;
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.length, n);
+  }
+  WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep);
+  return (client->noClientException);
+}
+
+static int SProcVncExtConnect(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtConnectReq);
+  swaps(&stuff->length, n);
+  REQUEST_AT_LEAST_SIZE(xVncExtConnectReq);
+  return ProcVncExtConnect(client);
+}
+
+static int ProcVncExtDispatch(ClientPtr client)
+{
+  REQUEST(xReq);
+  switch (stuff->data) {
+  case X_VncExtSetParam:
+    return ProcVncExtSetParam(client);
+  case X_VncExtGetParam:
+    return ProcVncExtGetParam(client);
+  case X_VncExtGetParamDesc:
+    return ProcVncExtGetParamDesc(client);
+  case X_VncExtListParams:
+    return ProcVncExtListParams(client);
+  case X_VncExtSetServerCutText:
+    return ProcVncExtSetServerCutText(client);
+  case X_VncExtGetClientCutText:
+    return ProcVncExtGetClientCutText(client);
+  case X_VncExtSelectInput:
+    return ProcVncExtSelectInput(client);
+  case X_VncExtConnect:
+    return ProcVncExtConnect(client);
+  default:
+    return BadRequest;
+  }
+}
+
+static int SProcVncExtDispatch(ClientPtr client)
+{
+  REQUEST(xReq);
+  switch (stuff->data) {
+  case X_VncExtSetParam:
+    return SProcVncExtSetParam(client);
+  case X_VncExtGetParam:
+    return SProcVncExtGetParam(client);
+  case X_VncExtGetParamDesc:
+    return SProcVncExtGetParamDesc(client);
+  case X_VncExtListParams:
+    return SProcVncExtListParams(client);
+  case X_VncExtSetServerCutText:
+    return SProcVncExtSetServerCutText(client);
+  case X_VncExtGetClientCutText:
+    return SProcVncExtGetClientCutText(client);
+  case X_VncExtSelectInput:
+    return SProcVncExtSelectInput(client);
+  case X_VncExtConnect:
+    return SProcVncExtConnect(client);
+  default:
+    return BadRequest;
+  }
+}
+
diff --git a/xc/programs/Xserver/vnc/vncExtInit.h b/xc/programs/Xserver/vnc/vncExtInit.h
new file mode 100644
index 0000000..947f34d
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncExtInit.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __VNCEXTINIT_H__
+#define __VNCEXTINIT_H__
+
+#include <rfb/Configuration.h>
+
+extern void vncClientCutText(const char* str, int len);
+extern void vncClientGone(int fd);
+extern void vncBell();
+extern void* vncFbptr[];
+extern int vncInetdSock;
+extern rfb::StringParameter httpDir;
+
+#endif
diff --git a/xc/programs/Xserver/vnc/vncHooks.cc b/xc/programs/Xserver/vnc/vncHooks.cc
new file mode 100644
index 0000000..d52a497
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncHooks.cc
@@ -0,0 +1,1475 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+
+#include <stdio.h>
+#include "XserverDesktop.h"
+#include "vncHooks.h"
+
+extern "C" {
+#define class c_class
+#define private c_private
+#include "scrnintstr.h"
+#include "windowstr.h"
+#include "gcstruct.h"
+#include "regionstr.h"
+#include "dixfontstr.h"
+#include "colormapst.h"
+
+#ifdef GC_HAS_COMPOSITE_CLIP
+#define COMPOSITE_CLIP(gc) ((gc)->pCompositeClip)
+#else
+#include "mfb.h"
+#define COMPOSITE_CLIP(gc) \
+  (((mfbPrivGCPtr)((gc)->devPrivates[mfbGCPrivateIndex].ptr))->pCompositeClip)
+#endif
+
+#undef class
+#undef private
+}
+
+#include "RegionHelper.h"
+
+#define DBGPRINT(x) //(fprintf x)
+
+// MAX_RECTS_PER_OP is the maximum number of rectangles we generate from
+// operations like Polylines and PolySegment.  If the operation is more complex
+// than this, we simply use the bounding box.  Ideally it would be a
+// command-line option, but that would involve an extra malloc each time, so we
+// fix it here.
+#define MAX_RECTS_PER_OP 5
+
+static unsigned long vncHooksGeneration = 0;
+
+// vncHooksScreenRec and vncHooksGCRec contain pointers to the original
+// functions which we "wrap" in order to hook the screen changes.  The screen
+// functions are each wrapped individually, while the GC "funcs" and "ops" are
+// wrapped as a unit.
+
+typedef struct {
+  XserverDesktop* desktop;
+
+  CloseScreenProcPtr           CloseScreen;
+  CreateGCProcPtr              CreateGC;
+  PaintWindowBackgroundProcPtr PaintWindowBackground;
+  PaintWindowBorderProcPtr     PaintWindowBorder;
+  CopyWindowProcPtr            CopyWindow;
+  ClearToBackgroundProcPtr     ClearToBackground;
+  RestoreAreasProcPtr          RestoreAreas;
+  InstallColormapProcPtr       InstallColormap;
+  StoreColorsProcPtr           StoreColors;
+  DisplayCursorProcPtr         DisplayCursor;
+  ScreenBlockHandlerProcPtr    BlockHandler;
+} vncHooksScreenRec, *vncHooksScreenPtr;
+
+typedef struct {
+    GCFuncs *wrappedFuncs;
+    GCOps *wrappedOps;
+} vncHooksGCRec, *vncHooksGCPtr;
+
+static int vncHooksScreenIndex;
+static int vncHooksGCIndex;
+
+
+// screen functions
+
+static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen);
+static Bool vncHooksCreateGC(GCPtr pGC);
+static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion,
+                                          int what);
+static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion,
+                                      int what);
+static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg,
+                               RegionPtr pOldRegion);
+static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w,
+                                      int h, Bool generateExposures);
+static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr prgnExposed);
+static void vncHooksInstallColormap(ColormapPtr pColormap);
+static void vncHooksStoreColors(ColormapPtr pColormap, int ndef,
+                                xColorItem* pdef);
+static Bool vncHooksDisplayCursor(ScreenPtr pScreen, CursorPtr cursor);
+static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout,
+                                 pointer pReadmask);
+
+// GC "funcs"
+
+static void vncHooksValidateGC(GCPtr pGC, unsigned long changes,
+                               DrawablePtr pDrawable);
+static void vncHooksChangeGC(GCPtr pGC, unsigned long mask);
+static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst);
+static void vncHooksDestroyGC(GCPtr pGC);
+static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue,int nrects);
+static void vncHooksDestroyClip(GCPtr pGC);
+static void vncHooksCopyClip(GCPtr dst, GCPtr src);
+
+static GCFuncs vncHooksGCFuncs = {
+  vncHooksValidateGC, vncHooksChangeGC, vncHooksCopyGC, vncHooksDestroyGC,
+  vncHooksChangeClip, vncHooksDestroyClip, vncHooksCopyClip,
+};
+
+// GC "ops"
+
+static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit,
+                              DDXPointPtr pptInit, int *pwidthInit,
+                              int fSorted);
+static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+                             DDXPointPtr ppt, int *pwidth, int nspans,
+                             int fSorted);
+static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+                             int x, int y, int w, int h, int leftPad,
+                             int format, char *pBits);
+static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+                                  GCPtr pGC, int srcx, int srcy, int w, int h,
+                                  int dstx, int dsty);
+static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+                                   GCPtr pGC, int srcx, int srcy, int w, int h,
+                                   int dstx, int dsty, unsigned long plane);
+static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+                              int npt, xPoint *pts);
+static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+                              int npt, DDXPointPtr ppts);
+static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg,
+                                xSegment *segs);
+static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+                                  xRectangle *rects);
+static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+                            xArc *arcs);
+static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape,
+                                int mode, int count, DDXPointPtr pts);
+static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+                                 xRectangle *rects);
+static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+                                xArc *arcs);
+static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                             int count, char *chars);
+static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                              int count, unsigned short *chars);
+static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                               int count, char *chars);
+static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                                int count, unsigned short *chars);
+static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+                                  int y, unsigned int nglyph,
+                                  CharInfoPtr *ppci, pointer pglyphBase);
+static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+                                 int y, unsigned int nglyph,
+                                 CharInfoPtr *ppci, pointer pglyphBase);
+static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap,
+                               DrawablePtr pDrawable, int w, int h, int x,
+                               int y);
+
+static GCOps vncHooksGCOps = {
+  vncHooksFillSpans, vncHooksSetSpans, vncHooksPutImage, vncHooksCopyArea,
+  vncHooksCopyPlane, vncHooksPolyPoint, vncHooksPolylines, vncHooksPolySegment,
+  vncHooksPolyRectangle, vncHooksPolyArc, vncHooksFillPolygon,
+  vncHooksPolyFillRect, vncHooksPolyFillArc, vncHooksPolyText8,
+  vncHooksPolyText16, vncHooksImageText8, vncHooksImageText16,
+  vncHooksImageGlyphBlt, vncHooksPolyGlyphBlt, vncHooksPushPixels
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// vncHooksInit() is called at initialisation time and every time the server
+// resets.  It is called once for each screen, but the indexes are only
+// allocated once for each server generation.
+
+Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop)
+{
+  vncHooksScreenPtr vncHooksScreen;
+
+  if (vncHooksGeneration != serverGeneration) {
+    vncHooksGeneration = serverGeneration;
+
+    vncHooksScreenIndex = AllocateScreenPrivateIndex();
+    if (vncHooksScreenIndex < 0) {
+      ErrorF("vncHooksInit: AllocateScreenPrivateIndex failed\n");
+      return FALSE;
+    }
+
+    vncHooksGCIndex = AllocateGCPrivateIndex();
+    if (vncHooksGCIndex < 0) {
+      ErrorF("vncHooksInit: AllocateGCPrivateIndex failed\n");
+      return FALSE;
+    }
+  }
+
+  if (!AllocateGCPrivate(pScreen, vncHooksGCIndex, sizeof(vncHooksGCRec))) {
+    ErrorF("vncHooksInit: AllocateGCPrivate failed\n");
+    return FALSE;
+  }
+
+  vncHooksScreen = (vncHooksScreenPtr)xnfalloc(sizeof(vncHooksScreenRec));
+  pScreen->devPrivates[vncHooksScreenIndex].ptr = (pointer)vncHooksScreen;
+
+  vncHooksScreen->desktop = desktop;
+
+  vncHooksScreen->CloseScreen = pScreen->CloseScreen;
+  vncHooksScreen->CreateGC = pScreen->CreateGC;
+  vncHooksScreen->PaintWindowBackground = pScreen->PaintWindowBackground;
+  vncHooksScreen->PaintWindowBorder = pScreen->PaintWindowBorder;
+  vncHooksScreen->CopyWindow = pScreen->CopyWindow;
+  vncHooksScreen->ClearToBackground = pScreen->ClearToBackground;
+  vncHooksScreen->RestoreAreas = pScreen->RestoreAreas;
+  vncHooksScreen->InstallColormap = pScreen->InstallColormap;
+  vncHooksScreen->StoreColors = pScreen->StoreColors;
+  vncHooksScreen->DisplayCursor = pScreen->DisplayCursor;
+  vncHooksScreen->BlockHandler = pScreen->BlockHandler;
+
+  pScreen->CloseScreen = vncHooksCloseScreen;
+  pScreen->CreateGC = vncHooksCreateGC;
+  pScreen->PaintWindowBackground = vncHooksPaintWindowBackground;
+  pScreen->PaintWindowBorder = vncHooksPaintWindowBorder;
+  pScreen->CopyWindow = vncHooksCopyWindow;
+  pScreen->ClearToBackground = vncHooksClearToBackground;
+  pScreen->RestoreAreas = vncHooksRestoreAreas;
+  pScreen->InstallColormap = vncHooksInstallColormap;
+  pScreen->StoreColors = vncHooksStoreColors;
+  pScreen->DisplayCursor = vncHooksDisplayCursor;
+  pScreen->BlockHandler = vncHooksBlockHandler;
+
+  return TRUE;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// screen functions
+//
+
+// SCREEN_UNWRAP and SCREEN_REWRAP unwrap and rewrap the given screen function.
+// It would be nice to do this with a C++ class, but each function is of a
+// distinct type, so it would have to use templates, and it's not worth that
+// much pain.
+
+#define SCREEN_UNWRAP(scrn,field)                                         \
+  ScreenPtr pScreen = scrn;                                               \
+  vncHooksScreenPtr vncHooksScreen                                        \
+    = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \
+  pScreen->field = vncHooksScreen->field;                                 \
+  DBGPRINT((stderr,"vncHooks" #field " called\n"));
+
+#define SCREEN_REWRAP(field) pScreen->field = vncHooks##field;
+
+
+// CloseScreen - unwrap the screen functions and call the original CloseScreen
+// function
+
+static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen_)
+{
+  SCREEN_UNWRAP(pScreen_, CloseScreen);
+
+  pScreen->CreateGC = vncHooksScreen->CreateGC;
+  pScreen->PaintWindowBackground = vncHooksScreen->PaintWindowBackground;
+  pScreen->PaintWindowBorder = vncHooksScreen->PaintWindowBorder;
+  pScreen->CopyWindow = vncHooksScreen->CopyWindow;
+  pScreen->ClearToBackground = vncHooksScreen->ClearToBackground;
+  pScreen->RestoreAreas = vncHooksScreen->RestoreAreas;
+  pScreen->InstallColormap = vncHooksScreen->InstallColormap;
+  pScreen->StoreColors = vncHooksScreen->StoreColors;
+  pScreen->DisplayCursor = vncHooksScreen->DisplayCursor;
+  pScreen->BlockHandler = vncHooksScreen->BlockHandler;
+
+  xfree((pointer)vncHooksScreen);
+
+  DBGPRINT((stderr,"vncHooksCloseScreen: unwrapped screen functions\n"));
+
+  return (*pScreen->CloseScreen)(i, pScreen);
+}
+
+// CreateGC - wrap the "GC funcs"
+
+static Bool vncHooksCreateGC(GCPtr pGC)
+{
+  SCREEN_UNWRAP(pGC->pScreen, CreateGC);
+    
+  vncHooksGCPtr vncHooksGC
+    = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+
+  Bool ret = (*pScreen->CreateGC) (pGC);
+
+  vncHooksGC->wrappedOps = 0;
+  vncHooksGC->wrappedFuncs = pGC->funcs;
+  pGC->funcs = &vncHooksGCFuncs;
+
+  SCREEN_REWRAP(CreateGC);
+
+  return ret;
+}
+
+// PaintWindowBackground - changed region is the given region
+
+static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion,
+                                          int what)
+{
+  SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBackground);
+
+  RegionHelper changed(pScreen, pRegion);
+
+  (*pScreen->PaintWindowBackground) (pWin, pRegion, what);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  SCREEN_REWRAP(PaintWindowBackground);
+}
+
+// PaintWindowBorder - changed region is the given region
+
+static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion,
+                                      int what)
+{
+  SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBorder);
+
+  RegionHelper changed(pScreen, pRegion);
+
+  (*pScreen->PaintWindowBorder) (pWin, pRegion, what);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  SCREEN_REWRAP(PaintWindowBorder);
+}
+
+// CopyWindow - destination of the copy is the old region, clipped by
+// borderClip, translated by the delta.  This call only does the copy - it
+// doesn't affect any other bits.
+
+static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg,
+                               RegionPtr pOldRegion)
+{
+  SCREEN_UNWRAP(pWin->drawable.pScreen, CopyWindow);
+
+  RegionHelper copied(pScreen, pOldRegion);
+  int dx = pWin->drawable.x - ptOldOrg.x;
+  int dy = pWin->drawable.y - ptOldOrg.y;
+  REGION_TRANSLATE(pScreen, copied.reg, dx, dy);
+  REGION_INTERSECT(pWin->drawable.pScreen, copied.reg, copied.reg,
+                   &pWin->borderClip);
+
+  (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion);
+
+  vncHooksScreen->desktop->add_copied(copied.reg, dx, dy);
+
+  SCREEN_REWRAP(CopyWindow);
+}
+
+// ClearToBackground - changed region is the given rectangle, clipped by
+// clipList, but only if generateExposures is false.
+
+static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w,
+                                      int h, Bool generateExposures)
+{
+  SCREEN_UNWRAP(pWin->drawable.pScreen, ClearToBackground);
+
+  BoxRec box;
+  box.x1 = x + pWin->drawable.x;
+  box.y1 = y + pWin->drawable.y;
+  box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width);
+  box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, &pWin->clipList);
+
+  (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures);
+
+  if (!generateExposures) {
+    vncHooksScreen->desktop->add_changed(changed.reg);
+  }
+
+  SCREEN_REWRAP(ClearToBackground);
+}
+
+// RestoreAreas - changed region is the given region
+
+static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr pRegion)
+{
+  SCREEN_UNWRAP(pWin->drawable.pScreen, RestoreAreas);
+
+  RegionHelper changed(pScreen, pRegion);
+
+  RegionPtr result = (*pScreen->RestoreAreas) (pWin, pRegion);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  SCREEN_REWRAP(RestoreAreas);
+
+  return result;
+}
+
+// InstallColormap - get the new colormap
+
+static void vncHooksInstallColormap(ColormapPtr pColormap)
+{
+  SCREEN_UNWRAP(pColormap->pScreen, InstallColormap);
+
+  (*pScreen->InstallColormap) (pColormap);
+
+  vncHooksScreen->desktop->setColormap(pColormap);
+
+  SCREEN_REWRAP(InstallColormap);
+}
+
+// StoreColors - get the colormap changes
+
+static void vncHooksStoreColors(ColormapPtr pColormap, int ndef,
+                                xColorItem* pdef)
+{
+  SCREEN_UNWRAP(pColormap->pScreen, StoreColors);
+
+  (*pScreen->StoreColors) (pColormap, ndef, pdef);
+
+  vncHooksScreen->desktop->setColourMapEntries(pColormap, ndef, pdef);
+
+  SCREEN_REWRAP(StoreColors);
+}
+
+// DisplayCursor - get the cursor shape
+
+static Bool vncHooksDisplayCursor(ScreenPtr pScreen_, CursorPtr cursor)
+{
+  SCREEN_UNWRAP(pScreen_, DisplayCursor);
+
+  Bool ret = (*pScreen->DisplayCursor) (pScreen, cursor);
+
+  vncHooksScreen->desktop->setCursor(cursor);
+
+  SCREEN_REWRAP(DisplayCursor);
+
+  return ret;
+}
+
+// BlockHandler - ignore any changes during the block handler - it's likely
+// these are just drawing the cursor.
+
+static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout,
+                                 pointer pReadmask)
+{
+  SCREEN_UNWRAP(screenInfo.screens[i], BlockHandler);
+
+  vncHooksScreen->desktop->ignoreHooks(true);
+
+  (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
+
+  vncHooksScreen->desktop->ignoreHooks(false);
+
+  SCREEN_REWRAP(BlockHandler);
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// GC "funcs"
+//
+
+// GCFuncUnwrapper is a helper class which unwraps the GC funcs and ops in its
+// constructor and rewraps them in its destructor.
+
+class GCFuncUnwrapper {
+public:
+  GCFuncUnwrapper(GCPtr pGC_) : pGC(pGC_) {
+    vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+    pGC->funcs = vncHooksGC->wrappedFuncs;
+    if (vncHooksGC->wrappedOps)
+      pGC->ops = vncHooksGC->wrappedOps;
+  }
+  ~GCFuncUnwrapper() {
+    vncHooksGC->wrappedFuncs = pGC->funcs;
+    pGC->funcs = &vncHooksGCFuncs;
+    if (vncHooksGC->wrappedOps) {
+      vncHooksGC->wrappedOps = pGC->ops;
+      pGC->ops = &vncHooksGCOps;
+    }
+  }
+  GCPtr pGC;
+  vncHooksGCPtr vncHooksGC;
+};
+
+
+// ValidateGC - wrap the "ops" if a viewable window
+
+static void vncHooksValidateGC(GCPtr pGC, unsigned long changes,
+                               DrawablePtr pDrawable)
+{
+  GCFuncUnwrapper u(pGC);
+
+  DBGPRINT((stderr,"vncHooksValidateGC called\n"));
+
+  (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable);
+    
+  u.vncHooksGC->wrappedOps = 0;
+  if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable) {
+    WindowPtr pWin = (WindowPtr)pDrawable;
+    RegionPtr pRegion = &pWin->clipList;
+
+    if (pGC->subWindowMode == IncludeInferiors)
+      pRegion = &pWin->borderClip;
+    if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) {
+      u.vncHooksGC->wrappedOps = pGC->ops;
+      DBGPRINT((stderr,"vncHooksValidateGC: wrapped GC ops\n"));
+    }
+  }
+}
+
+// Other GC funcs - just unwrap and call on
+
+static void vncHooksChangeGC(GCPtr pGC, unsigned long mask) {
+  GCFuncUnwrapper u(pGC);
+  (*pGC->funcs->ChangeGC) (pGC, mask);
+}
+static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst) {
+  GCFuncUnwrapper u(dst);
+  (*dst->funcs->CopyGC) (src, mask, dst);
+}
+static void vncHooksDestroyGC(GCPtr pGC) {
+  GCFuncUnwrapper u(pGC);
+  (*pGC->funcs->DestroyGC) (pGC);
+}
+static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue, int nrects)
+{
+  GCFuncUnwrapper u(pGC);
+  (*pGC->funcs->ChangeClip) (pGC, type, pValue, nrects);
+}
+static void vncHooksDestroyClip(GCPtr pGC) {
+  GCFuncUnwrapper u(pGC);
+  (*pGC->funcs->DestroyClip) (pGC);
+}
+static void vncHooksCopyClip(GCPtr dst, GCPtr src) {
+  GCFuncUnwrapper u(dst);
+  (*dst->funcs->CopyClip) (dst, src);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// GC "ops"
+//
+
+// GCOpUnwrapper is a helper class which unwraps the GC funcs and ops in its
+// constructor and rewraps them in its destructor.
+
+class GCOpUnwrapper {
+public:
+  GCOpUnwrapper(DrawablePtr pDrawable, GCPtr pGC_)
+    : pGC(pGC_), pScreen(pDrawable->pScreen)
+  {
+    vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+    oldFuncs = pGC->funcs;
+    pGC->funcs = vncHooksGC->wrappedFuncs;
+    pGC->ops = vncHooksGC->wrappedOps;
+  }
+  ~GCOpUnwrapper() {
+    vncHooksGC->wrappedOps = pGC->ops;
+    pGC->funcs = oldFuncs;
+    pGC->ops = &vncHooksGCOps;
+  }
+  GCPtr pGC;
+  vncHooksGCPtr vncHooksGC;
+  GCFuncs* oldFuncs;
+  ScreenPtr pScreen;
+};
+
+#define GC_OP_UNWRAPPER(pDrawable, pGC, name)                             \
+  GCOpUnwrapper u(pDrawable, pGC);                                        \
+  ScreenPtr pScreen = (pDrawable)->pScreen;                               \
+  vncHooksScreenPtr vncHooksScreen                                        \
+    = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \
+  DBGPRINT((stderr,"vncHooks" #name " called\n"));
+
+
+// FillSpans - changed region is the whole of borderClip.  This is pessimistic,
+// but I believe this function is rarely used so it doesn't matter.
+
+static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit,
+                              DDXPointPtr pptInit, int *pwidthInit,
+                              int fSorted)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, FillSpans);
+
+  RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip);
+
+  (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// SetSpans - changed region is the whole of borderClip.  This is pessimistic,
+// but I believe this function is rarely used so it doesn't matter.
+
+static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+                             DDXPointPtr ppt, int *pwidth, int nspans,
+                             int fSorted)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, SetSpans);
+
+  RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip);
+
+  (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PutImage - changed region is the given rectangle, clipped by pCompositeClip
+
+static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+                             int x, int y, int w, int h, int leftPad,
+                             int format, char *pBits)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PutImage);
+
+  BoxRec box;
+  box.x1 = x + pDrawable->x;
+  box.y1 = y + pDrawable->y;
+  box.x2 = box.x1 + w;
+  box.y2 = box.y1 + h;
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, leftPad, format,
+                         pBits);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// CopyArea - destination of the copy is the dest rectangle, clipped by
+// pCompositeClip.  Any parts of the destination which cannot be copied from
+// the source (could be all of it) go into the changed region.
+
+static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+                                  GCPtr pGC, int srcx, int srcy, int w, int h,
+                                  int dstx, int dsty)
+{
+  GC_OP_UNWRAPPER(pDst, pGC, CopyArea);
+
+  BoxRec box;
+  box.x1 = dstx + pDst->x;
+  box.y1 = dsty + pDst->y;
+  box.x2 = box.x1 + w;
+  box.y2 = box.y1 + h;
+
+  RegionHelper dst(pScreen, &box, 0);
+  REGION_INTERSECT(pScreen, dst.reg, dst.reg, COMPOSITE_CLIP(pGC));
+
+  RegionHelper src(pScreen);
+
+  if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pScreen)) {
+    box.x1 = srcx + pSrc->x;
+    box.y1 = srcy + pSrc->y;
+    box.x2 = box.x1 + w;
+    box.y2 = box.y1 + h;
+
+    src.init(&box, 0);
+    REGION_INTERSECT(pScreen, src.reg, src.reg, &((WindowPtr)pSrc)->clipList);
+    REGION_TRANSLATE(pScreen, src.reg,
+                     dstx + pDst->x - srcx - pSrc->x,
+                     dsty + pDst->y - srcy - pSrc->y);
+  } else {
+    src.init(NullBox, 0);
+  }
+
+  RegionHelper changed(pScreen, NullBox, 0);
+  REGION_SUBTRACT(pScreen, changed.reg, dst.reg, src.reg);
+  REGION_INTERSECT(pScreen, dst.reg, dst.reg, src.reg);
+
+  RegionPtr rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h,
+                                         dstx, dsty);
+
+  if (REGION_NOTEMPTY(pScreen, dst.reg))
+    vncHooksScreen->desktop->add_copied(dst.reg,
+                                        dstx + pDst->x - srcx - pSrc->x,
+                                        dsty + pDst->y - srcy - pSrc->y);
+
+  if (REGION_NOTEMPTY(pScreen, changed.reg))
+    vncHooksScreen->desktop->add_changed(changed.reg);
+
+  return rgn;
+}
+
+
+// CopyPlane - changed region is the destination rectangle, clipped by
+// pCompositeClip
+
+static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+                                   GCPtr pGC, int srcx, int srcy, int w, int h,
+                                   int dstx, int dsty, unsigned long plane)
+{
+  GC_OP_UNWRAPPER(pDst, pGC, CopyPlane);
+
+  BoxRec box;
+  box.x1 = dstx + pDst->x;
+  box.y1 = dsty + pDst->y;
+  box.x2 = box.x1 + w;
+  box.y2 = box.y1 + h;
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  RegionPtr rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h,
+                                          dstx, dsty, plane);
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  return rgn;
+}
+
+// PolyPoint - changed region is the bounding rect, clipped by pCompositeClip
+
+static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+                              int npt, xPoint *pts)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyPoint);
+
+  if (npt == 0) {
+    (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
+    return;
+  }
+
+  int minX = pts[0].x;
+  int maxX = pts[0].x;
+  int minY = pts[0].y;
+  int maxY = pts[0].y;
+
+  if (mode == CoordModePrevious) {
+    int x = pts[0].x;
+    int y = pts[0].y;
+
+    for (int i = 1; i < npt; i++) {
+      x += pts[i].x;
+      y += pts[i].y;
+      if (x < minX) minX = x;
+      if (x > maxX) maxX = x;
+      if (y < minY) minY = y;
+      if (y > maxY) maxY = y;
+    }
+  } else {
+    for (int i = 1; i < npt; i++) {
+      if (pts[i].x < minX) minX = pts[i].x;
+      if (pts[i].x > maxX) maxX = pts[i].x;
+      if (pts[i].y < minY) minY = pts[i].y;
+      if (pts[i].y > maxY) maxY = pts[i].y;
+    }
+  }
+
+  BoxRec box;
+  box.x1 = minX + pDrawable->x;
+  box.y1 = minY + pDrawable->y;
+  box.x2 = maxX + 1 + pDrawable->x;
+  box.y2 = maxY + 1 + pDrawable->y;
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// Polylines - changed region is the union of the bounding rects of each line,
+// clipped by pCompositeClip.  If there are more than MAX_RECTS_PER_OP lines,
+// just use the bounding rect of all the lines.
+
+static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+                              int npt, DDXPointPtr ppts)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, Polylines);
+
+  if (npt == 0) {
+    (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
+    return;
+  }
+
+  int nRegRects = npt - 1;
+  xRectangle regRects[MAX_RECTS_PER_OP];
+
+  int lw = pGC->lineWidth;
+  if (lw == 0) lw = 1;
+
+  if (npt == 1)
+  {
+    // a single point
+    nRegRects = 1;
+    regRects[0].x = pDrawable->x + ppts[0].x - lw;
+    regRects[0].y = pDrawable->y + ppts[0].y - lw;
+    regRects[0].width = 2*lw;
+    regRects[0].height = 2*lw;
+  }
+  else
+  {
+    /*
+     * mitered joins can project quite a way from
+     * the line end; the 11 degree miter limit limits
+     * this extension to lw / (2 * tan(11/2)), rounded up
+     * and converted to int yields 6 * lw
+     */
+
+    int extra = lw / 2;
+    if (pGC->joinStyle == JoinMiter) {
+      extra = 6 * lw;
+    }
+
+    int prevX, prevY, curX, curY;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+
+    prevX = ppts[0].x + pDrawable->x;
+    prevY = ppts[0].y + pDrawable->y;
+    minX = maxX = prevX;
+    minY = maxY = prevY;
+
+    for (int i = 0; i < nRegRects; i++) {
+      if (mode == CoordModeOrigin) {
+        curX = pDrawable->x + ppts[i+1].x;
+        curY = pDrawable->y + ppts[i+1].y;
+      } else {
+        curX = prevX + ppts[i+1].x;
+        curY = prevY + ppts[i+1].y;
+      }
+
+      if (prevX > curX) {
+        rectX1 = curX - extra;
+        rectX2 = prevX + extra + 1;
+      } else {
+        rectX1 = prevX - extra;
+        rectX2 = curX + extra + 1;
+      }
+
+      if (prevY > curY) {
+        rectY1 = curY - extra;
+        rectY2 = prevY + extra + 1;
+      } else {
+        rectY1 = prevY - extra;
+        rectY2 = curY + extra + 1;
+      }
+
+      if (nRegRects <= MAX_RECTS_PER_OP) {
+        regRects[i].x = rectX1;
+        regRects[i].y = rectY1;
+        regRects[i].width = rectX2 - rectX1;
+        regRects[i].height = rectY2 - rectY1;
+      } else {
+        if (rectX1 < minX) minX = rectX1;
+        if (rectY1 < minY) minY = rectY1;
+        if (rectX2 > maxX) maxX = rectX2;
+        if (rectY2 > maxY) maxY = rectY2;
+      }
+
+      prevX = curX;
+      prevY = curY;
+    }
+
+    if (nRegRects > MAX_RECTS_PER_OP) {
+      regRects[0].x = minX;
+      regRects[0].y = minY;
+      regRects[0].width = maxX - minX;
+      regRects[0].height = maxY - minY;
+      nRegRects = 1;
+    }
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolySegment - changed region is the union of the bounding rects of each
+// segment, clipped by pCompositeClip.  If there are more than MAX_RECTS_PER_OP
+// segments, just use the bounding rect of all the segments.
+
+static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg,
+                                xSegment *segs)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolySegment);
+
+  if (nseg == 0) {
+    (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
+    return;
+  }
+
+  xRectangle regRects[MAX_RECTS_PER_OP];
+  int nRegRects = nseg;
+
+  int lw = pGC->lineWidth;
+  int extra = lw / 2;
+
+  int rectX1, rectY1, rectX2, rectY2;
+  int minX, minY, maxX, maxY;
+
+  minX = maxX = segs[0].x1;
+  minY = maxY = segs[0].y1;
+
+  for (int i = 0; i < nseg; i++) {
+    if (segs[i].x1 > segs[i].x2) {
+      rectX1 = pDrawable->x + segs[i].x2 - extra;
+      rectX2 = pDrawable->x + segs[i].x1 + extra + 1;
+    } else {
+      rectX1 = pDrawable->x + segs[i].x1 - extra;
+      rectX2 = pDrawable->x + segs[i].x2 + extra + 1;
+    }
+
+    if (segs[i].y1 > segs[i].y2) {
+      rectY1 = pDrawable->y + segs[i].y2 - extra;
+      rectY2 = pDrawable->y + segs[i].y1 + extra + 1;
+    } else {
+      rectY1 = pDrawable->y + segs[i].y1 - extra;
+      rectY2 = pDrawable->y + segs[i].y2 + extra + 1;
+    }
+
+    if (nseg <= MAX_RECTS_PER_OP) {
+      regRects[i].x = rectX1;
+      regRects[i].y = rectY1;
+      regRects[i].width = rectX2 - rectX1;
+      regRects[i].height = rectY2 - rectY1;
+    } else {
+      if (rectX1 < minX) minX = rectX1;
+      if (rectY1 < minY) minY = rectY1;
+      if (rectX2 > maxX) maxX = rectX2;
+      if (rectY2 > maxY) maxY = rectY2;
+    }
+  }
+
+  if (nseg > MAX_RECTS_PER_OP) {
+    regRects[0].x = minX;
+    regRects[0].y = minY;
+    regRects[0].width = maxX - minX;
+    regRects[0].height = maxY - minY;
+    nRegRects = 1;
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyRectangle - changed region is the union of the bounding rects around
+// each side of the outline rectangles, clipped by pCompositeClip.  If there
+// are more than MAX_RECTS_PER_OP rectangles, just use the bounding rect of all
+// the rectangles.
+
+static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+                                  xRectangle *rects)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyRectangle);
+
+  if (nrects == 0) {
+    (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
+    return;
+  }
+
+  xRectangle regRects[MAX_RECTS_PER_OP*4];
+  int nRegRects = nrects * 4;
+
+  int lw = pGC->lineWidth;
+  int extra = lw / 2;
+
+  int rectX1, rectY1, rectX2, rectY2;
+  int minX, minY, maxX, maxY;
+
+  minX = maxX = rects[0].x;
+  minY = maxY = rects[0].y;
+
+  for (int i = 0; i < nrects; i++) {
+    if (nrects <= MAX_RECTS_PER_OP) {
+      regRects[i*4].x = rects[i].x - extra + pDrawable->x;
+      regRects[i*4].y = rects[i].y - extra + pDrawable->y;
+      regRects[i*4].width = rects[i].width + 1 + 2 * extra;
+      regRects[i*4].height = 1 + 2 * extra;
+
+      regRects[i*4+1].x = rects[i].x - extra + pDrawable->x;
+      regRects[i*4+1].y = rects[i].y - extra + pDrawable->y;
+      regRects[i*4+1].width = 1 + 2 * extra;
+      regRects[i*4+1].height = rects[i].height + 1 + 2 * extra;
+
+      regRects[i*4+2].x = rects[i].x + rects[i].width - extra + pDrawable->x;
+      regRects[i*4+2].y = rects[i].y - extra + pDrawable->y;
+      regRects[i*4+2].width = 1 + 2 * extra;
+      regRects[i*4+2].height = rects[i].height + 1 + 2 * extra;
+
+      regRects[i*4+3].x = rects[i].x - extra + pDrawable->x;
+      regRects[i*4+3].y = rects[i].y + rects[i].height - extra + pDrawable->y;
+      regRects[i*4+3].width = rects[i].width + 1 + 2 * extra;
+      regRects[i*4+3].height = 1 + 2 * extra;
+    } else {
+      rectX1 = pDrawable->x + rects[i].x - extra;
+      rectY1 = pDrawable->y + rects[i].y - extra;
+      rectX2 = pDrawable->x + rects[i].x + rects[i].width + extra+1;
+      rectY2 = pDrawable->y + rects[i].y + rects[i].height + extra+1;
+      if (rectX1 < minX) minX = rectX1;
+      if (rectY1 < minY) minY = rectY1;
+      if (rectX2 > maxX) maxX = rectX2;
+      if (rectY2 > maxY) maxY = rectY2;
+    }
+  }
+
+  if (nrects > MAX_RECTS_PER_OP) {
+    regRects[0].x = minX;
+    regRects[0].y = minY;
+    regRects[0].width = maxX - minX;
+    regRects[0].height = maxY - minY;
+    nRegRects = 1;
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyArc - changed region is the union of bounding rects around each arc,
+// clipped by pCompositeClip.  If there are more than MAX_RECTS_PER_OP
+// arcs, just use the bounding rect of all the arcs.
+
+static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+                            xArc *arcs)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyArc);
+
+  if (narcs == 0) {
+    (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
+    return;
+  }
+
+  xRectangle regRects[MAX_RECTS_PER_OP];
+  int nRegRects = narcs;
+
+  int lw = pGC->lineWidth;
+  if (lw == 0) lw = 1;
+  int extra = lw / 2;
+
+  int rectX1, rectY1, rectX2, rectY2;
+  int minX, minY, maxX, maxY;
+
+  minX = maxX = arcs[0].x;
+  minY = maxY = arcs[0].y;
+
+  for (int i = 0; i < narcs; i++) {
+    if (narcs <= MAX_RECTS_PER_OP) {
+      regRects[i].x = arcs[i].x - extra + pDrawable->x;
+      regRects[i].y = arcs[i].y - extra + pDrawable->y;
+      regRects[i].width = arcs[i].width + lw;
+      regRects[i].height = arcs[i].height + lw;
+    } else {
+      rectX1 = pDrawable->x + arcs[i].x - extra;
+      rectY1 = pDrawable->y + arcs[i].y - extra;
+      rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+      rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+      if (rectX1 < minX) minX = rectX1;
+      if (rectY1 < minY) minY = rectY1;
+      if (rectX2 > maxX) maxX = rectX2;
+      if (rectY2 > maxY) maxY = rectY2;
+    }
+  }
+
+  if (narcs > MAX_RECTS_PER_OP) {
+    regRects[0].x = minX;
+    regRects[0].y = minY;
+    regRects[0].width = maxX - minX;
+    regRects[0].height = maxY - minY;
+    nRegRects = 1;
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+
+// FillPolygon - changed region is the bounding rect around the polygon,
+// clipped by pCompositeClip
+
+static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape,
+                                int mode, int count, DDXPointPtr pts)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, FillPolygon);
+
+  if (count == 0) {
+    (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
+    return;
+  }
+
+  int minX = pts[0].x;
+  int maxX = pts[0].x;
+  int minY = pts[0].y;
+  int maxY = pts[0].y;
+
+  if (mode == CoordModePrevious) {
+    int x = pts[0].x;
+    int y = pts[0].y;
+
+    for (int i = 1; i < count; i++) {
+      x += pts[i].x;
+      y += pts[i].y;
+      if (x < minX) minX = x;
+      if (x > maxX) maxX = x;
+      if (y < minY) minY = y;
+      if (y > maxY) maxY = y;
+    }
+  } else {
+    for (int i = 1; i < count; i++) {
+      if (pts[i].x < minX) minX = pts[i].x;
+      if (pts[i].x > maxX) maxX = pts[i].x;
+      if (pts[i].y < minY) minY = pts[i].y;
+      if (pts[i].y > maxY) maxY = pts[i].y;
+    }
+  }
+
+  BoxRec box;
+  box.x1 = minX + pDrawable->x;
+  box.y1 = minY + pDrawable->y;
+  box.x2 = maxX + 1 + pDrawable->x;
+  box.y2 = maxY + 1 + pDrawable->y;
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyFillRect - changed region is the union of the rectangles, clipped by
+// pCompositeClip.  If there are more than MAX_RECTS_PER_OP rectangles, just
+// use the bounding rect of all the rectangles.
+
+static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+                                 xRectangle *rects)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillRect);
+
+  if (nrects == 0) {
+    (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
+    return;
+  }
+
+  xRectangle regRects[MAX_RECTS_PER_OP];
+  int nRegRects = nrects;
+  int rectX1, rectY1, rectX2, rectY2;
+  int minX, minY, maxX, maxY;
+  minX = maxX = rects[0].x;
+  minY = maxY = rects[0].y;
+
+  for (int i = 0; i < nrects; i++) {
+    if (nrects <= MAX_RECTS_PER_OP) {
+      regRects[i].x = rects[i].x + pDrawable->x;
+      regRects[i].y = rects[i].y + pDrawable->y;
+      regRects[i].width = rects[i].width;
+      regRects[i].height = rects[i].height;
+    } else {
+      rectX1 = pDrawable->x + rects[i].x;
+      rectY1 = pDrawable->y + rects[i].y;
+      rectX2 = pDrawable->x + rects[i].x + rects[i].width;
+      rectY2 = pDrawable->y + rects[i].y + rects[i].height;
+      if (rectX1 < minX) minX = rectX1;
+      if (rectY1 < minY) minY = rectY1;
+      if (rectX2 > maxX) maxX = rectX2;
+      if (rectY2 > maxY) maxY = rectY2;
+    }
+  }
+
+  if (nrects > MAX_RECTS_PER_OP) {
+    regRects[0].x = minX;
+    regRects[0].y = minY;
+    regRects[0].width = maxX - minX;
+    regRects[0].height = maxY - minY;
+    nRegRects = 1;
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyFillArc - changed region is the union of bounding rects around each arc,
+// clipped by pCompositeClip.  If there are more than MAX_RECTS_PER_OP arcs,
+// just use the bounding rect of all the arcs.
+
+static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+                                xArc *arcs)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillArc);
+
+  if (narcs == 0) {
+    (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
+    return;
+  }
+
+  xRectangle regRects[MAX_RECTS_PER_OP];
+  int nRegRects = narcs;
+
+  int lw = pGC->lineWidth;
+  if (lw == 0) lw = 1;
+  int extra = lw / 2;
+
+  int rectX1, rectY1, rectX2, rectY2;
+  int minX, minY, maxX, maxY;
+
+  minX = maxX = arcs[0].x;
+  minY = maxY = arcs[0].y;
+
+  for (int i = 0; i < narcs; i++) {
+    if (narcs <= MAX_RECTS_PER_OP) {
+      regRects[i].x = arcs[i].x - extra + pDrawable->x;
+      regRects[i].y = arcs[i].y - extra + pDrawable->y;
+      regRects[i].width = arcs[i].width + lw;
+      regRects[i].height = arcs[i].height + lw;
+    } else {
+      rectX1 = pDrawable->x + arcs[i].x - extra;
+      rectY1 = pDrawable->y + arcs[i].y - extra;
+      rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+      rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+      if (rectX1 < minX) minX = rectX1;
+      if (rectY1 < minY) minY = rectY1;
+      if (rectX2 > maxX) maxX = rectX2;
+      if (rectY2 > maxY) maxY = rectY2;
+    }
+  }
+
+  if (narcs > MAX_RECTS_PER_OP) {
+    regRects[0].x = minX;
+    regRects[0].y = minY;
+    regRects[0].width = maxX - minX;
+    regRects[0].height = maxY - minY;
+    nRegRects = 1;
+  }
+
+  RegionHelper changed(pScreen, nRegRects, regRects);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// GetTextBoundingRect - calculate a bounding rectangle around n chars of a
+// font.  Not particularly accurate, but good enough.
+
+static void GetTextBoundingRect(DrawablePtr pDrawable, FontPtr font, int x,
+                                int y, int nchars, BoxPtr box)
+{
+  int ascent = max(FONTASCENT(font), FONTMAXBOUNDS(font, ascent));
+  int descent = max(FONTDESCENT(font), FONTMAXBOUNDS(font, descent));
+  int charWidth = max(FONTMAXBOUNDS(font,rightSideBearing),
+                      FONTMAXBOUNDS(font,characterWidth));
+
+  box->x1 = pDrawable->x + x;
+  box->y1 = pDrawable->y + y - ascent;
+  box->x2 = box->x1 + charWidth * nchars;
+  box->y2 = box->y1 + ascent + descent;
+
+  if (FONTMINBOUNDS(font,leftSideBearing) < 0)
+    box->x1 += FONTMINBOUNDS(font,leftSideBearing);
+}
+
+// PolyText8 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                             int count, char *chars)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyText8);
+
+  if (count == 0)
+    return (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  int ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  return ret;
+}
+
+// PolyText16 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                              int count, unsigned short *chars)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyText16);
+
+  if (count == 0)
+    return (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  int ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+
+  return ret;
+}
+
+// ImageText8 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                               int count, char *chars)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, ImageText8);
+
+  if (count == 0) {
+    (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
+    return;
+  }
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// ImageText16 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+                                int count, unsigned short *chars)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, ImageText16);
+
+  if (count == 0) {
+    (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
+    return;
+  }
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// ImageGlyphBlt - changed region is bounding rect around nglyph chars, clipped
+// by pCompositeClip
+
+static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+                                  int y, unsigned int nglyph,
+                                  CharInfoPtr *ppci, pointer pglyphBase)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, ImageGlyphBlt);
+
+  if (nglyph == 0) {
+    (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
+    return;
+  }
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyGlyphBlt - changed region is bounding rect around nglyph chars, clipped
+// by pCompositeClip
+
+static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+                                 int y, unsigned int nglyph,
+                                 CharInfoPtr *ppci, pointer pglyphBase)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PolyGlyphBlt);
+
+  if (nglyph == 0) {
+    (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
+    return;
+  }
+
+  BoxRec box;
+  GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box);
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PushPixels - changed region is the given rectangle, clipped by
+// pCompositeClip
+
+static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap,
+                               DrawablePtr pDrawable, int w, int h, int x,
+                               int y)
+{
+  GC_OP_UNWRAPPER(pDrawable, pGC, PushPixels);
+
+  BoxRec box;
+  box.x1 = x + pDrawable->x;
+  box.y1 = y + pDrawable->y;
+  box.x2 = box.x1 + w;
+  box.y2 = box.y1 + h;
+
+  RegionHelper changed(pScreen, &box, 0);
+
+  REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+  (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y);
+
+  vncHooksScreen->desktop->add_changed(changed.reg);
+}
diff --git a/xc/programs/Xserver/vnc/vncHooks.h b/xc/programs/Xserver/vnc/vncHooks.h
new file mode 100644
index 0000000..c2ca825
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncHooks.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+#ifndef __VNCHOOKS_H__
+#define __VNCHOOKS_H__
+
+extern "C" {
+#include <screenint.h>
+  extern Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop);
+}
+
+#endif
diff --git a/xc/programs/Xserver/vnc/xf86vncModule.cc b/xc/programs/Xserver/vnc/xf86vncModule.cc
new file mode 100644
index 0000000..3504c6a
--- /dev/null
+++ b/xc/programs/Xserver/vnc/xf86vncModule.cc
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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; either 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.
+ */
+/*  This is the xf86 module code for the vnc extension.
+ */
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+
+extern "C" {
+#define class c_class
+#define private c_private
+#define bool c_bool
+#define new c_new
+#include "xf86.h"
+#include "xf86Module.h"
+#undef class
+#undef private
+#undef bool
+#undef new
+
+extern void vncExtensionInit();
+static void vncExtensionInitWithParams(INITARGS);
+
+#ifdef XFree86LOADER
+
+static MODULESETUPPROTO(vncSetup);
+
+ExtensionModule vncExt =
+{
+    vncExtensionInitWithParams,
+    "VNC",
+    NULL,
+    NULL,
+    NULL
+};
+
+static XF86ModuleVersionInfo vncVersRec =
+{
+    "vnc",
+    "RealVNC Ltd",
+    MODINFOSTRING1,
+    MODINFOSTRING2,
+    XF86_VERSION_CURRENT,
+    1, 0, 0,
+    ABI_CLASS_EXTENSION,         /* needs the server extension ABI */
+    ABI_EXTENSION_VERSION,
+    MOD_CLASS_EXTENSION,
+    {0,0,0,0}
+};
+
+XF86ModuleData vncModuleData = { &vncVersRec, vncSetup, NULL };
+
+static pointer
+vncSetup(pointer module, pointer opts, int *errmaj, int *errmin) {
+    LoadExtension(&vncExt, FALSE);
+    /* Need a non-NULL return value to indicate success */
+    return (pointer)1;
+}
+
+static void vncExtensionInitWithParams(INITARGS)
+{
+  rfb::initStdIOLoggers();
+  rfb::LogWriter::setLogParams("*:stderr:30");
+
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    ScrnInfoPtr pScrn = xf86Screens[scr];
+
+    rfb::VoidParameter* p;
+    for (p = rfb::Configuration::head; p; p = p->_next) {
+      char* val = xf86FindOptionValue(pScrn->options, p->getName());
+      if (val)
+        p->setParam(val);
+    }
+  }
+
+  vncExtensionInit();
+}
+
+#endif /* XFree86LOADER */
+}
diff --git a/zlib/ChangeLog b/zlib/ChangeLog
new file mode 100644
index 0000000..eded33d
--- /dev/null
+++ b/zlib/ChangeLog
@@ -0,0 +1,481 @@
+
+		ChangeLog file for zlib
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+  This creates a security problem described in
+  http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+  less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+  of 256 bytes. (A complete fix will be available in 1.1.5).
+	
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+  occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+  (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+  See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz  (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean"  (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+  . zutil.c, zutil.h: added "const" for zmem*
+  . Make_vms.com: fixed some typos
+  . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+  . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+  . msdos/Makefile.*: use model-dependent name for the built zlib library
+  . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+     new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+  See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+  completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode  (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+  (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+  compression ratio on some files. This also allows inlining _tr_tally for
+  matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+  on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+  the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+  them at run time (thanks to Ken Raeburn for this suggestion). To create
+  trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+  gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occuring only with compression level 0 (thanks to
+  Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+  (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+  (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+  inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+   contrib/asm386/ by Gilles Vollant <info@winimage.com>
+	386 asm code replacing longest_match().
+   contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+        A C++ I/O streams interface to the zlib gz* functions
+   contrib/iostream2/  by Tyge Løvset <Tyge.Lovset@cmr.no>
+	Another C++ I/O streams interface
+   contrib/untgz/  by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+	A very simple tar.gz file extractor using zlib
+   contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+        How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+  level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+  (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)	
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $Id: ChangeLog,v 1.1 2004/10/08 09:44:20 const_k Exp $. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+  
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+  bit, so the decompressor could decompress all the correct data but went
+  on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+  small and medium models; this makes the library incompatible with previous
+  versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+  avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+  Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+  and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+  -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+     warning C4746: 'inflate_mask' : unsized array treated as  '__far'
+     (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+  not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+  (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+  typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+  was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+  pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+  is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+  (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+  TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+  (one's complement) is now done inside crc32(). WARNING: this is
+  incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+  not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+  Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+  if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+  user-provided history buffer. This is supported only in deflateInit2
+  and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/zlib/FAQ b/zlib/FAQ
new file mode 100644
index 0000000..47a7d60
--- /dev/null
+++ b/zlib/FAQ
@@ -0,0 +1,100 @@
+
+		Frequently Asked Questions about zlib
+
+
+If your question is not there, please check the zlib home page 
+http://www.zlib.org which may have more recent information.
+The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html
+
+
+ 1. Is zlib Y2K-compliant?
+
+    Yes. zlib doesn't handle dates.
+
+ 2. Where can I get a Windows DLL version?
+
+    The zlib sources can be compiled without change to produce a DLL. If you
+    want a precompiled DLL, see http://www.winimage.com/zLibDll/ . Questions
+    about the zlib DLL should be sent to Gilles Vollant (info@winimage.com).
+
+ 3. Where can I get a Visual Basic interface to zlib?
+
+    See
+        * http://www.winimage.com/zLibDll/cmp-z-it.zip
+        * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm
+        * contrib/visual-basic.txt in the zlib distribution
+
+ 4. compress() returns Z_BUF_ERROR
+
+    Make sure that before the call of compress, the length of the compressed
+    buffer is equal to the total size of the compressed buffer and not
+    zero. For Visual Basic, check that this parameter is passed by reference
+    ("as any"), not by value ("as long").
+
+ 5. deflate() or inflate() returns Z_BUF_ERROR
+
+    Before making the call, make sure that avail_in and avail_out are not
+    zero. When setting the parameter flush equal to Z_FINISH, also make sure
+    that avail_out is big enough to allow processing all pending input.
+
+ 6. Where's the zlib documentation (man pages, etc.)?
+
+    It's in zlib.h for the moment, and Francis S. Lin has converted it to a
+    web page zlib.html. Volunteers to transform this to Unix-style man pages,
+    please contact Jean-loup Gailly (jloup@gzip.org). Examples of zlib usage
+    are in the files example.c and minigzip.c.
+
+ 7. Why don't you use GNU autoconf or libtool or ...?
+
+    Because we would like to keep zlib as a very small and simple
+    package. zlib is rather portable and doesn't need much configuration.
+
+ 8. I found a bug in zlib.
+
+    Most of the time, such problems are due to an incorrect usage of
+    zlib. Please try to reproduce the problem with a small program and send
+    the corresponding source to us at zlib@gzip.org . Do not send
+    multi-megabyte data files without prior agreement.
+
+ 9. Why do I get "undefined reference to gzputc"?
+
+    If "make test" produces something like
+
+       example.o(.text+0x154): undefined reference to `gzputc'
+      
+    check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
+    /usr/X11R6/lib. Remove any old versions, then do "make install".
+
+10. I need a Delphi interface to zlib.
+
+    See the directories contrib/delphi and contrib/delphi2 in the zlib
+    distribution.
+
+11. Can zlib handle .zip archives?
+
+    See the directory contrib/minizip in the zlib distribution.
+
+12. Can zlib handle .Z files?
+
+    No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
+    the code of uncompress on your own.
+
+13. How can I make a Unix shared library?
+
+    make clean
+    ./configure -s
+    make
+
+14. Why does "make test" fail on Mac OS X?
+
+    Mac OS X already includes zlib as a shared library, and so -lz links the
+    shared library instead of the one that the "make" compiled. For zlib
+    1.1.3, the two are incompatible due to different compile-time
+    options. Simply change the -lz in the Makefile to libz.a, and it will use
+    the compiled library instead of the shared one and the "make test" will
+    succeed.
+
+15. I have a question about OttoPDF
+
+    We are not the authors of OttoPDF. The real author is on the OttoPDF web
+    site Joel Hainley jhainley@myndkryme.com.
diff --git a/zlib/INDEX b/zlib/INDEX
new file mode 100644
index 0000000..8a24576
--- /dev/null
+++ b/zlib/INDEX
@@ -0,0 +1,86 @@
+ChangeLog		history of changes
+INDEX			this file
+FAQ			Frequently Asked Questions about zlib
+Make_vms.com		script for Vax/VMS
+Makefile		makefile for Unix (generated by configure)
+Makefile.in		makefile for Unix (template for configure)
+Makefile.riscos 	makefile for RISCOS
+README			guess what
+algorithm.txt		description of the (de)compression algorithm
+configure		configure script for Unix
+descrip.mms		makefile for Vax/VMS
+zlib.3			mini man page for zlib (volunteers to write full
+			man pages from zlib.h welcome. write to jloup@gzip.org)
+
+amiga/Makefile.sas	makefile for Amiga SAS/C
+amiga/Makefile.pup      makefile for Amiga powerUP SAS/C PPC
+
+msdos/Makefile.w32      makefile for Microsoft Visual C++ 32-bit
+msdos/Makefile.b32	makefile for Borland C++   32-bit
+msdos/Makefile.bor	makefile for Borland C/C++ 16-bit
+msdos/Makefile.dj2	makefile for DJGPP 2.x
+msdos/Makefile.emx	makefile for EMX 0.9c (32-bit DOS/OS2)
+msdos/Makefile.msc	makefile for Microsoft C 16-bit
+msdos/Makefile.tc	makefile for Turbo C
+msdos/Makefile.wat	makefile for Watcom C
+msdos/zlib.def        	definition file for Windows DLL
+msdos/zlib.rc         	definition file for Windows DLL
+
+nt/Makefile.nt		makefile for Windows NT
+nt/zlib.dnt		definition file for Windows NT DLL
+nt/Makefile.emx		makefile for EMX 0.9c/RSXNT 1.41 (Win32 Intel)
+nt/Makefile.gcc		makefile for Windows NT using GCC (mingw32)
+
+
+		zlib public header files (must be kept):
+zconf.h
+zlib.h
+
+		private source files used to build the zlib library:
+adler32.c
+compress.c
+crc32.c
+deflate.c
+deflate.h
+gzio.c
+infblock.c
+infblock.h
+infcodes.c
+infcodes.h
+inffast.c
+inffast.h
+inflate.c
+inftrees.c
+inftrees.h
+infutil.c
+infutil.h
+maketree.c
+trees.c
+uncompr.c
+zutil.c
+zutil.h
+
+		source files for sample programs:
+example.c
+minigzip.c
+
+		unsupported contribution by third parties
+
+contrib/asm386/ by Gilles Vollant <info@winimage.com>
+	386 asm code replacing longest_match().
+
+contrib/minizip/ by Gilles Vollant <info@winimage.com>
+	Mini zip and unzip based on zlib
+        See http://www.winimage.com/zLibDll/unzip.html
+
+contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+        A C++ I/O streams interface to the zlib gz* functions
+
+contrib/iostream2/  by Tyge Løvset <Tyge.Lovset@cmr.no>
+	Another C++ I/O streams interface
+
+contrib/untgz/  by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+	A very simple tar.gz extractor using zlib
+
+contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+        How to use compress(), uncompress() and the gz* functions from VB.
diff --git a/zlib/Make_vms.com b/zlib/Make_vms.com
new file mode 100644
index 0000000..1c57e8f
--- /dev/null
+++ b/zlib/Make_vms.com
@@ -0,0 +1,115 @@
+$! make libz under VMS
+$! written by Martin P.J. Zinser <m.zinser@gsi.de>
+$!
+$! Look for the compiler used
+$!
+$ ccopt = ""
+$ if f$getsyi("HW_MODEL").ge.1024
+$ then
+$  ccopt = "/prefix=all"+ccopt
+$  comp  = "__decc__=1"
+$  if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$ else
+$  if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs.""
+$   then
+$    comp  = "__vaxc__=1"
+$    if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$   else
+$    if f$trnlnm("SYS").eqs."" then define sys decc$library_include:
+$    ccopt = "/decc/prefix=all"+ccopt
+$    comp  = "__decc__=1"
+$  endif
+$ endif
+$!
+$! Build the thing plain or with mms
+$!
+$ write sys$output "Compiling Zlib sources ..."
+$ if f$search("SYS$SYSTEM:MMS.EXE").eqs.""
+$  then
+$   dele example.obj;*,minigzip.obj;*
+$   CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" -
+                adler32.c zlib.h zconf.h
+$   CALL MAKE compress.OBJ "CC ''CCOPT' compress" -
+                compress.c zlib.h zconf.h
+$   CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" -
+                crc32.c zlib.h zconf.h
+$   CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" -
+                deflate.c deflate.h zutil.h zlib.h zconf.h
+$   CALL MAKE gzio.OBJ "CC ''CCOPT' gzio" -
+                gzio.c zutil.h zlib.h zconf.h
+$   CALL MAKE infblock.OBJ "CC ''CCOPT' infblock" -
+                infblock.c zutil.h zlib.h zconf.h infblock.h
+$   CALL MAKE infcodes.OBJ "CC ''CCOPT' infcodes" -
+                infcodes.c zutil.h zlib.h zconf.h inftrees.h
+$   CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" -
+                inffast.c zutil.h zlib.h zconf.h inffast.h
+$   CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" -
+                inflate.c zutil.h zlib.h zconf.h infblock.h
+$   CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" -
+                inftrees.c zutil.h zlib.h zconf.h inftrees.h
+$   CALL MAKE infutil.OBJ "CC ''CCOPT' infutil" -
+                infutil.c zutil.h zlib.h zconf.h inftrees.h infutil.h
+$   CALL MAKE trees.OBJ "CC ''CCOPT' trees" -
+                trees.c deflate.h zutil.h zlib.h zconf.h
+$   CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" -
+                uncompr.c zlib.h zconf.h
+$   CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" -
+                zutil.c zutil.h zlib.h zconf.h
+$   write sys$output "Building Zlib ..."
+$   CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ
+$   write sys$output "Building example..."
+$   CALL MAKE example.OBJ "CC ''CCOPT' example" -
+                example.c zlib.h zconf.h
+$   call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb
+$   write sys$output "Building minigzip..."
+$   CALL MAKE minigzip.OBJ "CC ''CCOPT' minigzip" -
+                minigzip.c zlib.h zconf.h
+$   call make minigzip.exe - 
+                "LINK minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib" - 
+                minigzip.obj libz.olb
+$  else
+$   mms/macro=('comp')
+$  endif
+$ write sys$output "Zlib build completed"
+$ exit
+$!
+$!
+$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
+$ V = 'F$Verify(0)
+$! P1 = What we are trying to make
+$! P2 = Command to make it
+$! P3 - P8  What it depends on
+$
+$ If F$Search(P1) .Eqs. "" Then Goto Makeit
+$ Time = F$CvTime(F$File(P1,"RDT"))
+$arg=3
+$Loop:
+$       Argument = P'arg
+$       If Argument .Eqs. "" Then Goto Exit
+$       El=0
+$Loop2:
+$       File = F$Element(El," ",Argument)
+$       If File .Eqs. " " Then Goto Endl
+$       AFile = ""
+$Loop3:
+$       OFile = AFile
+$       AFile = F$Search(File)
+$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
+$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
+$       Goto Loop3
+$NextEL:
+$       El = El + 1
+$       Goto Loop2
+$EndL:
+$ arg=arg+1
+$ If arg .Le. 8 Then Goto Loop
+$ Goto Exit
+$
+$Makeit:
+$ VV=F$VERIFY(0)
+$ write sys$output P2
+$ 'P2
+$ VV='F$Verify(VV)
+$Exit:
+$ If V Then Set Verify
+$ENDSUBROUTINE
diff --git a/zlib/Makefile.in b/zlib/Makefile.in
new file mode 100644
index 0000000..531562b
--- /dev/null
+++ b/zlib/Makefile.in
@@ -0,0 +1,175 @@
+# Makefile for zlib
+# Copyright (C) 1995-2002 Jean-loup Gailly.
+# For conditions of distribution and use, see copyright notice in zlib.h 
+
+# To compile and test, type:
+#   ./configure; make test
+# The call of configure is optional if you don't have special requirements
+# If you wish to build zlib as a shared library, use: ./configure -s
+
+# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
+#    make install
+# To install in $HOME instead of /usr/local, use:
+#    make install prefix=$HOME
+
+CC=cc
+
+CFLAGS=-O
+#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
+#CFLAGS=-g -DDEBUG
+#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
+#           -Wstrict-prototypes -Wmissing-prototypes
+
+LDFLAGS=-L. -lz
+LDSHARED=$(CC)
+CPP=$(CC) -E
+
+VER=1.1.4
+LIBS=libz.a
+SHAREDLIB=libz.so
+
+AR=ar rc
+RANLIB=ranlib
+TAR=tar
+SHELL=/bin/sh
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+libdir = ${exec_prefix}/lib
+includedir = ${prefix}/include
+
+OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
+       zutil.o inflate.o infblock.o inftrees.o infcodes.o infutil.o inffast.o
+
+OBJA =
+# to use the asm code: make OBJA=match.o
+
+TEST_OBJS = example.o minigzip.o
+
+DISTFILES = README FAQ INDEX ChangeLog configure Make*[a-z0-9] *.[ch] *.mms \
+  algorithm.txt zlib.3 zlib.html \
+  msdos/Make*[a-z0-9] msdos/zlib.def msdos/zlib.rc \
+  nt/Make*[a-z0-9] nt/zlib.dnt amiga/Make*.??? os2/M*.os2 os2/zlib.def \
+  contrib/RE*.contrib contrib/*.txt contrib/asm386/*.asm contrib/asm386/*.c \
+  contrib/asm386/*.bat contrib/asm386/zlibvc.d?? contrib/asm[56]86/*.?86 \
+  contrib/asm[56]86/*.S contrib/iostream/*.cpp \
+  contrib/iostream/*.h  contrib/iostream2/*.h contrib/iostream2/*.cpp \
+  contrib/untgz/Makefile contrib/untgz/*.c contrib/untgz/*.w32 \
+  contrib/minizip/[CM]*[pe] contrib/minizip/*.[ch] contrib/minizip/*.[td]?? \
+  contrib/delphi*/*.???
+
+all: example minigzip
+
+test: all
+	@LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
+	echo hello world | ./minigzip | ./minigzip -d || \
+	  echo '		*** minigzip test FAILED ***' ; \
+	if ./example; then \
+	  echo '		*** zlib test OK ***'; \
+	else \
+	  echo '		*** zlib test FAILED ***'; \
+	fi
+
+libz.a: $(OBJS) $(OBJA)
+	$(AR) $@ $(OBJS) $(OBJA)
+	-@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+
+match.o: match.S
+	$(CPP) match.S > _match.s
+	$(CC) -c _match.s
+	mv _match.o match.o
+	rm -f _match.s
+
+$(SHAREDLIB).$(VER): $(OBJS)
+	$(LDSHARED) -o $@ $(OBJS)
+	rm -f $(SHAREDLIB) $(SHAREDLIB).1
+	ln -s $@ $(SHAREDLIB)
+	ln -s $@ $(SHAREDLIB).1
+
+example: example.o $(LIBS)
+	$(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+
+minigzip: minigzip.o $(LIBS)
+	$(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+
+install: $(LIBS)
+	-@if [ ! -d $(includedir)  ]; then mkdir $(includedir); fi
+	-@if [ ! -d $(libdir) ]; then mkdir $(libdir); fi
+	cp zlib.h zconf.h $(includedir)
+	chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+	cp $(LIBS) $(libdir)
+	cd $(libdir); chmod 755 $(LIBS)
+	-@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+	cd $(libdir); if test -f $(SHAREDLIB).$(VER); then \
+	  rm -f $(SHAREDLIB) $(SHAREDLIB).1; \
+	  ln -s $(SHAREDLIB).$(VER) $(SHAREDLIB); \
+	  ln -s $(SHAREDLIB).$(VER) $(SHAREDLIB).1; \
+	  (ldconfig || true)  >/dev/null 2>&1; \
+	fi
+# The ranlib in install is needed on NeXTSTEP which checks file times
+# ldconfig is for Linux
+
+uninstall:
+	cd $(includedir); \
+	v=$(VER); \
+	if test -f zlib.h; then \
+	  v=`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`; \
+          rm -f zlib.h zconf.h; \
+	fi; \
+	cd $(libdir); rm -f libz.a; \
+	if test -f $(SHAREDLIB).$$v; then \
+	  rm -f $(SHAREDLIB).$$v $(SHAREDLIB) $(SHAREDLIB).1; \
+	fi
+
+clean:
+	rm -f *.o *~ example minigzip libz.a libz.so* foo.gz so_locations \
+	   _match.s maketree
+
+distclean:	clean
+
+zip:
+	mv Makefile Makefile~; cp -p Makefile.in Makefile
+	rm -f test.c ztest*.c contrib/minizip/test.zip
+	v=`sed -n -e 's/\.//g' -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
+	zip -ul9 zlib$$v $(DISTFILES)
+	mv Makefile~ Makefile
+
+dist:
+	mv Makefile Makefile~; cp -p Makefile.in Makefile
+	rm -f test.c ztest*.c contrib/minizip/test.zip
+	d=zlib-`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
+	rm -f $$d.tar.gz; \
+	if test ! -d ../$$d; then rm -f ../$$d; ln -s `pwd` ../$$d; fi; \
+	files=""; \
+	for f in $(DISTFILES); do files="$$files $$d/$$f"; done; \
+	cd ..; \
+	GZIP=-9 $(TAR) chofz $$d/$$d.tar.gz $$files; \
+	if test ! -d $$d; then rm -f $$d; fi
+	mv Makefile~ Makefile
+
+tags:	
+	etags *.[ch]
+
+depend:
+	makedepend -- $(CFLAGS) -- *.[ch]
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adler32.o: zlib.h zconf.h
+compress.o: zlib.h zconf.h
+crc32.o: zlib.h zconf.h
+deflate.o: deflate.h zutil.h zlib.h zconf.h
+example.o: zlib.h zconf.h
+gzio.o: zutil.h zlib.h zconf.h
+infblock.o: infblock.h inftrees.h infcodes.h infutil.h zutil.h zlib.h zconf.h
+infcodes.o: zutil.h zlib.h zconf.h
+infcodes.o: inftrees.h infblock.h infcodes.h infutil.h inffast.h
+inffast.o: zutil.h zlib.h zconf.h inftrees.h
+inffast.o: infblock.h infcodes.h infutil.h inffast.h
+inflate.o: zutil.h zlib.h zconf.h infblock.h
+inftrees.o: zutil.h zlib.h zconf.h inftrees.h
+infutil.o: zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
+minigzip.o:  zlib.h zconf.h 
+trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
+uncompr.o: zlib.h zconf.h
+zutil.o: zutil.h zlib.h zconf.h  
diff --git a/zlib/Makefile.riscos b/zlib/Makefile.riscos
new file mode 100644
index 0000000..d97f449
--- /dev/null
+++ b/zlib/Makefile.riscos
@@ -0,0 +1,151 @@
+# Project:   zlib_1_03
+# Patched for zlib 1.1.2 rw@shadow.org.uk 19980430
+# test works out-of-the-box, installs `somewhere' on demand
+
+# Toolflags:
+CCflags = -c -depend !Depend -IC: -g -throwback  -DRISCOS  -fah 
+C++flags = -c -depend !Depend -IC: -throwback
+Linkflags = -aif -c++ -o $@ 
+ObjAsmflags = -throwback -NoCache -depend !Depend
+CMHGflags = 
+LibFileflags = -c -l -o $@ 
+Squeezeflags = -o $@
+
+# change the line below to where _you_ want the library installed.
+libdest = lib:zlib
+
+# Final targets:
+@.lib:   @.o.adler32 @.o.compress @.o.crc32 @.o.deflate @.o.gzio \
+        @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil @.o.trees \
+        @.o.uncompr @.o.zutil 
+        LibFile $(LibFileflags) @.o.adler32 @.o.compress @.o.crc32 @.o.deflate \
+        @.o.gzio @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil \
+        @.o.trees @.o.uncompr @.o.zutil 
+test:   @.minigzip @.example @.lib
+	@copy @.lib @.libc  A~C~DF~L~N~P~Q~RS~TV
+	@echo running tests: hang on.
+	@/@.minigzip -f -9 libc
+	@/@.minigzip -d libc-gz
+	@/@.minigzip -f -1 libc
+	@/@.minigzip -d libc-gz
+	@/@.minigzip -h -9 libc
+	@/@.minigzip -d libc-gz
+	@/@.minigzip -h -1 libc
+	@/@.minigzip -d libc-gz
+	@/@.minigzip -9 libc
+	@/@.minigzip -d libc-gz
+	@/@.minigzip -1 libc
+	@/@.minigzip -d libc-gz
+	@diff @.lib @.libc
+	@echo that should have reported '@.lib and @.libc identical' if you have diff.
+	@/@.example @.fred @.fred
+	@echo that will have given lots of hello!'s.
+
+@.minigzip:   @.o.minigzip @.lib C:o.Stubs 
+        Link $(Linkflags) @.o.minigzip @.lib C:o.Stubs 
+@.example:   @.o.example @.lib C:o.Stubs 
+        Link $(Linkflags) @.o.example @.lib C:o.Stubs
+
+install: @.lib
+	cdir $(libdest)
+	cdir $(libdest).h
+	@copy @.h.zlib $(libdest).h.zlib A~C~DF~L~N~P~Q~RS~TV
+	@copy @.h.zconf $(libdest).h.zconf A~C~DF~L~N~P~Q~RS~TV
+	@copy @.lib $(libdest).lib  A~C~DF~L~N~P~Q~RS~TV
+	@echo okay, installed zlib in $(libdest)
+
+clean:; remove @.minigzip
+	remove @.example
+	remove @.libc
+	-wipe @.o.* F~r~cV
+	remove @.fred
+
+# User-editable dependencies:
+.c.o:
+        cc $(ccflags) -o $@ $<
+
+# Static dependencies:
+
+# Dynamic dependencies:
+o.example:	c.example
+o.example:	h.zlib
+o.example:	h.zconf
+o.minigzip:	c.minigzip
+o.minigzip:	h.zlib
+o.minigzip:	h.zconf
+o.adler32:	c.adler32
+o.adler32:	h.zlib
+o.adler32:	h.zconf
+o.compress:	c.compress
+o.compress:	h.zlib
+o.compress:	h.zconf
+o.crc32:	c.crc32
+o.crc32:	h.zlib
+o.crc32:	h.zconf
+o.deflate:	c.deflate
+o.deflate:	h.deflate
+o.deflate:	h.zutil
+o.deflate:	h.zlib
+o.deflate:	h.zconf
+o.gzio:	c.gzio
+o.gzio:	h.zutil
+o.gzio:	h.zlib
+o.gzio:	h.zconf
+o.infblock:	c.infblock
+o.infblock:	h.zutil
+o.infblock:	h.zlib
+o.infblock:	h.zconf
+o.infblock:	h.infblock
+o.infblock:	h.inftrees
+o.infblock:	h.infcodes
+o.infblock:	h.infutil
+o.infcodes:	c.infcodes
+o.infcodes:	h.zutil
+o.infcodes:	h.zlib
+o.infcodes:	h.zconf
+o.infcodes:	h.inftrees
+o.infcodes:	h.infblock
+o.infcodes:	h.infcodes
+o.infcodes:	h.infutil
+o.infcodes:	h.inffast
+o.inffast:	c.inffast
+o.inffast:	h.zutil
+o.inffast:	h.zlib
+o.inffast:	h.zconf
+o.inffast:	h.inftrees
+o.inffast:	h.infblock
+o.inffast:	h.infcodes
+o.inffast:	h.infutil
+o.inffast:	h.inffast
+o.inflate:	c.inflate
+o.inflate:	h.zutil
+o.inflate:	h.zlib
+o.inflate:	h.zconf
+o.inflate:	h.infblock
+o.inftrees:	c.inftrees
+o.inftrees:	h.zutil
+o.inftrees:	h.zlib
+o.inftrees:	h.zconf
+o.inftrees:	h.inftrees
+o.inftrees:	h.inffixed
+o.infutil:	c.infutil
+o.infutil:	h.zutil
+o.infutil:	h.zlib
+o.infutil:	h.zconf
+o.infutil:	h.infblock
+o.infutil:	h.inftrees
+o.infutil:	h.infcodes
+o.infutil:	h.infutil
+o.trees:	c.trees
+o.trees:	h.deflate
+o.trees:	h.zutil
+o.trees:	h.zlib
+o.trees:	h.zconf
+o.trees:	h.trees
+o.uncompr:	c.uncompr
+o.uncompr:	h.zlib
+o.uncompr:	h.zconf
+o.zutil:	c.zutil
+o.zutil:	h.zutil
+o.zutil:	h.zlib
+o.zutil:	h.zconf
diff --git a/zlib/README b/zlib/README
new file mode 100644
index 0000000..29d6714
--- /dev/null
+++ b/zlib/README
@@ -0,0 +1,147 @@
+zlib 1.1.4 is a general purpose data compression library.  All the code
+is thread safe.  The data format used by the zlib library
+is described by RFCs (Request for Comments) 1950 to 1952 in the files 
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate
+format) and rfc1952.txt (gzip format). These documents are also available in
+other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact jloup@gzip.org). A usage
+example of the library is given in the file example.c which also tests that
+the library is working correctly. Another example is given in the file
+minigzip.c. The compression library itself is composed of all source files
+except example.c and minigzip.c.
+
+To compile all files and run the test program, follow the instructions
+given at the top of Makefile. In short "make test; make install"
+should work for most machines. For Unix: "./configure; make test; make install"
+For MSDOS, use one of the special makefiles such as Makefile.msc.
+For VMS, use Make_vms.com or descrip.mms.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to
+Gilles Vollant <info@winimage.com> for the Windows DLL version.
+The zlib home page is http://www.zlib.org or http://www.gzip.org/zlib/
+Before reporting a problem, please check this site to verify that
+you have the latest version of zlib; otherwise get the latest version and
+check whether the problem still exists or not.
+
+PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html
+before asking for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of  Dr. Dobb's Journal; a copy of the article is available in
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+
+The changes made in version 1.1.4 are documented in the file ChangeLog.
+The only changes made since 1.1.3 are bug corrections:
+
+- ZFREE was repeated on same allocation on some error conditions.
+  This creates a security problem described in
+  http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+  less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+  of 256 bytes. (A complete fix will be available in 1.1.5).
+
+The beta version 1.1.5beta includes many more changes. A new official
+version 1.1.5 will be released as soon as extensive testing has been
+completed on it.
+
+
+Unsupported third party contributions are provided in directory "contrib".
+
+A Java implementation of zlib is available in the Java Development Kit
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+See the zlib home page http://www.zlib.org for details.
+
+A Perl interface to zlib written by Paul Marquess <pmarquess@bfsec.bt.co.uk>
+is in the CPAN (Comprehensive Perl Archive Network) sites
+http://www.cpan.org/modules/by-module/Compress/
+
+A Python interface to zlib written by A.M. Kuchling <amk@magnet.com>
+is available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html
+
+A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com>
+is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html
+
+An experimental package to read and write files in .zip format,
+written on top of zlib by Gilles Vollant <info@winimage.com>, is
+available at http://www.winimage.com/zLibDll/unzip.html
+and also in the contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc
+  and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL
+  The zlib DLL support was initially done by Alessandro Iacopetti and is
+  now maintained by Gilles Vollant <info@winimage.com>. Check the zlib DLL
+  home page at http://www.winimage.com/zLibDll
+
+  From Visual Basic, you can call the DLL functions which do not take
+  a structure as argument: compress, uncompress and all gz* functions.
+  See contrib/visual-basic.txt for more information, or get
+  http://www.tcfb.com/dowseware/cmp-z-it.zip
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization.
+  With -O, one libpng test fails. The test works in 32 bit mode (with
+  the -n32 compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1   
+  it works when compiled with cc.
+
+- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1
+  is necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works
+  with other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
+
+- For Turbo C the small model is supported only with reduced performance to
+  avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
+
+- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html
+  Per Harald Myrvang <perm@stud.cs.uit.no>
+
+
+Acknowledgments:
+
+  The deflate format used by zlib was defined by Phil Katz. The deflate
+  and zlib specifications were written by L. Peter Deutsch. Thanks to all the
+  people who reported problems and suggested various improvements in zlib;
+  they are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind.  The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes.
diff --git a/zlib/adler32.c b/zlib/adler32.c
new file mode 100644
index 0000000..828a3ff
--- /dev/null
+++ b/zlib/adler32.c
@@ -0,0 +1,48 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: adler32.c,v 1.1 2004/10/08 09:44:21 const_k Exp $ */
+
+#include "zlib.h"
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+    uLong adler;
+    const Bytef *buf;
+    uInt len;
+{
+    unsigned long s1 = adler & 0xffff;
+    unsigned long s2 = (adler >> 16) & 0xffff;
+    int k;
+
+    if (buf == Z_NULL) return 1L;
+
+    while (len > 0) {
+        k = len < NMAX ? len : NMAX;
+        len -= k;
+        while (k >= 16) {
+            DO16(buf);
+	    buf += 16;
+            k -= 16;
+        }
+        if (k != 0) do {
+            s1 += *buf++;
+	    s2 += s1;
+        } while (--k);
+        s1 %= BASE;
+        s2 %= BASE;
+    }
+    return (s2 << 16) | s1;
+}
diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt
new file mode 100644
index 0000000..cdc830b
--- /dev/null
+++ b/zlib/algorithm.txt
@@ -0,0 +1,213 @@
+1. Compression algorithm (deflate)
+
+The deflation algorithm used by gzip (also zip and zlib) is a variation of
+LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
+the input data.  The second occurrence of a string is replaced by a
+pointer to the previous string, in the form of a pair (distance,
+length).  Distances are limited to 32K bytes, and lengths are limited
+to 258 bytes. When a string does not occur anywhere in the previous
+32K bytes, it is emitted as a sequence of literal bytes.  (In this
+description, `string' must be taken as an arbitrary sequence of bytes,
+and is not restricted to printable characters.)
+
+Literals or match lengths are compressed with one Huffman tree, and
+match distances are compressed with another tree. The trees are stored
+in a compact form at the start of each block. The blocks can have any
+size (except that the compressed data for one block must fit in
+available memory). A block is terminated when deflate() determines that
+it would be useful to start another block with fresh trees. (This is
+somewhat similar to the behavior of LZW-based _compress_.)
+
+Duplicated strings are found using a hash table. All input strings of
+length 3 are inserted in the hash table. A hash index is computed for
+the next 3 bytes. If the hash chain for this index is not empty, all
+strings in the chain are compared with the current input string, and
+the longest match is selected.
+
+The hash chains are searched starting with the most recent strings, to
+favor small distances and thus take advantage of the Huffman encoding.
+The hash chains are singly linked. There are no deletions from the
+hash chains, the algorithm simply discards matches that are too old.
+
+To avoid a worst-case situation, very long hash chains are arbitrarily
+truncated at a certain length, determined by a runtime option (level
+parameter of deflateInit). So deflate() does not always find the longest
+possible match but generally finds a match which is long enough.
+
+deflate() also defers the selection of matches with a lazy evaluation
+mechanism. After a match of length N has been found, deflate() searches for
+a longer match at the next input byte. If a longer match is found, the
+previous match is truncated to a length of one (thus producing a single
+literal byte) and the process of lazy evaluation begins again. Otherwise,
+the original match is kept, and the next match search is attempted only N
+steps later.
+
+The lazy match evaluation is also subject to a runtime parameter. If
+the current match is long enough, deflate() reduces the search for a longer
+match, thus speeding up the whole process. If compression ratio is more
+important than speed, deflate() attempts a complete second search even if
+the first match is already long enough.
+
+The lazy match evaluation is not performed for the fastest compression
+modes (level parameter 1 to 3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
+
+2. Decompression algorithm (inflate)
+
+2.1 Introduction
+
+The real question is, given a Huffman tree, how to decode fast.  The most
+important realization is that shorter codes are much more common than
+longer codes, so pay attention to decoding the short codes fast, and let
+the long codes take longer to decode.
+
+inflate() sets up a first level table that covers some number of bits of
+input less than the length of longest code.  It gets that many bits from the
+stream, and looks it up in the table.  The table will tell if the next
+code is that many bits or less and how many, and if it is, it will tell
+the value, else it will point to the next level table for which inflate()
+grabs more bits and tries to decode a longer code.
+
+How many bits to make the first lookup is a tradeoff between the time it
+takes to decode and the time it takes to build the table.  If building the
+table took no time (and if you had infinite memory), then there would only
+be a first level table to cover all the way to the longest code.  However,
+building the table ends up taking a lot longer for more bits since short
+codes are replicated many times in such a table.  What inflate() does is
+simply to make the number of bits in the first table a variable, and set it
+for the maximum speed.
+
+inflate() sends new trees relatively often, so it is possibly set for a
+smaller first level table than an application that has only one tree for
+all the data.  For inflate, which has 286 possible codes for the
+literal/length tree, the size of the first table is nine bits.  Also the
+distance trees have 30 possible values, and the size of the first table is
+six bits.  Note that for each of those cases, the table ended up one bit
+longer than the ``average'' code length, i.e. the code length of an
+approximately flat code which would be a little more than eight bits for
+286 symbols and a little less than five bits for 30 symbols.  It would be
+interesting to see if optimizing the first level table for other
+applications gave values within a bit or two of the flat code size.
+
+
+2.2 More details on the inflate table lookup
+
+Ok, you want to know what this cleverly obfuscated inflate tree actually  
+looks like.  You are correct that it's not a Huffman tree.  It is simply a  
+lookup table for the first, let's say, nine bits of a Huffman symbol.  The  
+symbol could be as short as one bit or as long as 15 bits.  If a particular  
+symbol is shorter than nine bits, then that symbol's translation is duplicated
+in all those entries that start with that symbol's bits.  For example, if the  
+symbol is four bits, then it's duplicated 32 times in a nine-bit table.  If a  
+symbol is nine bits long, it appears in the table once.
+
+If the symbol is longer than nine bits, then that entry in the table points  
+to another similar table for the remaining bits.  Again, there are duplicated  
+entries as needed.  The idea is that most of the time the symbol will be short
+and there will only be one table look up.  (That's whole idea behind data  
+compression in the first place.)  For the less frequent long symbols, there  
+will be two lookups.  If you had a compression method with really long  
+symbols, you could have as many levels of lookups as is efficient.  For  
+inflate, two is enough.
+
+So a table entry either points to another table (in which case nine bits in  
+the above example are gobbled), or it contains the translation for the symbol  
+and the number of bits to gobble.  Then you start again with the next  
+ungobbled bit.
+
+You may wonder: why not just have one lookup table for how ever many bits the  
+longest symbol is?  The reason is that if you do that, you end up spending  
+more time filling in duplicate symbol entries than you do actually decoding.   
+At least for deflate's output that generates new trees every several 10's of  
+kbytes.  You can imagine that filling in a 2^15 entry table for a 15-bit code  
+would take too long if you're only decoding several thousand symbols.  At the  
+other extreme, you could make a new table for every bit in the code.  In fact,
+that's essentially a Huffman tree.  But then you spend two much time  
+traversing the tree while decoding, even for short symbols.
+
+So the number of bits for the first lookup table is a trade of the time to  
+fill out the table vs. the time spent looking at the second level and above of
+the table.
+
+Here is an example, scaled down:
+
+The code being decoded, with 10 symbols, from 1 to 6 bits long:
+
+A: 0
+B: 10
+C: 1100
+D: 11010
+E: 11011
+F: 11100
+G: 11101
+H: 11110
+I: 111110
+J: 111111
+
+Let's make the first table three bits long (eight entries):
+
+000: A,1
+001: A,1
+010: A,1
+011: A,1
+100: B,2
+101: B,2
+110: -> table X (gobble 3 bits)
+111: -> table Y (gobble 3 bits)
+
+Each entry is what the bits decode to and how many bits that is, i.e. how  
+many bits to gobble.  Or the entry points to another table, with the number of
+bits to gobble implicit in the size of the table.
+
+Table X is two bits long since the longest code starting with 110 is five bits
+long:
+
+00: C,1
+01: C,1
+10: D,2
+11: E,2
+
+Table Y is three bits long since the longest code starting with 111 is six  
+bits long:
+
+000: F,2
+001: F,2
+010: G,2
+011: G,2
+100: H,2
+101: H,2
+110: I,3
+111: J,3
+
+So what we have here are three tables with a total of 20 entries that had to  
+be constructed.  That's compared to 64 entries for a single table.  Or  
+compared to 16 entries for a Huffman tree (six two entry tables and one four  
+entry table).  Assuming that the code ideally represents the probability of  
+the symbols, it takes on the average 1.25 lookups per symbol.  That's compared
+to one lookup for the single table, or 1.66 lookups per symbol for the  
+Huffman tree.
+
+There, I think that gives you a picture of what's going on.  For inflate, the  
+meaning of a particular symbol is often more than just a letter.  It can be a  
+byte (a "literal"), or it can be either a length or a distance which  
+indicates a base value and a number of bits to fetch after the code that is  
+added to the base value.  Or it might be the special end-of-block code.  The  
+data structures created in inftrees.c try to encode all that information  
+compactly in the tables.
+
+
+Jean-loup Gailly        Mark Adler
+jloup@gzip.org          madler@alumni.caltech.edu
+
+
+References:
+
+[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
+Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
+pp. 337-343.
+
+``DEFLATE Compressed Data Format Specification'' available in
+ftp://ds.internic.net/rfc/rfc1951.txt
diff --git a/zlib/compress.c b/zlib/compress.c
new file mode 100644
index 0000000..5a97749
--- /dev/null
+++ b/zlib/compress.c
@@ -0,0 +1,68 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: compress.c,v 1.1 2004/10/08 09:44:22 const_k Exp $ */
+
+#include "zlib.h"
+
+/* ===========================================================================
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+    int level;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = deflateInit(&stream, level);
+    if (err != Z_OK) return err;
+
+    err = deflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        deflateEnd(&stream);
+        return err == Z_OK ? Z_BUF_ERROR : err;
+    }
+    *destLen = stream.total_out;
+
+    err = deflateEnd(&stream);
+    return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
diff --git a/zlib/configure b/zlib/configure
new file mode 100755
index 0000000..e894235
--- /dev/null
+++ b/zlib/configure
@@ -0,0 +1,212 @@
+#!/bin/sh
+# configure script for zlib. This script is needed only if
+# you wish to build a shared library and your system supports them,
+# of if you need special compiler, flags or install directory.
+# Otherwise, you can just use directly "make test; make install"
+#
+# To create a shared library, use "configure --shared"; by default a static
+# library is created. If the primitive shared library support provided here
+# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz
+#
+# To impose specific compiler or flags or install directory, use for example:
+#    prefix=$HOME CC=cc CFLAGS="-O4" ./configure
+# or for csh/tcsh users:
+#    (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
+# LDSHARED is the command to be used to create a shared library
+
+# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
+# If you have problems, try without defining CC and CFLAGS before reporting
+# an error.
+
+LIBS=libz.a
+SHAREDLIB=libz.so
+VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+AR=${AR-"ar rc"}
+RANLIB=${RANLIB-"ranlib"}
+prefix=${prefix-/usr/local}
+exec_prefix=${exec_prefix-'${prefix}'}
+libdir=${libdir-'${exec_prefix}/lib'}
+includedir=${includedir-'${prefix}/include'}
+shared_ext='.so'
+shared=0
+gcc=0
+old_cc="$CC"
+old_cflags="$CFLAGS"
+
+while test $# -ge 1
+do
+case "$1" in
+    -h* | --h*)
+      echo 'usage:'
+      echo '  configure [--shared] [--prefix=PREFIX]  [--exec_prefix=EXPREFIX]'
+      echo '     [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
+        exit 0;;
+    -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
+    -p* | --p*) prefix="$2"; shift; shift;;
+    -e* | --e*) exec_prefix="$2"; shift; shift;;
+    -l* | --l*) libdir="$2"; shift; shift;;
+    -i* | --i*) includedir="$2"; shift; shift;;
+    -s* | --s*) shared=1; shift;;
+    esac
+done
+
+test=ztest$$
+cat > $test.c <<EOF
+extern int getchar();
+int hello() {return getchar();}
+EOF
+
+test -z "$CC" && echo Checking for gcc...
+cc=${CC-gcc}
+cflags=${CFLAGS-"-O3"}
+# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+case "$cc" in
+  *gcc*) gcc=1;;
+esac
+
+if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+  CC="$cc"
+  SFLAGS=${CFLAGS-"-fPIC -O3"}
+  CFLAGS="$cflags"
+  case `(uname -s || echo unknown) 2>/dev/null` in
+  Linux | linux) LDSHARED=${LDSHARED-"gcc -shared -Wl,-soname,libz.so.1"};;
+  *)             LDSHARED=${LDSHARED-"gcc -shared"};;
+  esac
+else
+  # find system name and corresponding cc options
+  CC=${CC-cc}
+  case `(uname -sr || echo unknown) 2>/dev/null` in
+  HP-UX*)    SFLAGS=${CFLAGS-"-O +z"}
+	     CFLAGS=${CFLAGS-"-O"}
+#	     LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
+	     LDSHARED=${LDSHARED-"ld -b"}
+	     shared_ext='.sl'
+	     SHAREDLIB='libz.sl';;
+  IRIX*)     SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
+	     CFLAGS=${CFLAGS-"-ansi -O2"}
+	     LDSHARED=${LDSHARED-"cc -shared"};;
+  OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
+	     CFLAGS=${CFLAGS-"-O -std1"}
+	     LDSHARED=${LDSHARED-"cc -shared  -Wl,-soname,$SHAREDLIB -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};;
+  OSF1*)     SFLAGS=${CFLAGS-"-O -std1"}
+	     CFLAGS=${CFLAGS-"-O -std1"}
+	     LDSHARED=${LDSHARED-"cc -shared"};;
+  QNX*)      SFLAGS=${CFLAGS-"-4 -O"}
+             CFLAGS=${CFLAGS-"-4 -O"}
+	     LDSHARED=${LDSHARED-"cc"}
+             RANLIB=${RANLIB-"true"}
+             AR="cc -A";;
+  SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
+	     CFLAGS=${CFLAGS-"-O3"}
+	     LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};;
+  SunOS\ 5*) SFLAGS=${CFLAGS-"-fast -xcg89 -KPIC -R."}
+             CFLAGS=${CFLAGS-"-fast -xcg89"}
+	     LDSHARED=${LDSHARED-"cc -G"};;
+  SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
+	     CFLAGS=${CFLAGS-"-O2"}
+	     LDSHARED=${LDSHARED-"ld"};;
+  UNIX_System_V\ 4.2.0) 
+	     SFLAGS=${CFLAGS-"-KPIC -O"}
+	     CFLAGS=${CFLAGS-"-O"}
+	     LDSHARED=${LDSHARED-"cc -G"};;
+  UNIX_SV\ 4.2MP)
+	     SFLAGS=${CFLAGS-"-Kconform_pic -O"}
+	     CFLAGS=${CFLAGS-"-O"}
+	     LDSHARED=${LDSHARED-"cc -G"};;
+  # send working options for other systems to support@gzip.org
+  *)         SFLAGS=${CFLAGS-"-O"}
+	     CFLAGS=${CFLAGS-"-O"}
+	     LDSHARED=${LDSHARED-"cc -shared"};;
+  esac
+fi
+
+if test $shared -eq 1; then
+  echo Checking for shared library support...
+  # we must test in two steps (cc then ld), required at least on SunOS 4.x
+  if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" &&
+     test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then
+    CFLAGS="$SFLAGS"
+    LIBS="$SHAREDLIB.$VER"
+    echo Building shared library $SHAREDLIB.$VER with $CC.
+  elif test -z "$old_cc" -a -z "$old_cflags"; then
+    echo No shared library suppport.
+    shared=0;
+  else
+    echo 'No shared library suppport; try without defining CC and CFLAGS'
+    shared=0;
+  fi
+fi
+if test $shared -eq 0; then
+  LDSHARED="$CC"
+  echo Building static library $LIBS version $VER with $CC.
+fi
+
+cat > $test.c <<EOF
+#include <unistd.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  CFLAGS="$CFLAGS -DHAVE_UNISTD_H"
+  echo "Checking for unistd.h... Yes."
+else
+  echo "Checking for unistd.h... No."
+fi
+
+cat > $test.c <<EOF
+#include <errno.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking for errno.h...	 Yes."
+else
+  echo "Checking for errno.h...	 No."
+  CFLAGS="$CFLAGS -DNO_ERRNO_H"
+fi
+
+cat > $test.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+caddr_t hello() {
+  return mmap((caddr_t)0, (off_t)0, PROT_READ, MAP_SHARED, 0, (off_t)0); 
+}
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  CFLAGS="$CFLAGS -DUSE_MMAP"
+  echo Checking for mmap support... Yes.
+else
+  echo Checking for mmap support... No.
+fi
+
+CPP=${CPP-"$CC -E"}
+case $CFLAGS in
+  *ASMV*)
+    if test "`nm $test.o | grep _hello`" = ""; then
+      CPP="$CPP -DNO_UNDERLINE"
+      echo Checking for underline in external names... No.
+    else
+      echo Checking for underline in external names... Yes.
+    fi;;
+esac
+
+rm -f $test.[co] $test$shared_ext
+
+# udpate Makefile
+sed < Makefile.in "
+/^CC *=/s%=.*%=$CC%
+/^CFLAGS *=/s%=.*%=$CFLAGS%
+/^CPP *=/s%=.*%=$CPP%
+/^LDSHARED *=/s%=.*%=$LDSHARED%
+/^LIBS *=/s%=.*%=$LIBS%
+/^SHAREDLIB *=/s%=.*%=$SHAREDLIB%
+/^AR *=/s%=.*%=$AR%
+/^RANLIB *=/s%=.*%=$RANLIB%
+/^VER *=/s%=.*%=$VER%
+/^prefix *=/s%=.*%=$prefix%
+/^exec_prefix *=/s%=.*%=$exec_prefix%
+/^libdir *=/s%=.*%=$libdir%
+/^includedir *=/s%=.*%=$includedir%
+" > Makefile
diff --git a/zlib/crc32.c b/zlib/crc32.c
new file mode 100644
index 0000000..b9a8a92
--- /dev/null
+++ b/zlib/crc32.c
@@ -0,0 +1,162 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: crc32.c,v 1.1 2004/10/08 09:44:23 const_k Exp $ */
+
+#include "zlib.h"
+
+#define local static
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local int crc_table_empty = 1;
+local uLongf crc_table[256];
+local void make_crc_table OF((void));
+
+/*
+  Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
+  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+  Polynomials over GF(2) are represented in binary, one bit per coefficient,
+  with the lowest powers in the most significant bit.  Then adding polynomials
+  is just exclusive-or, and multiplying a polynomial by x is a right shift by
+  one.  If we call the above polynomial p, and represent a byte as the
+  polynomial q, also with the lowest power in the most significant bit (so the
+  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+  where a mod b means the remainder after dividing a by b.
+
+  This calculation is done using the shift-register method of multiplying and
+  taking the remainder.  The register is initialized to zero, and for each
+  incoming bit, x^32 is added mod p to the register if the bit is a one (where
+  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+  x (which is shifting right by one and adding x^32 mod p if the bit shifted
+  out is a one).  We start with the highest power (least significant bit) of
+  q and repeat for all eight bits of q.
+
+  The table is simply the CRC of all possible eight bit values.  This is all
+  the information needed to generate CRC's on data a byte at a time for all
+  combinations of CRC register values and incoming bytes.
+*/
+local void make_crc_table()
+{
+  uLong c;
+  int n, k;
+  uLong poly;            /* polynomial exclusive-or pattern */
+  /* terms of polynomial defining this crc (except x^32): */
+  static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+  /* make exclusive-or pattern from polynomial (0xedb88320L) */
+  poly = 0L;
+  for (n = 0; n < sizeof(p)/sizeof(Byte); n++)
+    poly |= 1L << (31 - p[n]);
+ 
+  for (n = 0; n < 256; n++)
+  {
+    c = (uLong)n;
+    for (k = 0; k < 8; k++)
+      c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+    crc_table[n] = c;
+  }
+  crc_table_empty = 0;
+}
+#else
+/* ========================================================================
+ * Table of CRC-32's of all single-byte values (made by make_crc_table)
+ */
+local const uLongf crc_table[256] = {
+  0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+  0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+  0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+  0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+  0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+  0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+  0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+  0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+  0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+  0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+  0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+  0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+  0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+  0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+  0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+  0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+  0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+  0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+  0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+  0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+  0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+  0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+  0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+  0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+  0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+  0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+  0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+  0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+  0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+  0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+  0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+  0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+  0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+  0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+  0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+  0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+  0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+  0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+  0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+  0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+  0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+  0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+  0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+  0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+  0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+  0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+  0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+  0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+  0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+  0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+  0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+  0x2d02ef8dL
+};
+#endif
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const uLongf * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+  if (crc_table_empty) make_crc_table();
+#endif
+  return (const uLongf *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+#define DO2(buf)  DO1(buf); DO1(buf);
+#define DO4(buf)  DO2(buf); DO2(buf);
+#define DO8(buf)  DO4(buf); DO4(buf);
+
+/* ========================================================================= */
+uLong ZEXPORT crc32(crc, buf, len)
+    uLong crc;
+    const Bytef *buf;
+    uInt len;
+{
+    if (buf == Z_NULL) return 0L;
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+      make_crc_table();
+#endif
+    crc = crc ^ 0xffffffffL;
+    while (len >= 8)
+    {
+      DO8(buf);
+      len -= 8;
+    }
+    if (len) do {
+      DO1(buf);
+    } while (--len);
+    return crc ^ 0xffffffffL;
+}
diff --git a/zlib/deflate.c b/zlib/deflate.c
new file mode 100644
index 0000000..57a590c
--- /dev/null
+++ b/zlib/deflate.c
@@ -0,0 +1,1350 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process depends on being able to identify portions
+ *      of the input text which are identical to earlier input (within a
+ *      sliding window trailing behind the input currently being processed).
+ *
+ *      The most straightforward technique turns out to be the fastest for
+ *      most input files: try all possible matches and select the longest.
+ *      The key feature of this algorithm is that insertions into the string
+ *      dictionary are very simple and thus fast, and deletions are avoided
+ *      completely. Insertions are performed at each input character, whereas
+ *      string matches are performed only when the previous match ends. So it
+ *      is preferable to spend more time in matches to allow very fast string
+ *      insertions and avoid deletions. The matching algorithm for small
+ *      strings is inspired from that of Rabin & Karp. A brute force approach
+ *      is used to find longer strings when a small match has been found.
+ *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ *      (by Leonid Broukhis).
+ *         A previous version of this file used a more sophisticated algorithm
+ *      (by Fiala and Greene) which is guaranteed to run in linear amortized
+ *      time, but has a larger average cost, uses more memory and is patented.
+ *      However the F&G algorithm may be faster for some highly redundant
+ *      files if the parameter max_chain_length (described below) is too large.
+ *
+ *  ACKNOWLEDGEMENTS
+ *
+ *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ *      I found it in 'freeze' written by Leonid Broukhis.
+ *      Thanks to many people for bug reports and testing.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ *      Available in ftp://ds.internic.net/rfc/rfc1951.txt
+ *
+ *      A description of the Rabin and Karp algorithm is given in the book
+ *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ *      Fiala,E.R., and Greene,D.H.
+ *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id: deflate.c,v 1.1 2004/10/08 09:44:24 const_k Exp $ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+   " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ *  Function prototypes.
+ */
+typedef enum {
+    need_more,      /* block not completed, need more input or more output */
+    block_done,     /* block flush performed */
+    finish_started, /* finish started, need only more output at next deflate */
+    finish_done     /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window    OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast   OF((deflate_state *s, int flush));
+local block_state deflate_slow   OF((deflate_state *s, int flush));
+local void lm_init        OF((deflate_state *s));
+local void putShortMSB    OF((deflate_state *s, uInt b));
+local void flush_pending  OF((z_streamp strm));
+local int read_buf        OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifdef ASMV
+      void match_init OF((void)); /* asm code initialization */
+      uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG
+local  void check_match OF((deflate_state *s, IPos start, IPos match,
+                            int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+   ush good_length; /* reduce lazy search above this match length */
+   ush max_lazy;    /* do not perform lazy search above this match length */
+   ush nice_length; /* quit search above this match length */
+   ush max_chain;
+   compress_func func;
+} config;
+
+local const config configuration_table[10] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}, /* maximum speed, no lazy matches */
+/* 2 */ {4,    5, 16,    8, deflate_fast},
+/* 3 */ {4,    6, 32,   32, deflate_fast},
+
+/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
+/* 5 */ {8,   16, 32,   32, deflate_slow},
+/* 6 */ {8,   16, 128, 128, deflate_slow},
+/* 7 */ {8,   32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ *    input characters, so that a running hash key can be computed from the
+ *    previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ *    input characters and the first MIN_MATCH bytes of str are valid
+ *    (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+    s->head[s->hash_size-1] = NIL; \
+    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+    z_streamp strm;
+    int level;
+    const char *version;
+    int stream_size;
+{
+    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+			 Z_DEFAULT_STRATEGY, version, stream_size);
+    /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+		  version, stream_size)
+    z_streamp strm;
+    int  level;
+    int  method;
+    int  windowBits;
+    int  memLevel;
+    int  strategy;
+    const char *version;
+    int stream_size;
+{
+    deflate_state *s;
+    int noheader = 0;
+    static const char* my_version = ZLIB_VERSION;
+
+    ushf *overlay;
+    /* We overlay pending_buf and d_buf+l_buf. This works since the average
+     * output size for (length,distance) codes is <= 24 bits.
+     */
+
+    if (version == Z_NULL || version[0] != my_version[0] ||
+        stream_size != sizeof(z_stream)) {
+	return Z_VERSION_ERROR;
+    }
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+    strm->msg = Z_NULL;
+    if (strm->zalloc == Z_NULL) {
+	strm->zalloc = zcalloc;
+	strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == Z_NULL) strm->zfree = zcfree;
+
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#ifdef FASTEST
+    level = 1;
+#endif
+
+    if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+        noheader = 1;
+        windowBits = -windowBits;
+    }
+    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+        windowBits < 9 || windowBits > 15 || level < 0 || level > 9 ||
+	strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+        return Z_STREAM_ERROR;
+    }
+    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+    if (s == Z_NULL) return Z_MEM_ERROR;
+    strm->state = (struct internal_state FAR *)s;
+    s->strm = strm;
+
+    s->noheader = noheader;
+    s->w_bits = windowBits;
+    s->w_size = 1 << s->w_bits;
+    s->w_mask = s->w_size - 1;
+
+    s->hash_bits = memLevel + 7;
+    s->hash_size = 1 << s->hash_bits;
+    s->hash_mask = s->hash_size - 1;
+    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));
+    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+    s->pending_buf = (uchf *) overlay;
+    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+        s->pending_buf == Z_NULL) {
+        strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+        deflateEnd (strm);
+        return Z_MEM_ERROR;
+    }
+    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+    s->level = level;
+    s->strategy = strategy;
+    s->method = (Byte)method;
+
+    return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+    z_streamp strm;
+    const Bytef *dictionary;
+    uInt  dictLength;
+{
+    deflate_state *s;
+    uInt length = dictLength;
+    uInt n;
+    IPos hash_head = 0;
+
+    if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+        strm->state->status != INIT_STATE) return Z_STREAM_ERROR;
+
+    s = strm->state;
+    strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+    if (length < MIN_MATCH) return Z_OK;
+    if (length > MAX_DIST(s)) {
+	length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+	dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+    }
+    zmemcpy(s->window, dictionary, length);
+    s->strstart = length;
+    s->block_start = (long)length;
+
+    /* Insert all strings in the hash table (except for the last two bytes).
+     * s->lookahead stays null, so s->ins_h will be recomputed at the next
+     * call of fill_window.
+     */
+    s->ins_h = s->window[0];
+    UPDATE_HASH(s, s->ins_h, s->window[1]);
+    for (n = 0; n <= length - MIN_MATCH; n++) {
+	INSERT_STRING(s, n, hash_head);
+    }
+    if (hash_head) hash_head = 0;  /* to make compiler happy */
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+    z_streamp strm;
+{
+    deflate_state *s;
+    
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+        strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+    strm->total_in = strm->total_out = 0;
+    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+    strm->data_type = Z_UNKNOWN;
+
+    s = (deflate_state *)strm->state;
+    s->pending = 0;
+    s->pending_out = s->pending_buf;
+
+    if (s->noheader < 0) {
+        s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+    }
+    s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+    strm->adler = 1;
+    s->last_flush = Z_NO_FLUSH;
+
+    _tr_init(s);
+    lm_init(s);
+
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+    z_streamp strm;
+    int level;
+    int strategy;
+{
+    deflate_state *s;
+    compress_func func;
+    int err = Z_OK;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    s = strm->state;
+
+    if (level == Z_DEFAULT_COMPRESSION) {
+	level = 6;
+    }
+    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+	return Z_STREAM_ERROR;
+    }
+    func = configuration_table[s->level].func;
+
+    if (func != configuration_table[level].func && strm->total_in != 0) {
+	/* Flush the last buffer: */
+	err = deflate(strm, Z_PARTIAL_FLUSH);
+    }
+    if (s->level != level) {
+	s->level = level;
+	s->max_lazy_match   = configuration_table[level].max_lazy;
+	s->good_match       = configuration_table[level].good_length;
+	s->nice_match       = configuration_table[level].nice_length;
+	s->max_chain_length = configuration_table[level].max_chain;
+    }
+    s->strategy = strategy;
+    return err;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+    deflate_state *s;
+    uInt b;
+{
+    put_byte(s, (Byte)(b >> 8));
+    put_byte(s, (Byte)(b & 0xff));
+}   
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+    z_streamp strm;
+{
+    unsigned len = strm->state->pending;
+
+    if (len > strm->avail_out) len = strm->avail_out;
+    if (len == 0) return;
+
+    zmemcpy(strm->next_out, strm->state->pending_out, len);
+    strm->next_out  += len;
+    strm->state->pending_out  += len;
+    strm->total_out += len;
+    strm->avail_out  -= len;
+    strm->state->pending -= len;
+    if (strm->state->pending == 0) {
+        strm->state->pending_out = strm->state->pending_buf;
+    }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+    z_streamp strm;
+    int flush;
+{
+    int old_flush; /* value of flush param for previous deflate call */
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+	flush > Z_FINISH || flush < 0) {
+        return Z_STREAM_ERROR;
+    }
+    s = strm->state;
+
+    if (strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+	(s->status == FINISH_STATE && flush != Z_FINISH)) {
+        ERR_RETURN(strm, Z_STREAM_ERROR);
+    }
+    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+    s->strm = strm; /* just in case */
+    old_flush = s->last_flush;
+    s->last_flush = flush;
+
+    /* Write the zlib header */
+    if (s->status == INIT_STATE) {
+
+        uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+        uInt level_flags = (s->level-1) >> 1;
+
+        if (level_flags > 3) level_flags = 3;
+        header |= (level_flags << 6);
+	if (s->strstart != 0) header |= PRESET_DICT;
+        header += 31 - (header % 31);
+
+        s->status = BUSY_STATE;
+        putShortMSB(s, header);
+
+	/* Save the adler32 of the preset dictionary: */
+	if (s->strstart != 0) {
+	    putShortMSB(s, (uInt)(strm->adler >> 16));
+	    putShortMSB(s, (uInt)(strm->adler & 0xffff));
+	}
+	strm->adler = 1L;
+    }
+
+    /* Flush as much pending output as possible */
+    if (s->pending != 0) {
+        flush_pending(strm);
+        if (strm->avail_out == 0) {
+	    /* Since avail_out is 0, deflate will be called again with
+	     * more output space, but possibly with both pending and
+	     * avail_in equal to zero. There won't be anything to do,
+	     * but this is not an error situation so make sure we
+	     * return OK instead of BUF_ERROR at next call of deflate:
+             */
+	    s->last_flush = -1;
+	    return Z_OK;
+	}
+
+    /* Make sure there is something to do and avoid duplicate consecutive
+     * flushes. For repeated and useless calls with Z_FINISH, we keep
+     * returning Z_STREAM_END instead of Z_BUFF_ERROR.
+     */
+    } else if (strm->avail_in == 0 && flush <= old_flush &&
+	       flush != Z_FINISH) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* User must not provide more input after the first FINISH: */
+    if (s->status == FINISH_STATE && strm->avail_in != 0) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* Start a new block or continue the current one.
+     */
+    if (strm->avail_in != 0 || s->lookahead != 0 ||
+        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+        block_state bstate;
+
+	bstate = (*(configuration_table[s->level].func))(s, flush);
+
+        if (bstate == finish_started || bstate == finish_done) {
+            s->status = FINISH_STATE;
+        }
+        if (bstate == need_more || bstate == finish_started) {
+	    if (strm->avail_out == 0) {
+	        s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+	    }
+	    return Z_OK;
+	    /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+	     * of deflate should use the same flush parameter to make sure
+	     * that the flush is complete. So we don't have to output an
+	     * empty block here, this will be done at next call. This also
+	     * ensures that for a very small output buffer, we emit at most
+	     * one empty block.
+	     */
+	}
+        if (bstate == block_done) {
+            if (flush == Z_PARTIAL_FLUSH) {
+                _tr_align(s);
+            } else { /* FULL_FLUSH or SYNC_FLUSH */
+                _tr_stored_block(s, (char*)0, 0L, 0);
+                /* For a full flush, this empty block will be recognized
+                 * as a special marker by inflate_sync().
+                 */
+                if (flush == Z_FULL_FLUSH) {
+                    CLEAR_HASH(s);             /* forget history */
+                }
+            }
+            flush_pending(strm);
+	    if (strm->avail_out == 0) {
+	      s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+	      return Z_OK;
+	    }
+        }
+    }
+    Assert(strm->avail_out > 0, "bug2");
+
+    if (flush != Z_FINISH) return Z_OK;
+    if (s->noheader) return Z_STREAM_END;
+
+    /* Write the zlib trailer (adler32) */
+    putShortMSB(s, (uInt)(strm->adler >> 16));
+    putShortMSB(s, (uInt)(strm->adler & 0xffff));
+    flush_pending(strm);
+    /* If avail_out is zero, the application will call deflate again
+     * to flush the rest.
+     */
+    s->noheader = -1; /* write the trailer only once! */
+    return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+    z_streamp strm;
+{
+    int status;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+    status = strm->state->status;
+    if (status != INIT_STATE && status != BUSY_STATE &&
+	status != FINISH_STATE) {
+      return Z_STREAM_ERROR;
+    }
+
+    /* Deallocate in reverse order of allocations: */
+    TRY_FREE(strm, strm->state->pending_buf);
+    TRY_FREE(strm, strm->state->head);
+    TRY_FREE(strm, strm->state->prev);
+    TRY_FREE(strm, strm->state->window);
+
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+
+    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+    z_streamp dest;
+    z_streamp source;
+{
+#ifdef MAXSEG_64K
+    return Z_STREAM_ERROR;
+#else
+    deflate_state *ds;
+    deflate_state *ss;
+    ushf *overlay;
+
+
+    if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+        return Z_STREAM_ERROR;
+    }
+
+    ss = source->state;
+
+    *dest = *source;
+
+    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+    if (ds == Z_NULL) return Z_MEM_ERROR;
+    dest->state = (struct internal_state FAR *) ds;
+    *ds = *ss;
+    ds->strm = dest;
+
+    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));
+    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));
+    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+    ds->pending_buf = (uchf *) overlay;
+
+    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+        ds->pending_buf == Z_NULL) {
+        deflateEnd (dest);
+        return Z_MEM_ERROR;
+    }
+    /* following zmemcpy do not work for 16-bit MSDOS */
+    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+    zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+    zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+    ds->l_desc.dyn_tree = ds->dyn_ltree;
+    ds->d_desc.dyn_tree = ds->dyn_dtree;
+    ds->bl_desc.dyn_tree = ds->bl_tree;
+
+    return Z_OK;
+#endif
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.  All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+    z_streamp strm;
+    Bytef *buf;
+    unsigned size;
+{
+    unsigned len = strm->avail_in;
+
+    if (len > size) len = size;
+    if (len == 0) return 0;
+
+    strm->avail_in  -= len;
+
+    if (!strm->state->noheader) {
+        strm->adler = adler32(strm->adler, strm->next_in, len);
+    }
+    zmemcpy(buf, strm->next_in, len);
+    strm->next_in  += len;
+    strm->total_in += len;
+
+    return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+    deflate_state *s;
+{
+    s->window_size = (ulg)2L*s->w_size;
+
+    CLEAR_HASH(s);
+
+    /* Set the default configuration parameters:
+     */
+    s->max_lazy_match   = configuration_table[s->level].max_lazy;
+    s->good_match       = configuration_table[s->level].good_length;
+    s->nice_match       = configuration_table[s->level].nice_length;
+    s->max_chain_length = configuration_table[s->level].max_chain;
+
+    s->strstart = 0;
+    s->block_start = 0L;
+    s->lookahead = 0;
+    s->match_length = s->prev_length = MIN_MATCH-1;
+    s->match_available = 0;
+    s->ins_h = 0;
+#ifdef ASMV
+    match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+#ifndef FASTEST
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    unsigned chain_length = s->max_chain_length;/* max hash chain length */
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    int best_len = s->prev_length;              /* best match length so far */
+    int nice_match = s->nice_match;             /* stop if match long enough */
+    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+        s->strstart - (IPos)MAX_DIST(s) : NIL;
+    /* Stop when cur_match becomes <= limit. To simplify the code,
+     * we prevent matches with the string of window index 0.
+     */
+    Posf *prev = s->prev;
+    uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+    /* Compare two bytes at a time. Note: this is not always beneficial.
+     * Try with and without -DUNALIGNED_OK to check.
+     */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+    register ush scan_start = *(ushf*)scan;
+    register ush scan_end   = *(ushf*)(scan+best_len-1);
+#else
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    register Byte scan_end1  = scan[best_len-1];
+    register Byte scan_end   = scan[best_len];
+#endif
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    /* Do not waste too much time if we already have a good match: */
+    if (s->prev_length >= s->good_match) {
+        chain_length >>= 2;
+    }
+    /* Do not look for matches beyond the end of the input. This is necessary
+     * to make deflate deterministic.
+     */
+    if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    do {
+        Assert(cur_match < s->strstart, "no future");
+        match = s->window + cur_match;
+
+        /* Skip to next match if the match length cannot increase
+         * or if the match length is less than 2:
+         */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+        /* This code assumes sizeof(unsigned short) == 2. Do not use
+         * UNALIGNED_OK if your compiler uses a different size.
+         */
+        if (*(ushf*)(match+best_len-1) != scan_end ||
+            *(ushf*)match != scan_start) continue;
+
+        /* It is not necessary to compare scan[2] and match[2] since they are
+         * always equal when the other bytes match, given that the hash keys
+         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+         * strstart+3, +5, ... up to strstart+257. We check for insufficient
+         * lookahead only every 4th comparison; the 128th check will be made
+         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+         * necessary to put more guard bytes at the end of the window, or
+         * to check more often for insufficient lookahead.
+         */
+        Assert(scan[2] == match[2], "scan[2]?");
+        scan++, match++;
+        do {
+        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 scan < strend);
+        /* The funny "do {}" generates better code on most compilers */
+
+        /* Here, scan <= window+strstart+257 */
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+        if (*scan == *match) scan++;
+
+        len = (MAX_MATCH - 1) - (int)(strend-scan);
+        scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+        if (match[best_len]   != scan_end  ||
+            match[best_len-1] != scan_end1 ||
+            *match            != *scan     ||
+            *++match          != scan[1])      continue;
+
+        /* The check at best_len-1 can be removed because it will be made
+         * again later. (This heuristic is not always a win.)
+         * It is not necessary to compare scan[2] and match[2] since they
+         * are always equal when the other bytes match, given that
+         * the hash keys are equal and that HASH_BITS >= 8.
+         */
+        scan += 2, match++;
+        Assert(*scan == *match, "match[2]?");
+
+        /* We check for insufficient lookahead only every 8th comparison;
+         * the 256th check will be made at strstart+258.
+         */
+        do {
+        } while (*++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 scan < strend);
+
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+        len = MAX_MATCH - (int)(strend - scan);
+        scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+        if (len > best_len) {
+            s->match_start = cur_match;
+            best_len = len;
+            if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+            scan_end = *(ushf*)(scan+best_len-1);
+#else
+            scan_end1  = scan[best_len-1];
+            scan_end   = scan[best_len];
+#endif
+        }
+    } while ((cur_match = prev[cur_match & wmask]) > limit
+             && --chain_length != 0);
+
+    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+    return s->lookahead;
+}
+
+#else /* FASTEST */
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 only
+ */
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    Assert(cur_match < s->strstart, "no future");
+
+    match = s->window + cur_match;
+
+    /* Return failure if the match length is less than 2:
+     */
+    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+    /* The check at best_len-1 can be removed because it will be made
+     * again later. (This heuristic is not always a win.)
+     * It is not necessary to compare scan[2] and match[2] since they
+     * are always equal when the other bytes match, given that
+     * the hash keys are equal and that HASH_BITS >= 8.
+     */
+    scan += 2, match += 2;
+    Assert(*scan == *match, "match[2]?");
+
+    /* We check for insufficient lookahead only every 8th comparison;
+     * the 256th check will be made at strstart+258.
+     */
+    do {
+    } while (*++scan == *++match && *++scan == *++match &&
+	     *++scan == *++match && *++scan == *++match &&
+	     *++scan == *++match && *++scan == *++match &&
+	     *++scan == *++match && *++scan == *++match &&
+	     scan < strend);
+
+    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+    len = MAX_MATCH - (int)(strend - scan);
+
+    if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+    s->match_start = cur_match;
+    return len <= s->lookahead ? len : s->lookahead;
+}
+#endif /* FASTEST */
+#endif /* ASMV */
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+    deflate_state *s;
+    IPos start, match;
+    int length;
+{
+    /* check that the match is indeed a match */
+    if (zmemcmp(s->window + match,
+                s->window + start, length) != EQUAL) {
+        fprintf(stderr, " start %u, match %u, length %d\n",
+		start, match, length);
+        do {
+	    fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+	} while (--length != 0);
+        z_error("invalid match");
+    }
+    if (z_verbose > 1) {
+        fprintf(stderr,"\\[%d,%d]", start-match, length);
+        do { putc(s->window[start++], stderr); } while (--length != 0);
+    }
+}
+#else
+#  define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ *    At least one byte has been read, or avail_in == 0; reads are
+ *    performed for at least two bytes (required for the zip translate_eol
+ *    option -- not supported here).
+ */
+local void fill_window(s)
+    deflate_state *s;
+{
+    register unsigned n, m;
+    register Posf *p;
+    unsigned more;    /* Amount of free space at the end of the window. */
+    uInt wsize = s->w_size;
+
+    do {
+        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+        /* Deal with !@#$% 64K limit: */
+        if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+            more = wsize;
+
+        } else if (more == (unsigned)(-1)) {
+            /* Very unlikely, but possible on 16 bit machine if strstart == 0
+             * and lookahead == 1 (input done one byte at time)
+             */
+            more--;
+
+        /* If the window is almost full and there is insufficient lookahead,
+         * move the upper half to the lower one to make room in the upper half.
+         */
+        } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+            zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+            s->match_start -= wsize;
+            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */
+            s->block_start -= (long) wsize;
+
+            /* Slide the hash table (could be avoided with 32 bit values
+               at the expense of memory usage). We slide even when level == 0
+               to keep the hash table consistent if we switch back to level > 0
+               later. (Using level 0 permanently is not an optimal usage of
+               zlib, so we don't care about this pathological case.)
+             */
+	    n = s->hash_size;
+	    p = &s->head[n];
+	    do {
+		m = *--p;
+		*p = (Pos)(m >= wsize ? m-wsize : NIL);
+	    } while (--n);
+
+	    n = wsize;
+#ifndef FASTEST
+	    p = &s->prev[n];
+	    do {
+		m = *--p;
+		*p = (Pos)(m >= wsize ? m-wsize : NIL);
+		/* If n is not on any hash chain, prev[n] is garbage but
+		 * its value will never be used.
+		 */
+	    } while (--n);
+#endif
+            more += wsize;
+        }
+        if (s->strm->avail_in == 0) return;
+
+        /* If there was no sliding:
+         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+         *    more == window_size - lookahead - strstart
+         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+         * => more >= window_size - 2*WSIZE + 2
+         * In the BIG_MEM or MMAP case (not yet supported),
+         *   window_size == input_size + MIN_LOOKAHEAD  &&
+         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+         * Otherwise, window_size == 2*WSIZE so more >= 2.
+         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+         */
+        Assert(more >= 2, "more < 2");
+
+        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+        s->lookahead += n;
+
+        /* Initialize the hash value now that we have some input: */
+        if (s->lookahead >= MIN_MATCH) {
+            s->ins_h = s->window[s->strstart];
+            UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+            Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+        }
+        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+         * but this is not important since only literal bytes will be emitted.
+         */
+
+    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+   _tr_flush_block(s, (s->block_start >= 0L ? \
+                   (charf *)&s->window[(unsigned)s->block_start] : \
+                   (charf *)Z_NULL), \
+		(ulg)((long)s->strstart - s->block_start), \
+		(eof)); \
+   s->block_start = s->strstart; \
+   flush_pending(s->strm); \
+   Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+   FLUSH_BLOCK_ONLY(s, eof); \
+   if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+     * to pending_buf_size, and each stored block has a 5 byte header:
+     */
+    ulg max_block_size = 0xffff;
+    ulg max_start;
+
+    if (max_block_size > s->pending_buf_size - 5) {
+        max_block_size = s->pending_buf_size - 5;
+    }
+
+    /* Copy as much as possible from input to output: */
+    for (;;) {
+        /* Fill the window as much as possible: */
+        if (s->lookahead <= 1) {
+
+            Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+		   s->block_start >= (long)s->w_size, "slide too late");
+
+            fill_window(s);
+            if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+	Assert(s->block_start >= 0L, "block gone");
+
+	s->strstart += s->lookahead;
+	s->lookahead = 0;
+
+	/* Emit a stored block if pending_buf will be full: */
+ 	max_start = s->block_start + max_block_size;
+        if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+	    /* strstart == 0 is possible when wraparound on 16-bit machine */
+	    s->lookahead = (uInt)(s->strstart - max_start);
+	    s->strstart = (uInt)max_start;
+            FLUSH_BLOCK(s, 0);
+	}
+	/* Flush if we may have to slide, otherwise block_start may become
+         * negative and the data will be gone:
+         */
+        if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+            FLUSH_BLOCK(s, 0);
+	}
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL; /* head of the hash chain */
+    int bflush;           /* set if current block must be flushed */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+	        return need_more;
+	    }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         * At this point we have always match_length < MIN_MATCH
+         */
+        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            if (s->strategy != Z_HUFFMAN_ONLY) {
+                s->match_length = longest_match (s, hash_head);
+            }
+            /* longest_match() sets match_start */
+        }
+        if (s->match_length >= MIN_MATCH) {
+            check_match(s, s->strstart, s->match_start, s->match_length);
+
+            _tr_tally_dist(s, s->strstart - s->match_start,
+                           s->match_length - MIN_MATCH, bflush);
+
+            s->lookahead -= s->match_length;
+
+            /* Insert new strings in the hash table only if the match length
+             * is not too large. This saves time but degrades compression.
+             */
+#ifndef FASTEST
+            if (s->match_length <= s->max_insert_length &&
+                s->lookahead >= MIN_MATCH) {
+                s->match_length--; /* string at strstart already in hash table */
+                do {
+                    s->strstart++;
+                    INSERT_STRING(s, s->strstart, hash_head);
+                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                     * always MIN_MATCH bytes ahead.
+                     */
+                } while (--s->match_length != 0);
+                s->strstart++; 
+            } else
+#endif
+	    {
+                s->strstart += s->match_length;
+                s->match_length = 0;
+                s->ins_h = s->window[s->strstart];
+                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+                Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+                 * matter since it will be recomputed at next deflate call.
+                 */
+            }
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++; 
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL;    /* head of hash chain */
+    int bflush;              /* set if current block must be flushed */
+
+    /* Process the input block. */
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+	        return need_more;
+	    }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         */
+        s->prev_length = s->match_length, s->prev_match = s->match_start;
+        s->match_length = MIN_MATCH-1;
+
+        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+            s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            if (s->strategy != Z_HUFFMAN_ONLY) {
+                s->match_length = longest_match (s, hash_head);
+            }
+            /* longest_match() sets match_start */
+
+            if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+                 (s->match_length == MIN_MATCH &&
+                  s->strstart - s->match_start > TOO_FAR))) {
+
+                /* If prev_match is also MIN_MATCH, match_start is garbage
+                 * but we will ignore the current match anyway.
+                 */
+                s->match_length = MIN_MATCH-1;
+            }
+        }
+        /* If there was a match at the previous step and the current
+         * match is not better, output the previous match:
+         */
+        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+            /* Do not insert strings in hash table beyond this. */
+
+            check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+			   s->prev_length - MIN_MATCH, bflush);
+
+            /* Insert in hash table all strings up to the end of the match.
+             * strstart-1 and strstart are already inserted. If there is not
+             * enough lookahead, the last two strings are not inserted in
+             * the hash table.
+             */
+            s->lookahead -= s->prev_length-1;
+            s->prev_length -= 2;
+            do {
+                if (++s->strstart <= max_insert) {
+                    INSERT_STRING(s, s->strstart, hash_head);
+                }
+            } while (--s->prev_length != 0);
+            s->match_available = 0;
+            s->match_length = MIN_MATCH-1;
+            s->strstart++;
+
+            if (bflush) FLUSH_BLOCK(s, 0);
+
+        } else if (s->match_available) {
+            /* If there was no match at the previous position, output a
+             * single literal. If there was a match but the current match
+             * is longer, truncate the previous match to a single literal.
+             */
+            Tracevv((stderr,"%c", s->window[s->strstart-1]));
+	    _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+	    if (bflush) {
+                FLUSH_BLOCK_ONLY(s, 0);
+            }
+            s->strstart++;
+            s->lookahead--;
+            if (s->strm->avail_out == 0) return need_more;
+        } else {
+            /* There is no previous match to compare with, wait for
+             * the next step to decide.
+             */
+            s->match_available = 1;
+            s->strstart++;
+            s->lookahead--;
+        }
+    }
+    Assert (flush != Z_NO_FLUSH, "no flush?");
+    if (s->match_available) {
+        Tracevv((stderr,"%c", s->window[s->strstart-1]));
+        _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+        s->match_available = 0;
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
diff --git a/zlib/deflate.h b/zlib/deflate.h
new file mode 100644
index 0000000..39cfaeb
--- /dev/null
+++ b/zlib/deflate.h
@@ -0,0 +1,318 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: deflate.h,v 1.1 2004/10/08 09:44:24 const_k Exp $ */
+
+#ifndef _DEFLATE_H
+#define _DEFLATE_H
+
+#include "zutil.h"
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE    42
+#define BUSY_STATE   113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+    union {
+        ush  freq;       /* frequency count */
+        ush  code;       /* bit string */
+    } fc;
+    union {
+        ush  dad;        /* father node in Huffman tree */
+        ush  len;        /* length of bit string */
+    } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+typedef struct static_tree_desc_s  static_tree_desc;
+
+typedef struct tree_desc_s {
+    ct_data *dyn_tree;           /* the dynamic tree */
+    int     max_code;            /* largest code with non zero frequency */
+    static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+    z_streamp strm;      /* pointer back to this zlib stream */
+    int   status;        /* as the name implies */
+    Bytef *pending_buf;  /* output still pending */
+    ulg   pending_buf_size; /* size of pending_buf */
+    Bytef *pending_out;  /* next pending byte to output to the stream */
+    int   pending;       /* nb of bytes in the pending buffer */
+    int   noheader;      /* suppress zlib header and adler32 */
+    Byte  data_type;     /* UNKNOWN, BINARY or ASCII */
+    Byte  method;        /* STORED (for zip only) or DEFLATED */
+    int   last_flush;    /* value of flush param for previous deflate call */
+
+                /* used by deflate.c: */
+
+    uInt  w_size;        /* LZ77 window size (32K by default) */
+    uInt  w_bits;        /* log2(w_size)  (8..16) */
+    uInt  w_mask;        /* w_size - 1 */
+
+    Bytef *window;
+    /* Sliding window. Input bytes are read into the second half of the window,
+     * and move to the first half later to keep a dictionary of at least wSize
+     * bytes. With this organization, matches are limited to a distance of
+     * wSize-MAX_MATCH bytes, but this ensures that IO is always
+     * performed with a length multiple of the block size. Also, it limits
+     * the window size to 64K, which is quite useful on MSDOS.
+     * To do: use the user input buffer as sliding window.
+     */
+
+    ulg window_size;
+    /* Actual size of window: 2*wSize, except when the user input buffer
+     * is directly used as sliding window.
+     */
+
+    Posf *prev;
+    /* Link to older string with same hash index. To limit the size of this
+     * array to 64K, this link is maintained only for the last 32K strings.
+     * An index in this array is thus a window index modulo 32K.
+     */
+
+    Posf *head; /* Heads of the hash chains or NIL. */
+
+    uInt  ins_h;          /* hash index of string to be inserted */
+    uInt  hash_size;      /* number of elements in hash table */
+    uInt  hash_bits;      /* log2(hash_size) */
+    uInt  hash_mask;      /* hash_size-1 */
+
+    uInt  hash_shift;
+    /* Number of bits by which ins_h must be shifted at each input
+     * step. It must be such that after MIN_MATCH steps, the oldest
+     * byte no longer takes part in the hash key, that is:
+     *   hash_shift * MIN_MATCH >= hash_bits
+     */
+
+    long block_start;
+    /* Window position at the beginning of the current output block. Gets
+     * negative when the window is moved backwards.
+     */
+
+    uInt match_length;           /* length of best match */
+    IPos prev_match;             /* previous match */
+    int match_available;         /* set if previous match exists */
+    uInt strstart;               /* start of string to insert */
+    uInt match_start;            /* start of matching string */
+    uInt lookahead;              /* number of valid bytes ahead in window */
+
+    uInt prev_length;
+    /* Length of the best match at previous step. Matches not greater than this
+     * are discarded. This is used in the lazy match evaluation.
+     */
+
+    uInt max_chain_length;
+    /* To speed up deflation, hash chains are never searched beyond this
+     * length.  A higher limit improves compression ratio but degrades the
+     * speed.
+     */
+
+    uInt max_lazy_match;
+    /* Attempt to find a better match only when the current match is strictly
+     * smaller than this value. This mechanism is used only for compression
+     * levels >= 4.
+     */
+#   define max_insert_length  max_lazy_match
+    /* Insert new strings in the hash table only if the match length is not
+     * greater than this length. This saves time but degrades compression.
+     * max_insert_length is used only for compression levels <= 3.
+     */
+
+    int level;    /* compression level (1..9) */
+    int strategy; /* favor or force Huffman coding*/
+
+    uInt good_match;
+    /* Use a faster search when the previous match is longer than this */
+
+    int nice_match; /* Stop searching when current match exceeds this */
+
+                /* used by trees.c: */
+    /* Didn't use ct_data typedef below to supress compiler warning */
+    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
+
+    struct tree_desc_s l_desc;               /* desc. for literal tree */
+    struct tree_desc_s d_desc;               /* desc. for distance tree */
+    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
+
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
+    int heap_len;               /* number of elements in the heap */
+    int heap_max;               /* element of largest frequency */
+    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+     * The same heap array is used to build all trees.
+     */
+
+    uch depth[2*L_CODES+1];
+    /* Depth of each subtree used as tie breaker for trees of equal frequency
+     */
+
+    uchf *l_buf;          /* buffer for literals or lengths */
+
+    uInt  lit_bufsize;
+    /* Size of match buffer for literals/lengths.  There are 4 reasons for
+     * limiting lit_bufsize to 64K:
+     *   - frequencies can be kept in 16 bit counters
+     *   - if compression is not successful for the first block, all input
+     *     data is still in the window so we can still emit a stored block even
+     *     when input comes from standard input.  (This can also be done for
+     *     all blocks if lit_bufsize is not greater than 32K.)
+     *   - if compression is not successful for a file smaller than 64K, we can
+     *     even emit a stored file instead of a stored block (saving 5 bytes).
+     *     This is applicable only for zip (not gzip or zlib).
+     *   - creating new Huffman trees less frequently may not provide fast
+     *     adaptation to changes in the input data statistics. (Take for
+     *     example a binary file with poorly compressible code followed by
+     *     a highly compressible string table.) Smaller buffer sizes give
+     *     fast adaptation but have of course the overhead of transmitting
+     *     trees more frequently.
+     *   - I can't count above 4
+     */
+
+    uInt last_lit;      /* running index in l_buf */
+
+    ushf *d_buf;
+    /* Buffer for distances. To simplify the code, d_buf and l_buf have
+     * the same number of elements. To use different lengths, an extra flag
+     * array would be necessary.
+     */
+
+    ulg opt_len;        /* bit length of current block with optimal trees */
+    ulg static_len;     /* bit length of current block with static trees */
+    uInt matches;       /* number of string matches in current block */
+    int last_eob_len;   /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
+#endif
+
+    ush bi_buf;
+    /* Output buffer. bits are inserted starting at the bottom (least
+     * significant bits).
+     */
+    int bi_valid;
+    /* Number of valid bits in bi_buf.  All bits above the last valid bit
+     * are always zero.
+     */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+        /* in trees.c */
+void _tr_init         OF((deflate_state *s));
+int  _tr_tally        OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block  OF((deflate_state *s, charf *buf, ulg stored_len,
+			  int eof));
+void _tr_align        OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+                          int eof));
+
+#define d_code(dist) \
+   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+  extern uch _length_code[];
+  extern uch _dist_code[];
+#else
+  extern const uch _length_code[];
+  extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+  { uch cc = (c); \
+    s->d_buf[s->last_lit] = 0; \
+    s->l_buf[s->last_lit++] = cc; \
+    s->dyn_ltree[cc].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+   }
+# define _tr_tally_dist(s, distance, length, flush) \
+  { uch len = (length); \
+    ush dist = (distance); \
+    s->d_buf[s->last_lit] = dist; \
+    s->l_buf[s->last_lit++] = len; \
+    dist--; \
+    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+    s->dyn_dtree[d_code(dist)].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+  }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+              flush = _tr_tally(s, distance, length) 
+#endif
+
+#endif
diff --git a/zlib/descrip.mms b/zlib/descrip.mms
new file mode 100644
index 0000000..9d36459
--- /dev/null
+++ b/zlib/descrip.mms
@@ -0,0 +1,48 @@
+# descrip.mms: MMS description file for building zlib on VMS
+# written by Martin P.J. Zinser <m.zinser@gsi.de>
+
+cc_defs = 
+c_deb = 
+
+.ifdef __DECC__
+pref = /prefix=all
+.endif
+
+OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\
+       deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\
+       inftrees.obj, infcodes.obj, infutil.obj, inffast.obj
+
+CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF)
+
+all : example.exe minigzip.exe
+        @ write sys$output " Example applications available"
+libz.olb : libz.olb($(OBJS))
+	@ write sys$output " libz available"
+
+example.exe : example.obj libz.olb
+              link example,libz.olb/lib
+
+minigzip.exe : minigzip.obj libz.olb
+              link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib
+
+clean : 
+	delete *.obj;*,libz.olb;*
+
+
+# Other dependencies.
+adler32.obj : zutil.h zlib.h zconf.h
+compress.obj : zlib.h zconf.h
+crc32.obj : zutil.h zlib.h zconf.h
+deflate.obj : deflate.h zutil.h zlib.h zconf.h
+example.obj : zlib.h zconf.h
+gzio.obj : zutil.h zlib.h zconf.h
+infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
+infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h
+inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h
+inflate.obj : zutil.h zlib.h zconf.h infblock.h
+inftrees.obj : zutil.h zlib.h zconf.h inftrees.h
+infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h
+minigzip.obj : zlib.h zconf.h
+trees.obj : deflate.h zutil.h zlib.h zconf.h
+uncompr.obj : zlib.h zconf.h
+zutil.obj : zutil.h zlib.h zconf.h
diff --git a/zlib/example.c b/zlib/example.c
new file mode 100644
index 0000000..255662f
--- /dev/null
+++ b/zlib/example.c
@@ -0,0 +1,556 @@
+/* example.c -- usage example of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: example.c,v 1.1 2004/10/08 09:44:25 const_k Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#else
+   extern void exit  OF((int));
+#endif
+
+#if defined(VMS) || defined(RISCOS)
+#  define TESTFILE "foo-gz"
+#else
+#  define TESTFILE "foo.gz"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+const char hello[] = "hello, hello!";
+/* "hello world" would be more standard, but the repeated "hello"
+ * stresses the compression code better, sorry...
+ */
+
+const char dictionary[] = "hello";
+uLong dictId; /* Adler32 value of the dictionary */
+
+void test_compress      OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+void test_gzio          OF((const char *out, const char *in, 
+		            Byte *uncompr, int uncomprLen));
+void test_deflate       OF((Byte *compr, uLong comprLen));
+void test_inflate       OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+void test_large_deflate OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+void test_large_inflate OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+void test_flush         OF((Byte *compr, uLong *comprLen));
+void test_sync          OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+void test_dict_deflate  OF((Byte *compr, uLong comprLen));
+void test_dict_inflate  OF((Byte *compr, uLong comprLen,
+		            Byte *uncompr, uLong uncomprLen));
+int  main               OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Test compress() and uncompress()
+ */
+void test_compress(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    uLong len = strlen(hello)+1;
+
+    err = compress(compr, &comprLen, (const Bytef*)hello, len);
+    CHECK_ERR(err, "compress");
+
+    strcpy((char*)uncompr, "garbage");
+
+    err = uncompress(uncompr, &uncomprLen, compr, comprLen);
+    CHECK_ERR(err, "uncompress");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad uncompress\n");
+	exit(1);
+    } else {
+        printf("uncompress(): %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Test read/write of .gz files
+ */
+void test_gzio(out, in, uncompr, uncomprLen)
+    const char *out; /* compressed output file */
+    const char *in;  /* compressed input file */
+    Byte *uncompr;
+    int  uncomprLen;
+{
+    int err;
+    int len = strlen(hello)+1;
+    gzFile file;
+    z_off_t pos;
+
+    file = gzopen(out, "wb");
+    if (file == NULL) {
+        fprintf(stderr, "gzopen error\n");
+        exit(1);
+    }
+    gzputc(file, 'h');
+    if (gzputs(file, "ello") != 4) {
+        fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
+	exit(1);
+    }
+    if (gzprintf(file, ", %s!", "hello") != 8) {
+        fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
+	exit(1);
+    }
+    gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
+    gzclose(file);
+
+    file = gzopen(in, "rb");
+    if (file == NULL) {
+        fprintf(stderr, "gzopen error\n");
+    }
+    strcpy((char*)uncompr, "garbage");
+
+    uncomprLen = gzread(file, uncompr, (unsigned)uncomprLen);
+    if (uncomprLen != len) {
+        fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
+	exit(1);
+    }
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
+	exit(1);
+    } else {
+        printf("gzread(): %s\n", (char *)uncompr);
+    }
+
+    pos = gzseek(file, -8L, SEEK_CUR);
+    if (pos != 6 || gztell(file) != pos) {
+	fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
+		(long)pos, (long)gztell(file));
+	exit(1);
+    }
+
+    if (gzgetc(file) != ' ') {
+	fprintf(stderr, "gzgetc error\n");
+	exit(1);
+    }
+
+    gzgets(file, (char*)uncompr, uncomprLen);
+    uncomprLen = strlen((char*)uncompr);
+    if (uncomprLen != 6) { /* "hello!" */
+        fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
+	exit(1);
+    }
+    if (strcmp((char*)uncompr, hello+7)) {
+        fprintf(stderr, "bad gzgets after gzseek\n");
+	exit(1);
+    } else {
+        printf("gzgets() after gzseek: %s\n", (char *)uncompr);
+    }
+
+    gzclose(file);
+}
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(compr, comprLen)
+    Byte *compr;
+    uLong comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+    int len = strlen(hello)+1;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_in  = (Bytef*)hello;
+    c_stream.next_out = compr;
+
+    while (c_stream.total_in != (uLong)len && c_stream.total_out < comprLen) {
+        c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+        err = deflate(&c_stream, Z_NO_FLUSH);
+        CHECK_ERR(err, "deflate");
+    }
+    /* Finish the stream, still forcing small buffers: */
+    for (;;) {
+        c_stream.avail_out = 1;
+        err = deflate(&c_stream, Z_FINISH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "deflate");
+    }
+
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = 0;
+    d_stream.next_out = uncompr;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+        d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "inflate");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad inflate\n");
+	exit(1);
+    } else {
+        printf("inflate(): %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_BEST_SPEED);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_out = compr;
+    c_stream.avail_out = (uInt)comprLen;
+
+    /* At this point, uncompr is still mostly zeroes, so it should compress
+     * very well:
+     */
+    c_stream.next_in = uncompr;
+    c_stream.avail_in = (uInt)uncomprLen;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+    if (c_stream.avail_in != 0) {
+        fprintf(stderr, "deflate not greedy\n");
+	exit(1);
+    }
+
+    /* Feed in already compressed data and switch to no compression: */
+    deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+    c_stream.next_in = compr;
+    c_stream.avail_in = (uInt)comprLen/2;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    /* Switch back to compressing mode: */
+    deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+    c_stream.next_in = uncompr;
+    c_stream.avail_in = (uInt)uncomprLen;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        fprintf(stderr, "deflate should report Z_STREAM_END\n");
+	exit(1);
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = (uInt)comprLen;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    for (;;) {
+        d_stream.next_out = uncompr;            /* discard the output */
+	d_stream.avail_out = (uInt)uncomprLen;
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "large inflate");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
+        fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
+	exit(1);
+    } else {
+        printf("large_inflate(): OK\n");
+    }
+}
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(compr, comprLen)
+    Byte *compr;
+    uLong *comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+    int len = strlen(hello)+1;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_in  = (Bytef*)hello;
+    c_stream.next_out = compr;
+    c_stream.avail_in = 3;
+    c_stream.avail_out = (uInt)*comprLen;
+    err = deflate(&c_stream, Z_FULL_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    compr[3]++; /* force an error in first compressed block */
+    c_stream.avail_in = len - 3;
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        CHECK_ERR(err, "deflate");
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+
+    *comprLen = c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = 2; /* just read the zlib header */
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    d_stream.next_out = uncompr;
+    d_stream.avail_out = (uInt)uncomprLen;
+
+    inflate(&d_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "inflate");
+
+    d_stream.avail_in = (uInt)comprLen-2;   /* read all compressed data */
+    err = inflateSync(&d_stream);           /* but skip the damaged part */
+    CHECK_ERR(err, "inflateSync");
+
+    err = inflate(&d_stream, Z_FINISH);
+    if (err != Z_DATA_ERROR) {
+        fprintf(stderr, "inflate should report DATA_ERROR\n");
+        /* Because of incorrect adler32 */
+	exit(1);
+    }
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    printf("after inflateSync(): hel%s\n", (char *)uncompr);
+}
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(compr, comprLen)
+    Byte *compr;
+    uLong comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    err = deflateSetDictionary(&c_stream,
+			       (const Bytef*)dictionary, sizeof(dictionary));
+    CHECK_ERR(err, "deflateSetDictionary");
+
+    dictId = c_stream.adler;
+    c_stream.next_out = compr;
+    c_stream.avail_out = (uInt)comprLen;
+
+    c_stream.next_in = (Bytef*)hello;
+    c_stream.avail_in = (uInt)strlen(hello)+1;
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        fprintf(stderr, "deflate should report Z_STREAM_END\n");
+	exit(1);
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = (uInt)comprLen;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    d_stream.next_out = uncompr;
+    d_stream.avail_out = (uInt)uncomprLen;
+
+    for (;;) {
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+	if (err == Z_NEED_DICT) {
+	    if (d_stream.adler != dictId) {
+		fprintf(stderr, "unexpected dictionary");
+		exit(1);
+	    }
+	    err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
+				       sizeof(dictionary));
+	}
+        CHECK_ERR(err, "inflate with dict");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad inflate with dict\n");
+	exit(1);
+    } else {
+        printf("inflate with dictionary: %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Usage:  example [output.gz  [input.gz]]
+ */
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    Byte *compr, *uncompr;
+    uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
+    uLong uncomprLen = comprLen;
+    static const char* myVersion = ZLIB_VERSION;
+
+    if (zlibVersion()[0] != myVersion[0]) {
+        fprintf(stderr, "incompatible zlib version\n");
+        exit(1);
+
+    } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
+        fprintf(stderr, "warning: different zlib version\n");
+    }
+
+    compr    = (Byte*)calloc((uInt)comprLen, 1);
+    uncompr  = (Byte*)calloc((uInt)uncomprLen, 1);
+    /* compr and uncompr are cleared to avoid reading uninitialized
+     * data and to ensure that uncompr compresses well.
+     */
+    if (compr == Z_NULL || uncompr == Z_NULL) {
+        printf("out of memory\n");
+	exit(1);
+    }
+    test_compress(compr, comprLen, uncompr, uncomprLen);
+
+    test_gzio((argc > 1 ? argv[1] : TESTFILE),
+              (argc > 2 ? argv[2] : TESTFILE),
+	      uncompr, (int)uncomprLen);
+
+    test_deflate(compr, comprLen);
+    test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+    test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    test_flush(compr, &comprLen);
+    test_sync(compr, comprLen, uncompr, uncomprLen);
+    comprLen = uncomprLen;
+
+    test_dict_deflate(compr, comprLen);
+    test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    exit(0);
+    return 0; /* to avoid warning */
+}
diff --git a/zlib/gzio.c b/zlib/gzio.c
new file mode 100644
index 0000000..518b573
--- /dev/null
+++ b/zlib/gzio.c
@@ -0,0 +1,875 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_DEFLATE to avoid the compression code.
+ */
+
+/* @(#) $Id: gzio.c,v 1.1 2004/10/08 09:44:25 const_k Exp $ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+#ifndef Z_BUFSIZE
+#  ifdef MAXSEG_64K
+#    define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+#  else
+#    define Z_BUFSIZE 16384
+#  endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+#  define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define RESERVED     0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+    z_stream stream;
+    int      z_err;   /* error code for last stream operation */
+    int      z_eof;   /* set if end of input file */
+    FILE     *file;   /* .gz file */
+    Byte     *inbuf;  /* input buffer */
+    Byte     *outbuf; /* output buffer */
+    uLong    crc;     /* crc32 of uncompressed data */
+    char     *msg;    /* error message */
+    char     *path;   /* path name for debugging only */
+    int      transparent; /* 1 if input file is not a .gz file */
+    char     mode;    /* 'w' or 'r' */
+    long     startpos; /* start of compressed data in file (header skipped) */
+} gz_stream;
+
+
+local gzFile gz_open      OF((const char *path, const char *mode, int  fd));
+local int do_flush        OF((gzFile file, int flush));
+local int    get_byte     OF((gz_stream *s));
+local void   check_header OF((gz_stream *s));
+local int    destroy      OF((gz_stream *s));
+local void   putLong      OF((FILE *file, uLong x));
+local uLong  getLong      OF((gz_stream *s));
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+   or path name (if fd == -1).
+     gz_open return NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+    const char *path;
+    const char *mode;
+    int  fd;
+{
+    int err;
+    int level = Z_DEFAULT_COMPRESSION; /* compression level */
+    int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+    char *p = (char*)mode;
+    gz_stream *s;
+    char fmode[80]; /* copy of mode, without the compression level */
+    char *m = fmode;
+
+    if (!path || !mode) return Z_NULL;
+
+    s = (gz_stream *)ALLOC(sizeof(gz_stream));
+    if (!s) return Z_NULL;
+
+    s->stream.zalloc = (alloc_func)0;
+    s->stream.zfree = (free_func)0;
+    s->stream.opaque = (voidpf)0;
+    s->stream.next_in = s->inbuf = Z_NULL;
+    s->stream.next_out = s->outbuf = Z_NULL;
+    s->stream.avail_in = s->stream.avail_out = 0;
+    s->file = NULL;
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->crc = crc32(0L, Z_NULL, 0);
+    s->msg = NULL;
+    s->transparent = 0;
+
+    s->path = (char*)ALLOC(strlen(path)+1);
+    if (s->path == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    strcpy(s->path, path); /* do this early for debugging */
+
+    s->mode = '\0';
+    do {
+        if (*p == 'r') s->mode = 'r';
+        if (*p == 'w' || *p == 'a') s->mode = 'w';
+        if (*p >= '0' && *p <= '9') {
+	    level = *p - '0';
+	} else if (*p == 'f') {
+	  strategy = Z_FILTERED;
+	} else if (*p == 'h') {
+	  strategy = Z_HUFFMAN_ONLY;
+	} else {
+	    *m++ = *p; /* copy the mode */
+	}
+    } while (*p++ && m != fmode + sizeof(fmode));
+    if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+    
+    if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+        err = Z_STREAM_ERROR;
+#else
+        err = deflateInit2(&(s->stream), level,
+                           Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+        /* windowBits is passed < 0 to suppress zlib header */
+
+        s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+        if (err != Z_OK || s->outbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    } else {
+        s->stream.next_in  = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+        err = inflateInit2(&(s->stream), -MAX_WBITS);
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+         * present after the compressed stream.
+         */
+        if (err != Z_OK || s->inbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    }
+    s->stream.avail_out = Z_BUFSIZE;
+
+    errno = 0;
+    s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+    if (s->file == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    if (s->mode == 'w') {
+        /* Write a very simple .gz header:
+         */
+        fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+             Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+	s->startpos = 10L;
+	/* We use 10L instead of ftell(s->file) to because ftell causes an
+         * fflush on some systems. This version of the library doesn't use
+         * startpos anyway in write mode, so this initialization is not
+         * necessary.
+         */
+    } else {
+	check_header(s); /* skip the .gz header */
+	s->startpos = (ftell(s->file) - s->stream.avail_in);
+    }
+    
+    return (gzFile)s;
+}
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+     Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+   to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+    int fd;
+    const char *mode;
+{
+    char name[20];
+
+    if (fd < 0) return (gzFile)Z_NULL;
+    sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+    return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+    gzFile file;
+    int level;
+    int strategy;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    /* Make room to allow flushing */
+    if (s->stream.avail_out == 0) {
+
+	s->stream.next_out = s->outbuf;
+	if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+	    s->z_err = Z_ERRNO;
+	}
+	s->stream.avail_out = Z_BUFSIZE;
+    }
+
+    return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+   for end of file.
+   IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+    gz_stream *s;
+{
+    if (s->z_eof) return EOF;
+    if (s->stream.avail_in == 0) {
+	errno = 0;
+	s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+	if (s->stream.avail_in == 0) {
+	    s->z_eof = 1;
+	    if (ferror(s->file)) s->z_err = Z_ERRNO;
+	    return EOF;
+	}
+	s->stream.next_in = s->inbuf;
+    }
+    s->stream.avail_in--;
+    return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+      Check the gzip header of a gz_stream opened for reading. Set the stream
+    mode to transparent if the gzip magic header is not present; set s->err
+    to Z_DATA_ERROR if the magic header is present but the rest of the header
+    is incorrect.
+    IN assertion: the stream s has already been created sucessfully;
+       s->stream.avail_in is zero for the first time, but may be non-zero
+       for concatenated .gz files.
+*/
+local void check_header(s)
+    gz_stream *s;
+{
+    int method; /* method byte */
+    int flags;  /* flags byte */
+    uInt len;
+    int c;
+
+    /* Check the gzip magic header */
+    for (len = 0; len < 2; len++) {
+	c = get_byte(s);
+	if (c != gz_magic[len]) {
+	    if (len != 0) s->stream.avail_in++, s->stream.next_in--;
+	    if (c != EOF) {
+		s->stream.avail_in++, s->stream.next_in--;
+		s->transparent = 1;
+	    }
+	    s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
+	    return;
+	}
+    }
+    method = get_byte(s);
+    flags = get_byte(s);
+    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+	s->z_err = Z_DATA_ERROR;
+	return;
+    }
+
+    /* Discard time, xflags and OS code: */
+    for (len = 0; len < 6; len++) (void)get_byte(s);
+
+    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+	len  =  (uInt)get_byte(s);
+	len += ((uInt)get_byte(s))<<8;
+	/* len is garbage if EOF but the loop below will quit anyway */
+	while (len-- != 0 && get_byte(s) != EOF) ;
+    }
+    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+	while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
+	while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
+	for (len = 0; len < 2; len++) (void)get_byte(s);
+    }
+    s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+   Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+    gz_stream *s;
+{
+    int err = Z_OK;
+
+    if (!s) return Z_STREAM_ERROR;
+
+    TRYFREE(s->msg);
+
+    if (s->stream.state != NULL) {
+	if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+	    err = Z_STREAM_ERROR;
+#else
+	    err = deflateEnd(&(s->stream));
+#endif
+	} else if (s->mode == 'r') {
+	    err = inflateEnd(&(s->stream));
+	}
+    }
+    if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+	if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+	    err = Z_ERRNO;
+    }
+    if (s->z_err < 0) err = s->z_err;
+
+    TRYFREE(s->inbuf);
+    TRYFREE(s->outbuf);
+    TRYFREE(s->path);
+    TRYFREE(s);
+    return err;
+}
+
+/* ===========================================================================
+     Reads the given number of uncompressed bytes from the compressed file.
+   gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+    gzFile file;
+    voidp buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+    Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+    Byte  *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+    if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+    if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+    if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
+
+    next_out = (Byte*)buf;
+    s->stream.next_out = (Bytef*)buf;
+    s->stream.avail_out = len;
+
+    while (s->stream.avail_out != 0) {
+
+	if (s->transparent) {
+	    /* Copy first the lookahead bytes: */
+	    uInt n = s->stream.avail_in;
+	    if (n > s->stream.avail_out) n = s->stream.avail_out;
+	    if (n > 0) {
+		zmemcpy(s->stream.next_out, s->stream.next_in, n);
+		next_out += n;
+		s->stream.next_out = next_out;
+		s->stream.next_in   += n;
+		s->stream.avail_out -= n;
+		s->stream.avail_in  -= n;
+	    }
+	    if (s->stream.avail_out > 0) {
+		s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
+					     s->file);
+	    }
+	    len -= s->stream.avail_out;
+	    s->stream.total_in  += (uLong)len;
+	    s->stream.total_out += (uLong)len;
+            if (len == 0) s->z_eof = 1;
+	    return (int)len;
+	}
+        if (s->stream.avail_in == 0 && !s->z_eof) {
+
+            errno = 0;
+            s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+            if (s->stream.avail_in == 0) {
+                s->z_eof = 1;
+		if (ferror(s->file)) {
+		    s->z_err = Z_ERRNO;
+		    break;
+		}
+            }
+            s->stream.next_in = s->inbuf;
+        }
+        s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+
+	if (s->z_err == Z_STREAM_END) {
+	    /* Check CRC and original size */
+	    s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+	    start = s->stream.next_out;
+
+	    if (getLong(s) != s->crc) {
+		s->z_err = Z_DATA_ERROR;
+	    } else {
+	        (void)getLong(s);
+                /* The uncompressed length returned by above getlong() may
+                 * be different from s->stream.total_out) in case of
+		 * concatenated .gz files. Check for such files:
+		 */
+		check_header(s);
+		if (s->z_err == Z_OK) {
+		    uLong total_in = s->stream.total_in;
+		    uLong total_out = s->stream.total_out;
+
+		    inflateReset(&(s->stream));
+		    s->stream.total_in = total_in;
+		    s->stream.total_out = total_out;
+		    s->crc = crc32(0L, Z_NULL, 0);
+		}
+	    }
+	}
+	if (s->z_err != Z_OK || s->z_eof) break;
+    }
+    s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+    return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+    gzFile file;
+{
+    unsigned char c;
+
+    return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+      Reads bytes from the compressed file until len-1 characters are
+   read, or a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  The string is then terminated
+   with a null character.
+      gzgets returns buf, or Z_NULL in case of error.
+
+      The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+    gzFile file;
+    char *buf;
+    int len;
+{
+    char *b = buf;
+    if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+    while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+    *buf = '\0';
+    return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_DEFLATE
+/* ===========================================================================
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+    gzFile file;
+    const voidp buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.next_in = (Bytef*)buf;
+    s->stream.avail_in = len;
+
+    while (s->stream.avail_in != 0) {
+
+        if (s->stream.avail_out == 0) {
+
+            s->stream.next_out = s->outbuf;
+            if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+                s->z_err = Z_ERRNO;
+                break;
+            }
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+        if (s->z_err != Z_OK) break;
+    }
+    s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+    return (int)(len - s->stream.avail_in);
+}
+
+/* ===========================================================================
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    va_list va;
+    int len;
+
+    va_start(va, format);
+#ifdef HAS_vsnprintf
+    (void)vsnprintf(buf, sizeof(buf), format, va);
+#else
+    (void)vsprintf(buf, format, va);
+#endif
+    va_end(va);
+    len = strlen(buf); /* some *sprintf don't return the nb of bytes written */
+    if (len <= 0) return 0;
+
+    return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+	               a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+    gzFile file;
+    const char *format;
+    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+	a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    int len;
+
+#ifdef HAS_snprintf
+    snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+	     a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#else
+    sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+	    a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#endif
+    len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */
+    if (len <= 0) return 0;
+
+    return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+    gzFile file;
+    int c;
+{
+    unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+    return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+    gzFile file;
+    const char *s;
+{
+    return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+    gzFile file;
+    int flush;
+{
+    uInt len;
+    int done = 0;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.avail_in = 0; /* should be zero already anyway */
+
+    for (;;) {
+        len = Z_BUFSIZE - s->stream.avail_out;
+
+        if (len != 0) {
+            if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+                s->z_err = Z_ERRNO;
+                return Z_ERRNO;
+            }
+            s->stream.next_out = s->outbuf;
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        if (done) break;
+        s->z_err = deflate(&(s->stream), flush);
+
+	/* Ignore the second of two consecutive flushes: */
+	if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+        /* deflate has finished flushing only when it hasn't used up
+         * all the available space in the output buffer: 
+         */
+        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+ 
+        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+    }
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+     gzFile file;
+     int flush;
+{
+    gz_stream *s = (gz_stream*)file;
+    int err = do_flush (file, flush);
+
+    if (err) return err;
+    fflush(s->file);
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_DEFLATE */
+
+/* ===========================================================================
+      Sets the starting position for the next gzread or gzwrite on the given
+   compressed file. The offset represents a number of bytes in the
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error.
+      SEEK_END is not implemented, returns error.
+      In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+    gzFile file;
+    z_off_t offset;
+    int whence;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || whence == SEEK_END ||
+	s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+	return -1L;
+    }
+    
+    if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+	return -1L;
+#else
+	if (whence == SEEK_SET) {
+	    offset -= s->stream.total_in;
+	}
+	if (offset < 0) return -1L;
+
+	/* At this point, offset is the number of zero bytes to write. */
+	if (s->inbuf == Z_NULL) {
+	    s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+	    zmemzero(s->inbuf, Z_BUFSIZE);
+	}
+	while (offset > 0)  {
+	    uInt size = Z_BUFSIZE;
+	    if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+	    size = gzwrite(file, s->inbuf, size);
+	    if (size == 0) return -1L;
+
+	    offset -= size;
+	}
+	return (z_off_t)s->stream.total_in;
+#endif
+    }
+    /* Rest of function is for reading only */
+
+    /* compute absolute position */
+    if (whence == SEEK_CUR) {
+	offset += s->stream.total_out;
+    }
+    if (offset < 0) return -1L;
+
+    if (s->transparent) {
+	/* map to fseek */
+	s->stream.avail_in = 0;
+	s->stream.next_in = s->inbuf;
+        if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+	s->stream.total_in = s->stream.total_out = (uLong)offset;
+	return offset;
+    }
+
+    /* For a negative seek, rewind and use positive seek */
+    if ((uLong)offset >= s->stream.total_out) {
+	offset -= s->stream.total_out;
+    } else if (gzrewind(file) < 0) {
+	return -1L;
+    }
+    /* offset is now the number of bytes to skip. */
+
+    if (offset != 0 && s->outbuf == Z_NULL) {
+	s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+    }
+    while (offset > 0)  {
+	int size = Z_BUFSIZE;
+	if (offset < Z_BUFSIZE) size = (int)offset;
+
+	size = gzread(file, s->outbuf, (uInt)size);
+	if (size <= 0) return -1L;
+	offset -= size;
+    }
+    return (z_off_t)s->stream.total_out;
+}
+
+/* ===========================================================================
+     Rewinds input file. 
+*/
+int ZEXPORT gzrewind (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+    
+    if (s == NULL || s->mode != 'r') return -1;
+
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->stream.avail_in = 0;
+    s->stream.next_in = s->inbuf;
+    s->crc = crc32(0L, Z_NULL, 0);
+	
+    if (s->startpos == 0) { /* not a compressed file */
+	rewind(s->file);
+	return 0;
+    }
+
+    (void) inflateReset(&s->stream);
+    return fseek(s->file, s->startpos, SEEK_SET);
+}
+
+/* ===========================================================================
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+    gzFile file;
+{
+    return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+    
+    return (s == NULL || s->mode != 'r') ? 0 : s->z_eof;
+}
+
+/* ===========================================================================
+   Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+    FILE *file;
+    uLong x;
+{
+    int n;
+    for (n = 0; n < 4; n++) {
+        fputc((int)(x & 0xff), file);
+        x >>= 8;
+    }
+}
+
+/* ===========================================================================
+   Reads a long in LSB order from the given gz_stream. Sets z_err in case
+   of error.
+*/
+local uLong getLong (s)
+    gz_stream *s;
+{
+    uLong x = (uLong)get_byte(s);
+    int c;
+
+    x += ((uLong)get_byte(s))<<8;
+    x += ((uLong)get_byte(s))<<16;
+    c = get_byte(s);
+    if (c == EOF) s->z_err = Z_DATA_ERROR;
+    x += ((uLong)c)<<24;
+    return x;
+}
+
+/* ===========================================================================
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+    gzFile file;
+{
+    int err;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) return Z_STREAM_ERROR;
+
+    if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+	return Z_STREAM_ERROR;
+#else
+        err = do_flush (file, Z_FINISH);
+        if (err != Z_OK) return destroy((gz_stream*)file);
+
+        putLong (s->file, s->crc);
+        putLong (s->file, s->stream.total_in);
+#endif
+    }
+    return destroy((gz_stream*)file);
+}
+
+/* ===========================================================================
+     Returns the error message for the last error which occured on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occured in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+const char*  ZEXPORT gzerror (file, errnum)
+    gzFile file;
+    int *errnum;
+{
+    char *m;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) {
+        *errnum = Z_STREAM_ERROR;
+        return (const char*)ERR_MSG(Z_STREAM_ERROR);
+    }
+    *errnum = s->z_err;
+    if (*errnum == Z_OK) return (const char*)"";
+
+    m =  (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+    if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+    TRYFREE(s->msg);
+    s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+    strcpy(s->msg, s->path);
+    strcat(s->msg, ": ");
+    strcat(s->msg, m);
+    return (const char*)s->msg;
+}
diff --git a/zlib/infblock.c b/zlib/infblock.c
new file mode 100644
index 0000000..dd7a6d4
--- /dev/null
+++ b/zlib/infblock.c
@@ -0,0 +1,403 @@
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+   Notes beyond the 1.93a appnote.txt:
+
+   1. Distance pointers never point before the beginning of the output
+      stream.
+   2. Distance pointers can point back across blocks, up to 32k away.
+   3. There is an implied maximum of 7 bits for the bit length table and
+      15 bits for the actual data.
+   4. If only one code exists, then it is encoded using one bit.  (Zero
+      would be more efficient, but perhaps a little confusing.)  If two
+      codes exist, they are coded using one bit each (0 and 1).
+   5. There is no way of sending zero distance codes--a dummy must be
+      sent if there are none.  (History: a pre 2.0 version of PKZIP would
+      store blocks with no distance codes, but this was discovered to be
+      too harsh a criterion.)  Valid only for 1.93a.  2.04c does allow
+      zero distance codes, which is sent as one code of zero bits in
+      length.
+   6. There are up to 286 literal/length codes.  Code 256 represents the
+      end-of-block.  Note however that the static length tree defines
+      288 codes just to fill out the Huffman codes.  Codes 286 and 287
+      cannot be used though, since there is no length base or extra bits
+      defined for them.  Similarily, there are up to 30 distance codes.
+      However, static trees define 32 codes (all 5 bits) to fill out the
+      Huffman codes, but the last two had better not show up in the data.
+   7. Unzip can check dynamic Huffman blocks for complete code sets.
+      The exception is that a single code would not be complete (see #4).
+   8. The five bits following the block type is really the number of
+      literal codes sent minus 257.
+   9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+      (1+6+6).  Therefore, to output three times the length, you output
+      three codes (1+1+1), whereas to output four times the same length,
+      you only need two codes (1+3).  Hmm.
+  10. In the tree reconstruction algorithm, Code = Code + Increment
+      only if BitLength(i) is not zero.  (Pretty obvious.)
+  11. Correction: 4 Bits: # of Bit Length codes - 4     (4 - 19)
+  12. Note: length code 284 can represent 227-258, but length code 285
+      really is 258.  The last length deserves its own, short code
+      since it gets used a lot in very redundant files.  The length
+      258 is special since 258 - 3 (the min match length) is 255.
+  13. The literal/length and distance code bit lengths are read as a
+      single stream of lengths.  It is possible (and advantageous) for
+      a repeat code (16, 17, or 18) to go across the boundary between
+      the two sets of lengths.
+ */
+
+
+void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+  if (c != Z_NULL)
+    *c = s->check;
+  if (s->mode == BTREE || s->mode == DTREE)
+    ZFREE(z, s->sub.trees.blens);
+  if (s->mode == CODES)
+    inflate_codes_free(s->sub.decode.codes, z);
+  s->mode = TYPE;
+  s->bitk = 0;
+  s->bitb = 0;
+  s->read = s->write = s->window;
+  if (s->checkfn != Z_NULL)
+    z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
+  Tracev((stderr, "inflate:   blocks reset\n"));
+}
+
+
+inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_streamp z;
+check_func c;
+uInt w;
+{
+  inflate_blocks_statef *s;
+
+  if ((s = (inflate_blocks_statef *)ZALLOC
+       (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+    return s;
+  if ((s->hufts =
+       (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL)
+  {
+    ZFREE(z, s);
+    return Z_NULL;
+  }
+  if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+  {
+    ZFREE(z, s->hufts);
+    ZFREE(z, s);
+    return Z_NULL;
+  }
+  s->end = s->window + w;
+  s->checkfn = c;
+  s->mode = TYPE;
+  Tracev((stderr, "inflate:   blocks allocated\n"));
+  inflate_blocks_reset(s, z, Z_NULL);
+  return s;
+}
+
+
+int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+  uInt t;               /* temporary storage */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input based on current state */
+  while (1) switch (s->mode)
+  {
+    case TYPE:
+      NEEDBITS(3)
+      t = (uInt)b & 7;
+      s->last = t & 1;
+      switch (t >> 1)
+      {
+        case 0:                         /* stored */
+          Tracev((stderr, "inflate:     stored block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          t = k & 7;                    /* go to byte boundary */
+          DUMPBITS(t)
+          s->mode = LENS;               /* get length of stored block */
+          break;
+        case 1:                         /* fixed */
+          Tracev((stderr, "inflate:     fixed codes block%s\n",
+                 s->last ? " (last)" : ""));
+          {
+            uInt bl, bd;
+            inflate_huft *tl, *td;
+
+            inflate_trees_fixed(&bl, &bd, &tl, &td, z);
+            s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+            if (s->sub.decode.codes == Z_NULL)
+            {
+              r = Z_MEM_ERROR;
+              LEAVE
+            }
+          }
+          DUMPBITS(3)
+          s->mode = CODES;
+          break;
+        case 2:                         /* dynamic */
+          Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          s->mode = TABLE;
+          break;
+        case 3:                         /* illegal */
+          DUMPBITS(3)
+          s->mode = BAD;
+          z->msg = (char*)"invalid block type";
+          r = Z_DATA_ERROR;
+          LEAVE
+      }
+      break;
+    case LENS:
+      NEEDBITS(32)
+      if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+      {
+        s->mode = BAD;
+        z->msg = (char*)"invalid stored block lengths";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+      s->sub.left = (uInt)b & 0xffff;
+      b = k = 0;                      /* dump bits */
+      Tracev((stderr, "inflate:       stored length %u\n", s->sub.left));
+      s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+      break;
+    case STORED:
+      if (n == 0)
+        LEAVE
+      NEEDOUT
+      t = s->sub.left;
+      if (t > n) t = n;
+      if (t > m) t = m;
+      zmemcpy(q, p, t);
+      p += t;  n -= t;
+      q += t;  m -= t;
+      if ((s->sub.left -= t) != 0)
+        break;
+      Tracev((stderr, "inflate:       stored end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      s->mode = s->last ? DRY : TYPE;
+      break;
+    case TABLE:
+      NEEDBITS(14)
+      s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+      if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+      {
+        s->mode = BAD;
+        z->msg = (char*)"too many length or distance symbols";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+#endif
+      t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+      if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+      {
+        r = Z_MEM_ERROR;
+        LEAVE
+      }
+      DUMPBITS(14)
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       table sizes ok\n"));
+      s->mode = BTREE;
+    case BTREE:
+      while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+      {
+        NEEDBITS(3)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+        DUMPBITS(3)
+      }
+      while (s->sub.trees.index < 19)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+      s->sub.trees.bb = 7;
+      t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+                             &s->sub.trees.tb, s->hufts, z);
+      if (t != Z_OK)
+      {
+        r = t;
+        if (r == Z_DATA_ERROR)
+        {
+          ZFREE(z, s->sub.trees.blens);
+          s->mode = BAD;
+        }
+        LEAVE
+      }
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       bits tree ok\n"));
+      s->mode = DTREE;
+    case DTREE:
+      while (t = s->sub.trees.table,
+             s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+      {
+        inflate_huft *h;
+        uInt i, j, c;
+
+        t = s->sub.trees.bb;
+        NEEDBITS(t)
+        h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+        t = h->bits;
+        c = h->base;
+        if (c < 16)
+        {
+          DUMPBITS(t)
+          s->sub.trees.blens[s->sub.trees.index++] = c;
+        }
+        else /* c == 16..18 */
+        {
+          i = c == 18 ? 7 : c - 14;
+          j = c == 18 ? 11 : 3;
+          NEEDBITS(t + i)
+          DUMPBITS(t)
+          j += (uInt)b & inflate_mask[i];
+          DUMPBITS(i)
+          i = s->sub.trees.index;
+          t = s->sub.trees.table;
+          if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+              (c == 16 && i < 1))
+          {
+            ZFREE(z, s->sub.trees.blens);
+            s->mode = BAD;
+            z->msg = (char*)"invalid bit length repeat";
+            r = Z_DATA_ERROR;
+            LEAVE
+          }
+          c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+          do {
+            s->sub.trees.blens[i++] = c;
+          } while (--j);
+          s->sub.trees.index = i;
+        }
+      }
+      s->sub.trees.tb = Z_NULL;
+      {
+        uInt bl, bd;
+        inflate_huft *tl, *td;
+        inflate_codes_statef *c;
+
+        bl = 9;         /* must be <= 9 for lookahead assumptions */
+        bd = 6;         /* must be <= 9 for lookahead assumptions */
+        t = s->sub.trees.table;
+        t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+                                  s->sub.trees.blens, &bl, &bd, &tl, &td,
+                                  s->hufts, z);
+        if (t != Z_OK)
+        {
+          if (t == (uInt)Z_DATA_ERROR)
+          {
+            ZFREE(z, s->sub.trees.blens);
+            s->mode = BAD;
+          }
+          r = t;
+          LEAVE
+        }
+        Tracev((stderr, "inflate:       trees ok\n"));
+        if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+        {
+          r = Z_MEM_ERROR;
+          LEAVE
+        }
+        s->sub.decode.codes = c;
+      }
+      ZFREE(z, s->sub.trees.blens);
+      s->mode = CODES;
+    case CODES:
+      UPDATE
+      if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+        return inflate_flush(s, z, r);
+      r = Z_OK;
+      inflate_codes_free(s->sub.decode.codes, z);
+      LOAD
+      Tracev((stderr, "inflate:       codes end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      if (!s->last)
+      {
+        s->mode = TYPE;
+        break;
+      }
+      s->mode = DRY;
+    case DRY:
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      s->mode = DONE;
+    case DONE:
+      r = Z_STREAM_END;
+      LEAVE
+    case BAD:
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+int inflate_blocks_free(s, z)
+inflate_blocks_statef *s;
+z_streamp z;
+{
+  inflate_blocks_reset(s, z, Z_NULL);
+  ZFREE(z, s->window);
+  ZFREE(z, s->hufts);
+  ZFREE(z, s);
+  Tracev((stderr, "inflate:   blocks freed\n"));
+  return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt  n;
+{
+  zmemcpy(s->window, d, n);
+  s->read = s->write = s->window + n;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. 
+ * IN assertion: s != Z_NULL
+ */
+int inflate_blocks_sync_point(s)
+inflate_blocks_statef *s;
+{
+  return s->mode == LENS;
+}
diff --git a/zlib/infblock.h b/zlib/infblock.h
new file mode 100644
index 0000000..173b226
--- /dev/null
+++ b/zlib/infblock.h
@@ -0,0 +1,39 @@
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+extern inflate_blocks_statef * inflate_blocks_new OF((
+    z_streamp z,
+    check_func c,               /* check function */
+    uInt w));                   /* window size */
+
+extern int inflate_blocks OF((
+    inflate_blocks_statef *,
+    z_streamp ,
+    int));                      /* initial return code */
+
+extern void inflate_blocks_reset OF((
+    inflate_blocks_statef *,
+    z_streamp ,
+    uLongf *));                  /* check value on output */
+
+extern int inflate_blocks_free OF((
+    inflate_blocks_statef *,
+    z_streamp));
+
+extern void inflate_set_dictionary OF((
+    inflate_blocks_statef *s,
+    const Bytef *d,  /* dictionary */
+    uInt  n));       /* dictionary length */
+
+extern int inflate_blocks_sync_point OF((
+    inflate_blocks_statef *s));
diff --git a/zlib/infcodes.c b/zlib/infcodes.c
new file mode 100644
index 0000000..9abe541
--- /dev/null
+++ b/zlib/infcodes.c
@@ -0,0 +1,251 @@
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+typedef enum {        /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+      START,    /* x: set up for LEN */
+      LEN,      /* i: get length/literal/eob next */
+      LENEXT,   /* i: getting length extra (have base) */
+      DIST,     /* i: get distance next */
+      DISTEXT,  /* i: getting distance extra */
+      COPY,     /* o: copying bytes in window, waiting for space */
+      LIT,      /* o: got literal, waiting for output space */
+      WASH,     /* o: got eob, possibly still output waiting */
+      END,      /* x: got eob and all data flushed */
+      BADCODE}  /* x: got error */
+inflate_codes_mode;
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+  /* mode */
+  inflate_codes_mode mode;      /* current inflate_codes mode */
+
+  /* mode dependent information */
+  uInt len;
+  union {
+    struct {
+      inflate_huft *tree;       /* pointer into tree */
+      uInt need;                /* bits needed */
+    } code;             /* if LEN or DIST, where in tree */
+    uInt lit;           /* if LIT, literal */
+    struct {
+      uInt get;                 /* bits to get for extra */
+      uInt dist;                /* distance back to copy from */
+    } copy;             /* if EXT or COPY, where and how much */
+  } sub;                /* submode */
+
+  /* mode independent information */
+  Byte lbits;           /* ltree bits decoded per branch */
+  Byte dbits;           /* dtree bits decoder per branch */
+  inflate_huft *ltree;          /* literal/length/eob tree */
+  inflate_huft *dtree;          /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+  inflate_codes_statef *c;
+
+  if ((c = (inflate_codes_statef *)
+       ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+  {
+    c->mode = START;
+    c->lbits = (Byte)bl;
+    c->dbits = (Byte)bd;
+    c->ltree = tl;
+    c->dtree = td;
+    Tracev((stderr, "inflate:       codes new\n"));
+  }
+  return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+  uInt j;               /* temporary storage */
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  Bytef *f;             /* pointer to copy strings from */
+  inflate_codes_statef *c = s->sub.decode.codes;  /* codes state */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input and output based on current state */
+  while (1) switch (c->mode)
+  {             /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+    case START:         /* x: set up for LEN */
+#ifndef SLOW
+      if (m >= 258 && n >= 10)
+      {
+        UPDATE
+        r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+        LOAD
+        if (r != Z_OK)
+        {
+          c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+          break;
+        }
+      }
+#endif /* !SLOW */
+      c->sub.code.need = c->lbits;
+      c->sub.code.tree = c->ltree;
+      c->mode = LEN;
+    case LEN:           /* i: get length/literal/eob next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e == 0)               /* literal */
+      {
+        c->sub.lit = t->base;
+        Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                 "inflate:         literal '%c'\n" :
+                 "inflate:         literal 0x%02x\n", t->base));
+        c->mode = LIT;
+        break;
+      }
+      if (e & 16)               /* length */
+      {
+        c->sub.copy.get = e & 15;
+        c->len = t->base;
+        c->mode = LENEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t + t->base;
+        break;
+      }
+      if (e & 32)               /* end of block */
+      {
+        Tracevv((stderr, "inflate:         end of block\n"));
+        c->mode = WASH;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = (char*)"invalid literal/length code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case LENEXT:        /* i: getting length extra (have base) */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->len += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      c->sub.code.need = c->dbits;
+      c->sub.code.tree = c->dtree;
+      Tracevv((stderr, "inflate:         length %u\n", c->len));
+      c->mode = DIST;
+    case DIST:          /* i: get distance next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e & 16)               /* distance */
+      {
+        c->sub.copy.get = e & 15;
+        c->sub.copy.dist = t->base;
+        c->mode = DISTEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t + t->base;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = (char*)"invalid distance code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case DISTEXT:       /* i: getting distance extra */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->sub.copy.dist += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      Tracevv((stderr, "inflate:         distance %u\n", c->sub.copy.dist));
+      c->mode = COPY;
+    case COPY:          /* o: copying bytes in window, waiting for space */
+      f = q - c->sub.copy.dist;
+      while (f < s->window)             /* modulo window size-"while" instead */
+        f += s->end - s->window;        /* of "if" handles invalid distances */
+      while (c->len)
+      {
+        NEEDOUT
+        OUTBYTE(*f++)
+        if (f == s->end)
+          f = s->window;
+        c->len--;
+      }
+      c->mode = START;
+      break;
+    case LIT:           /* o: got literal, waiting for output space */
+      NEEDOUT
+      OUTBYTE(c->sub.lit)
+      c->mode = START;
+      break;
+    case WASH:          /* o: got eob, possibly more output */
+      if (k > 7)        /* return unused byte, if any */
+      {
+        Assert(k < 16, "inflate_codes grabbed too many bytes")
+        k -= 8;
+        n++;
+        p--;            /* can always return one */
+      }
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      c->mode = END;
+    case END:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADCODE:       /* x: got error */
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+#ifdef NEED_DUMMY_RETURN
+  return Z_STREAM_ERROR;  /* Some dumb compilers complain without this */
+#endif
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+  ZFREE(z, c);
+  Tracev((stderr, "inflate:       codes free\n"));
+}
diff --git a/zlib/infcodes.h b/zlib/infcodes.h
new file mode 100644
index 0000000..46821a0
--- /dev/null
+++ b/zlib/infcodes.h
@@ -0,0 +1,27 @@
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+extern inflate_codes_statef *inflate_codes_new OF((
+    uInt, uInt,
+    inflate_huft *, inflate_huft *,
+    z_streamp ));
+
+extern int inflate_codes OF((
+    inflate_blocks_statef *,
+    z_streamp ,
+    int));
+
+extern void inflate_codes_free OF((
+    inflate_codes_statef *,
+    z_streamp ));
+
diff --git a/zlib/inffast.c b/zlib/inffast.c
new file mode 100644
index 0000000..aa7f1d4
--- /dev/null
+++ b/zlib/inffast.c
@@ -0,0 +1,183 @@
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
+
+/* Called with number of bytes left to write in window at least 258
+   (the maximum string length) and number of input bytes available
+   at least ten.  The ten bytes are six bytes for the longest length/
+   distance pair plus four bytes for overloading the bit buffer. */
+
+int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+inflate_blocks_statef *s;
+z_streamp z;
+{
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  uInt ml;              /* mask for literal/length tree */
+  uInt md;              /* mask for distance tree */
+  uInt c;               /* bytes to copy */
+  uInt d;               /* distance back to copy from */
+  Bytef *r;             /* copy source pointer */
+
+  /* load input, output, bit values */
+  LOAD
+
+  /* initialize masks */
+  ml = inflate_mask[bl];
+  md = inflate_mask[bd];
+
+  /* do until not enough input or output space for fast loop */
+  do {                          /* assume called with m >= 258 && n >= 10 */
+    /* get literal/length code */
+    GRABBITS(20)                /* max bits for literal/length code */
+    if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+    {
+      DUMPBITS(t->bits)
+      Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                "inflate:         * literal '%c'\n" :
+                "inflate:         * literal 0x%02x\n", t->base));
+      *q++ = (Byte)t->base;
+      m--;
+      continue;
+    }
+    do {
+      DUMPBITS(t->bits)
+      if (e & 16)
+      {
+        /* get extra bits for length */
+        e &= 15;
+        c = t->base + ((uInt)b & inflate_mask[e]);
+        DUMPBITS(e)
+        Tracevv((stderr, "inflate:         * length %u\n", c));
+
+        /* decode distance base of block to copy */
+        GRABBITS(15);           /* max bits for distance code */
+        e = (t = td + ((uInt)b & md))->exop;
+        do {
+          DUMPBITS(t->bits)
+          if (e & 16)
+          {
+            /* get extra bits to add to distance base */
+            e &= 15;
+            GRABBITS(e)         /* get extra bits (up to 13) */
+            d = t->base + ((uInt)b & inflate_mask[e]);
+            DUMPBITS(e)
+            Tracevv((stderr, "inflate:         * distance %u\n", d));
+
+            /* do the copy */
+            m -= c;
+            r = q - d;
+            if (r < s->window)                  /* wrap if needed */
+            {
+              do {
+                r += s->end - s->window;        /* force pointer in window */
+              } while (r < s->window);          /* covers invalid distances */
+              e = s->end - r;
+              if (c > e)
+              {
+                c -= e;                         /* wrapped copy */
+                do {
+                    *q++ = *r++;
+                } while (--e);
+                r = s->window;
+                do {
+                    *q++ = *r++;
+                } while (--c);
+              }
+              else                              /* normal copy */
+              {
+                *q++ = *r++;  c--;
+                *q++ = *r++;  c--;
+                do {
+                    *q++ = *r++;
+                } while (--c);
+              }
+            }
+            else                                /* normal copy */
+            {
+              *q++ = *r++;  c--;
+              *q++ = *r++;  c--;
+              do {
+                *q++ = *r++;
+              } while (--c);
+            }
+            break;
+          }
+          else if ((e & 64) == 0)
+          {
+            t += t->base;
+            e = (t += ((uInt)b & inflate_mask[e]))->exop;
+          }
+          else
+          {
+            z->msg = (char*)"invalid distance code";
+            UNGRAB
+            UPDATE
+            return Z_DATA_ERROR;
+          }
+        } while (1);
+        break;
+      }
+      if ((e & 64) == 0)
+      {
+        t += t->base;
+        if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0)
+        {
+          DUMPBITS(t->bits)
+          Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                    "inflate:         * literal '%c'\n" :
+                    "inflate:         * literal 0x%02x\n", t->base));
+          *q++ = (Byte)t->base;
+          m--;
+          break;
+        }
+      }
+      else if (e & 32)
+      {
+        Tracevv((stderr, "inflate:         * end of block\n"));
+        UNGRAB
+        UPDATE
+        return Z_STREAM_END;
+      }
+      else
+      {
+        z->msg = (char*)"invalid literal/length code";
+        UNGRAB
+        UPDATE
+        return Z_DATA_ERROR;
+      }
+    } while (1);
+  } while (m >= 258 && n >= 10);
+
+  /* not enough input or output--restore pointers and return */
+  UNGRAB
+  UPDATE
+  return Z_OK;
+}
diff --git a/zlib/inffast.h b/zlib/inffast.h
new file mode 100644
index 0000000..a31a4bb
--- /dev/null
+++ b/zlib/inffast.h
@@ -0,0 +1,17 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+    uInt,
+    uInt,
+    inflate_huft *,
+    inflate_huft *,
+    inflate_blocks_statef *,
+    z_streamp ));
diff --git a/zlib/inffixed.h b/zlib/inffixed.h
new file mode 100644
index 0000000..77f7e76
--- /dev/null
+++ b/zlib/inffixed.h
@@ -0,0 +1,151 @@
+/* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by the maketree.c program
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+local uInt fixed_bl = 9;
+local uInt fixed_bd = 5;
+local inflate_huft fixed_tl[] = {
+    {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+    {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
+    {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
+    {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
+    {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
+    {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
+    {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
+    {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
+    {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+    {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
+    {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
+    {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
+    {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
+    {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
+    {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
+    {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
+    {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+    {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
+    {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
+    {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
+    {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
+    {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
+    {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
+    {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
+    {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+    {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
+    {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
+    {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
+    {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
+    {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
+    {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
+    {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
+    {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+    {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
+    {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
+    {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
+    {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
+    {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
+    {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
+    {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
+    {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+    {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
+    {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
+    {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
+    {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
+    {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
+    {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
+    {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
+    {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+    {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
+    {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
+    {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
+    {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
+    {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
+    {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
+    {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
+    {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+    {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
+    {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
+    {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
+    {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
+    {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
+    {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
+    {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
+    {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+    {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
+    {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
+    {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
+    {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
+    {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
+    {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
+    {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
+    {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+    {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
+    {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
+    {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
+    {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
+    {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
+    {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
+    {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
+    {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+    {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
+    {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
+    {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
+    {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
+    {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
+    {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
+    {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
+    {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+    {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
+    {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
+    {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
+    {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
+    {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
+    {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
+    {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
+    {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+    {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
+    {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
+    {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
+    {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
+    {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
+    {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
+    {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
+    {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+    {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
+    {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
+    {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
+    {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
+    {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
+    {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
+    {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
+    {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+    {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
+    {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
+    {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
+    {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
+    {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
+    {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
+    {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
+    {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+    {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
+    {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
+    {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
+    {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
+    {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
+    {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
+    {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
+  };
+local inflate_huft fixed_td[] = {
+    {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
+    {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
+    {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
+    {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
+    {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
+    {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
+    {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
+    {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
+  };
diff --git a/zlib/inflate.c b/zlib/inflate.c
new file mode 100644
index 0000000..dfb2e86
--- /dev/null
+++ b/zlib/inflate.c
@@ -0,0 +1,366 @@
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+
+struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
+
+typedef enum {
+      METHOD,   /* waiting for method byte */
+      FLAG,     /* waiting for flag byte */
+      DICT4,    /* four dictionary check bytes to go */
+      DICT3,    /* three dictionary check bytes to go */
+      DICT2,    /* two dictionary check bytes to go */
+      DICT1,    /* one dictionary check byte to go */
+      DICT0,    /* waiting for inflateSetDictionary */
+      BLOCKS,   /* decompressing blocks */
+      CHECK4,   /* four check bytes to go */
+      CHECK3,   /* three check bytes to go */
+      CHECK2,   /* two check bytes to go */
+      CHECK1,   /* one check byte to go */
+      DONE,     /* finished check, done */
+      BAD}      /* got an error--stay here */
+inflate_mode;
+
+/* inflate private state */
+struct internal_state {
+
+  /* mode */
+  inflate_mode  mode;   /* current inflate mode */
+
+  /* mode dependent information */
+  union {
+    uInt method;        /* if FLAGS, method byte */
+    struct {
+      uLong was;                /* computed check value */
+      uLong need;               /* stream check value */
+    } check;            /* if CHECK, check values to compare */
+    uInt marker;        /* if BAD, inflateSync's marker bytes count */
+  } sub;        /* submode */
+
+  /* mode independent information */
+  int  nowrap;          /* flag for no wrapper */
+  uInt wbits;           /* log2(window size)  (8..15, defaults to 15) */
+  inflate_blocks_statef 
+    *blocks;            /* current inflate_blocks state */
+
+};
+
+
+int ZEXPORT inflateReset(z)
+z_streamp z;
+{
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  z->total_in = z->total_out = 0;
+  z->msg = Z_NULL;
+  z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+  inflate_blocks_reset(z->state->blocks, z, Z_NULL);
+  Tracev((stderr, "inflate: reset\n"));
+  return Z_OK;
+}
+
+
+int ZEXPORT inflateEnd(z)
+z_streamp z;
+{
+  if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->blocks != Z_NULL)
+    inflate_blocks_free(z->state->blocks, z);
+  ZFREE(z, z->state);
+  z->state = Z_NULL;
+  Tracev((stderr, "inflate: end\n"));
+  return Z_OK;
+}
+
+
+int ZEXPORT inflateInit2_(z, w, version, stream_size)
+z_streamp z;
+int w;
+const char *version;
+int stream_size;
+{
+  if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+      stream_size != sizeof(z_stream))
+      return Z_VERSION_ERROR;
+
+  /* initialize state */
+  if (z == Z_NULL)
+    return Z_STREAM_ERROR;
+  z->msg = Z_NULL;
+  if (z->zalloc == Z_NULL)
+  {
+    z->zalloc = zcalloc;
+    z->opaque = (voidpf)0;
+  }
+  if (z->zfree == Z_NULL) z->zfree = zcfree;
+  if ((z->state = (struct internal_state FAR *)
+       ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+    return Z_MEM_ERROR;
+  z->state->blocks = Z_NULL;
+
+  /* handle undocumented nowrap option (no zlib header or check) */
+  z->state->nowrap = 0;
+  if (w < 0)
+  {
+    w = - w;
+    z->state->nowrap = 1;
+  }
+
+  /* set window size */
+  if (w < 8 || w > 15)
+  {
+    inflateEnd(z);
+    return Z_STREAM_ERROR;
+  }
+  z->state->wbits = (uInt)w;
+
+  /* create inflate_blocks state */
+  if ((z->state->blocks =
+      inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+      == Z_NULL)
+  {
+    inflateEnd(z);
+    return Z_MEM_ERROR;
+  }
+  Tracev((stderr, "inflate: allocated\n"));
+
+  /* reset state */
+  inflateReset(z);
+  return Z_OK;
+}
+
+
+int ZEXPORT inflateInit_(z, version, stream_size)
+z_streamp z;
+const char *version;
+int stream_size;
+{
+  return inflateInit2_(z, DEF_WBITS, version, stream_size);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int ZEXPORT inflate(z, f)
+z_streamp z;
+int f;
+{
+  int r;
+  uInt b;
+
+  if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
+    return Z_STREAM_ERROR;
+  f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
+  r = Z_BUF_ERROR;
+  while (1) switch (z->state->mode)
+  {
+    case METHOD:
+      NEEDBYTE
+      if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+      {
+        z->state->mode = BAD;
+        z->msg = (char*)"unknown compression method";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+      {
+        z->state->mode = BAD;
+        z->msg = (char*)"invalid window size";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      z->state->mode = FLAG;
+    case FLAG:
+      NEEDBYTE
+      b = NEXTBYTE;
+      if (((z->state->sub.method << 8) + b) % 31)
+      {
+        z->state->mode = BAD;
+        z->msg = (char*)"incorrect header check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Tracev((stderr, "inflate: zlib header ok\n"));
+      if (!(b & PRESET_DICT))
+      {
+        z->state->mode = BLOCKS;
+        break;
+      }
+      z->state->mode = DICT4;
+    case DICT4:
+      NEEDBYTE
+      z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+      z->state->mode = DICT3;
+    case DICT3:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+      z->state->mode = DICT2;
+    case DICT2:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+      z->state->mode = DICT1;
+    case DICT1:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE;
+      z->adler = z->state->sub.check.need;
+      z->state->mode = DICT0;
+      return Z_NEED_DICT;
+    case DICT0:
+      z->state->mode = BAD;
+      z->msg = (char*)"need dictionary";
+      z->state->sub.marker = 0;       /* can try inflateSync */
+      return Z_STREAM_ERROR;
+    case BLOCKS:
+      r = inflate_blocks(z->state->blocks, z, r);
+      if (r == Z_DATA_ERROR)
+      {
+        z->state->mode = BAD;
+        z->state->sub.marker = 0;       /* can try inflateSync */
+        break;
+      }
+      if (r == Z_OK)
+        r = f;
+      if (r != Z_STREAM_END)
+        return r;
+      r = f;
+      inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+      if (z->state->nowrap)
+      {
+        z->state->mode = DONE;
+        break;
+      }
+      z->state->mode = CHECK4;
+    case CHECK4:
+      NEEDBYTE
+      z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+      z->state->mode = CHECK3;
+    case CHECK3:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+      z->state->mode = CHECK2;
+    case CHECK2:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+      z->state->mode = CHECK1;
+    case CHECK1:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE;
+
+      if (z->state->sub.check.was != z->state->sub.check.need)
+      {
+        z->state->mode = BAD;
+        z->msg = (char*)"incorrect data check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Tracev((stderr, "inflate: zlib check ok\n"));
+      z->state->mode = DONE;
+    case DONE:
+      return Z_STREAM_END;
+    case BAD:
+      return Z_DATA_ERROR;
+    default:
+      return Z_STREAM_ERROR;
+  }
+#ifdef NEED_DUMMY_RETURN
+  return Z_STREAM_ERROR;  /* Some dumb compilers complain without this */
+#endif
+}
+
+
+int ZEXPORT inflateSetDictionary(z, dictionary, dictLength)
+z_streamp z;
+const Bytef *dictionary;
+uInt  dictLength;
+{
+  uInt length = dictLength;
+
+  if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
+    return Z_STREAM_ERROR;
+
+  if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
+  z->adler = 1L;
+
+  if (length >= ((uInt)1<<z->state->wbits))
+  {
+    length = (1<<z->state->wbits)-1;
+    dictionary += dictLength - length;
+  }
+  inflate_set_dictionary(z->state->blocks, dictionary, length);
+  z->state->mode = BLOCKS;
+  return Z_OK;
+}
+
+
+int ZEXPORT inflateSync(z)
+z_streamp z;
+{
+  uInt n;       /* number of bytes to look at */
+  Bytef *p;     /* pointer to bytes */
+  uInt m;       /* number of marker bytes found in a row */
+  uLong r, w;   /* temporaries to save total_in and total_out */
+
+  /* set up */
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->mode != BAD)
+  {
+    z->state->mode = BAD;
+    z->state->sub.marker = 0;
+  }
+  if ((n = z->avail_in) == 0)
+    return Z_BUF_ERROR;
+  p = z->next_in;
+  m = z->state->sub.marker;
+
+  /* search */
+  while (n && m < 4)
+  {
+    static const Byte mark[4] = {0, 0, 0xff, 0xff};
+    if (*p == mark[m])
+      m++;
+    else if (*p)
+      m = 0;
+    else
+      m = 4 - m;
+    p++, n--;
+  }
+
+  /* restore */
+  z->total_in += p - z->next_in;
+  z->next_in = p;
+  z->avail_in = n;
+  z->state->sub.marker = m;
+
+  /* return no joy or set up to restart on a new block */
+  if (m != 4)
+    return Z_DATA_ERROR;
+  r = z->total_in;  w = z->total_out;
+  inflateReset(z);
+  z->total_in = r;  z->total_out = w;
+  z->state->mode = BLOCKS;
+  return Z_OK;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
+ * but removes the length bytes of the resulting empty stored block. When
+ * decompressing, PPP checks that at the end of input packet, inflate is
+ * waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(z)
+z_streamp z;
+{
+  if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL)
+    return Z_STREAM_ERROR;
+  return inflate_blocks_sync_point(z->state->blocks);
+}
diff --git a/zlib/inftrees.c b/zlib/inftrees.c
new file mode 100644
index 0000000..4c32ca3
--- /dev/null
+++ b/zlib/inftrees.c
@@ -0,0 +1,454 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#if !defined(BUILDFIXED) && !defined(STDC)
+#  define BUILDFIXED   /* non ANSI compilers may not accept inffixed.h */
+#endif
+
+const char inflate_copyright[] =
+   " inflate 1.1.4 Copyright 1995-2002 Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+struct internal_state  {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+    uIntf *,            /* code lengths in bits */
+    uInt,               /* number of codes */
+    uInt,               /* number of "simple" codes */
+    const uIntf *,      /* list of base values for non-simple codes */
+    const uIntf *,      /* list of extra bits for non-simple codes */
+    inflate_huft * FAR*,/* result: starting table */
+    uIntf *,            /* maximum lookup bits (returns actual) */
+    inflate_huft *,     /* space for trees */
+    uInt *,             /* hufts used in space */
+    uIntf * ));         /* space for values */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15         /* maximum bit length of any code */
+
+local int huft_build(b, n, s, d, e, t, m, hp, hn, v)
+uIntf *b;               /* code lengths in bits (all assumed <= BMAX) */
+uInt n;                 /* number of codes (assumed <= 288) */
+uInt s;                 /* number of simple-valued codes (0..s-1) */
+const uIntf *d;         /* list of base values for non-simple codes */
+const uIntf *e;         /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t;  /* result: starting table */
+uIntf *m;               /* maximum lookup bits, returns actual */
+inflate_huft *hp;       /* space for trees */
+uInt *hn;               /* hufts used in space */
+uIntf *v;               /* working area: values in order of bit length */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return Z_OK on success, Z_BUF_ERROR
+   if the given code set is incomplete (the tables are still built in this
+   case), or Z_DATA_ERROR if the input is invalid. */
+{
+
+  uInt a;                       /* counter for codes of length k */
+  uInt c[BMAX+1];               /* bit length count table */
+  uInt f;                       /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register uInt i;              /* counter, current code */
+  register uInt j;              /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  uInt mask;                    /* (1 << w) - 1, to avoid cc -O bug on HP */
+  register uIntf *p;            /* pointer into c[], b[], or v[] */
+  inflate_huft *q;              /* points to current table */
+  struct inflate_huft_s r;      /* table entry for structure assignment */
+  inflate_huft *u[BMAX];        /* table stack */
+  register int w;               /* bits before this table == (l * h) */
+  uInt x[BMAX+1];               /* bit offsets, then code stack */
+  uIntf *xp;                    /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  uInt z;                       /* number of entries in current table */
+
+
+  /* Generate counts for each bit length */
+  p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+  C4                            /* clear c[]--assume BMAX+1 is 16 */
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (inflate_huft *)Z_NULL;
+    *m = 0;
+    return Z_OK;
+  }
+
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((uInt)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((uInt)l > i)
+    l = i;
+  *m = l;
+
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return Z_DATA_ERROR;
+  if ((y -= c[i]) < 0)
+    return Z_DATA_ERROR;
+  c[i] += y;
+
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+  n = x[g];                     /* set n to length of v */
+
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (inflate_huft *)Z_NULL;        /* just to keep compilers happy */
+  q = (inflate_huft *)Z_NULL;   /* ditto */
+  z = 0;                        /* ditto */
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+    a = c[k];
+    while (a--)
+    {
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = g - w;
+        z = z > (uInt)l ? l : z;        /* table size upper limit */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          if (j < z)
+            while (++j < z)     /* try smaller tables up to z bits */
+            {
+              if ((f <<= 1) <= *++xp)
+                break;          /* enough codes to use up j bits */
+              f -= *xp;         /* else deduct codes from patterns */
+            }
+        }
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate new table */
+        if (*hn + z > MANY)     /* (note: doesn't matter for fixed) */
+          return Z_DATA_ERROR;  /* overflow of MANY */
+        u[h] = q = hp + *hn;
+        *hn += z;
+
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.bits = (Byte)l;     /* bits to dump before this table */
+          r.exop = (Byte)j;     /* bits in this table */
+          j = i >> (w - l);
+          r.base = (uInt)(q - u[h-1] - j);   /* offset to this table */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+        else
+          *t = q;               /* first table is returned result */
+      }
+
+      /* set up table entry in r */
+      r.bits = (Byte)(k - w);
+      if (p >= v + n)
+        r.exop = 128 + 64;      /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.exop = (Byte)(*p < 256 ? 0 : 32 + 64);     /* 256 is end-of-block */
+        r.base = *p++;          /* simple code is just the value */
+      }
+      else
+      {
+        r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+        r.base = d[*p++ - s];
+      }
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      mask = (1 << w) - 1;      /* needed on HP, cc -O bug */
+      while ((i & mask) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+        mask = (1 << w) - 1;
+      }
+    }
+  }
+
+
+  /* Return Z_BUF_ERROR if we were given an incomplete table */
+  return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, hp, z)
+uIntf *c;               /* 19 code lengths */
+uIntf *bb;              /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+inflate_huft *hp;       /* space for trees */
+z_streamp z;            /* for messages */
+{
+  int r;
+  uInt hn = 0;          /* hufts used in space */
+  uIntf *v;             /* work area for huft_build */
+
+  if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL)
+    return Z_MEM_ERROR;
+  r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
+                 tb, bb, hp, &hn, v);
+  if (r == Z_DATA_ERROR)
+    z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+  else if (r == Z_BUF_ERROR || *bb == 0)
+  {
+    z->msg = (char*)"incomplete dynamic bit lengths tree";
+    r = Z_DATA_ERROR;
+  }
+  ZFREE(z, v);
+  return r;
+}
+
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z)
+uInt nl;                /* number of literal/length codes */
+uInt nd;                /* number of distance codes */
+uIntf *c;               /* that many (total) code lengths */
+uIntf *bl;              /* literal desired/actual bit depth */
+uIntf *bd;              /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+inflate_huft *hp;       /* space for trees */
+z_streamp z;            /* for messages */
+{
+  int r;
+  uInt hn = 0;          /* hufts used in space */
+  uIntf *v;             /* work area for huft_build */
+
+  /* allocate work area */
+  if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+    return Z_MEM_ERROR;
+
+  /* build literal/length tree */
+  r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+  if (r != Z_OK || *bl == 0)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = (char*)"oversubscribed literal/length tree";
+    else if (r != Z_MEM_ERROR)
+    {
+      z->msg = (char*)"incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    ZFREE(z, v);
+    return r;
+  }
+
+  /* build distance tree */
+  r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+  if (r != Z_OK || (*bd == 0 && nl > 257))
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = (char*)"oversubscribed distance tree";
+    else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+      r = Z_OK;
+    }
+#else
+      z->msg = (char*)"incomplete distance tree";
+      r = Z_DATA_ERROR;
+    }
+    else if (r != Z_MEM_ERROR)
+    {
+      z->msg = (char*)"empty distance tree with lengths";
+      r = Z_DATA_ERROR;
+    }
+    ZFREE(z, v);
+    return r;
+#endif
+  }
+
+  /* done */
+  ZFREE(z, v);
+  return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+#ifdef BUILDFIXED
+local int fixed_built = 0;
+#define FIXEDH 544      /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+#else
+#include "inffixed.h"
+#endif
+
+
+int inflate_trees_fixed(bl, bd, tl, td, z)
+uIntf *bl;               /* literal desired/actual bit depth */
+uIntf *bd;               /* distance desired/actual bit depth */
+inflate_huft * FAR *tl;  /* literal/length tree result */
+inflate_huft * FAR *td;  /* distance tree result */
+z_streamp z;             /* for memory allocation */
+{
+#ifdef BUILDFIXED
+  /* build fixed tables if not already */
+  if (!fixed_built)
+  {
+    int k;              /* temporary variable */
+    uInt f = 0;         /* number of hufts used in fixed_mem */
+    uIntf *c;           /* length list for huft_build */
+    uIntf *v;           /* work area for huft_build */
+
+    /* allocate memory */
+    if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+      return Z_MEM_ERROR;
+    if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+    {
+      ZFREE(z, c);
+      return Z_MEM_ERROR;
+    }
+
+    /* literal table */
+    for (k = 0; k < 144; k++)
+      c[k] = 8;
+    for (; k < 256; k++)
+      c[k] = 9;
+    for (; k < 280; k++)
+      c[k] = 7;
+    for (; k < 288; k++)
+      c[k] = 8;
+    fixed_bl = 9;
+    huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl,
+               fixed_mem, &f, v);
+
+    /* distance table */
+    for (k = 0; k < 30; k++)
+      c[k] = 5;
+    fixed_bd = 5;
+    huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd,
+               fixed_mem, &f, v);
+
+    /* done */
+    ZFREE(z, v);
+    ZFREE(z, c);
+    fixed_built = 1;
+  }
+#endif
+  *bl = fixed_bl;
+  *bd = fixed_bd;
+  *tl = fixed_tl;
+  *td = fixed_td;
+  return Z_OK;
+}
diff --git a/zlib/inftrees.h b/zlib/inftrees.h
new file mode 100644
index 0000000..04b73b7
--- /dev/null
+++ b/zlib/inftrees.h
@@ -0,0 +1,58 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+  union {
+    struct {
+      Byte Exop;        /* number of extra bits or operation */
+      Byte Bits;        /* number of bits in this code or subcode */
+    } what;
+    uInt pad;           /* pad structure to a power of 2 (4 bytes for */
+  } word;               /*  16-bit, 8 bytes for 32-bit int's) */
+  uInt base;            /* literal, length base, distance base,
+                           or table offset */
+};
+
+/* Maximum size of dynamic tree.  The maximum found in a long but non-
+   exhaustive search was 1004 huft structures (850 for length/literals
+   and 154 for distances, the latter actually the result of an
+   exhaustive search).  The actual maximum is not known, but the
+   value below is more than safe. */
+#define MANY 1440
+
+extern int inflate_trees_bits OF((
+    uIntf *,                    /* 19 code lengths */
+    uIntf *,                    /* bits tree desired/actual depth */
+    inflate_huft * FAR *,       /* bits tree result */
+    inflate_huft *,             /* space for trees */
+    z_streamp));                /* for messages */
+
+extern int inflate_trees_dynamic OF((
+    uInt,                       /* number of literal/length codes */
+    uInt,                       /* number of distance codes */
+    uIntf *,                    /* that many (total) code lengths */
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *,       /* distance tree result */
+    inflate_huft *,             /* space for trees */
+    z_streamp));                /* for messages */
+
+extern int inflate_trees_fixed OF((
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *,       /* distance tree result */
+    z_streamp));                /* for memory allocation */
diff --git a/zlib/infutil.c b/zlib/infutil.c
new file mode 100644
index 0000000..9a07622
--- /dev/null
+++ b/zlib/infutil.c
@@ -0,0 +1,87 @@
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+  uInt n;
+  Bytef *p;
+  Bytef *q;
+
+  /* local copies of source and destination pointers */
+  p = z->next_out;
+  q = s->read;
+
+  /* compute number of bytes to copy as far as end of window */
+  n = (uInt)((q <= s->write ? s->write : s->end) - q);
+  if (n > z->avail_out) n = z->avail_out;
+  if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+  /* update counters */
+  z->avail_out -= n;
+  z->total_out += n;
+
+  /* update check information */
+  if (s->checkfn != Z_NULL)
+    z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+  /* copy as far as end of window */
+  zmemcpy(p, q, n);
+  p += n;
+  q += n;
+
+  /* see if more to copy at beginning of window */
+  if (q == s->end)
+  {
+    /* wrap pointers */
+    q = s->window;
+    if (s->write == s->end)
+      s->write = s->window;
+
+    /* compute bytes to copy */
+    n = (uInt)(s->write - q);
+    if (n > z->avail_out) n = z->avail_out;
+    if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+    /* update counters */
+    z->avail_out -= n;
+    z->total_out += n;
+
+    /* update check information */
+    if (s->checkfn != Z_NULL)
+      z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+    /* copy */
+    zmemcpy(p, q, n);
+    p += n;
+    q += n;
+  }
+
+  /* update pointers */
+  z->next_out = p;
+  s->read = q;
+
+  /* done */
+  return r;
+}
diff --git a/zlib/infutil.h b/zlib/infutil.h
new file mode 100644
index 0000000..4401df8
--- /dev/null
+++ b/zlib/infutil.h
@@ -0,0 +1,98 @@
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+      TYPE,     /* get type bits (3, including end bit) */
+      LENS,     /* get lengths for stored */
+      STORED,   /* processing stored block */
+      TABLE,    /* get table lengths */
+      BTREE,    /* get bit lengths tree for a dynamic block */
+      DTREE,    /* get length, distance trees for a dynamic block */
+      CODES,    /* processing fixed or dynamic block */
+      DRY,      /* output remaining window bytes */
+      DONE,     /* finished last block, done */
+      BAD}      /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+  /* mode */
+  inflate_block_mode  mode;     /* current inflate_block mode */
+
+  /* mode dependent information */
+  union {
+    uInt left;          /* if STORED, bytes left to copy */
+    struct {
+      uInt table;               /* table lengths (14 bits) */
+      uInt index;               /* index into blens (or border) */
+      uIntf *blens;             /* bit lengths of codes */
+      uInt bb;                  /* bit length tree depth */
+      inflate_huft *tb;         /* bit length decoding tree */
+    } trees;            /* if DTREE, decoding info for trees */
+    struct {
+      inflate_codes_statef 
+         *codes;
+    } decode;           /* if CODES, current state */
+  } sub;                /* submode */
+  uInt last;            /* true if this block is the last block */
+
+  /* mode independent information */
+  uInt bitk;            /* bits in bit buffer */
+  uLong bitb;           /* bit buffer */
+  inflate_huft *hufts;  /* single malloc for tree space */
+  Bytef *window;        /* sliding window */
+  Bytef *end;           /* one byte after sliding window */
+  Bytef *read;          /* window read pointer */
+  Bytef *write;         /* window write pointer */
+  check_func checkfn;   /* check function */
+  uLong check;          /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/*   update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/*   get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/*   output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/*   load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+extern uInt inflate_mask[17];
+
+/* copy as much as possible from the sliding window to the output area */
+extern int inflate_flush OF((
+    inflate_blocks_statef *,
+    z_streamp ,
+    int));
+
+struct internal_state      {int dummy;}; /* for buggy compilers */
+
+#endif
diff --git a/zlib/maketree.c b/zlib/maketree.c
new file mode 100644
index 0000000..a16d4b1
--- /dev/null
+++ b/zlib/maketree.c
@@ -0,0 +1,85 @@
+/* maketree.c -- make inffixed.h table for decoding fixed codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* This program is included in the distribution for completeness.
+   You do not need to compile or run this program since inffixed.h
+   is already included in the distribution.  To use this program
+   you need to compile zlib with BUILDFIXED defined and then compile
+   and link this program with the zlib library.  Then the output of
+   this program can be piped to inffixed.h. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "zutil.h"
+#include "inftrees.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* generate initialization table for an inflate_huft structure array */
+void maketree(uInt b, inflate_huft *t)
+{
+  int i, e;
+
+  i = 0;
+  while (1)
+  {
+    e = t[i].exop;
+    if (e && (e & (16+64)) == 0)        /* table pointer */
+    {
+      fprintf(stderr, "maketree: cannot initialize sub-tables!\n");
+      exit(1);
+    }
+    if (i % 4 == 0)
+      printf("\n   ");
+    printf(" {{{%u,%u}},%u}", t[i].exop, t[i].bits, t[i].base);
+    if (++i == (1<<b))
+      break;
+    putchar(',');
+  }
+  puts("");
+}
+
+/* create the fixed tables in C initialization syntax */
+void main(void)
+{
+  int r;
+  uInt bl, bd;
+  inflate_huft *tl, *td;
+  z_stream z;
+
+  z.zalloc = zcalloc;
+  z.opaque = (voidpf)0;
+  z.zfree = zcfree;
+  r = inflate_trees_fixed(&bl, &bd, &tl, &td, &z);
+  if (r)
+  {
+    fprintf(stderr, "inflate_trees_fixed error %d\n", r);
+    return;
+  }
+  puts("/* inffixed.h -- table for decoding fixed codes");
+  puts(" * Generated automatically by the maketree.c program");
+  puts(" */");
+  puts("");
+  puts("/* WARNING: this file should *not* be used by applications. It is");
+  puts("   part of the implementation of the compression library and is");
+  puts("   subject to change. Applications should only use zlib.h.");
+  puts(" */");
+  puts("");
+  printf("local uInt fixed_bl = %d;\n", bl);
+  printf("local uInt fixed_bd = %d;\n", bd);
+  printf("local inflate_huft fixed_tl[] = {");
+  maketree(bl, tl);
+  puts("  };");
+  printf("local inflate_huft fixed_td[] = {");
+  maketree(bd, td);
+  puts("  };");
+}
diff --git a/zlib/minigzip.c b/zlib/minigzip.c
new file mode 100644
index 0000000..8be02bd
--- /dev/null
+++ b/zlib/minigzip.c
@@ -0,0 +1,320 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $Id: minigzip.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#else
+   extern void exit  OF((int));
+#endif
+
+#ifdef USE_MMAP
+#  include <sys/types.h>
+#  include <sys/mman.h>
+#  include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32)
+#  include <fcntl.h>
+#  include <io.h>
+#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+#  define SET_BINARY_MODE(file)
+#endif
+
+#ifdef VMS
+#  define unlink delete
+#  define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+#  define unlink remove
+#  define GZ_SUFFIX "-gz"
+#  define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#  include <unix.h> /* for fileno */
+#endif
+
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+  extern int unlink OF((const char *));
+#endif
+
+#ifndef GZ_SUFFIX
+#  define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN      16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+#  define local static
+   /* Needed for systems with limitation on stack size. */
+#else
+#  define local
+#endif
+
+char *prog;
+
+void error            OF((const char *msg));
+void gz_compress      OF((FILE   *in, gzFile out));
+#ifdef USE_MMAP
+int  gz_compress_mmap OF((FILE   *in, gzFile out));
+#endif
+void gz_uncompress    OF((gzFile in, FILE   *out));
+void file_compress    OF((char  *file, char *mode));
+void file_uncompress  OF((char  *file));
+int  main             OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(msg)
+    const char *msg;
+{
+    fprintf(stderr, "%s: %s\n", prog, msg);
+    exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(in, out)
+    FILE   *in;
+    gzFile out;
+{
+    local char buf[BUFLEN];
+    int len;
+    int err;
+
+#ifdef USE_MMAP
+    /* Try first compressing with mmap. If mmap fails (minigzip used in a
+     * pipe), use the normal fread loop.
+     */
+    if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+    for (;;) {
+        len = fread(buf, 1, sizeof(buf), in);
+        if (ferror(in)) {
+            perror("fread");
+            exit(1);
+        }
+        if (len == 0) break;
+
+        if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+    }
+    fclose(in);
+    if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(in, out)
+    FILE   *in;
+    gzFile out;
+{
+    int len;
+    int err;
+    int ifd = fileno(in);
+    caddr_t buf;    /* mmap'ed buffer for the entire input file */
+    off_t buf_len;  /* length of the input file */
+    struct stat sb;
+
+    /* Determine the size of the file, needed for mmap: */
+    if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+    buf_len = sb.st_size;
+    if (buf_len <= 0) return Z_ERRNO;
+
+    /* Now do the actual mmap: */
+    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); 
+    if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+    /* Compress the whole file at once: */
+    len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+    if (len != (int)buf_len) error(gzerror(out, &err));
+
+    munmap(buf, buf_len);
+    fclose(in);
+    if (gzclose(out) != Z_OK) error("failed gzclose");
+    return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(in, out)
+    gzFile in;
+    FILE   *out;
+{
+    local char buf[BUFLEN];
+    int len;
+    int err;
+
+    for (;;) {
+        len = gzread(in, buf, sizeof(buf));
+        if (len < 0) error (gzerror(in, &err));
+        if (len == 0) break;
+
+        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+	    error("failed fwrite");
+	}
+    }
+    if (fclose(out)) error("failed fclose");
+
+    if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(file, mode)
+    char  *file;
+    char  *mode;
+{
+    local char outfile[MAX_NAME_LEN];
+    FILE  *in;
+    gzFile out;
+
+    strcpy(outfile, file);
+    strcat(outfile, GZ_SUFFIX);
+
+    in = fopen(file, "rb");
+    if (in == NULL) {
+        perror(file);
+        exit(1);
+    }
+    out = gzopen(outfile, mode);
+    if (out == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+        exit(1);
+    }
+    gz_compress(in, out);
+
+    unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(file)
+    char  *file;
+{
+    local char buf[MAX_NAME_LEN];
+    char *infile, *outfile;
+    FILE  *out;
+    gzFile in;
+    int len = strlen(file);
+
+    strcpy(buf, file);
+
+    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+        infile = file;
+        outfile = buf;
+        outfile[len-3] = '\0';
+    } else {
+        outfile = file;
+        infile = buf;
+        strcat(infile, GZ_SUFFIX);
+    }
+    in = gzopen(infile, "rb");
+    if (in == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+        exit(1);
+    }
+    out = fopen(outfile, "wb");
+    if (out == NULL) {
+        perror(file);
+        exit(1);
+    }
+
+    gz_uncompress(in, out);
+
+    unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage:  minigzip [-d] [-f] [-h] [-1 to -9] [files...]
+ *   -d : decompress
+ *   -f : compress with Z_FILTERED
+ *   -h : compress with Z_HUFFMAN_ONLY
+ *   -1 to -9 : compression level
+ */
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int uncompr = 0;
+    gzFile file;
+    char outmode[20];
+
+    strcpy(outmode, "wb6 ");
+
+    prog = argv[0];
+    argc--, argv++;
+
+    while (argc > 0) {
+      if (strcmp(*argv, "-d") == 0)
+	uncompr = 1;
+      else if (strcmp(*argv, "-f") == 0)
+	outmode[3] = 'f';
+      else if (strcmp(*argv, "-h") == 0)
+	outmode[3] = 'h';
+      else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+	       (*argv)[2] == 0)
+	outmode[2] = (*argv)[1];
+      else
+	break;
+      argc--, argv++;
+    }
+    if (argc == 0) {
+        SET_BINARY_MODE(stdin);
+        SET_BINARY_MODE(stdout);
+        if (uncompr) {
+            file = gzdopen(fileno(stdin), "rb");
+            if (file == NULL) error("can't gzdopen stdin");
+            gz_uncompress(file, stdout);
+        } else {
+            file = gzdopen(fileno(stdout), outmode);
+            if (file == NULL) error("can't gzdopen stdout");
+            gz_compress(stdin, file);
+        }
+    } else {
+        do {
+            if (uncompr) {
+                file_uncompress(*argv);
+            } else {
+                file_compress(*argv, outmode);
+            }
+        } while (argv++, --argc);
+    }
+    exit(0);
+    return 0; /* to avoid warning */
+}
diff --git a/zlib/trees.c b/zlib/trees.c
new file mode 100644
index 0000000..123c2b3
--- /dev/null
+++ b/zlib/trees.c
@@ -0,0 +1,1214 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values).  The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id: trees.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+#  include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN  512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+#  include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+    const ct_data *static_tree;  /* static tree or NULL */
+    const intf *extra_bits;      /* extra bits for each code or NULL */
+    int     extra_base;          /* base index for extra_bits */
+    int     elems;               /* max number of elements in the tree */
+    int     max_length;          /* max bit length for the codes */
+};
+
+local static_tree_desc  static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc  static_d_desc =
+{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
+
+local static_tree_desc  static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block     OF((deflate_state *s));
+local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
+local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree     OF((deflate_state *s, tree_desc *desc));
+local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local int  build_bl_tree  OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+                              int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+                              ct_data *dtree));
+local void set_data_type  OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup      OF((deflate_state *s));
+local void bi_flush       OF((deflate_state *s));
+local void copy_block     OF((deflate_state *s, charf *buf, unsigned len,
+                              int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+   /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+#  define send_code(s, c, tree) \
+     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+       send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+    put_byte(s, (uch)((w) & 0xff)); \
+    put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits      OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+    deflate_state *s;
+    int value;  /* value to send */
+    int length; /* number of bits */
+{
+    Tracevv((stderr," l %2d v %4x ", length, value));
+    Assert(length > 0 && length <= 15, "invalid length");
+    s->bits_sent += (ulg)length;
+
+    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+     * unused bits in value.
+     */
+    if (s->bi_valid > (int)Buf_size - length) {
+        s->bi_buf |= (value << s->bi_valid);
+        put_short(s, s->bi_buf);
+        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+        s->bi_valid += length - Buf_size;
+    } else {
+        s->bi_buf |= value << s->bi_valid;
+        s->bi_valid += length;
+    }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+  if (s->bi_valid > (int)Buf_size - len) {\
+    int val = value;\
+    s->bi_buf |= (val << s->bi_valid);\
+    put_short(s, s->bi_buf);\
+    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+    s->bi_valid += len - Buf_size;\
+  } else {\
+    s->bi_buf |= (value) << s->bi_valid;\
+    s->bi_valid += len;\
+  }\
+}
+#endif /* DEBUG */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+    static int static_init_done = 0;
+    int n;        /* iterates over tree elements */
+    int bits;     /* bit counter */
+    int length;   /* length value */
+    int code;     /* code value */
+    int dist;     /* distance index */
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    if (static_init_done) return;
+
+    /* For some embedded targets, global variables are not initialized: */
+    static_l_desc.static_tree = static_ltree;
+    static_l_desc.extra_bits = extra_lbits;
+    static_d_desc.static_tree = static_dtree;
+    static_d_desc.extra_bits = extra_dbits;
+    static_bl_desc.extra_bits = extra_blbits;
+
+    /* Initialize the mapping length (0..255) -> length code (0..28) */
+    length = 0;
+    for (code = 0; code < LENGTH_CODES-1; code++) {
+        base_length[code] = length;
+        for (n = 0; n < (1<<extra_lbits[code]); n++) {
+            _length_code[length++] = (uch)code;
+        }
+    }
+    Assert (length == 256, "tr_static_init: length != 256");
+    /* Note that the length 255 (match length 258) can be represented
+     * in two different ways: code 284 + 5 bits or code 285, so we
+     * overwrite length_code[255] to use the best encoding:
+     */
+    _length_code[length-1] = (uch)code;
+
+    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+    dist = 0;
+    for (code = 0 ; code < 16; code++) {
+        base_dist[code] = dist;
+        for (n = 0; n < (1<<extra_dbits[code]); n++) {
+            _dist_code[dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: dist != 256");
+    dist >>= 7; /* from now on, all distances are divided by 128 */
+    for ( ; code < D_CODES; code++) {
+        base_dist[code] = dist << 7;
+        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+            _dist_code[256 + dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+    /* Construct the codes of the static literal tree */
+    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+    n = 0;
+    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+    /* Codes 286 and 287 do not exist, but we must include them in the
+     * tree construction to get a canonical Huffman tree (longest code
+     * all ones)
+     */
+    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+    /* The static distance tree is trivial: */
+    for (n = 0; n < D_CODES; n++) {
+        static_dtree[n].Len = 5;
+        static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+    }
+    static_init_done = 1;
+
+#  ifdef GEN_TREES_H
+    gen_trees_header();
+#  endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+#  ifndef DEBUG
+#    include <stdio.h>
+#  endif
+
+#  define SEPARATOR(i, last, width) \
+      ((i) == (last)? "\n};\n\n" :    \
+       ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+    FILE *header = fopen("trees.h", "w");
+    int i;
+
+    Assert (header != NULL, "Can't open trees.h");
+    fprintf(header,
+	    "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+    for (i = 0; i < L_CODES+2; i++) {
+	fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+		static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+    }
+
+    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+	fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+		static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+    }
+
+    fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+    for (i = 0; i < DIST_CODE_LEN; i++) {
+	fprintf(header, "%2u%s", _dist_code[i],
+		SEPARATOR(i, DIST_CODE_LEN-1, 20));
+    }
+
+    fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+	fprintf(header, "%2u%s", _length_code[i],
+		SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+    }
+
+    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+    for (i = 0; i < LENGTH_CODES; i++) {
+	fprintf(header, "%1u%s", base_length[i],
+		SEPARATOR(i, LENGTH_CODES-1, 20));
+    }
+
+    fprintf(header, "local const int base_dist[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+	fprintf(header, "%5u%s", base_dist[i],
+		SEPARATOR(i, D_CODES-1, 10));
+    }
+
+    fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+    deflate_state *s;
+{
+    tr_static_init();
+
+    s->l_desc.dyn_tree = s->dyn_ltree;
+    s->l_desc.stat_desc = &static_l_desc;
+
+    s->d_desc.dyn_tree = s->dyn_dtree;
+    s->d_desc.stat_desc = &static_d_desc;
+
+    s->bl_desc.dyn_tree = s->bl_tree;
+    s->bl_desc.stat_desc = &static_bl_desc;
+
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+    s->compressed_len = 0L;
+    s->bits_sent = 0L;
+#endif
+
+    /* Initialize the first block of the first file: */
+    init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+    deflate_state *s;
+{
+    int n; /* iterates over tree elements */
+
+    /* Initialize the trees. */
+    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
+    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
+    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+    s->dyn_ltree[END_BLOCK].Freq = 1;
+    s->opt_len = s->static_len = 0L;
+    s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+    top = s->heap[SMALLEST]; \
+    s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+    pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+   (tree[n].Freq < tree[m].Freq || \
+   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+    deflate_state *s;
+    ct_data *tree;  /* the tree to restore */
+    int k;               /* node to move down */
+{
+    int v = s->heap[k];
+    int j = k << 1;  /* left son of k */
+    while (j <= s->heap_len) {
+        /* Set j to the smallest of the two sons: */
+        if (j < s->heap_len &&
+            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+            j++;
+        }
+        /* Exit if v is smaller than both sons */
+        if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+        /* Exchange v with the smallest son */
+        s->heap[k] = s->heap[j];  k = j;
+
+        /* And continue down the tree, setting j to the left son of k */
+        j <<= 1;
+    }
+    s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+local void gen_bitlen(s, desc)
+    deflate_state *s;
+    tree_desc *desc;    /* the tree descriptor */
+{
+    ct_data *tree        = desc->dyn_tree;
+    int max_code         = desc->max_code;
+    const ct_data *stree = desc->stat_desc->static_tree;
+    const intf *extra    = desc->stat_desc->extra_bits;
+    int base             = desc->stat_desc->extra_base;
+    int max_length       = desc->stat_desc->max_length;
+    int h;              /* heap index */
+    int n, m;           /* iterate over the tree elements */
+    int bits;           /* bit length */
+    int xbits;          /* extra bits */
+    ush f;              /* frequency */
+    int overflow = 0;   /* number of elements with bit length too large */
+
+    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+    /* In a first pass, compute the optimal bit lengths (which may
+     * overflow in the case of the bit length tree).
+     */
+    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+        n = s->heap[h];
+        bits = tree[tree[n].Dad].Len + 1;
+        if (bits > max_length) bits = max_length, overflow++;
+        tree[n].Len = (ush)bits;
+        /* We overwrite tree[n].Dad which is no longer needed */
+
+        if (n > max_code) continue; /* not a leaf node */
+
+        s->bl_count[bits]++;
+        xbits = 0;
+        if (n >= base) xbits = extra[n-base];
+        f = tree[n].Freq;
+        s->opt_len += (ulg)f * (bits + xbits);
+        if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+    }
+    if (overflow == 0) return;
+
+    Trace((stderr,"\nbit length overflow\n"));
+    /* This happens for example on obj2 and pic of the Calgary corpus */
+
+    /* Find the first bit length which could increase: */
+    do {
+        bits = max_length-1;
+        while (s->bl_count[bits] == 0) bits--;
+        s->bl_count[bits]--;      /* move one leaf down the tree */
+        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+        s->bl_count[max_length]--;
+        /* The brother of the overflow item also moves one step up,
+         * but this does not affect bl_count[max_length]
+         */
+        overflow -= 2;
+    } while (overflow > 0);
+
+    /* Now recompute all bit lengths, scanning in increasing frequency.
+     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+     * lengths instead of fixing only the wrong ones. This idea is taken
+     * from 'ar' written by Haruhiko Okumura.)
+     */
+    for (bits = max_length; bits != 0; bits--) {
+        n = s->bl_count[bits];
+        while (n != 0) {
+            m = s->heap[--h];
+            if (m > max_code) continue;
+            if (tree[m].Len != (unsigned) bits) {
+                Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+                s->opt_len += ((long)bits - (long)tree[m].Len)
+                              *(long)tree[m].Freq;
+                tree[m].Len = (ush)bits;
+            }
+            n--;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+    ct_data *tree;             /* the tree to decorate */
+    int max_code;              /* largest code with non zero frequency */
+    ushf *bl_count;            /* number of codes at each bit length */
+{
+    ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+    ush code = 0;              /* running code value */
+    int bits;                  /* bit index */
+    int n;                     /* code index */
+
+    /* The distribution counts are first used to generate the code values
+     * without bit reversal.
+     */
+    for (bits = 1; bits <= MAX_BITS; bits++) {
+        next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+    }
+    /* Check that the bit counts in bl_count are consistent. The last code
+     * must be all ones.
+     */
+    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+            "inconsistent bit counts");
+    Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+    for (n = 0;  n <= max_code; n++) {
+        int len = tree[n].Len;
+        if (len == 0) continue;
+        /* Now reverse the bits */
+        tree[n].Code = bi_reverse(next_code[len]++, len);
+
+        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+    }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+    deflate_state *s;
+    tree_desc *desc; /* the tree descriptor */
+{
+    ct_data *tree         = desc->dyn_tree;
+    const ct_data *stree  = desc->stat_desc->static_tree;
+    int elems             = desc->stat_desc->elems;
+    int n, m;          /* iterate over heap elements */
+    int max_code = -1; /* largest code with non zero frequency */
+    int node;          /* new node being created */
+
+    /* Construct the initial heap, with least frequent element in
+     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+     * heap[0] is not used.
+     */
+    s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+    for (n = 0; n < elems; n++) {
+        if (tree[n].Freq != 0) {
+            s->heap[++(s->heap_len)] = max_code = n;
+            s->depth[n] = 0;
+        } else {
+            tree[n].Len = 0;
+        }
+    }
+
+    /* The pkzip format requires that at least one distance code exists,
+     * and that at least one bit should be sent even if there is only one
+     * possible code. So to avoid special checks later on we force at least
+     * two codes of non zero frequency.
+     */
+    while (s->heap_len < 2) {
+        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+        tree[node].Freq = 1;
+        s->depth[node] = 0;
+        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+        /* node is 0 or 1 so it does not have extra bits */
+    }
+    desc->max_code = max_code;
+
+    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+     * establish sub-heaps of increasing lengths:
+     */
+    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+    /* Construct the Huffman tree by repeatedly combining the least two
+     * frequent nodes.
+     */
+    node = elems;              /* next internal node of the tree */
+    do {
+        pqremove(s, tree, n);  /* n = node of least frequency */
+        m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+        s->heap[--(s->heap_max)] = m;
+
+        /* Create a new node father of n and m */
+        tree[node].Freq = tree[n].Freq + tree[m].Freq;
+        s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+        tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+        if (tree == s->bl_tree) {
+            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+        }
+#endif
+        /* and insert the new node in the heap */
+        s->heap[SMALLEST] = node++;
+        pqdownheap(s, tree, SMALLEST);
+
+    } while (s->heap_len >= 2);
+
+    s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+    /* At this point, the fields freq and dad are set. We can now
+     * generate the bit lengths.
+     */
+    gen_bitlen(s, (tree_desc *)desc);
+
+    /* The field len is now set, we can generate the bit codes */
+    gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree;   /* the tree to be scanned */
+    int max_code;    /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    if (nextlen == 0) max_count = 138, min_count = 3;
+    tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            s->bl_tree[curlen].Freq += count;
+        } else if (curlen != 0) {
+            if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+            s->bl_tree[REP_3_6].Freq++;
+        } else if (count <= 10) {
+            s->bl_tree[REPZ_3_10].Freq++;
+        } else {
+            s->bl_tree[REPZ_11_138].Freq++;
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree; /* the tree to be scanned */
+    int max_code;       /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    /* tree[max_code+1].Len = -1; */  /* guard already set */
+    if (nextlen == 0) max_count = 138, min_count = 3;
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+        } else if (curlen != 0) {
+            if (curlen != prevlen) {
+                send_code(s, curlen, s->bl_tree); count--;
+            }
+            Assert(count >= 3 && count <= 6, " 3_6?");
+            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+        } else if (count <= 10) {
+            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+        } else {
+            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+    deflate_state *s;
+{
+    int max_blindex;  /* index of last bit length code of non zero freq */
+
+    /* Determine the bit length frequencies for literal and distance trees */
+    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+    /* Build the bit length tree: */
+    build_tree(s, (tree_desc *)(&(s->bl_desc)));
+    /* opt_len now includes the length of the tree representations, except
+     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+     */
+
+    /* Determine the number of bit length codes to send. The pkzip format
+     * requires that at least 4 bit length codes be sent. (appnote.txt says
+     * 3 but the actual value used is 4.)
+     */
+    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+    }
+    /* Update opt_len to include the bit length tree and counts */
+    s->opt_len += 3*(max_blindex+1) + 5+5+4;
+    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+            s->opt_len, s->static_len));
+
+    return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+    deflate_state *s;
+    int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+    int rank;                    /* index in bl_order */
+
+    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+            "too many codes");
+    Tracev((stderr, "\nbl counts: "));
+    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+    send_bits(s, dcodes-1,   5);
+    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
+    for (rank = 0; rank < blcodes; rank++) {
+        Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+    }
+    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    send_bits(s, (STORED_BLOCK<<1)+eof, 3);  /* send block type */
+#ifdef DEBUG
+    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+    s->compressed_len += (stored_len + 4) << 3;
+#endif
+    copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+    deflate_state *s;
+{
+    send_bits(s, STATIC_TREES<<1, 3);
+    send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+    bi_flush(s);
+    /* Of the 10 bits for the empty block, we have already sent
+     * (10 - bi_valid) bits. The lookahead for the last real code (before
+     * the EOB of the previous block) was thus at least one plus the length
+     * of the EOB plus what we have just sent of the empty static block.
+     */
+    if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+        send_bits(s, STATIC_TREES<<1, 3);
+        send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+        s->compressed_len += 10L;
+#endif
+        bi_flush(s);
+    }
+    s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block, or NULL if too old */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+    int max_blindex = 0;  /* index of last bit length code of non zero freq */
+
+    /* Build the Huffman trees unless a stored block is forced */
+    if (s->level > 0) {
+
+	 /* Check if the file is ascii or binary */
+	if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+	/* Construct the literal and distance trees */
+	build_tree(s, (tree_desc *)(&(s->l_desc)));
+	Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+		s->static_len));
+
+	build_tree(s, (tree_desc *)(&(s->d_desc)));
+	Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+		s->static_len));
+	/* At this point, opt_len and static_len are the total bit lengths of
+	 * the compressed block data, excluding the tree representations.
+	 */
+
+	/* Build the bit length tree for the above two trees, and get the index
+	 * in bl_order of the last bit length code to send.
+	 */
+	max_blindex = build_bl_tree(s);
+
+	/* Determine the best encoding. Compute first the block length in bytes*/
+	opt_lenb = (s->opt_len+3+7)>>3;
+	static_lenb = (s->static_len+3+7)>>3;
+
+	Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+		opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+		s->last_lit));
+
+	if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+    } else {
+        Assert(buf != (char*)0, "lost buf");
+	opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+    }
+
+#ifdef FORCE_STORED
+    if (buf != (char*)0) { /* force stored block */
+#else
+    if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+                       /* 4: two words for the lengths */
+#endif
+        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+         * Otherwise we can't have processed more than WSIZE input bytes since
+         * the last block flush, because compression would have been
+         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+         * transform a block into a stored block.
+         */
+        _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+    } else if (static_lenb >= 0) { /* force static trees */
+#else
+    } else if (static_lenb == opt_lenb) {
+#endif
+        send_bits(s, (STATIC_TREES<<1)+eof, 3);
+        compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->static_len;
+#endif
+    } else {
+        send_bits(s, (DYN_TREES<<1)+eof, 3);
+        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+                       max_blindex+1);
+        compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->opt_len;
+#endif
+    }
+    Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+    /* The above check is made mod 2^32, for files larger than 512 MB
+     * and uLong implemented on 32 bits.
+     */
+    init_block(s);
+
+    if (eof) {
+        bi_windup(s);
+#ifdef DEBUG
+        s->compressed_len += 7;  /* align on byte boundary */
+#endif
+    }
+    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+           s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+    deflate_state *s;
+    unsigned dist;  /* distance of matched string */
+    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+    s->d_buf[s->last_lit] = (ush)dist;
+    s->l_buf[s->last_lit++] = (uch)lc;
+    if (dist == 0) {
+        /* lc is the unmatched char */
+        s->dyn_ltree[lc].Freq++;
+    } else {
+        s->matches++;
+        /* Here, lc is the match length - MIN_MATCH */
+        dist--;             /* dist = match distance - 1 */
+        Assert((ush)dist < (ush)MAX_DIST(s) &&
+               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");
+
+        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+        s->dyn_dtree[d_code(dist)].Freq++;
+    }
+
+#ifdef TRUNCATE_BLOCK
+    /* Try to guess if it is profitable to stop the current block here */
+    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+        /* Compute an upper bound for the compressed length */
+        ulg out_length = (ulg)s->last_lit*8L;
+        ulg in_length = (ulg)((long)s->strstart - s->block_start);
+        int dcode;
+        for (dcode = 0; dcode < D_CODES; dcode++) {
+            out_length += (ulg)s->dyn_dtree[dcode].Freq *
+                (5L+extra_dbits[dcode]);
+        }
+        out_length >>= 3;
+        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+               s->last_lit, in_length, out_length,
+               100L - out_length*100L/in_length));
+        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+    }
+#endif
+    return (s->last_lit == s->lit_bufsize-1);
+    /* We avoid equality with lit_bufsize because of wraparound at 64K
+     * on 16 bit machines and because stored blocks are restricted to
+     * 64K-1 bytes.
+     */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+    deflate_state *s;
+    ct_data *ltree; /* literal tree */
+    ct_data *dtree; /* distance tree */
+{
+    unsigned dist;      /* distance of matched string */
+    int lc;             /* match length or unmatched char (if dist == 0) */
+    unsigned lx = 0;    /* running index in l_buf */
+    unsigned code;      /* the code to send */
+    int extra;          /* number of extra bits to send */
+
+    if (s->last_lit != 0) do {
+        dist = s->d_buf[lx];
+        lc = s->l_buf[lx++];
+        if (dist == 0) {
+            send_code(s, lc, ltree); /* send a literal byte */
+            Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+        } else {
+            /* Here, lc is the match length - MIN_MATCH */
+            code = _length_code[lc];
+            send_code(s, code+LITERALS+1, ltree); /* send the length code */
+            extra = extra_lbits[code];
+            if (extra != 0) {
+                lc -= base_length[code];
+                send_bits(s, lc, extra);       /* send the extra length bits */
+            }
+            dist--; /* dist is now the match distance - 1 */
+            code = d_code(dist);
+            Assert (code < D_CODES, "bad d_code");
+
+            send_code(s, code, dtree);       /* send the distance code */
+            extra = extra_dbits[code];
+            if (extra != 0) {
+                dist -= base_dist[code];
+                send_bits(s, dist, extra);   /* send the extra distance bits */
+            }
+        } /* literal or match pair ? */
+
+        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+        Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+    } while (lx < s->last_lit);
+
+    send_code(s, END_BLOCK, ltree);
+    s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+    deflate_state *s;
+{
+    int n = 0;
+    unsigned ascii_freq = 0;
+    unsigned bin_freq = 0;
+    while (n < 7)        bin_freq += s->dyn_ltree[n++].Freq;
+    while (n < 128)    ascii_freq += s->dyn_ltree[n++].Freq;
+    while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+    s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+    unsigned code; /* the value to invert */
+    int len;       /* its bit length */
+{
+    register unsigned res = 0;
+    do {
+        res |= code & 1;
+        code >>= 1, res <<= 1;
+    } while (--len > 0);
+    return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+    deflate_state *s;
+{
+    if (s->bi_valid == 16) {
+        put_short(s, s->bi_buf);
+        s->bi_buf = 0;
+        s->bi_valid = 0;
+    } else if (s->bi_valid >= 8) {
+        put_byte(s, (Byte)s->bi_buf);
+        s->bi_buf >>= 8;
+        s->bi_valid -= 8;
+    }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+    deflate_state *s;
+{
+    if (s->bi_valid > 8) {
+        put_short(s, s->bi_buf);
+    } else if (s->bi_valid > 0) {
+        put_byte(s, (Byte)s->bi_buf);
+    }
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+#ifdef DEBUG
+    s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+    deflate_state *s;
+    charf    *buf;    /* the input data */
+    unsigned len;     /* its length */
+    int      header;  /* true if block header must be written */
+{
+    bi_windup(s);        /* align on byte boundary */
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+
+    if (header) {
+        put_short(s, (ush)len);   
+        put_short(s, (ush)~len);
+#ifdef DEBUG
+        s->bits_sent += 2*16;
+#endif
+    }
+#ifdef DEBUG
+    s->bits_sent += (ulg)len<<3;
+#endif
+    while (len--) {
+        put_byte(s, *buf++);
+    }
+}
diff --git a/zlib/trees.h b/zlib/trees.h
new file mode 100644
index 0000000..72facf9
--- /dev/null
+++ b/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
+{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
+{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
+{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
+{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
+{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
+{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
+{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
+{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
+{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
+{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
+{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
+{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
+{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
+{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
+{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
+{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
+{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
+{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
+{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
+{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
+{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
+{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
+{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
+{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
+{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
+{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
+{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
+{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
+{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
+{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
+{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
+{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
+{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
+{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
+{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
+{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
+{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
+{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
+{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
+{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
+{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
+{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
+{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
+{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
+{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
+{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
+{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
+{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
+{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
+{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
+{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
+{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
+{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
+{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
+{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
+{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
+{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
+ 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
+   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
+ 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
+};
+
diff --git a/zlib/uncompr.c b/zlib/uncompr.c
new file mode 100644
index 0000000..d8a4a69
--- /dev/null
+++ b/zlib/uncompr.c
@@ -0,0 +1,58 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: uncompr.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#include "zlib.h"
+
+/* ===========================================================================
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+
+    err = inflateInit(&stream);
+    if (err != Z_OK) return err;
+
+    err = inflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        inflateEnd(&stream);
+        return err == Z_OK ? Z_BUF_ERROR : err;
+    }
+    *destLen = stream.total_out;
+
+    err = inflateEnd(&stream);
+    return err;
+}
diff --git a/zlib/zconf.h b/zlib/zconf.h
new file mode 100644
index 0000000..c8d6ce9
--- /dev/null
+++ b/zlib/zconf.h
@@ -0,0 +1,279 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: zconf.h,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+#  define deflateInit_	z_deflateInit_
+#  define deflate	z_deflate
+#  define deflateEnd	z_deflateEnd
+#  define inflateInit_ 	z_inflateInit_
+#  define inflate	z_inflate
+#  define inflateEnd	z_inflateEnd
+#  define deflateInit2_	z_deflateInit2_
+#  define deflateSetDictionary z_deflateSetDictionary
+#  define deflateCopy	z_deflateCopy
+#  define deflateReset	z_deflateReset
+#  define deflateParams	z_deflateParams
+#  define inflateInit2_	z_inflateInit2_
+#  define inflateSetDictionary z_inflateSetDictionary
+#  define inflateSync	z_inflateSync
+#  define inflateSyncPoint z_inflateSyncPoint
+#  define inflateReset	z_inflateReset
+#  define compress	z_compress
+#  define compress2	z_compress2
+#  define uncompress	z_uncompress
+#  define adler32	z_adler32
+#  define crc32		z_crc32
+#  define get_crc_table z_get_crc_table
+
+#  define Byte		z_Byte
+#  define uInt		z_uInt
+#  define uLong		z_uLong
+#  define Bytef	        z_Bytef
+#  define charf		z_charf
+#  define intf		z_intf
+#  define uIntf		z_uIntf
+#  define uLongf	z_uLongf
+#  define voidpf	z_voidpf
+#  define voidp		z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+#  define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+#  ifndef __32BIT__
+#    define __32BIT__
+#  endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32))  && !defined(STDC)
+#  define STDC
+#endif
+#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
+#  ifndef STDC
+#    define STDC
+#  endif
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const
+#  endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Old Borland C incorrectly complains about missing returns: */
+#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
+#  define NEED_DUMMY_RETURN
+#endif
+
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+   /* MSC small or medium model */
+#  define SMALL_MEDIUM
+#  ifdef _MSC_VER
+#    define FAR _far
+#  else
+#    define FAR far
+#  endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+#  ifndef __32BIT__
+#    define SMALL_MEDIUM
+#    define FAR _far
+#  endif
+#endif
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if defined(ZLIB_DLL)
+#  if defined(_WINDOWS) || defined(WINDOWS)
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+#    define ZEXPORT  WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA  WINAPIV
+#    else
+#      define ZEXPORTVA  FAR _cdecl _export
+#    endif
+#  endif
+#  if defined (__BORLANDC__)
+#    if (__BORLANDC__ >= 0x0500) && defined (WIN32)
+#      include <windows.h>
+#      define ZEXPORT __declspec(dllexport) WINAPI
+#      define ZEXPORTRVA __declspec(dllexport) WINAPIV
+#    else
+#      if defined (_Windows) && defined (__DLL__)
+#        define ZEXPORT _export
+#        define ZEXPORTVA _export
+#      endif
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  if defined (ZLIB_DLL)
+#    define ZEXTERN extern __declspec(dllexport)
+#  else
+#    define ZEXTERN extern __declspec(dllimport)
+#  endif
+#endif
+
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+
+#ifndef FAR
+#   define FAR
+#endif
+
+#if !defined(MACOS) && !defined(TARGET_OS_MAC)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void FAR *voidpf;
+   typedef void     *voidp;
+#else
+   typedef Byte FAR *voidpf;
+   typedef Byte     *voidp;
+#endif
+
+#ifdef HAVE_UNISTD_H
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  define z_off_t  off_t
+#endif
+#ifndef SEEK_SET
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+#  define  z_off_t long
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+#   pragma map(deflateInit_,"DEIN")
+#   pragma map(deflateInit2_,"DEIN2")
+#   pragma map(deflateEnd,"DEEND")
+#   pragma map(inflateInit_,"ININ")
+#   pragma map(inflateInit2_,"ININ2")
+#   pragma map(inflateEnd,"INEND")
+#   pragma map(inflateSync,"INSY")
+#   pragma map(inflateSetDictionary,"INSEDI")
+#   pragma map(inflate_blocks,"INBL")
+#   pragma map(inflate_blocks_new,"INBLNE")
+#   pragma map(inflate_blocks_free,"INBLFR")
+#   pragma map(inflate_blocks_reset,"INBLRE")
+#   pragma map(inflate_codes_free,"INCOFR")
+#   pragma map(inflate_codes,"INCO")
+#   pragma map(inflate_fast,"INFA")
+#   pragma map(inflate_flush,"INFLU")
+#   pragma map(inflate_mask,"INMA")
+#   pragma map(inflate_set_dictionary,"INSEDI2")
+#   pragma map(inflate_copyright,"INCOPY")
+#   pragma map(inflate_trees_bits,"INTRBI")
+#   pragma map(inflate_trees_dynamic,"INTRDY")
+#   pragma map(inflate_trees_fixed,"INTRFI")
+#   pragma map(inflate_trees_free,"INTRFR")
+#endif
+
+#endif /* _ZCONF_H */
diff --git a/zlib/zlib.3 b/zlib/zlib.3
new file mode 100644
index 0000000..3a6e450
--- /dev/null
+++ b/zlib/zlib.3
@@ -0,0 +1,107 @@
+.TH ZLIB 3 "11 March 2002"
+.SH NAME
+zlib \- compression/decompression library
+.SH SYNOPSIS
+[see
+.I zlib.h
+for full description]
+.SH DESCRIPTION
+The
+.I zlib
+library is a general purpose data compression library.
+The code is thread safe.
+It provides in-memory compression and decompression functions,
+including integrity checks of the uncompressed data.
+This version of the library supports only one compression method (deflation)
+but other algorithms will be added later and will have the same stream interface.
+.LP
+Compression can be done in a single step if the buffers are large enough
+(for example if an input file is mmap'ed),
+or can be done by repeated calls of the compression function.
+In the latter case,
+the application must provide more input and/or consume the output
+(providing more output space) before each call.
+.LP
+The library also supports reading and writing files in
+.I gzip
+(.gz) format
+with an interface similar to that of stdio.
+.LP
+The library does not install any signal handler. The decoder checks
+the consistency of the compressed data, so the library should never
+crash even in case of corrupted input.
+.LP
+All functions of the compression library are documented in the file
+.IR zlib.h.
+The distribution source includes examples of use of the library
+the files
+.I example.c
+and
+.IR minigzip.c .
+.LP
+A Java implementation of
+.IR zlib
+is available in the Java Development Kit 1.1
+.IP
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+.LP
+A Perl interface to
+.IR zlib ,
+written by Paul Marquess (pmarquess@bfsec.bt.co.uk)
+is available at CPAN (Comprehensive Perl Archive Network) sites,
+such as:
+.IP
+ftp://ftp.cis.ufl.edu/pub/perl/CPAN/modules/by-module/Compress/Compress-Zlib*
+.LP
+A Python interface to
+.IR zlib
+written by A.M. Kuchling <amk@magnet.com>
+is available from the Python Software Association sites, such as:
+.IP
+ftp://ftp.python.org/pub/python/contrib/Encoding/zlib*.tar.gz
+.SH "SEE ALSO"
+Questions about zlib should be sent to:
+.IP
+zlib@quest.jpl.nasa.gov
+or, if this fails, to the author addresses given below.
+The zlib home page is:
+.IP
+http://www.cdrom.com/pub/infozip/zlib/
+.LP
+The data format used by the zlib library is described by RFC
+(Request for Comments) 1950 to 1952 in the files: 
+.IP
+ftp://ds.internic.net/rfc/rfc1950.txt (zlib format)
+.br
+rfc1951.txt (deflate format)
+.br
+rfc1952.txt (gzip format)
+.LP
+These documents are also available in other formats from:
+.IP
+ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+.SH AUTHORS
+Version 1.1.4
+Copyright (C) 1995-2002 Jean-loup Gailly (jloup@gzip.org)
+and Mark Adler (madler@alumni.caltech.edu).
+.LP
+This software is provided "as-is,"
+without any express or implied warranty.
+In no event will the authors be held liable for any damages
+arising from the use of this software.
+See the distribution directory with respect to requirements
+governing redistribution.
+The deflate format used by
+.I zlib
+was defined by Phil Katz.
+The deflate and
+.I zlib
+specifications were written by L. Peter Deutsch.
+Thanks to all the people who reported problems and suggested various
+improvements in
+.IR zlib ;
+who are too numerous to cite here.
+.LP
+UNIX manual page by R. P. C. Rodgers,
+U.S. National Library of Medicine (rodgers@nlm.nih.gov).
+.\" end of man page
diff --git a/zlib/zlib.dsp b/zlib/zlib.dsp
new file mode 100644
index 0000000..18f00b4
--- /dev/null
+++ b/zlib/zlib.dsp
@@ -0,0 +1,217 @@
+# Microsoft Developer Studio Project File - Name="zlib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=zlib - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "zlib.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "zlib.mak" CFG="zlib - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "zlib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "zlib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "zlib - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "zlib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "zlib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "zlib - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "zlib___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "zlib___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "zlib - Win32 Release"
+# Name "zlib - Win32 Debug"
+# Name "zlib - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\adler32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\compress.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\crc32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\deflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\gzio.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\uncompr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\deflate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644
index 0000000..52cb529
--- /dev/null
+++ b/zlib/zlib.h
@@ -0,0 +1,893 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.1.4, March 11th, 2002
+
+  Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.1.4"
+
+/* 
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms will be added later and will have the same
+  stream interface.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+     The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio.
+
+     The library does not install any signal handler. The decoder checks
+  the consistency of the compressed data, so the library should never
+  crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: ascii or binary */
+    uLong   adler;      /* adler32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.
+
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_ASCII    1
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+   This check is automatically made by deflateInit and inflateInit.
+ */
+
+/* 
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression. The fields
+   zalloc, zfree and opaque must be initialized before by the caller.
+   If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+   use default allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at
+   all (the input data is simply copied a block at a time).
+   Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+   compression (currently equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).
+   msg is set to null if there is no error message.  deflateInit does not
+   perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce some
+  output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows. deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly. This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).
+    Some output may be provided even if flush is not set.
+
+  Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating avail_in or avail_out accordingly; avail_out
+  should never be zero before the call. The application can consume the
+  compressed output when it wants, for example when the output buffer is full
+  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+  and with zero avail_out, it must be called again after making room in the
+  output buffer because there might be more output pending.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far. (In particular
+  avail_in is zero after the call if enough output space has been provided
+  before the call.)  Flushing may degrade compression for some compression
+  algorithms and so it should be used only when necessary.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+  the compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out).
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there
+  was enough output space; if deflate returns with Z_OK, this function must be
+  called again with Z_FINISH and more output space (updated avail_out) but no
+  more input data, until it returns with Z_STREAM_END or an error. After
+  deflate has returned Z_STREAM_END, the only possible operations on the
+  stream are deflateReset or deflateEnd.
+  
+    Z_FINISH can be used immediately after deflateInit if all the compression
+  is to be done in a single step. In this case, avail_out must be at least
+  0.1% larger than avail_in plus 12 bytes.  If deflate does not return
+  Z_STREAM_END, then it must be called again as described above.
+
+    deflate() sets strm->adler to the adler32 checksum of all input read
+  so far (that is, total_in bytes).
+
+    deflate() may update data_type if it can make a good guess about
+  the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+  binary. This field is only for information purposes and does not affect
+  the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+  (for example avail_in or avail_out was zero).
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded). In the error case,
+   msg may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/* 
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression. The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+   value depends on the compression method), inflateInit determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly; otherwise the allocation will be deferred to the first call of
+   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+   use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller.  msg is set to null if there is no error
+   message. inflateInit does not perform any decompression apart from reading
+   the zlib header if present: this will be done by inflate().  (So next_in and
+   avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may some
+  introduce some output latency (reading input without producing any output)
+  except when forced to flush.
+
+  The detailed semantics are as follows. inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there
+    is no more input data or no more space in the output buffer (see below
+    about the flush parameter).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+  must be called again after making room in the output buffer because there
+  might be more output pending.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
+  output as possible to the output buffer. The flushing behavior of inflate is
+  not specified for values of the flush parameter other than Z_SYNC_FLUSH
+  and Z_FINISH, but the current implementation actually flushes as much output
+  as possible anyway.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster routine
+  may be used for the single inflate() call.
+
+     If a preset dictionary is needed at this point (see inflateSetDictionary
+  below), inflate sets strm-adler to the adler32 checksum of the
+  dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise 
+  it sets strm->adler to the adler32 checksum of all output produced
+  so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
+  an error code as described below. At the end of the stream, inflate()
+  checks that its computed adler32 checksum is equal to that saved by the
+  compressor and returns Z_STREAM_END only if the checksum is correct.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect
+  adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
+  (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
+  enough memory, Z_BUF_ERROR if no progress is possible or if there was not
+  enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
+  case, the application may then call inflateSync to look for a good
+  compression block.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*   
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options. The
+   fields next_in, zalloc, zfree and opaque must be initialized before by
+   the caller.
+
+     The method parameter is the compression method. It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library. Larger values of this parameter result in better
+   compression at the expense of memory usage. The default value is 15 if
+   deflateInit is used instead.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state. memLevel=1 uses minimum memory but
+   is slow and reduces compression ratio; memLevel=9 uses maximum memory
+   for optimal speed. The default value is 8. See zconf.h for total memory
+   usage as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm. Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match).  Filtered data consists mostly of small values with a
+   somewhat random distribution. In this case, the compression algorithm is
+   tuned to compress them better. The effect of Z_FILTERED is to force more
+   Huffman coding and less string matching; it is somewhat intermediate
+   between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+   the compression ratio but not the correctness of the compressed output even
+   if it is not set appropriately.
+
+      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+   method). msg is set to null if there is no error message.  deflateInit2 does
+   not perform any compression: this will be done by deflate().
+*/
+                            
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output. This function must be called
+   immediately after deflateInit, deflateInit2 or deflateReset, before any
+   call of deflate. The compressor and decompressor must use exactly the same
+   dictionary (see inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary. Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size in
+   deflate or deflate2. Thus the strings most likely to be useful should be
+   put at the end of the dictionary, not at the front.
+
+     Upon return of this function, strm->adler is set to the Adler32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor. (The Adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.)
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if the compression method is bsort). deflateSetDictionary does not
+   perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter. The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and
+   can consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit,
+   but does not free and reallocate all the internal compression state.
+   The stream will keep the same compression level and any other attributes
+   that may have been set by deflateInit2.
+
+      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+				      int level,
+				      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different
+   strategy. If the compression level is changed, the input available so far
+   is compressed with the old level (and may be flushed); the new level will
+   take effect only at the next call of deflate().
+
+     Before the call of deflateParams, the stream state must be set as for
+   a call of deflate(), since the currently available input may have to
+   be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+   if strm->avail_out was zero.
+*/
+
+/*   
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter. The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library. The default value is 15 if inflateInit is used
+   instead. If a compressed stream with a larger window size is given as
+   input, inflate() will return with the error code Z_DATA_ERROR instead of
+   trying to allocate a larger window.
+
+      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+   memLevel). msg is set to null if there is no error message.  inflateInit2
+   does not perform any decompression apart from reading the zlib header if
+   present: this will be done by inflate(). (So next_in and avail_in may be
+   modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence. This function must be called immediately after a call of inflate
+   if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+   can be determined from the Adler32 value returned by this call of
+   inflate. The compressor and decompressor must use exactly the same
+   dictionary (see deflateSetDictionary).
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect Adler32 value). inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/* 
+    Skips invalid compressed data until a full flush point (see above the
+  description of deflate with Z_FULL_FLUSH) can be found, or until all
+  available input is skipped. No output is provided.
+
+    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the
+   basic stream-oriented functions. To simplify the interface, some
+   default options are assumed (compression level and memory usage,
+   standard memory allocation functions). The source code of these
+   utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be at least 0.1% larger than
+   sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+   compressed buffer.
+     This function can be used to compress a whole file at once if the
+   input file is mmap'ed.
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode));
+/*
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb") but can also include a compression level
+   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+   Huffman only compression as in "wb1h". (See the description
+   of deflateInit2 for more information about the strategy parameter.)
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.
+
+     gzopen returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).  */
+
+ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode));
+/*
+     gzdopen() associates a gzFile with the file descriptor fd.  File
+   descriptors are obtained from calls like open, dup, creat, pipe or
+   fileno (in the file has been previously opened with fopen).
+   The mode parameter is as in gzopen.
+     The next call of gzclose on the returned gzFile will also close the
+   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+     gzdopen returns NULL if there was insufficient memory to allocate
+   the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy. See the description
+   of deflateInit2 for the meaning of these parameters.
+     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+   opened for writing.
+*/
+
+ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.
+   If the input file was not in gzip format, gzread copies the given number
+   of bytes into the buffer.
+     gzread returns the number of uncompressed bytes actually read (0 for
+   end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT    gzwrite OF((gzFile file, 
+				   const voidp buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes actually written
+   (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+      Reads bytes from the compressed file until len-1 characters are read, or
+   a newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  The string is then terminated with a null
+   character.
+      gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c));
+/*
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzgetc OF((gzFile file));
+/*
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function. The return value is the zlib
+   error number (see function gzerror below). gzflush returns Z_OK if
+   the flush parameter is Z_FINISH and all output could be flushed.
+     gzflush should be called only when strictly necessary because it can
+   degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file,
+				      z_off_t offset, int whence));
+/* 
+      Sets the starting position for the next gzread or gzwrite on the
+   given compressed file. The offset represents a number of bytes in the
+   uncompressed data stream. The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow. If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+/*
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+
+   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state. The return value is the zlib
+   error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the
+   compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running crc with the bytes buf[0..len-1] and return the updated
+   crc. If buf is NULL, this function returns the required initial value
+   for the crc. Pre- and post-conditioning (one's complement) is performed
+   within this function so it shouldn't be done by the application.
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+#define deflateInit(strm, level) \
+        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                      (strategy),           ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char   * ZEXPORT zError           OF((int err));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
diff --git a/zlib/zlib.html b/zlib/zlib.html
new file mode 100644
index 0000000..c343703
--- /dev/null
+++ b/zlib/zlib.html
@@ -0,0 +1,971 @@
+<html>
+<head>
+ <title>
+ zlib general purpose compression library version 1.1.4
+ </title>
+</head>
+<body bgcolor="White" text="Black" vlink="Red" alink="Navy" link="Red">
+<!-- background="zlibbg.gif" -->
+
+<h1> zlib 1.1.4 Manual </h1>
+<hr>
+<a name="Contents"><h2>Contents</h2>
+<ol type="I">
+<li> <a href="#Prologue">Prologue</a>
+<li> <a href="#Introduction">Introduction</a>
+<li> <a href="#Utility functions">Utility functions</a>
+<li> <a href="#Basic functions">Basic functions</a>
+<li> <a href="#Advanced functions">Advanced functions</a>
+<li> <a href="#Constants">Constants</a>
+<li> <a href="#struct z_stream_s">struct z_stream_s</a>
+<li> <a href="#Checksum functions">Checksum functions</a>
+<li> <a href="#Misc">Misc</a>
+</ol>
+<hr>
+<a name="Prologue"><h2> Prologue </h2>
+  'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+  <p>
+  Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+  <p>
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+  <p>
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+  <ol>
+   <li> The origin of this software must not be misrepresented ; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+   <li> Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+   <li> This notice may not be removed or altered from any source distribution.
+  </ol>
+
+  <dl>
+  <dt>Jean-loup Gailly        
+  <dd><a href="mailto:jloup@gzip.org">jloup@gzip.org</a>
+  <dt>Mark Adler
+  <dd><a href="mailto:madler@alumni.caltech.edu">madler@alumni.caltech.edu</a>
+  </dl>
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files
+  <a href="ftp://ds.internic.net/rfc/rfc1950.txt">
+  ftp://ds.internic.net/rfc/rfc1950.txt </a>
+  (zlib format), 
+  <a href="ftp://ds.internic.net/rfc/rfc1951.txt">
+  rfc1951.txt </a>
+  (<a href="#deflate">deflate</a> format) and 
+  <a href="ftp://ds.internic.net/rfc/rfc1952.txt">
+  rfc1952.txt </a>
+  (gzip format).
+  <p>
+  This manual is converted from zlib.h by 
+  <a href="mailto:piaip@csie.ntu.edu.tw"> piaip </a>
+  <p>
+  Visit <a href="http://ftp.cdrom.com/pub/infozip/zlib/">
+  http://ftp.cdrom.com/pub/infozip/zlib/</a> 
+  for the official zlib web page.
+  <p>
+
+<hr>
+<a name="Introduction"><h2> Introduction </h2>
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms will be added later and will have the same
+  stream interface.
+  <p>
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+  <p>
+
+     The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio.
+  <p>
+
+     The library does not install any signal handler. The decoder checks
+  the consistency of the compressed data, so the library should never
+  crash even in case of corrupted input.
+  <p>
+
+<hr>
+<a name="Utility functions"><h2> Utility functions </h2>
+     The following utility functions are implemented on top of the
+   <a href="#Basic functions">basic stream-oriented functions</a>. 
+   To simplify the interface, some
+   default options are assumed (compression level and memory usage,
+   standard memory allocation functions). The source code of these
+   utility functions can easily be modified if you need special options.
+<h3> Function list </h3>
+<ul>
+<li> int  <a href="#compress">compress</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen);
+<li> int  <a href="#compress2">compress2</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen, int level);
+<li> int  <a href="#uncompress">uncompress</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen);
+<li> typedef voidp gzFile;
+<li>  gzFile  <a href="#gzopen">gzopen</a>  (const char *path, const char *mode);
+<li> gzFile  <a href="#gzdopen">gzdopen</a>  (int fd, const char *mode);
+<li> int  <a href="#gzsetparams">gzsetparams</a> (gzFile file, int level, int strategy);
+<li> int     <a href="#gzread">gzread</a>  (gzFile file, voidp buf, unsigned len);
+<li> int     <a href="#gzwrite">gzwrite</a> (gzFile file, const voidp buf, unsigned len);
+<li> int VA   <a href="#gzprintf">gzprintf</a> (gzFile file, const char *format, ...);
+<li> int  <a href="#gzputs">gzputs</a> (gzFile file, const char *s);
+<li> char *  <a href="#gzgets">gzgets</a> (gzFile file, char *buf, int len);
+<li> int     <a href="#gzputc">gzputc</a> (gzFile file, int c);
+<li> int     <a href="#gzgetc">gzgetc</a> (gzFile file);
+<li> int     <a href="#gzflush">gzflush</a> (gzFile file, int flush);
+<li> z_off_t     <a href="#gzseek">gzseek</a> (gzFile file, z_off_t offset, int whence);
+<li> z_off_t     <a href="#gztell">gztell</a> (gzFile file);
+<li> int     <a href="#gzrewind">gzrewind</a> (gzFile file);
+<li> int  <a href="#gzeof">gzeof</a> (gzFile file);
+<li> int     <a href="#gzclose">gzclose</a> (gzFile file);
+<li> const char *  <a href="#gzerror">gzerror</a> (gzFile file, int *errnum);
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> int  <a name="compress">compress</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen);</font>
+<dd>
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be at least 0.1% larger than
+   sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+   compressed buffer.<p>
+     This function can be used to <a href="#compress">compress</a> a whole file at once if the
+   input file is mmap'ed.<p>
+     <a href="#compress">compress</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+   enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output
+   buffer.<p>
+
+<font color="Blue"><dt> int  <a name="compress2">compress2</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen, int level);</font>
+<dd>
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in <a href="#deflateInit">deflateInit</a>.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+   <p>
+
+     <a href="#compress2">compress2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+   memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output buffer,
+   <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the level parameter is invalid.
+   <p>
+
+<font color="Blue"><dt> int  <a name="uncompress">uncompress</a> (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen);</font>
+<dd>
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer. <p>
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+   <p>
+
+     <a href="#uncompress">uncompress</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+   enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output
+   buffer, or <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the input data was corrupted.
+   <p>
+
+<dt> typedef voidp gzFile;
+<dd> <p>
+
+<font color="Blue"><dt>  gzFile  <a name="gzopen">gzopen</a>  (const char *path, const char *mode);</font>
+<dd>
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb") but can also include a compression level
+   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+   Huffman only compression as in "wb1h". (See the description
+   of <a href="#deflateInit2">deflateInit2</a> for more information about the strategy parameter.)
+   <p>
+
+     <a href="#gzopen">gzopen</a> can be used to read a file which is not in gzip format ; in this
+   case <a href="#gzread">gzread</a> will directly read from the file without decompression.
+   <p>
+
+     <a href="#gzopen">gzopen</a> returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression <a href="#state">state</a> ; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a>).
+   <p>
+
+<font color="Blue"><dt> gzFile  <a name="gzdopen">gzdopen</a>  (int fd, const char *mode);</font>
+<dd>
+     <a href="#gzdopen">gzdopen</a>() associates a gzFile with the file descriptor fd.  File
+   descriptors are obtained from calls like open, dup, creat, pipe or
+   fileno (in the file has been previously opened with fopen).
+   The mode parameter is as in <a href="#gzopen">gzopen</a>.
+   <p>
+     The next call of <a href="#gzclose">gzclose</a> on the returned gzFile will also close the
+   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+   descriptor fd. If you want to keep fd open, use <a href="#gzdopen">gzdopen</a>(dup(fd), mode).
+   <p>
+     <a href="#gzdopen">gzdopen</a> returns NULL if there was insufficient memory to allocate
+   the (de)compression <a href="#state">state</a>.
+   <p>
+
+<font color="Blue"><dt> int  <a name="gzsetparams">gzsetparams</a> (gzFile file, int level, int strategy);</font>
+<dd>
+     Dynamically update the compression level or strategy. See the description
+   of <a href="#deflateInit2">deflateInit2</a> for the meaning of these parameters.
+   <p>
+     <a href="#gzsetparams">gzsetparams</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the file was not
+   opened for writing.
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzread">gzread</a>  (gzFile file, voidp buf, unsigned len);</font>
+<dd>
+     Reads the given number of uncompressed bytes from the compressed file.
+   If the input file was not in gzip format, <a href="#gzread">gzread</a> copies the given number
+   of bytes into the buffer.
+   <p>
+     <a href="#gzread">gzread</a> returns the number of uncompressed bytes actually read (0 for
+   end of file, -1 for error).
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzwrite">gzwrite</a> (gzFile file, const voidp buf, unsigned len);</font>
+<dd>
+     Writes the given number of uncompressed bytes into the compressed file.
+   <a href="#gzwrite">gzwrite</a> returns the number of uncompressed bytes actually written
+   (0 in case of error).
+   <p>
+
+<font color="Blue"><dt> int VA   <a name="gzprintf">gzprintf</a> (gzFile file, const char *format, ...);</font>
+<dd>
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. <a href="#gzprintf">gzprintf</a> returns the number of
+   uncompressed bytes actually written (0 in case of error).
+   <p>
+
+<font color="Blue"><dt> int  <a name="gzputs">gzputs</a> (gzFile file, const char *s);</font>
+<dd>
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+   <p>
+      <a href="#gzputs">gzputs</a> returns the number of characters written, or -1 in case of error.
+      <p>
+
+<font color="Blue"><dt> char *  <a name="gzgets">gzgets</a> (gzFile file, char *buf, int len);</font>
+<dd>
+      Reads bytes from the compressed file until len-1 characters are read, or
+   a newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  The string is then terminated with a null
+   character.
+   <p>
+      <a href="#gzgets">gzgets</a> returns buf, or <a href="#Z_NULL">Z_NULL</a> in case of error.
+      <p>
+
+<font color="Blue"><dt> int     <a name="gzputc">gzputc</a> (gzFile file, int c);</font>
+<dd>
+      Writes c, converted to an unsigned char, into the compressed file.
+   <a href="#gzputc">gzputc</a> returns the value that was written, or -1 in case of error.
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzgetc">gzgetc</a> (gzFile file);</font>
+<dd>
+      Reads one byte from the compressed file. <a href="#gzgetc">gzgetc</a> returns this byte
+   or -1 in case of end of file or error.
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzflush">gzflush</a> (gzFile file, int flush);</font>
+<dd>
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the <a href="#deflate">deflate</a>() function. The return value is the zlib
+   error number (see function <a href="#gzerror">gzerror</a> below). <a href="#gzflush">gzflush</a> returns <a href="#Z_OK">Z_OK</a> if
+   the flush parameter is <a href="#Z_FINISH">Z_FINISH</a> and all output could be flushed.
+   <p>
+     <a href="#gzflush">gzflush</a> should be called only when strictly necessary because it can
+   degrade compression.
+   <p>
+
+<font color="Blue"><dt> z_off_t     <a name="gzseek">gzseek</a> (gzFile file, z_off_t offset, int whence);</font>
+<dd>
+      Sets the starting position for the next <a href="#gzread">gzread</a> or <a href="#gzwrite">gzwrite</a> on the
+   given compressed file. The offset represents a number of bytes in the
+   uncompressed data stream. The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+   <p>
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow. If the file is opened for writing, only forward seeks are
+   supported ; <a href="#gzseek">gzseek</a> then compresses a sequence of zeroes up to the new
+   starting position.
+   <p>
+      <a href="#gzseek">gzseek</a> returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzrewind">gzrewind</a> (gzFile file);</font>
+<dd>
+     Rewinds the given file. This function is supported only for reading.
+     <p>
+   <a href="#gzrewind">gzrewind</a>(file) is equivalent to (int)<a href="#gzseek">gzseek</a>(file, 0L, SEEK_SET)
+   <p>
+
+<font color="Blue"><dt> z_off_t     <a name="gztell">gztell</a> (gzFile file);</font>
+<dd>
+     Returns the starting position for the next <a href="#gzread">gzread</a> or <a href="#gzwrite">gzwrite</a> on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+   <p>
+
+   <a href="#gztell">gztell</a>(file) is equivalent to <a href="#gzseek">gzseek</a>(file, 0L, SEEK_CUR)
+   <p>
+
+<font color="Blue"><dt> int  <a name="gzeof">gzeof</a> (gzFile file);</font>
+<dd>
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+   <p>
+
+<font color="Blue"><dt> int     <a name="gzclose">gzclose</a> (gzFile file);</font>
+<dd>
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression <a href="#state">state</a>. The return value is the zlib
+   error number (see function <a href="#gzerror">gzerror</a> below).
+   <p>
+
+<font color="Blue"><dt> const char *  <a name="gzerror">gzerror</a> (gzFile file, int *errnum);</font>
+<dd>
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to <a href="#Z_ERRNO">Z_ERRNO</a> and the application may consult errno
+   to get the exact error code.
+   <p>
+</dl>
+<hr>
+<a name="Basic functions"><h2> Basic functions </h2>
+<h3> Function list </h3>
+<ul>
+<li>  const char *  <a href="#zlibVersion">zlibVersion</a> (void);
+<li>  int  <a href="#deflateInit">deflateInit</a> (<a href="#z_streamp">z_streamp</a> strm, int level);
+<li>  int  <a href="#deflate">deflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);
+<li>  int  <a href="#deflateEnd">deflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li>  int  <a href="#inflateInit">inflateInit</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li>  int  <a href="#inflate">inflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);
+<li>  int  <a href="#inflateEnd">inflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);
+</ul>
+
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt>  const char *  <a name="zlibVersion">zlibVersion</a> (void);</font>
+<dd> The application can compare <a href="#zlibVersion">zlibVersion</a> and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+   This check is automatically made by <a href="#deflateInit">deflateInit</a> and <a href="#inflateInit">inflateInit</a>.
+   <p>
+
+<font color="Blue"><dt> int  <a name="deflateInit">deflateInit</a> (<a href="#z_streamp">z_streamp</a> strm, int level);</font>
+<dd>
+     Initializes the internal stream <a href="#state">state</a> for compression. The fields
+   <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by the caller.
+   If <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> are set to <a href="#Z_NULL">Z_NULL</a>, <a href="#deflateInit">deflateInit</a> updates them to
+   use default allocation functions.
+   <p>
+
+     The compression level must be <a href="#Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a>, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at
+   all (the input data is simply copied a block at a time).
+   <p>
+
+   <a href="#Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a> requests a default compromise between speed and
+   compression (currently equivalent to level 6).
+   <p>
+
+     <a href="#deflateInit">deflateInit</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+   enough memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if level is not a valid compression level,
+   <a href="#Z_VERSION_ERROR">Z_VERSION_ERROR</a> if the zlib library version (<a href="#zlib_version">zlib_version</a>) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).
+   <a href="#msg">msg</a> is set to null if there is no error message.  <a href="#deflateInit">deflateInit</a> does not
+   perform any compression: this will be done by <a href="#deflate">deflate</a>().
+   <p>
+
+<font color="Blue"><dt>  int  <a name="deflate">deflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);</font>
+<dd>
+    <a href="#deflate">deflate</a> compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce some
+  output latency (reading input without producing any output) except when
+  forced to flush.<p>
+
+    The detailed semantics are as follows. <a href="#deflate">deflate</a> performs one or both of the
+  following actions:
+
+  <ul>
+  <li> Compress more input starting at <a href="#next_in">next_in</a> and update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a>
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> are updated and
+    processing will resume at this point for the next call of <a href="#deflate">deflate</a>().
+
+  <li> 
+    Provide more output starting at <a href="#next_out">next_out</a> and update <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a>
+    accordingly. This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).
+    Some output may be provided even if flush is not set.
+  </ul> <p>
+
+  Before the call of <a href="#deflate">deflate</a>(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating <a href="#avail_in">avail_in</a> or <a href="#avail_out">avail_out</a> accordingly ; <a href="#avail_out">avail_out</a>
+  should never be zero before the call. The application can consume the
+  compressed output when it wants, for example when the output buffer is full
+  (<a href="#avail_out">avail_out</a> == 0), or after each call of <a href="#deflate">deflate</a>(). If <a href="#deflate">deflate</a> returns <a href="#Z_OK">Z_OK</a>
+  and with zero <a href="#avail_out">avail_out</a>, it must be called again after making room in the
+  output buffer because there might be more output pending.
+  <p>
+
+    If the parameter flush is set to <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far. (In particular
+  <a href="#avail_in">avail_in</a> is zero after the call if enough output space has been provided
+  before the call.)  Flushing may degrade compression for some compression
+  algorithms and so it should be used only when necessary.
+  <p>
+
+    If flush is set to <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a>, all output is flushed as with
+  <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, and the compression <a href="#state">state</a> is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired. Using <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a> too often can seriously degrade
+  the compression.
+  <p>
+
+    If <a href="#deflate">deflate</a> returns with <a href="#avail_out">avail_out</a> == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  <a href="#avail_out">avail_out</a>), until the flush is complete (<a href="#deflate">deflate</a> returns with non-zero
+  <a href="#avail_out">avail_out</a>).
+  <p>
+
+    If the parameter flush is set to <a href="#Z_FINISH">Z_FINISH</a>, pending input is processed,
+  pending output is flushed and <a href="#deflate">deflate</a> returns with <a href="#Z_STREAM_END">Z_STREAM_END</a> if there
+  was enough output space ; if <a href="#deflate">deflate</a> returns with <a href="#Z_OK">Z_OK</a>, this function must be
+  called again with <a href="#Z_FINISH">Z_FINISH</a> and more output space (updated <a href="#avail_out">avail_out</a>) but no
+  more input data, until it returns with <a href="#Z_STREAM_END">Z_STREAM_END</a> or an error. After
+  <a href="#deflate">deflate</a> has returned <a href="#Z_STREAM_END">Z_STREAM_END</a>, the only possible operations on the
+  stream are <a href="#deflateReset">deflateReset</a> or <a href="#deflateEnd">deflateEnd</a>.
+  <p>
+  
+    <a href="#Z_FINISH">Z_FINISH</a> can be used immediately after <a href="#deflateInit">deflateInit</a> if all the compression
+  is to be done in a single step. In this case, <a href="#avail_out">avail_out</a> must be at least
+  0.1% larger than <a href="#avail_in">avail_in</a> plus 12 bytes.  If <a href="#deflate">deflate</a> does not return
+  <a href="#Z_STREAM_END">Z_STREAM_END</a>, then it must be called again as described above.
+  <p>
+
+    <a href="#deflate">deflate</a>() sets strm-&gt <a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of all input read
+  so far (that is, <a href="#total_in">total_in</a> bytes).
+  <p>
+
+    <a href="#deflate">deflate</a>() may update <a href="#data_type">data_type</a> if it can make a good guess about
+  the input data type (<a href="#Z_ASCII">Z_ASCII</a> or <a href="#Z_BINARY">Z_BINARY</a>). In doubt, the data is considered
+  binary. This field is only for information purposes and does not affect
+  the compression algorithm in any manner.
+  <p>
+
+    <a href="#deflate">deflate</a>() returns <a href="#Z_OK">Z_OK</a> if some progress has been made (more input
+  processed or more output produced), <a href="#Z_STREAM_END">Z_STREAM_END</a> if all input has been
+  consumed and all output has been produced (only when flush is set to
+  <a href="#Z_FINISH">Z_FINISH</a>), <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream <a href="#state">state</a> was inconsistent (for example
+  if <a href="#next_in">next_in</a> or <a href="#next_out">next_out</a> was NULL), <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if no progress is possible
+  (for example <a href="#avail_in">avail_in</a> or <a href="#avail_out">avail_out</a> was zero).
+  <p>
+
+<font color="Blue"><dt>  int  <a name="deflateEnd">deflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+   <p>
+
+     <a href="#deflateEnd">deflateEnd</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the
+   stream <a href="#state">state</a> was inconsistent, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the stream was freed
+   prematurely (some input or output was discarded). In the error case,
+   <a href="#msg">msg</a> may be set but then points to a static string (which must not be
+   deallocated).
+   <p>
+
+<font color="Blue"><dt>  int  <a name="inflateInit">inflateInit</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>     
+	Initializes the internal stream <a href="#state">state</a> for decompression. The fields
+   <a href="#next_in">next_in</a>, <a href="#avail_in">avail_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by
+   the caller. If <a href="#next_in">next_in</a> is not <a href="#Z_NULL">Z_NULL</a> and <a href="#avail_in">avail_in</a> is large enough (the exact
+   value depends on the compression method), <a href="#inflateInit">inflateInit</a> determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly ; otherwise the allocation will be deferred to the first call of
+   <a href="#inflate">inflate</a>.  If <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> are set to <a href="#Z_NULL">Z_NULL</a>, <a href="#inflateInit">inflateInit</a> updates them to
+   use default allocation functions.
+   <p>
+
+     <a href="#inflateInit">inflateInit</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+   memory, <a href="#Z_VERSION_ERROR">Z_VERSION_ERROR</a> if the zlib library version is incompatible with the
+   version assumed by the caller.  <a href="#msg">msg</a> is set to null if there is no error
+   message. <a href="#inflateInit">inflateInit</a> does not perform any decompression apart from reading
+   the zlib header if present: this will be done by <a href="#inflate">inflate</a>().  (So <a href="#next_in">next_in</a> and
+   <a href="#avail_in">avail_in</a> may be modified, but <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> are unchanged.)
+   <p>
+
+<font color="Blue"><dt>  int  <a name="inflate">inflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);</font>
+<dd>
+    <a href="#inflate">inflate</a> decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may some
+  introduce some output latency (reading input without producing any output)
+  except when forced to flush.
+  <p>
+
+  The detailed semantics are as follows. <a href="#inflate">inflate</a> performs one or both of the
+  following actions:
+
+  <ul>
+  <li> Decompress more input starting at <a href="#next_in">next_in</a> and update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a>
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), <a href="#next_in">next_in</a> is updated and processing
+    will resume at this point for the next call of <a href="#inflate">inflate</a>().
+
+  <li> Provide more output starting at <a href="#next_out">next_out</a> and update <a href="#next_out">next_out</a> and 
+    <a href="#avail_out">avail_out</a> accordingly.  <a href="#inflate">inflate</a>() provides as much output as possible, 
+    until there is no more input data or no more space in the output buffer 
+    (see below about the flush parameter).
+  </ul> <p>
+
+  Before the call of <a href="#inflate">inflate</a>(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (<a href="#avail_out">avail_out</a> == 0), or after each
+  call of <a href="#inflate">inflate</a>(). If <a href="#inflate">inflate</a> returns <a href="#Z_OK">Z_OK</a> and with zero <a href="#avail_out">avail_out</a>, it
+  must be called again after making room in the output buffer because there
+  might be more output pending.
+  <p>
+
+    If the parameter flush is set to <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, <a href="#inflate">inflate</a> flushes as much
+  output as possible to the output buffer. The flushing behavior of <a href="#inflate">inflate</a> is
+  not specified for values of the flush parameter other than <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>
+  and <a href="#Z_FINISH">Z_FINISH</a>, but the current implementation actually flushes as much output
+  as possible anyway.
+  <p>
+
+    <a href="#inflate">inflate</a>() should normally be called until it returns <a href="#Z_STREAM_END">Z_STREAM_END</a> or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of <a href="#inflate">inflate</a>), the parameter flush should be set to
+  <a href="#Z_FINISH">Z_FINISH</a>. In this case all pending input is processed and all pending
+  output is flushed ; <a href="#avail_out">avail_out</a> must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be <a href="#inflateEnd">inflateEnd</a> to deallocate the decompression <a href="#state">state</a>. The use of <a href="#Z_FINISH">Z_FINISH</a>
+  is never required, but can be used to inform <a href="#inflate">inflate</a> that a faster routine
+  may be used for the single <a href="#inflate">inflate</a>() call.
+  <p>
+
+     If a preset dictionary is needed at this point (see <a href="#inflateSetDictionary">inflateSetDictionary</a>
+  below), <a href="#inflate">inflate</a> sets strm-<a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of the
+  dictionary chosen by the compressor and returns <a href="#Z_NEED_DICT">Z_NEED_DICT</a> ; otherwise 
+  it sets strm-&gt <a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of all output produced
+  so far (that is, <a href="#total_out">total_out</a> bytes) and returns <a href="#Z_OK">Z_OK</a>, <a href="#Z_STREAM_END">Z_STREAM_END</a> or
+  an error code as described below. At the end of the stream, <a href="#inflate">inflate</a>()
+  checks that its computed <a href="#adler32">adler32</a> checksum is equal to that saved by the
+  compressor and returns <a href="#Z_STREAM_END">Z_STREAM_END</a> only if the checksum is correct.
+  <p>
+
+    <a href="#inflate">inflate</a>() returns <a href="#Z_OK">Z_OK</a> if some progress has been made (more input processed
+  or more output produced), <a href="#Z_STREAM_END">Z_STREAM_END</a> if the end of the compressed data has
+  been reached and all uncompressed output has been produced, <a href="#Z_NEED_DICT">Z_NEED_DICT</a> if a
+  preset dictionary is needed at this point, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect
+  <a href="#adler32">adler32</a> checksum), <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream structure was inconsistent
+  (for example if <a href="#next_in">next_in</a> or <a href="#next_out">next_out</a> was NULL), <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+  enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if no progress is possible or if there was not
+  enough room in the output buffer when <a href="#Z_FINISH">Z_FINISH</a> is used. In the <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a>
+  case, the application may then call <a href="#inflateSync">inflateSync</a> to look for a good
+  compression block.
+  <p>
+
+<font color="Blue"><dt>  int  <a name="inflateEnd">inflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+   <p>
+
+     <a href="#inflateEnd">inflateEnd</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream <a href="#state">state</a>
+   was inconsistent. In the error case, <a href="#msg">msg</a> may be set but then points to a
+   static string (which must not be deallocated).
+</dl>
+<hr>
+<a name="Advanced functions"><h2> Advanced functions </h2>
+    The following functions are needed only in some special applications.
+<h3> Function list </h3>
+<ul>
+<li>  int  <a href="#deflateInit2">deflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm,
+<li> int  <a href="#deflateSetDictionary">deflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt  dictLength);
+<li> int  <a href="#deflateCopy">deflateCopy</a> (<a href="#z_streamp">z_streamp</a> dest, <a href="#z_streamp">z_streamp</a> source);
+<li> int  <a href="#deflateReset">deflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int  <a href="#deflateParams">deflateParams</a> (<a href="#z_streamp">z_streamp</a> strm, int level, int strategy);
+<li> int  <a href="#inflateInit2">inflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int  windowBits);
+<li>  int  <a href="#inflateSetDictionary">inflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt  dictLength);
+<li> int  <a href="#inflateSync">inflateSync</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int  <a href="#inflateReset">inflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);
+
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt>  int  <a name="deflateInit2">deflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int  level, int  method, int  windowBits, int  memLevel, int  strategy);</font>
+
+<dd> This is another version of <a href="#deflateInit">deflateInit</a> with more compression options. The
+   fields <a href="#next_in">next_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by
+   the caller.<p>
+
+     The method parameter is the compression method. It must be <a href="#Z_DEFLATED">Z_DEFLATED</a> in
+   this version of the library.<p>
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library. Larger values of this parameter result in better
+   compression at the expense of memory usage. The default value is 15 if
+   <a href="#deflateInit">deflateInit</a> is used instead.<p>
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression <a href="#state">state</a>. memLevel=1 uses minimum memory but
+   is slow and reduces compression ratio ; memLevel=9 uses maximum memory
+   for optimal speed. The default value is 8. See zconf.h for total memory
+   usage as a function of windowBits and memLevel.<p>
+
+     The strategy parameter is used to tune the compression algorithm. Use the
+   value <a href="#Z_DEFAULT_STRATEGY">Z_DEFAULT_STRATEGY</a> for normal data, <a href="#Z_FILTERED">Z_FILTERED</a> for data produced by a
+   filter (or predictor), or <a href="#Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a> to force Huffman encoding only (no
+   string match).  Filtered data consists mostly of small values with a
+   somewhat random distribution. In this case, the compression algorithm is
+   tuned to <a href="#compress">compress</a> them better. The effect of <a href="#Z_FILTERED">Z_FILTERED</a> is to force more
+   Huffman coding and less string matching ; it is somewhat intermediate
+   between Z_DEFAULT and <a href="#Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a>. The strategy parameter only affects
+   the compression ratio but not the correctness of the compressed output even
+   if it is not set appropriately.<p>
+
+      <a href="#deflateInit2">deflateInit2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+   memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a parameter is invalid (such as an invalid
+   method). <a href="#msg">msg</a> is set to null if there is no error message.  <a href="#deflateInit2">deflateInit2</a> does
+   not perform any compression: this will be done by <a href="#deflate">deflate</a>().<p>
+                            
+<font color="Blue"><dt> int  <a name="deflateSetDictionary">deflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt  dictLength);</font>
+<dd>
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output. This function must be called
+   immediately after <a href="#deflateInit">deflateInit</a>, <a href="#deflateInit2">deflateInit2</a> or <a href="#deflateReset">deflateReset</a>, before any
+   call of <a href="#deflate">deflate</a>. The compressor and decompressor must use exactly the same
+   dictionary (see <a href="#inflateSetDictionary">inflateSetDictionary</a>).<p>
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary. Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy ; the data can then be compressed better than
+   with the default empty dictionary.<p>
+
+     Depending on the size of the compression data structures selected by
+   <a href="#deflateInit">deflateInit</a> or <a href="#deflateInit2">deflateInit2</a>, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size in
+   <a href="#deflate">deflate</a> or deflate2. Thus the strings most likely to be useful should be
+   put at the end of the dictionary, not at the front.<p>
+
+     Upon return of this function, strm-&gt <a href="#adler">adler</a> is set to the Adler32 value
+   of the dictionary ; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor. (The Adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.)<p>
+
+     <a href="#deflateSetDictionary">deflateSetDictionary</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a
+   parameter is invalid (such as NULL dictionary) or the stream <a href="#state">state</a> is
+   inconsistent (for example if <a href="#deflate">deflate</a> has already been called for this stream
+   or if the compression method is bsort). <a href="#deflateSetDictionary">deflateSetDictionary</a> does not
+   perform any compression: this will be done by <a href="#deflate">deflate</a>().<p>
+
+<font color="Blue"><dt> int  <a name="deflateCopy">deflateCopy</a> (<a href="#z_streamp">z_streamp</a> dest, <a href="#z_streamp">z_streamp</a> source);</font>
+<dd>
+     Sets the destination stream as a complete copy of the source stream.<p>
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter. The streams that will be discarded should then be freed
+   by calling <a href="#deflateEnd">deflateEnd</a>.  Note that <a href="#deflateCopy">deflateCopy</a> duplicates the internal
+   compression <a href="#state">state</a> which can be quite large, so this strategy is slow and
+   can consume lots of memory.<p>
+
+     <a href="#deflateCopy">deflateCopy</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+   enough memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source stream <a href="#state">state</a> was inconsistent
+   (such as <a href="#zalloc">zalloc</a> being NULL). <a href="#msg">msg</a> is left unchanged in both source and
+   destination.<p>
+
+<font color="Blue"><dt> int  <a name="deflateReset">deflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>     This function is equivalent to <a href="#deflateEnd">deflateEnd</a> followed by <a href="#deflateInit">deflateInit</a>,
+   but does not free and reallocate all the internal compression <a href="#state">state</a>.
+   The stream will keep the same compression level and any other attributes
+   that may have been set by <a href="#deflateInit2">deflateInit2</a>.<p>
+
+      <a href="#deflateReset">deflateReset</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+   stream <a href="#state">state</a> was inconsistent (such as <a href="#zalloc">zalloc</a> or <a href="#state">state</a> being NULL).<p>
+
+<font color="Blue"><dt> int  <a name="deflateParams">deflateParams</a> (<a href="#z_streamp">z_streamp</a> strm, int level, int strategy);</font>
+<dd>
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in <a href="#deflateInit2">deflateInit2</a>.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different
+   strategy. If the compression level is changed, the input available so far
+   is compressed with the old level (and may be flushed); the new level will
+   take effect only at the next call of <a href="#deflate">deflate</a>().<p>
+
+     Before the call of <a href="#deflateParams">deflateParams</a>, the stream <a href="#state">state</a> must be set as for
+   a call of <a href="#deflate">deflate</a>(), since the currently available input may have to
+   be compressed and flushed. In particular, strm-&gt <a href="#avail_out">avail_out</a> must be 
+   non-zero.<p>
+
+     <a href="#deflateParams">deflateParams</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+   stream <a href="#state">state</a> was inconsistent or if a parameter was invalid, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a>
+   if strm-&gtavail_out was zero.<p>
+
+<font color="Blue"><dt> int  <a name="inflateInit2">inflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int  windowBits);</font>
+
+<dd>     This is another version of <a href="#inflateInit">inflateInit</a> with an extra parameter. The
+   fields <a href="#next_in">next_in</a>, <a href="#avail_in">avail_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized
+   before by the caller.<p>
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library. The default value is 15 if <a href="#inflateInit">inflateInit</a> is used
+   instead. If a compressed stream with a larger window size is given as
+   input, <a href="#inflate">inflate</a>() will return with the error code <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> instead of
+   trying to allocate a larger window.<p>
+
+      <a href="#inflateInit2">inflateInit2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+   memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a parameter is invalid (such as a negative
+   memLevel). <a href="#msg">msg</a> is set to null if there is no error message.  <a href="#inflateInit2">inflateInit2</a>
+   does not perform any decompression apart from reading the zlib header if
+   present: this will be done by <a href="#inflate">inflate</a>(). (So <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> may be
+   modified, but <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> are unchanged.)<p>
+
+<font color="Blue"><dt>  int  <a name="inflateSetDictionary">inflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt  dictLength);</font>
+<dd>
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence. This function must be called immediately after a call of <a href="#inflate">inflate</a>
+   if this call returned <a href="#Z_NEED_DICT">Z_NEED_DICT</a>. The dictionary chosen by the compressor
+   can be determined from the Adler32 value returned by this call of
+   <a href="#inflate">inflate</a>. The compressor and decompressor must use exactly the same
+   dictionary (see <a href="#deflateSetDictionary">deflateSetDictionary</a>).<p>
+
+     <a href="#inflateSetDictionary">inflateSetDictionary</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a
+   parameter is invalid (such as NULL dictionary) or the stream <a href="#state">state</a> is
+   inconsistent, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the given dictionary doesn't match the
+   expected one (incorrect Adler32 value). <a href="#inflateSetDictionary">inflateSetDictionary</a> does not
+   perform any decompression: this will be done by subsequent calls of
+   <a href="#inflate">inflate</a>().<p>
+
+<font color="Blue"><dt> int  <a name="inflateSync">inflateSync</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+
+<dd>    Skips invalid compressed data until a full flush point (see above the
+  description of <a href="#deflate">deflate</a> with <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a>) can be found, or until all
+  available input is skipped. No output is provided.<p>
+
+    <a href="#inflateSync">inflateSync</a> returns <a href="#Z_OK">Z_OK</a> if a full flush point has been found, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a>
+  if no more input was provided, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if no flush point has been found,
+  or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of <a href="#total_in">total_in</a> which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call <a href="#inflateSync">inflateSync</a>, providing more input each time,
+  until success or end of the input data.<p>
+
+<font color="Blue"><dt> int  <a name="inflateReset">inflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+     This function is equivalent to <a href="#inflateEnd">inflateEnd</a> followed by <a href="#inflateInit">inflateInit</a>,
+   but does not free and reallocate all the internal decompression <a href="#state">state</a>.
+   The stream will keep attributes that may have been set by <a href="#inflateInit2">inflateInit2</a>.
+   <p>
+
+      <a href="#inflateReset">inflateReset</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+   stream <a href="#state">state</a> was inconsistent (such as <a href="#zalloc">zalloc</a> or <a href="#state">state</a> being NULL).
+   <p>
+</dl>
+
+<hr>
+<a name="Checksum functions"><h2> Checksum functions </h2>
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the
+   compression library.
+<h3> Function list </h3>
+<ul>
+<li> uLong  <a href="#adler32">adler32</a> (uLong <a href="#adler">adler</a>, const Bytef *buf, uInt len);
+<li> uLong  <a href="#crc32">crc32</a>   (uLong crc, const Bytef *buf, uInt len);
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> uLong  <a name="adler32">adler32</a> (uLong <a href="#adler">adler</a>, const Bytef *buf, uInt len);</font>
+<dd>
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   <p>
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+   <pre>
+
+     uLong <a href="#adler">adler</a> = <a href="#adler32">adler32</a>(0L, <a href="#Z_NULL">Z_NULL</a>, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       <a href="#adler">adler</a> = <a href="#adler32">adler32</a>(<a href="#adler">adler</a>, buffer, length);
+     }
+     if (<a href="#adler">adler</a> != original_adler) error();
+   </pre>
+
+<font color="Blue"><dt> uLong  <a name="crc32">crc32</a>   (uLong crc, const Bytef *buf, uInt len);</font>
+<dd>
+     Update a running crc with the bytes buf[0..len-1] and return the updated
+   crc. If buf is NULL, this function returns the required initial value
+   for the crc. Pre- and post-conditioning (one's complement) is performed
+   within this function so it shouldn't be done by the application.
+   Usage example:
+   <pre>
+
+     uLong crc = <a href="#crc32">crc32</a>(0L, <a href="#Z_NULL">Z_NULL</a>, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = <a href="#crc32">crc32</a>(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+   </pre>
+</dl>
+<hr>
+<a name="struct z_stream_s"><h2> struct z_stream_s </h2>
+<font color="Blue">
+<a name="z_stream_s">
+<pre>
+typedef struct z_stream_s {
+    Bytef    *<a name="next_in">next_in</a>;  /* next input byte */
+    uInt     <a name="avail_in">avail_in</a>;  /* number of bytes available at <a href="#next_in">next_in</a> */
+    uLong    <a name="total_in">total_in</a>;  /* total nb of input bytes read so far */
+
+    Bytef    *<a name="next_out">next_out</a>; /* next output byte should be put there */
+    uInt     <a name="avail_out">avail_out</a>; /* remaining free space at <a href="#next_out">next_out</a> */
+    uLong    <a name="total_out">total_out</a>; /* total nb of bytes output so far */
+
+    char     *<a name="msg">msg</a>;      /* last error message, NULL if no error */
+    struct internal_state FAR *<a name="state">state</a>; /* not visible by applications */
+
+    alloc_func <a name="zalloc">zalloc</a>;  /* used to allocate the internal <a href="#state">state</a> */
+    free_func  <a name="zfree">zfree</a>;   /* used to free the internal <a href="#state">state</a> */
+    voidpf     <a name="opaque">opaque</a>;  /* private data object passed to <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> */
+
+    int     <a name="data_type">data_type</a>;  /* best guess about the data type: ascii or binary */
+    uLong   <a name="adler">adler</a>;      /* <a href="#adler32">adler32</a> value of the uncompressed data */
+    uLong   <a name="reserved">reserved</a>;   /* <a href="#reserved">reserved</a> for future use */
+} <a href="#z_stream_s">z_stream</a> ;
+
+typedef <a href="#z_stream_s">z_stream</a> FAR * <a name="z_streamp">z_streamp</a>;  ÿ 
+</pre>
+</font>
+   The application must update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> when <a href="#avail_in">avail_in</a> has
+   dropped to zero. It must update <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> when <a href="#avail_out">avail_out</a>
+   has dropped to zero. The application must initialize <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and
+   <a href="#opaque">opaque</a> before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application. <p>
+
+   The <a href="#opaque">opaque</a> value provided by the application will be passed as the first
+   parameter for calls of <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a>. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   <a href="#opaque">opaque</a> value. <p>
+
+   <a href="#zalloc">zalloc</a> must return <a href="#Z_NULL">Z_NULL</a> if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> must be
+   thread safe. <p>
+
+   On 16-bit systems, the functions <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by <a href="#zalloc">zalloc</a> for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+   <p>
+
+   The fields <a href="#total_in">total_in</a> and <a href="#total_out">total_out</a> can be used for statistics or
+   progress reports. After compression, <a href="#total_in">total_in</a> holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step). <p>
+
+<hr>
+<a name="Constants"><h2> Constants </h2>
+<font color="Blue">
+<pre>
+#define <a name="Z_NO_FLUSH">Z_NO_FLUSH</a>      0
+#define <a name="Z_PARTIAL_FLUSH">Z_PARTIAL_FLUSH</a> 1 
+	/* will be removed, use <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a> instead */
+#define <a name="Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>    2
+#define <a name="Z_FULL_FLUSH">Z_FULL_FLUSH</a>    3
+#define <a name="Z_FINISH">Z_FINISH</a>        4
+/* Allowed flush values ; see <a href="#deflate">deflate</a>() below for details */
+
+#define <a name="Z_OK">Z_OK</a>            0
+#define <a name="Z_STREAM_END">Z_STREAM_END</a>    1
+#define <a name="Z_NEED_DICT">Z_NEED_DICT</a>     2
+#define <a name="Z_ERRNO">Z_ERRNO</a>        (-1)
+#define <a name="Z_STREAM_ERROR">Z_STREAM_ERROR</a> (-2)
+#define <a name="Z_DATA_ERROR">Z_DATA_ERROR</a>   (-3)
+#define <a name="Z_MEM_ERROR">Z_MEM_ERROR</a>    (-4)
+#define <a name="Z_BUF_ERROR">Z_BUF_ERROR</a>    (-5)
+#define <a name="Z_VERSION_ERROR">Z_VERSION_ERROR</a> (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define <a name="Z_NO_COMPRESSION">Z_NO_COMPRESSION</a>         0
+#define <a name="Z_BEST_SPEED">Z_BEST_SPEED</a>             1
+#define <a name="Z_BEST_COMPRESSION">Z_BEST_COMPRESSION</a>       9
+#define <a name="Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a>  (-1)
+/* compression levels */
+
+#define <a name="Z_FILTERED">Z_FILTERED</a>            1
+#define <a name="Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a>        2
+#define <a name="Z_DEFAULT_STRATEGY">Z_DEFAULT_STRATEGY</a>    0
+/* compression strategy ; see <a href="#deflateInit2">deflateInit2</a>() below for details */
+
+#define <a name="Z_BINARY">Z_BINARY</a>   0
+#define <a name="Z_ASCII">Z_ASCII</a>    1
+#define <a name="Z_UNKNOWN">Z_UNKNOWN</a>  2
+/* Possible values of the <a href="#data_type">data_type</a> field */
+
+#define <a name="Z_DEFLATED">Z_DEFLATED</a>   8
+/* The <a href="#deflate">deflate</a> compression method (the only one supported in this version) */
+
+#define <a name="Z_NULL">Z_NULL</a>  0  /* for initializing <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a>, <a href="#opaque">opaque</a> */
+
+#define <a name="zlib_version">zlib_version</a> <a href="#zlibVersion">zlibVersion</a>()
+/* for compatibility with versions less than 1.0.2 */
+</pre>
+</font>
+
+<hr>
+<a name="Misc"><h2> Misc </h2>
+ <a href="#deflateInit">deflateInit</a> and <a href="#inflateInit">inflateInit</a> are macros to allow checking the zlib version
+ and the compiler's view of <a href="#z_stream_s">z_stream</a>.
+ <p>
+ Other functions:
+ <dl>
+ <font color="Blue"><dt> const char   *  <a name="zError">zError</a>           (int err);</font>
+ <font color="Blue"><dt> int             <a name="inflateSyncPoint">inflateSyncPoint</a> (<a href="#z_streamp">z_streamp</a> z);</font>
+ <font color="Blue"><dt> const uLongf *  <a name="get_crc_table">get_crc_table</a>    (void);</font>
+ </dl>
+ <hr>
+ <font size="-1">
+ Last update: Wed Oct 13 20:42:34 1999<br>
+ piapi@csie.ntu.edu.tw
+ </font>
+
+</body>
+</html>
diff --git a/zlib/zutil.c b/zlib/zutil.c
new file mode 100644
index 0000000..65beb59
--- /dev/null
+++ b/zlib/zutil.c
@@ -0,0 +1,225 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: zutil.c,v 1.1 2004/10/08 09:44:28 const_k Exp $ */
+
+#include "zutil.h"
+
+struct internal_state      {int dummy;}; /* for buggy compilers */
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char *z_errmsg[10] = {
+"need dictionary",     /* Z_NEED_DICT       2  */
+"stream end",          /* Z_STREAM_END      1  */
+"",                    /* Z_OK              0  */
+"file error",          /* Z_ERRNO         (-1) */
+"stream error",        /* Z_STREAM_ERROR  (-2) */
+"data error",          /* Z_DATA_ERROR    (-3) */
+"insufficient memory", /* Z_MEM_ERROR     (-4) */
+"buffer error",        /* Z_BUF_ERROR     (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+    return ZLIB_VERSION;
+}
+
+#ifdef DEBUG
+
+#  ifndef verbose
+#    define verbose 0
+#  endif
+int z_verbose = verbose;
+
+void z_error (m)
+    char *m;
+{
+    fprintf(stderr, "%s\n", m);
+    exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+    int err;
+{
+    return ERR_MSG(err);
+}
+
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+    Bytef* dest;
+    const Bytef* source;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = *source++; /* ??? to be unrolled */
+    } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+    const Bytef* s1;
+    const Bytef* s2;
+    uInt  len;
+{
+    uInt j;
+
+    for (j = 0; j < len; j++) {
+        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+    }
+    return 0;
+}
+
+void zmemzero(dest, len)
+    Bytef* dest;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = 0;  /* ??? to be unrolled */
+    } while (--len != 0);
+}
+#endif
+
+#ifdef __TURBOC__
+#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
+/* Small and medium model in Turbo C are for now limited to near allocation
+ * with reduced MAX_WBITS and MAX_MEM_LEVEL
+ */
+#  define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+    voidpf org_ptr;
+    voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    voidpf buf = opaque; /* just to make some compilers happy */
+    ulg bsize = (ulg)items*size;
+
+    /* If we allocate less than 65520 bytes, we assume that farmalloc
+     * will return a usable pointer which doesn't have to be normalized.
+     */
+    if (bsize < 65520L) {
+        buf = farmalloc(bsize);
+        if (*(ush*)&buf != 0) return buf;
+    } else {
+        buf = farmalloc(bsize + 16L);
+    }
+    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+    table[next_ptr].org_ptr = buf;
+
+    /* Normalize the pointer to seg:0 */
+    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+    *(ush*)&buf = 0;
+    table[next_ptr++].new_ptr = buf;
+    return buf;
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    int n;
+    if (*(ush*)&ptr != 0) { /* object < 64K */
+        farfree(ptr);
+        return;
+    }
+    /* Find the original pointer */
+    for (n = 0; n < next_ptr; n++) {
+        if (ptr != table[n].new_ptr) continue;
+
+        farfree(table[n].org_ptr);
+        while (++n < next_ptr) {
+            table[n-1] = table[n];
+        }
+        next_ptr--;
+        return;
+    }
+    ptr = opaque; /* just to make some compilers happy */
+    Assert(0, "zcfree: ptr not found");
+}
+#endif
+#endif /* __TURBOC__ */
+
+
+#if defined(M_I86) && !defined(__32BIT__)
+/* Microsoft C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+#  define _halloc  halloc
+#  define _hfree   hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    return _halloc((long)items, size);
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    _hfree(ptr);
+}
+
+#endif /* MSC */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp  calloc OF((uInt items, uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+    voidpf opaque;
+    unsigned items;
+    unsigned size;
+{
+    if (opaque) items += size - size; /* make compiler happy */
+    return (voidpf)calloc(items, size);
+}
+
+void  zcfree (opaque, ptr)
+    voidpf opaque;
+    voidpf ptr;
+{
+    free(ptr);
+    if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/zlib/zutil.h b/zlib/zutil.h
new file mode 100644
index 0000000..17eb77f
--- /dev/null
+++ b/zlib/zutil.h
@@ -0,0 +1,220 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: zutil.h,v 1.1 2004/10/08 09:44:28 const_k Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+#  include <stddef.h>
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+    extern int errno;
+#else
+#   include <errno.h>
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+  return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+        /* common constants */
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+        /* target dependencies */
+
+#ifdef MSDOS
+#  define OS_CODE  0x00
+#  if defined(__TURBOC__) || defined(__BORLANDC__)
+#    if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+       /* Allow compilation with ANSI keywords only enabled */
+       void _Cdecl farfree( void *block );
+       void *_Cdecl farmalloc( unsigned long nbytes );
+#    else
+#     include <alloc.h>
+#    endif
+#  else /* MSC or DJGPP */
+#    include <malloc.h>
+#  endif
+#endif
+
+#ifdef OS2
+#  define OS_CODE  0x06
+#endif
+
+#ifdef WIN32 /* Window 95 & Windows NT */
+#  define OS_CODE  0x0b
+#endif
+
+#if defined(VAXC) || defined(VMS)
+#  define OS_CODE  0x02
+#  define F_OPEN(name, mode) \
+     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef AMIGA
+#  define OS_CODE  0x01
+#endif
+
+#if defined(ATARI) || defined(atarist)
+#  define OS_CODE  0x05
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+#  define OS_CODE  0x07
+#  if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#    include <unix.h> /* for fdopen */
+#  else
+#    ifndef fdopen
+#      define fdopen(fd,mode) NULL /* No fdopen() */
+#    endif
+#  endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+#  define OS_CODE  0x0F
+#endif
+
+#ifdef TOPS20
+#  define OS_CODE  0x0a
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+#  define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+#  define fdopen(fd,type)  _fdopen(fd,type)
+#endif
+
+
+        /* Common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  0x03  /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+#  define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+         /* functions */
+
+#ifdef HAVE_STRERROR
+   extern char *strerror OF((int));
+#  define zstrerror(errnum) strerror(errnum)
+#else
+#  define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+#  define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+  * You may have to use the same strategy for Borland C (untested).
+  * The __SC__ check is for Symantec.
+  */
+#  define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+#  define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+#    define zmemcpy _fmemcpy
+#    define zmemcmp _fmemcmp
+#    define zmemzero(dest, len) _fmemset(dest, 0, len)
+#  else
+#    define zmemcpy memcpy
+#    define zmemcmp memcmp
+#    define zmemzero(dest, len) memset(dest, 0, len)
+#  endif
+#else
+   extern void zmemcpy  OF((Bytef* dest, const Bytef* source, uInt len));
+   extern int  zmemcmp  OF((const Bytef* s1, const Bytef* s2, uInt len));
+   extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  include <stdio.h>
+   extern int z_verbose;
+   extern void z_error    OF((char *m));
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
+#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
+#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
+				       uInt len));
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void   zcfree  OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */