summaryrefslogtreecommitdiff
path: root/graphtft-fe
diff options
context:
space:
mode:
authorhorchi <vdr@jwendel.de>2017-03-05 16:47:41 +0100
committerhorchi <vdr@jwendel.de>2017-03-05 16:47:41 +0100
commit22ffee20bbacbc3378e4ba0df5b7f0c3daaeffc0 (patch)
treede46c945c62d43d1febb027b5bfa075e58c5b69a /graphtft-fe
downloadvdr-plugin-graphtftng-22ffee20bbacbc3378e4ba0df5b7f0c3daaeffc0.tar.gz
vdr-plugin-graphtftng-22ffee20bbacbc3378e4ba0df5b7f0c3daaeffc0.tar.bz2
Diffstat (limited to 'graphtft-fe')
-rw-r--r--graphtft-fe/COPYING340
-rw-r--r--graphtft-fe/Makefile37
-rw-r--r--graphtft-fe/README24
-rw-r--r--graphtft-fe/common.cc52
-rw-r--r--graphtft-fe/common.hpp14
-rw-r--r--graphtft-fe/comthread.cc231
-rw-r--r--graphtft-fe/graphtft.cc736
-rw-r--r--graphtft-fe/graphtft.hpp143
-rw-r--r--graphtft-fe/main.cc19
-rw-r--r--graphtft-fe/tcpchannel.cc532
-rw-r--r--graphtft-fe/tcpchannel.h97
-rw-r--r--graphtft-fe/thread.cc383
-rw-r--r--graphtft-fe/thread.h160
13 files changed, 2768 insertions, 0 deletions
diff --git a/graphtft-fe/COPYING b/graphtft-fe/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/graphtft-fe/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/graphtft-fe/Makefile b/graphtft-fe/Makefile
new file mode 100644
index 0000000..95f5b46
--- /dev/null
+++ b/graphtft-fe/Makefile
@@ -0,0 +1,37 @@
+
+CXX ?= g++
+CXXFLAGS ?= -pipe -ggdb -O2 -Wall -W -D_REENTRANT -fPIC
+CXXFLAGS += -Wno-deprecated-declarations
+LFLAGS = -Wl,--no-undefined
+LIBS = -lpthread -ljpeg -lX11
+LIBS += $(shell imlib2-config --libs)
+AR = ar
+
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
+BINDIR = $(call PKGCFG,bindir)
+
+TARGET = graphtft-fe
+
+OBJECTS = fecommon.o \
+ comthread.o \
+ graphtft.o \
+ main.o \
+ tcpchannel.o \
+ thread.o
+
+all:
+ @$(MAKE) $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CXX) $(LFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET)
+install:
+ @cp -v --remove-destination graphtft-fe $(DESTDIR)$(BINDIR)
+
+clean:
+ rm -f *.o $(TARGET) *~
+
+.cc.o:
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
+
+fecommon.o : ../common.c ../common.h
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
diff --git a/graphtft-fe/README b/graphtft-fe/README
new file mode 100644
index 0000000..0ac5b33
--- /dev/null
+++ b/graphtft-fe/README
@@ -0,0 +1,24 @@
+
+graphtft-fe ist das X-Sever-Frontend des graphTFT Plugin, die Bedinung des VDR ist mit
+entsprechender Theme mittels Maus/Touch und Tastatur möglich.
+
+Voraussetzungen
+---------------
+
+Pakete:
+
+imlib2, imlib2-dev, libjpeg-dev
+xorg-x11-devel
+
+Installation
+------------
+
+make -s clean all
+
+Start / Optionen
+----------------
+
+Eine Liste der Optionen wird mit
+./graphtft-fe --help
+angezeigt
+
diff --git a/graphtft-fe/common.cc b/graphtft-fe/common.cc
new file mode 100644
index 0000000..0d60aad
--- /dev/null
+++ b/graphtft-fe/common.cc
@@ -0,0 +1,52 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File common.cc
+// Date 04.11.06 - Jörg Wendel
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//***************************************************************************
+
+#include <sys/time.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <graphtft.hpp>
+
+//***************************************************************************
+// Tell
+//***************************************************************************
+
+int tell(int eloquence, const char* format, ...)
+{
+ const int sizeTime = 8; // "12:12:34"
+ const int sizeMSec = 4; // ",142"
+ const int sizeHeader = sizeTime + sizeMSec + 1;
+ const int maxBuf = 1000;
+
+ struct timeval tp;
+ char buf[maxBuf];
+ va_list ap;
+ time_t now;
+
+ va_start(ap, format);
+
+ if (GraphTft::getEloquence() >= eloquence)
+ {
+ time(&now);
+ gettimeofday(&tp, 0);
+
+ vsnprintf(buf + sizeHeader, maxBuf - sizeHeader, format, ap);
+ strftime(buf, sizeTime+1, "%H:%M:%S", localtime(&now));
+
+ sprintf(buf+sizeTime, ",%3.3ld", tp.tv_usec / 1000);
+
+ buf[sizeHeader-1] = ' ';
+ printf("%s\n", buf);
+ }
+
+ va_end(ap);
+
+ return 0;
+}
diff --git a/graphtft-fe/common.hpp b/graphtft-fe/common.hpp
new file mode 100644
index 0000000..30d6f85
--- /dev/null
+++ b/graphtft-fe/common.hpp
@@ -0,0 +1,14 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File common.hpp
+// Date 04.11.06 - Jörg Wendel
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//***************************************************************************
+
+#ifndef __COMMON_HPP__
+#define __COMMON_HPP__
+
+int tell(int eloquence, const char* format, ...);
+
+#endif // __COMMON_HPP__
diff --git a/graphtft-fe/comthread.cc b/graphtft-fe/comthread.cc
new file mode 100644
index 0000000..aa3429c
--- /dev/null
+++ b/graphtft-fe/comthread.cc
@@ -0,0 +1,231 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File comthread.cc
+// Date 28.10.06 - Jörg Wendel
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//--------------------------------------------------------------------------
+// Class ComThread
+//***************************************************************************
+
+#include <arpa/inet.h>
+
+#include "tcpchannel.h"
+#include "graphtft.hpp"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+ComThread::ComThread()
+ : cMyThread()
+{
+ line = new TcpChannel();
+
+ bufferSize = maxBuffer;
+
+ buffer = new char[bufferSize+1];
+ header = new TcpChannel::Header;
+
+ timeout = 1;
+ port = -1;
+ *host = 0;
+ client = 0;
+ jpegQuality = na;
+}
+
+ComThread::~ComThread()
+{
+ if (line->isConnected())
+ {
+ tell(eloAlways, "Logout from server, closing tcp connection");
+ line->write(cGraphTftComService::cmdLogout);
+ line->close();
+ }
+
+ delete line;
+ delete header;
+ delete[] buffer;
+}
+
+void ComThread::stop()
+{
+ running = false;
+
+ Cancel(2);
+}
+
+//***************************************************************************
+// Run
+//***************************************************************************
+
+void ComThread::Action()
+{
+ const int checkTimeout = 30;
+
+ int status;
+ time_t lastCheck = time(0);
+ int quality = htonl(jpegQuality);
+
+ running = true;
+
+ while (running)
+ {
+ if (!line->isConnected())
+ {
+ tell(eloAlways, "Trying connecting to '%s' at port (%d)", host, port);
+
+ if (line->open(port, host) == 0)
+ {
+ tell(eloAlways, "Connection to '%s' established", host);
+
+ if (jpegQuality > na)
+ line->write(cGraphTftComService::cmdJpegQuality, (char*)&quality, sizeof(int));
+ }
+ else
+ tell(eloAlways, "Connecting to '%s' failed", host);
+ }
+
+ while (line->isConnected() && running)
+ {
+ if (lastCheck+checkTimeout < time(0))
+ {
+ line->write(cGraphTftComService::cmdCheck);
+ lastCheck = time(0);
+ }
+
+ if ((status = line->look(1)) != success)
+ {
+ if (status != TcpChannel::wrnNoEventPending)
+ {
+ tell(eloAlways, "Error: Communication problems, closing line! status was (%d)",
+ status);
+ line->close();
+
+ break;
+ }
+
+ continue;
+ }
+
+ if ((status = read()) != 0)
+ {
+ line->close();
+ tell(eloAlways, "Error: Communication problems, closing line! status was (%d)",
+ status);
+ }
+ }
+
+ if (!running) break;
+
+ tell(eloAlways, "Retrying in %ld seconds", timeout);
+
+ for (int i = 0; i < timeout && running; i++)
+ sleep(1);
+ }
+}
+
+//***************************************************************************
+// Transmit events
+//***************************************************************************
+
+int ComThread::mouseEvent(int x, int y, int button, int flag, int data)
+{
+ GraphTftTouchEvent m;
+
+ m.x = htonl(x);
+ m.y = htonl(y);
+ m.button = htonl(button);
+ m.flag = htonl(flag);
+ m.data = htonl(data);
+
+ line->write(cGraphTftComService::cmdMouseEvent, (char*)&m, sizeof(GraphTftTouchEvent));
+
+ return 0;
+}
+
+//***************************************************************************
+// ...
+//***************************************************************************
+
+int ComThread::keyEvent(int key, int flag)
+{
+ GraphTftTouchEvent m;
+
+ m.x = htonl(0);
+ m.y = htonl(0);
+ m.button = htonl(key);
+ m.flag = htonl(flag | efKeyboard);
+
+ line->write(cGraphTftComService::cmdMouseEvent, (char*)&m, sizeof(GraphTftTouchEvent));
+
+ return 0;
+}
+
+//***************************************************************************
+// Read
+//***************************************************************************
+
+int ComThread::read()
+{
+ int status;
+ TcpChannel::Header tmp;
+
+ // es stehen Daten an, erst einmal den Header abholen ..
+
+ if ((status = line->read((char*)&tmp, sizeof(TcpChannel::Header))) == 0)
+ {
+ header->command = ntohl(tmp.command);
+ header->size = ntohl(tmp.size);
+
+ switch (header->command)
+ {
+ case cGraphTftComService::cmdWelcome:
+ {
+ tell(eloAlways, "Got welcome");
+
+ break;
+ }
+
+ case cGraphTftComService::cmdLogout:
+ {
+ tell(eloAlways, "Got logout from client, closing line");
+ line->close();
+
+ break;
+ }
+
+ case cGraphTftComService::cmdData:
+ {
+ tell(eloDebug, "Debug: Start reading %d kb from TCP", header->size/1024);
+ status = line->read(buffer, header->size);
+ tell(eloDebug, "Debug: Received %d kb", header->size/1024);
+
+ if (status == 0 && client)
+ client->updateImage((unsigned char*)buffer, header->size);
+
+ break;
+ }
+
+ case cGraphTftComService::cmdMouseEvent:
+ {
+ GraphTftTouchEvent ev;
+
+ status = line->read((char*)&ev, header->size);
+ tell(eloAlways, "Got mouse event, button (%d) at (%d/%d)", ev.button, ev.x, ev.y);
+
+ break;
+ }
+
+ default:
+ {
+ tell(eloAlways, "Got unexpected protocol (%d), aborting", header->command);
+ status = -1;
+
+ break;
+ }
+ }
+ }
+
+ return status;
+}
diff --git a/graphtft-fe/graphtft.cc b/graphtft-fe/graphtft.cc
new file mode 100644
index 0000000..693ca5f
--- /dev/null
+++ b/graphtft-fe/graphtft.cc
@@ -0,0 +1,736 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File graphtft.hpp
+// Date 28.10.06 - Jörg Wendel
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//--------------------------------------------------------------------------
+// Class GrapTFT
+//***************************************************************************
+
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <jpeglib.h>
+
+#include "graphtft.hpp"
+
+//#define _DEBUG
+
+//***************************************************************************
+// Class GraphTft
+//***************************************************************************
+
+int GraphTft::eloquence = eloOff;
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+GraphTft::GraphTft()
+{
+ // init
+
+ showHelp = false;
+ resize = false;
+ image = 0;
+ hideCursorDelay = 0;
+ managed = true;
+ vdrWidth = 720;
+ vdrHeight = 576;
+ width = 720;
+ height = 576;
+ border = 0;
+ *dump = 0;
+ cursorVisible = yes;
+ lastMotion = time(0);
+ borderVisible = yes;
+ ignoreEsc = no;
+ screen = 0;
+
+ thread = new ComThread();
+
+ // the defaults
+
+ thread->setHost("localhost");
+ thread->setPort(2039);
+}
+
+GraphTft::~GraphTft()
+{
+ if (thread)
+ {
+ tell(eloAlways, "Stopping thread");
+
+ thread->stop();
+
+ delete thread;
+ }
+}
+
+void GraphTft::setArgs(int argc, char* argv[])
+{
+ if (argc > 1 && (argv[1][0] == '?' || (strcmp(argv[1], "--help") == 0)))
+ {
+ showHelp = true;
+ return ;
+ }
+
+ for (int i = 0; argv[i]; i++)
+ {
+ if (argv[i][0] != '-' || strlen(argv[i]) != 2)
+ continue;
+
+ switch (argv[i][1])
+ {
+ case 'i': if (argv[i+1]) ignoreEsc = yes; break;
+ case 'h': if (argv[i+1]) thread->setHost(argv[i+1]); break;
+ case 'p': if (argv[i+1]) thread->setPort(atoi(argv[i+1])); break;
+ case 'e': if (argv[i+1]) setEloquence(atoi(argv[i+1])); break;
+ case 'W': if (argv[i+1]) width = atoi(argv[i+1]); break;
+ case 'H': if (argv[i+1]) height = atoi(argv[i+1]); break;
+ case 'd': if (argv[i+1]) strcpy(dump, argv[i+1]); break;
+ case 'c': if (argv[i+1]) hideCursorDelay = atoi(argv[i+1]); break;
+ case 'j': if (argv[i+1]) thread->setJpegQuality(atoi(argv[i+1])); break;
+
+ case 'b': borderVisible = no; break;
+ case 'n': managed = false; break;
+ case 'r': resize = true; break;
+ }
+ }
+}
+
+//***************************************************************************
+// Show Usage
+//***************************************************************************
+
+void GraphTft::showUsage()
+{
+ printf("Usage: graphtft-fe\n"
+ " Parameter:\n"
+ " -h <host> vdr host no default, please specify\n"
+ " -p <port> plugin port (default 2039)\n"
+ " -e <eloquence> log level (default 0)\n"
+ " -W <width> width (default 720)\n"
+ " -H <height> height (default 576)\n"
+ " -d <file> dump each image to file (default off)\n"
+ " -n not managed (default managed)\n"
+ " -r resize image (default off)\n"
+ " -j <qunality> JPEG quality (0-100)\n"
+ " -c <seconds> hide mouse curser after <seconds>\n"
+ " -b no boarder\n"
+ " -i no exit on ESC key\n"
+ " ?, --help this help\n"
+ );
+}
+
+//***************************************************************************
+// Start
+//***************************************************************************
+
+int GraphTft::start()
+{
+ if (showHelp)
+ {
+ showUsage();
+ return 0;
+ }
+
+ if (init() != success)
+ return fail;
+
+ run();
+ exit();
+
+ return success;
+}
+
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+//***************************************************************************
+// init/exit
+//***************************************************************************
+
+int GraphTft::init()
+{
+ Visual* vis;
+ Colormap cm;
+ int depth;
+
+ // init X
+
+ disp = XOpenDisplay(0);
+
+ if (!disp)
+ {
+ printf("Invalid display, aborting\n");
+ return fail;
+ }
+
+ // init communication thread
+
+ thread->setClient(this);
+ thread->Start();
+
+ // init dispaly
+
+ screen = DefaultScreen(disp);
+ vis = DefaultVisual(disp, screen);
+ depth = DefaultDepth(disp, screen);
+ cm = DefaultColormap(disp, screen);
+
+ // create simple window
+
+ if (managed)
+ {
+ const char* appName = "graphtft-fe";
+
+ win = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
+ 0, 0, width, height, 0, 0, 0);
+
+ XSetStandardProperties(disp, win, appName, appName, None,
+ 0, 0, 0);
+
+
+ XClassHint* classHint;
+ XStoreName(disp, win, appName);
+
+ /* set the name and class hints for the window manager to use */
+
+ classHint = XAllocClassHint();
+
+ if (classHint)
+ {
+ classHint->res_name = (char*)appName;
+ classHint->res_class = (char*)appName;
+ }
+
+ XSetClassHint(disp, win, classHint);
+ XFree(classHint);
+
+ if (!borderVisible)
+ hideBorder();
+ }
+ else
+ {
+ // create window more complex
+
+ // attributes
+
+ XSetWindowAttributes windowAttributes;
+
+ windowAttributes.border_pixel = BlackPixel(disp, screen);
+ windowAttributes.border_pixmap = CopyFromParent;
+ windowAttributes.background_pixel = WhitePixel(disp, screen);
+ windowAttributes.override_redirect = True;
+ windowAttributes.bit_gravity = NorthWestGravity;
+ windowAttributes.event_mask = ButtonPressMask | ButtonReleaseMask |
+ KeyPressMask | ExposureMask | SubstructureNotifyMask;
+
+
+ win = XCreateWindow(disp, RootWindow(disp, screen),
+ 0, 0, width, height,
+ border, depth,
+ InputOutput,
+ vis,
+ CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWBitGravity | CWEventMask,
+ &windowAttributes);
+
+
+ }
+
+ XSelectInput(disp, win,
+ ButtonPressMask |
+ ButtonReleaseMask |
+ PointerMotionMask |
+ KeyPressMask |
+ ClientMessage |
+ SubstructureNotifyMask |
+ ExposureMask); // events to receive
+
+
+ XMapWindow(disp, win); // show
+ XFlush(disp);
+
+ Screen* scn = DefaultScreenOfDisplay(disp);
+ pix = XCreatePixmap(disp, win, width, height, DefaultDepthOfScreen(scn));
+
+ imlib_set_cache_size(16 * 1024 * 1024);
+ imlib_set_color_usage(256);
+
+ imlib_context_set_dither(0); // dither for depths < 24bpp
+ imlib_context_set_display(disp); // set the display
+ imlib_context_set_visual(vis); // visual,
+ imlib_context_set_colormap(cm); // colormap
+
+ // imlib_context_set_drawable(win); // and the drawable we are using
+ imlib_context_set_drawable(pix); // and the drawable we are using
+
+ return 0;
+}
+
+void GraphTft::hideBorder()
+{
+ struct MwmHints
+ {
+ int flags;
+ int functions;
+ int decorations;
+ int input_mode;
+ int status;
+ };
+
+ MwmHints mwmhints;
+ Atom prop;
+
+ memset(&mwmhints, 0, sizeof(mwmhints));
+ mwmhints.flags = 1L << 1;
+ mwmhints.decorations = 0;
+
+ prop = XInternAtom(disp, "_MOTIF_WM_HINTS", False);
+
+ XChangeProperty(disp, win, prop, prop, 32, PropModeReplace,
+ (unsigned char*)&mwmhints, sizeof(mwmhints)/sizeof(long));
+}
+
+int GraphTft::exit()
+{
+ XFreePixmap(disp, pix);
+ XCloseDisplay(disp);
+ imlib_free_image();
+
+ return 0;
+}
+
+//***************************************************************************
+// Send Exent
+//***************************************************************************
+
+int GraphTft::sendEvent()
+{
+ XEvent ev;
+ Display* d;
+
+ if ((d = XOpenDisplay(0)) == 0)
+ {
+ tell(eloAlways, "Error: Sending event failed, cannot open display");
+ return fail;
+ }
+
+ ev.type = Expose;
+ XSendEvent(d, win, False, 0, &ev);
+
+ XCloseDisplay(d);
+
+ return success;
+}
+
+int fromJpeg(Imlib_Image& image, unsigned char* buffer, int size)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int w, h;
+ DATA8 *ptr, *line[16], *data;
+ DATA32 *ptr2, *dest;
+ int x, y;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jpeg_create_decompress(&cinfo);
+ jpeg_mem_src(&cinfo, buffer, size);
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.do_fancy_upsampling = FALSE;
+ cinfo.do_block_smoothing = FALSE;
+
+ jpeg_start_decompress(&cinfo);
+
+ w = cinfo.output_width;
+ h = cinfo.output_height;
+
+ image = imlib_create_image(w, h);
+ imlib_context_set_image(image);
+
+ dest = ptr2 = imlib_image_get_data();
+ data = (DATA8*)malloc(w * 16 * cinfo.output_components);
+
+ for (int i = 0; i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * cinfo.output_components);
+
+ for (int l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ int scans = cinfo.rec_outbuf_height;
+
+ if (h - l < scans)
+ scans = h - l;
+
+ ptr = data;
+
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ *ptr2 = (0xff000000) | ((ptr[0]) << 16) | ((ptr[1]) << 8) | (ptr[2]);
+ ptr += cinfo.output_components;
+ ptr2++;
+ }
+ }
+ }
+
+ free(data);
+
+ imlib_image_put_back_data(dest);
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return success;
+}
+
+//***************************************************************************
+// Load Image
+//***************************************************************************
+
+void GraphTft::updateImage(const unsigned char* buffer, int size)
+{
+ tell(eloAlways, "update image");
+
+ bufferLock.Lock();
+
+ if (image)
+ {
+ imlib_context_set_image(image);
+ imlib_free_image();
+ }
+
+#ifdef _DEBUG
+
+ tell(eloAlways, "loading image, from file");
+
+ image = imlib_load_image("test.jpg");
+ sendEvent();
+ dumpImage(image);
+
+ bufferLock.Unlock();
+
+ return ;
+#endif
+
+ tell(eloAlways, "loading image, size (%d)", size);
+
+ if (size)
+ {
+ fromJpeg(image, (unsigned char*)buffer, size);
+
+ dumpImage(image);
+ sendEvent();
+ }
+
+ bufferLock.Unlock();
+}
+
+//***************************************************************************
+// Paint
+//***************************************************************************
+
+int GraphTft::paint()
+{
+ XWindowAttributes windowAttributes;
+
+ if (!image)
+ return fail;
+
+ tell(eloAlways, "paint ...");
+
+ // get actual window size
+
+ XGetWindowAttributes(disp, win, &windowAttributes);
+ width = windowAttributes.width;
+ height = windowAttributes.height;
+
+ // lock buffer
+
+ bufferLock.Lock();
+
+ imlib_context_set_image(image);
+
+ // get VDR's image size
+
+ vdrWidth = imlib_image_get_width();
+ vdrHeight = imlib_image_get_height();
+
+ if (!resize)
+ {
+ imlib_render_image_on_drawable(0, 0); // render image on drawable
+ }
+ else
+ {
+ Imlib_Image buffer;
+
+ buffer = imlib_create_image(width, height);
+
+ imlib_context_set_image(buffer);
+
+ imlib_blend_image_onto_image(image, 0,
+ 0, 0, vdrWidth, vdrHeight,
+ 0, 0, width, height);
+
+ imlib_render_image_on_drawable(0, 0);
+ imlib_free_image();
+ }
+
+ XSetWindowBackgroundPixmap(disp, win, pix);
+ XClearWindow(disp, win);
+
+ bufferLock.Unlock();
+
+ return success;
+}
+
+//***************************************************************************
+// Dump Image
+//***************************************************************************
+
+void GraphTft::dumpImage(Imlib_Image image)
+{
+ if (*dump)
+ {
+ imlib_context_set_image(image);
+ imlib_save_image(dump);
+ }
+}
+
+//***************************************************************************
+// Run loop
+//***************************************************************************
+
+int GraphTft::run()
+{
+ XEvent ev;
+ KeySym key_symbol;
+ int running = true;
+ int update = false;
+
+ while (running)
+ {
+ while (XPending(disp))
+ {
+ XNextEvent(disp, &ev);
+
+ switch (ev.type)
+ {
+ case Expose: update = true; break;
+ case CreateNotify: tell(eloAlways, "Create"); break;
+ case DestroyNotify: tell(eloAlways, "Destroy"); break;
+ case MotionNotify:
+ onMotion();
+ onButtonPress(ev, na); break;
+ case ButtonRelease: onButtonPress(ev, no); break;
+ case ButtonPress: onButtonPress(ev, yes); break;
+
+ case KeyPress:
+ {
+ key_symbol = XKeycodeToKeysym(disp, ev.xkey.keycode, 0);
+ tell(eloAlways, "Key (%ld) pressed", key_symbol);
+
+ if (key_symbol == XK_Escape && !ignoreEsc)
+ running = false;
+ else
+ onKeyPress(ev);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (update)
+ {
+ update = false;
+ paint();
+ }
+
+ // check mouse cursor
+
+ if (hideCursorDelay && lastMotion < time(0) - hideCursorDelay && cursorVisible)
+ hideCursor();
+
+ if (!XPending(disp))
+ usleep(10000);
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// On Motion
+//***************************************************************************
+
+int GraphTft::onMotion()
+{
+ lastMotion = time(0);
+
+ if (!cursorVisible)
+ showCursor();
+
+ return done;
+}
+
+//***************************************************************************
+// On key Press (keyboard)
+//***************************************************************************
+
+int GraphTft::onKeyPress(XEvent event)
+{
+ int x = event.xmotion.x;
+ int y = event.xmotion.y;
+ int flag = ComThread::efKeyboard;
+ int button = event.xkey.keycode;
+
+ thread->mouseEvent(x, y, button, flag);
+
+ return 0;
+}
+
+//***************************************************************************
+// On Button Press (mouse)
+//***************************************************************************
+
+int GraphTft::onButtonPress(XEvent event, int press)
+{
+ static long lastTime = 0;
+ static int lastButton = na;
+ static int lastPressX = 0;
+ static int lastPressY = 0;
+ static int lastPressed = na;
+
+ int x = event.xmotion.x;
+ int y = event.xmotion.y;
+ int flag = 0;
+
+ if (press != na)
+ tell(eloAlways, "Button '%s' at (%d/%d) button %d, time (%ld)",
+ press ? "press" : "release",
+ event.xmotion.x, event.xmotion.y,
+ event.xbutton.button,
+ event.xbutton.time);
+
+ if (resize)
+ {
+ x = (int)(((double)event.xmotion.x / (double)width) * (double)vdrWidth);
+ y = (int)(((double)event.xmotion.y / (double)height) * (double)vdrHeight);
+ }
+
+ if (press == no)
+ {
+ // on button release
+
+ if (abs(y - lastPressY) < 5 && abs(x - lastPressX) < 5)
+ {
+ if (lastButton == (int)event.xbutton.button
+ && event.xbutton.button == cGraphTftComService::mbLeft
+ && event.xbutton.time-lastTime < 300)
+ {
+ tell(eloAlways, "assuming double-click");
+ flag |= cGraphTftComService::efDoubleClick;
+ }
+
+ thread->mouseEvent(x, y, event.xbutton.button, flag);
+
+ lastTime = event.xbutton.time;
+ lastButton = event.xbutton.button;
+ }
+
+ lastPressed = press;
+ }
+ else if (press == na && lastPressed == yes)
+ {
+ // no Button action, only motion with pressed button
+
+ if (abs(y - lastPressY) > 5 || abs(x - lastPressX) > 5)
+ {
+ if (abs(y - lastPressY) > abs(x - lastPressX))
+ {
+ tell(eloAlways, "V-Whipe of (%d) pixel detected", y - lastPressY);
+ thread->mouseEvent(x, y,
+ lastButton, cGraphTftComService::efVWhipe,
+ y - lastPressY);
+ }
+ else
+ {
+ tell(eloAlways, "H-Whipe of (%d) pixel detected", x - lastPressX);
+ thread->mouseEvent(x, y,
+ lastButton, cGraphTftComService::efHWhipe,
+ x - lastPressX);
+ }
+
+ lastPressX = x;
+ lastPressY = y;
+ }
+ }
+ else if (press == yes)
+ {
+ // on button press
+
+ lastPressX = x;
+ lastPressY = y;
+ lastPressed = press;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Hide Cursor
+//***************************************************************************
+
+void GraphTft::hideCursor()
+{
+ // Hide the cursor
+
+ Cursor invisibleCursor;
+ Pixmap bitmapNoData;
+ XColor black;
+
+ static char noData[] = { 0,0,0,0,0,0,0,0 };
+ black.red = black.green = black.blue = 0;
+
+ tell(eloAlways, "Hide mouse cursor");
+
+ bitmapNoData = XCreateBitmapFromData(disp, win, noData, 8, 8);
+ invisibleCursor = XCreatePixmapCursor(disp, bitmapNoData, bitmapNoData,
+ &black, &black, 0, 0);
+ XDefineCursor(disp, win, invisibleCursor);
+ XFreeCursor(disp, invisibleCursor);
+
+ cursorVisible = no;
+}
+
+//***************************************************************************
+// Show Cursor
+//***************************************************************************
+
+void GraphTft::showCursor()
+{
+ // Restore the X left facing cursor
+
+ Cursor cursor;
+
+ tell(eloAlways, "Show mouse cursor");
+
+ cursor = XCreateFontCursor(disp, XC_left_ptr);
+ XDefineCursor(disp, win, cursor);
+ XFreeCursor(disp, cursor);
+
+ cursorVisible = yes;
+}
diff --git a/graphtft-fe/graphtft.hpp b/graphtft-fe/graphtft.hpp
new file mode 100644
index 0000000..e6f2d9a
--- /dev/null
+++ b/graphtft-fe/graphtft.hpp
@@ -0,0 +1,143 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File graphtft.hpp
+// Date 28.10.06 - Jörg Wendel
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//--------------------------------------------------------------------------
+// Class GraphTft
+// Class ComThread
+//***************************************************************************
+
+#ifndef __GRAPHTFT_HPP__
+#define __GRAPHTFT_HPP__
+
+#include <X11/Xlib.h>
+#include <Imlib2.h>
+#include <string.h>
+#include <unistd.h>
+
+#define __FRONTEND
+
+#include "../common.h"
+#include "../service.h"
+
+#include "thread.h"
+#include "tcpchannel.h"
+
+class GraphTft;
+
+//***************************************************************************
+// Communication Thread
+//***************************************************************************
+
+class ComThread : public cMyThread, public cGraphTftComService
+{
+ public:
+
+ enum Misc
+ {
+ maxBuffer = 1024*1024
+ };
+
+ ComThread();
+ virtual ~ComThread();
+
+ void stop();
+
+ int mouseEvent(int x, int y, int button, int flag, int data = 0);
+ int keyEvent(int key, int flag);
+
+ const char* getBuffer() { return buffer; }
+ int getSize() { return header->size; }
+
+ void setHost(const char* aHost) { strcpy(host, aHost); }
+ void setPort(unsigned short aPort) { port = aPort; }
+ void setClient(GraphTft* aClient) { client = aClient; }
+ void setJpegQuality(int quality) { jpegQuality = quality; }
+
+ protected:
+
+ void Action();
+ int read();
+
+ TcpChannel* line;
+
+ char* buffer;
+ int bufferSize;
+ GraphTft* client;
+
+ long timeout;
+ int running;
+ int jpegQuality;
+ TcpChannel::Header* header;
+ unsigned short port;
+ char host[100];
+};
+
+//***************************************************************************
+// Graph TFT
+//***************************************************************************
+
+class GraphTft
+{
+ public:
+
+ GraphTft();
+ virtual ~GraphTft();
+
+ int init();
+ int exit();
+ int start();
+ int run();
+ int paint();
+
+ void setArgs(int argc, char *argv[]);
+ int sendEvent();
+ void updateImage(const unsigned char* buffer, int size);
+ void dumpImage(Imlib_Image image);
+ void showUsage();
+ int onMotion();
+ int onButtonPress(XEvent event, int press);
+ int onKeyPress(XEvent event);
+
+ static void setEloquence(int aElo) { eloquence = aElo; }
+ static int getEloquence() { return eloquence; }
+
+ protected:
+
+ // functions
+
+ void hideCursor();
+ void showCursor();
+ void hideBorder();
+
+ // data
+
+ Window win;
+ Display* disp;
+ int screen;
+ Pixmap pix;
+ Imlib_Image image;
+ ComThread* thread;
+ int hideCursorDelay;
+ int resize;
+ int managed;
+ int width;
+ int height;
+ int border;
+ char dump[200];
+ int showHelp;
+ cMutex bufferLock;
+ int vdrWidth;
+ int vdrHeight;
+ int cursorVisible;
+ int borderVisible;
+ int ignoreEsc;
+ time_t lastMotion;
+
+ static int eloquence;
+};
+
+//***************************************************************************
+#endif // __GRAPHTFT_HPP__
diff --git a/graphtft-fe/main.cc b/graphtft-fe/main.cc
new file mode 100644
index 0000000..11f8fcc
--- /dev/null
+++ b/graphtft-fe/main.cc
@@ -0,0 +1,19 @@
+
+
+#include "graphtft.hpp"
+
+//***************************************************************************
+// Main
+//***************************************************************************
+
+int main(int argc, char *argv[])
+{
+ GraphTft graphTft;
+
+ graphTft.setArgs(argc, argv);
+
+ graphTft.start();
+
+ return 0;
+}
+
diff --git a/graphtft-fe/tcpchannel.cc b/graphtft-fe/tcpchannel.cc
new file mode 100644
index 0000000..4e2a239
--- /dev/null
+++ b/graphtft-fe/tcpchannel.cc
@@ -0,0 +1,532 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File tcpchannel.cc
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+// (c) 2006-2014 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class TcpChannel
+//***************************************************************************
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+
+#include "../common.h"
+#include "tcpchannel.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+TcpChannel::TcpChannel(int aTimeout, int aHandle)
+{
+ handle = aHandle;
+ timeout = aTimeout;
+
+ localAddr = 0;
+ port = 0;
+ remoteAddr = 0;
+
+ *localHost = 0;
+ *remoteHost = 0;
+
+ nTtlSent = 0;
+ nTtlReceived = 0;
+
+ lookAheadChar = false;
+ lookAhead = 0;
+}
+
+TcpChannel::~TcpChannel()
+{
+ close();
+}
+
+//***************************************************************************
+// OpenLstn -> Start Listener
+//***************************************************************************
+
+int TcpChannel::openLstn(unsigned short aPort, const char* aLocalHost)
+{
+ struct sockaddr_in localSockAddr;
+ struct hostent* hostInfo;
+ int value = 1;
+ int aHandle;
+
+ // clear
+
+ memset((char*)&localSockAddr, 0, sizeof(localSockAddr));
+
+ // init
+
+ localSockAddr.sin_family = AF_INET;
+
+ // resolve local host
+
+ if (aLocalHost && *aLocalHost)
+ {
+ // search alias
+
+ if ((hostInfo = ::gethostbyname(aLocalHost)))
+ memcpy((char*)&localAddr, hostInfo->h_addr, hostInfo->h_length);
+
+ else if ((unsigned int)(localAddr = inet_addr(aLocalHost)) == INADDR_NONE)
+ {
+ tell(1, "unknown hostname '%s'", aLocalHost);
+ return fail;
+ }
+
+ // set local endpoint
+
+ memcpy(&localSockAddr.sin_addr, &localAddr, sizeof(struct in_addr));
+ }
+
+ // Server-Socket
+
+ localSockAddr.sin_port = htons(aPort);
+
+ // open socket
+
+ if ((aHandle = ::socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ tell(1, "Error: ");
+ return fail;
+ }
+
+ // set socket non-blocking
+
+ if (fcntl(aHandle, F_SETFL, O_NONBLOCK) < 0)
+ tell(1, "Error: Setting socket options failed, errno (%d)", errno);
+
+ setsockopt(aHandle, SOL_SOCKET, SO_REUSEADDR,
+ (char*)&value, sizeof(value));
+
+ // bind address to socket
+
+ if (::bind(aHandle, (struct sockaddr*)&localSockAddr, sizeof(localSockAddr)) < 0)
+ {
+ ::close(aHandle);
+ tell(1, "Error: Bind failed, errno (%d)", errno);
+
+ return fail;
+ }
+
+ if (::listen(aHandle, 5) < 0)
+ {
+ ::close(aHandle);
+
+ return fail;
+ }
+
+ // save
+
+ handle = aHandle;
+ port = aPort;
+
+ return success;
+}
+
+//***************************************************************************
+// Open
+//***************************************************************************
+
+int TcpChannel::open(unsigned short aPort, const char* aHost)
+{
+ const char* hostName;
+ struct sockaddr_in localSockAddr, remoteSockAddr;
+ struct hostent* hostInfo;
+ int aHandle;
+
+ if (!aHost || !*aHost)
+ return fail;
+
+ hostName = aHost;
+
+ // clear
+
+ memset((char*)&localSockAddr, 0, sizeof(localSockAddr));
+ memset((char*)&remoteSockAddr, 0, sizeof(remoteSockAddr));
+
+ // init
+
+ localSockAddr.sin_family = remoteSockAddr.sin_family = AF_INET;
+ remoteSockAddr.sin_port = htons(aPort);
+
+ // resolve local host
+
+ if (localHost && *localHost)
+ {
+ // search alias
+
+ if ((hostInfo = ::gethostbyname(localHost)))
+ memcpy((char*)&localAddr, hostInfo->h_addr, hostInfo->h_length);
+
+ else if ((localAddr = inet_addr(localHost)) == (int)INADDR_NONE)
+ return errUnknownHostname;
+
+ // set local endpoint
+
+ memcpy(&localSockAddr.sin_addr, &localAddr, sizeof(struct in_addr));
+ }
+
+ // map hostname to ip
+
+ if ((hostInfo = ::gethostbyname(hostName)))
+ memcpy((char*)&remoteAddr, hostInfo->h_addr, hostInfo->h_length);
+
+ else if ((remoteAddr = inet_addr(hostName)) == (int)INADDR_NONE)
+ return errUnknownHostname;
+
+ // save hostname
+
+ strncpy(remoteHost, hostName, sizeof(remoteHost));
+
+ // set sockaddr
+
+ memcpy(&remoteSockAddr.sin_addr, &remoteAddr, sizeof(struct in_addr));
+
+ // create new socket
+
+ if ((aHandle = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ return errOpenEndpointFailed;
+
+ // bind only if localSockAddr is set
+
+ if (*((int*)&localSockAddr.sin_addr) != 0)
+ {
+ // bind local address to socket
+
+ if (::bind(aHandle, (struct sockaddr*)&localSockAddr, sizeof(localSockAddr)) < 0)
+ {
+ ::close(aHandle);
+
+ return errBindAddressFailed;
+ }
+ }
+
+ // connect to server
+
+ if (connect(aHandle, (struct sockaddr*)&remoteSockAddr, sizeof(remoteSockAddr)) < 0)
+ {
+ ::close(aHandle);
+
+ if (errno != ECONNREFUSED)
+ return errConnectFailed;
+
+ return wrnNoResponseFromServer;
+ }
+
+ // save results
+
+ handle = aHandle;
+ port = aPort;
+
+ return success;
+}
+
+//***************************************************************************
+// Read
+//***************************************************************************
+
+int TcpChannel::read(char* buf, int bufLen)
+{
+ int nfds, result;
+ fd_set readFD;
+ int nReceived;
+ struct timeval wait;
+
+ if (!handle)
+ return fail;
+
+ memset(buf, 0, bufLen);
+ nReceived = 0;
+
+ if (lookAhead)
+ {
+ *(buf) = lookAheadChar;
+ lookAhead = false;
+ nReceived++;
+ }
+
+ while (nReceived < bufLen)
+ {
+ result = ::read(handle, buf + nReceived, bufLen - nReceived);
+
+ if (result < 0)
+ {
+ if (errno != EWOULDBLOCK)
+ return checkErrno();
+
+ // time-out for select
+
+ wait.tv_sec = timeout;
+ wait.tv_usec = 0;
+
+ // clear and set file-descriptors
+
+ FD_ZERO(&readFD);
+ FD_SET(handle, &readFD);
+
+ // look event
+
+ if ((nfds = ::select(handle+1, &readFD, 0, 0, &wait)) < 0)
+ return checkErrno();
+
+ // no event occured -> timeout
+
+ if (nfds == 0)
+ return wrnTimeout;
+ }
+
+ else if (result == 0)
+ {
+ // connection closed -> eof received
+
+ return errConnectionClosed;
+ }
+
+ else
+ {
+ // inc read char count
+
+ nReceived += result;
+ }
+ }
+
+ nTtlReceived += nReceived;
+
+ return success;
+}
+
+//***************************************************************************
+// Look
+//***************************************************************************
+
+int TcpChannel::look(int aTimeout)
+{
+ struct timeval tv;
+ fd_set readFD, writeFD, exceptFD;
+ int n;
+
+ if (!handle)
+ return fail;
+
+ // time-out for select
+
+ tv.tv_sec = aTimeout;
+ tv.tv_usec = 1;
+
+ // clear and set file-descriptors
+
+ FD_ZERO(&readFD);
+ FD_ZERO(&writeFD);
+ FD_ZERO(&exceptFD);
+
+ FD_SET(handle, &readFD);
+ FD_SET(handle, &writeFD);
+ FD_SET(handle, &exceptFD);
+
+ // look event
+
+ n = ::select(handle+1, &readFD, (aTimeout ? 0 : &writeFD), &exceptFD, &tv);
+
+ if (n < 0)
+ return checkErrno();
+
+ // check exception
+
+ if (FD_ISSET(handle, &exceptFD))
+ return errUnexpectedEvent;
+
+ // check write ok
+
+ if (!FD_ISSET(handle, &writeFD))
+ return wrnChannelBlocked;
+
+ // check read-event
+
+ if (!FD_ISSET(handle, &readFD))
+ return wrnNoEventPending;
+
+ // check first-char
+
+ if (::read(handle, &lookAheadChar, 1) == 0)
+ return errConnectionClosed;
+
+ // look ahead char received
+
+ lookAhead = true;
+
+ return success;
+}
+
+//***************************************************************************
+// Listen
+//***************************************************************************
+
+int TcpChannel::listen(TcpChannel*& child)
+{
+ struct sockaddr_in remote;
+ struct timeval tv;
+ fd_set readFD;
+ int aHandle, num, len;
+
+ child = 0;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+ len = sizeof(remote);
+
+ // clear and set file-descriptor
+
+ FD_ZERO(&readFD);
+ FD_SET(handle, &readFD);
+
+ // call select to look for request
+
+ if ((num = ::select(handle+1, &readFD,(fd_set*)0,(fd_set*)0, &tv)) < 0)
+ return checkErrno();
+
+ if (!FD_ISSET(handle, &readFD))
+ return wrnNoConnectIndication;
+
+ // accept client
+
+ if ((aHandle = ::accept(handle, (struct sockaddr*)&remote, (socklen_t*)&len)) < 0)
+ {
+ tell(1, "Error: Accept failed, errno was %d - '%s'", errno, strerror(errno));
+ return errAcceptFailed;
+ }
+
+ // set none blocking, event for the new connection
+
+ if (fcntl(aHandle, F_SETFL, O_NONBLOCK) < 0)
+ return fail;
+
+ // create new tcp channel
+
+ child = new TcpChannel(timeout, aHandle);
+
+ return success;
+}
+
+//***************************************************************************
+// Write to client
+//***************************************************************************
+
+int TcpChannel::write(int command, const char* buf, int bufLen)
+{
+ struct timeval wait;
+ int result, nfds;
+ fd_set writeFD;
+ int nSent = 0;
+ Header header;
+
+ if (!handle)
+ return fail;
+
+#ifdef VDR_PLUGIN
+ cMutexLock lock(&_mutex);
+#endif
+
+ if (buf && !bufLen)
+ bufLen = strlen(buf);
+
+ tell(eloDebug, "Writing (%ld) header bytes, command (%d), size (%d)",
+ sizeof(Header), command, bufLen);
+
+ header.command = htonl(command);
+ header.size = htonl(bufLen);
+ result = ::write(handle, &header, sizeof(Header));
+
+ if (result != sizeof(Header))
+ return errIOError;
+
+ if (!buf)
+ return success;
+
+ tell(eloDebug, "Writing (%d) kb now", bufLen/1024);
+
+ do
+ {
+ result = ::write(handle, buf + nSent, bufLen - nSent);
+
+ if (result < 0)
+ {
+ if (errno != EWOULDBLOCK)
+ return checkErrno();
+
+ // time-out for select
+
+ wait.tv_sec = timeout;
+ wait.tv_usec = 0;
+
+ // clear and set file-descriptors
+
+ FD_ZERO(&writeFD);
+ FD_SET(handle, &writeFD);
+
+ // look event
+
+ if ((nfds = ::select(handle+1, 0, &writeFD, 0, &wait)) < 0)
+ {
+ // Error: Select failed
+
+ return checkErrno();
+ }
+
+ // no event occured -> timeout
+
+ if (nfds == 0)
+ return wrnTimeout;
+ }
+ else
+ {
+ nSent += result;
+ }
+
+ } while (nSent < bufLen);
+
+ // increase send counter
+
+ nTtlSent += nSent;
+
+ return success;
+}
+
+//***************************************************************************
+// Close
+//***************************************************************************
+
+int TcpChannel::close()
+{
+ if (handle)
+ {
+ ::close(handle);
+ handle = 0;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Check Errno
+//***************************************************************************
+
+int TcpChannel::checkErrno()
+{
+ switch (errno)
+ {
+ case EINTR: return wrnSysInterrupt;
+ case EBADF: return errInvalidEndpoint;
+ case EWOULDBLOCK: return wrnNoDataAvaileble;
+ case ECONNRESET: return errConnectionClosed;
+ default: return errIOError;
+ }
+}
diff --git a/graphtft-fe/tcpchannel.h b/graphtft-fe/tcpchannel.h
new file mode 100644
index 0000000..c7b5881
--- /dev/null
+++ b/graphtft-fe/tcpchannel.h
@@ -0,0 +1,97 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File tcpchannel.h
+// Date 31.10.06
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+// (c) 2006-2014 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class TcpChannel
+//***************************************************************************
+
+#ifndef __GTFT_TCPCHANNEL_H__
+#define __GTFT_TCPCHANNEL_H__
+
+//***************************************************************************
+// Class TcpChannel
+//***************************************************************************
+
+class TcpChannel
+{
+ public:
+
+ // declarations
+
+ enum Errors
+ {
+ errChannel = -100,
+
+ errUnknownHostname,
+ errBindAddressFailed,
+ errAcceptFailed,
+ errListenFailed,
+ errConnectFailed,
+ errIOError,
+ errConnectionClosed,
+ errInvalidEndpoint,
+ errOpenEndpointFailed,
+
+ // Warnungen
+
+ wrnNoEventPending,
+ errUnexpectedEvent,
+ wrnChannelBlocked,
+ wrnNoConnectIndication,
+ wrnNoResponseFromServer,
+ wrnNoDataAvaileble,
+ wrnSysInterrupt,
+ wrnTimeout
+ };
+
+#pragma pack(1)
+ struct Header
+ {
+ int command;
+ int size;
+ };
+#pragma pack()
+
+ // object
+
+ TcpChannel(int aTimeout = 2, int aHandle = 0);
+ ~TcpChannel();
+
+ // api function
+
+ int openLstn(unsigned short aPort, const char* aLocalHost = 0);
+ int open(unsigned short aPort, const char* aHost);
+ int close();
+ int listen(TcpChannel*& child);
+ int look(int aTimeout);
+ int read(char* buf, int bufLen);
+ int write(int command, const char* buf = 0, int bufLen = 0);
+ int isConnected() { return handle != 0; }
+
+ private:
+
+ int checkErrno();
+
+ int handle;
+ unsigned short port;
+ char localHost[100];
+ char remoteHost[100];
+ long localAddr;
+ long remoteAddr;
+ long timeout;
+ int lookAheadChar;
+ int lookAhead;
+ int nTtlReceived;
+ int nTtlSent;
+
+#ifdef VDR_PLUGIN
+ cMutex _mutex;
+#endif
+};
+
+//***************************************************************************
+#endif // __GTFT_TCPCHANNEL_H__
diff --git a/graphtft-fe/thread.cc b/graphtft-fe/thread.cc
new file mode 100644
index 0000000..6982341
--- /dev/null
+++ b/graphtft-fe/thread.cc
@@ -0,0 +1,383 @@
+/*
+ * thread.c: A simple thread base class
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#include <errno.h>
+#include <linux/unistd.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "thread.h"
+#include "../common.h"
+
+static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+ struct timeval now;
+ if (gettimeofday(&now, NULL) == 0) { // get current time
+ now.tv_usec += MillisecondsFromNow * 1000; // add the timeout
+ while (now.tv_usec >= 1000000) { // take care of an overflow
+ now.tv_sec++;
+ now.tv_usec -= 1000000;
+ }
+ Abstime->tv_sec = now.tv_sec; // seconds
+ Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
+ return true;
+ }
+ return false;
+}
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+ signaled = false;
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+ pthread_cond_broadcast(&cond); // wake up any sleepers
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+ cCondWait w;
+ w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+ pthread_mutex_lock(&mutex);
+ if (!signaled) {
+ if (TimeoutMs) {
+ struct timespec abstime;
+ if (GetAbsTime(&abstime, TimeoutMs)) {
+ while (!signaled) {
+ if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+ break;
+ }
+ }
+ }
+ else
+ pthread_cond_wait(&cond, &mutex);
+ }
+ bool r = signaled;
+ signaled = false;
+ pthread_mutex_unlock(&mutex);
+ return r;
+}
+
+void cCondWait::Signal(void)
+{
+ pthread_mutex_lock(&mutex);
+ signaled = true;
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+ pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+ pthread_cond_broadcast(&cond); // wake up any sleepers
+ pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+ if (Mutex.locked) {
+ int locked = Mutex.locked;
+ Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
+ // does an implicit unlock of the mutex
+ pthread_cond_wait(&cond, &Mutex.mutex);
+ Mutex.locked = locked;
+ }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+ bool r = true; // true = condition signaled, false = timeout
+
+ if (Mutex.locked) {
+ struct timespec abstime;
+ if (GetAbsTime(&abstime, TimeoutMs)) {
+ int locked = Mutex.locked;
+ Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+ // does an implicit unlock of the mutex.
+ if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
+ r = false;
+ Mutex.locked = locked;
+ }
+ }
+ return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+ pthread_cond_broadcast(&cond);
+}
+
+// --- cRwLock ---------------------------------------------------------------
+
+cRwLock::cRwLock(bool PreferWriter)
+{
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
+ pthread_rwlock_init(&rwlock, &attr);
+}
+
+cRwLock::~cRwLock()
+{
+ pthread_rwlock_destroy(&rwlock);
+}
+
+bool cRwLock::Lock(bool Write, int TimeoutMs)
+{
+ int Result = 0;
+ struct timespec abstime;
+ if (TimeoutMs) {
+ if (!GetAbsTime(&abstime, TimeoutMs))
+ TimeoutMs = 0;
+ }
+ if (Write)
+ Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
+ else
+ Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
+ return Result == 0;
+}
+
+void cRwLock::Unlock(void)
+{
+ pthread_rwlock_unlock(&rwlock);
+}
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+ locked = 0;
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+ pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+ pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+ pthread_mutex_lock(&mutex);
+ locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+ pthread_mutex_unlock(&mutex);
+}
+
+// --- cMyThread ---------------------------------------------------------------
+
+tThreadId cMyThread::mainThreadId = 0;
+
+cMyThread::cMyThread(const char *Description)
+{
+ active = running = false;
+ childTid = 0;
+ childThreadId = 0;
+ description = NULL;
+ if (Description)
+ SetDescription(Description);
+}
+
+cMyThread::~cMyThread()
+{
+ Cancel(); // just in case the derived class didn't call it
+ free(description);
+}
+
+void cMyThread::SetDescription(const char *Description)
+{
+ free(description);
+ description = NULL;
+ description = strdup(Description);
+}
+
+void *cMyThread::StartThread(cMyThread *Thread)
+{
+ Thread->childThreadId = ThreadId();
+ if (Thread->description) {
+ printf("%s thread started (pid=%d, tid=%d)\n", Thread->description, getpid(), Thread->childThreadId);
+#ifdef PR_SET_NAME
+ if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
+ printf("%s thread naming failed (pid=%d, tid=%d)\n", Thread->description, getpid(), Thread->childThreadId);
+#endif
+ }
+ Thread->Action();
+ if (Thread->description)
+ printf("%s thread ended (pid=%d, tid=%d)\n", Thread->description, getpid(), Thread->childThreadId);
+ Thread->running = false;
+ Thread->active = false;
+ return NULL;
+}
+
+#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
+
+bool cMyThread::Start(void)
+{
+ if (!running)
+ {
+ if (active)
+ {
+ return true;
+ }
+ if (!active)
+ {
+ active = running = true;
+ if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+ pthread_detach(childTid); // auto-reap
+ }
+ else {
+ printf("Error: ...\n");
+ active = running = false;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool cMyThread::Active(void)
+{
+ if (active) {
+ //
+ // Single UNIX Spec v2 says:
+ //
+ // The pthread_kill() function is used to request
+ // that a signal be delivered to the specified thread.
+ //
+ // As in kill(), if sig is zero, error checking is
+ // performed but no signal is actually sent.
+ //
+ int err;
+ if ((err = pthread_kill(childTid, 0)) != 0) {
+ if (err != ESRCH)
+ printf("Error: ...\n");
+ childTid = 0;
+ active = running = false;
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+void cMyThread::Cancel(int WaitSeconds)
+{
+ running = false;
+ if (active && WaitSeconds > -1) {
+ if (WaitSeconds > 0) {
+ for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
+ if (!Active())
+ return;
+ cCondWait::SleepMs(10);
+ }
+ printf("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...\n",
+ description ? description : "", childThreadId, WaitSeconds);
+ }
+ pthread_cancel(childTid);
+ childTid = 0;
+ active = false;
+ }
+}
+
+tThreadId cMyThread::ThreadId(void)
+{
+ return syscall(__NR_gettid);
+}
+
+void cMyThread::SetMainThreadId(void)
+{
+ if (mainThreadId == 0)
+ mainThreadId = ThreadId();
+ else
+ printf("ERROR: attempt to set main thread id to %d while it already is %d\n",
+ ThreadId(), mainThreadId);
+}
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+ mutex = NULL;
+ locked = false;
+ Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+ if (mutex && locked)
+ mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+ if (Mutex && !mutex) {
+ mutex = Mutex;
+ Mutex->Lock();
+ locked = true;
+ return true;
+ }
+ return false;
+}
+
+// --- cMyThreadLock -----------------------------------------------------------
+
+cMyThreadLock::cMyThreadLock(cMyThread *Thread)
+{
+ thread = NULL;
+ locked = false;
+ Lock(Thread);
+}
+
+cMyThreadLock::~cMyThreadLock()
+{
+ if (thread && locked)
+ thread->Unlock();
+}
+
+bool cMyThreadLock::Lock(cMyThread *Thread)
+{
+ if (Thread && !thread) {
+ thread = Thread;
+ Thread->Lock();
+ locked = true;
+ return true;
+ }
+ return false;
+}
diff --git a/graphtft-fe/thread.h b/graphtft-fe/thread.h
new file mode 100644
index 0000000..e40d222
--- /dev/null
+++ b/graphtft-fe/thread.h
@@ -0,0 +1,160 @@
+/*
+ * thread.h: A simple thread base class
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: thread.h 2.0 2007/02/24 16:13:28 kls Exp $
+ */
+
+#ifndef __MYTHREAD_H
+#define __MYTHREAD_H
+
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+class cCondWait
+{
+ private:
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ bool signaled;
+ public:
+ cCondWait(void);
+ ~cCondWait();
+ static void SleepMs(int TimeoutMs);
+ ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+ ///< milliseconds, immediately giving up the calling thread's time
+ ///< slice and thus avoiding a "busy wait".
+ ///< In order to avoid a possible busy wait, TimeoutMs will be automatically
+ ///< limited to values >2.
+ bool Wait(int TimeoutMs = 0);
+ ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+ ///< forever if TimeoutMs is 0.
+ ///< \return Returns true if Signal() has been called, false it the given
+ ///< timeout has expired.
+ void Signal(void);
+ ///< Signals a caller of Wait() that the condition it is waiting for is met.
+};
+
+class cMutex;
+
+class cCondVar {
+private:
+ pthread_cond_t cond;
+public:
+ cCondVar(void);
+ ~cCondVar();
+ void Wait(cMutex &Mutex);
+ bool TimedWait(cMutex &Mutex, int TimeoutMs);
+ void Broadcast(void);
+ };
+
+class cRwLock {
+private:
+ pthread_rwlock_t rwlock;
+public:
+ cRwLock(bool PreferWriter = false);
+ ~cRwLock();
+ bool Lock(bool Write, int TimeoutMs = 0);
+ void Unlock(void);
+ };
+
+class cMutex {
+ friend class cCondVar;
+private:
+ pthread_mutex_t mutex;
+ int locked;
+public:
+ cMutex(void);
+ ~cMutex();
+ void Lock(void);
+ void Unlock(void);
+ };
+
+typedef pid_t tThreadId;
+
+class cMyThread {
+ friend class cMyThreadLock;
+private:
+ bool active;
+ bool running;
+ pthread_t childTid;
+ tThreadId childThreadId;
+ cMutex mutex;
+ char *description;
+ static tThreadId mainThreadId;
+ static void *StartThread(cMyThread *Thread);
+protected:
+ void SetPriority(int Priority);
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+ virtual void Action(void) = 0;
+ ///< A derived cMyThread class must implement the code it wants to
+ ///< execute as a separate thread in this function. If this is
+ ///< a loop, it must check Running() repeatedly to see whether
+ ///< it's time to stop.
+ bool Running(void) { return running; }
+ ///< Returns false if a derived cMyThread object shall leave its Action()
+ ///< function.
+ void Cancel(int WaitSeconds = 0);
+ ///< Cancels the thread by first setting 'running' to false, so that
+ ///< the Action() loop can finish in an orderly fashion and then waiting
+ ///< up to WaitSeconds seconds for the thread to actually end. If the
+ ///< thread doesn't end by itself, it is killed.
+ ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+ ///< returns immediately, without killing the thread.
+public:
+ cMyThread(const char *Description = NULL);
+ ///< Creates a new thread.
+ ///< If Description is present, a log file entry will be made when
+ ///< the thread starts and stops. The Start() function must be called
+ ///< to actually start the thread.
+ virtual ~cMyThread();
+ void SetDescription(const char *Description);
+ bool Start(void);
+ ///< Actually starts the thread.
+ ///< If the thread is already running, nothing happens.
+ bool Active(void);
+ ///< Checks whether the thread is still alive.
+ static tThreadId ThreadId(void);
+ static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
+ static void SetMainThreadId(void);
+ };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
+// short ones.
+
+class cMutexLock {
+private:
+ cMutex *mutex;
+ bool locked;
+public:
+ cMutexLock(cMutex *Mutex = NULL);
+ ~cMutexLock();
+ bool Lock(cMutex *Mutex);
+ };
+
+// cMyThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMyThreadLock may itself use a cMyThreadLock to make one longer lock instead of many
+// short ones.
+
+class cMyThreadLock {
+private:
+ cMyThread *thread;
+ bool locked;
+public:
+ cMyThreadLock(cMyThread *Thread = NULL);
+ ~cMyThreadLock();
+ bool Lock(cMyThread *Thread);
+ };
+
+//#define LOCK_THREAD cMyThreadLock ThreadLock(this)
+
+#endif //__MYTHREAD_H