summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzwer <zwer@1f4bef6d-8e0a-0410-8695-e467da8aaccf>2006-01-24 12:54:00 +0000
committerzwer <zwer@1f4bef6d-8e0a-0410-8695-e467da8aaccf>2006-01-24 12:54:00 +0000
commitb998c31e7e0f4f84b2f64c50093069c815772808 (patch)
tree7b65667843ea5db07766d23688f045d20140361c
downloadvdr-plugin-ffnetdev-b998c31e7e0f4f84b2f64c50093069c815772808.tar.gz
vdr-plugin-ffnetdev-b998c31e7e0f4f84b2f64c50093069c815772808.tar.bz2
FFNetDev-Plugin
git-svn-id: svn://svn.berlios.de/ffnetdev/trunk@1 1f4bef6d-8e0a-0410-8695-e467da8aaccf
-rw-r--r--CHANGELOG25
-rw-r--r--COPYING340
-rw-r--r--Makefile109
-rw-r--r--MinMax.h51
-rw-r--r--README95
-rw-r--r--config.h14
-rw-r--r--ffnetdev.c245
-rw-r--r--ffnetdev.h50
-rw-r--r--ffnetdevsetup.c26
-rw-r--r--ffnetdevsetup.h17
-rw-r--r--i18n.c44
-rw-r--r--i18n.h15
-rw-r--r--libvdr-ffnetdev.sobin0 -> 434326 bytes
-rw-r--r--netosd.c78
-rw-r--r--netosd.h38
-rw-r--r--osdworker.c756
-rw-r--r--osdworker.h122
-rw-r--r--pes2ts.c210
-rw-r--r--pes2ts.h50
-rw-r--r--remote.c58
-rw-r--r--remote.h36
-rw-r--r--rfb.h85
-rw-r--r--rfbproto.h786
-rw-r--r--streamdevice.c166
-rw-r--r--streamdevice.h48
-rw-r--r--tableinitcmtemplate.c108
-rw-r--r--tableinittctemplate.c122
-rw-r--r--tabletranstemplate.c99
-rw-r--r--tools/select.c49
-rw-r--r--tools/select.h75
-rw-r--r--tools/socket.c135
-rw-r--r--tools/socket.h108
-rw-r--r--tools/source.c169
-rw-r--r--tools/source.h109
-rw-r--r--tools/tools.c12
-rw-r--r--tools/tools.h67
-rw-r--r--translate.c138
-rw-r--r--translate.h81
-rw-r--r--tsworker.c248
-rw-r--r--tsworker.h56
-rw-r--r--vncEncodeCoRRE.c536
-rw-r--r--vncEncodeCoRRE.h84
-rw-r--r--vncEncodeHexT.c412
-rw-r--r--vncEncodeHexT.h75
-rw-r--r--vncEncodeRRE.c349
-rw-r--r--vncEncodeRRE.h69
-rw-r--r--vncEncoder.c673
-rw-r--r--vncEncoder.h141
48 files changed, 7379 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..d366286
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,25 @@
+Changelog
+ 2005-04-18: ffnetdev-0.0.1
+ - first version of ffnetdev
+
+ 2005-07-07: ffnetdev-0.0.2
+ - splitted the main worker thread into two threads. One for VNC and one for TS streaming.
+ - fixed busy-wait condition causing a high CPU load
+ - revised the OSD code to support VNC protocol version 3.3 including truecolor mode.
+ - added command line options for OSD(VNC) port, TS port and an option for explicitly enabling
+ the plugin's remote control in VDR
+
+ 2005-07-20: ffnetdev-0.0.3
+ - fixed Makefile so that "make dist" works
+ - fixed a bug in osdworker.c which caused a double action on a single keypress (VNC)
+ - added support for clients with bitsperpixel=8 and depth=8
+ - changed README: changed the required VDR version number to >= 1.3.18 due to changes in cDevice
+ - changed README: added a short description of supported VNC features
+ - added support for the bigEndian flag in VNC SetPixelFormat Message
+ - added support for bitsperpixel=32
+ - cleanup of debug output
+
+ 2005-08-27: ffnetdev-0.0.4
+ - fixed PES2TS remuxer: PES packets shorter than TS payload size(184 bytes) did not have the PUSI
+ (payload unit start indicator) flag set
+ - reworked PES2TS remuxer code and added a mutex to synchronize input ringbuffer access
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -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
+
+ 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) <year> <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) year 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 b/Makefile
new file mode 100644
index 0000000..fa42c2a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,109 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = ffnetdev
+DEBUG = 1
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'const char .*VERSION *=' ffnetdev.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -W -Woverloaded-virtual
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I.
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+COMMONOBJS = i18n.o \
+ \
+ tools/source.o tools/select.o tools/socket.o tools/tools.o
+
+
+SERVEROBJS = $(PLUGIN).o \
+ \
+ ffnetdev.o ffnetdevsetup.o osdworker.o tsworker.o netosd.o streamdevice.o pes2ts.o remote.o \
+ vncEncodeRRE.o vncEncodeCoRRE.o vncEncodeHexT.o \
+ vncEncoder.o translate.o \
+
+ifdef DEBUG
+ DEFINES += -DDEBUG
+ CXXFLAGS += -g
+else
+ CXXFLAGS += -O2
+endif
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+ifdef GCC3
+$(DEPFILE): Makefile
+ @rm -f $@
+ @for i in $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \
+ $(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \
+ done
+else
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) \
+ $(COMMONOBJS:%.o=%.c) > $@
+endif
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS)
+
+%.so:
+ $(CXX) $(CXXFLAGS) -shared $^ -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar cjf $(PACKAGE).tar.bz2 --exclude SCCS -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tar.bz2
+
+clean:
+ @-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tar.bz2 core* *~ *.bak
diff --git a/MinMax.h b/MinMax.h
new file mode 100644
index 0000000..1c7d479
--- /dev/null
+++ b/MinMax.h
@@ -0,0 +1,51 @@
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// Routines to calculate the maximum and minimum of two integers
+
+#if !defined(MINMAX_INCLUDED)
+#define MINMAX_INCLUDED
+#pragma once
+
+// Some routines used internally to minimise and maximise integers
+inline int Max(int x, int y)
+{
+ if (x>y)
+ return x;
+ else
+ return y;
+}
+
+inline int Min(int x, int y)
+{
+ if (x>y)
+ return y;
+ else
+ return x;
+}
+
+
+#endif
diff --git a/README b/README
new file mode 100644
index 0000000..bd5f25e
--- /dev/null
+++ b/README
@@ -0,0 +1,95 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Christian Cier-Zniewski <c.cier@gmx.de>
+ Jurij Retzlaff <jurij@topofweb.de>
+some code taken from: Sascha Volkenandt's streamdev plugin <sascha@akv-soft.de>,
+ TightVNC <http://www.tightvnc.com>
+
+
+Project's homepage: https://developer.berlios.de/projects/ffnetdev/
+
+See the file COPYING for license information.
+
+!!! WARNING !!!
+The code of this plugin is alpha quality. So expect it to have all kinds of bugs.
+If it crashes your machine, do not blame me. You have been warned!!! :-)
+!!! WARNING !!!
+
+------------
+Description:
+------------
+
+The purpose of this plugin is to provide an "easy" way of connecting possible streaming clients
+to VDR by emulating a full featured DVB device over the network including OSD and TS streaming capabilities.
+
+The FFnetDev Plugin creates two listening TCP ports:
+1) a VNC server on default port 20001 that exports the OSD and accepts remote control keys from the keyboard(or whatever the VNC client uses).
+2) a TS streaming server on default port 20002 that streams the MPEG2 data instead of decoding and rendering it.
+ The streaming server registers as a "normal" Full Featured DVB device in VDR. Therefore offering playback capabilites including playback of
+ recordings and "transfer mode".
+
+Both built-in servers work independent from each other.
+So you can just use the plugin for viewing the OSD on a remote machine for example.
+
+--------
+Details:
+--------
+
+OSD (and text2skin)
+-------------------
+The OSD is transfered using the VNC protocol (aka VNC). See rfbproto.h for details.
+Features supported:
+-currently supported encodings: RAW, RRE, Hextile, CoRRE (CoRRE have a bug)
+-colour-map and truecolor
+-depths in truecolor mode: 8-bit, 16-bit and 32-bit
+
+You can also use the text2skin plugin to get a nice skinned OSD.
+BUT BE AWARE that the code supports ONE BIG AREA only(method CanHandleAreas).
+This limitation is due to the fact that the built-in VNC server also supports palette based (a.k.a. colour map) framebuffer via VNC.
+So if you want to use the text2skin plugin you have to change the desired skin to only use ONE
+<window> tag with the greatest bounding rectangle the skin wants to draw in. bpp should be set to 8 bits.
+This is NOT going to change in future versions of the ffnetdev plugin.
+
+TS streaming
+------------
+The PES packets are multiplexed into a TS by the plugins own very simple PES2TS remux code.
+No PAT/PMT insertion is currently being done.
+The two TS PIDs for Audio and Video PIDs have fixed values. So changing a channel does not result in a change
+of the TS PIDs. (Video-PID: 99, Audio-PID: 100 [decimal])
+
+Existing clients
+----------------
+-Dreambox 5620S (simple native client, it is NOT an enigma plugin!, supports OSD via VNC and TS-Streaming)
+-Tuxbox VDRViewer(simple native client, it is NOT an enigma plugin!, supports OSD via VNC and TS-Streaming)
+-Any VNC client using RFB protocol version 3.3 or 3.5 (TightVNC, RealVNC, etc.) should work
+
+Command line options
+--------------------
+
+ -t PORT, --tsport PORT port number for sending TS to.
+ -o PORT, --osdport PORT listen on this port for OSD connect.
+ -e enable remote control over OSD connection.
+
+Option "-e" registers a new remote control object causing VDR to start the learning procedure for this new remote control if you start
+VDR and the ffnetdev plugin for the first time. If you want to control VDR over SVDRP you can simply leave out the "-e" option.
+
+--------------
+Prerequisites:
+--------------
+This plugin only works for VDR versions >=1.3.18.
+It is currently being developed with VDR version 1.3.28.
+
+Installation:
+-------------
+
+Install ffnetdev like any other plugin. In this example I assume that you have
+changed to the folder where the VDR sourcecode is located, and that it is
+version 0.0.3 of the plugin you wish to install.
+
+root@linux # cd PLUGINS/src
+root@linux # wget http://nano.gmxhome.de/ffnetdev/vdr-ffnetdev-0.0.4.tar.bz2
+root@linux # tar xfj vdr-ffnetdev-0.0.4.tar.bz2
+root@linux # cd ../..
+root@linux # make plugins
+root@linux # ./vdr -P ffnetdev
+
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..d10b02c
--- /dev/null
+++ b/config.h
@@ -0,0 +1,14 @@
+#ifndef __FFNETDEVCONFIG_H
+#define __FFNETDEVCONFIG_H
+
+
+struct sFFNetDevConfig {
+ int iAutoSetPrimaryDVB;
+};
+
+extern sFFNetDevConfig config;
+
+// #define DEBUG
+
+#endif //__FFNETDEVCONFIG_H
+
diff --git a/ffnetdev.c b/ffnetdev.c
new file mode 100644
index 0000000..a10bc14
--- /dev/null
+++ b/ffnetdev.c
@@ -0,0 +1,245 @@
+/*
+ * ffnetdev.c: Full Featured Network Device VDR-Plugin for Streaming
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+
+#include <vdr/tools.h>
+
+#include "i18n.h"
+#include "tsworker.h"
+#include "netosd.h"
+#include "ffnetdev.h"
+#include "streamdevice.h"
+#include "remote.h"
+#include "osdworker.h"
+#include "config.h"
+#include "ffnetdevsetup.h"
+
+
+const char *cPluginFFNetDev::VERSION = "0.0.4";
+const char *cPluginFFNetDev::DESCRIPTION = "Full Featured Network Device for Streaming";
+//const char *cOSDWorker::MAINMENUENTRY = "FFNetDev";
+
+// --- cNetOSDProvider -----------------------------------------------
+
+cNetOSDProvider::cNetOSDProvider(void)
+{
+
+}
+
+cOsd * cNetOSDProvider::CreateOsd(int Left, int Top)
+{
+
+ osd = new cNetOSD(Left, Top);
+ return osd;
+}
+
+
+// --- cPluginFFNetDev ----------------------------------------------------------
+
+const char *cPluginFFNetDev::Version(void) {
+ return tr(VERSION);
+}
+
+const char *cPluginFFNetDev::Description(void) {
+ return tr(DESCRIPTION);
+}
+
+cPluginFFNetDev::cPluginFFNetDev(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ TSPort = STREAMPORT;
+ OSDPort = OSDPORT;
+ EnableRemote = false;
+ m_origPrimaryDevice = -1;
+ m_Remote = NULL;
+
+ config.iAutoSetPrimaryDVB = 0;
+}
+
+cPluginFFNetDev::~cPluginFFNetDev()
+{
+ cOSDWorker::Exit();
+ cTSWorker::Exit();
+}
+
+const char *cPluginFFNetDev::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return
+ " -t PORT, --tsport PORT port number for sending TS to.\n"
+ " -o PORT, --osdport PORT listen on this port for OSD connect.\n"
+ " -e enable remote control over OSD connection.\n"
+ "\n";
+}
+
+bool cPluginFFNetDev::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ fprintf (stderr, "[ffnetdev] processing args.\n");
+ static struct option long_options[] = {
+ { "tsport", required_argument , NULL, 't' },
+ { "osdport", required_argument , NULL, 'o' },
+ { "enable-remote", no_argument , NULL, 'e' },
+ { NULL, 0 , NULL, 0 }
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "t:o:e", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'e': EnableRemote = true;
+ break;
+ case 't': if (isnumber(optarg)) {
+ int n = atoi(optarg);
+ if (0 < n && n < 65536) {
+ TSPort = n;
+ fprintf(stderr, "[ffnetdev] TS Port: %d\n", n);
+ break;
+ }
+ }
+ fprintf(stderr, "[ffnetdev] invalid port number: %s\n", optarg);
+ return 2;
+ break;
+ case 'o': if (isnumber(optarg)) {
+ int n = atoi(optarg);
+ if (0 < n && n < 65536) {
+ OSDPort = n;
+ fprintf(stderr, "[ffnetdev] OSD Port: %d\n", n);
+ break;
+ }
+ }
+ fprintf(stderr, "[ffnetdev] invalid port number: %s\n", optarg);
+ return 2;
+ break;
+ default : return 2;
+ }
+ }
+ fprintf (stderr, "[ffnetdev] finished processing args.\n");
+ return true;
+}
+
+bool cPluginFFNetDev::Active(void) {
+ return (cOSDWorker::Active() || cTSWorker::Active());
+}
+
+bool cPluginFFNetDev::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ RegisterI18n(Phrases);
+
+
+ cOSDWorker::Init(OSDPort, this);
+ cTSWorker::Init(m_StreamDevice, TSPort, this);
+
+ return true;
+}
+
+bool cPluginFFNetDev::Initialize(void)
+{
+ // Start any background activities the plugin shall perform.
+ fprintf(stderr,"[ffnetdev] initializing plugin.\n");
+ m_StreamDevice = new cStreamDevice();
+ return true;
+}
+
+void cPluginFFNetDev::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+cOsdObject *cPluginFFNetDev::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ fprintf (stderr, "[ffnetdev] MainMenuAction called.\n");
+// return new cMenuSetupSoftdevice;
+ return NULL;
+}
+
+cMenuSetupPage *cPluginFFNetDev::SetupMenu(void)
+{
+ // Return our setup menu
+// return new cMenuSetupFFNetDev;
+ return new cFFNetDevSetup;
+}
+
+bool cPluginFFNetDev::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ if (!strcasecmp(Name, "AutoSetPrimaryDVB")) config.iAutoSetPrimaryDVB = atoi(Value);
+ else
+ return false;
+
+ return true;
+}
+
+void cPluginFFNetDev::SetPrimaryDevice()
+{
+ if ((config.iAutoSetPrimaryDVB == 1) && (m_origPrimaryDevice == -1))
+ {
+ cDevice *PrimaryDevice;
+ if ((PrimaryDevice = cDevice::PrimaryDevice()) != NULL)
+ m_origPrimaryDevice = PrimaryDevice->DeviceNumber() + 1;
+ else
+ m_origPrimaryDevice = -1;
+
+ if (m_StreamDevice->DeviceNumber() + 1 != m_origPrimaryDevice)
+ {
+ cDevice::SetPrimaryDevice(m_StreamDevice->DeviceNumber() + 1);
+ isyslog("[ffnetdev] set Primary Device to %d", m_StreamDevice->DeviceNumber() + 1);
+ }
+ else
+ {
+ m_origPrimaryDevice = -1;
+ }
+ }
+
+ if(EnableRemote)
+ {
+ m_Remote = new cMyRemote("ffnetdev");
+ new cLearningThread();
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] remote control enabled.\n");
+#endif
+ isyslog("[ffnetdev] remote control enabled.\n");
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] remote control disabled.\n");
+#endif
+ isyslog("[ffnetdev] remote control disabled.\n");
+ delete m_Remote;
+ m_Remote = NULL;
+ }
+}
+
+
+void cPluginFFNetDev::RestorePrimaryDevice()
+{
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] remote control disabled.\n");
+#endif
+ isyslog("[ffnetdev] remote control disabled.\n");
+ delete m_Remote;
+ m_Remote = NULL;
+
+ if (m_origPrimaryDevice != -1)
+ {
+ cDevice::SetPrimaryDevice(m_origPrimaryDevice);
+ isyslog("[ffnetdev] restore Primary Device to %d", m_origPrimaryDevice);
+ m_origPrimaryDevice = -1;
+ sleep(5);
+ }
+}
+
+
+sFFNetDevConfig config;
+
+VDRPLUGINCREATOR(cPluginFFNetDev); // Don't touch this!
diff --git a/ffnetdev.h b/ffnetdev.h
new file mode 100644
index 0000000..9e5e327
--- /dev/null
+++ b/ffnetdev.h
@@ -0,0 +1,50 @@
+/*
+ * ffnetdev.h: Full Featured Network Device VDR-Plugin for Streaming
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+#ifndef _FFNETDEV__H
+#define _FFNETDEV__H
+
+#include <vdr/plugin.h>
+#include "streamdevice.h"
+#include "remote.h"
+
+#define OSDPORT 20001
+#define STREAMPORT 20002
+
+class cPluginFFNetDev : public cPlugin {
+private:
+ static const char *DESCRIPTION;
+ static const char *VERSION;
+ //static const char *MAINMENUENTRY;
+ cStreamDevice *m_StreamDevice;
+ cMyRemote *m_Remote;
+ int OSDPort;
+ int TSPort;
+ bool EnableRemote;
+ int m_origPrimaryDevice;
+
+public:
+ cPluginFFNetDev(void);
+ virtual ~cPluginFFNetDev();
+ virtual const char *Description(void);
+ virtual const char *Version(void);
+ virtual const char *CommandLineHelp(void);
+ virtual bool Initialize(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+ //virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ virtual bool Active(void);
+
+ void SetPrimaryDevice();
+ void RestorePrimaryDevice();
+ cMyRemote *GetRemote() { return m_Remote; }
+};
+
+#endif
diff --git a/ffnetdevsetup.c b/ffnetdevsetup.c
new file mode 100644
index 0000000..6c0c339
--- /dev/null
+++ b/ffnetdevsetup.c
@@ -0,0 +1,26 @@
+/*
+ * ffnetdevsetup.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+
+#include "ffnetdevsetup.h"
+#include "config.h"
+
+cFFNetDevSetup::cFFNetDevSetup(void)
+{
+
+ m_iAutoSetPrimaryDVB = config.iAutoSetPrimaryDVB;
+
+ Add(new cMenuEditBoolItem(tr("auto set as primary device"), &m_iAutoSetPrimaryDVB, tr("no"), tr("yes")));
+}
+
+void cFFNetDevSetup::Store(void)
+{
+ SetupStore("AutoSetPrimaryDVB", config.iAutoSetPrimaryDVB = m_iAutoSetPrimaryDVB);
+}
+
+
diff --git a/ffnetdevsetup.h b/ffnetdevsetup.h
new file mode 100644
index 0000000..befbd6f
--- /dev/null
+++ b/ffnetdevsetup.h
@@ -0,0 +1,17 @@
+#ifndef __FFNETDEVSETUP_H
+#define __FFNETDEVSETUP_H
+
+#include <vdr/plugin.h>
+
+class cFFNetDevSetup : public cMenuSetupPage
+{
+private:
+ int m_iAutoSetPrimaryDVB;
+protected:
+ virtual void Store(void);
+public:
+ cFFNetDevSetup(void);
+};
+
+#endif //__FFNETDEVSETUP_H
+
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..9a6eff7
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,44 @@
+/*
+ * i18n.c: Internationalization
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "i18n.h"
+
+const tI18nPhrase Phrases[] = {
+ { "Full Featured Network Device for Streaming",
+ "Full Featured Network Device for Streaming",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
+ { "auto set as primary device",
+ "auto set as primary device",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
+ { NULL }
+ };
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..2f6951e
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,15 @@
+/*
+ * i18n.h: Internationalization
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _I18N__H
+#define _I18N__H
+
+#include <vdr/i18n.h>
+
+extern const tI18nPhrase Phrases[];
+
+#endif //_I18N__H
diff --git a/libvdr-ffnetdev.so b/libvdr-ffnetdev.so
new file mode 100644
index 0000000..4369558
--- /dev/null
+++ b/libvdr-ffnetdev.so
Binary files differ
diff --git a/netosd.c b/netosd.c
new file mode 100644
index 0000000..5bfdab6
--- /dev/null
+++ b/netosd.c
@@ -0,0 +1,78 @@
+/*
+ * netosd.c: OSD over network
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "netosd.h"
+#include "osdworker.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+cNetOSD::cNetOSD(int Left, int Top) : cOsd(Left, Top)
+{
+#ifdef DEBUG
+ fprintf(stderr,"[ffnetdev] NetOSD: Constructor cNetOSD.\n");
+#endif
+}
+
+cNetOSD::~cNetOSD()
+{
+#ifdef DEBUG
+ fprintf(stderr,"[ffnetdev] NetOSD: Destructor cNetOSD.\n");
+#endif
+
+ cOSDWorker::ClearScreen();
+}
+
+eOsdError cNetOSD::CanHandleAreas(const tArea *Areas, int NumAreas)
+{
+ eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas);
+ if (Result == oeOk) {
+ if (NumAreas > 1) // Handle only one big area (because we support VNC colour map mode)
+ return oeTooManyAreas; // We cannot handle multiple areas having different colour maps. We need a single colourmap. Thus, only one area.
+ int TotalMemory = 0;
+ for (int i = 0; i < NumAreas; i++) {
+ if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8)
+ return oeBppNotSupported;
+ if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0)
+ return oeWrongAlignment;
+ TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] NetOSD: CanHandleAreas: OSD area size: %d bytes.\r\n", TotalMemory);
+#endif
+ if (TotalMemory > MAXOSDMEMORY)
+ return oeOutOfMemory;
+ }
+ return Result;
+}
+
+void cNetOSD::Flush(void)
+{
+ cBitmap *Bitmap;
+
+ for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++)
+ {
+ int x1=0, x2=0, y1=0, y2=0;
+ if (Bitmap->Dirty(x1, y1, x2, y2))
+ {
+ // commit colors:
+ int NumColors;
+ const tColor *Colors = Bitmap->Colors(NumColors);
+ if (Colors) {
+ cOSDWorker::SendCMAP(NumColors , Colors);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] NetOSD: Left: %d, Top: %d, X0: %d, Y0: %d, Width: %d, Height: %d\n",
+ Left(), Top(), Bitmap->X0(), Bitmap->Y0(), Bitmap->Width(), Bitmap->Height());
+ fprintf(stderr, "[ffnetdev] NetOSD: Dirty area x1: %d, y1: %d, x2: %d, y2: %d\n",x1,y1,x2,y2);
+#endif
+ // commit modified data:
+ cOSDWorker::SendScreen(Bitmap->Width(), Left()+x1 +Bitmap->X0(), Top()+y1+Bitmap->Y0(), x2-x1+1, y2-y1+1, Bitmap->Data(x1, y1));
+ }
+ Bitmap->Clean();
+ }
+}
+
+
diff --git a/netosd.h b/netosd.h
new file mode 100644
index 0000000..1657d5e
--- /dev/null
+++ b/netosd.h
@@ -0,0 +1,38 @@
+/*
+ * netosd.h: OSD over network
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _NETOSD__H
+#define _NETOSD__H
+
+#include <vdr/osd.h>
+#include "tools/socket.h"
+
+// --- cNetOSD -----------------------------------------------
+class cNetOSD : public cOsd {
+private:
+ bool truecolor;
+protected:
+public:
+ cNetOSD(int XOfs, int XOfs);
+ virtual ~cNetOSD();
+ virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
+ virtual void Flush(void);
+};
+
+// --- cNetOSDProvider ----------------------------------------
+
+class cNetOSDProvider : public cOsdProvider {
+private:
+ cOsd *osd;
+ cNetOSD **NetOSD;
+public:
+ cNetOSDProvider(void);
+ virtual cOsd *CreateOsd(int Left, int Top);
+};
+
+#endif //_NETOSD__H
+
diff --git a/osdworker.c b/osdworker.c
new file mode 100644
index 0000000..8147ddb
--- /dev/null
+++ b/osdworker.c
@@ -0,0 +1,756 @@
+/*
+ * osdworker.c: osd worker thread
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <vdr/tools.h>
+#include <sys/time.h>
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include "osdworker.h"
+
+
+#include "vncEncodeRRE.h"
+#include "vncEncodeCoRRE.h"
+#include "vncEncodeHexT.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+cOSDWorker *cOSDWorker::m_Instance = NULL;
+
+cOSDWorker::cOSDWorker(void)
+ : cThread("[ffnetdev] OSD via VNC")
+{
+ m_Active = false;
+ m_OSDClient = NULL;
+ m_pSendBuffer = NULL;
+ m_SendBufferSize = 0;
+ state = NO_CLIENT;
+ close_OSDclient_request = false;
+
+ UseAlpha = false;
+
+ m_pEncoder = NULL;
+
+ ServerFormat.trueColour = false;
+ ServerFormat.bitsPerPixel = 8;
+ ServerFormat.depth = 8;
+ ServerFormat.bigEndian = false;
+ ServerFormat.redShift = 16;
+ ServerFormat.redMax = 255;
+ ServerFormat.greenShift = 8;
+ ServerFormat.greenMax = 255;
+ ServerFormat.blueShift = 0;
+ ServerFormat.blueMax = 255;
+
+ memset(&m_OSDBuffer, 0, sizeof(m_OSDBuffer));
+
+ numOSDColors = 0;
+ memset(&OSDColors, 0, sizeof(OSDColors));
+
+ m_notupdatedLeft = -1;
+ m_notupdatedTop = -1;
+ m_notupdatedRight = -1;
+ m_notupdatedBottom = -1;
+ memset(&m_lasttime, 0, sizeof(m_lasttime));
+ memset(&m_lastupdate, 0, sizeof(m_lastupdate));
+}
+
+cOSDWorker::~cOSDWorker() {
+ if (m_Active) Stop();
+
+ if (m_pEncoder != NULL)
+ {
+ m_pEncoder->LogStats();
+ delete m_pEncoder;
+ m_pEncoder = NULL;
+ }
+
+ if (m_pSendBuffer != NULL)
+ delete [] m_pSendBuffer;
+}
+
+
+
+
+
+void cOSDWorker::Init(int osdport, cPluginFFNetDev *pPlugin) {
+ if (m_Instance == NULL)
+ {
+ m_Instance = new cOSDWorker;
+ m_Instance->OSDPort = osdport;
+ m_Instance->Start();
+ m_Instance->m_pPlugin = pPlugin;
+ }
+}
+
+
+void cOSDWorker::Exit(void) {
+ if (m_Instance != NULL) {
+ m_Instance->Stop();
+ DELETENULL(m_Instance);
+ }
+}
+
+void cOSDWorker::Stop(void) {
+ m_Active = false;
+ Cancel(3);
+}
+
+void cOSDWorker::CloseOSDClient(void) {
+ if (m_Instance == NULL)
+ return;
+
+ m_Instance->close_OSDclient_request = true;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Closing of OSD client socket requested.\r\n");
+#endif
+}
+
+bool cOSDWorker::SendPlayMode(ePlayMode PlayMode) {
+
+ // not yet implemented
+ return true;
+}
+
+
+void cOSDWorker::CreateSendBuffer(int SendBufferSize)
+{
+ if (SendBufferSize != m_SendBufferSize)
+ {
+ if (m_pSendBuffer != NULL)
+ delete [] m_pSendBuffer;
+
+ m_pSendBuffer = new BYTE[SendBufferSize];
+ memset(m_pSendBuffer, 0, SendBufferSize);
+
+ m_SendBufferSize = SendBufferSize;
+ }
+}
+
+bool cOSDWorker::ClearScreen(void)
+{
+ if (m_Instance == NULL)
+ return false;
+
+ // this should be improved;
+ // 1) maybe we should send a our very "special" pseudo encoding[CLEAR_SCREEN] to our "special" VNC client to get an empty screen really fast
+ bool bRetVal = false;
+ memset(&(m_Instance->m_OSDBuffer), 0, 720*576);
+ memset(&(m_Instance->m_lasttime), 0, sizeof(m_Instance->m_lasttime));
+ bRetVal = SendScreen(720,0,0,720,576,&(m_Instance->m_OSDBuffer));
+
+ rfbBellMsg fu;
+ fu.type=rfbBell;
+ OSDWrite((unsigned char*)&fu, sz_rfbBellMsg);
+
+ return bRetVal;
+}
+
+
+bool cOSDWorker::SendScreen(unsigned int stride, unsigned int x1, unsigned int y1, unsigned int w, unsigned int h, const void *data)
+{
+ if (m_Instance == NULL)
+ return false;
+
+ rfbFramebufferUpdateMsg fu;
+ rfbFramebufferUpdateRectHeader furh;
+
+ unsigned int x;
+ unsigned int y;
+ for (y=0; y<h; y++)
+ for (x=0; x<w; x++)
+ m_Instance->m_OSDBuffer[(720*(y+y1))+x+x1] = *((unsigned char *)data+(y*stride)+x);
+
+ x = x1;
+ y = y1;
+ x1 = (m_Instance->m_notupdatedLeft != -1 && m_Instance->m_notupdatedLeft < (int)x1)
+ ? m_Instance->m_notupdatedLeft : x1;
+ y1 = (m_Instance->m_notupdatedTop != -1 && m_Instance->m_notupdatedTop < (int)y1)
+ ? m_Instance->m_notupdatedTop : y1;
+
+ w = (m_Instance->m_notupdatedRight != -1 && m_Instance->m_notupdatedRight > (int)(x1 + w))
+ ? m_Instance->m_notupdatedRight - x1 : w + (x - x1);
+ h = (m_Instance->m_notupdatedBottom != -1 && m_Instance->m_notupdatedBottom > (int)(y1 + h))
+ ? m_Instance->m_notupdatedBottom - y1 : h + (y - y1);
+
+ struct timeval curtime;
+ gettimeofday(&curtime, 0);
+ curtime.tv_sec = curtime.tv_sec - (((int)curtime.tv_sec / 1000000) * 1000000);
+ if ((curtime.tv_sec * 1000 + (curtime.tv_usec / 1000) < m_Instance->m_lasttime.tv_sec * 1000 + (m_Instance->m_lasttime.tv_usec / 1000) + 100) ||
+ (m_Instance->m_pEncoder == NULL))
+ {
+ m_Instance->m_notupdatedLeft = (int)x1;
+ m_Instance->m_notupdatedTop = (int)y1;
+ m_Instance->m_notupdatedRight = (int)(x1 + w);
+ m_Instance->m_notupdatedBottom = (int)(y1 + h);
+ m_Instance->m_lasttime = curtime;
+
+ return false;
+ }
+ else
+ {
+ m_Instance->m_notupdatedLeft = -1;
+ m_Instance->m_notupdatedTop = -1;
+ m_Instance->m_notupdatedRight = -1;
+ m_Instance->m_notupdatedBottom = -1;
+ m_Instance->m_lasttime = curtime;
+ m_Instance->m_lastupdate = curtime;
+ }
+
+ if ((m_Instance->state==HANDSHAKE_OK) && (m_Instance->m_pEncoder != NULL)) {
+ RECT rect = {x1, y1, x1 + w, y1 + h};
+ fu.type=rfbFramebufferUpdate;
+ fu.nRects=Swap16IfLE(1);
+ OSDWrite((unsigned char*)&fu, sz_rfbFramebufferUpdateMsg);
+
+ int BufferSize = m_Instance->m_pEncoder->RequiredBuffSize(w, h);
+ m_Instance->CreateSendBuffer(BufferSize);
+ BufferSize = m_Instance->m_pEncoder->EncodeRect( &(m_Instance->m_OSDBuffer[0]), m_Instance->m_pSendBuffer, rect);
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Send OSD Data %d Bytes\n", BufferSize);
+#endif
+ dsyslog("[ffnetdev] VNC: Send OSD Data %d Bytes\n", BufferSize);
+ OSDWrite((unsigned char*)m_Instance->m_pSendBuffer, BufferSize);
+
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+
+bool cOSDWorker::SendCMAP(int NumColors, const tColor *Colors)
+{
+ if (m_Instance == NULL)
+ return false;
+
+ rfbSetColourMapEntriesMsg scme;
+ CARD16 red;
+ CARD16 green;
+ CARD16 blue;
+ CARD16 alpha;
+
+ int i;
+
+ if ((m_Instance->state==HANDSHAKE_OK) && !(m_Instance->ClientFormat.trueColour)) {
+ scme.type=rfbSetColourMapEntries;
+ scme.firstColour = Swap16IfLE(0);
+ scme.nColours = Swap16IfLE((CARD16)NumColors);
+
+ OSDWrite((unsigned char*)&scme, sz_rfbSetColourMapEntriesMsg);
+
+ for(i=0; i<NumColors; i++)
+ {
+ red = ((Colors[i]&0x00FF0000) >> 16);
+ green = ((Colors[i]&0x0000FF00) >> 8);
+ blue = ((Colors[i]&0x000000FF) );
+
+ if (m_Instance->UseAlpha) {
+ alpha = ((Colors[i]&0xFF000000) >> 24);
+ OSDWrite( (unsigned char*) &alpha, 2);
+ }
+
+ OSDWrite( (unsigned char*) &red, 2);
+ OSDWrite( (unsigned char*) &green, 2);
+ OSDWrite( (unsigned char*) &blue, 2);
+
+ m_Instance->OSDColors[i] = Colors[i];
+ }
+ m_Instance->numOSDColors = NumColors;
+ if (m_Instance->m_pEncoder != NULL)
+ m_Instance->m_pEncoder->SetLocalFormat(m_Instance->ServerFormat, 720, 576);
+
+ return true;
+ }
+ else {
+ for (i=0; i<NumColors; i++)
+ m_Instance->OSDColors[i] = Colors[i];
+ m_Instance->numOSDColors = NumColors;
+ if (m_Instance->m_pEncoder != NULL)
+ m_Instance->m_pEncoder->SetLocalFormat(m_Instance->ServerFormat, 720, 576);
+
+ return false;
+ }
+
+
+}
+
+bool cOSDWorker::OSDWrite(unsigned char *data, unsigned int data_length)
+{
+ if (m_Instance == NULL)
+ return false;
+
+ if (m_Instance->m_OSDClient!=NULL) {
+ if (m_Instance->m_OSDClient->IsOpen())
+ {
+ //fprintf(stderr, "[ffnetdev] VNC: Trying to send...\n");
+ if (!m_Instance->m_OSDClient->SafeWrite(data, data_length))
+ {
+ CloseOSDClient();
+ return false;
+ }
+ } //else fprintf(stderr, "[ffnetdev] VNC: Cannot send...\n");
+ //for (unsigned int i=0; i<data_length; i++) fprintf(stderr,"%02X ",*(data+i));
+ //fprintf(stderr,"\n");
+
+ return true;
+ }
+ return false;
+}
+
+bool cOSDWorker::RFBRead(char *buffer, int len)
+{
+ if (m_Instance == NULL)
+ return false;
+
+ if ( m_OSDClient->Read(buffer, len)==0 ) {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client closed connection.\n");
+#endif
+ isyslog("[ffnetdev] VNC: Connection closed: client %s:%d",
+ m_OSDClient->RemoteIp().c_str(), m_OSDClient->RemotePort());
+ state = NO_CLIENT;
+ m_Instance->m_OSDClient->Close();
+ delete m_pEncoder;
+ m_pEncoder = NULL;
+
+ return false;
+ }
+ else
+ return true;
+}
+
+void cOSDWorker::HandleClientRequests(cTBSelect *select)
+{
+ if ( select->CanRead(*m_OSDClient) ) {
+ rfbClientToServerMsg msg;
+
+ if (!RFBRead((char*)&msg, 1))
+ return;
+
+ switch (msg.type) {
+
+ case rfbSetPixelFormat: if (!RFBRead( ((char*)&msg.spf)+1, sz_rfbSetPixelFormatMsg-1))
+ return;
+ dsyslog("[ffnetdev] VNC: SetPixelFormat\n");
+ dsyslog("[ffnetdev] VNC: ->bitsPerPixel: %d\n",msg.spf.format.bitsPerPixel);
+ dsyslog("[ffnetdev] VNC: ->depth: %d\n",msg.spf.format.depth);
+ dsyslog("[ffnetdev] VNC: ->bigEndian: %d\n",msg.spf.format.bigEndian);
+ dsyslog("[ffnetdev] VNC: ->trueColour: %d\n",msg.spf.format.trueColour);
+ dsyslog("[ffnetdev] VNC: ->redMax: %d\n",Swap16IfLE(msg.spf.format.redMax));
+ dsyslog("[ffnetdev] VNC: ->greenMax: %d\n",Swap16IfLE(msg.spf.format.greenMax));
+ dsyslog("[ffnetdev] VNC: ->blueMax: %d\n",Swap16IfLE(msg.spf.format.blueMax));
+ dsyslog("[ffnetdev] VNC: ->redShift: %d\n",msg.spf.format.redShift);
+ dsyslog("[ffnetdev] VNC: ->greenShift: %d\n",msg.spf.format.greenShift);
+ dsyslog("[ffnetdev] VNC: ->blueShift: %d\n",msg.spf.format.blueShift);
+
+ if ( (msg.spf.format.bitsPerPixel!=8 )
+ &&(msg.spf.format.bitsPerPixel!=16)
+ &&(msg.spf.format.bitsPerPixel!=32)) {
+ esyslog("[ffnetdev] SORRY!!! VNC client requested unsupported pixel format! Closing connection...\n");
+ CloseOSDClient();
+ return;
+ }
+ else
+ ClientFormat = msg.spf.format;
+ dsyslog("[ffnetdev] VNC: RGB %d %d %d %d %d %d\n",
+ ClientFormat.redShift, ClientFormat.redMax, ClientFormat.greenShift, ClientFormat.greenMax, ClientFormat.blueShift, ClientFormat.blueMax);
+ break;
+ case rfbFixColourMapEntries: if (!RFBRead( ((char*)&msg.fcme)+1, sz_rfbFixColourMapEntriesMsg-1))
+ return;
+ isyslog("[ffnetdev] VNC: FixColourMapEntries (ignored).\n");
+ break;
+ case rfbSetEncodings: if (!RFBRead( ((char*)&msg.se)+1, sz_rfbSetEncodingsMsg-1))
+ return;
+
+ dsyslog("[ffnetdev] VNC: SetEncodings: Client offers %d encodings:\n", Swap16IfLE(msg.se.nEncodings));
+
+ CARD32 encodings[16];
+ if (!RFBRead( ((char*)&encodings), Swap16IfLE(msg.se.nEncodings)*sizeof(CARD32) ))
+ return;
+
+ for (int i=0;i<Swap16IfLE(msg.se.nEncodings);i++) {
+ switch (Swap32IfLE(encodings[i])) {
+ case rfbEncodingRaw:
+ if (m_pEncoder == NULL)
+ {
+ dsyslog("[ffnetdev] VNC: ->RAW encoding.\n");
+ m_pEncoder = new vncEncoder();
+ }
+ break;
+ case rfbEncodingCopyRect:
+ isyslog("[ffnetdev] VNC: ->CopyRect encoding(not supported).\n");
+ break;
+ case rfbEncodingRRE:
+ if (m_pEncoder == NULL)
+ {
+ dsyslog("[ffnetdev] VNC: ->RRE encoding.\n");
+ m_pEncoder = new vncEncodeRRE();
+ }
+ break;
+ case rfbEncodingCoRRE:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->CoRRE encoding(not supported).\n");
+ m_pEncoder = new vncEncodeCoRRE();
+ }
+ break;
+ case rfbEncodingHextile:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->Hextile encoding(not supported).\n");
+ m_pEncoder = new vncEncodeHexT();
+ }
+ break;
+ case rfbEncodingZlib:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->Zlib encoding(not supported).\n");
+ //m_pEncoder = new vncEncodeZlib();
+ }
+ break;
+ case rfbEncodingTight:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->Tight encoding(not supported).\n");
+ //m_pEncoder = new vncEncodeTight();
+ }
+ break;
+ case rfbEncodingZlibHex:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->ZlibHex encoding(not supported).\n");
+ //m_pEncoder = new vncEncodeZlibHex();
+ }
+ break;
+ case rfbEncodingZRLE:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->ZRLE encoding(not supported).\n");
+ }
+ break;
+ case rfbEncSpecialUseAlpha:
+ if (m_pEncoder == NULL)
+ {
+ isyslog("[ffnetdev] VNC: ->Special FFnetDev Encoding: client wants alpha channel.\n");
+ }
+ UseAlpha = true;
+ break;
+ default:
+ esyslog("[ffnetdev] VNC: ->Unknown encoding or unknown pseudo encoding.\n");
+ }
+
+ if (m_pEncoder != NULL)
+ {
+ m_pEncoder->Init();
+ m_pEncoder->SetLocalFormat(ServerFormat, 720, 576);
+ m_pEncoder->SetCompressLevel(6);
+ m_pEncoder->SetQualityLevel(9);
+ m_pEncoder->SetRemoteFormat(ClientFormat);
+ }
+ }
+ break;
+ case rfbFramebufferUpdateRequest:
+ if (!RFBRead( ((char*)&msg.fur)+1, sz_rfbFramebufferUpdateRequestMsg-1))
+ return;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: FramebufferUpdateRequest: Client wants %s:\n",
+ msg.fur.incremental? "incremental update":"complete update");
+ fprintf(stderr, "[ffnetdev] VNC: x: %d, y: %d, w: %d, h: %d\n",
+ Swap16IfLE(msg.fur.x),
+ Swap16IfLE(msg.fur.y),
+ Swap16IfLE(msg.fur.w),
+ Swap16IfLE(msg.fur.h)
+ );
+#endif
+ dsyslog("[ffnetdev] VNC: FramebufferUpdateRequest: Client wants %s:\n",
+ msg.fur.incremental? "incremental update":"complete update");
+ dsyslog("[ffnetdev] VNC: x: %d, y: %d, w: %d, h: %d\n",
+ Swap16IfLE(msg.fur.x),
+ Swap16IfLE(msg.fur.y),
+ Swap16IfLE(msg.fur.w),
+ Swap16IfLE(msg.fur.h)
+ );
+ if (FirstUpdateRequest) {
+ ClearScreen();
+ FirstUpdateRequest = false;
+ }
+ else
+ {
+ SendCMAP(numOSDColors, OSDColors);
+ SendScreen(720, //stride
+ Swap16IfLE(msg.fur.x), Swap16IfLE(msg.fur.y),
+ Swap16IfLE(msg.fur.w), Swap16IfLE(msg.fur.h),
+ &m_OSDBuffer);
+ }
+ break;
+ case rfbKeyEvent: if (!RFBRead( ((char*)&msg.ke)+1, sz_rfbKeyEventMsg-1))
+ return;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: KeyEvent\n");
+#endif
+ dsyslog("[ffnetdev] VNC: KeyEvent\n");
+ cMyRemote *pRemote;
+ if ((pRemote = m_pPlugin->GetRemote()) != NULL) {
+ if (msg.ke.down)
+ pRemote->Put(Swap32IfLE(msg.ke.key), false, false);
+ else
+ pRemote->Put(Swap32IfLE(msg.ke.key), false, true);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Remote: %04X\n", msg.ke.key);
+#endif
+ dsyslog("[ffnetdev] VNC: Remote: %04X\n", msg.ke.key);
+ break;
+ case rfbPointerEvent: if (!RFBRead( ((char*)&msg.pe)+1, sz_rfbPointerEventMsg-1))
+ return;
+ //fprintf(stderr, "[ffnetdev] VNC: PointerEvent\n");
+ break;
+ case rfbClientCutText: if (!RFBRead( ((char*)&msg.cct)+1, sz_rfbClientCutTextMsg-1))
+ return;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: ClientCutText\n");
+#endif
+ dsyslog("[ffnetdev] VNC: ClientCutText\n");
+ break;
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Unknown RFB message.\n");
+#endif
+ dsyslog("[ffnetdev] VNC: Unknown RFB message.\n");
+ }
+ }
+}
+
+void cOSDWorker::Action(void) {
+ cTBSelect select;
+ cTBSocket m_OSDListen;
+
+ const char* m_ListenIp = "0.0.0.0";
+ uint m_OSDListenPort = OSDPort;
+
+ m_OSDClient = new cTBSocket;
+
+ m_Active = true;
+
+ if (!m_OSDListen.Listen(m_ListenIp, m_OSDListenPort, 1)) {
+ esyslog("[ffnetdev] VNC: Couldn't listen %s:%d: %s", m_ListenIp, m_OSDListenPort, strerror(errno));
+ m_Active = false;
+ }
+ else
+ isyslog("[ffnetdev] VNC: Listening on port %d", m_OSDListenPort);
+
+
+ while (m_Active) {
+ select.Clear();
+
+ if (state==NO_CLIENT)
+ select.Add(m_OSDListen, false);
+ else
+ select.Add(*m_OSDClient, false);
+
+ int numfd;
+ /* React on status change of any of the above file descriptor */
+ if ((numfd=select.Select(1000)) < 0) {
+ if (!m_Active) // Exit was requested while polling
+ continue;
+ esyslog("FFNetDev: Fatal error, FFNetDev exiting: %s", strerror(errno));
+ m_Active = false;
+ continue;
+ }
+
+ //DEBUG
+ /*
+ fprintf(stderr, "Num_FD OSD: %d\n", numfd);
+
+ if (select.CanRead(m_OSDListen) || select.CanWrite(m_OSDListen))
+ fprintf (stderr, "m_OSDListen can act.\n");
+ if (select.CanRead(*m_OSDClient) || select.CanWrite(*m_OSDClient))
+ fprintf (stderr, "m_OSDClient can act.\n");
+ */
+ //fprintf(stderr, "State: %d\n",state);
+
+ int res;
+ switch (state) {
+
+ case NO_CLIENT: /* Accept connecting OSD clients */
+ if (select.CanRead(m_OSDListen) ) {
+ if (m_OSDClient->Accept(m_OSDListen)) {
+ isyslog("[ffnetdev] VNC: Accepted client %s:%d",
+ m_OSDClient->RemoteIp().c_str(), m_OSDClient->RemotePort());
+ state = CLIENT_CONNECTED;
+ /* Sending RFB protocol version */
+ rfbProtocolVersionMsg msg;
+ sprintf(msg, rfbProtocolVersionFormat, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+ if (!m_OSDClient->SafeWrite(&msg, sz_rfbProtocolVersionMsg))
+ {
+ state = NO_CLIENT;
+ }
+ else
+ {
+// m_pPlugin->SetPrimaryDevice();
+ }
+ break;
+
+ }
+ else {
+ esyslog("[ffnetdev] VNC: Couldn't accept : %s", strerror(errno));
+ state = NO_CLIENT;
+ m_Active = false;
+ continue;
+ }
+ }
+ break;
+
+ case CLIENT_CONNECTED:
+ rfbProtocolVersionMsg pvmsg;
+ res = m_OSDClient->Read(&pvmsg, sz_rfbProtocolVersionMsg);
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client wants RFB protocol version %s\n", pvmsg);
+#endif
+ dsyslog("[ffnetdev] VNC: Client wants RFB protocol version %s\n", pvmsg);
+ if (1) {//FIXME:protocol ok
+ state = PROTOCOL_OK;
+ /* Sending security type */
+ CARD32 secmsg;
+ secmsg = Swap32IfLE(rfbNoAuth);
+ if (!m_OSDClient->SafeWrite(&secmsg, sizeof(secmsg)))
+ {
+ state = NO_CLIENT;
+ break;
+ }
+ }
+ else {
+ state = NO_CLIENT;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client wants unsupported protocol version.\n");
+#endif
+ dsyslog("[ffnetdev] VNC: Client wants unsupported protocol version.\n");
+ if ( m_OSDClient->Close() ) {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client socket closed successfully.\n");
+#endif
+ isyslog("[ffnetdev] VNC: Connection closed.");
+ }
+ else {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Error closing client socket.\n");
+#endif
+ esyslog("[ffnetdev] VNC: Error closing connection.");
+ m_Active=false;
+ continue;
+ }
+ }
+ break;
+ case PROTOCOL_OK: /* Since we do not need authentication challenge(rfbNoAuth), proceed to next state */
+ state = AUTHENTICATED;
+ break;
+ case AUTHENTICATED:
+ rfbClientInitMsg cimsg;
+ res = m_OSDClient->Read(&cimsg, sz_rfbClientInitMsg);
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client wants %s desktop(ignored).\n",
+ cimsg.shared ? "shared":"non-shared");
+#endif
+ dsyslog("[ffnetdev] VNC: Client wants %s desktop(ignored).\n",
+ cimsg.shared ? "shared":"non-shared");
+
+ rfbPixelFormat pxfmt;
+ pxfmt.bitsPerPixel = 8;
+ pxfmt.depth = 8;
+ pxfmt.bigEndian = 0;
+ pxfmt.trueColour = 0;
+ pxfmt.redMax = 0;
+ pxfmt.greenMax = 0;
+ pxfmt.blueMax = 0;
+ pxfmt.redShift = 0;
+ pxfmt.greenShift = 0;
+ pxfmt.blueShift = 0;
+ pxfmt.pad1 = 0;
+ pxfmt.pad2 = 0;
+
+ rfbServerInitMsg simsg;
+ simsg.framebufferWidth = Swap16IfLE(720);
+ simsg.framebufferHeight = Swap16IfLE(576);
+ simsg.format = pxfmt;
+ simsg.nameLength = Swap32IfLE(7);
+ if (!m_OSDClient->SafeWrite(&simsg, sizeof(simsg)))
+ {
+ state = NO_CLIENT;
+ break;
+ }
+
+ CARD8 name[7];
+ strcpy((char*)&name, "VDR-OSD");
+ if (!m_OSDClient->SafeWrite(&name, 7))
+ {
+ state = NO_CLIENT;
+ break;
+ }
+
+ state = HANDSHAKE_OK;
+ FirstUpdateRequest = true;
+ break;
+
+ case HANDSHAKE_OK:
+
+ /* Check for closed OSD connection */
+ if (close_OSDclient_request==true) {
+ close_OSDclient_request = false;
+ state = NO_CLIENT;
+ UseAlpha = false;
+
+ delete m_pEncoder;
+ m_pEncoder = NULL;
+
+ m_pPlugin->RestorePrimaryDevice();
+
+ if ( m_OSDClient->Close() ) {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Client socket closed successfully.\n");
+#endif
+ isyslog("[ffnetdev] VNC: Connection closed.");
+ }
+ else {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] VNC: Error closing client socket.\n");
+#endif
+ esyslog("[ffnetdev] VNC: Error closing connection.");
+ m_Active=false;
+ continue;
+ }
+ }
+ HandleClientRequests(&select);
+
+ break;
+ default: fprintf(stderr, "[ffnetdev] VNC: Undefined state! This is a bug! Please report.\n");
+ esyslog("[ffnetdev] VNC: Fatal error, FFNetDev exiting: undefined state");
+ m_Active = false;
+ continue;
+ }
+
+
+
+ struct timeval curtime;
+ gettimeofday(&curtime, 0);
+ curtime.tv_sec = curtime.tv_sec - (((int)curtime.tv_sec / 1000000) * 1000000);
+ if ((curtime.tv_sec * 1000 + (curtime.tv_usec / 1000) > m_lastupdate.tv_sec * 1000 + (m_lastupdate.tv_usec / 1000) + 500) &&
+ (m_notupdatedLeft != -1) && (m_notupdatedTop != -1) && (m_notupdatedRight != -1) &&
+ (m_notupdatedBottom != -1))
+ {
+ memset(&m_lasttime, 0, sizeof(m_lasttime));
+ SendScreen(720, m_notupdatedLeft, m_notupdatedTop,
+ m_notupdatedRight - m_notupdatedLeft,
+ m_notupdatedBottom - m_notupdatedTop,
+ &(m_Instance->m_OSDBuffer[m_notupdatedTop * 720 + m_notupdatedLeft]));
+ }
+ } // while(m_Active)
+
+}
diff --git a/osdworker.h b/osdworker.h
new file mode 100644
index 0000000..296c8b3
--- /dev/null
+++ b/osdworker.h
@@ -0,0 +1,122 @@
+/*
+ * osdworker.h: OSD worker thread
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _OSDWORKER__H
+#define _OSDWORKER__H
+
+#include <vdr/thread.h>
+
+#include "tools/socket.h"
+#include "tools/select.h"
+#include "streamdevice.h"
+#include "remote.h"
+#include "netosd.h"
+#include "ffnetdev.h"
+
+#include "vncEncoder.h"
+
+#define MAXOSDMEMORY 2048000
+
+#define rfbEncSpecialUseAlpha 0xFFFFF000 // server shall send alpha values in addition to RGB values
+
+/*
+ * Macros for endian swapping.
+ */
+/*
+#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+
+#define Swap32(l) (((l) >> 24) | \
+ (((l) & 0x00ff0000) >> 8) | \
+ (((l) & 0x0000ff00) << 8) | \
+ ((l) << 24))
+
+static const int rfbEndianTest = 1; // true means swap TODO: check machine endianess - for now -> hardcoded
+
+#define Swap16IfLE(s) (*(const char *)&rfbEndianTest ? Swap16(s) : (s))
+
+#define Swap32IfLE(l) (*(const char *)&rfbEndianTest ? Swap32(l) : (l))
+*/
+
+// --- cOSDWorker -------------------------------------------------------------
+enum states {
+ NO_CLIENT,
+ CLIENT_CONNECTED,
+ PROTOCOL_OK,
+ AUTHENTICATED,
+ HANDSHAKE_OK
+};
+
+class cOSDWorker : public cThread {
+private:
+ bool m_Active;
+
+ static cOSDWorker *m_Instance;
+
+ cTBSocket *m_OSDClient;
+
+ bool close_OSDclient_request;
+
+ int OSDPort;
+ int state;
+ bool UseAlpha;
+ bool FirstUpdateRequest;
+ int numOSDColors;
+ rfbPixelFormat ClientFormat;
+ rfbPixelFormat ServerFormat;
+
+ BYTE m_OSDBuffer[720*576];
+// BYTE m_oldOSDData[720*576];
+ BYTE *m_pSendBuffer;
+ int m_SendBufferSize;
+ tColor OSDColors[256];
+
+ void HandleClientRequests(cTBSelect *select);
+ bool RFBRead(char *buffer, int len);
+ void CreateSendBuffer(int OSDBufferSize);
+
+ vncEncoder *m_pEncoder;
+
+ struct timeval m_lasttime;
+ struct timeval m_lastupdate;
+ int m_notupdatedLeft;
+ int m_notupdatedTop;
+ int m_notupdatedRight;
+ int m_notupdatedBottom;
+
+ cPluginFFNetDev *m_pPlugin;
+
+protected:
+ virtual void Action(void);
+ void Stop(void);
+public:
+ cOSDWorker(void);
+ virtual ~cOSDWorker();
+
+ static void Init(int, cPluginFFNetDev*);
+ static void Exit(void);
+ static bool Active(void);
+ static bool ClientIsReady(void);
+
+ static void CloseOSDClient(void);
+
+ static bool ClearScreen(void);
+ static bool SendScreen(unsigned int stride, unsigned int x1, unsigned int y1, unsigned int w, unsigned int h, const void *data);
+ static bool SendCMAP(int NumColors, const tColor *Colors);
+ static bool OSDWrite(unsigned char *data, unsigned int data_length);
+ static bool SendPlayMode(ePlayMode PlayMode);
+ static void GetOSDColors(tColor **OSDColors, int *numOSDColors) { *OSDColors = &m_Instance->OSDColors[0]; (*numOSDColors) = m_Instance->numOSDColors; };
+};
+
+inline bool cOSDWorker::Active(void) {
+ return m_Instance && (m_Instance->state!=NO_CLIENT);
+}
+
+inline bool cOSDWorker::ClientIsReady(void) {
+ return m_Instance && (m_Instance->state==HANDSHAKE_OK);
+}
+
+#endif
diff --git a/pes2ts.c b/pes2ts.c
new file mode 100644
index 0000000..506abd9
--- /dev/null
+++ b/pes2ts.c
@@ -0,0 +1,210 @@
+/*
+ * pes2ts.c: PES2TS remux
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+#include <vdr/tools.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "tsworker.h"
+
+#include "pes2ts.h"
+
+cPES2TSRemux::cPES2TSRemux(int VPid, int APid):
+ cThread("[ffnetdev] PES2TS remux"),
+ m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, TS_SIZE * 2)),
+ m_InputBuffer(new cRingBufferLinear(INPUTBUFSIZE, IPACKS)),
+ m_Active(false),
+ m_PlayModeChanged(false)
+{
+ vpid = VPid;
+ apid = APid;
+ m_InputBuffer->SetTimeouts(0, 1000); // IMPORTANT to avoid busy wait in threads main loop and thus a high CPU load
+ Start();
+ OutputLocked = false;
+}
+
+cPES2TSRemux::~cPES2TSRemux()
+{
+ m_Active = false;
+ delete m_InputBuffer;
+ delete m_OutputBuffer;
+
+}
+
+void cPES2TSRemux::Action(void)
+{
+ unsigned int i;
+ uchar acc=0; // continutiy counter for audio packets
+ uchar vcc=0; // continutiy counter for video packets
+ uchar *cc; // either cc=&vcc; or cc=&acc;
+ unsigned short pid=0;
+ unsigned int packetlen;
+ uchar ts[188];
+ uchar pes[IPACKS];
+
+ unsigned int minNeededPacketlen = 10; // needed for read packet len: 6 Should be enought ... but makes no sense
+
+
+ m_Active = true;
+ while (m_Active) {
+ int count=0;
+// fprintf(stderr, "[ffnetdev] Remuxer: Inputbuffersize: %d, Outputbuffersize: %d\n",
+// m_InputBuffer->Available(), m_OutputBuffer->Available());
+
+ if (m_PlayModeChanged)
+ {
+ cCondWait::SleepMs(1500);
+ m_PlayModeChanged = false;
+ }
+
+ if (m_InputBuffer->Available() < (int)IPACKS*10) {
+ cCondWait::SleepMs(5);
+ continue;
+ }
+
+ if (!cTSWorker::HaveStreamClient()) {
+ ClearOutput();
+ cCondWait::SleepMs(10);
+ continue;
+ }
+
+
+ InputMutex.Lock();
+ uchar *data = m_InputBuffer->Get(count);
+ if (data==NULL) {
+ InputMutex.Unlock();
+ cCondWait::SleepMs(3);
+ continue;
+ }
+
+// fprintf(stderr, "[ffnetdev] count: %07d Free: %07d AvailO %07d AvailI: %07d\n", count, m_InputBuffer->Free(),
+// m_OutputBuffer->Available(), m_InputBuffer->Available());
+
+ if ( count < (int)minNeededPacketlen ) {
+ fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count);
+ InputMutex.Unlock();
+ cCondWait::SleepMs(2);
+ continue;
+ }
+
+ //DEBUG
+ //fprintf(stderr, "Data ready to read: %d\n", count);
+ //for (i=0; i<20; i++)
+ // fprintf(stderr, "%02X ", data[i]);
+ // END DEBUG
+
+
+ // check for valid PES signature in PES header
+ if ( (data[0]==0x00) && (data[1]==0x00) && (data[2]==0x01) ) {
+
+ packetlen = ((data[4]<<8) | data[5]) + 6 ;
+
+ if ( packetlen>IPACKS) {
+ fprintf(stderr, "[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n"
+ "This should not happen! Please report!\n", packetlen, IPACKS);
+ }
+
+ if ( count < (int)packetlen) {
+ fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen);
+ InputMutex.Unlock();
+ cCondWait::SleepMs(1);
+ continue;
+ }
+
+
+ // check for valid stream id type: is it video or audio or unknown?
+ if ( (data[3]>=0xC0) && (data[3]<=0xDF) ) {
+ pid=apid;
+ cc=&acc;
+ }
+ else {
+ if ( (data[3]>=0xE0) && (data[3]<=0xEF) ) {
+ pid=vpid;
+ cc=&vcc;
+ }
+ else {
+ fprintf(stderr, "[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n");
+ // throw away whole PES packet
+ m_InputBuffer->Del(packetlen);
+ InputMutex.Unlock();
+ continue;
+ }
+ }
+
+ memcpy( pes, data, packetlen);
+ // we are now finished with the PES packet, delete it from ring buffer
+ m_InputBuffer->Del(packetlen);
+
+ InputMutex.Unlock();
+ }
+ else {
+ // no valid PES signature was found, so delete this stuff from ring buffer
+ // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more
+ // with each call in streamdevice.c (PlayVideo, PlayAudio)
+ fprintf(stderr, "[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n");
+
+ m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!?
+ InputMutex.Unlock();
+ continue;
+ }
+
+ int tspacketlen = ((int)packetlen/184) * 188 + ((packetlen % 184 > 0) ? 188 : 0);
+ while (m_OutputBuffer->Free() < tspacketlen) {
+ if (!m_Active)
+ continue;
+ cCondWait::SleepMs(10);
+ //fprintf(stderr, "[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen);
+ }
+
+ LockOutput();
+ bool first = true;
+ //--------------------------------------divide PES packet into small TS packets-----------------------
+ for (i=0; i< packetlen/184; i++) {
+ ts[0] = 0x47; //SYNC Byte
+ if (first) ts[1] = 0x40; // Set PUSI or
+ else ts[1] = 0x00; // clear PUSI, TODO: PID (high) is missing
+ ts[2] = pid & 0xFF; // PID (low)
+ ts[3] = 0x10 | ((*cc)&0x0F); // No adaptation field, payload only, continuity counter
+ memcpy(ts + 4, pes + i * 184, 184);
+ ++(*cc);
+ m_OutputBuffer->Put(ts, 188);
+ first = false;
+ }
+ uchar rest = packetlen % 184;
+ if (rest>0) {
+ ts[0] = 0x47; //SYNC Byte
+ if (first) ts[1] = 0x40; // Set PUSI or
+ else ts[1] = 0x00; // clear PUSI, TODO: PID (high) is missing
+ ts[2] = pid & 0xFF; // PID (low)
+ ts[3] = 0x30 | ((*cc)&0x0F); // adaptation field, payload, continuity counter
+ ++(*cc);
+ ts[4] = 183-rest;
+ if (ts[4]>0) {
+ ts[5] = 0x00;
+ memset(ts + 6, 0xFF, ts[4] - 1);
+ }
+ memcpy(ts + 5 + ts[4], pes + i * 184, rest);
+ m_OutputBuffer->Put(ts, 188);
+ first = false;
+ }
+
+ UnlockOutput();
+
+ }
+ m_Active = false;
+}
+
+
+int cPES2TSRemux::Put(const uchar *Data, int Count)
+{
+ InputMutex.Lock();
+ int result = m_InputBuffer->Put(Data, Count);
+ InputMutex.Unlock();
+ return ( result);
+}
+
+
diff --git a/pes2ts.h b/pes2ts.h
new file mode 100644
index 0000000..43fa3a1
--- /dev/null
+++ b/pes2ts.h
@@ -0,0 +1,50 @@
+/*
+ * pes2ts.h: PES2TS remux
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef PES2TSREMUX_H
+#define PES2TSREMUX_H
+
+#include <vdr/ringbuffer.h>
+#include <vdr/tools.h>
+
+#define INPUTBUFSIZE KILOBYTE(2048)
+#define OUTPUTBUFSIZE KILOBYTE(2048)
+#define TS_SIZE 188
+#define IPACKS 2048
+
+class cPES2TSRemux: public cThread {
+private:
+ cRingBufferLinear *m_OutputBuffer;
+ cRingBufferLinear *m_InputBuffer;
+ bool m_Active;
+ unsigned short vpid;
+ unsigned short apid;
+ bool OutputLocked;
+ cMutex InputMutex;
+ bool m_PlayModeChanged;
+
+protected:
+ virtual void Action(void);
+
+public:
+ cPES2TSRemux(int VPid, int APid);
+ virtual ~cPES2TSRemux();
+
+ int Free(void) { return m_InputBuffer->Free(); }
+ int Available(void) { return m_OutputBuffer->Available(); }
+ int Put(const uchar *Data, int Count);
+ uchar *Get(int &Count) { return m_OutputBuffer->Get(Count); }
+ void DelOutput(int Count) { m_OutputBuffer->Del(Count); }
+ void DelInput (int Count) { InputMutex.Lock(); m_InputBuffer ->Del(Count); InputMutex.Unlock(); }
+ void ClearOutput() { LockOutput(); m_OutputBuffer->Clear(); UnlockOutput(); }
+ void ClearInput () { InputMutex.Lock(); m_InputBuffer ->Clear(); InputMutex.Unlock(); }
+ void LockOutput() { while (OutputLocked) cCondWait::SleepMs(1); OutputLocked = true; }
+ void UnlockOutput() { OutputLocked = false; }
+ void PlayModeChange() { m_PlayModeChanged = true; }
+};
+
+#endif // PES2TSREMUX_H
diff --git a/remote.c b/remote.c
new file mode 100644
index 0000000..e1f392d
--- /dev/null
+++ b/remote.c
@@ -0,0 +1,58 @@
+/*
+ * remote.c: remote control
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+
+#include <vdr/interface.h>
+
+#include "osdworker.h"
+#include "remote.h"
+
+cMyRemote::cMyRemote(const char *Name)
+:cRemote(Name)
+{
+
+}
+
+bool cMyRemote::Ready(void)
+{
+ return true;
+}
+
+bool cMyRemote::Initialize(void)
+{
+ return true;
+}
+
+bool cMyRemote::Put(uint64 Code, bool Repeat, bool Release)
+{
+ return cRemote::Put(Code, Repeat ,Release);
+}
+
+
+
+cLearningThread::cLearningThread(void)
+{
+ Start();
+}
+
+cLearningThread::~cLearningThread(void)
+{
+}
+
+void cLearningThread::Action(void)
+{
+ while (!cOSDWorker::ClientIsReady())
+ usleep(100000);
+
+ while (cOsd::IsOpen() > 0)
+ usleep(100000);
+
+ sleep(5);
+ dsyslog("[ffnetdev] start learning keys");
+ Interface->LearnKeys();
+ delete this;
+}
diff --git a/remote.h b/remote.h
new file mode 100644
index 0000000..583693d
--- /dev/null
+++ b/remote.h
@@ -0,0 +1,36 @@
+/*
+ * remote.h: remote control
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef REMOTE_H
+#define REMOTE_H
+
+#include <vdr/remote.h>
+#include <vdr/thread.h>
+
+class cMyRemote : public cRemote {
+private:
+public:
+ cMyRemote(const char *Name);
+ virtual bool Initialize(void);
+ virtual bool Ready(void);
+ virtual bool Put(uint64 Code, bool Repeat = false, bool Release = false);
+};
+
+
+class cLearningThread : public cThread {
+private:
+
+public:
+ cLearningThread(void);
+ virtual ~cLearningThread(void);
+
+protected:
+ virtual void Action(void);
+};
+
+#endif
+
diff --git a/rfb.h b/rfb.h
new file mode 100644
index 0000000..b5194ed
--- /dev/null
+++ b/rfb.h
@@ -0,0 +1,85 @@
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// rfb.h
+// This includes the rfb spec header, the port numbers,
+// the CARD type definitions and various useful macros.
+//
+
+#ifndef RFB_H__
+#define RFB_H__
+
+// Define the CARD* types
+typedef unsigned long CARD32;
+typedef unsigned short CARD16;
+typedef unsigned char CARD8;
+
+// Define the port number offsets
+#define FLASH_PORT_OFFSET 5400
+#define INCOMING_PORT_OFFSET 5500
+#define HTTP_PORT_OFFSET 5800
+#define RFB_PORT_OFFSET 5900
+
+#define PORT_TO_DISPLAY(p) ( (p) - RFB_PORT_OFFSET )
+#define HPORT_TO_DISPLAY(p) ( (p) - HTTP_PORT_OFFSET )
+#define DISPLAY_TO_PORT(d) ( (d) + RFB_PORT_OFFSET )
+#define DISPLAY_TO_HPORT(d) ( (d) + HTTP_PORT_OFFSET )
+
+// include the protocol spec
+#include "rfbproto.h"
+
+// define some quick endian conversions
+// change this if necessary
+#define LITTLE_ENDIAN_HOST
+
+#ifdef LITTLE_ENDIAN_HOST
+
+#define Swap16IfLE(s) \
+ ((CARD16) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)))
+#define Swap32IfLE(l) \
+ ((CARD32) ((((l) & 0xff000000) >> 24) | \
+ (((l) & 0x00ff0000) >> 8) | \
+ (((l) & 0x0000ff00) << 8) | \
+ (((l) & 0x000000ff) << 24)))
+
+#else
+
+#define Swap16IfLE(s) (s)
+#define Swap32IfLE(l) (l)
+
+#endif
+
+// unconditional swaps
+#define Swap16(s) \
+ ((CARD16) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)))
+#define Swap32(l) \
+ ((CARD32) ((((l) & 0xff000000) >> 24) | \
+ (((l) & 0x00ff0000) >> 8) | \
+ (((l) & 0x0000ff00) << 8) | \
+ (((l) & 0x000000ff) << 24)))
+
+
+#endif
diff --git a/rfbproto.h b/rfbproto.h
new file mode 100644
index 0000000..85118ad
--- /dev/null
+++ b/rfbproto.h
@@ -0,0 +1,786 @@
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * 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.
+ */
+
+/*
+ * rfbproto.h - header file for the RFB protocol version 3.3
+ *
+ * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed
+ * integer (for n = 8, 16 and 32).
+ *
+ * All multiple byte integers are in big endian (network) order (most
+ * significant byte first). Unless noted otherwise there is no special
+ * alignment of protocol structures.
+ *
+ *
+ * Once the initial handshaking is done, all messages start with a type byte,
+ * (usually) followed by message-specific data. The order of definitions in
+ * this file is as follows:
+ *
+ * (1) Structures used in several types of message.
+ * (2) Structures used in the initial handshaking.
+ * (3) Message types.
+ * (4) Encoding types.
+ * (5) For each message type, the form of the data following the type byte.
+ * Sometimes this is defined by a single structure but the more complex
+ * messages have to be explained by comments.
+ */
+
+
+/*****************************************************************************
+ *
+ * Structures used in several messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify a rectangle. This structure is a multiple of 4
+ * bytes so that it can be interspersed with 32-bit pixel data without
+ * affecting alignment.
+ */
+
+typedef struct {
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbRectangle;
+
+#define sz_rfbRectangle 8
+
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify pixel format.
+ */
+
+typedef struct {
+
+ CARD8 bitsPerPixel; /* 8,16,32 only */
+
+ CARD8 depth; /* 8 to 32 */
+
+ CARD8 bigEndian; /* True if multi-byte pixels are interpreted
+ as big endian, or if single-bit-per-pixel
+ has most significant bit of the byte
+ corresponding to first (leftmost) pixel. Of
+ course this is meaningless for 8 bits/pix */
+
+ CARD8 trueColour; /* If false then we need a "colour map" to
+ convert pixels to RGB. If true, xxxMax and
+ xxxShift specify bits used for red, green
+ and blue */
+
+ /* the following fields are only meaningful if trueColour is true */
+
+ CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the
+ number of bits used for red). Note this
+ value is always in big endian order. */
+
+ CARD16 greenMax; /* similar for green */
+
+ CARD16 blueMax; /* and blue */
+
+ CARD8 redShift; /* number of shifts needed to get the red
+ value in a pixel to the least significant
+ bit. To find the red value from a given
+ pixel, do the following:
+ 1) Swap pixel value according to bigEndian
+ (e.g. if bigEndian is false and host byte
+ order is big endian, then swap).
+ 2) Shift right by redShift.
+ 3) AND with redMax (in host byte order).
+ 4) You now have the red value between 0 and
+ redMax. */
+
+ CARD8 greenShift; /* similar for green */
+
+ CARD8 blueShift; /* and blue */
+
+ CARD8 pad1;
+ CARD16 pad2;
+
+} rfbPixelFormat;
+
+#define sz_rfbPixelFormat 16
+
+
+
+/*****************************************************************************
+ *
+ * Initial handshaking messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Protocol Version
+ *
+ * The server always sends 12 bytes to start which identifies the latest RFB
+ * protocol version number which it supports. These bytes are interpreted
+ * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where
+ * xxx and yyy are the major and minor version numbers (for version 3.3
+ * this is "RFB 003.003\n").
+ *
+ * The client then replies with a similar 12-byte message giving the version
+ * number of the protocol which should actually be used (which may be different
+ * to that quoted by the server).
+ *
+ * It is intended that both clients and servers may provide some level of
+ * backwards compatibility by this mechanism. Servers in particular should
+ * attempt to provide backwards compatibility, and even forwards compatibility
+ * to some extent. For example if a client demands version 3.1 of the
+ * protocol, a 3.0 server can probably assume that by ignoring requests for
+ * encoding types it doesn't understand, everything will still work OK. This
+ * will probably not be the case for changes in the major version number.
+ *
+ * The format string below can be used in sprintf or sscanf to generate or
+ * decode the version string respectively.
+ */
+
+#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
+#define rfbProtocolMajorVersion 3
+#define rfbProtocolMinorVersion 3
+
+typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
+
+#define sz_rfbProtocolVersionMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * Authentication
+ *
+ * Once the protocol version has been decided, the server then sends a 32-bit
+ * word indicating whether any authentication is needed on the connection.
+ * The value of this word determines the authentication scheme in use. For
+ * version 3.0 of the protocol this may have one of the following values:
+ */
+
+#define rfbConnFailed 0
+#define rfbNoAuth 1
+#define rfbVncAuth 2
+
+/*
+ * rfbConnFailed: For some reason the connection failed (e.g. the server
+ * cannot support the desired protocol version). This is
+ * followed by a string describing the reason (where a
+ * string is specified as a 32-bit length followed by that
+ * many ASCII characters).
+ *
+ * rfbNoAuth: No authentication is needed.
+ *
+ * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte
+ * challenge follows, which the client encrypts as
+ * appropriate using the password and sends the resulting
+ * 16-byte response. If the response is correct, the
+ * server sends the 32-bit word rfbVncAuthOK. If a simple
+ * failure happens, the server sends rfbVncAuthFailed and
+ * closes the connection. If the server decides that too
+ * many failures have occurred, it sends rfbVncAuthTooMany
+ * and closes the connection. In the latter case, the
+ * server should not allow an immediate reconnection by
+ * the client.
+ */
+
+#define rfbVncAuthOK 0
+#define rfbVncAuthFailed 1
+#define rfbVncAuthTooMany 2
+
+
+/*-----------------------------------------------------------------------------
+ * Client Initialisation Message
+ *
+ * Once the client and server are sure that they're happy to talk to one
+ * another, the client sends an initialisation message. At present this
+ * message only consists of a boolean indicating whether the server should try
+ * to share the desktop by leaving other clients connected, or give exclusive
+ * access to this client by disconnecting all other clients.
+ */
+
+typedef struct {
+ CARD8 shared;
+} rfbClientInitMsg;
+
+#define sz_rfbClientInitMsg 1
+
+
+/*-----------------------------------------------------------------------------
+ * Server Initialisation Message
+ *
+ * After the client initialisation message, the server sends one of its own.
+ * This tells the client the width and height of the server's framebuffer,
+ * its pixel format and the name associated with the desktop.
+ */
+
+typedef struct {
+ CARD16 framebufferWidth;
+ CARD16 framebufferHeight;
+ rfbPixelFormat format; /* the server's preferred pixel format */
+ CARD32 nameLength;
+ /* followed by char name[nameLength] */
+} rfbServerInitMsg;
+
+#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+
+
+/*
+ * Following the server initialisation message it's up to the client to send
+ * whichever protocol messages it wants. Typically it will send a
+ * SetPixelFormat message and a SetEncodings message, followed by a
+ * FramebufferUpdateRequest. From then on the server will send
+ * FramebufferUpdate messages in response to the client's
+ * FramebufferUpdateRequest messages. The client should send
+ * FramebufferUpdateRequest messages with incremental set to true when it has
+ * finished processing one FramebufferUpdate and is ready to process another.
+ * With a fast client, the rate at which FramebufferUpdateRequests are sent
+ * should be regulated to avoid hogging the network.
+ */
+
+
+
+/*****************************************************************************
+ *
+ * Message types
+ *
+ *****************************************************************************/
+
+/* server -> client */
+
+#define rfbFramebufferUpdate 0
+#define rfbSetColourMapEntries 1
+#define rfbBell 2
+#define rfbServerCutText 3
+
+
+/* client -> server */
+
+#define rfbSetPixelFormat 0
+#define rfbFixColourMapEntries 1 /* not currently supported */
+#define rfbSetEncodings 2
+#define rfbFramebufferUpdateRequest 3
+#define rfbKeyEvent 4
+#define rfbPointerEvent 5
+#define rfbClientCutText 6
+
+
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define rfbEncodingRaw 0
+#define rfbEncodingCopyRect 1
+#define rfbEncodingRRE 2
+#define rfbEncodingCoRRE 4
+#define rfbEncodingHextile 5
+#define rfbEncodingZlib 6
+#define rfbEncodingTight 7
+#define rfbEncodingZlibHex 8
+#define rfbEncodingZRLE 16
+
+/*
+ * Special encoding numbers:
+ * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels;
+ * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data;
+ * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions;
+ * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet;
+ * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor;
+ * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels.
+ */
+
+#define rfbEncodingCompressLevel0 0xFFFFFF00
+#define rfbEncodingCompressLevel1 0xFFFFFF01
+#define rfbEncodingCompressLevel2 0xFFFFFF02
+#define rfbEncodingCompressLevel3 0xFFFFFF03
+#define rfbEncodingCompressLevel4 0xFFFFFF04
+#define rfbEncodingCompressLevel5 0xFFFFFF05
+#define rfbEncodingCompressLevel6 0xFFFFFF06
+#define rfbEncodingCompressLevel7 0xFFFFFF07
+#define rfbEncodingCompressLevel8 0xFFFFFF08
+#define rfbEncodingCompressLevel9 0xFFFFFF09
+
+#define rfbEncodingXCursor 0xFFFFFF10
+#define rfbEncodingRichCursor 0xFFFFFF11
+
+#define rfbEncodingLastRect 0xFFFFFF20
+
+#define rfbEncodingQualityLevel0 0xFFFFFFE0
+#define rfbEncodingQualityLevel1 0xFFFFFFE1
+#define rfbEncodingQualityLevel2 0xFFFFFFE2
+#define rfbEncodingQualityLevel3 0xFFFFFFE3
+#define rfbEncodingQualityLevel4 0xFFFFFFE4
+#define rfbEncodingQualityLevel5 0xFFFFFFE5
+#define rfbEncodingQualityLevel6 0xFFFFFFE6
+#define rfbEncodingQualityLevel7 0xFFFFFFE7
+#define rfbEncodingQualityLevel8 0xFFFFFFE8
+#define rfbEncodingQualityLevel9 0xFFFFFFE9
+
+
+/*****************************************************************************
+ *
+ * Server -> client message definitions
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdate - a block of rectangles to be copied to the framebuffer.
+ *
+ * This message consists of a header giving the number of rectangles of pixel
+ * data followed by the rectangles themselves. The header is padded so that
+ * together with the type byte it is an exact multiple of 4 bytes (to help
+ * with alignment of 32-bit pixels):
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdate */
+ CARD8 pad;
+ CARD16 nRects;
+ /* followed by nRects rectangles */
+} rfbFramebufferUpdateMsg;
+
+#define sz_rfbFramebufferUpdateMsg 4
+
+/*
+ * Each rectangle of pixel data consists of a header describing the position
+ * and size of the rectangle and a type word describing the encoding of the
+ * pixel data, followed finally by the pixel data. Note that if the client has
+ * not sent a SetEncodings message then it will only receive raw pixel data.
+ * Also note again that this structure is a multiple of 4 bytes.
+ */
+
+typedef struct {
+ rfbRectangle r;
+ CARD32 encoding; /* one of the encoding types rfbEncoding... */
+} rfbFramebufferUpdateRectHeader;
+
+#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Raw Encoding. Pixels are sent in top-to-bottom scanline order,
+ * left-to-right within a scanline with no padding in between.
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CopyRect Encoding. The pixels are specified simply by the x and y position
+ * of the source rectangle.
+ */
+
+typedef struct {
+ CARD16 srcX;
+ CARD16 srcY;
+} rfbCopyRect;
+
+#define sz_rfbCopyRect 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure
+ * giving the number of subrectangles following. Finally the data follows in
+ * the form [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbRectangle>].
+ */
+
+typedef struct {
+ CARD32 nSubrects;
+} rfbRREHeader;
+
+#define sz_rfbRREHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving
+ * the number of subrectangles following. Finally the data follows in the form
+ * [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbCoRRERectangle>]. This means that
+ * the whole rectangle must be at most 255x255 pixels.
+ */
+
+typedef struct {
+ CARD8 x;
+ CARD8 y;
+ CARD8 w;
+ CARD8 h;
+} rfbCoRRERectangle;
+
+#define sz_rfbCoRRERectangle 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels,
+ * starting at the top left going in left-to-right, top-to-bottom order. If
+ * the width of the rectangle is not an exact multiple of 16 then the width of
+ * the last tile in each row will be correspondingly smaller. Similarly if the
+ * height is not an exact multiple of 16 then the height of each tile in the
+ * final row will also be smaller. Each tile begins with a "subencoding" type
+ * byte, which is a mask made up of a number of bits. If the Raw bit is set
+ * then the other bits are irrelevant; w*h pixel values follow (where w and h
+ * are the width and height of the tile). Otherwise the tile is encoded in a
+ * similar way to RRE, except that the position and size of each subrectangle
+ * can be specified in just two bytes. The other bits in the mask are as
+ * follows:
+ *
+ * BackgroundSpecified - if set, a pixel value follows which specifies
+ * the background colour for this tile. The first non-raw tile in a
+ * rectangle must have this bit set. If this bit isn't set then the
+ * background is the same as the last tile.
+ *
+ * ForegroundSpecified - if set, a pixel value follows which specifies
+ * the foreground colour to be used for all subrectangles in this tile.
+ * If this bit is set then the SubrectsColoured bit must be zero.
+ *
+ * AnySubrects - if set, a single byte follows giving the number of
+ * subrectangles following. If not set, there are no subrectangles (i.e.
+ * the whole tile is just solid background colour).
+ *
+ * SubrectsColoured - if set then each subrectangle is preceded by a pixel
+ * value giving the colour of that subrectangle. If not set, all
+ * subrectangles are the same colour, the foreground colour; if the
+ * ForegroundSpecified bit wasn't set then the foreground is the same as
+ * the last tile.
+ *
+ * The position and size of each subrectangle is specified in two bytes. The
+ * Pack macros below can be used to generate the two bytes from x, y, w, h,
+ * and the Extract macros can be used to extract the x, y, w, h values from
+ * the two bytes.
+ */
+
+#define rfbHextileRaw (1 << 0)
+#define rfbHextileBackgroundSpecified (1 << 1)
+#define rfbHextileForegroundSpecified (1 << 2)
+#define rfbHextileAnySubrects (1 << 3)
+#define rfbHextileSubrectsColoured (1 << 4)
+
+#define rfbHextilePackXY(x,y) (((x) << 4) | (y))
+#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1))
+#define rfbHextileExtractX(byte) ((byte) >> 4)
+#define rfbHextileExtractY(byte) ((byte) & 0xf)
+#define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
+#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure
+ * giving the number of bytes following. Finally the data follows is
+ * zlib compressed version of the raw pixel data as negotiated.
+ */
+
+typedef struct {
+ CARD32 nBytes;
+} rfbZlibHeader;
+
+#define sz_rfbZlibHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Tight Encoding. FIXME: Add more documentation.
+ */
+
+#define rfbTightExplicitFilter 0x04
+#define rfbTightFill 0x08
+#define rfbTightJpeg 0x09
+#define rfbTightMaxSubencoding 0x09
+
+/* Filters to improve compression efficiency */
+#define rfbTightFilterCopy 0x00
+#define rfbTightFilterPalette 0x01
+#define rfbTightFilterGradient 0x02
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * XCursor encoding. This is a special encoding used to transmit X-style
+ * cursor shapes from server to clients. Note that for this encoding,
+ * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot
+ * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB
+ * samples are sent after header in the rfbXCursorColors structure. They
+ * denote foreground and background colors of the cursor. If a client
+ * supports only black-and-white cursors, it should ignore these colors and
+ * assume that foreground is black and background is white. Next, two bitmaps
+ * (1 bits per pixel) follow: first one with actual data (value 0 denotes
+ * background color, value 1 denotes foreground color), second one with
+ * transparency data (bits with zero value mean that these pixels are
+ * transparent). Both bitmaps represent cursor data in a byte stream, from
+ * left to right, from top to bottom, and each row is byte-aligned. Most
+ * significant bits correspond to leftmost pixels. The number of bytes in
+ * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor
+ * should be hidden (or default local cursor should be set by the client).
+ */
+
+typedef struct {
+ CARD8 foreRed;
+ CARD8 foreGreen;
+ CARD8 foreBlue;
+ CARD8 backRed;
+ CARD8 backGreen;
+ CARD8 backBlue;
+} rfbXCursorColors;
+
+#define sz_rfbXCursorColors 6
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RichCursor encoding. This is a special encoding used to transmit cursor
+ * shapes from server to clients. It is similar to the XCursor encoding but
+ * uses client pixel format instead of two RGB colors to represent cursor
+ * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader
+ * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h).
+ * After header, two pixmaps follow: first one with cursor image in current
+ * client pixel format (like in raw encoding), second with transparency data
+ * (1 bit per pixel, exactly the same format as used for transparency bitmap
+ * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or
+ * default local cursor should be set by the client).
+ */
+
+
+/*-----------------------------------------------------------------------------
+ * SetColourMapEntries - these messages are only sent if the pixel
+ * format uses a "colour map" (i.e. trueColour false) and the client has not
+ * fixed the entire colour map using FixColourMapEntries. In addition they
+ * will only start being sent after the client has sent its first
+ * FramebufferUpdateRequest. So if the client always tells the server to use
+ * trueColour then it never needs to process this type of message.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbSetColourMapEntriesMsg;
+
+#define sz_rfbSetColourMapEntriesMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * Bell - ring a bell on the client if it has one.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbBell */
+} rfbBellMsg;
+
+#define sz_rfbBellMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ServerCutText - the server has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbServerCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbServerCutTextMsg;
+
+#define sz_rfbServerCutTextMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all server->client messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbFramebufferUpdateMsg fu;
+ rfbSetColourMapEntriesMsg scme;
+ rfbBellMsg b;
+ rfbServerCutTextMsg sct;
+} rfbServerToClientMsg;
+
+
+
+/*****************************************************************************
+ *
+ * Message definitions (client -> server)
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * SetPixelFormat - tell the RFB server the format in which the client wants
+ * pixels sent.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetPixelFormat */
+ CARD8 pad1;
+ CARD16 pad2;
+ rfbPixelFormat format;
+} rfbSetPixelFormatMsg;
+
+#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4)
+
+
+/*-----------------------------------------------------------------------------
+ * FixColourMapEntries - when the pixel format uses a "colour map", fix
+ * read-only colour map entries.
+ *
+ * ***************** NOT CURRENTLY SUPPORTED *****************
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFixColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbFixColourMapEntriesMsg;
+
+#define sz_rfbFixColourMapEntriesMsg 6
+
+
+/*-----------------------------------------------------------------------------
+ * SetEncodings - tell the RFB server which encoding types we accept. Put them
+ * in order of preference, if we have any. We may always receive raw
+ * encoding, even if we don't specify it here.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetEncodings */
+ CARD8 pad;
+ CARD16 nEncodings;
+ /* followed by nEncodings * CARD32 encoding types */
+} rfbSetEncodingsMsg;
+
+#define sz_rfbSetEncodingsMsg 4
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdateRequest - request for a framebuffer update. If incremental
+ * is true then the client just wants the changes since the last update. If
+ * false then it wants the whole of the specified rectangle.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdateRequest */
+ CARD8 incremental;
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbFramebufferUpdateRequestMsg;
+
+#define sz_rfbFramebufferUpdateRequestMsg 10
+
+
+/*-----------------------------------------------------------------------------
+ * KeyEvent - key press or release
+ *
+ * Keys are specified using the "keysym" values defined by the X Window System.
+ * For most ordinary keys, the keysym is the same as the corresponding ASCII
+ * value. Other common keys are:
+ *
+ * BackSpace 0xff08
+ * Tab 0xff09
+ * Return or Enter 0xff0d
+ * Escape 0xff1b
+ * Insert 0xff63
+ * Delete 0xffff
+ * Home 0xff50
+ * End 0xff57
+ * Page Up 0xff55
+ * Page Down 0xff56
+ * Left 0xff51
+ * Up 0xff52
+ * Right 0xff53
+ * Down 0xff54
+ * F1 0xffbe
+ * F2 0xffbf
+ * ... ...
+ * F12 0xffc9
+ * Shift 0xffe1
+ * Control 0xffe3
+ * Meta 0xffe7
+ * Alt 0xffe9
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbKeyEvent */
+ CARD8 down; /* true if down (press), false if up */
+ CARD16 pad;
+ CARD32 key; /* key is specified as an X keysym */
+} rfbKeyEventMsg;
+
+#define sz_rfbKeyEventMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * PointerEvent - mouse/pen move and/or button press.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbPointerEvent */
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+ CARD16 x;
+ CARD16 y;
+} rfbPointerEventMsg;
+
+#define rfbButton1Mask 1
+#define rfbButton2Mask 2
+#define rfbButton3Mask 4
+
+#define sz_rfbPointerEventMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * ClientCutText - the client has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbClientCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbClientCutTextMsg;
+
+#define sz_rfbClientCutTextMsg 8
+
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all client->server messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbSetPixelFormatMsg spf;
+ rfbFixColourMapEntriesMsg fcme;
+ rfbSetEncodingsMsg se;
+ rfbFramebufferUpdateRequestMsg fur;
+ rfbKeyEventMsg ke;
+ rfbPointerEventMsg pe;
+ rfbClientCutTextMsg cct;
+} rfbClientToServerMsg;
diff --git a/streamdevice.c b/streamdevice.c
new file mode 100644
index 0000000..9c9aef1
--- /dev/null
+++ b/streamdevice.c
@@ -0,0 +1,166 @@
+/*
+ * streamdevice.c: streaming network device
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "streamdevice.h"
+#include "osdworker.h"
+#include "tsworker.h"
+#include "netosd.h"
+
+cStreamDevice::cStreamDevice(void)
+{
+#ifdef DEBUG
+ fprintf(stderr,"[ffnetdev] Device: Constructor cStreamDevice \n");
+#endif
+ m_Remux = new cPES2TSRemux(TS_VPID, TS_APID);
+
+}
+
+cStreamDevice::~cStreamDevice(void)
+{
+#ifdef DEBUG
+ fprintf(stderr,"[ffnetdev] Device: Destructor cStreamDevice \n");
+#endif
+ DELETENULL(m_Remux);
+}
+
+
+void cStreamDevice::MakePrimaryDevice(bool On)
+{
+#ifdef DEBUG
+ fprintf(stderr,"[ffnetdev] Device: ffnetdev becomes primary device. Registering our OSD provider...\n");
+#endif
+ new cNetOSDProvider();
+}
+
+int cStreamDevice::ProvidesCa(const cChannel *Channel) const
+{
+ return 0;
+}
+
+bool cStreamDevice::HasDecoder(void) const
+{
+ return true; // We can decode MPEG2
+}
+
+bool cStreamDevice::CanReplay(void) const
+{
+ return true; // We can replay
+}
+
+bool cStreamDevice::SetPlayMode(ePlayMode PlayMode)
+{
+ fprintf(stderr, "[ffnetdev] Device: Setting playmode(not implemented). Mode: %d\n",PlayMode);
+ cOSDWorker::SendPlayMode(PlayMode);
+ m_Remux->ClearInput();
+ m_Remux->ClearOutput();
+ m_Remux->PlayModeChange();
+ return true;
+}
+
+void cStreamDevice::TrickSpeed(int Speed)
+{
+ fprintf(stderr,"[ffnetdev] Device: Trickspeed(not implemented). Speed: %d\n", Speed);
+ m_Remux->ClearInput();
+ m_Remux->ClearOutput();
+ m_Remux->PlayModeChange();
+}
+
+void cStreamDevice::Clear(void)
+{
+ fprintf(stderr,"[ffnetdev] Device: Clear(not implemented).\n");
+ m_Remux->ClearInput();
+ m_Remux->ClearOutput();
+ m_Remux->PlayModeChange();
+// cDevice::Clear();
+}
+void cStreamDevice::Play(void)
+{
+ fprintf(stderr,"[ffnetdev] Device: Play(not implemented).\n");
+// cDevice::Play();
+}
+
+void cStreamDevice::Freeze(void)
+{
+ fprintf(stderr,"[ffnetdev] Device: Freeze(not implemented).\n");
+// cDevice::Freeze();
+}
+
+void cStreamDevice::Mute(void)
+{
+ fprintf(stderr,"[ffnetdev] Device: Mute(not implemented).\n");
+// cDevice::Mute();
+}
+
+void cStreamDevice::SetVolumeDevice(int Volume)
+{
+ fprintf (stderr, "[ffnetdev] Device: Setting volume to %d (not implemented).\n", Volume);
+}
+
+void cStreamDevice::StillPicture(const uchar *Data, int Length)
+{
+ fprintf(stderr,"[ffnetdev] Device: StillPicture(not implemented).\n");
+}
+
+bool cStreamDevice::Poll(cPoller &Poller, int TimeoutMs)
+{
+ //fprintf(stderr,"[ffnetdev] Device: Poll TimeoutMs: %d ....\n",TimeoutMs);
+ return true;
+}
+/* ----------------------------------------------------------------------------
+ */
+int cStreamDevice::PlayAudio(const uchar *Data, int Length)
+{
+ if (cTSWorker::HaveStreamClient()) {
+ while ((m_Remux->Free() < Length) && cTSWorker::HaveStreamClient())
+ cCondWait::SleepMs(1);
+ int result=m_Remux->Put(Data, Length);
+ if (result!=Length) {
+ fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(audio). result:%d Length: %d Skipping Audio PES packet...\n", result, Length );
+ // Delete part of data already written to buffer
+ m_Remux->DelInput(result);
+ return (0);
+ }
+ else
+ {
+ return ( Length );
+ }
+ }
+ else
+ {
+ m_Remux->ClearInput();
+ usleep(100000);
+ return ( 0 );
+ }
+
+}
+
+/* ----------------------------------------------------------------------------
+ */
+int cStreamDevice::PlayVideo(const uchar *Data, int Length)
+{
+ if (cTSWorker::HaveStreamClient()) {
+ while ((m_Remux->Free() < Length) && cTSWorker::HaveStreamClient())
+ cCondWait::SleepMs(1);
+ int result=m_Remux->Put(Data, Length);
+ if (result!=Length) {
+ fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(video). result:%d Length: %d Skipping Video PES packet...\n", result, Length );
+ // Delete part of data already written to buffer
+ m_Remux->DelInput(result);
+ return (0);
+ }
+ else
+ {
+ return ( Length );
+ }
+ }
+ else
+ {
+ m_Remux->ClearInput();
+ usleep(100000);
+ return ( 0 );
+ }
+}
diff --git a/streamdevice.h b/streamdevice.h
new file mode 100644
index 0000000..64a1f0b
--- /dev/null
+++ b/streamdevice.h
@@ -0,0 +1,48 @@
+/*
+ * streamdevice.h: streaming network device
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _STREAMDEVICE__H
+#define _STREAMDEVICE__H
+
+#define TS_VPID 99
+#define TS_APID 100
+
+#include <vdr/device.h>
+
+#include "pes2ts.h"
+
+class cStreamDevice: public cDevice {
+private:
+ cPES2TSRemux *m_Remux;
+protected:
+public:
+ cStreamDevice(void);
+ ~cStreamDevice(void);
+ virtual bool HasDecoder(void) const;
+ virtual bool CanReplay(void) const;
+ virtual bool SetPlayMode(ePlayMode PlayMode);
+ virtual void TrickSpeed(int Speed);
+ virtual void Clear(void);
+ virtual void Play(void);
+ virtual void Freeze(void);
+ virtual void Mute(void);
+ virtual void SetVolumeDevice (int Volume);
+ virtual void StillPicture(const uchar *Data, int Length);
+ virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
+ virtual int PlayVideo(const uchar *Data, int Length);
+ virtual int PlayAudio(const uchar *Data, int Length);
+ virtual int ProvidesCa(const cChannel *Channel) const;
+ virtual void MakePrimaryDevice(bool On);
+ uchar *Get(int &Count) { return m_Remux->Get(Count); }
+ void LockOutput() { m_Remux->LockOutput(); }
+ void UnlockOutput() { m_Remux->UnlockOutput(); }
+ void Del(int Count) { m_Remux->DelOutput(Count); }
+ void ClearOutput() { m_Remux->ClearOutput(); }
+ int Available(void) { return m_Remux->Available(); }
+};
+
+#endif
diff --git a/tableinitcmtemplate.c b/tableinitcmtemplate.c
new file mode 100644
index 0000000..b282326
--- /dev/null
+++ b/tableinitcmtemplate.c
@@ -0,0 +1,108 @@
+/*
+ * tableinitcmtemplate.c - template for initialising lookup tables for
+ * translation from a colour map to true colour.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with a different definition of the macro OUTBPP.
+ * For each value of OUTBPP, this file defines a function which allocates an
+ * appropriately sized lookup table and initialises it.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+#if !defined(OUTBPP)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define OUT_T CONCAT2E(CARD,OUTBPP)
+#define SwapOUT(x) CONCAT2E(Swap,OUTBPP) (x)
+#define rfbInitColourMapSingleTableOUT \
+ CONCAT2E(rfbInitColourMapSingleTable,OUTBPP)
+
+#include <vdr/plugin.h>
+#include <osdworker.h>
+
+// THIS CODE HAS BEEN MODIFIED FROM THE ORIGINAL UNIX SOURCE
+// TO WORK FOR WINVNC. THE PALETTE SHOULD REALLY BE RETRIEVED
+// FROM THE VNCDESKTOP OBJECT, RATHER THAN FROM THE OS DIRECTLY
+
+static void
+rfbInitColourMapSingleTableOUT (char **table,
+ rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ fprintf(stderr, "[ffnetdev] VNC: rfbInitColourMapSingleTable called\n");
+
+ // ALLOCATE SPACE FOR COLOUR TABLE
+
+ int nEntries = 1 << in->bitsPerPixel;
+
+ // Allocate the table
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * sizeof(OUT_T));
+ if (*table == NULL)
+ {
+ fprintf(stderr, "[ffnetdev] VNC: failed to allocate translation table\n");
+ return;
+ }
+
+ // Obtain the system palette
+ /*
+ HDC hDC = GetDC(NULL);
+ PALETTEENTRY palette[256];
+ if (GetSystemPaletteEntries(hDC,
+ 0, 256, palette) == 0)
+ {
+ fprintf(stderr, "[ffnetdev] VNC: failed to get system palette, error=%d\n",
+ GetLastError());
+ ReleaseDC(NULL, hDC);
+ return;
+ }
+ ReleaseDC(NULL, hDC);
+ */
+
+ tColor *pColors;
+ int NumColors;
+ cOSDWorker::GetOSDColors(&pColors, &NumColors);
+
+ // COLOUR TRANSLATION
+
+ // We now have the colour table intact. Map it into a translation table
+ int i, r, g, b;
+ OUT_T outRed, outGreen, outBlue;
+ OUT_T *t = (OUT_T *)*table;
+
+ tColor *pColor = pColors;
+ for (i = 0; i < NumColors; i++)
+ {
+ // Split down the RGB data
+ r = (((tColor)*pColor) >> in->redShift) & in->redMax;
+ g = (((tColor)*pColor) >> in->greenShift) & in->greenMax;
+ b = (((tColor)*pColor) >> in->blueShift) & in->blueMax;
+
+
+ outRed = (r / (256 / (1 + Swap16IfLE(out->redMax )))) & Swap16IfLE(out->redMax);
+ outGreen = (g / (256 / (1 + Swap16IfLE(out->greenMax)))) & Swap16IfLE(out->greenMax);
+ outBlue = (b / (256 / (1 + Swap16IfLE(out->blueMax )))) & Swap16IfLE(out->blueMax);
+
+ // Now translate it
+ t[i] = ((outRed << out->redShift) |
+ (outGreen << out->greenShift) |
+ (outBlue << out->blueShift));
+#if (OUTBPP != 8)
+ if (out->bigEndian != in->bigEndian)
+ {
+ t[i] = SwapOUT(t[i]);
+ }
+#endif
+ pColor++;
+ }
+
+ fprintf(stderr, "[ffnetdev] VNC: rfbInitColourMapSingleTable done\n");
+}
+
+#undef OUT_T
+#undef SwapOUT
+#undef rfbInitColourMapSingleTableOUT
diff --git a/tableinittctemplate.c b/tableinittctemplate.c
new file mode 100644
index 0000000..9849864
--- /dev/null
+++ b/tableinittctemplate.c
@@ -0,0 +1,122 @@
+/*
+ * tableinittctemplate.c - template for initialising lookup tables for
+ * truecolour to truecolour translation.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with a different definition of the macro OUTBPP.
+ * For each value of OUTBPP, this file defines two functions for initialising
+ * lookup tables. One is for truecolour translation using a single lookup
+ * table, the other is for truecolour translation using three separate
+ * lookup tables for the red, green and blue values.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+#if !defined(OUTBPP)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define OUT_T CONCAT2E(CARD,OUTBPP)
+#define SwapOUT(x) CONCAT2E(Swap,OUTBPP) (x)
+#define rfbInitTrueColourSingleTableOUT \
+ CONCAT2E(rfbInitTrueColourSingleTable,OUTBPP)
+#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUTBPP)
+#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUTBPP)
+
+static void
+rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift,
+ int swap);
+
+
+/*
+ * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour
+ * translation.
+ */
+
+static void
+rfbInitTrueColourSingleTableOUT (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ int i;
+ int inRed, inGreen, inBlue, outRed, outGreen, outBlue;
+ OUT_T *t;
+ int nEntries = 1 << in->bitsPerPixel;
+
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * sizeof(OUT_T));
+ if (table == NULL) return;
+ t = (OUT_T *)*table;
+
+ for (i = 0; i < nEntries; i++) {
+ inRed = (i >> in->redShift) & in->redMax;
+ inGreen = (i >> in->greenShift) & in->greenMax;
+ inBlue = (i >> in->blueShift) & in->blueMax;
+
+ outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax;
+ outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax;
+ outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax;
+
+ t[i] = ((outRed << out->redShift) |
+ (outGreen << out->greenShift) |
+ (outBlue << out->blueShift));
+#if (OUTBPP != 8)
+ if (out->bigEndian != in->bigEndian) {
+ t[i] = SwapOUT(t[i]);
+ }
+#endif
+ }
+}
+
+
+/*
+ * rfbInitTrueColourRGBTables sets up three separate lookup tables for the
+ * red, green and blue values.
+ */
+
+static void
+rfbInitTrueColourRGBTablesOUT (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ OUT_T *redTable;
+ OUT_T *greenTable;
+ OUT_T *blueTable;
+
+ if (*table) free(*table);
+ *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3)
+ * sizeof(OUT_T));
+ redTable = (OUT_T *)*table;
+ greenTable = redTable + in->redMax + 1;
+ blueTable = greenTable + in->greenMax + 1;
+
+ rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax,
+ out->redShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax,
+ out->greenShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax,
+ out->blueShift, (out->bigEndian != in->bigEndian));
+}
+
+static void
+rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift,
+ int swap)
+{
+ int i;
+ int nEntries = inMax + 1;
+
+ for (i = 0; i < nEntries; i++) {
+ table[i] = ((i * outMax + inMax / 2) / inMax) << outShift;
+#if (OUTBPP != 8)
+ if (swap) {
+ table[i] = SwapOUT(table[i]);
+ }
+#endif
+ }
+}
+
+#undef OUT_T
+#undef SwapOUT
+#undef rfbInitTrueColourSingleTableOUT
+#undef rfbInitTrueColourRGBTablesOUT
+#undef rfbInitOneRGBTableOUT
diff --git a/tabletranstemplate.c b/tabletranstemplate.c
new file mode 100644
index 0000000..dc603fb
--- /dev/null
+++ b/tabletranstemplate.c
@@ -0,0 +1,99 @@
+/*
+ * tabletranstemplate.c - template for translation using lookup tables.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with different definitions of the macros INBPP and OUTBPP.
+ *
+ * For each pair of values INBPP and OUTBPP, this file defines two functions for
+ * translating a given rectangle of pixel data. One uses a single lookup
+ * table, and the other uses three separate lookup tables for the red, green
+ * and blue values.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+#if !defined(INBPP) || !defined(OUTBPP)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define IN_T CONCAT2E(CARD,INBPP)
+#define OUT_T CONCAT2E(CARD,OUTBPP)
+#define rfbTranslateWithSingleTableINtoOUT \
+ CONCAT4E(rfbTranslateWithSingleTable,INBPP,to,OUTBPP)
+#define rfbTranslateWithRGBTablesINtoOUT \
+ CONCAT4E(rfbTranslateWithRGBTables,INBPP,to,OUTBPP)
+
+/*
+ * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data
+ * using a single lookup table.
+ */
+
+static void
+rfbTranslateWithSingleTableINtoOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ OUT_T *opLineEnd;
+ OUT_T *t = (OUT_T *)table;
+
+ while (height > 0) {
+ opLineEnd = op + width;
+
+ while (op < opLineEnd) {
+ *(op++) = t[*(ip++)];
+ }
+
+ ip += ipextra;
+ height--;
+ }
+}
+
+
+/*
+ * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data
+ * using three separate lookup tables for the red, green and blue values.
+ */
+
+static void
+rfbTranslateWithRGBTablesINtoOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ OUT_T *opLineEnd;
+ OUT_T *redTable = (OUT_T *)table;
+ OUT_T *greenTable = redTable + in->redMax + 1;
+ OUT_T *blueTable = greenTable + in->greenMax + 1;
+ IN_T in_pix;
+ OUT_T out_pix;
+
+ while (height > 0) {
+ opLineEnd = op + width;
+
+ while (op < opLineEnd) {
+ in_pix = *ip++;
+ out_pix = redTable[(in_pix >> in->redShift) & in->redMax];
+ out_pix |= greenTable[(in_pix >> in->greenShift) & in->greenMax];
+ out_pix |= blueTable[(in_pix >> in->blueShift) & in->blueMax];
+ *op++ = out_pix;
+ }
+ ip += ipextra;
+ height--;
+ }
+}
+
+#undef IN_T
+#undef OUT_T
+#undef rfbTranslateWithSingleTableINtoOUT
+#undef rfbTranslateWithRGBTablesINtoOUT
diff --git a/tools/select.c b/tools/select.c
new file mode 100644
index 0000000..0ab5f9b
--- /dev/null
+++ b/tools/select.c
@@ -0,0 +1,49 @@
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+cTBSelect::cTBSelect(void) {
+ Clear();
+}
+
+cTBSelect::~cTBSelect() {
+}
+
+int cTBSelect::Select(uint TimeoutMs) {
+ struct timeval tv;
+ ssize_t res;
+ int ms;
+
+ tv.tv_usec = (TimeoutMs % 1000) * 1000;
+ tv.tv_sec = TimeoutMs / 1000;
+
+ if (TimeoutMs == 0)
+ return ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, &tv);
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ while (ms > 0 && (res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL,
+ &tv)) == -1 && errno == EINTR) {
+ ms = TimeoutMs - starttime.Elapsed();
+ tv.tv_usec = (ms % 1000) * 1000;
+ tv.tv_sec = ms / 1000;
+ }
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return res;
+}
+
+int cTBSelect::Select(void) {
+ ssize_t res;
+ while ((res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, NULL)) == -1
+ && errno == EINTR)
+ ;
+ return res;
+}
diff --git a/tools/select.h b/tools/select.h
new file mode 100644
index 0000000..7e873e2
--- /dev/null
+++ b/tools/select.h
@@ -0,0 +1,75 @@
+#ifndef TOOLBOX_SELECT_H
+#define TOOLBOX_SELECT_H
+
+#include "tools/tools.h"
+
+#include <sys/types.h>
+
+/* cTBSelect provides an interface for polling UNIX-like file descriptors. */
+
+class cTBSelect {
+private:
+ int m_MaxFiled;
+
+ fd_set m_Rfds;
+ fd_set m_Wfds;
+
+public:
+ cTBSelect(void);
+ virtual ~cTBSelect();
+
+ /* Clear() resets the object for use in a new Select() call. All file
+ descriptors and their previous states are invalidated. */
+ virtual void Clear(void);
+
+ /* Add() adds a file descriptor to be polled in the next Select() call.
+ That call polls if the file is readable if Output is set to false,
+ writeable otherwise. */
+ virtual bool Add(int Filed, bool Output = false);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable), or after
+ TimeoutMs milliseconds, whichever happens first. It returns the number
+ of filedescriptors that have changed state. On error, -1 is returned
+ and errno is set appropriately. */
+ virtual int Select(uint TimeoutMs);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable). It returns the
+ number of filedescriptors that have changed state. On error, -1 is
+ returned and errno is set appropriately. */
+ virtual int Select(void);
+
+ /* CanRead() returns true if the descriptor has changed to readable during
+ the last Select() call. Otherwise false is returned. */
+ virtual bool CanRead(int FileNo) const;
+
+ /* CanWrite() returns true if the descriptor has changed to writeable
+ during the last Select() call. Otherwise false is returned. */
+ virtual bool CanWrite(int FileNo) const;
+};
+
+inline void cTBSelect::Clear(void) {
+ FD_ZERO(&m_Rfds);
+ FD_ZERO(&m_Wfds);
+ m_MaxFiled = -1;
+}
+
+inline bool cTBSelect::Add(int Filed, bool Output /* = false */) {
+ if (Filed < 0) return false;
+ FD_SET(Filed, Output ? &m_Wfds : &m_Rfds);
+ if (Filed > m_MaxFiled) m_MaxFiled = Filed;
+ return true;
+}
+
+inline bool cTBSelect::CanRead(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_Rfds);
+}
+
+inline bool cTBSelect::CanWrite(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_Wfds);
+}
+
+#endif // TOOLBOX_SELECT_H
diff --git a/tools/socket.c b/tools/socket.c
new file mode 100644
index 0000000..3e3be65
--- /dev/null
+++ b/tools/socket.c
@@ -0,0 +1,135 @@
+#include "tools/socket.h"
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+cTBSocket::cTBSocket(int Type) {
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+ m_Type = Type;
+}
+
+cTBSocket::~cTBSocket() {
+ if (IsOpen()) Close();
+}
+
+bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = 0;
+ m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1)
+ return false;
+
+ m_RemoteAddr.sin_family = AF_INET;
+ m_RemoteAddr.sin_port = htons(Port);
+ m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
+ if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
+ sizeof(m_RemoteAddr)) == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+ return false;
+
+ return cTBSource::Open(socket);
+}
+
+bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
+ int val;
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
+ return false;
+
+ val = 1;
+ if (::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = htons(Port);
+ m_LocalAddr.sin_addr.s_addr = inet_addr(Ip.c_str());
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+ return false;
+
+ if (m_Type == SOCK_STREAM && ::listen(socket, BackLog) == -1)
+ return false;
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+bool cTBSocket::Accept(const cTBSocket &Listener) {
+ socklen_t addrlen;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ addrlen = sizeof(struct sockaddr_in);
+ if ((socket = ::accept(Listener, (struct sockaddr*)&m_RemoteAddr,
+ &addrlen)) == -1)
+ return false;
+
+ addrlen = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1)
+ return false;
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+RETURNS(cTBSocket, cTBSocket::Accept(void) const, ret)
+ ret.Accept(*this);
+RETURN(ret)
+
+bool cTBSocket::Close(void) {
+ bool ret = true;
+
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ if (::close(*this) == -1)
+ ret = false;
+
+ if (!cTBSource::Close())
+ ret = false;
+
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+
+ return ret;
+}
+
+bool cTBSocket::Shutdown(int how) {
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ return ::shutdown(*this, how) != -1;
+}
diff --git a/tools/socket.h b/tools/socket.h
new file mode 100644
index 0000000..d1a7d62
--- /dev/null
+++ b/tools/socket.h
@@ -0,0 +1,108 @@
+#ifndef TOOLBOX_SOCKET_H
+#define TOOLBOX_SOCKET_H
+
+#include "tools/tools.h"
+#include "tools/source.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string>
+
+/* cTBSocket provides a cTBSource-derived interface for input and output on
+ TCP/IPv4-sockets. */
+
+class cTBSocket: public cTBSource {
+private:
+ struct sockaddr_in m_LocalAddr;
+ struct sockaddr_in m_RemoteAddr;
+
+ int m_Type;
+
+public:
+ cTBSocket(int Type = SOCK_STREAM);
+ virtual ~cTBSocket();
+
+ /* See cTBSource::SysRead()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const;
+
+ /* See cTBSource::SysWrite()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
+
+ /* Connect() tries to connect an available local socket to the port given
+ by Port of the target host given by Host in numbers-and-dots notation
+ (i.e. "212.43.45.21"). Returns true if the connection attempt was
+ successful and false otherwise, setting errno appropriately. */
+ virtual bool Connect(const std::string &Host, uint Port);
+
+ /* Shutdown() shuts down one or both ends of a socket. If called with How
+ set to SHUT_RD, further reads on this socket will be denied. If called
+ with SHUT_WR, all writes are denied. Called with SHUT_RDWR, all firther
+ action on this socket will be denied. Returns true on success and false
+ otherwise, setting errno appropriately. */
+ virtual bool Shutdown(int How);
+
+ /* Close() closes the associated socket and releases all structures.
+ Returns true on success and false otherwise, setting errno
+ appropriately. The object is in the closed state afterwards, regardless
+ of any errors. */
+ virtual bool Close(void);
+
+ /* Listen() listens on the local port Port for incoming connections. The
+ BackLog parameter defines the maximum length the queue of pending
+ connections may grow to. Returns true if the object is listening on
+ the specified port and false otherwise, setting errno appropriately. */
+ virtual bool Listen(const std::string &Ip, uint Port, int BackLog);
+
+ /* Accept() returns a newly created cTBSocket, which is connected to the
+ first connection request on the queue of pending connections of a
+ listening socket. If no connection request was pending, or if any other
+ error occured, the resulting cTBSocket is closed. */
+ virtual cTBSocket Accept(void) const;
+
+ /* Accept() extracts the first connection request on the queue of pending
+ connections of the listening socket Listener and connects it to this
+ object. Returns true on success and false otherwise, setting errno to
+ an appropriate value. */
+ virtual bool Accept(const cTBSocket &Listener);
+
+ /* LocalPort() returns the port number this socket is connected to locally.
+ The result is undefined for a non-open socket. */
+ int LocalPort(void) const { return ntohs(m_LocalAddr.sin_port); }
+
+ /* RemotePort() returns the port number this socket is connected to on the
+ remote side. The result is undefined for a non-open socket. */
+ int RemotePort(void) const { return ntohs(m_RemoteAddr.sin_port); }
+
+ /* LocalIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to locally. This can be
+ "0.0.0.0" for a listening socket listening to all interfaces. If the
+ socket is in its closed state, the result is undefined. */
+ std::string LocalIp(void) const { return inet_ntoa(m_LocalAddr.sin_addr); }
+
+ /* RemoteIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to on the remote side. If the
+ socket is in its closed state, the result is undefined. */
+ std::string RemoteIp(void) const { return inet_ntoa(m_RemoteAddr.sin_addr); }
+
+ in_addr_t LocalIpAddr(void) const { return m_LocalAddr.sin_addr.s_addr; }
+ in_addr_t RemoteIpAddr(void) const { return m_RemoteAddr.sin_addr.s_addr; }
+
+ int Type(void) const { return m_Type; }
+};
+
+inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const {
+ if (m_Type == SOCK_DGRAM) {
+ socklen_t len = sizeof(m_RemoteAddr);
+ return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len);
+ } else
+ return ::recv(*this, Buffer, Length, 0);
+}
+
+inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const {
+ return ::send(*this, Buffer, Length, 0);
+}
+
+#endif // TOOLBOX_SOCKET_H
diff --git a/tools/source.c b/tools/source.c
new file mode 100644
index 0000000..f8751b6
--- /dev/null
+++ b/tools/source.c
@@ -0,0 +1,169 @@
+#include "tools/source.h"
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+cTBSource::cTBSource(void) {
+ m_BytesRead = 0;
+ m_BytesWritten = 0;
+ m_Filed = -1;
+}
+
+bool cTBSource::Open(int Filed, bool IsUnixFd) {
+ if (IsOpen())
+ Close();
+
+ m_Filed = Filed;
+ if (IsUnixFd && ::fcntl(m_Filed, F_SETFL, O_NONBLOCK) == -1)
+ return false;
+
+ return true;
+}
+
+cTBSource::~cTBSource() {
+}
+
+bool cTBSource::Close(void) {
+ if (!IsOpen()) {
+ errno = EBADF;
+ return false;
+ }
+
+ m_Filed = -1;
+ return true;
+}
+
+ssize_t cTBSource::Read(void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysRead(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesRead += res;
+ return res;
+}
+
+ssize_t cTBSource::Write(const void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysWrite(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesWritten += res;
+ return res;
+}
+
+bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
+ cTBSelect sel;
+ int ms, offs;
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ offs = 0;
+ while (Length > 0) {
+ int b;
+
+ sel.Clear();
+ sel.Add(m_Filed, true);
+ if (sel.Select(ms) == -1)
+ return false;
+
+ if (sel.CanWrite(m_Filed)) {
+ if ((b = Write((char*)Buffer + offs, Length)) == -1)
+ return false;
+ offs += b;
+ Length -= b;
+ }
+
+ ms = TimeoutMs - starttime.Elapsed();
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cTBSource::SafeWrite(const void *Buffer, size_t Length) {
+ cTBSelect sel;
+ int offs;
+
+ offs = 0;
+ while (Length > 0) {
+ int b;
+
+ sel.Clear();
+ sel.Add(m_Filed, true);
+ if (sel.Select() == -1)
+ return false;
+
+ if (sel.CanWrite(m_Filed)) {
+ if ((b = Write((char*)Buffer + offs, Length)) == -1)
+ return false;
+ offs += b;
+ Length -= b;
+ }
+
+ }
+ return true;
+}
+
+ssize_t cTBSource::ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs) {
+ int ms;
+ size_t len;
+ cTBSelect sel;
+
+ if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
+ if (len > Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer.data(), len);
+ m_LineBuffer.erase(0, len + strlen(Seq));
+ //Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
+ // (char*)Buffer);
+ return len;
+ }
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ while (m_LineBuffer.size() < BUFSIZ) {
+ sel.Clear();
+ sel.Add(m_Filed, false);
+
+ if (sel.Select(ms) == -1)
+ return -1;
+
+ if (sel.CanRead(m_Filed)) {
+ int b;
+
+ len = m_LineBuffer.size();
+ m_LineBuffer.resize(BUFSIZ);
+ if ((b = Read((char*)m_LineBuffer.data() + len, BUFSIZ - len)) == -1)
+ return -1;
+ m_LineBuffer.resize(len + b);
+
+ if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
+ if (len > Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer.data(), len);
+ m_LineBuffer.erase(0, len + strlen(Seq));
+ //Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
+ // (char*)Buffer);
+ return len;
+ }
+ }
+
+ ms = TimeoutMs - starttime.Elapsed();
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ errno = ENOBUFS;
+ return -1;
+}
+
diff --git a/tools/source.h b/tools/source.h
new file mode 100644
index 0000000..09c4bf3
--- /dev/null
+++ b/tools/source.h
@@ -0,0 +1,109 @@
+#ifndef TOOLBOX_SOURCE_H
+#define TOOLBOX_SOURCE_H
+
+#include "tools/tools.h"
+
+#include <sys/types.h>
+#include <string>
+
+/* cTBSource provides an abstract interface for input and output. It can
+ be used to have common access to different types of UNIX-files. */
+
+class cTBSource {
+private:
+ int m_Filed;
+
+ size_t m_BytesRead;
+ size_t m_BytesWritten;
+
+ std::string m_LineBuffer;
+
+public:
+ cTBSource(void);
+ virtual ~cTBSource();
+
+ /* SysRead() implements the low-level read on the source. It will store
+ data into the area pointed to by Buffer, which is at least Length
+ bytes in size. It will return the exact number of bytes read (which
+ can be fewer than requested). On error, -1 is returned, and errno
+ is set to an appropriate value. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const = 0;
+
+ /* SysWrite() implements the low-level write on the source. It will write
+ at most Length bytes of the data pointed to by Buffer. It will return
+ the exact number of bytes written (which can be fewer than requested).
+ On error, -1 is returned, and errno is set to an appropriate value. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const = 0;
+
+ /* IsOpen() returns true, if this source refers to a valid descriptor.
+ It is not checked whether this source is really open, so only if
+ opened by the appropriate Methods this function will return the
+ correct value */
+ virtual bool IsOpen(void) const { return m_Filed != -1; }
+
+ /* Open() associates this source with the descriptor Filed, setting it
+ to non-blocking mode if IsUnixFd in true. Returns true on success,
+ and false on error, setting errno to appropriately.
+ If you want to implement sources that can't be represented by UNIX
+ filedescriptors, you can use Filed to store any useful information
+ about the source.
+ This must be called by any derivations in an appropriate Method (like
+ open for files, connect for sockets). */
+ virtual bool Open(int Filed, bool IsUnixFd = true);
+
+ /* Close() resets the source to the uninitialized state (IsOpen() == false)
+ and must be called by any derivations after really closing the source.
+ Returns true on success and false on error, setting errno appropriately.
+ The object is in closed state afterwards, even if an error occured. */
+ virtual bool Close(void);
+
+ /* Read() reads at most Length bytes into the storage pointed to by Buffer,
+ which must be at least Length bytes in size, using the SysRead()-
+ Interface. It retries if an EINTR occurs (i.e. the low-level call was
+ interrupted). It returns the exact number of bytes read (which can be
+ fewer than requested). On error, -1 is returned, and errno is set
+ appropriately. */
+ ssize_t Read(void *Buffer, size_t Length);
+
+ /* Write() writes at most Length bytes from the storage pointed to by
+ Buffer, using the SysWrite()-Interface. It retries if EINTR occurs
+ (i.e. the low-level call was interrupted). It returns the exact number
+ of bytes written (which can be fewer than requested). On error, -1 is
+ returned and errno is set appropriately. */
+ ssize_t Write(const void *Buffer, size_t Length);
+
+ /* TimedWrite() tries to write Length bytes from the storage pointed to by
+ Buffer within the time specified by TimeoutMs, using the Write()-
+ Interface. On success, true is returned. On error, false is returned
+ and errno is set appropriately. TimedRead only works on UNIX file
+ descriptor sources. */
+ bool TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs);
+
+ bool SafeWrite(const void *Buffer, size_t Length);
+
+ /* ReadUntil() tries to read at most Length bytes into the storage pointed
+ to by Buffer, which must be at least Length bytes in size, within the
+ time specified by TimeoutMs, using the Read()-Interface. Reading stops
+ after the character sequence Seq has been read and on end-of-file.
+ Returns the number of bytes read (if that is equal to Length, you have
+ to check if the buffer ends with Seq), or -1 on error, in which case
+ errno is set appropriately. */
+ ssize_t ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs);
+
+ /* BytesRead() returns the exact number of bytes read through the Read()
+ method since Close() has been called on this source (or since its
+ creation). */
+ size_t BytesRead(void) const { return m_BytesRead; }
+
+ /* BytesWritten() returns the exact number of bytes written through the
+ Write() method since Close() has been called on this source (or since
+ its creation). */
+ size_t BytesWritten(void) const { return m_BytesWritten; }
+
+ /* operator int() returns the descriptor (or informative number) associated
+ with this source. */
+ operator int() const { return m_Filed; }
+};
+
+#endif // TOOLBOX_SOURCE_H
diff --git a/tools/tools.c b/tools/tools.c
new file mode 100644
index 0000000..fa813fa
--- /dev/null
+++ b/tools/tools.c
@@ -0,0 +1,12 @@
+#include "tools/tools.h"
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+void *operator new(size_t nSize, void *p) throw () {
+ return p;
+}
+
diff --git a/tools/tools.h b/tools/tools.h
new file mode 100644
index 0000000..ab00c60
--- /dev/null
+++ b/tools/tools.h
@@ -0,0 +1,67 @@
+#ifndef TOOLBOX_TOOLS_H
+#define TOOLBOX_TOOLS_H
+
+//#include <stdio.h>
+//#include <iostream>
+#include <sys/types.h>
+
+//#define KILOBYTE(x) ((x)*1024)
+//#define MEGABYTE(x) (KILOBYTE(x)*1024)
+
+//typedef unsigned int uint;
+//typedef unsigned long ulong;
+typedef unsigned char uchar;
+//typedef unsigned short ushort;
+
+// Special constructor for CreateElements
+void *operator new(size_t, void*) throw ();
+
+#ifdef TOOLBOX_DEBUG
+# define ASSERT(x) if ((x)) cerr << "Warning: ASSERT failed At " << __FILE__ << ":" << __LINE__ << " ["#x"]" << endl
+# define CHECK_PTR(x) if (!(x)) cerr << "Warning: Pointer is NULL At " << __FILE__ << ":" << __LINE__ << endl;
+# define CHECK_NEXT_ALLOC() _checkNextAlloc()
+# define DPRINT(x...) LOGi(x)
+#else
+# define ASSERT(x)
+# define CHECK_PTR(x)
+# define CHECK_NEXT_ALLOC()
+# define DPRINT(x...)
+#endif
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+/* RETURNS() and RETURN() are macros that can be used if a class object is
+ being returned. They make use of the GNU C-Compiler's named return value
+ feature, if available. In this case, the class object isn't returned and
+ copied, but the result itself is filled.
+
+ RETURNS(ReturnType, FunctionDeclaration, Result)
+ ... function-body working on Result ...
+ RETURN(Result)
+
+ A function like this (cXYZ is a class type):
+
+ cXYZ myfunction(int a, char *b) {
+ cXYZ result;
+ ... something happens with result ...
+ return result;
+ }
+
+ can be written like this:
+
+ RETURNS(cXYZ, myfunction(int a, char *b), result)
+ ... something happens with result ...
+ RETURN(result)
+
+ DISABLED SINCE GCC 3.x
+*/
+//#ifdef __GNUC__
+//# define RETURNS(t,x,r) t x return r {
+//# define RETURN(x) }
+//#else
+# define RETURNS(t,x,r) t x { t r;
+# define RETURN(x) return x; }
+//#endif
+
+#endif // TOOLBOX_TOOLS_H
diff --git a/translate.c b/translate.c
new file mode 100644
index 0000000..76b980a
--- /dev/null
+++ b/translate.c
@@ -0,0 +1,138 @@
+/*
+ * translate.c - translate between different pixel formats
+ */
+
+/*
+ * 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.
+ */
+
+//#include "stdhdrs.h"
+
+#include "translate.h"
+#include <stdio.h>
+#include "rfb.h"
+#include <vdr/plugin.h>
+
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#define CONCAT4(a,b,c,d) a##b##c##d
+#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d)
+
+#define OUTBPP 8
+#include "tableinittctemplate.c"
+#include "tableinitcmtemplate.c"
+#define INBPP 8
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 16
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 32
+#include "tabletranstemplate.c"
+#undef INBPP
+#undef OUTBPP
+
+#define OUTBPP 16
+#include "tableinittctemplate.c"
+#include "tableinitcmtemplate.c"
+#define INBPP 8
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 16
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 32
+#include "tabletranstemplate.c"
+#undef INBPP
+#undef OUTBPP
+
+#define OUTBPP 32
+#include "tableinittctemplate.c"
+#include "tableinitcmtemplate.c"
+#define INBPP 8
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 16
+#include "tabletranstemplate.c"
+#undef INBPP
+#define INBPP 32
+#include "tabletranstemplate.c"
+#undef INBPP
+#undef OUTBPP
+
+rfbInitTableFnType rfbInitTrueColourSingleTableFns[3] = {
+ rfbInitTrueColourSingleTable8,
+ rfbInitTrueColourSingleTable16,
+ rfbInitTrueColourSingleTable32
+};
+
+rfbInitTableFnType rfbInitColourMapSingleTableFns[3] = {
+ rfbInitColourMapSingleTable8,
+ rfbInitColourMapSingleTable16,
+ rfbInitColourMapSingleTable32
+};
+
+rfbInitTableFnType rfbInitTrueColourRGBTablesFns[3] = {
+ rfbInitTrueColourRGBTables8,
+ rfbInitTrueColourRGBTables16,
+ rfbInitTrueColourRGBTables32
+};
+
+rfbTranslateFnType rfbTranslateWithSingleTableFns[3][3] = {
+ { rfbTranslateWithSingleTable8to8,
+ rfbTranslateWithSingleTable8to16,
+ rfbTranslateWithSingleTable8to32 },
+ { rfbTranslateWithSingleTable16to8,
+ rfbTranslateWithSingleTable16to16,
+ rfbTranslateWithSingleTable16to32 },
+ { rfbTranslateWithSingleTable32to8,
+ rfbTranslateWithSingleTable32to16,
+ rfbTranslateWithSingleTable32to32 }
+};
+
+rfbTranslateFnType rfbTranslateWithRGBTablesFns[3][3] = {
+ { rfbTranslateWithRGBTables8to8,
+ rfbTranslateWithRGBTables8to16,
+ rfbTranslateWithRGBTables8to32 },
+ { rfbTranslateWithRGBTables16to8,
+ rfbTranslateWithRGBTables16to16,
+ rfbTranslateWithRGBTables16to32 },
+ { rfbTranslateWithRGBTables32to8,
+ rfbTranslateWithRGBTables32to16,
+ rfbTranslateWithRGBTables32to32 }
+};
+
+
+
+// rfbTranslateNone is used when no translation is required.
+
+void
+rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out,
+ char *iptr, char *optr, int bytesBetweenInputLines,
+ int width, int height)
+{
+ int bytesPerOutputLine = width * (out->bitsPerPixel / 8);
+
+ while (height > 0) {
+ memcpy(optr, iptr, bytesPerOutputLine);
+ iptr += bytesBetweenInputLines;
+ optr += bytesPerOutputLine;
+ height--;
+ }
+}
+
diff --git a/translate.h b/translate.h
new file mode 100644
index 0000000..da1c7b0
--- /dev/null
+++ b/translate.h
@@ -0,0 +1,81 @@
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+/* translate.h - prototypes of functions in translate.cpp */
+
+#ifndef TRANSLATE_H__
+#define TRANSLATE_H__
+
+//#include "stdhdrs.h"
+#include "rfb.h"
+
+// Translate function prototype!
+typedef void (*rfbTranslateFnType)(char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height);
+
+// Init function prototype!
+typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out);
+
+
+// External translation stuff
+extern void rfbTranslateNone(char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height);
+
+// Macro to compare pixel formats.
+#define PF_EQ(x,y) \
+ ((x.bitsPerPixel == y.bitsPerPixel) && \
+ (x.depth == y.depth) && \
+ (x.trueColour == y.trueColour) && \
+ ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \
+ (!x.trueColour || ((x.redMax == y.redMax) && \
+ (x.greenMax == y.greenMax) && \
+ (x.blueMax == y.blueMax) && \
+ (x.redShift == y.redShift) && \
+ (x.greenShift == y.greenShift) && \
+ (x.blueShift == y.blueShift))))
+
+// Translation functions themselves
+extern rfbInitTableFnType rfbInitTrueColourSingleTableFns[];
+extern rfbInitTableFnType rfbInitColourMapSingleTableFns[];
+extern rfbInitTableFnType rfbInitTrueColourRGBTablesFns[];
+extern rfbTranslateFnType rfbTranslateWithSingleTableFns[3][3];
+extern rfbTranslateFnType rfbTranslateWithRGBTablesFns[3][3];
+
+/*
+extern bool rfbSetTranslateFunction(rfbClientPtr cl);
+extern void rfbSetClientColourMaps(int firstColour, int nColours);
+extern bool rfbSetClientColourMap(rfbClientPtr cl, int firstColour,
+ int nColours);
+*/
+
+#endif
diff --git a/tsworker.c b/tsworker.c
new file mode 100644
index 0000000..98e893e
--- /dev/null
+++ b/tsworker.c
@@ -0,0 +1,248 @@
+/*
+ * tsworker.c: ts streaming worker thread
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <sys/time.h>
+
+#include <vdr/tools.h>
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include "tsworker.h"
+#include "config.h"
+
+#define MINSENDBYTES KILOBYTE(500)
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+cTSWorker *cTSWorker::m_Instance = NULL;
+
+cTSWorker::cTSWorker(void)
+ : cThread("[ffnetdev] TS streamer")
+{
+ m_Active = false;
+
+ m_StreamClient = NULL;
+ origPrimaryDevice = -1;
+}
+
+cTSWorker::~cTSWorker() {
+ if (m_Active) Stop();
+}
+
+void cTSWorker::Init(cStreamDevice *StreamDevice, int tsport, cPluginFFNetDev *pPlugin ) {
+ if (m_Instance == NULL) {
+ m_Instance = new cTSWorker;
+ m_Instance->m_StreamDevice = StreamDevice;
+ m_Instance->TSPort = tsport;
+ m_Instance->Start();
+ m_Instance->m_pPlugin = pPlugin;
+ }
+}
+
+void cTSWorker::Exit(void) {
+ if (m_Instance != NULL) {
+ m_Instance->Stop();
+ DELETENULL(m_Instance);
+ }
+}
+
+void cTSWorker::Stop(void) {
+ m_Active = false;
+ Cancel(3);
+}
+
+void cTSWorker::CloseStreamClient(void) {
+ m_Instance->close_Streamclient_request = true;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] Streamer: Closing of TS-streaming client socket requested.\r\n");
+#endif
+}
+
+void cTSWorker::Action(void) {
+ cTBSelect select;
+ //cTBSocket m_StreamListen(SOCK_DGRAM);
+ cTBSocket m_StreamListen;
+ struct timeval oldtime;
+ long bytessend = 0;
+ long oldbytessend = 0;
+
+ memset(&oldtime, 0, sizeof(oldtime));
+
+ const char* m_ListenIp = "0.0.0.0";
+ uint m_StreamListenPort = TSPort;
+
+ m_StreamClient = new cTBSocket;
+
+ m_Active = true;
+ have_Streamclient = false;
+
+ if (!m_StreamListen.Listen(m_ListenIp, m_StreamListenPort, 1)) { // ToDo JN place to allow more connections/clients!
+ esyslog("[ffnetdev] Streamer: Couldn't listen %s:%d: %s", m_ListenIp, m_StreamListenPort, strerror(errno));
+ m_Active = false;
+ }
+ else
+ isyslog("[ffnetdev] Streamer: Listening on port %d", m_StreamListenPort);
+
+ while (m_Active) {
+ select.Clear();
+
+ if (have_Streamclient==false)
+ select.Add(m_StreamListen, false);
+ else {
+ select.Add(*m_StreamClient, true); //select for writing fd
+ select.Add(*m_StreamClient, false); //select for reading fd
+ }
+
+ int numfd;
+ /* React on status change of any of the above file descriptor */
+ if ((numfd=select.Select(1000)) < 0) {
+ if (!m_Active) // Exit was requested while polling
+ continue;
+ esyslog("[ffnetdev] Streamer: Fatal error, ffnetdev exiting: %s", strerror(errno));
+ m_Active = false;
+ continue;
+ }
+
+
+ //DEBUG
+ /*
+ fprintf(stderr, "[ffnetdev] Streamer: Num_FD TS: %d", numfd);
+
+ if (select.CanRead(m_StreamListen) || select.CanWrite(m_StreamListen))
+ fprintf (stderr, "m_StreamListen can act.\n");
+ if (select.CanRead(*m_StreamClient) || select.CanWrite(*m_StreamClient))
+ fprintf (stderr, "m_StreamClient can act.\n");
+ */
+
+ /* Accept connecting streaming clients */
+ if ( (have_Streamclient==false)&&select.CanRead(m_StreamListen) ) {
+ if (m_StreamClient->Accept(m_StreamListen)) {
+ isyslog("[ffnetdev] Streamer: Accepted client %s:%d",
+ m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort());
+ have_Streamclient = true;
+
+ m_pPlugin->SetPrimaryDevice();
+ }
+ else {
+ esyslog("[ffnetdev] Streamer: Couldn't accept : %s", strerror(errno));
+ have_Streamclient = false;
+ m_Active = false;
+ continue;
+ }
+ }
+
+
+ /* Check for closed streaming client connection */
+ if (have_Streamclient==true) {
+ if (close_Streamclient_request==true) {
+ close_Streamclient_request = false;
+ have_Streamclient = false;
+
+ m_pPlugin->RestorePrimaryDevice();
+
+ if ( m_StreamClient->Close() ) {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] Streamer: Client socket closed successfully.\n");
+#endif
+ isyslog("[ffnetdev] Streamer: Connection closed: client %s:%d",
+ m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort());
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] Streamer: Error closing client socket.\n");
+#endif
+ esyslog("[ffnetdev] Streamer: Error closing connection.");
+ m_Active=false;
+ continue;
+ }
+
+ }
+
+ if ( select.CanWrite(*m_StreamClient) ) {
+ int count=0;
+
+ m_StreamDevice->LockOutput();
+ uchar *buffer = m_StreamDevice->Get(count);
+ if (buffer!=NULL) {
+ int available = count;
+ int done = 0;
+ int written = 0;
+ while ((available > 0) && (have_Streamclient == true) &&
+ (!close_Streamclient_request))
+ {
+
+ if (((written=m_StreamClient->Write(&buffer[done], available)) < 0) &&
+ (errno != EAGAIN))
+ {
+ CloseStreamClient();
+ }
+
+ if (written > 0)
+ {
+
+ available -= written;
+ done += written;
+ }
+ else
+ {
+ cCondWait::SleepMs(5);
+ }
+ }
+ m_StreamDevice->Del(count);
+
+ struct timeval curtime;
+ gettimeofday(&curtime, 0);
+ if (oldtime.tv_sec == 0)
+ {
+ oldtime = curtime;
+ bytessend = 0;
+ oldbytessend = 0;
+ }
+
+ bytessend += count;
+ if (curtime.tv_sec > oldtime.tv_sec + 10)
+ {
+ double secs = (curtime.tv_sec * 1000 + (curtime.tv_usec / 1000)) / 1000
+ - (oldtime.tv_sec * 1000 + (oldtime.tv_usec / 1000)) / 1000;
+#ifdef DEBUG
+ fprintf(stderr, "[ffnetdev] Streamer: current TransferRate %d Byte/Sec, %d Bytes send\n",
+ (int)((bytessend - oldbytessend) / secs), bytessend - oldbytessend);
+#endif
+ dsyslog("[ffnetdev] Streamer: current TransferRate %d Byte/Sec, %d Bytes send\n",
+ (int)((bytessend - oldbytessend) / secs), bytessend - oldbytessend);
+
+ oldbytessend = bytessend;
+ oldtime = curtime;
+ }
+ }
+ m_StreamDevice->UnlockOutput();
+ }
+
+ if ( select.CanRead(*m_StreamClient) )
+ if ( m_StreamClient->Read(NULL, 1)==0 )
+ CloseStreamClient();
+ }
+ else {
+ /* simply discard all data in ringbuffer */
+ int count=0;
+ if ( (m_StreamDevice->Get(count)) !=NULL ) {
+ m_StreamDevice->Del(count);
+#ifdef DEBUG
+ fprintf (stderr, "[ffnetdev] Streamer: Bytes not sent, but deleted from ringbuffer: %d\n",count);
+#endif
+ dsyslog("[ffnetdev] Streamer: Bytes not sent, but deleted from ringbuffer: %d\n",count);
+ }
+ }
+ cCondWait::SleepMs(3);
+
+ } // while(m_Active)
+
+}
+
diff --git a/tsworker.h b/tsworker.h
new file mode 100644
index 0000000..45e13ab
--- /dev/null
+++ b/tsworker.h
@@ -0,0 +1,56 @@
+/*
+ * tsworker.h: TS worker thread
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _TSWORKER__H
+#define _TSWORKER__H
+
+#include <vdr/thread.h>
+
+#include "tools/socket.h"
+#include "streamdevice.h"
+#include "remote.h"
+#include "netosd.h"
+#include "ffnetdev.h"
+
+
+// --- cTSWorker -------------------------------------------------------------
+
+class cTSWorker : public cThread {
+private:
+ bool m_Active;
+ bool have_Streamclient;
+ bool close_Streamclient_request;
+ static cTSWorker *m_Instance;
+ cStreamDevice *m_StreamDevice;
+ cTBSocket *m_StreamClient;
+ int TSPort;
+ int origPrimaryDevice;
+ cPluginFFNetDev *m_pPlugin;
+
+protected:
+ virtual void Action(void);
+ void Stop(void);
+public:
+ cTSWorker(void);
+ virtual ~cTSWorker();
+
+ static void Init(cStreamDevice*, int, cPluginFFNetDev*);
+ static void Exit(void);
+ static bool Active(void);
+
+ static bool HaveStreamClient(void);
+ static void CloseStreamClient(void);
+};
+
+inline bool cTSWorker::Active(void) {
+ return m_Instance && (m_Instance->have_Streamclient==true);
+}
+
+inline bool cTSWorker::HaveStreamClient(void) {
+ return m_Instance->have_Streamclient;
+}
+#endif
diff --git a/vncEncodeCoRRE.c b/vncEncodeCoRRE.c
new file mode 100644
index 0000000..af398f3
--- /dev/null
+++ b/vncEncodeCoRRE.c
@@ -0,0 +1,536 @@
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeCoRRE
+
+// This file implements the vncEncoder-derived vncEncodeCoRRE class.
+// This class overrides some vncEncoder functions to produce a
+// Compact RRE encoder. Compact RRE (CoRRE) uses fewer bytes to
+// encode each subrect, which makes it faster in general. It also
+// splits large rectangles up into ones of at most 256 pixels width
+// & height. This results in better granularity to use for deciding
+// whether to send RAW or CoRRE/RRE.
+
+#include "vncEncodeCoRRE.h"
+#include "rfb.h"
+#include "MinMax.h"
+#include <stdlib.h>
+#include <time.h>
+
+vncEncodeCoRRE::vncEncodeCoRRE()
+{
+ m_buffer = NULL;
+ m_bufflen = 0;
+
+ // Set some sensible defaults
+ m_maxwidth = 24;
+ m_maxheight = 24;
+ m_maxadjust = 1;
+
+ // Set the threshold up/down probability
+ m_threshold = 50;
+
+ // Seed the random number generator
+ srand((unsigned)time(NULL));
+
+ m_statsready = false;
+ m_encodedbytes = 0;
+ m_rectbytes = 0;
+}
+
+vncEncodeCoRRE::~vncEncodeCoRRE()
+{
+ if (m_buffer != NULL)
+ {
+ delete [] m_buffer;
+ m_buffer = NULL;
+ }
+}
+
+void vncEncodeCoRRE::Init()
+{
+ vncEncoder::Init();
+}
+
+UINT vncEncodeCoRRE::RequiredBuffSize(UINT width, UINT height)
+{
+ RECT fullscreen;
+ UINT codedrects;
+
+ // Work out how many rectangles the entire screen would
+ // be re-encoded to...
+ fullscreen.left = 0;
+ fullscreen.top = 0;
+ fullscreen.right = width;
+ fullscreen.bottom = height;
+ codedrects = NumCodedRects(fullscreen);
+
+ // The buffer size required is the size of raw data for the whole
+ // screen plus enough space for the required number of rectangle
+ // headers.
+ // This is inherently always greater than the RAW encoded size of
+ // the whole screen!
+ return (codedrects * sz_rfbFramebufferUpdateRectHeader) +
+ (width * height * m_remoteformat.bitsPerPixel)/8;
+}
+
+UINT
+vncEncodeCoRRE::NumCodedRects(RECT &rect)
+{
+ // If we have any statistical data handy then adjust the CoRRE sizes
+ if (m_statsready)
+ {
+ m_statsready = false;
+
+ UINT newscore = m_encodedbytes * m_lastrectbytes;
+ UINT oldscore = m_lastencodedbytes * m_rectbytes;
+
+ if (newscore <= oldscore)
+ {
+ // The change was a good one, so adjust the threshold accordingly!
+ m_threshold = Max(5, Min(95, m_threshold + m_maxadjust));
+
+ m_maxwidth = Max(8, Min(255, m_maxwidth + m_maxadjust));
+ m_maxheight = Max(8, Min(255, m_maxheight + m_maxadjust));
+ }
+ else
+ {
+ // The change was a bad one, so adjust the threshold accordingly!
+ // m_threshold = Max(5, Min(95, m_threshold - m_maxadjust));
+ }
+
+ // Now calculate a new adjustment and apply it
+ m_maxadjust = ((rand() % 99)<m_threshold) ? 1 : -1;
+
+ // Prepare the stats data for next time...
+ m_lastencodedbytes = m_encodedbytes;
+ m_lastrectbytes = m_rectbytes;
+
+ m_encodedbytes = 0;
+ m_rectbytes = 0;
+ }
+
+ // Now return the number of rects that this one would encode to
+ if ((rect.bottom-rect.top) > m_maxheight)
+ {
+ RECT subrect1, subrect2;
+
+ // Find how many rects the two subrects would take
+ subrect1.left = rect.left;
+ subrect1.right = rect.right;
+ subrect1.top = rect.top;
+ subrect1.bottom = rect.top + m_maxheight;
+
+ subrect2.left = rect.left;
+ subrect2.right = rect.right;
+ subrect2.top = rect.top + m_maxheight;
+ subrect2.bottom = rect.bottom;
+
+ return NumCodedRects(subrect1) + NumCodedRects(subrect2);
+ }
+
+ if ((rect.right-rect.left) > m_maxwidth)
+ {
+ RECT subrect1, subrect2;
+
+ // Find how many rects the two subrects would take
+ subrect1.left = rect.left;
+ subrect1.right = rect.left + m_maxwidth;
+ subrect1.top = rect.top;
+ subrect1.bottom = rect.bottom;
+
+ subrect2.left = rect.left + m_maxwidth;
+ subrect2.right = rect.right;
+ subrect2.top = rect.top;
+ subrect2.bottom = rect.bottom;
+ return NumCodedRects(subrect1) + NumCodedRects(subrect2);
+ }
+
+ // This rectangle is small enough not to require splitting
+ return 1;
+}
+
+/*
+ * corre.c
+ *
+ * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This
+ * code is based on krw's original javatel rfbserver.
+ */
+
+/*
+ * This version modified for WinVNC by jnw.
+ */
+
+static int rreAfterBufLen;
+
+static int subrectEncode8 (CARD8 *source, CARD8 *dest, int w, int h, int max);
+static int subrectEncode16 (CARD16 *source, CARD8 *dest, int w, int h, int max);
+static int subrectEncode32 (CARD32 *source, CARD8 *dest, int w, int h, int max);
+static CARD32 getBgColour (char *data, int size, int bpp);
+
+/*
+ * vncEncodeCoRRE::EncodeRect - send an arbitrary size rectangle using CoRRE
+ * encoding.
+ */
+
+UINT
+vncEncodeCoRRE::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ // Do the encoding
+ UINT size = InternalEncodeRect(source, dest, rect);
+
+ const int rectW = rect.right - rect.left;
+ const int rectH = rect.bottom - rect.top;
+
+ // Will this rectangle have been split for encoding?
+ if ((rectW>m_maxwidth) || (rectH>m_maxheight))
+ {
+ // Yes : Once we return, the stats will be valid!
+ m_statsready = true;
+
+ // Update the stats
+ m_encodedbytes += size;
+ m_rectbytes += sz_rfbFramebufferUpdateRectHeader +
+ (rectW*rectH*m_remoteformat.bitsPerPixel/8);
+ }
+
+ return size;
+}
+
+UINT
+vncEncodeCoRRE::InternalEncodeRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ int size = 0;
+
+ if ((rect.bottom-rect.top) > m_maxheight)
+ {
+ RECT subrect;
+
+ // Rectangle is too high - split it into two subrects to send
+ subrect.left = rect.left;
+ subrect.right = rect.right;
+ subrect.top = rect.top;
+ subrect.bottom = rect.top + m_maxheight;
+ size += InternalEncodeRect(source, dest + size, subrect);
+
+ subrect.left = rect.left;
+ subrect.right = rect.right;
+ subrect.top = rect.top + m_maxheight;
+ subrect.bottom = rect.bottom;
+ size += InternalEncodeRect(source, dest + size, subrect);
+
+ return size;
+ }
+
+ if ((rect.right-rect.left) > m_maxwidth)
+ {
+ RECT subrect;
+
+ // Rectangle is too high - split it into two subrects to send
+ subrect.left = rect.left;
+ subrect.right = rect.left + m_maxwidth;
+ subrect.top = rect.top;
+ subrect.bottom = rect.bottom;
+ size += InternalEncodeRect(source, dest + size, subrect);
+
+ subrect.left = rect.left + m_maxwidth;
+ subrect.right = rect.right;
+ subrect.top = rect.top;
+ subrect.bottom = rect.bottom;
+ size += InternalEncodeRect(source, dest + size, subrect);
+
+ return size;
+ }
+
+ return EncodeSmallRect(source, dest, rect);
+}
+
+void
+vncEncodeCoRRE::SetCoRREMax(BYTE width, BYTE height)
+{
+ m_maxwidth = width;
+ m_maxheight = height;
+}
+
+/*
+ * EncodeSmallRect - send a small (guaranteed < 256x256)
+ * rectangle using CoRRE encoding.
+ */
+
+UINT
+vncEncodeCoRRE::EncodeSmallRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ int subrects = -1;
+
+ const UINT rectW = rect.right - rect.left;
+ const UINT rectH = rect.bottom - rect.top;
+
+ // Create the rectangle header
+ rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest;
+ surh->r.x = (CARD16) rect.left;
+ surh->r.y = (CARD16) rect.top;
+ surh->r.w = (CARD16) (rectW);
+ surh->r.h = (CARD16) (rectH);
+ surh->r.x = Swap16IfLE(surh->r.x);
+ surh->r.y = Swap16IfLE(surh->r.y);
+ surh->r.w = Swap16IfLE(surh->r.w);
+ surh->r.h = Swap16IfLE(surh->r.h);
+ surh->encoding = Swap32IfLE(rfbEncodingCoRRE);
+
+ // create a space big enough for the CoRRE encoded pixels
+
+ size_t rectSize = rectW * rectH * (m_remoteformat.bitsPerPixel / 8);
+ if (m_bufflen < rectSize)
+ {
+ if (m_buffer != NULL)
+ {
+ delete [] m_buffer;
+ m_buffer = NULL;
+ }
+ m_buffer = new BYTE [rectSize + 1];
+ if (m_buffer == NULL)
+ return vncEncoder::EncodeRect(source, dest, rect);
+
+ m_bufflen = rectSize;
+ }
+
+ // Translate the data into our new buffer
+ Translate(source, m_buffer, rect);
+
+ // The Buffer object will have ensured that the destination buffer is
+ // big enough using RequiredBuffSize
+
+ // Choose the appropriate encoding routine (for speed...)
+ switch(m_remoteformat.bitsPerPixel)
+ {
+ case 8:
+ subrects = subrectEncode8(
+ m_buffer,
+ dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader,
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ case 16:
+ subrects = subrectEncode16(
+ (CARD16 *)m_buffer,
+ (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader),
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ case 32:
+ subrects = subrectEncode32(
+ (CARD32 *)m_buffer,
+ (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader),
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ }
+
+ // If we couldn't encode the rectangles then just send the data raw
+ if (subrects < 0)
+ return vncEncoder::EncodeRect(source, dest, rect);
+
+ // Send the RREHeader
+ rfbRREHeader *rreh=(rfbRREHeader *)(dest+sz_rfbFramebufferUpdateRectHeader);
+ rreh->nSubrects = Swap32IfLE(subrects);
+
+ // Update the statistics for this rectangle.
+ encodedSize += sz_rfbRREHeader + rreAfterBufLen;
+ rectangleOverhead += sz_rfbFramebufferUpdateRectHeader;
+ dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen;
+
+ // Calculate the size of the buffer produced
+ return sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen;
+}
+
+/*
+ * subrectEncode() encodes the given multicoloured rectangle as a background
+ * colour overwritten by single-coloured rectangles. It returns the number
+ * of subrectangles in the encoded buffer, or -1 if subrect encoding won't
+ * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The
+ * single-colour rectangle partition is not optimal, but does find the biggest
+ * horizontal or vertical rectangle top-left anchored to each consecutive
+ * coordinate position.
+ *
+ * The coding scheme is simply [<bgcolour><subrect><subrect>...] where each
+ * <subrect> is [<colour><x><y><w><h>].
+ */
+
+#define DEFINE_SUBRECT_ENCODE(bpp) \
+static int \
+subrectEncode##bpp( \
+ CARD##bpp *source, \
+ CARD8 *dest, \
+ int w, \
+ int h, \
+ int maxbytes) \
+{ \
+ CARD##bpp cl; \
+ rfbCoRRERectangle subrect; \
+ int x,y; \
+ int i,j; \
+ int hx=0,hy,vx=0,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ CARD##bpp bg = (CARD##bpp)getBgColour((char*)source,w*h,bpp); \
+ \
+ *((CARD##bpp*)dest) = bg; \
+ \
+ rreAfterBufLen = (bpp/8); \
+ \
+ for (y=0; y<h; y++) { \
+ line = source+(y*w); \
+ for (x=0; x<w; x++) { \
+ if (line[x] != bg) { \
+ cl = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) { \
+ seg = source+(j*w); \
+ if (seg[x] != cl) {break;} \
+ i = x; \
+ while ((seg[i] == cl) && (i < w)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \
+ * We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) { \
+ thew = hw; \
+ theh = hh; \
+ } else { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ subrect.x = thex; \
+ subrect.y = they; \
+ subrect.w = thew; \
+ subrect.h = theh; \
+ \
+ newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \
+ if ((newLen > (w * h * (bpp/8))) || (newLen > maxbytes)) \
+ return -1; \
+ \
+ numsubs += 1; \
+ *((CARD##bpp*)(dest + rreAfterBufLen)) = cl; \
+ rreAfterBufLen += (bpp/8); \
+ memcpy(&dest[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \
+ rreAfterBufLen += sz_rfbCoRRERectangle; \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) { \
+ for (i=thex; i < (thex+thew); i++) { \
+ source[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ return numsubs; \
+}
+
+DEFINE_SUBRECT_ENCODE(8)
+DEFINE_SUBRECT_ENCODE(16)
+DEFINE_SUBRECT_ENCODE(32)
+
+/*
+ * getBgColour() gets the most prevalent colour in a byte array.
+ */
+static CARD32
+getBgColour(
+ char *data,
+ int size,
+ int bpp)
+{
+
+#define NUMCLRS 256
+
+ static int counts[NUMCLRS];
+ int i,j,k;
+
+ int maxcount = 0;
+ CARD8 maxclr = 0;
+
+ if (bpp != 8) {
+ if (bpp == 16) {
+ return ((CARD16 *)data)[0];
+ } else if (bpp == 32) {
+ return ((CARD32 *)data)[0];
+ } else {
+ fprintf(stderr,"getBgColour: bpp %d?\n",bpp);
+ exit(1);
+ }
+ }
+
+ for (i=0; i<NUMCLRS; i++) {
+ counts[i] = 0;
+ }
+
+ for (j=0; j<size; j++) {
+ k = (int)(((CARD8 *)data)[j]);
+ if (k >= NUMCLRS) {
+ fprintf(stderr, "%s: unusual colour = %d\n", "getBgColour",k);
+ exit(1);
+ }
+ counts[k] += 1;
+ if (counts[k] > maxcount) {
+ maxcount = counts[k];
+ maxclr = ((CARD8 *)data)[j];
+ }
+ }
+
+ return maxclr;
+}
diff --git a/vncEncodeCoRRE.h b/vncEncodeCoRRE.h
new file mode 100644
index 0000000..6fbd752
--- /dev/null
+++ b/vncEncodeCoRRE.h
@@ -0,0 +1,84 @@
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeCoRRE object
+
+// The vncEncodeCoRRE object uses a compression encoding to send rectangles
+// to a client
+
+class vncEncodeCoRRE;
+
+#if !defined(_WINVNC_ENCODECORRRE)
+#define _WINVNC_ENCODECORRE
+#pragma once
+
+#include "vncEncoder.h"
+#include "vdr/plugin.h"
+
+// Class definition
+
+class vncEncodeCoRRE : public vncEncoder
+{
+// Fields
+public:
+
+// Methods
+public:
+ // Create/Destroy methods
+ vncEncodeCoRRE();
+ ~vncEncodeCoRRE();
+
+ virtual void Init();
+ virtual const char* GetEncodingName() { return "CoRRE"; }
+
+ virtual UINT RequiredBuffSize(UINT width, UINT height);
+ virtual UINT NumCodedRects(RECT &rect);
+
+ virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect);
+ virtual void SetCoRREMax(BYTE width, BYTE height);
+protected:
+ virtual UINT InternalEncodeRect(BYTE *source, BYTE *dest, const RECT &rect);
+ virtual UINT EncodeSmallRect(BYTE *source, BYTE *dest, const RECT &rect);
+
+// Implementation
+protected:
+ BYTE *m_buffer;
+ size_t m_bufflen;
+
+ // Maximum height & width for CoRRE
+ int m_maxwidth;
+ int m_maxheight;
+
+ // Last-update stats for CoRRE
+ UINT m_encodedbytes, m_rectbytes;
+ UINT m_lastencodedbytes, m_lastrectbytes;
+ int m_maxadjust;
+ int m_threshold;
+ bool m_statsready;
+};
+
+#endif // _WINVNC_ENCODECORRE
+
diff --git a/vncEncodeHexT.c b/vncEncodeHexT.c
new file mode 100644
index 0000000..ce7c3f0
--- /dev/null
+++ b/vncEncodeHexT.c
@@ -0,0 +1,412 @@
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeHexT
+
+// This file implements the vncEncoder-derived vncEncodeHexT class.
+// This class overrides some vncEncoder functions to produce a
+// Hextile encoder. Hextile splits all top-level update rectangles
+// into smaller, 16x16 rectangles and encodes these using the
+// optimised Hextile sub-encodings.
+
+#include "vncEncodeHexT.h"
+#include "rfb.h"
+#include "MinMax.h"
+#include <stdlib.h>
+#include <time.h>
+
+vncEncodeHexT::vncEncodeHexT()
+{
+}
+
+vncEncodeHexT::~vncEncodeHexT()
+{
+}
+
+void
+vncEncodeHexT::Init()
+{
+ vncEncoder::Init();
+}
+
+UINT
+vncEncodeHexT::RequiredBuffSize(UINT width, UINT height)
+{
+ return vncEncoder::RequiredBuffSize(width, height) + (((width/16)+1) * ((height/16)+1));
+}
+
+UINT
+vncEncodeHexT::NumCodedRects(RECT &rect)
+{
+ return 1;
+}
+
+/*
+ * hextile.c
+ *
+ * Routines to implement Hextile Encoding
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+/*
+ * vncEncodeHexT::EncodeRect - send a rectangle using hextile encoding.
+ */
+
+UINT
+vncEncodeHexT::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ const UINT rectW = rect.right - rect.left;
+ const UINT rectH = rect.bottom - rect.top;
+ int encodedResult;
+
+ // Create the rectangle header
+ rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest;
+ surh->r.x = (CARD16) rect.left;
+ surh->r.y = (CARD16) rect.top;
+ surh->r.w = (CARD16) (rectW);
+ surh->r.h = (CARD16) (rectH);
+ surh->r.x = Swap16IfLE(surh->r.x);
+ surh->r.y = Swap16IfLE(surh->r.y);
+ surh->r.w = Swap16IfLE(surh->r.w);
+ surh->r.h = Swap16IfLE(surh->r.h);
+ surh->encoding = Swap32IfLE(rfbEncodingHextile);
+
+ rectangleOverhead += sz_rfbFramebufferUpdateRectHeader;
+ dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+
+ // Do the encoding
+ switch (m_remoteformat.bitsPerPixel)
+ {
+ case 8:
+ encodedResult = EncodeHextiles8(source, dest + sz_rfbFramebufferUpdateRectHeader,
+ rect.left, rect.top, rectW, rectH);
+ encodedSize += encodedResult;
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ return sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ case 16:
+ encodedResult = EncodeHextiles16(source, dest + sz_rfbFramebufferUpdateRectHeader,
+ rect.left, rect.top, rectW, rectH);
+ encodedSize += encodedResult;
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ return sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ case 32:
+ encodedResult = EncodeHextiles32(source, dest + sz_rfbFramebufferUpdateRectHeader,
+ rect.left, rect.top, rectW, rectH);
+ encodedSize += encodedResult;
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ return sz_rfbFramebufferUpdateRectHeader + encodedResult;
+ }
+
+ return vncEncoder::EncodeRect(source, dest, rect);
+}
+
+#define PUT_PIXEL8(pix) (dest[destoffset++] = (pix))
+
+#define PUT_PIXEL16(pix) (dest[destoffset++] = ((char*)&(pix))[0], \
+ dest[destoffset++] = ((char*)&(pix))[1])
+
+#define PUT_PIXEL32(pix) (dest[destoffset++] = ((char*)&(pix))[0], \
+ dest[destoffset++] = ((char*)&(pix))[1], \
+ dest[destoffset++] = ((char*)&(pix))[2], \
+ dest[destoffset++] = ((char*)&(pix))[3])
+
+#define DEFINE_SEND_HEXTILES(bpp) \
+ \
+static UINT subrectEncode##bpp(CARD##bpp *src, BYTE *dest, \
+ int w, int h, CARD##bpp bg, \
+ CARD##bpp fg, bool mono); \
+static void testColours##bpp(CARD##bpp *data, int size, bool *mono, \
+ bool *solid, CARD##bpp *bg, CARD##bpp *fg); \
+ \
+ \
+/* \
+ * rfbSendHextiles \
+ */ \
+ \
+UINT \
+vncEncodeHexT::EncodeHextiles##bpp(BYTE *source, BYTE *dest, \
+ int rx, int ry, int rw, int rh) \
+{ \
+ int x, y, w, h; \
+ int rectoffset, destoffset; \
+ CARD##bpp bg, fg, newBg, newFg; \
+ bool mono, solid; \
+ bool validBg = false; \
+ CARD##bpp clientPixelData[16*16*(bpp/8)]; \
+ bool validFg = false; \
+ \
+ destoffset = 0; \
+ \
+ for (y = ry; y < ry+rh; y += 16) \
+ { \
+ for (x = rx; x < rx+rw; x += 16) \
+ { \
+ w = h = 16; \
+ if (rx+rw - x < 16) \
+ w = rx+rw - x; \
+ if (ry+rh - y < 16) \
+ h = ry+rh - y; \
+ \
+ RECT hexrect; \
+ hexrect.left = x; \
+ hexrect.top = y; \
+ hexrect.right = x+w; \
+ hexrect.bottom = y+h; \
+ Translate(source, (BYTE *) &clientPixelData, hexrect); \
+ \
+ rectoffset = destoffset; \
+ dest[rectoffset] = 0; \
+ destoffset++; \
+ \
+ testColours##bpp(clientPixelData, w * h, \
+ &mono, &solid, &newBg, &newFg); \
+ \
+ if (!validBg || (newBg != bg)) \
+ { \
+ validBg = true; \
+ bg = newBg; \
+ dest[rectoffset] |= rfbHextileBackgroundSpecified; \
+ PUT_PIXEL##bpp(bg); \
+ } \
+ \
+ if (solid) \
+ continue; \
+ \
+ dest[rectoffset] |= rfbHextileAnySubrects; \
+ \
+ if (mono) \
+ { \
+ if (!validFg || (newFg != fg)) \
+ { \
+ validFg = true; \
+ fg = newFg; \
+ dest[rectoffset] |= rfbHextileForegroundSpecified; \
+ PUT_PIXEL##bpp(fg); \
+ } \
+ } \
+ else \
+ { \
+ validFg = false; \
+ dest[rectoffset] |= rfbHextileSubrectsColoured; \
+ } \
+ \
+ int encodedbytes = subrectEncode##bpp(clientPixelData, \
+ dest + destoffset, \
+ w, h, bg, fg, mono); \
+ destoffset += encodedbytes; \
+ if (encodedbytes == 0) \
+ { \
+ /* encoding was too large, use raw */ \
+ validBg = false; \
+ validFg = false; \
+ destoffset = rectoffset; \
+ dest[destoffset++] = rfbHextileRaw; \
+ \
+ Translate(source, (BYTE *) (dest + destoffset), hexrect); \
+ \
+ destoffset += w * h * (bpp/8); \
+ } \
+ } \
+ } \
+ \
+ return destoffset; \
+} \
+ \
+static UINT \
+subrectEncode##bpp(CARD##bpp *src, BYTE *dest, int w, int h, CARD##bpp bg, \
+ CARD##bpp fg, bool mono) \
+{ \
+ CARD##bpp cl; \
+ int x,y; \
+ int i,j; \
+ int hx=0,hy,vx=0,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ int rectoffset; \
+ int destoffset; \
+ \
+ destoffset = 0; \
+ rectoffset = destoffset; \
+ destoffset++; \
+ \
+ for (y=0; y<h; y++) \
+ { \
+ line = src+(y*w); \
+ for (x=0; x<w; x++) \
+ { \
+ if (line[x] != bg) \
+ { \
+ cl = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) \
+ { \
+ seg = src+(j*w); \
+ if (seg[x] != cl) {break;} \
+ i = x; \
+ while ((seg[i] == cl) && (i < w)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) \
+ { \
+ hy += 1; \
+ } \
+ else \
+ { \
+ hyflag = 0; \
+ } \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and \
+ * (x,y,vx,vy). We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) \
+ { \
+ thew = hw; \
+ theh = hh; \
+ } \
+ else \
+ { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ if (mono) \
+ { \
+ newLen = destoffset - rectoffset + 2; \
+ } \
+ else \
+ { \
+ newLen = destoffset - rectoffset + bpp/8 + 2; \
+ } \
+ \
+ if (newLen > (w * h * (bpp/8))) \
+ return 0; \
+ \
+ numsubs += 1; \
+ \
+ if (!mono) PUT_PIXEL##bpp(cl); \
+ \
+ dest[destoffset++] = rfbHextilePackXY(thex,they); \
+ dest[destoffset++] = rfbHextilePackWH(thew,theh); \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) \
+ { \
+ for (i=thex; i < (thex+thew); i++) \
+ { \
+ src[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ dest[rectoffset] = numsubs; \
+ \
+ return destoffset; \
+} \
+ \
+ \
+/* \
+ * testColours() tests if there are one (solid), two (mono) or more \
+ * colours in a tile and gets a reasonable guess at the best background \
+ * pixel, and the foreground pixel for mono. \
+ */ \
+ \
+static void \
+testColours##bpp(CARD##bpp *data, int size, \
+ bool *mono, bool *solid, \
+ CARD##bpp *bg, CARD##bpp *fg) \
+{ \
+ CARD##bpp colour1, colour2; \
+ int n1 = 0, n2 = 0; \
+ *mono = true; \
+ *solid = true; \
+ \
+ for (; size > 0; size--, data++) \
+ { \
+ \
+ if (n1 == 0) \
+ colour1 = *data; \
+ \
+ if (*data == colour1) \
+ { \
+ n1++; \
+ continue; \
+ } \
+ \
+ if (n2 == 0) \
+ { \
+ *solid = false; \
+ colour2 = *data; \
+ } \
+ \
+ if (*data == colour2) \
+ { \
+ n2++; \
+ continue; \
+ } \
+ \
+ *mono = false; \
+ break; \
+ } \
+ \
+ if (n1 > n2) \
+ { \
+ *bg = colour1; \
+ *fg = colour2; \
+ } \
+ else \
+ { \
+ *bg = colour2; \
+ *fg = colour1; \
+ } \
+}
+
+DEFINE_SEND_HEXTILES(8)
+DEFINE_SEND_HEXTILES(16)
+DEFINE_SEND_HEXTILES(32)
diff --git a/vncEncodeHexT.h b/vncEncodeHexT.h
new file mode 100644
index 0000000..828934a
--- /dev/null
+++ b/vncEncodeHexT.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeHexT object
+
+// The vncEncodeHexT object uses a compression encoding to send rectangles
+// to a client
+
+class vncEncodeHexT;
+
+#if !defined(_WINVNC_ENCODEHEXTILE)
+#define _WINVNC_ENCODEHEXTILE
+#pragma once
+
+#include "vncEncoder.h"
+
+// Class definition
+
+class vncEncodeHexT : public vncEncoder
+{
+// Fields
+public:
+
+// Methods
+public:
+ // Create/Destroy methods
+ vncEncodeHexT();
+ ~vncEncodeHexT();
+
+ virtual void Init();
+ virtual const char* GetEncodingName() { return "Hextile"; }
+
+ virtual UINT RequiredBuffSize(UINT width, UINT height);
+ virtual UINT NumCodedRects(RECT &rect);
+
+ virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect);
+
+protected:
+ virtual UINT EncodeHextiles8(BYTE *source, BYTE *dest,
+ int x, int y, int w, int h);
+ virtual UINT EncodeHextiles16(BYTE *source, BYTE *dest,
+ int x, int y, int w, int h);
+ virtual UINT EncodeHextiles32(BYTE *source, BYTE *dest,
+ int x, int y, int w, int h);
+
+// Implementation
+protected:
+};
+
+#endif // _WINVNC_ENCODEHEXTILE
+
diff --git a/vncEncodeRRE.c b/vncEncodeRRE.c
new file mode 100644
index 0000000..3a9d44e
--- /dev/null
+++ b/vncEncodeRRE.c
@@ -0,0 +1,349 @@
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeRRE
+
+// This file implements the vncEncoder-derived vncEncodeRRE class.
+// This class overrides some vncEncoder functions to produce a bitmap
+// to RRE encoder. RRE is much more efficient than RAW format on
+// most screen data.
+
+#include <vdr/tools.h>
+#include <sys/types.h>
+#include "vncEncodeRRE.h"
+
+vncEncodeRRE::vncEncodeRRE()
+{
+ m_buffer = NULL;
+ m_bufflen = 0;
+}
+
+vncEncodeRRE::~vncEncodeRRE()
+{
+ if (m_buffer != NULL)
+ {
+ delete [] m_buffer;
+ m_buffer = NULL;
+ }
+}
+
+void
+vncEncodeRRE::Init()
+{
+ vncEncoder::Init();
+}
+
+UINT
+vncEncodeRRE::RequiredBuffSize(UINT width, UINT height)
+{
+ return vncEncoder::RequiredBuffSize(width, height);
+}
+
+UINT
+vncEncodeRRE::NumCodedRects(RECT &rect)
+{
+ return 1;
+}
+
+/*****************************************************************************
+ *
+ * Routines to implement Rise-and-Run-length Encoding (RRE). This code is
+ * based on krw's original javatel rfbserver.
+ * This code courtesy of tjr
+ */
+
+/*
+ * rreBeforeBuf contains pixel data in the client's format.
+ * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is
+ * larger than the raw data or if it exceeds rreAfterBufSize then
+ * normal encoding is used instead.
+ */
+
+static int rreAfterBufLen;
+
+static int subrectEncode8 (CARD8 *data, CARD8 *buf, int w, int h, int maxBytes);
+static int subrectEncode16 (CARD16 *data, CARD8 *buf, int w, int h, int maxBytes);
+static int subrectEncode32 (CARD32 *data, CARD8 *buf, int w, int h, int maxBytes);
+static CARD32 getBgColour (char *data, int size, int bpp);
+
+/*
+ * subrectEncode() encodes the given multicoloured rectangle as a background
+ * colour overwritten by single-coloured rectangles. It returns the number
+ * of subrectangles in the encoded buffer, or -1 if subrect encoding won't
+ * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The
+ * single-colour rectangle partition is not optimal, but does find the biggest
+ * horizontal or vertical rectangle top-left anchored to each consecutive
+ * coordinate position.
+ *
+ * The coding scheme is simply [<bgcolour><subrect><subrect>...] where each
+ * <subrect> is [<colour><x><y><w><h>].
+ *
+ * This code has been modified from tjr's original by Wez(jnw)
+ */
+
+#define DEFINE_SUBRECT_ENCODE(bpp) \
+static int \
+subrectEncode##bpp( \
+ CARD##bpp *data, \
+ CARD8 *buf, \
+ int w, \
+ int h, \
+ int maxBytes \
+ ) \
+{ \
+ CARD##bpp cl; \
+ rfbRectangle subrect; \
+ int x,y; \
+ int i,j; \
+ int hx,hy,vx,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \
+ \
+ /* Set the background colour value */ \
+ *((CARD##bpp *)buf) = bg; \
+ \
+ rreAfterBufLen = (bpp/8); \
+ \
+ for (y=0; y<h; y++) { \
+ line = data+(y*w); \
+ for (x=0; x<w; x++) { \
+ if (line[x] != bg) { \
+ cl = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) { \
+ seg = data+(j*w); \
+ if (seg[x] != cl) {break;} \
+ i = x; \
+ while ((i < w) && (seg[i] == cl)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \
+ * We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) { \
+ thew = hw; \
+ theh = hh; \
+ } else { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ subrect.x = Swap16IfLE(thex); \
+ subrect.y = Swap16IfLE(they); \
+ subrect.w = Swap16IfLE(thew); \
+ subrect.h = Swap16IfLE(theh); \
+ \
+ newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \
+ if ((newLen > (w * h * (bpp/8))) || (newLen > maxBytes)) \
+ return -1; \
+ \
+ numsubs += 1; \
+ *((CARD##bpp *)(buf + rreAfterBufLen)) = cl; \
+ rreAfterBufLen += (bpp/8); \
+ memcpy(&buf[rreAfterBufLen],&subrect, sz_rfbRectangle); \
+ rreAfterBufLen += sz_rfbRectangle; \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) { \
+ for (i=thex; i < (thex+thew); i++) { \
+ data[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ return numsubs; \
+}
+
+DEFINE_SUBRECT_ENCODE(8)
+DEFINE_SUBRECT_ENCODE(16)
+DEFINE_SUBRECT_ENCODE(32)
+
+/*
+ * getBgColour() gets the most prevalent colour in a byte array.
+ */
+static CARD32
+getBgColour(char *data, int size, int bpp)
+{
+
+#define NUMCLRS 256
+
+ static int counts[NUMCLRS];
+ int i,j,k;
+
+ int maxcount = 0;
+ CARD8 maxclr = 0;
+
+ if (bpp != 8) {
+ if (bpp == 16) {
+ return ((CARD16 *)data)[0];
+ } else if (bpp == 32) {
+ return ((CARD32 *)data)[0];
+ } else {
+ fprintf(stderr,"getBgColour: bpp %d?\n",bpp);
+ return 0;
+ }
+ }
+
+ for (i=0; i<NUMCLRS; i++) {
+ counts[i] = 0;
+ }
+
+ for (j=0; j<size; j++) {
+ k = (int)(((CARD8 *)data)[j]);
+ if (k >= NUMCLRS) {
+ fprintf(stderr, "%s: unusual colour = %d\n", "getBgColour",k);
+ return 0;
+ }
+ counts[k] += 1;
+ if (counts[k] > maxcount) {
+ maxcount = counts[k];
+ maxclr = ((CARD8 *)data)[j];
+ }
+ }
+
+ return maxclr;
+}
+
+// Encode the rectangle using RRE
+inline UINT
+vncEncodeRRE::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ int subrects = -1;
+
+ const UINT rectW = rect.right - rect.left;
+ const UINT rectH = rect.bottom - rect.top;
+
+ // Create the rectangle header
+ rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest;
+ surh->r.x = (CARD16) rect.left;
+ surh->r.y = (CARD16) rect.top;
+ surh->r.w = (CARD16) (rectW);
+ surh->r.h = (CARD16) (rectH);
+ surh->r.x = Swap16IfLE(surh->r.x);
+ surh->r.y = Swap16IfLE(surh->r.y);
+ surh->r.w = Swap16IfLE(surh->r.w);
+ surh->r.h = Swap16IfLE(surh->r.h);
+ surh->encoding = Swap32IfLE(rfbEncodingRRE);
+
+ // create a space big enough for the RRE encoded pixels
+ size_t rectSize = rectW * rectH * (m_remoteformat.bitsPerPixel / 8);
+ if (m_bufflen < rectSize)
+ {
+ if (m_buffer != NULL)
+ {
+ delete [] m_buffer;
+ m_buffer = NULL;
+ }
+ m_buffer = new BYTE [rectSize + 1];
+ if (m_buffer == NULL)
+ return vncEncoder::EncodeRect(source, dest, rect);
+ m_bufflen = rectSize;
+ }
+
+ // Translate the data into our new buffer
+ Translate(source, m_buffer, rect);
+
+ // Choose the appropriate encoding routine (for speed...)
+ switch(m_remoteformat.bitsPerPixel)
+ {
+ case 8:
+ subrects = subrectEncode8(
+ m_buffer,
+ dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader,
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ case 16:
+ subrects = subrectEncode16(
+ (CARD16 *)m_buffer,
+ (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader),
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ case 32:
+ subrects = subrectEncode32(
+ (CARD32 *)m_buffer,
+ (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader),
+ rectW,
+ rectH,
+ m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader
+ );
+ break;
+ }
+
+ // If we couldn't encode the rectangles then just send the data raw
+ if (subrects < 0)
+ return vncEncoder::EncodeRect(source, dest, rect);
+
+ // Send the RREHeader
+ rfbRREHeader *rreh=(rfbRREHeader *)(dest+sz_rfbFramebufferUpdateRectHeader);
+ rreh->nSubrects = Swap32IfLE(subrects);
+
+ // Update statistics for this rectangle.
+ rectangleOverhead += sz_rfbFramebufferUpdateRectHeader;
+ dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+ encodedSize += sz_rfbRREHeader +
+ (m_remoteformat.bitsPerPixel / 8) +
+ (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8));
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader +
+ sz_rfbRREHeader +
+ (m_remoteformat.bitsPerPixel / 8) +
+ (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8));
+
+ // Return the amount of data sent
+ return sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader +
+ (m_remoteformat.bitsPerPixel / 8) +
+ (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8));
+}
diff --git a/vncEncodeRRE.h b/vncEncodeRRE.h
new file mode 100644
index 0000000..f11b131
--- /dev/null
+++ b/vncEncodeRRE.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncodeRRE object
+
+// The vncEncodeRRE object uses a compression encoding to send rectangles
+// to a client
+
+class vncEncodeRRE;
+
+#if !defined(_WINVNC_ENCODERRRE)
+#define _WINVNC_ENCODERRE
+#pragma once
+
+#include "vncEncoder.h"
+
+// Class definition
+
+class vncEncodeRRE : public vncEncoder
+{
+// Fields
+public:
+
+// Methods
+public:
+ // Create/Destroy methods
+ vncEncodeRRE();
+ ~vncEncodeRRE();
+
+ virtual void Init();
+ virtual const char* GetEncodingName() { return "RRE"; }
+
+ virtual UINT RequiredBuffSize(UINT width, UINT height);
+ virtual UINT NumCodedRects(RECT &rect);
+
+ virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect);
+
+// Implementation
+protected:
+ BYTE *m_buffer;
+ size_t m_bufflen;
+};
+
+#endif // _WINVNC_ENCODERRE
+
diff --git a/vncEncoder.c b/vncEncoder.c
new file mode 100644
index 0000000..04827ae
--- /dev/null
+++ b/vncEncoder.c
@@ -0,0 +1,673 @@
+// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncoder - Object used to encode data for RFB
+
+#include "vncEncoder.h"
+
+#include <vdr/plugin.h>
+//#include "vncBuffer.h"
+
+// Pixel format used internally when the client is palette-based & server is truecolour
+
+static const rfbPixelFormat BGR233Format = {
+ 8, 8, 0, 1, 7, 7, 3, 0, 3, 6
+};
+
+// The base (RAW) encoder class
+
+vncEncoder::vncEncoder()
+{
+ memset(&m_remoteformat, 0, sizeof(m_remoteformat));
+ memset(&m_localformat, 0, sizeof(m_localformat));
+ memset(&m_transformat, 0, sizeof(m_transformat));
+ m_transtable = NULL;
+ m_localpalette = NULL;
+ m_bytesPerRow = 0;
+ m_compresslevel = 6;
+ m_qualitylevel = -1;
+ m_use_xcursor = false;
+ m_use_richcursor = false;
+ m_use_lastrect = false;
+}
+
+vncEncoder::~vncEncoder()
+{
+ if (m_transtable != NULL)
+ {
+ free(m_transtable);
+ m_transtable = NULL;
+ }
+ if (m_localpalette != NULL)
+ {
+ free(m_localpalette);
+ m_localpalette = NULL;
+ }
+}
+
+void
+vncEncoder::Init()
+{
+ dataSize = 0;
+ rectangleOverhead = 0;
+ encodedSize = 0;
+ transmittedSize = 0;
+}
+
+void
+vncEncoder::LogStats()
+{
+ fprintf(stderr, "[ffnetdev] VNC: %s encoder stats: data=%d, overhead=%d, "
+ "encoded=%d, sent=%d\n",
+ GetEncodingName(),
+ dataSize, rectangleOverhead, encodedSize, transmittedSize);
+
+ if (dataSize != 0) {
+ fprintf(stderr, "[ffnetdev] VNC: %s encoder efficiency: %.3f%%\n",
+ GetEncodingName(),
+ (double)((double)((dataSize - transmittedSize) * 100) / dataSize));
+ }
+}
+
+UINT
+vncEncoder::RequiredBuffSize(UINT width, UINT height)
+{
+ return sz_rfbFramebufferUpdateRectHeader +
+ (width * height * m_remoteformat.bitsPerPixel)/8;
+}
+
+UINT
+vncEncoder::NumCodedRects(RECT &rect)
+{
+ return 1;
+}
+
+// Translate a rectangle
+inline void
+vncEncoder::Translate(BYTE *source, BYTE *dest, const RECT &rect)
+{
+ // Calculate where in the source rectangle to read from
+ BYTE *sourcepos = (BYTE *)(source + (m_bytesPerRow * rect.top)+(rect.left * (m_localformat.bitsPerPixel / 8)));
+
+ // Call the translation function
+ (*m_transfunc) (m_transtable,
+ &m_localformat,
+ &m_transformat,
+ (char *)sourcepos,
+ (char *)dest,
+ m_bytesPerRow,
+ rect.right-rect.left,
+ rect.bottom-rect.top
+ );
+}
+
+// Translate a rectangle (using arbitrary m_bytesPerRow value,
+// always translating from the beginning of the source pixel array)
+// NOTE: overloaded function!
+inline void
+vncEncoder::Translate(BYTE *source, BYTE *dest, int w, int h, int bytesPerRow)
+{
+ // Call the translation function
+ (*m_transfunc) (m_transtable, &m_localformat, &m_transformat,
+ (char *)source, (char *)dest, bytesPerRow, w, h);
+}
+
+// Encode a rectangle
+inline UINT
+vncEncoder::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect)
+{
+
+ const int rectW = rect.right - rect.left;
+ const int rectH = rect.bottom - rect.top;
+
+
+ // Create the header for the update in the destination area
+ rfbFramebufferUpdateRectHeader *surh = (rfbFramebufferUpdateRectHeader *)dest;
+ surh->r.x = (CARD16) rect.left;
+ surh->r.y = (CARD16) rect.top;
+ surh->r.w = (CARD16) rectW;
+ surh->r.h = (CARD16) rectH;
+ surh->r.x = Swap16IfLE(surh->r.x);
+ surh->r.y = Swap16IfLE(surh->r.y);
+ surh->r.w = Swap16IfLE(surh->r.w);
+ surh->r.h = Swap16IfLE(surh->r.h);
+ surh->encoding = Swap32IfLE(rfbEncodingRaw);
+
+ // Update raw encoding statistics
+ rectangleOverhead += sz_rfbFramebufferUpdateRectHeader;
+ dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+ encodedSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+ transmittedSize += sz_rfbFramebufferUpdateRectHeader + ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8;
+
+ // Translate the data in place in the output buffer
+ Translate(source, dest + sz_rfbFramebufferUpdateRectHeader, rect);
+
+ // Return the buffer size
+ return sz_rfbFramebufferUpdateRectHeader +
+ (rectW*rectH*m_remoteformat.bitsPerPixel) / 8;
+}
+
+// Encode a rectangle directly to the output stream.
+// This implementation may not be the best, but it will work with all
+// of the existing EncodeRect(BYTE *, BYTE *, const RECT &) implementations.
+// Note, that the returned value is that of any data in the dest buffer that
+// was not yet transmitted on the outConn.
+// The primary justification for adding this method is to allow encodings to
+// transmit partial data during the encoding process. This can improve
+// performance considerably for slower (more complex) encoding algorithms.
+/*inline UINT
+vncEncoder::EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest, const RECT &rect)
+{
+
+ return EncodeRect(source, dest, rect);
+
+}
+
+bool
+vncEncoder::GetRemotePalette(RGBQUAD *quadlist, UINT ncolours)
+{
+ vnclog.Print(LL_INTINFO, VNCLOG("remote palette data requested\n"));
+
+ // If the local server is palette-based then call SetTranslateFunction
+ // to update the palette-to-truecolour mapping:
+ if (!m_localformat.trueColour)
+ {
+ if (!SetTranslateFunction())
+ return false;
+ }
+
+ // If the client is truecolour then don't fill in the palette buffer...
+ if (m_remoteformat.trueColour)
+ return false;
+
+ // If the server is truecolour then fake BGR233
+ if (m_localformat.trueColour)
+ {
+ // Fake BGR233...
+ vnclog.Print(LL_INTINFO, VNCLOG("generating BGR233 palette data\n"));
+
+ int ncolours = 1 << m_transformat.bitsPerPixel;
+ if (m_localpalette != NULL)
+ free(m_localpalette);
+ m_localpalette = (char *)malloc(ncolours * sizeof(RGBQUAD));
+
+ if (m_localpalette != NULL)
+ {
+ RGBQUAD *colour = (RGBQUAD *)m_localpalette;
+
+ for (int i=0; i<ncolours; i++)
+ {
+ colour[i].rgbBlue = (((i >> m_transformat.blueShift) & m_transformat.blueMax) * 255) / m_transformat.blueMax;
+ colour[i].rgbRed = (((i >> m_transformat.redShift) & m_transformat.redMax) * 255) / m_transformat.redMax;
+ colour[i].rgbGreen = (((i >> m_transformat.greenShift) & m_transformat.greenMax) * 255) / m_transformat.greenMax;
+ }
+ }
+ }
+ else
+ {
+ // Set up RGBQUAD rfbPixelFormat info
+ vnclog.Print(LL_INTINFO, VNCLOG("generating 8-bit palette data\n"));
+
+ rfbPixelFormat remote;
+ remote.trueColour = true;
+ remote.bitsPerPixel = 32;
+ remote.depth = 24;
+ remote.bigEndian = false;
+ remote.redMax = remote.greenMax = remote.blueMax = 255;
+ remote.redShift = 16;
+ remote.greenShift = 8;
+ remote.blueShift = 0;
+
+ // We get the ColourMapSingleTableFns procedure to handle retrieval of the
+ // palette for us, to avoid replicating the code!
+ (*rfbInitColourMapSingleTableFns[remote.bitsPerPixel / 16])
+ (&m_localpalette, &m_localformat, &remote);
+ }
+
+ // Did we create some palette info?
+ if (m_localpalette == NULL)
+ {
+ vnclog.Print(LL_INTERR, VNCLOG("failed to obtain colour map data!\n"));
+ return false;
+ }
+
+ // Copy the data into the RGBQUAD buffer
+ memcpy(quadlist, m_localpalette, ncolours*sizeof(RGBQUAD));
+
+ return true;
+}*/
+
+bool
+vncEncoder::SetTranslateFunction()
+{
+ fprintf(stderr, "[ffnetdev] VNC: SetTranslateFunction called\n");
+
+ // By default, the actual format translated to matches the client format
+ m_transformat = m_remoteformat;
+
+ // Check that bits per pixel values are valid
+
+ if ((m_transformat.bitsPerPixel != 8) &&
+ (m_transformat.bitsPerPixel != 16) &&
+ (m_transformat.bitsPerPixel != 32))
+ {
+ fprintf(stderr, "[ffnetdev] only 8, 16 or 32 bits supported remotely - %d requested\n",
+ m_transformat.bitsPerPixel
+ );
+
+ return false;
+ }
+
+ if ((m_localformat.bitsPerPixel != 8) &&
+ (m_localformat.bitsPerPixel != 16) &&
+ (m_localformat.bitsPerPixel != 32))
+ {
+ fprintf(stderr, "[ffnetdev] only 8, 16 or 32 bits supported locally - %d in use\n",
+ m_localformat.bitsPerPixel
+ );
+
+ return false;
+ }
+
+ if (!m_transformat.trueColour && (m_transformat.bitsPerPixel != 8))
+ {
+ fprintf(stderr, "[ffnetdev] only 8-bit palette format supported remotely\n");
+ return false;
+ }
+ if (!m_localformat.trueColour && (m_localformat.bitsPerPixel != 8))
+ {
+ fprintf(stderr, "[ffnetdev] only 8-bit palette format supported locally\n");
+ return false;
+ }
+
+ // Now choose the translation function to use
+
+ // We don't do remote palettes unless they're 8-bit
+
+ if (!m_transformat.trueColour)
+ {
+ // Is the local format the same?
+ if (!m_localformat.trueColour &&
+ (m_localformat.bitsPerPixel == m_transformat.bitsPerPixel))
+ {
+ // Yes, so don't do any encoding
+ fprintf(stderr, "[ffnetdev] no encoding required - both 8-bit palettized\n");
+
+ m_transfunc = rfbTranslateNone;
+
+ // The first time the client sends an update, it will call
+ // GetRemotePalette to get the palette information required
+ return true;
+ }
+ else if (m_localformat.trueColour)
+ {
+ // Local side is truecolour, remote is palettized
+ fprintf(stderr, "[ffnetdev] local truecolour, remote palettized. using BGR233 palette\n");
+
+ // Fill out the translation table as if writing to BGR233
+ m_transformat = BGR233Format;
+
+ // Continue on down to the main translation section
+ }
+ else
+ {
+ // No, so not supported yet...
+ fprintf(stderr, "[ffnetdev] unknown local pixel format in use!\n");
+ return false;
+ }
+ }
+
+ // REMOTE FORMAT IS true-COLOUR
+
+ // Handle 8-bit palette-based local data
+ if (!m_localformat.trueColour)
+ {
+ // 8-bit palette to truecolour...
+
+ // Yes, so pick the right translation function!
+ fprintf(stderr, "[ffnetdev] using 8-bit colourmap to truecolour translation\n");
+
+ m_transfunc = rfbTranslateWithSingleTableFns
+ [m_localformat.bitsPerPixel / 16]
+ [m_transformat.bitsPerPixel / 16];
+
+ (*rfbInitColourMapSingleTableFns[m_transformat.bitsPerPixel / 16])
+ (&m_transtable, &m_localformat, &m_transformat);
+ return m_transtable != NULL;
+ }
+
+ // If we reach here then we're doing truecolour to truecolour
+
+ // Are the formats identical?
+ if (PF_EQ(m_transformat,m_localformat))
+ {
+ // Yes, so use the null translation function
+ fprintf(stderr, "[ffnetdev] no translation required\n");
+
+ m_transfunc = rfbTranslateNone;
+
+ return true;
+ }
+
+ // Is the local display a 16-bit one
+ if (m_localformat.bitsPerPixel == 16)
+ {
+ // Yes, so use a single lookup-table
+ fprintf(stderr, "[ffnetdev] single LUT used\n");
+
+ m_transfunc = rfbTranslateWithSingleTableFns
+ [m_localformat.bitsPerPixel / 16]
+ [m_transformat.bitsPerPixel / 16];
+
+ (*rfbInitTrueColourSingleTableFns[m_transformat.bitsPerPixel / 16])
+ (&m_transtable, &m_localformat, &m_transformat);
+ }
+ else
+ {
+ // No, so use three tables - one for each of R, G, B.
+ fprintf(stderr, "[ffnetdev] triple LUT used\n");
+
+ m_transfunc = rfbTranslateWithRGBTablesFns
+ [m_localformat.bitsPerPixel / 16]
+ [m_transformat.bitsPerPixel / 16];
+
+ (*rfbInitTrueColourRGBTablesFns[m_transformat.bitsPerPixel / 16])
+ (&m_transtable, &m_localformat, &m_transformat);
+ }
+
+ return m_transtable != NULL;
+}
+
+bool
+vncEncoder::SetLocalFormat(rfbPixelFormat &pixformat, int width, int height)
+{
+ // Work out the bytes per row at the local end - useful
+ m_bytesPerRow = width * pixformat.bitsPerPixel/8;
+
+ // Save the pixel format
+ m_localformat = pixformat;
+
+ // Don't call SetTranslateFunction() if remote format is not set yet.
+ if (m_remoteformat.depth == 0)
+ return true;
+
+ return SetTranslateFunction();
+}
+
+bool
+vncEncoder::SetRemoteFormat(rfbPixelFormat &pixformat)
+{
+ // Save the client pixel format
+ m_remoteformat = pixformat;
+
+ return SetTranslateFunction();
+}
+
+void
+vncEncoder::SetCompressLevel(int level)
+{
+ m_compresslevel = (level >= 0 && level <= 9) ? level : 6;
+}
+
+void
+vncEncoder::SetQualityLevel(int level)
+{
+ m_qualitylevel = (level >= 0 && level <= 9) ? level : -1;
+}
+
+//
+// New code implementing cursor shape updates.
+//
+/*
+bool
+vncEncoder::SendEmptyCursorShape(VSocket *outConn)
+{
+ rfbFramebufferUpdateRectHeader hdr;
+ hdr.r.x = Swap16IfLE(0);
+ hdr.r.y = Swap16IfLE(0);
+ hdr.r.w = Swap16IfLE(0);
+ hdr.r.h = Swap16IfLE(0);
+ if (m_use_xcursor) {
+ hdr.encoding = Swap32IfLE(rfbEncodingXCursor);
+ } else {
+ hdr.encoding = Swap32IfLE(rfbEncodingRichCursor);
+ }
+
+ return outConn->SendQueued((char *)&hdr, sizeof(hdr));
+}
+
+bool
+vncEncoder::SendCursorShape(VSocket *outConn, vncDesktop *desktop)
+{
+ // Make sure the function is used correctly
+ if (!m_use_xcursor && !m_use_richcursor)
+ return false;
+
+ // Check mouse cursor handle
+ HCURSOR hcursor = desktop->GetCursor();
+ if (hcursor == NULL) {
+ fprintf(stderr, "[ffnetdev] cursor handle is NULL.\n");
+ return false;
+ }
+
+ // Get cursor info
+ ICONINFO IconInfo;
+ if (!GetIconInfo(hcursor, &IconInfo)) {
+ fprintf(stderr, "[ffnetdev] GetIconInfo() failed.\n");
+ return false;
+ }
+ bool isColorCursor = false;
+ if (IconInfo.hbmColor != NULL) {
+ isColorCursor = true;
+ DeleteObject(IconInfo.hbmColor);
+ }
+ if (IconInfo.hbmMask == NULL) {
+ fprintf(stderr, "[ffnetdev] cursor bitmap handle is NULL.\n");
+ return false;
+ }
+
+ // Check bitmap info for the cursor
+ BITMAP bmMask;
+ if (!GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask)) {
+ fprintf(stderr, "[ffnetdev] GetObject() for bitmap failed.\n");
+ DeleteObject(IconInfo.hbmMask);
+ return false;
+ }
+ if (bmMask.bmPlanes != 1 || bmMask.bmBitsPixel != 1) {
+ fprintf(stderr, "[ffnetdev] incorrect data in cursor bitmap.\n");
+ DeleteObject(IconInfo.hbmMask);
+ return false;
+ }
+
+ // Get monochrome bitmap data for cursor
+ // NOTE: they say we should use GetDIBits() instead of GetBitmapBits().
+ BYTE *mbits = new BYTE[bmMask.bmWidthBytes * bmMask.bmHeight];
+ if (mbits == NULL)
+ return false;
+
+ bool success = GetBitmapBits(IconInfo.hbmMask,
+ bmMask.bmWidthBytes * bmMask.bmHeight, mbits);
+ DeleteObject(IconInfo.hbmMask);
+
+ if (!success) {
+ fprintf(stderr, "[ffnetdev] GetBitmapBits() failed.\n");
+ delete[] mbits;
+ return false;
+ }
+
+ // Compute cursor dimensions
+ int width = bmMask.bmWidth;
+ int height = (isColorCursor) ? bmMask.bmHeight : bmMask.bmHeight/2;
+
+ // Call appropriate routine to send cursor shape update
+ if (!isColorCursor && m_use_xcursor) {
+ FixCursorMask(mbits, NULL, width, bmMask.bmHeight, bmMask.bmWidthBytes);
+ success = SendXCursorShape(outConn, mbits,
+ IconInfo.xHotspot, IconInfo.yHotspot,
+ width, height);
+ }
+ else if (m_use_richcursor) {
+ int cbits_size = width * height * 4;
+ BYTE *cbits = new BYTE[cbits_size];
+ if (cbits == NULL) {
+ delete[] mbits;
+ return false;
+ }
+ if (!desktop->GetRichCursorData(cbits, hcursor, width, height)) {
+ fprintf(stderr, "[ffnetdev] vncDesktop::GetRichCursorData() failed.\n");
+ delete[] mbits;
+ delete[] cbits;
+ return false;
+ }
+ FixCursorMask(mbits, cbits, width, height, bmMask.bmWidthBytes);
+ success = SendRichCursorShape(outConn, mbits, cbits,
+ IconInfo.xHotspot, IconInfo.yHotspot,
+ width, height);
+ delete[] cbits;
+ }
+ else {
+ success = false; // FIXME: We could convert RichCursor -> XCursor.
+ }
+
+ // Cleanup
+ delete[] mbits;
+
+ return success;
+}
+
+bool
+vncEncoder::SendXCursorShape(VSocket *outConn, BYTE *mask,
+ int xhot, int yhot, int width, int height)
+{
+ rfbFramebufferUpdateRectHeader hdr;
+ hdr.r.x = Swap16IfLE(xhot);
+ hdr.r.y = Swap16IfLE(yhot);
+ hdr.r.w = Swap16IfLE(width);
+ hdr.r.h = Swap16IfLE(height);
+ hdr.encoding = Swap32IfLE(rfbEncodingXCursor);
+
+ BYTE colors[6] = { 0, 0, 0, 0xFF, 0xFF, 0xFF };
+ int maskRowSize = (width + 7) / 8;
+ int maskSize = maskRowSize * height;
+
+ if ( !outConn->SendQueued((char *)&hdr, sizeof(hdr)) ||
+ !outConn->SendQueued((char *)colors, 6) ||
+ !outConn->SendQueued((char *)&mask[maskSize], maskSize) ||
+ !outConn->SendQueued((char *)mask, maskSize) ) {
+ return false;
+ }
+ return true;
+}
+
+bool
+vncEncoder::SendRichCursorShape(VSocket *outConn, BYTE *mbits, BYTE *cbits,
+ int xhot, int yhot, int width, int height)
+{
+ rfbFramebufferUpdateRectHeader hdr;
+ hdr.r.x = Swap16IfLE(xhot);
+ hdr.r.y = Swap16IfLE(yhot);
+ hdr.r.w = Swap16IfLE(width);
+ hdr.r.h = Swap16IfLE(height);
+ hdr.encoding = Swap32IfLE(rfbEncodingRichCursor);
+
+ // Cet cursor image in local pixel format
+ int srcbuf_rowsize = width * (m_localformat.bitsPerPixel / 8);
+ while (srcbuf_rowsize % sizeof(DWORD))
+ srcbuf_rowsize++; // Actually, this should never happen
+
+ // Translate image to client pixel format
+ int dstbuf_size = width * height * (m_remoteformat.bitsPerPixel / 8);
+ BYTE *dstbuf = new BYTE[dstbuf_size];
+ Translate(cbits, dstbuf, width, height, srcbuf_rowsize);
+
+ // Send the data
+ int mask_rowsize = (width + 7) / 8;
+ int mask_size = mask_rowsize * height;
+ if ( !outConn->SendQueued((char *)&hdr, sizeof(hdr)) ||
+ !outConn->SendQueued((char *)dstbuf, dstbuf_size) ||
+ !outConn->SendQueued((char *)mbits, mask_size) ) {
+ delete[] dstbuf;
+ return false;
+ }
+ delete[] dstbuf;
+ return true;
+}
+*/
+void
+vncEncoder::FixCursorMask(BYTE *mbits, BYTE *cbits,
+ int width, int height, int width_bytes)
+{
+ int packed_width_bytes = (width + 7) / 8;
+
+ // Pack and invert bitmap data (mbits)
+ int x, y;
+ for (y = 0; y < height; y++)
+ for (x = 0; x < packed_width_bytes; x++)
+ mbits[y * packed_width_bytes + x] = ~mbits[y * width_bytes + x];
+
+ // Replace "inverted background" bits with black color to ensure
+ // cross-platform interoperability. Not beautiful but necessary code.
+ if (cbits == NULL) {
+ BYTE m, c;
+ height /= 2;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < packed_width_bytes; x++) {
+ m = mbits[y * packed_width_bytes + x];
+ c = mbits[(height + y) * packed_width_bytes + x];
+ mbits[y * packed_width_bytes + x] |= ~(m | c);
+ mbits[(height + y) * packed_width_bytes + x] |= ~(m | c);
+ }
+ }
+ } else {
+ int bytes_pixel = m_localformat.bitsPerPixel / 8;
+ int bytes_row = width * bytes_pixel;
+ while (bytes_row % sizeof(DWORD))
+ bytes_row++; // Actually, this should never happen
+
+ BYTE bitmask;
+ int b1, b2;
+ for (y = 0; y < height; y++) {
+ bitmask = 0x80;
+ for (x = 0; x < width; x++) {
+ if ((mbits[y * packed_width_bytes + x / 8] & bitmask) == 0) {
+ for (b1 = 0; b1 < bytes_pixel; b1++) {
+ if (cbits[y * bytes_row + x * bytes_pixel + b1] != 0) {
+ mbits[y * packed_width_bytes + x / 8] ^= bitmask;
+ for (b2 = b1; b2 < bytes_pixel; b2++)
+ cbits[y * bytes_row + x * bytes_pixel + b2] = 0x00;
+ break;
+ }
+ }
+ }
+ if ((bitmask >>= 1) == 0)
+ bitmask = 0x80;
+ }
+ }
+ }
+}
+
diff --git a/vncEncoder.h b/vncEncoder.h
new file mode 100644
index 0000000..561d8ad
--- /dev/null
+++ b/vncEncoder.h
@@ -0,0 +1,141 @@
+// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
+// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+//
+// This file is part of the VNC system.
+//
+// The VNC system is free software; you can redistribute it and/or modify
+// it under the terms of the GNU 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.
+//
+// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+//
+// If the source code for the VNC system is not available from the place
+// whence you received this file, check http://www.uk.research.att.com/vnc or contact
+// the authors on vnc@uk.research.att.com for information on obtaining it.
+
+
+// vncEncoder object
+
+// The vncEncoder object encodes regions of a display buffer for sending
+// to a client
+
+class vncEncoder;
+
+#if !defined(RFBENCODER_DEFINED)
+#define RFBENCODER_DEFINED
+#pragma once
+
+
+typedef unsigned char BYTE;
+typedef unsigned int UINT;
+typedef unsigned long DWORD;
+
+//static
+
+#include "translate.h"
+
+typedef struct {
+ int left;
+ int top;
+ int right;
+ int bottom;
+} RECT;
+
+// Class definition
+
+class vncEncoder
+{
+// Fields
+public:
+
+// Methods
+public:
+ // Create/Destroy methods
+ vncEncoder();
+ virtual ~vncEncoder();
+
+ // Initialisation
+ virtual void Init();
+
+ // A method to return the encoding name, used by the LogStats() method
+ virtual const char* GetEncodingName() { return "Raw"; }
+
+ // Central method for outputing encoding statistics
+ virtual void LogStats();
+
+ // Encoder stats used by the buffer object
+ virtual UINT RequiredBuffSize(UINT width, UINT height);
+ virtual UINT NumCodedRects(RECT &rect);
+
+ // Translation & encoding routines
+ // - Source is the base address of the ENTIRE SCREEN buffer.
+ // The Translate routine compensates automatically for the desired rectangle.
+ // - Dest is the base address to encode the rect to. The rect will be encoded
+ // into a contiguous region of the buffer.
+ virtual void Translate(BYTE *source, BYTE *dest, const RECT &rect);
+ virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect);
+// virtual UINT EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest, const RECT &rect);
+
+ // Additional translation function for cursor shape data (overloaded!)
+ void Translate(BYTE *source, BYTE *dest, int w, int h, int bytesPerRow);
+
+ // Translation handling
+ bool SetLocalFormat(rfbPixelFormat &pixformat, int width, int height);
+ bool SetRemoteFormat(rfbPixelFormat &pixformat);
+
+ // Configuring encoder
+ void SetCompressLevel(int level);
+ void SetQualityLevel(int level);
+ void EnableXCursor(bool enable) { m_use_xcursor = enable; }
+ void EnableRichCursor(bool enable) { m_use_richcursor = enable; }
+ void EnableLastRect(bool enable) { m_use_lastrect = enable; }
+
+ // Colour map handling
+// BOOL GetRemotePalette(RGBQUAD *quadlist, UINT ncolours);
+
+ // Supporting cursor shape updates
+// BOOL SendEmptyCursorShape(VSocket *outConn);
+// BOOL SendCursorShape(VSocket *outConn, vncDesktop *desktop);
+
+protected:
+ bool SetTranslateFunction();
+
+ // Supporting cursor shape updates
+// BOOL SendXCursorShape(VSocket *outConn, BYTE *mask, int xhot,int yhot,int width,int height);
+// BOOL SendRichCursorShape(VSocket *outConn, BYTE *mbits, BYTE *cbits, int xhot,int yhot,int width,int height);
+ void FixCursorMask(BYTE *mbits, BYTE *cbits, int width, int height, int width_bytes);
+
+// Implementation
+protected:
+ rfbTranslateFnType m_transfunc; // Translator function
+ char* m_transtable; // Colour translation LUT
+ char* m_localpalette; // Palette info if client is palette-based
+ rfbPixelFormat m_localformat; // Pixel Format info
+ rfbPixelFormat m_remoteformat; // Client pixel format info
+ rfbPixelFormat m_transformat; // Internal format used for translation (usually == client format)
+ int m_bytesPerRow; // Number of bytes per row locally
+ int dataSize; // Total size of raw data encoded
+ int rectangleOverhead; // Total size of rectangle header data
+ int encodedSize; // Total size of encoded data
+ int transmittedSize; // Total amount of data sent
+
+ int m_compresslevel; // Encoding-specific compression level (if needed).
+ int m_qualitylevel; // Image quality level for lossy JPEG compression.
+ bool m_use_xcursor; // XCursor cursor shape updates allowed.
+ bool m_use_richcursor; // RichCursor cursor shape updates allowed.
+ bool m_use_lastrect; // LastRect pseudo-encoding allowed.
+};
+
+#endif // vncENCODER_DEFINED