diff options
435 files changed, 72719 insertions, 0 deletions
diff --git a/LICENCE.TXT b/LICENCE.TXT new file mode 100644 index 00000000..ae3b5319 --- /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 00000000..9341f4ad --- /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 @@ -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 00000000..ecbe9667 --- /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 00000000..0811395d --- /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 00000000..8f89fe93 --- /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 00000000..878a29b6 --- /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 00000000..bf2d123b --- /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 = ®ion.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(®ion, 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(®1->extents, ®2->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(®2->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(®M->extents, ®S->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 = ▭ + 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(®ion->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 00000000..87d04bff --- /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 00000000..7fa44d96 --- /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 00000000..2ddf12ca --- /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 00000000..979731c5 --- /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 00000000..40951d21 --- /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 00000000..bb49f83b --- /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 00000000..51d4cd63 --- /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 00000000..389c6751 --- /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 00000000..aecb6131 --- /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 Binary files differnew file mode 100644 index 00000000..f1699ba5 --- /dev/null +++ b/java/logo150x150.gif diff --git a/java/vncviewer.jar b/java/vncviewer.jar Binary files differnew file mode 100644 index 00000000..3c92b5bb --- /dev/null +++ b/java/vncviewer.jar diff --git a/logmessages/logmessages.dsp b/logmessages/logmessages.dsp new file mode 100644 index 00000000..b2c86155 --- /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 00000000..bfb8c56d --- /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 00000000..0bc8329e --- /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 00000000..0885a897 --- /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 00000000..8aed303a --- /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 00000000..a08afe53 --- /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 00000000..b536e673 --- /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 00000000..95333402 --- /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 00000000..e93f2bbc --- /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 00000000..4290700c --- /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 00000000..5f7799f1 --- /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 00000000..98b3f0e9 --- /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 00000000..397847a4 --- /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 00000000..d038b1b3 --- /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 00000000..6795fc89 --- /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 00000000..9c46db94 --- /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 00000000..f149e600 --- /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 00000000..a454ca8e --- /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 00000000..fbfc2738 --- /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 00000000..f82d9f55 --- /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 00000000..691a16bc --- /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 00000000..13a6fa19 --- /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 00000000..2048daa6 --- /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 00000000..9edf2842 --- /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 00000000..2b05e3df --- /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 00000000..3456f5c7 --- /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 00000000..e940f2af --- /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 00000000..84a56e5f --- /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 00000000..a064bcc9 --- /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 00000000..7f62e091 --- /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 00000000..c4aaaa6a --- /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 00000000..3a0559b7 --- /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 00000000..52e4dd35 --- /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 00000000..81eb161c --- /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 00000000..6aadde13 --- /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 00000000..e51db73b --- /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 00000000..e93f2bbc --- /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 00000000..260f8f01 --- /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 00000000..3798c975 --- /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 00000000..4c4f95b2 --- /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 00000000..4df7ec80 --- /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 00000000..c6a3eed8 --- /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 00000000..480fca30 --- /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 00000000..10109169 --- /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 00000000..7a69f145 --- /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 00000000..46973eb2 --- /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 00000000..8b4638c7 --- /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 00000000..1f974fd5 --- /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 00000000..93c8c6a5 --- /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 00000000..a7e4fb93 --- /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 00000000..8d6e373c --- /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 00000000..a6ad237c --- /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 00000000..0cf6157a --- /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 00000000..639d550e --- /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 00000000..23b36cee --- /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 00000000..3d6c87c5 --- /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 00000000..bfa40b3b --- /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 00000000..904256c4 --- /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 00000000..ee7783dc --- /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 00000000..0c44d85a --- /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 00000000..bec93d1f --- /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 00000000..048fb77d --- /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 00000000..1b37ac94 --- /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 00000000..95529405 --- /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 00000000..fb96a7a6 --- /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 00000000..b50d9255 --- /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 00000000..0f187750 --- /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 00000000..816cb6e4 --- /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 00000000..914b26a9 --- /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 00000000..f2d6d3d2 --- /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 00000000..7795d90b --- /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 00000000..aa98271b --- /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 00000000..55e6909e --- /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 00000000..9431195c --- /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 00000000..97c7ca74 --- /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 00000000..718bd38b --- /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 00000000..736bd5f7 --- /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 00000000..f09ead8e --- /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 00000000..bdff474e --- /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 00000000..b550a12f --- /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 00000000..6f267f12 --- /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, ¶ms.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 00000000..58e81f28 --- /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 00000000..84b8f559 --- /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 00000000..4233964b --- /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 00000000..ac249b39 --- /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 00000000..30c3f402 --- /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 00000000..ac9556e1 --- /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 00000000..68f795fc --- /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 00000000..50bc04c2 --- /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 00000000..2b1aaf0d --- /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 00000000..fcad227e --- /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 00000000..2ba105a7 --- /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 00000000..683b53a0 --- /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 00000000..0f2edd26 --- /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 00000000..c613dbb8 --- /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 00000000..75a5e859 --- /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 00000000..612a869a --- /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 00000000..40b203e3 --- /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 00000000..5a5d62b7 --- /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 00000000..b3dd9b7c --- /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 00000000..d758ec6d --- /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 00000000..c8b6a62d --- /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 00000000..ee43e669 --- /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 00000000..bbcc892c --- /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 = ®ion.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 00000000..8fb9889e --- /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 00000000..e969ed8c --- /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 00000000..19453c62 --- /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 00000000..eb17a529 --- /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 00000000..d6a139c7 --- /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 00000000..f326ad43 --- /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 00000000..2939aa10 --- /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 00000000..4d269380 --- /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 00000000..e5ae744a --- /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 00000000..28cc7a63 --- /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 00000000..ac743826 --- /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 00000000..6eba0682 --- /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 00000000..20a72801 --- /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 00000000..3881061f --- /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 00000000..2ca53449 --- /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 00000000..e3a40aa3 --- /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 00000000..5fced046 --- /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 00000000..09b2db4f --- /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 00000000..532d1a6a --- /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 00000000..edbd7206 --- /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 00000000..7a20c569 --- /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 00000000..74d74432 --- /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 00000000..effc4363 --- /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 00000000..693b18c5 --- /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 00000000..60ab0699 --- /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 00000000..c0d49071 --- /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 00000000..cc0fb101 --- /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 ®ion) { + 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 ®ion) { + 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 00000000..5015a250 --- /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 ®ion) = 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 ®ion); + 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 ®ion); + 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 00000000..c242ed0a --- /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 00000000..3ae378e0 --- /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 00000000..ba480e59 --- /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 00000000..e80044ce --- /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 00000000..b3f9e88e --- /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 00000000..6655a0d6 --- /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 ®ion); + 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 00000000..2dd4a129 --- /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 00000000..c8b1febf --- /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 00000000..1f288691 --- /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 00000000..17222a3c --- /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 00000000..9227ddd3 --- /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 00000000..ea3da44c --- /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 00000000..56a64ee7 --- /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 00000000..ae104b41 --- /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 00000000..272afbba --- /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 00000000..dc685e3a --- /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 00000000..a55842a0 --- /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 00000000..979ebdd5 --- /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 00000000..9bcf4d52 --- /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 00000000..4127ce9d --- /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 00000000..2521bfdc --- /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 00000000..9f69ceeb --- /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 00000000..4877a12d --- /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 00000000..7c6c25ca --- /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 00000000..f0b326e9 --- /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 00000000..2658f9f0 --- /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 00000000..907b8396 --- /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 00000000..2dbc2df4 --- /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 00000000..d792c8d1 --- /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 00000000..6bd6a620 --- /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 00000000..18d87ade --- /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 00000000..28cfdb7e --- /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 00000000..e95e0f73 --- /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 00000000..d0c98c3c --- /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 00000000..4bde5ec7 --- /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 00000000..b5391b11 --- /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 00000000..a1582f2a --- /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 00000000..efb15c00 --- /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 00000000..cb1713d4 --- /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 00000000..ad852a0e --- /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 00000000..10346ffc --- /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 00000000..1cab662a --- /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 00000000..f111de74 --- /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 00000000..9fb83478 --- /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 00000000..73f41534 --- /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 00000000..96d1e942 --- /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 00000000..57297e1e --- /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 00000000..1a244853 --- /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 00000000..469946b6 --- /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 00000000..21743768 --- /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 00000000..51e2da31 --- /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 00000000..a4d10211 --- /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 00000000..5e97b222 --- /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 ®ion); + + // - 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 00000000..157cf5fe --- /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 00000000..d46133a0 --- /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 00000000..645d0ee9 --- /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 00000000..0a48d60c --- /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 00000000..6fd34e9c --- /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 00000000..519d6ab9 --- /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 00000000..94baca38 --- /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 00000000..1976098f --- /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 00000000..1d529431 --- /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 00000000..fcb309b5 --- /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(¬ifyMsg, 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 00000000..3fced85e --- /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 00000000..de9238f7 --- /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 00000000..1998c497 --- /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 00000000..6fa3ff0e --- /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 00000000..c4c08bf3 --- /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 00000000..457a8619 --- /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 00000000..dcd779ef --- /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 00000000..d92e314f --- /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 00000000..b00c2900 --- /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 00000000..164381a1 --- /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 00000000..6ebd5c0a --- /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 00000000..791370f2 --- /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 00000000..f8f03a69 --- /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 00000000..399e00a7 --- /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 00000000..85680f3f --- /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 00000000..871d9376 --- /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 00000000..a96822a7 --- /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 00000000..26a2363c --- /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 00000000..791df763 --- /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 00000000..9773abf5 --- /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 00000000..564d176f --- /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 00000000..f568b211 --- /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 00000000..3f3f402a --- /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 00000000..f6a74848 --- /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 00000000..7b81678f --- /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 00000000..46d85eac --- /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 00000000..0750d86e --- /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 00000000..e25f43ad --- /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 00000000..5f0ab5a0 --- /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 00000000..69ce66f2 --- /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 00000000..e50d9c1a --- /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 00000000..2118bcb2 --- /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 00000000..b953325a --- /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 00000000..3f0c57e6 --- /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 00000000..146091d0 --- /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 00000000..01677a9e --- /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 00000000..b51946e3 --- /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 00000000..d5b06493 --- /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 00000000..a90a6945 --- /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 00000000..44f70477 --- /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 00000000..4069f2d2 --- /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 00000000..82dafa56 --- /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 00000000..00c4eb5f --- /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 00000000..946ccffc --- /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 00000000..4cc2afa3 --- /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 00000000..67982e9f --- /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 00000000..9dddc2f8 --- /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 00000000..a649de40 --- /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 00000000..20c31c95 --- /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 00000000..78cff1c2 --- /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 00000000..dabe4cdf --- /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 00000000..e6b377c1 --- /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 00000000..5923c2cb --- /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 00000000..133e81c1 --- /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 00000000..a0a325a3 --- /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 00000000..b9bec15a --- /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 00000000..2735d24a --- /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 00000000..59266514 --- /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 00000000..e66dc56b --- /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 00000000..b63b39ad --- /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 00000000..99e2cea3 --- /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 00000000..2a98dbca --- /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 00000000..b557dfae --- /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 00000000..35a4164b --- /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 Binary files differnew file mode 100644 index 00000000..5b555dd4 --- /dev/null +++ b/vncconfig/vncconfig.ico diff --git a/vncconfig/vncconfig.rc b/vncconfig/vncconfig.rc new file mode 100644 index 00000000..e74c8f13 --- /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 00000000..58277c3a --- /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 00000000..a96031cc --- /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 00000000..ac1dab37 --- /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 00000000..de69f4ed --- /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 00000000..e707ffb9 --- /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 00000000..9a7d8ce8 --- /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 00000000..e5ab0172 --- /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 00000000..32783681 --- /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 00000000..338b62c4 --- /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 00000000..bf1c4fc5 --- /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 00000000..8fb4c23f --- /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 00000000..60740bcc --- /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 00000000..c3337982 --- /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 00000000..d6cd139d --- /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 >= ¬dotdot[ 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 00000000..73057ae7 --- /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 00000000..3f9b2fec --- /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 00000000..318046d3 --- /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 00000000..fb625e0b --- /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 00000000..c8dd777d --- /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 00000000..ab5e7616 --- /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 00000000..19333cb8 --- /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 " . "edString($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 .= " " . "edString($arg); +} +$cmd .= " >> " . "edString($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 >> " . "edString($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 00000000..cd243ffe --- /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 00000000..09ed1fe8 --- /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 00000000..3d11dd96 --- /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 00000000..089c4c43 --- /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], ']', §ion.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 00000000..9120bde2 --- /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 00000000..b146ced6 --- /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 00000000..c083444c --- /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 00000000..554c86fb --- /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 00000000..0d2313a5 --- /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 00000000..7a64d383 --- /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 00000000..9e993956 --- /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 00000000..ab45f8ce --- /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 00000000..eec9b96a --- /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 00000000..8ab4ba4c --- /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 00000000..998a49f1 --- /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 00000000..bab2e137 --- /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 Binary files differnew file mode 100644 index 00000000..20a713f7 --- /dev/null +++ b/vncviewer/cursor1.cur diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx new file mode 100644 index 00000000..7d5653df --- /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(), ¤t); + + // 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 00000000..2bee1c48 --- /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 00000000..53a0678d --- /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 00000000..351a2b0a --- /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 00000000..59e2d00b --- /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 00000000..25d358f8 --- /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 00000000..557e4563 --- /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 Binary files differnew file mode 100644 index 00000000..98e5578d --- /dev/null +++ b/vncviewer/vncviewer.ico diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc new file mode 100644 index 00000000..bd9ab6d0 --- /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 00000000..2f54f66b --- /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 00000000..0b6431bb --- /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 00000000..c95951bb --- /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 00000000..d0e4cb53 --- /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 00000000..9274f9d3 --- /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 00000000..b228bfcb --- /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 00000000..a49dacc9 --- /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 00000000..96268cc7 --- /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 00000000..3c1ee8dc --- /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 00000000..245743f8 --- /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 00000000..a96031cc --- /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 00000000..815f5f12 --- /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 00000000..7b9e37d2 --- /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 00000000..a0db4369 --- /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 00000000..d8e0af36 --- /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 00000000..fecbcee0 --- /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 00000000..20af786b --- /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 00000000..52d7249d --- /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 00000000..30dd2705 --- /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 00000000..7cfea3c2 --- /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 00000000..cfd5ec01 --- /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 00000000..c967a5a5 --- /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 00000000..378ad0c5 --- /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 00000000..a870cb1d --- /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 00000000..c824d542 --- /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 00000000..bab2e137 --- /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 Binary files differnew file mode 100644 index 00000000..d996bcda --- /dev/null +++ b/winvnc/connected.ico diff --git a/winvnc/java/index.vnc b/winvnc/java/index.vnc new file mode 100644 index 00000000..aecb6131 --- /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 Binary files differnew file mode 100644 index 00000000..f1699ba5 --- /dev/null +++ b/winvnc/java/logo150x150.gif diff --git a/winvnc/java/vncviewer.jar b/winvnc/java/vncviewer.jar Binary files differnew file mode 100644 index 00000000..3c92b5bb --- /dev/null +++ b/winvnc/java/vncviewer.jar diff --git a/winvnc/msvcwarning.h b/winvnc/msvcwarning.h new file mode 100644 index 00000000..53a0678d --- /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 00000000..81c89e22 --- /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 00000000..5ba6ebc5 --- /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)©Data); + 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)©Data); + 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 00000000..aa9580ab --- /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 Binary files differnew file mode 100644 index 00000000..55b16bd9 --- /dev/null +++ b/winvnc/winvnc.ico diff --git a/winvnc/winvnc.rc b/winvnc/winvnc.rc new file mode 100644 index 00000000..22d0ba99 --- /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 00000000..d5a2b87a --- /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 00000000..d0c98c3c --- /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 00000000..0d8f646f --- /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 00000000..6923db7e --- /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 00000000..b9198ab9 --- /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 00000000..3c5c1fd6 --- /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 00000000..afff4be7 --- /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 00000000..87d282bb --- /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 00000000..56a197e7 --- /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 00000000..1a9ff1cc --- /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 00000000..e0b06cb1 --- /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 00000000..a96031cc --- /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 00000000..df40c33a --- /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 00000000..9f813965 --- /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 00000000..fb9baf6f --- /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 00000000..2de89af0 --- /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 00000000..08b64968 --- /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 00000000..1852f4d3 --- /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 00000000..982233a5 --- /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 00000000..640d5589 --- /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(®Rec) + { + 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 = ®Rec; + 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 == ®Rec) { + 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 00000000..9b5294ee --- /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 00000000..c983ecee --- /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 00000000..d948887e --- /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 00000000..a96031cc --- /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 00000000..cf8fdf92 --- /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 00000000..46e548f3 --- /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 00000000..ccaf5b85 --- /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 00000000..947f34dc --- /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 00000000..d52a4971 --- /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 00000000..c2ca8255 --- /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 00000000..3504c6a6 --- /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 00000000..eded33d4 --- /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 00000000..47a7d60c --- /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 00000000..8a245766 --- /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 00000000..1c57e8f0 --- /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 00000000..531562b2 --- /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 00000000..d97f4492 --- /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 00000000..29d67146 --- /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 00000000..828a3ff5 --- /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 00000000..cdc830b5 --- /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 00000000..5a977490 --- /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 00000000..e8942359 --- /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 00000000..b9a8a923 --- /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 00000000..57a590c2 --- /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 00000000..39cfaeb1 --- /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 00000000..9d364598 --- /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 00000000..255662f2 --- /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 00000000..518b573b --- /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 00000000..dd7a6d40 --- /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 00000000..173b2267 --- /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 00000000..9abe5412 --- /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 00000000..46821a02 --- /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 00000000..aa7f1d4d --- /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 00000000..a31a4bbb --- /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 00000000..77f7e763 --- /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 00000000..dfb2e867 --- /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 00000000..4c32ca30 --- /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 00000000..04b73b72 --- /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 00000000..9a076221 --- /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 00000000..4401df82 --- /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 00000000..a16d4b14 --- /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 00000000..8be02bd6 --- /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 00000000..123c2b32 --- /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 00000000..72facf90 --- /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 00000000..d8a4a699 --- /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 00000000..c8d6ce9a --- /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 00000000..3a6e4504 --- /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 00000000..18f00b41 --- /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 00000000..52cb529f --- /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 00000000..c3437038 --- /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-> <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-> <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-> <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-> <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->avail_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 00000000..65beb591 --- /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 00000000..17eb77f2 --- /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 */ |