summaryrefslogtreecommitdiff
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
downloadvdr-plugin-graphtftng-master.tar.gz
vdr-plugin-graphtftng-master.tar.bz2
-rw-r--r--.gitignore10
-rw-r--r--COPYING340
-rw-r--r--HISTORY.h660
-rw-r--r--Makefile319
-rw-r--r--README123
-rw-r--r--README.themes419
-rw-r--r--TODO15
-rw-r--r--common.c703
-rw-r--r--common.h312
-rw-r--r--comthread.c414
-rw-r--r--comthread.h95
-rw-r--r--contrib/graphtft-fe.conf57
-rw-r--r--contrib/xsnow.conf29
-rw-r--r--display.c1592
-rw-r--r--display.h638
-rw-r--r--dspitems.c2614
-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
-rw-r--r--graphtftng.c792
-rw-r--r--graphtftng.h124
-rw-r--r--imlibrenderer/Makefile5
-rw-r--r--imlibrenderer/dmyrenderer/dmyrenderer.h28
-rw-r--r--imlibrenderer/fbrenderer/fbrenderer.c375
-rw-r--r--imlibrenderer/fbrenderer/fbrenderer.h65
-rw-r--r--imlibrenderer/imlib.cc81
-rw-r--r--imlibrenderer/imlibrenderer.c906
-rw-r--r--imlibrenderer/imlibrenderer.h76
-rw-r--r--imlibrenderer/xrenderer/xrenderer.c296
-rw-r--r--imlibrenderer/xrenderer/xrenderer.h54
-rw-r--r--patch/epgsearch-0.9.24.diff71
-rw-r--r--patch/extrecmenu-1.1.diff20
-rw-r--r--patch/mailbox-0.5.0-mailcount.diff78
-rw-r--r--patch/mp3-0.9.15pre14_graphtft-cover-file.diff119
-rw-r--r--patch/muggle-0.0.8_graphtft_cover_file.diff103
-rw-r--r--patch/remotetimers.patch52
-rw-r--r--patch/vdr-2.2.0_graphtftng.diff157
-rw-r--r--patch/vdr-2.3.1_graphtftng.diff157
-rw-r--r--patch/xsnow-gtftng.patch66
-rw-r--r--plasma/CMakeLists.txt27
-rw-r--r--plasma/README12
-rw-r--r--plasma/common.cc53
-rw-r--r--plasma/common.hpp15
-rw-r--r--plasma/comthread.cc226
-rw-r--r--plasma/comthread.hpp153
-rw-r--r--plasma/config.ui101
-rw-r--r--plasma/configdialog.cc42
-rw-r--r--plasma/configdialog.h28
-rw-r--r--plasma/gtft.cpp216
-rw-r--r--plasma/gtft.h66
-rwxr-xr-xplasma/install.sh12
-rw-r--r--plasma/plasma-applet-gtft.desktop16
-rw-r--r--plasma/tcpchannel.cc387
-rw-r--r--po/de_DE.po176
-rw-r--r--po/fi_FI.po179
-rw-r--r--po/it_IT.po196
-rw-r--r--renderer.c81
-rw-r--r--renderer.h102
-rw-r--r--scan.c90
-rw-r--r--scan.h75
-rw-r--r--scraper2vdr.c106
-rw-r--r--scraper2vdr.h293
-rwxr-xr-xscripts/create-channlelogo-symlinks.pl125
-rwxr-xr-xscripts/dia.sh84
-rwxr-xr-xscripts/noadcall.sh96
-rw-r--r--service.h139
-rw-r--r--setup.c335
-rw-r--r--setup.h104
-rw-r--r--span.h65
-rw-r--r--status.c715
-rw-r--r--sysinfo.c158
-rw-r--r--sysinfo.h39
-rw-r--r--tcpchannel.c422
-rw-r--r--tcpchannel.h92
-rw-r--r--test.c127
-rw-r--r--theme.c2012
-rw-r--r--theme.h1267
-rw-r--r--themes/DeepBlue.theme1469
-rw-r--r--themes/PearlHD.theme1137
-rw-r--r--themes/anthraize.theme591
-rw-r--r--themes/nOpacity.theme1276
-rw-r--r--touchthread.c322
-rw-r--r--touchthread.h87
-rw-r--r--vlookup.c798
94 files changed, 28845 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a48ab53
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*~
+*.a
+*.o
+*.log
+.dependencies
+po/*.mo
+po/*.pot
+graphtft-fe/graphtft-fe
+libvdr-graphtftng.so
+imlibrenderer/imlibtest
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/HISTORY.h b/HISTORY.h
new file mode 100644
index 0000000..6aeb4bf
--- /dev/null
+++ b/HISTORY.h
@@ -0,0 +1,660 @@
+/*
+ *
+ * ----------------------------------------------
+ * VDR Plugin 'graph-tft-ng' - Revision History
+ * ----------------------------------------------
+ *
+ */
+
+#define _VERSION "0.6.16"
+#define VERSION_DATE "13.02.2017"
+#define THEMEVERSION "0.4.1"
+
+#ifdef GIT_REV
+# define VERSION _VERSION "-GIT" GIT_REV
+#else
+# define VERSION _VERSION
+#endif
+
+/*
+ * ------------------------------------
+
+#109 Version 0.6.16, horchi 13.02.2017
+ - change: porting to newer libavutil and g++ 6.2
+
+#108 Version 0.6.15, horchi 24.11.2016
+ - change: Changed default host of graphtft-fe to localhost
+
+#107 Version 0.6.14, horchi 11.05.2016
+ - bugfix: Fixed timer state handling for epg2vdr timer
+
+#106 Version 0.6.13, horchi - 03.05.2016
+ - added: Trigger to update timerlist
+
+#105 Version 0.6.12, horchi - 02.05.2016
+ - added: support timer list even for remote timers via epg2vdr
+ -> enable WITH_EPG2VDR in Makefile
+
+#104 Version 0.6.11, horchi - 02.05.2016
+ - added: added test code for epg2vdr timer service interface
+
+#103 Version 0.6.10, horchi - 15.02.2016
+ - bugfix: fixed display of setup pages
+
+#102 Version 0.6.9, horchi - 04.02.2016
+ - bugfix: fixed typo
+
+#101 Version 0.6.8, horchi - 04.02.2016
+ - bugfix: fixed another possible divide by zero
+
+#100 Version 0.6.7, horchi - 04.02.2016
+ - bugfix: fixed possible divide by zero
+
+#99 Version 0.6.6, horchi - 03.02.2016
+ - bugfix: fixed menu display
+
+#98 Version 0.6.5, horchi - 02.02.2016
+ - change: now support max of 10 columns for 'Column' base menues (instead off 6)
+
+#97 Version 0.6.4, horchi - 01.02.2016
+ - bugfix: Fixed patch für vdr 2.2.0 and 2.3.1 (no recording data was displayed)
+ - bugfix: Fixed detection of menu section (no recording data was displayed)
+
+#96 Version 0.6.3, horchi - 05.01.2016
+ - changed: changed header path for libexif
+
+#95 Version 0.6.2, horchi - 23.12.2015
+ - added: autoflip by exif data for jpeg images
+
+#94 Version 0.6.1, horchi - 23.12.2015
+ - added: recursive scan of image folder for 'imageDir' item
+
+#93 Version 0.6.0, horchi - 22.12.2015
+ - bugfix: Switch of display for xorg usage via svdrpsend now working in all cases
+ - added: added new theme item imageDir for dia show, use like:
+ ImageDir x=0,y=0,width=1360,height=768,path=/path-to-jpegs,fit=yes,aspect_ratio=yes,rotate=1,delay=3;
+ It don't scans recursive, this will be implemented later
+
+#92 Version 0.5.7, horchi - 14.12.2015
+ - added: Switch of display for xorg usage via svdrpsend
+ - added: Switch of resolution of xorg display via svdrpsend
+
+#91 Version 0.5.6, horchi - 13.11.2015
+ - added: Support of scraper images even for recordings, in theme use:
+ (recording|replay|rowRecording|selectedRowRecording)Banner like {presentBanner}
+ (recording|replay|rowRecording|selectedRowRecording)Poster like {presentPoster}
+
+#90 Version 0.5.5, horchi - 12.11.2015
+ - change: cleared status interface from channel- and recording- locks
+ in context of vdr 2.3.1
+
+#89 Version 0.5.4, horchi - 11.11.2015
+ - added: Support of scraper images, in theme use:
+ (present|followin|event)Banner like {presentBanner}
+ (present|followin|event)Poster like {presentPoster}
+
+#88 Version 0.5.3, horchi - 08.11.2015
+ - added: Display of channel group with {channelGroup} while zapping
+
+#87 Version 0.5.2, horchi - 08.11.2015
+ - bugfix: fixed wrong channel display since vdr 2.3.1 porting
+
+#86 Version 0.5.1, horchi - 07.11.2015
+ - bugfix: fixed compile with vdr < 2.3.1
+ - added switch to compile with old patch
+ - change: A attach on alredy attachtd display force a re-attach now
+
+#85 Version 0.5.0, horchi - 16.10.2015
+ - change: VDR 2.3.1 porting
+ - change: desupport of VDR < 2.2.0
+ - change: redesign of VDR patch, less invasive
+ -> therefore some skin porting may necessary
+ since the naming of the menu section now
+ use the category name (prefixed by 'Menu') defined by vdr
+
+#84 Version 0.4.17, horchi - 03.08.2015
+ - added: autorotate by exif data for jpeg images
+
+#83 Version 0.4.16, horchi - 05.05.2015
+ - bugfix: fixed core of patch from 0.4.15
+
+#82 Version 0.4.15, horchi - 13.04.2015
+ - bugfix: delete old radio data by switching
+ radio channel (patch provided by Ulrich Eckhardt)
+
+#81 Version 0.4.14, horchi - 26.02.2015
+ - added: added check-builddep target (by Lars)
+
+#80 Version 0.4.14, horchi - 02.02.2015
+ - bugfix: fixed window position for FE - reported by nobanzai
+ - added: make install for graphtft-fe provided by 3PO
+
+#79 Version 0.4.13, horchi - 26.10.2014
+ - bugfix: fixed crash direct recording
+
+#78 Version 0.4.12, horchi - 05.10.2014
+ - bugfix: fixed crash on changes events
+
+#77 Version 0.4.11, horchi - 02.03.2014
+ - added: min to theme variables x, y, width and height
+
+#76 Version 0.4.10, horchi - 01.03.2014
+ - added: align_v to partingLine, remove leading -> from partingLine text
+
+#75 Version 0.4.9, horchi - 22.02.2014
+ - added: background image for 'Image' item (path2=)
+
+#74 Version 0.4.8, horchi - 21.02.2014
+ - bugfix: fixed framebuffer output (many thanks to reufer)
+
+#73 Version 0.4.7, horchi - 20.02.2014
+ - added: background image for TextList items (path=)
+ - added: svdrp commands to attach/dettach (ATTA/DETA) X connection (for xorg output)
+ - added: start detached if xorg not available
+ - added: apply xorg resolution without restart
+ - bugfix: fixed volume bar redraw
+ - bugfix: fixed minor redraw bugs
+ - bugfix: fixed scaling problem with xorg output
+
+#72 Version 0.4.6, horchi - 11.12.2013
+ - added: theme variables x, y, width and height
+ - added: Calc feature even to theme attribute width and x (like y of version 0.4.4)
+
+#71 Version 0.4.5, horchi - 10.12.2013
+ - bugfix: Fixed possible crash on empty menu items?!
+ - added: Calc feature even to theme attribute height (like y of version 0.4.4)
+
+#70 Version 0.4.4, horchi - 10.12.2013
+ - added: New theme variable (present,following,rowEvent,selectedRowEvent)ChannelId
+ - added: New theme variable lastHeight which is set to
+ the dynamic hight of the last drawn TextList item
+ - added: Calc feature to theme attribute y, therefore expressions like
+ y=3 + 4 and y=20 + {lastHeight} now supported (space around operator needed).
+
+#69 Version 0.4.3, horchi - 10.11.2013
+ - added: Support of multiple variable files per section
+ - added: Format option lower and upper to theme string variables
+ - bugix: Fixed volumebar (reportet by 3po)
+ - bugix: Fixed marquee and ticker effect
+ - added: new theme variables color and bg_color in format R/G/B/A, for example
+ you can now also use "color=220/125/30/255" instead of
+ "red=220,green=125,blue=30,transparent,255"
+ - added: new theme variables alpha which can used instead of transparent,
+ transparent was the wrong naming as long 255 mean opaque am 0 mean fully transparent
+ - added: multi line text scroll
+ - added: X renderer - direct X support from plugin without graphtft-fe,
+ activate with "-d xorg:...."
+ - change: Refresh only changed areas - only supported by X renderer (-d xorg:...)
+
+#68 Version 0.4.2, horchi - 03.10.2013
+ - added: option to ignore ESC key
+ - change: updated some log messages
+ - bugfix: fixed seldom crash at exit
+
+#67 Version 0.4.1, horchi - 16.05.2013
+ - change: replaced wildcard (?, ??) feature for image path with range (x-y)
+ due to conficts with ? in path names
+
+#66 Version 0.4.0, horchi - 26.04.2013
+ - change: removed DVB and DFB support
+ - change: removed PVR350 support
+ - change: removed image-magick dependency
+ - added: theme attribute overlay {yes|no} to avoid draw and clean of background
+ - change: renamed theme variable actRecordings to actTimers. Please adjust your theme(s)!
+ - change: changed default device to 'none' (instead of FB)
+ - added: new theme variables (actTimersTitle,actTimersFile,actTimersStart,actTimersStop,actTimersRunning)
+ for TextList item
+ - change: removed TextList variables actRunningRecordings and actPendingRecordings
+ - added: '@' sign to define a linfeed in the text parameter
+ - change: updated theme version to 0.4.0 due to theme changes
+ - change: removed argument path2 for items EventColumn, MenuColumn and Menu
+ use path instead
+ - bugfix: Update timer list after start
+ - added: new theme variables channelHasVtx, channelHasMultilang,
+ channelHasDD, channelIsEncrypted and channelIsRadio
+ - change: removed theme items SymVTX, SymDD, SymCrypt and Sym2ch
+ - change: added theme variable unseenMailCount (-1 if not avalible), removed theme item MailCount
+ - change: added theme variable hasNewMail, removed theme item MailSymbol
+ - added: multi client support
+ - added: set og jpeg quality to TCP protocol
+ - added: theme variabled xxxElapsed and xxxRemaining for present,following,selectedRowEvent,rowEvent,event
+ - change: #ifdef now working outside section scope
+ - change: fixed missing network/host byte order in tcp communication
+
+
+----------------------------------------------
+----------------------------------------------
+Started new branch - graphTFT NG
+----------------------------------------------
+----------------------------------------------
+
+
+----------------------------------------------
+VDR Plugin 'graph-tft' - Revision History
+----------------------------------------------
+
+
+#65 Version 0.3.12, horchi - 22.04.2013
+ - added: patch for interface to the music plugin, thanks to Dominik (dommi)
+ - added: patch for rec symbol, thanks to Ulrich Eckhardt
+ - bugfix Fixed missing background of some items since 0.3.11, reportet by Ulrich Eckhardt
+ - change: use path instead of path2 for EventColumns progress bar images,
+ (path2 is supported for compatibility)
+
+#64 Version 0.3.11, horchi - 19.04.2013
+ - added: svdrp command DUMP to trigger dump of current frame
+ - bugfix: Fixed double printed partingLine
+ - added: ubuntu-raring patch from Gerald (gda), thx
+
+#63 Version 0.3.10, horchi - 17.04.2013
+ - added: Feature, 'chg_' in image file-name now disable imlib cache (README for details)
+ - added: svdrp command PREV/NEXT to toggle NormalMode views
+ - bugfix: Fixed problem storing last selected 'NormalMode' in setup.conf
+
+#62 Version 0.3.9, Ulrich Eckhardt
+ - change: Ported to 1.7.35 and new makefile style
+
+#61 Version 0.3.8, horchi
+ - change: Ported to VDR 1.7.33
+
+#61 Version 0.3.7, Ulrich Eckhardt
+ - change: Ported to VDR 1.7.29
+ - change: Ported to actual ffmpeg libs.
+ - added: Patch for vdr 1.7.31
+
+#60 Version 0.3.6, horchi
+ - bugfix: added trigger on timer add and del
+ - added: added patch for temporary 'normal' view Mode - thanks to mini73
+
+#59 Version 0.3.5, horchi
+ - added: timers to TextList item.
+ New variables (actRecordings, actRunningRecordings, actPendingRecordings)
+
+#58 Version 0.3.4, horchi
+ - change: Ported to VDR 1.7.26 (thanks to yavdr team)
+ - change: Ported to new image-magick
+
+#57 Version 0.3.3, horchi
+ - added: added patch for plain VDR 1.7.4 (for use without zulu's extension patch)
+ - change: better handling to lookup ffmpeg header and libraries, thanks to ronnykornexl
+ - change: ported to vdr 1.7.4
+ - added: Update of italien translation, thanks to Diego Pierotto
+
+#56 Version 0.3.2, horchi
+ - bugfix: Fixed some memory bugs (thanks to Dominik)
+ - added: First version of KDE plasmoid frontend
+ - change: 64 bit porting
+ - bugfix: variable lookup problem
+ - added: noadcall.sh example to copy (store) EPG images after recording
+ - added: Support of more than 10 EPG Images by '??' instead of '?' in theme file
+ - added: keyboard support for plasma applet
+ - change: Middle click on plasma-applet assigned to 'back' due to right
+ click open the popup menu
+ - added: check command to TCP communication to detect and kick crashed clients
+ - bugfix: fixed probblem with 'blocking' TCP connections
+ - bugfix: fixed crash with directFB output
+ - change: improved Makefile to find ffmpeg header (again)
+
+#55 Version 0.3.1, horchi
+ - added: support of mouse whipes (touch whipe planned)
+ - added: svdrp command VIEW without a argument to switch to next normalView
+ - added: Support of key series for on_(dbl)click
+ - added: Support of 'ms' suffix for delay argument in theme
+ - bugfix: Fixes missing 'graphtft --help'
+ - bugfix: Fixes scale problem for touch calibration
+ - bugfix: Missing '[' ']' in menus now displayed correct
+ - added: animated images (up to 10 images displayed in a loop)
+ - bugfix: Fixes scale problem for touch calibration for eGalaxy Touchscreens
+ - bugfix: Fixed config of snapshot path
+ - bugfix: Fixed force redraw
+ - bugfix: Minor bugfixes of X-Frontend
+
+#54 Version 0.3.0, horchi
+ - change: QT not more needed by the X frontend
+ - added: Cyclic configuable force redraw (via setup.conf)
+ - added: Now Support of linefeeds in item definition (theme file)
+ - added: Theme variable 'foreground' to force items in foreground
+ - added: cascading ifdefs in themes
+ - added: support for touchMenus
+ - added: Global and section theme variables
+ - added: Support of touch devices
+ - added: Group title in setup now displayed on the tft
+ - bugfix: Crash on empty menus lists (reported by steffx)
+ - bugfix: Fixed display of event**** Variables
+ - bugfix: Fixed problem with display of wrong channel during
+ recording on primary card (thanks to googles!)
+ - change: Optimize Theme-Syntax (f.e. Delete "item=" ),
+ - added: Added if to theme parser
+ - bugfix: Many other litle Bugfixes
+
+#53 Version 0.2.2, horchi
+ - bugfix: Fixed problem with empty events
+ - added: New format instruction 'tologo' for theme variables
+ - change: Renamed svdrp command NORMALVIEW to VIEW (scripts/dia.sh ported)
+ - bugfix: Fixed refresh of OSD message
+ - change: Improved Makefile
+ - added: Warning if theme version does not match
+
+#52 Version 0.2.1, horchi
+ - bugfix: Fixed Missing 'include string.h' (thanks to ronnykornexl)
+ - added: Two new theme expressions (volumeMute and volumeLevel)
+ - bugfix: Fixed include problem
+
+#52 Version 0.2.0, horchi
+ - added: redesign of EPG menues (new theme
+ items EventColumn and EventColumnSelected)
+
+#51 Version 0.1.23-alpha, horchi
+ - added: font search path variable (fontPath)
+ to the theme item
+ - bugfix: minor fix in cover handling (if cover not set
+ by music plugin)
+ - added: now supporting transparent texts together
+ with backround image
+
+#50 Version 0.1.22-alpha, horchi
+ - bugfix: Fixed MenuKind of graphTFTMenu
+
+#49 Version 0.1.21-alpha, horchi
+ - bugfix: Fixed crashes and scale problems when using DFB output
+ (thanks to morone!)
+ - added: Support of image scale to DFB renderer (reported by morone)
+ - bugfix: Fixed text output (nothing was displayed id text don't fit
+ inconfigured dimensions) DFB renderer (reported by morone)
+ - added: Support for music plugin
+ - added: Display 'condition' for all items (see HOWTO-Themes)
+
+#48 Version 0.1.20-alpha, horchi
+ - added: patch for plain vdr (provided by 'GetItAll')
+ - change: time field update cycle now depending on their format string
+ - added: Service interface for music covers
+
+#47 Version 0.1.19-alpha, horchi
+ - bugfix: fixed compile error with fbrenderer
+ - added: new condition (colcount = x) for menu configuration
+ - bugfix: update time view (fix provided by 'machtnix')
+
+#46 Version 0.1.18-alpha, horchi
+ - bugfix: fixed refresh (hide) of volume bar (reported by ckone)
+ - bugfix: update total time during replay (reported by ckone)
+ - added: italian translation, thanks to Diego.
+
+#45 Version 0.1.17-alpha, horchi
+ - bugfix: added auto-refresh to svdrp command NORMALVIEW
+ - change: improved dia script
+ - bugfix: fixed order of included items
+ - added: #ifndef for theme file
+ - change: improved recording and epg image path
+ - buxfix: fixed column width calculation for
+ 'old style' menues (items MenuSelected and Menu)
+ - added: More DeepBlue like Volume display to demo theme,
+ previous style can activated in theme file
+ (see #define VOL_STYLE_BLUE)
+ - buxfix: fixed un-initialized variable in iconv call
+
+#44 Version 0.1.16-alpha, horchi
+ - change: Fixed compile with vdr 1.4.7
+
+#43 Version 0.1.15-alpha, horchi
+ - change: Rework of the README
+ - change: Re-Implemented the menuMap feature
+
+#42 Version 0.1.14-alpha, horchi
+ - change: comment sign now "//"
+ - bugfix: fixed crash while playing mp3s
+ - added: #define, #ifdef for theme files
+ - bugfix: fixed seldom crash during reload of theme file
+ - added: mail count to mailbox plugin interface
+ (ab mailbox plugin version-0.5.2-pre3,
+ oder dir 0.5.0 mit dem beiliegenden Patch)
+
+#41 Version 0.1.13-alpha, horchi
+ - added: support mailbox plugin (mail symbol)
+ - added: scrolling for epg movie and recording description
+ - added: image for epg movie and recording description
+
+#40 Version 0.1.12-alpha, horchi
+ - added: support of gettext (ab vdr 1.5.7)
+ - change: cleanup of i18n.c
+ - change: gcc 4.2 porting
+ - change: using 720x576 for dvb renderer
+ (setup of width and height are ignored for dvb device)
+
+#39 Version 0.1.11-alpha, horchi
+ - bugfix fixed iconv buffer size
+
+#38 Version 0.1.10-alpha, horchi
+ - bugfix fixed iconf integration for imlib
+ - added display width and height to plugin setup
+
+#37 Version 0.1.9-alpha, horchi
+ - changed now using iconv for font conversion
+
+#36 Version 0.1.8-alpha, horchi
+ - added SVDRP command 'REFRESH' to force redraw
+ - added SVDRP command 'ACTIVE' to activate/deactivate display
+ - added Menu entry to activate/deactivate display
+ - added SVDRP command 'NORMALVIEW' to change the 'normal view'
+ - added aspectRatio and fit for Image and ImageFile items
+ - added Dia section to example theme to show how a dia show can setup
+ - added expample script dia.sh for dia show
+
+#35 Version 0.1.7-alpha, horchi
+ - bugfix crash with DVB renderer
+ (thanks to mig for reporting an testing)
+ - change renamed some parameter in setup.conf
+ - added support of newer ffmeg versions
+ (thanks to Holger Brunn for the patch)
+
+#34 Version 0.1.6-alpha, horchi
+ - bugfix crash with unknown reference path of sysindo item
+ - bugfix crash on shutdown
+ - change implemented missing charWidthOf of DFB renderer
+ - added theme with and hight to themefile (for theme scaling)
+ - change accelerated scaling of imlib renderer
+ - change scaling with imlib renderer now only done if needed
+ - change speed up copy to framebuffer in 16 bit colordepth
+
+#33 Version 0.1.5-alpha, horchi
+ - bugfix added refresh for SpectrumAnalyzer Item
+ - change updated ReplayMP3 section of demo theme
+ - added cover patch for mp3 plugin version mp3-0.9.15pre14
+ - bugfix fixed missing update of replay sections
+
+#32 Version 0.1.4-alpha, horchi
+ - bugfix crash on missing theme sections
+ - change autodetect libgtop path in makefile
+ - change removed 'picture beside picture' menu entry
+ - added auto fill of 'NormalView' menu depending on theme file
+ - added compatibility with older libgtop versions (pre 2.14.8)
+
+#31 Version 0.1.3-alpha, horchi
+ - bugfix problem with item 'datetimeformat' fixed
+ - change now string is used to identify menu sections (patch changed!)
+ - added Other section names in epgsearch- and extrec- patch,
+ due to this the standard vdr sections and the sections
+ for the plugins can coexist in the theme file
+
+#30 Version 0.1.2-alpha, horchi
+ - bugfix core on channel change in program info of epgsearch
+ - added support for inline comments in theme files
+
+#29 Version 0.1.1-alpha, horchi
+ - bugfix permanent redraw at some systems due to uninitialized variable
+
+#28 Version 0.1.0-alpha, horchi
+ - change removed compatibility properties 'transperents'/'bg_transperents',
+ use 'transparent'/'bg_transparent' instead
+ - bugfix static image of menuColumnSelected items was not displayed
+ - change replaced item 'Date' with 'DateTimeFormat'
+ - change removed properties 'pathBACK' and 'pathFRONT'
+ - change ShowMutePermanent moved fom setup to theme (permanent={yes|no})
+ - change renamed theme property bg_heigth in bg_height
+ - change renamed theme item ReplayProcessbar in ReplayProgressbar
+ - added 'Defaults' item to setup property defaults of
+ all items in the current section
+ - change removed Item VolumeBackground, for background image
+ now path2 of Volumebar is used
+ - added force_refresh property
+
+#27 Version 0.0.20, horchi
+ - added scroll support for Colum-Items (see HOWTO.Themes also)
+ - fixed some problems in scroll mode
+
+#26 Version 0.0.19, horchi
+ - added OSD flip patch from Markus (mahlzeit)
+
+#25 Version 0.0.18, horchi
+ - added VDR 1.5.x support
+
+#24 Version 0.0.17, horchi
+ - added keyboard service for x frontend
+
+#23 Version 0.0.16, horchi
+ - added DateTimeFormat Item (siehe HOWTO.Themes)
+
+#22 Version 0.0.15a, horchi
+ - added flag and button to touchTFT interface
+
+#21 Version 0.0.15, horchi
+ - added touchTFT interface (the first step ;)
+ - added clock and logo view
+
+#20 Version 0.0.14b, horchi
+ - fixed scrolling in case scrollaround is enabled in the OSD setup
+ - fixed imageMap handling
+
+#19 Version 0.0.14a, horchi
+ - fixed now skipping unknown sections instead of core ;)
+ - added patch for extrecmenu
+ - added faster im memory image-convert for TCP communication
+ - added configuration option for the jpeg quality (0-100) used for
+ communication with the x frontend
+
+#18 Version 0.0.14, horchi
+ - added mouse support for x frontend
+ - fixed mutex lock while reloading themes
+ - fixed hide of volume bar!
+ - fixed display correct theme after reload
+ - fixed display of images/logos without height and/or width
+
+#17 Version 0.0.13, horchi
+ - fixed jumpy menu scrolling
+ (calc of the displayed top line totally reworked)
+ - added auto-scale for the column images
+ - fixed parser bug (e.g. static_x= is found for x=)
+ - added image with static position for menu cloumns
+ - added the image_map feature (known from 'normal' menues)
+ to the colunm oriented menus
+ - added bool handling to the theme parser
+ for the boolean parameters (like stat_pic, image_map, switch, animated)
+ now yes/no, true/false and 1/0 can used
+ - added new spacing attribute to themes parser for column-items
+ - hight of progress bar nor configurable (in percent of the row height)
+ - bugfix: DumpImageX, DumpImageY, UseStillPicture not loaded from setup.conf
+ - added include to theme config so sections can include others
+ - themes now don't loaded twice at startup
+ - image support for progress bars
+ - added reload thems via Main-Menu, if it was enabled in Makefile
+ (helpful during creating/testing themes)
+ - fixed theme change in setup (new bug since 0.0.12)
+ - graphical (image) progress now also for item timeline
+
+#16 Version 0.0.12a, horchi
+ - added column type 'image' e.g. for the status column (T,t,V,...)
+ - fixed problem with imagemap's using setup plugin
+ (now images for Menu's ending with "...")
+ - added span support (first BETA to be continued!)
+
+#15 Version 0.0.12, horchi
+ - added own thread for the tcp communication
+
+#14 Version 0.0.11b, horchi
+ - New sections WhatsOnNow, WatchsOnNext and WhatsOnElse for epgsearch
+ WhatsOnElse is the smae than WhatsOn (only a epgseach compatible name)
+ - New section MenuSetupPage
+
+#13 Version 0.0.11, horchi
+ - fixed typo 'transperents' in theme, transitional both spellings
+ can be used.
+
+#12 Version 0.0.10b, horchi
+ - added parting line handling for epgsearch
+ - added tcp communication for X frontend
+
+#11 Version 0.0.10a, horchi
+ - minor changes
+
+#10 Version 0.0.10, horchi - 23.10.2006
+ - added menu column support
+ so you can define each column of a menu as you need
+ (as example see enhanced DeepBlue Skin)
+
+ - more menu sections are available, these are:
+ MenuMain
+ MenuSchedule
+ MenuChannels
+ MenuTimers
+ MenuRecordings
+ MenuSetup
+ MenuCommands
+ MenuWhatsOn
+ MenuWhatsOnNow
+ due to this feature you can give (nearly) each menue it's own look,
+ for the rest and the sections from above wich dosen't apper n the Skin
+ as default the 'Menu' Section will be used.
+ For all menues the 'traditional' and likewise the column oriented configuration
+ method is available.
+
+ - graphical progress bar for MenuWhatsOnNow
+ - fixes timing bug of dump image (image never dumped since cTimeMs is used)
+ - included patch (found at vdr-portal) to support ffmeg newer than 0.4.8
+ - added support of APIVERSION to the Makefile
+ (and removed support of really old VDR Versions from the code)
+
+Version 0.0.5 - 0.0.9
+ - i don't know ;)
+
+Version 0.0.4
+ - added Menu-Icons
+ - added Tabs support in Menus
+ - added new Menu Item
+ - removed PbP-Mode (will be reworked soon)
+
+Version 0.0.3-pre5
+
+Version 0.0.3-pre4
+
+Version 0.0.3-pre3
+
+Version 0.0.3-pre2
+ - fixed a version dependency in transfer.c
+ - fixed missing include file on some systems in display.c
+ - fixed display of currently recording timers (stopped timers were not removed)
+ - fixed possible race condition in update thread
+ - fixed update in replay mode (now exactly once a second)
+ - fixed display of group names and pending channel switches
+ - fixed OsdClear() behaviour for status message without menu
+ - fixed update after stopping a replay
+ - moved thx to readme
+ - fix in volumebackgorund
+ - added following event title/subtitle to UpdateProgramme()
+ - fixed possible NULL pointer handling and update condition in OsdProgramme()
+ - added device deinitialization
+ - inserted slight delay on update thread startup (improves vdr startup)
+ - added animation code
+ - fixed display of OsdChannel for some OSD themes (Elchi for example)
+ - removed stale include file menu.h from graphtft.c
+
+Version 0.0.3-pre1
+ - most code rewritten and reordered
+
+Version 0.0.2
+ - add PbP mode
+ - add Theme support
+
+Version 0.0.1
+ - Initial revision.
+
+ * ------------------------------------
+ */
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8d85d07
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,319 @@
+#***************************************************************************
+# Group VDR/GraphTFTng
+# File Makefile
+# 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-2013 Jörg Wendel
+#***************************************************************************
+
+FFMDIR = /usr/include
+
+# ----------------------------------------------------------------------------
+# User settings
+
+# like to support a touch screen ?
+#WITH_TOUCH = 1
+
+# like the X renderer to display directly on local xorg (grpahtft-fe not needed)
+WITH_X = 1
+
+# compile the graphtft-fe an the therefor tcp communication
+WITH_TCPCOM = 1
+
+# compile wit old patch version (depricated)
+#WITH_OLD_PATCH = 1
+
+# compile with epg2vdr timer support
+#WITH_EPG2VDR = 1
+
+# Name of the plugin
+
+PLUGIN = graphtftng
+HISTFILE = "HISTORY.h"
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'define _VERSION ' $(HISTFILE) | awk '{ print $$3 }' | sed -e 's/[";]//g')
+LASTHIST = $(shell grep '^\#[0-9][0-9]' $(HISTFILE) | head -1)
+LASTCOMMENT = $(subst |,\n,$(shell sed -n '/$(LASTHIST)/,/^ *$$/p' $(HISTFILE) | tr '\n' '|'))
+LASTTAG = $(shell git describe --tags --abbrev=0)
+BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
+GIT_REV = $(shell git describe --always 2>/dev/null)
+
+# The directory environment
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags) -ggdb -fPIC -Wall -Wunused-variable -Wunused-label -Wunused-value -Wunused-function -Wno-unused-result
+
+### The version number of VDR's plugin API:
+
+APIVERSION = $(call PKGCFG,apiversion)
+
+#### Allow user defined options to overwrite defaults:
+
+-include $(PLGCFG)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I. \
+ -I./imlibrenderer \
+ -I./imlibrenderer/fbrenderer \
+ -I./imlibrenderer/xrenderer \
+ -I./imlibrenderer/dmyrenderer
+
+INCLUDES += $(shell pkg-config libgtop-2.0 --cflags 2>/dev/null)
+INCLUDES += $(shell pkg-config libexif --cflags 2>/dev/null)
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DVDR_PLUGIN
+DEFINES += -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS
+
+ifdef GIT_REV
+ DEFINES += -DGIT_REV='"$(GIT_REV)"'
+endif
+
+ifdef WITH_OLD_PATCH
+ DEFINES += -D_OLD_PATCH
+endif
+
+ifdef WITH_TCPCOM
+ DEFINES += -DWITH_TCPCOM
+endif
+
+ifdef WITH_EPG2VDR
+ DEFINES += -DWITH_EPG2VDR
+endif
+
+ifdef WITH_X
+ DEFINES += -DWITH_X
+endif
+
+# deactivate VDRs swap definition since the plugin needs std::sort() !
+
+DEFINES += -D__STL_CONFIG_H
+
+LIBS += $(shell pkg-config libexif --libs 2>/dev/null)
+LIBS += $(shell imlib2-config --libs)
+LIBS += $(shell pkg-config libgtop-2.0 --libs 2>/dev/null)
+
+ifdef WITH_TCPCOM
+ LIBS += -ljpeg
+endif
+
+ifdef WITH_TOUCH
+ DEFINES += -DWITH_TOUCH
+endif
+
+AVCODEC_INC = $(shell pkg-config libavcodec --cflags | sed -e 's/ //g')
+
+ifeq ($(strip $(AVCODEC_INC)),)
+ INCLUDES += -I$(FFMDIR) -I$(FFMDIR)/libavcodec
+else
+ INCLUDES += $(AVCODEC_INC)
+endif
+
+AVCODEC_LIBS = $(shell pkg-config libavcodec --libs)
+
+ifeq ($(strip $(AVCODEC_LIBS)),)
+ LIBS += -lavcodec
+else
+ LIBS += $(AVCODEC_LIBS)
+endif
+
+SWSCALE_INC = $(shell pkg-config libswscale --cflags 2>/dev/null)
+
+ifeq ($(strip $(SWSCALE_INC)),)
+ INCLUDES += -I$(FFMDIR) -I$(FFMDIR)/libswscale
+else
+ INCLUDES += $(SWSCALE_INC)
+endif
+
+SWSCALE_LIBS = $(shell pkg-config libswscale --libs 2>/dev/null)
+
+ifeq ($(strip $(SWSCALE_LIBS)),)
+ LIBS += -lswscale
+else
+ LIBS += $(SWSCALE_LIBS)
+endif
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o dspitems.o vlookup.o status.o display.o \
+ setup.o scan.o theme.o common.o sysinfo.o \
+ touchthread.o \
+ imlibrenderer/imlibrenderer.o \
+ imlibrenderer/fbrenderer/fbrenderer.o \
+ renderer.o comthread.o tcpchannel.o \
+ scraper2vdr.o
+
+ifdef WITH_X
+ OBJS += imlibrenderer/xrenderer/xrenderer.o
+ LIBS += -lX11
+endif
+
+### The main target:
+
+ifdef WITH_X
+ all: $(SOFILE) i18n fe
+else
+ all: $(SOFILE) i18n
+endif
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<vdr@jwendel.de>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+
+# ----------------------------------------------------------------------------
+# Check madatory environment
+#
+
+check-builddep:
+ if ! pkg-config libavcodec; then \
+ echo "Missing libavcodec, aborting build !!"; \
+ exit 1; \
+ fi
+ if ! pkg-config libswscale; then \
+ echo "Missing libswscale, aborting build !!"; \
+ exit 1; \
+ fi
+ if ! pkg-config imlib2; then \
+ echo "Missing imlib2, aborting build !!"; \
+ exit 1; \
+ fi
+ if ! pkg-config libexif; then \
+ echo "Missing libexif, aborting build !!"; \
+ exit 1; \
+ fi
+ if ! pkg-config libgtop-2.0; then \
+ echo "Missing libgtop-2.0, aborting build !!"; \
+ exit 1; \
+ fi
+
+$(SOFILE): check-builddep $(COMMONOBJS) $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
+
+fe:
+ $(MAKE) -C ./graphtft-fe all
+
+install-fe:
+ $(MAKE) -C ./graphtft-fe install
+
+install-lib: $(SOFILE)
+ install -D -m644 $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib
+
+dist: clean clean-fe
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+ifdef WITH_TCPCOM
+clean: clean-plug clean-fe
+else
+clean: clean-plug
+endif
+
+clean-plug:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core*
+ @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -f $(PACKAGE).tgz t1
+ @-rm -f *~ */*~ */*/*~
+
+clean-fe:
+ @rm -rf plasma/build imlibrenderer/imlibtest
+ $(MAKE) -C ./graphtft-fe clean
+
+t1: test.c scan.c scan.h
+ $(CXX) $(INCLUDES) -ggdb $(LIBS) scan.c test.c -lrt -o $@
+
+# ------------------------------------------------------
+# Git / Versioning / Tagging
+# ------------------------------------------------------
+
+vcheck:
+ git fetch
+ if test "$(LASTTAG)" = "$(VERSION)"; then \
+ echo "Warning: tag/version '$(VERSION)' already exists, update HISTORY first. Aborting!"; \
+ exit 1; \
+ fi
+
+push: vcheck
+ echo "tagging git with $(VERSION)"
+ git tag $(VERSION)
+ git push --tags
+ git push
+
+commit: vcheck
+ git commit -m "$(LASTCOMMENT)" -a
+
+git: commit push
+
+showv:
+ @echo "Git ($(BRANCH)):\\n Version: $(LASTTAG) (tag)"
+ @echo "Local:"
+ @echo " Version: $(VERSION)"
+ @echo " Change:"
+ @echo -n " $(LASTCOMMENT)"
+ echo
diff --git a/README b/README
new file mode 100644
index 0000000..dd2d485
--- /dev/null
+++ b/README
@@ -0,0 +1,123 @@
+---------------------------------------------------------------------------------
+Current Versions:
+
+Written by: Jörg Wendel <vdr@jwendel.de>
+Project's homepage: http://projects.vdr-developer.org/projects/plg-graphtftng
+
+---------------------------------------------------------------------------------
+Previous Versions:
+
+Written by: Lars Tegeler <graphtft@habmalnefrage.de>
+Project's homepage: www-math.uni-paderborn.de/~tegeler/vdr/graphtft
+---------------------------------------------------------------------------------
+
+---------------------------------------------------------------------------------
+This software is released under the GPL, version 2 (see COPYING).
+Additionally, compiling, linking, and/or using the OpenSSL toolkit in
+conjunction with this software is allowed.
+---------------------------------------------------------------------------------
+
+README.themes for creating themes and the HISTORY for changes.
+---------------------------------------------------------------------------------
+
+EPG Bilder:
+-----------
+EPG Bilder können in der Aufnahmebeschreibung, der EPG-Filmbeschreibung während
+der Wiedergabe und beim TV schauen angezeigt werden. Die Pfade sind frei konfigurierbar und können
+einen variablen Teil, in geschweifen Klammern, enthalten. Mehrere Pfade werden mit ':' getrennt angegeben.
+Pfade werden in der konfigurierten Reihenfolge durchsucht.
+
+Beispiel:
+
+Item=RecordingImage,x=150,y=300,width=400,height=200,path={recordingPath}/thumbnail.png:/video0/epgimages/{eventID}.png:/video0/images/{recordingTitle}.png,fit=yes,aspect_ratio=yes,delay=15;
+
+Recording Variablen (ReplayNormal, MenuRecording):
+ recordingPath
+ recordingTitle
+ eventID
+
+Bilder werden in einem Cache verwaltet (imlib feature) wodurch die Darstellung beschleunigt wird. Dadurch werden jedoch
+Änderungen auf dem Filesystem nicht erkannt. Sollen Bilder zur Laufzeit verändert werden kann der Chache für bestimmte Bilder
+mittels einem chg_ im Namen deaktiviert werden.
+
+Beispiel für Bilder in den Aufnahmedetails:
+
+[MenuRecording]
+...
+...
+// EPG Bild ausgeben
+
+Image x=600,y=60,width=200,height=125,
+ path={recordingPath}/thumbnail_?.png:
+ {varEPGimagesPath_1}{recordingEventID}.png:
+ {varEPGimagesPath_2}{recordingEventID}.png:
+ {varEPGimagesPath_3}{recordingEventID}.png:
+ {varImagesPath_1}{recordingTitle}.jpg:
+ backgrounds/filmspule_tr.png,
+ fit=yes,aspect_ratio=yes;
+...
+
+
+Demo-Theme (DeepBlue-horchi):
+-----------------------------
+Ein zur aktuellen Version passendes Beispiel-Theme
+lieg in themes/DeepBlue.theme (nur das Theme File). Das komplette Theme mit Symbolen und
+Logos gibt es hier www.jwendel.de/vdr/DeepBlue-horchi-x.x.x.tar.bz2 (bitte DeepBlue.theme
+gegen die aktuelle tauschen). Dann noch die Fonts (www.jwendel.de/vdr/deepblue-fonts.tar.bz2)
+nach <vdr-config-dir>/plugins/graphTFT/fonts/ auspacken.
+
+Im Theme sind für einige Items auskommentierte Alternativen enthalten,
+unter anderem:
+
+> // ways to display the timebar
+> Timebar x=523,y=12,width=184,height=18,bg_x=520,bg_y=9,bg_width=190,bg_height=24,bg_red=0,bg_green=0,bg_blue=0,path=columnimages/progress.png;
+> // Timebar x=525,y=12,width=180,height=21,bg_x=520,bg_y=8,bg_width=190,bg_height=29,bg_red=0,bg_green=0,bg_blue=0,switch=yes;
+> // Timebar x=525,y=12,width=180,height=21,bg_x=520,bg_y=8,bg_width=190,bg_height=29,bg_red=0,bg_green=0,bg_blue=0,switch=no;
+
+Die Sektion [MenuRecordings] geht von folgenden Einstellungen aus (ansonsten ist die Sektion anzupassen):
+ShowRecDate = 1
+ShowRecLength = 1
+ShowRecTime = 1
+
+
+ExtrecMenu Plugin:
+------------------
+Hierfür ist in den Themes eine eigene Sektion vorgesehen ([MenuExtRecordings]).
+
+Das Demo Theme Passt zu folgenden Einstellungen, sind andere gewünscht bitte das Theme
+entsprechend anpassen:
+
+extrecmenu.ShowRecDate = 1
+extrecmenu.ShowRecLength = 1
+extrecmenu.ShowRecTime = 1
+
+EpgSearch Plugin:
+-------------------
+Muss sofern hier patch/epgsearch-x.x.x.diff ein Patch zufinden ist damit
+gepatched werden. Es werden [MenuEpgs....] Sektionen verwendet. Um das Demo Theme
+nicht anpassen zu müssen kann thems/epgsearchmenu.conf verwendet werden.
+
+
+Blinkender Cursor:
+------------------
+(Danke DrBoon für den Tipp)
+Für die die /dev/fb0 als Ausgabedevice benutzen, und Probleme mit Konsolenausgaben
+oder dem Powermanagement des Monitors haben:
+
+setterm -powersave off
+setterm -blank 0
+setterm -powerdown 0
+chvt 11
+echo -e '\033[?25l' > /dev/tty11
+
+beseitigt den blinkenden Cursor, die Ausgaben der Konsole sowie das Umschalten
+in den Ruhezustand. Wobei "setterm -powersave off" nur funktioniert,
+wenn man auf der betreffenden Konsole ist und tty11 nicht bei allen
+Distributionen eingerichtet ist.
+
+
+Special thanks for help during development
+---------------------------------------------------------------------------
+Volker Leonhardt (data)
+For the avp theme, many ideas, testing and bug reporting
+
diff --git a/README.themes b/README.themes
new file mode 100644
index 0000000..407cc2a
--- /dev/null
+++ b/README.themes
@@ -0,0 +1,419 @@
+
+=========================================================================================
+== Eigene graphTft-NG Theme erstellen
+=========================================================================================
+
+
+Es gibt grundsäzlich folgede Syntax Bestandteile für Theme Files
+----------------------------------------------------------------
+
+1.) Sektionen
+Sektionen werden durch den Namen der Sektion in eckigen Klammern definiert.
+Eine Sektion hält eine Menge Items zusammen welche 'gleichzeitig' angezeigt werden sollen. So ziemlich jeder Ansicht/Menü des VDRs kann eine Sektion zugeordnet werden, das hängt von der Methode 'MenuKind' ab welche durch das graphTFT Patch in den VDR kommt und je Menü oder Ansicht einen anderen Bezeichner zurückgibt. Einige Plugins unterstützen dies ebenso (epgsearch, extrec, ...).
+Darüberhinaus kann man weitere, beliebige Sektionen definieren um Items zu gruppieren und in anderen Sektionen per Include wiederzuverwenden. Eine Besonderheit sind hierbei Sektionen dessen Name mit 'Normal' beginnt, zwischen diesen kann bei LiveTV mittels Plugin-Menü und SCVDP hin und her geschaltet werden.
+
+2.) Items
+Die anzuzeigenden Items wie Text, Image ColorButton, Menu... etc. (Liste im nächsten Thread)
+
+3.) Parameter
+Die Parameter der Theme Items, mittels dieser wird Position, Farbe, Größe etc. des Items bestimmt
+
+4.) Variablen
+Hier gibt es eine Menge automatisch gefüllter welche von Plugin je nach Kontext mit aktuellen Daten gefüllt werden welche gerade zur Verfügung stehen ist vom Kontext abhängig in dem der VDR sich befindet (LiveTV, Wiedergabe, EpgMenü, Mauptmenü, Menü(XYZ). Weiterhin kann man im Theme eigene Variablen definieren z.B. um an zentraler Stelle einige Einstellungen machen zu können. Variablen dienen in erster Linie der Zuordnung zu den Parametern, so setzt man z.B. den Wert des text Parameters auf den akztuellen Sendungstitel indem man die Variable presentTitle verwendet: text={presentTitle}.
+Variablen löst man mittels der geschweiften Klammern auf. Zum Teil gibt es zu den Variablen noch Formatierungsanweisung welche mittels / getrennt angegeben werden können. Aktuell beschränken sich diese auf 'upper', 'lower' und die Formatstrings für die Formatierung von datum und zeit welcher der Manpage von strftime entsprechen. Den aktuellen Titel in Kleinbuchstaben erhält man somit mittels: {presentTitle/lower}
+
+5.) Schlüsselwörter und Direktiven
+Dazu kommen Schlüsselwörter wie Include und var sowie die Direktiven (#ifdef, #define, #ifndef und #endif).
+
+6.) Kommentare, diese beginnen mit // und werden beim einlesen der Theme bis zum Zeilenende ignoriert.
+Inline Kommentage sind möglich.
+
+
+=========================================================================================
+== Direktiven
+=========================================================================================
+
+- #define <value>
+- #ifdef <value>
+- #ifndef <value>
+- #else
+- #endif
+
+=========================================================================================
+== Vorgegebene Theme Sektionen
+=========================================================================================
+
+"MenuEditChannel"
+"MenuChannels"
+"MenuTimers"
+"MenuWhatsOnNow"
+"MenuWhatsOnNext"
+"MenuSchedule"
+"MenuCam"
+"MenuRecording"
+"MenuSetupOsd"
+"MenuSetupEpg"
+"MenuSetupDvb"
+"MenuSetupLnb"
+"MenuSetupCam"
+"MenuSetupPlugins"
+"MenuSetup"
+"MenuText"
+"MenuTimerEdit"
+"MenuEvent"
+"MenuMain"
+"MenuRecordings"
+"MenuSetupPage"
+
+Für epgsearch:
+
+"MenuEpgsSchedule"
+"MenuEpgsWhatsOnElse"
+"MenuEpgsWhatsOnNow"
+"MenuEpgsFavorites"
+"MenuEpgsWhatsOnNext"
+
+
+Für extrec kommen diese hinzu:
+
+"MenuExtRecording"
+"MenuExtRecordings"
+
+Menüs welche (in Plugins) dies nicht definiert ist wird 'MenuUnknown' verwendet, für alle
+Sektionsanfragen die im Theme fehlen wird die Sektion "Menü" gewählt, diese sollte mindestens definiert sein.
+
+=========================================================================================
+== Theme Items
+=========================================================================================
+
+-----------------
+ Item: Text
+-----------------
+
+ x, y, width, height, size, font, ...: -> wie immer ;)
+
+ text: -> Anzuzeigender Text
+ Der Text kann Variablen enthalten, diese werden in
+ geschweiften Klammern angegeben. Für Datum/Zeit Felder
+ kann koptional ein Formatsting definiert werden.
+ Der Formatstring für Datum/Zeit entspicht dem in der
+ Manpage zu strftime beschriebenen.Anzuzeigende Kommata
+ und Semikola im Formatsring müssen mit '\' maskiert werden
+
+ Beispiel:
+
+ Text text={time/%d.%m %H:%M},x=520,y=27,width=200,height=40,red=52,green=162,blue=159;
+
+
+------------------
+ Item: TextList
+------------------
+
+ Parameter:
+
+ x, y: -> obere linke Ecke
+ width, height: -> Höhe/Breite der 'ganzen' Liste
+ size, font, ...: -> wie immer ;)
+
+ Bemerkung:
+
+ Es werden so viele Einträge angezeit wie in den vorgegebenen Bereich passen.
+
+ Beispiel:
+
+ TextList text={actTimersRunning},x=245,y=335,height=160,width=450,size=12;
+
+-----------------------------------
+ Item: Column und ColumnSelected
+-----------------------------------
+
+ Column -> the column
+ ColumnSelected -> the selected column
+
+ Parameter:
+
+ x:
+ not configured -> x will calculated of the last column's x and width
+ (regarding 'spacing')
+ -1 -> same as 'not configured'
+ geater org equal 0 -> uses as absolut position
+ less than -1 -> go x pixel back from the actual position
+ which is calculated like 'not configured'
+ (regarding 'spacing')
+
+
+ height -> height of the whole column (including all rows)
+
+ size -> font size, the row height will calculatetd
+ using this value
+
+ width -> the column width
+
+ spacing: -> used for auto calculated x positions,
+ add some extra space behind this column
+
+ type:
+ progress -> progress bar
+ text -> normal text column (the default)
+ image -> show a image instead of the text. The image name will build of the text.
+ The image is expected in the 'symbols' subdir of the theme.
+ Example:
+ For The Text "T" (Timer Flag) the image .../DeepBlue/symbols/T.png will used.
+ For The Text "V" (VPS Flag) the image .../DeepBlue/symbols/V.png will used.
+
+
+ bar_height: only for 'type=progress'
+ -> height of the progress bar, absolut (bar_height=24)
+ or in percent (bar_height=50%) of the row height
+
+
+ number:
+ the # of the original menu column this column depends on
+
+
+ stat_pic: only supported for itemColumnSelected
+ yes: -> display a image on position stat_x, stat_y
+ the image path and name is build like:
+ "..../graphTFT/themes/<your-theme>/columnimages/" + <column-text> + ".png";
+ If now image will found, nothing would displayed.
+ no: -> do nothing (default)
+
+
+ stat_text: only supported for itemColumnSelected
+ yes: -> display the column text on position stat_x, stat_y
+ no: -> do nothing (default)
+
+
+ scroll: activate/deactivate the scroll mode for this column
+ only supported for 'ColumnSelected' items
+
+ yes: -> activate scrolling (depending on plugin settings!)
+ no: -> deactivate scrolling
+
+
+ Hinweise:
+
+ - to display a static picture or a static text depend on a coulumn which you don't like
+ to display 'itself' define the coulumn with the stat_** attributes as you like and
+ suppress the column by setting with to -1.
+
+ - itemColumnSelected should always defined before the corresponding itemColumn
+ like itemMenuSelected and itemMenu formerly. For the image of the selected column
+ (path=) you can use one (wide enough for all columns) and set it for the first column.
+ Alternative it is possible to define one image per column.
+
+----------------------
+ 'PartingLine' item
+----------------------
+
+ Repräsentiert nicht auswählbare Menüzeilen (Treennlinien wie die z.B. Gruppierungen bei epgseearch)
+
+ path: -> the Image shown for lines with text, like "-------------- Thu 07.11.2006 -----------"
+ path2: -> the Image shown for lines without text, like "------------------------------------------"
+
+ align: text align
+ 0 -> left
+ 1 -> center
+ 2 -> right
+
+
+=========================================================================================
+== Vordefinierte Theme Variablen
+=========================================================================================
+
+Die Variablen setzen sich aus einem Präfix und Bezeicher zusammen, der Präfix spezifiziert den Context.
+Beispiel der Titel der aktuellen Sendung steht in "presentTitle", der Titel der folgenden Sendung in "followingTitle"
+Wo und wann die Variablen verfügbar sind ist abhängig vom Präfix, siege unten
+
+
+Präfix:
+----------------------------------------
+
+event (Event in der Event Info Ansicht)
+present (aktuelles Event in der Live TV/Radio)
+following (folgendes Event in der Live TV/Radio)
+rowEvent (Zeile eins EPG Menüs)
+selectedRowEvent (gewählte Zeile eins EPG Menüs)
+
+Für diese gibt es folgende Ausprägungen:
+----------------------------------------
+
+ Title
+ SubTitle
+ Description
+ ChannelName
+ ChannelNumber
+ ID
+ StartTime
+ EndTime
+ Duration
+ HasTimer
+ HasPartialTimer
+ HasPartialTimerBefore
+ HasPartialTimerAfter
+ IsRunning
+ Elapsed
+ Remaining
+ Progress
+ IsRecording
+
+
+Präfix:
+----------------------------------------
+
+recording (Aufnahme im Aufnahme-Info Menü)
+replay (aktuell wiedergegebene Aufnahme)
+
+Ausprägungen:
+----------------------------------------
+
+ Speed
+ Play
+ Forward
+ Current
+ Total
+ RawCurrent
+ RawTotal
+ Title
+ Path
+ Time
+ EventID
+ SubTitle
+ Description
+ Channel
+ Title
+ EventID
+ SubTitle
+ Channel
+ Path
+ Time
+ Description
+
+Präfix:
+----------------------------------------
+
+calibration (beim callibrieren des Touch)
+
+Ausprägungen:
+----------------------------------------
+
+ Instruction
+ Info
+ CursorX
+ CursorY
+ TouchedX
+ TouchedY
+ OffsetX
+ OffsetY
+ ScaleX
+ ScaleY
+
+
+Allgemeine Variablen ohne Präfix, in jeden Kontext verfügbar:
+--------------------------------------------------------------
+
+ textWidth
+ colCount
+ rowCount
+ visibleRows
+ currentRow
+ touchMenu
+ themeVersion
+ syntaxVersion
+ themeName
+ vdrVersion
+ mouseX
+ mouseY
+ mouseKey
+ actRecordingCount
+ actRecordingName
+ actTimersRunning
+ actTimersTitle
+ actTimersFile
+ actTimersStart
+ actTimersStop
+ STR
+ SNR
+ unseenMailCount
+ hasNewMail
+ channelHasVtx
+ channelHasMultilang
+ channelHasDD
+ channelIsEncrypted
+ channelIsRadio
+ videoSizeHeight
+ videoSizeWidth
+ time
+ volumeMute
+ volumeLevel
+
+ menuText (nur in Menüs mit Fließtext verfügbar)
+ menuTitle (nur in Menüs verfügbar)
+
+
+Präfix:
+----------------------------------------
+
+'music' (nur für das Music-Plugin)
+
+Ausprägungen:
+----------------------------------------
+
+ Track
+ Artist
+ Album
+ Genre
+ Year
+ Filename
+ Comment
+ Frequence
+ Bitrate
+ StereoMode
+ Index
+ Count
+ CurrentTrack
+ PlayStatus
+ CoverName
+ Rating
+ Loop
+ Timer
+ Copy
+ Lyrics
+ Shuffle
+ Shutdown
+ Recording
+ ButtonRed
+ ButtonGreen
+ ButtonYellow
+ ButtonBlue
+
+
+=========================================================================================
+== Details zu den Parametern
+=========================================================================================
+
+-----------------------
+ Parameter: condtition
+-----------------------
+
+ Sollen Theme Items nur in bestimmten Situationen angezeigt
+ werden kann dies uber den Parameter condition erreicht werden.
+ Bedingungen können aufbauend auf den unten beschriebenen Variablen
+ definiert werden.
+
+ Unterstütze Operatoren:
+
+ < keiner
+ > größer
+ <= keiner gleich
+ >= größer gleich
+ <> ungleich
+ != ungleich
+ == gleich (c/c++ like ;))
+
+ Beispiele:
+
+ TextList condition={actTimersRunning} = 0,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M} {actTimersTitle},
+ x=15,y=435,height=258,width=1340,size=26,color={normalColor};
+
+ Image condition={channelHasVtx} = 0,x=391,y=688,width=75,height=70,path=vtxOff.png,fit=yes,overlay=yes;
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..d18eb64
--- /dev/null
+++ b/TODO
@@ -0,0 +1,15 @@
+
+----
+BUGS
+----
+
+- Problem beim anwählen eine Menüzeile sobald das Menü mehrere
+ genau identischen Zeilen enthält
+
+-----
+TODOS
+-----
+
+- inotify
+
+
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..81940c3
--- /dev/null
+++ b/common.c
@@ -0,0 +1,703 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * common.c - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2008 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: common.c,v 1.10 2007/12/03 19:58:20 root Exp $
+ *
+ **/
+
+// includes
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <syslog.h>
+#include <regex.h>
+#include <string.h>
+#include <errno.h>
+
+#include <string>
+
+using std::string;
+
+#ifdef VDR_PLUGIN
+# include "setup.h"
+# include <vdr/tools.h>
+# include <vdr/thread.h>
+
+ cMutex logMutex;
+#endif
+
+#include "common.h"
+
+int logLevel = eloOff;
+int logDevice = devSyslog;
+
+//***************************************************************************
+// Log
+//***************************************************************************
+
+void tell(int eloquence, const char* format, ...)
+{
+ if (logLevel < eloquence)
+ return ;
+
+ const int sizeBuffer = 100000;
+ char t[sizeBuffer+100];
+ va_list ap;
+ time_t now;
+ struct tm tim;
+
+ memset(&tim, 0, sizeof(tm));
+
+#ifdef VDR_PLUGIN
+ cMutexLock lock(&logMutex);
+#endif
+
+ va_start(ap, format);
+
+ switch (logDevice)
+ {
+ case devNone: break;
+
+ case devStdOut:
+ {
+ char buf[50+TB];
+ timeval tp;
+
+ gettimeofday(&tp, 0);
+ tm* tm = localtime(&tp.tv_sec);
+
+ sprintf(buf,"%2.2d:%2.2d:%2.2d,%3.3ld ",
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ tp.tv_usec / 1000);
+
+ vsnprintf(t, sizeBuffer, format, ap);
+ printf("%s %s\n", buf, t);
+
+ break;
+ }
+
+ case devSyslog:
+ {
+ snprintf(t, sizeBuffer, "[graphTFT] ");
+ vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
+
+ syslog(LOG_DEBUG, "%s", t);
+
+ break;
+ }
+
+ case devFile:
+ {
+ FILE* lf;
+
+ lf = fopen("/tmp/graphTFT.log", "a");
+
+ if (lf)
+ {
+ timeval tp;
+ tm* tm;
+
+ vsnprintf(t + 24, sizeBuffer+21, format, ap);
+
+ time(&now);
+ strftime(t, sizeof(t), "%Y.%m.%d ", localtime_r(&now, &tim));
+ gettimeofday(&tp, 0);
+ tm = localtime_r(&tp.tv_sec, &tim);
+
+ sprintf(t + strlen(t),"%2.2d:%2.2d:%2.2d,%3.3ld",
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tp.tv_usec / 1000);
+
+ t[23] = ' ';
+ fprintf(lf, "%s\n", t);
+ fclose(lf);
+ }
+
+ break;
+ }
+ }
+
+ va_end(ap);
+}
+
+//***************************************************************************
+// Save Realloc
+//***************************************************************************
+
+char* srealloc(void* ptr, size_t size)
+{
+ void* n = realloc(ptr, size);
+
+ if (!n)
+ {
+ free(ptr);
+ ptr = 0;
+ }
+
+ return (char*)n;
+}
+
+//***************************************************************************
+// RGBA Stuff
+//***************************************************************************
+
+p_rgba str2rgba(const char* value, p_rgba rgba)
+{
+ int index;
+ char* pc;
+ char* col = strdup(value);
+
+ memset(rgba, 255, sizeof(t_rgba));
+
+ // "220:30:110:200" -> rgba
+
+ for (index = 0, pc = strtok(col, ":"); pc && index < 4; pc = strtok(0, ":"), index++)
+ rgba[index] = (unsigned char)atoi(pc);
+
+ free(col);
+
+ return rgba;
+}
+
+p_rgba int2rgba(int r, int g, int b, int a, p_rgba rgba)
+{
+ rgba[rgbR] = r;
+ rgba[rgbG] = g;
+ rgba[rgbB] = b;
+ rgba[rgbA] = a;
+
+ return rgba;
+}
+
+void rgba2int(p_rgba rgba, int& r, int& g, int& b, int& a)
+{
+ r = rgba[rgbR];
+ g = rgba[rgbG];
+ b = rgba[rgbB];
+ a = rgba[rgbA];
+}
+
+//***************************************************************************
+// check if a file exists
+//***************************************************************************
+
+int fileExists(std::string filename)
+{
+ struct stat file_stat;
+
+ return (stat(filename.c_str(), &file_stat) == 0) ? true : false;
+}
+
+const char* suffixOf(const char* path)
+{
+ const char* p;
+
+ if (path && (p = strrchr(path, '.')))
+ return p+1;
+
+ return "";
+}
+
+#ifdef VDR_PLUGIN
+
+//***************************************************************************
+// To UTF8
+//***************************************************************************
+
+int toUTF8(char* out, int outMax, const char* in)
+{
+ iconv_t cd;
+ size_t ret;
+ char* toPtr;
+ char* fromPtr;
+ size_t fromLen, outlen;
+
+ const char* to_code = "UTF-8";
+ const char* from_code = "ISO8859-1";
+
+ if (!out || !in || !outMax)
+ return fail;
+
+ *out = 0;
+ fromLen = strlen(in);
+
+ if (!fromLen)
+ return fail;
+
+#if VDRVERSNUM >= 10509
+ switch (I18nCurrentLanguage())
+#else
+ switch (Setup.OSDLanguage)
+#endif
+ {
+ case 11: from_code = "ISO8859-7"; break;
+ case 13:
+ case 17: from_code = "ISO8859-2"; break;
+ case 16: from_code = "ISO8859-5"; break;
+ case 18: from_code = "ISO8859-15"; break;
+ default: from_code = "ISO8859-1"; break;
+ }
+
+ cd = iconv_open(to_code, from_code);
+
+ if (cd == (iconv_t)-1)
+ return fail;
+
+ fromPtr = (char*)in;
+ toPtr = out;
+ outlen = outMax;
+
+ ret = iconv(cd, &fromPtr, &fromLen, &toPtr, &outlen);
+
+ *toPtr = 0;
+ iconv_close(cd);
+
+ if (ret == (size_t)-1)
+ {
+ tell(0, "Converting [%s] from '%s' to '%s' failed",
+ fromPtr, from_code, to_code);
+
+ return fail;
+ }
+
+ return success;
+}
+#endif
+
+//***************************************************************************
+// Load From File
+//***************************************************************************
+
+int loadFromFile(const char* infile, MemoryStruct* data)
+{
+ FILE* fin;
+ struct stat sb;
+
+ data->clear();
+
+ if (!fileExists(infile))
+ {
+ tell(0, "File '%s' not found'", infile);
+ return fail;
+ }
+
+ if (stat(infile, &sb) < 0)
+ {
+ tell(0, "Can't get info of '%s', error was '%s'", infile, strerror(errno));
+ return fail;
+ }
+
+ if ((fin = fopen(infile, "r")))
+ {
+ const char* sfx = suffixOf(infile);
+
+ data->size = sb.st_size;
+ data->modTime = sb.st_mtime;
+ data->memory = (char*)malloc(data->size);
+ fread(data->memory, sizeof(char), data->size, fin);
+ fclose(fin);
+ sprintf(data->tag, "%ld", (long int)data->size);
+
+ if (strcmp(sfx, "gz") == 0)
+ sprintf(data->contentEncoding, "gzip");
+
+ if (strcmp(sfx, "js") == 0)
+ sprintf(data->contentType, "application/javascript");
+
+ else if (strcmp(sfx, "png") == 0 || strcmp(sfx, "jpg") == 0 || strcmp(sfx, "gif") == 0)
+ sprintf(data->contentType, "image/%s", sfx);
+
+ else if (strcmp(sfx, "ico") == 0)
+ strcpy(data->contentType, "image/x-icon");
+
+ else
+ sprintf(data->contentType, "text/%s", sfx);
+ }
+ else
+ {
+ tell(0, "Error, can't open '%s' for reading, error was '%s'", infile, strerror(errno));
+ return fail;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// JPEG Dimensions
+//***************************************************************************
+
+int jpegDimensions(const char* path, unsigned int& pWidth, unsigned int& pHeight)
+{
+ MemoryStruct data;
+ unsigned char* pData;
+
+ pWidth = pHeight = 0;
+
+ if (loadFromFile(path, &data) != success)
+ {
+ tell(0, "Error loading '%s', error was '%s'", path, strerror(errno));
+ return fail;
+ }
+
+ pData = (unsigned char*)data.memory;
+
+ if (pData[0] != 0xFF || pData[1] != 0xD8)
+ return fail;
+
+ // retrieve the block length of the first block since the first block will not contain the size of file
+
+ for (unsigned int i = 4; i < data.size; i += 2)
+ {
+ unsigned short block_length = pData[i] * 256 + pData[i+1];
+
+ i += block_length; // next block
+
+ if (i >= data.size || pData[i] != 0xFF)
+ return fail;
+
+ // 0xFFC0 is the "Start Of Frame" marker which contains the file size
+
+ if (pData[i+1] == 0xC0)
+ {
+ pHeight = pData[i+5] * 256 + pData[i+6];
+ pWidth = pData[i+7] * 256 + pData[i+8];
+
+ return success;
+ }
+ }
+
+ return fail;
+}
+
+//***************************************************************************
+// Left Trim
+//***************************************************************************
+
+char* Str::lTrim(char* buf)
+{
+ if (buf)
+ {
+ char *tp = buf;
+
+ while (*tp && strchr("\n\r\t ",*tp))
+ tp++;
+
+ memmove(buf, tp, strlen(tp) +1);
+ }
+
+ return buf;
+}
+
+//*************************************************************************
+// Right Trim
+//*************************************************************************
+
+char* Str::rTrim(char* buf)
+{
+ if (buf)
+ {
+ char *tp = buf + strlen(buf);
+
+ while (tp >= buf && strchr("\n\r\t ",*tp))
+ tp--;
+
+ *(tp+1) = 0;
+ }
+
+ return buf;
+}
+
+//*************************************************************************
+// All Trim
+//*************************************************************************
+
+char* Str::allTrim(char* buf)
+{
+ return lTrim(rTrim(buf));
+}
+
+//*************************************************************************
+// Is Empyt
+//*************************************************************************
+
+int Str::isEmpty(const char* buf)
+{
+ if (buf && *buf)
+ return no;
+
+ return yes;
+}
+
+int Str::isBlank(const char* buf)
+{
+ int i = 0;
+
+ while (buf[i])
+ {
+ if (buf[i] != ' ' && buf[i] != '\t')
+ return no;
+
+ i++;
+ }
+
+ return yes;
+}
+
+const char* Str::toStr(const char* s)
+{
+ static char* buf = 0;
+ static unsigned int sizeBuf = 0;
+
+ if (!s)
+ return "";
+
+ if (!buf || sizeBuf < strlen(s))
+ {
+ if (buf) free(buf);
+
+ sizeBuf = strlen(s);
+ buf = (char*)malloc(sizeBuf);
+ }
+
+ strcpy(buf, s);
+
+ return buf;
+}
+
+const char* Str::toStr(bool value)
+{
+ static char buf[10+TB];
+
+ sprintf(buf, "%s", value ? "1" : "0");
+
+ return buf;
+}
+
+const char* Str::toStr(int value)
+{
+ static char buf[100+TB];
+
+ sprintf(buf, "%d", value);
+
+ return buf;
+}
+
+const char* Str::toStr(double value, int precision)
+{
+ static char buf[100+TB];
+
+ sprintf(buf, "%.*f", precision, value);
+
+ return buf;
+}
+
+//***************************************************************************
+// To Case (UTF-8 save)
+//***************************************************************************
+
+const char* Str::toCase(Case cs, char* str)
+{
+ char* s = str;
+ int lenSrc = strlen(str);
+
+ int csSrc; // size of character
+
+ for (int ps = 0; ps < lenSrc; ps += csSrc)
+ {
+ csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
+
+ if (csSrc == 1)
+ s[ps] = cs == cUpper ? toupper(s[ps]) : tolower(s[ps]);
+ else if (csSrc == 2 && s[ps] == (char)0xc3 && s[ps+1] >= (char)0xa0)
+ {
+ s[ps] = s[ps];
+ s[ps+1] = cs == cUpper ? toupper(s[ps+1]) : tolower(s[ps+1]);
+ }
+ else
+ {
+ for (int i = 0; i < csSrc; i++)
+ s[ps+i] = s[ps+i];
+ }
+ }
+
+ return str;
+}
+
+//***************************************************************************
+// Class LogDuration
+//***************************************************************************
+
+#ifdef VDR_PLUGIN
+
+# include <vdr/plugin.h>
+
+LogDuration::LogDuration(const char* aMessage, int aLogLevel)
+{
+ logLevel = aLogLevel;
+ strcpy(message, aMessage);
+
+ // at last !
+
+ durationStart = cTimeMs::Now();
+}
+
+LogDuration::~LogDuration()
+{
+ tell(logLevel, "duration '%s' was (%ldms)",
+ message, cTimeMs::Now() - durationStart);
+}
+
+void LogDuration::show(const char* label)
+{
+ tell(logLevel, "elapsed '%s' at '%s' was (%ldms)",
+ message, label, cTimeMs::Now() - durationStart);
+}
+
+#endif
+
+//**************************************************************************
+// Regular Expression Searching
+//**************************************************************************
+
+int rep(const char* string, const char* expression, Option options)
+{
+ const char* tmpA;
+ const char* tmpB;
+
+ return rep(string, expression, tmpA, tmpB, options);
+}
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ Option options)
+{
+ const char* tmpA;
+
+ return rep(string, expression, s_location, tmpA, options);
+}
+
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ const char*& e_location, Option options)
+{
+ regex_t reg;
+ regmatch_t rm;
+ int status;
+ int opt = 0;
+
+ // Vorbereiten von reg fuer die Expressionsuche mit regexec
+ // Flags: REG_EXTENDED = Use Extended Regular Expressions
+ // REG_ICASE = Ignore case in match.
+
+ reg.re_nsub = 0;
+
+ // Options umwandeln
+ if (options & repUseRegularExpression)
+ opt = opt | REG_EXTENDED;
+ if (options & repIgnoreCase)
+ opt = opt | REG_ICASE;
+
+ if (regcomp( &reg, expression, opt) != 0)
+ return fail;
+
+ // Suchen des ersten Vorkommens von reg in string
+
+ status = regexec(&reg, string, 1, &rm, 0);
+ regfree(&reg);
+
+ if (status != 0)
+ return fail;
+
+ // Suche erfolgreich =>
+ // Setzen der ermittelten Start- und Endpositionen
+
+ s_location = (char*)(string + rm.rm_so);
+ e_location = (char*)(string + rm.rm_eo);
+
+ return success;
+}
+
+#ifdef VDR_PLUGIN
+//***************************************************************************
+// STR / SNR
+//***************************************************************************
+
+#define FRONTEND_DEVICE "/dev/dvb/adapter%d/frontend%d"
+
+int getFrontendSTR()
+{
+ uint16_t value = 0;
+ cString dev = cString::sprintf(FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+
+ if (fe < 0)
+ return 0;
+
+ CHECK(ioctl(fe, FE_READ_SIGNAL_STRENGTH, &value));
+ close(fe);
+
+ return value / 655;
+}
+
+int getFrontendSNR()
+{
+ uint16_t value = 0;
+ cString dev = cString::sprintf(FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+
+ if (fe < 0)
+ return 0;
+
+ CHECK(ioctl(fe, FE_READ_SNR, &value));
+ close(fe);
+
+ return value / 655;
+}
+#endif
+
+uint64_t msNow()
+{
+ struct timeval t;
+
+ if (gettimeofday(&t, NULL) == 0)
+ return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+
+ return 0;
+}
+
+string replaceChar(string str, char ch1, char ch2)
+{
+ for (unsigned int i = 0; i < str.length(); ++i)
+ {
+ if (str[i] == ch1)
+ str[i] = ch2;
+ }
+
+ return str;
+}
+
+int clen(const char* s)
+{
+ int blen = strlen(s);
+ int cs;
+ int len = 0;
+
+ for (int bp = 0; bp < blen; bp += cs, len++)
+ cs = max(mblen(&s[bp], blen-bp), 1);
+
+ return len;
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..c3907e7
--- /dev/null
+++ b/common.h
@@ -0,0 +1,312 @@
+/**
+ * GraphTFTng plugin for the Video Disk Recorder
+ *
+ * common.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2014 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_H
+#define ___COMMON_H
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <string>
+#include <algorithm>
+
+#include <string.h>
+#include <iconv.h>
+#include <stdint.h>
+
+class MemoryStruct;
+
+//***************************************************************************
+//
+//***************************************************************************
+
+// since we compile with __STL_CONFIG_H
+
+template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+
+using std::string;
+
+//***************************************************************************
+//
+//***************************************************************************
+
+enum TimeConst
+{
+ tmeSecondsPerMinute = 60,
+ tmeSecondsPerHour = tmeSecondsPerMinute * 60,
+ tmeSecondsPerDay = tmeSecondsPerHour *24
+};
+
+enum Misc
+{
+ success = 0,
+ done = success,
+ fail = -1,
+ ignore = -1,
+ na = -1,
+ yes = 1,
+ on = 1,
+ off = 0,
+ no = 0,
+ TB = 1
+};
+
+enum Sizes
+{
+ sizeStamp = 14,
+ sizeTime = 6,
+ sizeDate = 8,
+ sizeHHMM = 4
+};
+
+enum LogDevice
+{
+ devNone, // 0
+ devStdOut, // 1
+ devSyslog, // 2
+ devFile // 3
+};
+
+enum Eloquence
+{
+ eloOff, // 0
+ eloAlways, // 1
+ eloDetail, // 2
+ eloDebug, // 3
+ eloDebug1 = eloDebug, // 3
+ eloDebug2, // 4
+ eloDebug3 // 5
+};
+
+extern int logLevel;
+extern int logDevice;
+
+void __attribute__ ((format(printf, 2, 3))) tell(int eloquence, const char* format, ...);
+
+char* srealloc(void* ptr, size_t size);
+const char* suffixOf(const char* path);
+int fileExists(string filename);
+int loadFromFile(const char* infile, MemoryStruct* data);
+int jpegDimensions(const char* path, unsigned int& pWidth, unsigned int& pHeight);
+
+#ifdef VDR_PLUGIN
+int toUTF8(char* out, int outMax, const char* in);
+#endif
+
+uint64_t msNow();
+
+//***************************************************************************
+// MemoryStruct
+//***************************************************************************
+
+struct MemoryStruct
+{
+ public:
+
+ MemoryStruct() { expireAt = 0; memory = 0; zmemory = 0; clear(); }
+ MemoryStruct(const MemoryStruct* o)
+ {
+ size = o->size;
+ memory = (char*)malloc(size);
+ memcpy(memory, o->memory, size);
+
+ zsize = o->zsize;
+ zmemory = (char*)malloc(zsize);
+ memcpy(zmemory, o->zmemory, zsize);
+
+ copyAttributes(o);
+ }
+
+ ~MemoryStruct() { clear(); }
+
+ int isEmpty() { return memory == 0; }
+
+ int append(const char* buf, int len)
+ {
+ memory = srealloc(memory, size+len);
+ memcpy(memory+size, buf, len);
+ size += len;
+
+ return success;
+ }
+
+ void copyAttributes(const MemoryStruct* o)
+ {
+ strcpy(tag, o->tag);
+ strcpy(name, o->name);
+ strcpy(contentType, o->contentType);
+ strcpy(contentEncoding, o->contentEncoding);
+ strcpy(mimeType, o->mimeType);
+ headerOnly = o->headerOnly;
+ modTime = o->modTime;
+ expireAt = o->expireAt;
+ }
+
+ void clear()
+ {
+ free(memory);
+ memory = 0;
+ size = 0;
+ free(zmemory);
+ zmemory = 0;
+ zsize = 0;
+ *tag = 0;
+ *name = 0;
+ *contentType = 0;
+ *contentEncoding = 0;
+ *mimeType = 0;
+ modTime = time(0);
+ headerOnly = no;
+ // expireAt = time(0); -> don't reset 'expireAt' here !!!!
+ }
+
+ // data
+
+ char* memory;
+ long unsigned int size;
+
+ char* zmemory;
+ long unsigned int zsize;
+
+ // tag attribute
+
+ char tag[100+TB]; // the tag to be compared
+ char name[100+TB]; // content name (filename)
+ char contentType[100+TB]; // e.g. text/html
+ char mimeType[100+TB]; //
+ char contentEncoding[100+TB]; //
+ int headerOnly;
+ time_t modTime;
+ time_t expireAt;
+};
+
+//***************************************************************************
+// RGBA Stuff
+//***************************************************************************
+
+typedef unsigned char t_rgba[4];
+typedef unsigned char* p_rgba;
+
+enum RGB
+{
+ rgbR,
+ rgbG,
+ rgbB,
+ rgbA
+};
+
+p_rgba str2rgba(const char* value, p_rgba rgba);
+p_rgba int2rgba(int r, int g, int b, int a, p_rgba rgba);
+void rgba2int(p_rgba rgba, int& r, int& g, int& b, int& a);
+
+//***************************************************************************
+// Wrapper Regual Expression Library
+//***************************************************************************
+
+enum Option
+{
+ repUseRegularExpression = 1,
+ repIgnoreCase = 2
+};
+
+int rep(const char* string, const char* expression, Option options = repUseRegularExpression);
+
+int rep(const char* string, const char* expression,
+ const char*& s_location, Option options = repUseRegularExpression);
+
+int rep(const char* string, const char* expression, const char*& s_location,
+ const char*& e_location, Option options = repUseRegularExpression);
+
+
+//***************************************************************************
+// Log Duration
+//***************************************************************************
+
+class LogDuration
+{
+ public:
+
+ LogDuration(const char* aMessage, int aLogLevel = 2);
+ ~LogDuration();
+
+ void show(const char* label = "");
+
+ protected:
+
+ char message[1000];
+ uint64_t durationStart;
+ int logLevel;
+};
+
+//***************************************************************************
+// std::string trim stuff
+//***************************************************************************
+
+static inline std::string &ltrim(std::string &s)
+{
+ s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
+ return s;
+}
+
+static inline std::string &rtrim(std::string &s)
+{
+ s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
+ return s;
+}
+
+static inline std::string &trim(std::string &s)
+{
+ return ltrim(rtrim(s));
+}
+
+//***************************************************************************
+// Class Str
+//***************************************************************************
+
+class Str
+{
+ public:
+
+ enum Case
+ {
+ cUpper,
+ cLower
+ };
+
+ // Manipulation
+
+ static char* rTrim(char* buf);
+ static char* lTrim(char* buf);
+ static char* allTrim(char* buf);
+ static const char* toCase(Case cs, char* str);
+
+ // converting
+
+ static const char* toStr(const char* s);
+ static const char* toStr(bool value);
+ static const char* toStr(int value);
+ static const char* toStr(double value, int precision = 2);
+
+ // Checks
+
+ static int isEmpty(const char* buf);
+ static int isBlank(const char* buf);
+ static const char* notNull(const char* s) { return s ? s : ""; }
+};
+
+int clen(const char* s);
+string replaceChar(string str, char ch1, char ch2);
+
+//***************************************************************************
+#endif //___COMMON_H
diff --git a/comthread.c b/comthread.c
new file mode 100644
index 0000000..266135a
--- /dev/null
+++ b/comthread.c
@@ -0,0 +1,414 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File comthread.c
+// Date 28.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-2013 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class ComThread
+//***************************************************************************
+
+#include "common.h"
+#include "comthread.h"
+#include "display.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+ComThread::ComThread(void* aDisplay, int width, int height)
+ : cRemote("graphtft-fe")
+{
+ display = aDisplay;
+ themeWidth = width;
+ themeHeight = height;
+
+ bufferSize = maxBuf;
+
+ buffer = new char[bufferSize+TB];
+
+ running = false;
+ timeout = 5;
+ checkTime = 60;
+ port = na;
+ *host = 0;
+ pid = 0;
+ jpgQuality = 60;
+ width = 720;
+ height = 576;
+
+ listener = new TcpChannel(timeout);
+}
+
+ComThread::~ComThread()
+{
+ if (display)
+ ((cGraphTFTDisplay*)display)->clearComThread();
+
+ Stop();
+
+ // close all client connections
+
+ while (clients.size())
+ {
+ clients[0].channel->close();
+ delete clients[0].channel;
+ clients.erase(clients.begin());
+ }
+
+ listener->close();
+
+ delete listener;
+ delete[] buffer;
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int ComThread::init(Renderer* aRenderer, unsigned int aPort, const char* aHost)
+{
+ renderer = aRenderer;
+
+ if (aHost)
+ setHost(aHost);
+
+ if (aPort)
+ setPort(aPort);
+
+ if (listener->open(port) != 0)
+ {
+ tell(0, "Error: Can't establish listener");
+ return fail;
+ }
+
+ tell(0, "Listener established!");
+
+ return success;
+}
+
+int ComThread::close(TcpClient* client, int status, const char* message)
+{
+ cMutexLock lock(&_mutex);
+
+ vector<TcpClient>::iterator it;
+
+ for (it = clients.begin(); it < clients.end(); it++)
+ {
+ TcpClient* c = &(*it);
+
+ if (c == client)
+ {
+ if (message)
+ tell(0, "(%d) %s", status, message);
+
+ c->channel->close();
+ delete c->channel;
+ c->channel = 0;
+ clients.erase(it);
+
+ break;
+ }
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Stop
+//***************************************************************************
+
+void ComThread::Stop()
+{
+ if (running)
+ {
+ isyslog("GraphTFT plugin try to stop communication thread");
+ running = false;
+ Cancel(3);
+ }
+}
+
+//***************************************************************************
+// Run
+//***************************************************************************
+
+void ComThread::Action()
+{
+ TcpClient client;
+ struct timeval tv;
+ fd_set readSet;
+ int maxFD; // the highest-numbered descriptor
+ int status;
+
+ pid = getpid();
+ tell(0, "TCP communication thread started (pid=%d)", pid);
+
+ running = true;
+
+ while (running)
+ {
+ if (!(time(0) % 120))
+ tell(2, "still running, %ld clients connected", clients.size());
+
+ // check client connections
+ // perform select on all connections
+
+ FD_ZERO(&readSet);
+ FD_SET(listener->getHandle(), &readSet); // file descriptor of listener
+ maxFD = listener->getHandle();
+
+ _mutex.Lock();
+
+ for (unsigned int i = 0; i < clients.size(); i++)
+ {
+ FD_SET(clients[i].channel->getHandle(), &readSet);
+ maxFD = max(maxFD, clients[i].channel->getHandle());
+ }
+
+ _mutex.Unlock();
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ // call select and wait up to 1 second
+
+ if ((status = select(maxFD+1, &readSet, 0, 0, &tv)) < 0)
+ {
+ tell(0, "Error: Select failed, error was %s", strerror(errno));
+ continue;
+ }
+
+ if (status == 0)
+ continue; // no data pending
+
+ // at least one tcp message pending
+
+ read(&readSet);
+
+ // check for new connection
+
+ if (FD_ISSET(listener->getHandle(), &readSet))
+ {
+ FD_CLR(listener->getHandle(), &readSet);
+
+ if (listener->listen(client.channel) == success)
+ {
+ tell(0, "Client connection accepted, now "
+ "%ld clients connected", clients.size()+1);
+
+ client.lastCheck = time(0);
+ client.jpgQuality = 30; // speed up first draw on slow connections
+ client.channel->write(cGraphTftComService::cmdWelcome);
+
+ // initial refresh
+
+ if (refresh(&client) == success)
+ {
+ client.jpgQuality = jpgQuality;
+ clients.push_back(client);
+ }
+ else
+ close(&client, 0, "initial refresh failed");
+ }
+ }
+ }
+
+ isyslog("GraphTFT plugin tcp communication thread ended (pid=%d)", pid);
+}
+
+//***************************************************************************
+// read
+//***************************************************************************
+
+int ComThread::read(fd_set* readSet)
+{
+ cMutexLock lock(&_mutex);
+ int status;
+
+ for (unsigned int i = 0; i < clients.size(); i++)
+ {
+ if (FD_ISSET(clients[i].channel->getHandle(), readSet))
+ {
+ FD_CLR(clients[i].channel->getHandle(), readSet);
+
+ if ((status = read(&clients[i])) != success && status != TcpChannel::wrnTimeout)
+ {
+ close(&clients[i], status, "Error: Communication problems, read failed, closing line!");
+ continue;
+ }
+ }
+
+ if (time(0) > clients[i].lastCheck + checkTime)
+ close(&clients[i], 0, "Error: Missing check command on tcp connection, closing line!");
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Read
+//***************************************************************************
+
+int ComThread::read(TcpClient* client)
+{
+ int status;
+ TcpChannel::Header tmp;
+ TcpChannel::Header header;
+
+ // es stehen Daten an, erst einmal den Header abholen ..
+
+ if ((status = client->channel->read((char*)&tmp, sizeof(TcpChannel::Header))) == 0)
+ {
+ header.command = ntohl(tmp.command);
+ header.size = ntohl(tmp.size);
+
+ tell(3, "Got command %d with %d data bytes", header.command, header.size);
+
+ switch (header.command)
+ {
+ case cGraphTftComService::cmdWelcome:
+ {
+ tell(1, "Got welcome");
+ break;
+ }
+
+ case cGraphTftComService::cmdLogout:
+ {
+ tell(1, "Got logout from client, closing line");
+ close(client, 0, "Closing connection due to client logout");
+
+ break;
+ }
+
+ case cGraphTftComService::cmdData:
+ {
+ tell(7, "Got data");
+ status = client->channel->read(buffer, header.size);
+ break;
+ }
+
+ case cGraphTftComService::cmdJpegQuality:
+ {
+ int quality;
+
+ status = client->channel->read((char*)&quality, header.size);
+ quality = ntohl(quality);
+
+ tell(0, "Got JPEG quality (%d)", quality);
+
+ if (quality > 0 && quality <= 100)
+ client->jpgQuality = quality;
+
+ break;
+ }
+
+ case cGraphTftComService::cmdMouseEvent:
+ {
+ GraphTftTouchEvent ev;
+
+ status = client->channel->read((char*)&ev, header.size);
+
+ ev.x = ntohl(ev.x);
+ ev.y = ntohl(ev.y);
+ ev.button = ntohl(ev.button);
+ ev.flag = ntohl(ev.flag);
+ ev.data = ntohl(ev.data);
+
+ tell(0, "Got mouse event, button (%d/%d) at (%d/%d)", ev.button, ev.flag, ev.x, ev.y);
+
+ if (ev.flag & ComThread::efKeyboard)
+ Put(ev.button);
+ else
+ ((cGraphTFTDisplay*)display)->mouseEvent(ev.x, ev.y, ev.button,
+ ev.flag, ev.data);
+
+ break;
+ }
+
+ case cGraphTftComService::cmdStartCalibration:
+ {
+ ((cGraphTFTDisplay*)display)->setCalibrate(true);
+
+ break;
+ }
+
+ case cGraphTftComService::cmdStopCalibration:
+ {
+ ((cGraphTFTDisplay*)display)->setCalibrate(false);
+
+ break;
+ }
+
+ case cGraphTftComService::cmdCheck:
+ {
+ client->lastCheck = time(0);
+
+ break;
+ }
+
+ default:
+ {
+ tell(0, "Got unexpected protocol (%d/%d), aborting",
+ header.command, header.size);
+ status = fail;
+
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+//***************************************************************************
+// Put Key Code
+//***************************************************************************
+
+bool ComThread::Put(uint64_t Code, bool Repeat, bool Release)
+{
+ tell(5, "Put key action (%ld)", Code);
+
+ return cRemote::Put(Code, Repeat, Release);
+}
+
+//***************************************************************************
+// Refresh
+//***************************************************************************
+
+int ComThread::refresh()
+{
+ cMutexLock lock(&_mutex);
+ int status;
+
+ for (unsigned int i = 0; i < clients.size(); i++)
+ {
+ if ((status = refresh(&clients[i])) != success)
+ close(&clients[i], status, "Refresh failed, closing connection");
+ }
+
+ return done;
+}
+
+int ComThread::refresh(TcpClient* client)
+{
+ long size;
+ unsigned char* jpeg = 0;
+ int status = success;
+
+ if (!client->channel || !client->channel->isConnected())
+ return fail;
+
+ LogDuration ld("ComThread::refresh()", 2);
+
+ if ((size = renderer->toJpeg(jpeg, client->jpgQuality)) > 0)
+ {
+ tell(7, "Info: Write %ld kb to %d", size/1024, client->channel->getHandle());
+
+ status = client->channel->write(cGraphTftComService::cmdData, (char*)jpeg, size);
+
+ free(jpeg);
+ }
+
+ return status;
+}
diff --git a/comthread.h b/comthread.h
new file mode 100644
index 0000000..84ef25d
--- /dev/null
+++ b/comthread.h
@@ -0,0 +1,95 @@
+//***************************************************************************
+// Group VDR/GraphTFTng
+// File comthread.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-2013 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class ComThread
+//***************************************************************************
+
+#ifndef __GTFT_COMTHREAD_H__
+#define __GTFT_COMTHREAD_H__
+
+#include <vector>
+
+#include <vdr/plugin.h>
+#include <vdr/remote.h>
+#include <tcpchannel.h>
+
+#include <renderer.h>
+#include <service.h>
+
+using std::vector;
+
+//***************************************************************************
+// Communication Thread
+//***************************************************************************
+
+class ComThread : protected cThread, protected cRemote, public cGraphTftComService
+{
+ public:
+
+ enum Misc
+ {
+ maxBuf = 512*1024
+ };
+
+ struct TcpClient
+ {
+ TcpChannel* channel;
+ int jpgQuality;
+ time_t lastCheck;
+ };
+
+ ComThread(void* aDisplay, int width, int height);
+ virtual ~ComThread();
+
+ void stop() { running = false; }
+ int refresh();
+
+ int init(Renderer* aRenderer, unsigned int aPort = 0, const char* aHost = 0);
+ bool Start() { return cThread::Start(); }
+ void Stop();
+
+ void setHost(const char* aHost) { strcpy(host, aHost); }
+ void setPort(unsigned short aPort) { port = aPort; }
+ void setJpegQuality(int value) { jpgQuality = value; }
+
+ protected:
+
+ void Action();
+
+ int close(TcpClient* client, int status, const char* message = 0);
+ int read(fd_set* readSet);
+ int read(TcpClient* client);
+ int refresh(TcpClient* client);
+
+ virtual bool Put(uint64_t Code, bool Repeat = false, bool Release = false);
+
+ // data
+
+ TcpChannel* listener;
+ Renderer* renderer;
+ void* display;
+ cMutex _mutex;
+
+ int themeWidth;
+ int themeHeight;
+
+ char* buffer;
+ int bufferSize;
+ int timeout;
+ int checkTime;
+ int running;
+ unsigned short port;
+ char host[100+TB];
+ int pid;
+ int jpgQuality;
+
+ vector<TcpClient> clients;
+};
+
+//***************************************************************************
+#endif // __GTFT_COMTHREAD_H__
diff --git a/contrib/graphtft-fe.conf b/contrib/graphtft-fe.conf
new file mode 100644
index 0000000..c471e95
--- /dev/null
+++ b/contrib/graphtft-fe.conf
@@ -0,0 +1,57 @@
+#################################################################################
+# #
+# The following configuration file is generated automatically by the yaVDR #
+# system. Don't change this file as every update of yaVDR will overwrite #
+# the local changes. Instead put your required customizations #
+# into /etc/yavdr/templates_custom/ based on the original templates #
+# under /usr/share/yavdr/templates. #
+# #
+# http://www.yavdr.org/developer-zone/template-overview/ #
+# #
+# #
+#################################################################################
+
+description "YaVDR Configuration webinterface"
+
+
+start on started vdr or started openbox-second \
+ or vdr-frontend-restart
+stop on stopping vdr or stopping openbox
+
+
+env DISPLAY=:1.1
+export DISPLAY
+
+setuid vdr
+setgid vdr
+
+respawn
+normal exit 0
+
+
+
+script
+# wait for vdr and Xorg (after wm started)
+start wait-for-job-state WAIT_FOR=vdr TARGET_GOAL=start WAIT_STATE=running WAITER=graphtft-fe WAIT_FOREVER=1 ||:
+start wait-for-job-state WAIT_FOR=openbox-second TARGET_GOAL=start WAIT_STATE=running WAITER=graphtft-fe WAIT_FOREVER=1 ||:
+
+# get resolution
+TFT_SIZE=`xrandr -q -d $DISPLAY | grep " connected"|cut -d ' ' -f 3`
+TFT_WIDTH=`echo $TFT_SIZE | cut -d 'x' -f1`
+TFT_HEIGHT=`echo $TFT_SIZE | cut -d 'x' -f2 | cut -d '+' -f1`
+MORE_OPTIONS="-c 10"
+
+# override default settings
+test -f /etc/default/graphtft-fe && . /etc/default/graphtft-fe
+
+GRAPHTFTFEOPTS="-W $TFT_WIDTH -H $TFT_HEIGHT $MORE_OPTIONS"
+
+exec /usr/bin/graphtft-fe $GRAPHTFTFEOPTS -h localhost
+
+end script
+
+post-stop script
+# /usr/bin/feh --bg-center /usr/share/yavdr/images/yavdr_logo.png
+/usr/bin/hsetroot -center /usr/share/yavdr/images/yavdr_logo.png
+end script
+
diff --git a/contrib/xsnow.conf b/contrib/xsnow.conf
new file mode 100644
index 0000000..a37601a
--- /dev/null
+++ b/contrib/xsnow.conf
@@ -0,0 +1,29 @@
+description "xsnow on graphtft-fe"
+author "horchi"
+
+start on started vdr or vdr-frontend-restart
+stop on stopping vdr or stopping openbox
+
+# start on started graphtft-fe
+# stop on stopping graphtft-fe
+
+env DISPLAY=:1.1
+export DISPLAY
+
+respawn
+
+script
+
+month=`date +%m`
+
+if [ $month -ge 11 ] || [ $month -le 2 ]; then
+ sleep 3
+ if [ $month = 12 ]; then
+ exec /usr/games/xsnow -ssnowdepth 1
+ else
+ exec /usr/games/xsnow -nosanta -norudolf -ssnowdepth 1
+ fi
+fi
+
+end script
+
diff --git a/display.c b/display.c
new file mode 100644
index 0000000..e44d4dd
--- /dev/null
+++ b/display.c
@@ -0,0 +1,1592 @@
+/**
+ * GraphTFTng plugin for the Video Disk Recorder
+ *
+ * display.c
+ *
+ * (c) 2006-2013 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ **/
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <sys/inotify.h>
+
+#include <unistd.h>
+
+#include <setup.h>
+#include <display.h>
+#include <scan.h>
+
+// the render devices
+
+#include "fbrenderer.h"
+#include "dmyrenderer.h"
+
+#ifdef WITH_X
+# include "xrenderer.h"
+#endif
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cGraphTFTDisplay::cGraphTFTDisplay(const char* aSyntaxVersion)
+{
+ startedAt = time(0);
+
+ // init renderer
+
+ renderer = 0;
+
+ // init thread stuff
+
+ _active = no;
+ comThread = 0;
+ touchThread = 0;
+ triggerTimerUpdate = no;
+ triggerChannelUpdate = no;
+ triggerFinalizeItemList = no;
+
+ // init contents
+
+ currentSection = 0;
+ lastSection = 0;
+ _eventsReady = no;
+ _menu.charInTabs[0] = _menu.charInTabs[1] = _menu.charInTabs[2] =
+ _menu.charInTabs[3] = _menu.charInTabs[4] =
+ _menu.charInTabs[5] = _menu.charInTabs[6] = 0;
+ _mode = NormalView;
+ _sectionName = "";
+ channelType = ctTv;
+ _channel = 0;
+ _channelGroup = "";
+ _presentChannel = 0;
+ _volume = cDevice::CurrentVolume();
+ _mute = cDevice::PrimaryDevice()->IsMute();
+ _replay.control = 0;
+ _replay.name = "";
+ _replay.fileName = "";
+ _replay.lastMode = ModeUnknown;
+ _menu.currentRowLast = na;
+ _menu.currentRow = na;
+ _menu.current = "";
+ _menu.visibleRows = 0;
+ _menu.topRow = na;
+ _menu.lineHeight = na;
+ _menu.drawingRow = na;
+ displayActive = yes;
+ forceNextDraw = yes;
+ wakeup = no;
+ userDumpFile = 0;
+ userDumpWidth = 0;
+ userDumpHeight = 0;
+ needLock = no;
+
+ _recording = "";
+ _coverPath = "";
+
+ _music.filename = "";
+ _music.artist = "";
+ _music.album = "";
+ _music.genre = "";
+ _music.comment = "";
+ _music.year = na;
+ _music.frequence = 0;
+ _music.bitrate = 0;
+ _music.smode = "";
+ _music.index = 0;
+ _music.cnt = 0;
+ _music.status = "";
+ _music.currentTrack = "";
+ _music.loop = no;
+ _music.shuffle = no;
+ _music.shutdown = no;
+ _music.recording = no;
+ _music.rating = 0;
+ _music.lyrics = no;
+ _music.copy = no;
+ _music.timer = no;
+
+ // snapshot
+
+ snapshotPending = no;
+
+ // calibration stuff
+
+ calibration.cursorX = 0;
+ calibration.cursorY = 0;
+ calibration.instruction = "";
+ calibration.info = "";
+ calibration.state = csUnknown;
+ mouseX = 0;
+ mouseY = 0;
+ mouseKey = 0;
+ touchMenu = 0;
+ touchMenuHideTime = 0;
+ touchMenuHideAt = 0;
+
+ // initialize inotify
+
+ fdInotify = inotify_init1(IN_NONBLOCK);
+
+ if (fdInotify < 0)
+ {
+ fdInotify = na;
+ tell(0, "Couldn't initialize inotify, %m");
+ }
+}
+
+cGraphTFTDisplay::~cGraphTFTDisplay()
+{
+ Stop();
+
+ if (touchThread) delete touchThread;
+ if (comThread) delete comThread;
+ if (renderer) delete renderer;
+
+ if (fdInotify != na)
+ close(fdInotify);
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int cGraphTFTDisplay::init(const char* dev, int port, int startDetached)
+{
+ char* devName = 0;
+
+ // create renderer
+
+ if (strstr(dev, "/dev/fb"))
+ {
+ devName = strdup(dev);
+ tell(0, "Create FB renderer");
+ renderer = new FbRenderer(GraphTFTSetup.xOffset, GraphTFTSetup.yOffset,
+ GraphTFTSetup.width, GraphTFTSetup.height,
+ GraphTFTSetup.configPath, GraphTFTSetup.Iso2Utf,
+ Thms::theTheme->getDir());
+ }
+
+#ifdef WITH_X
+ else if (strstr(dev, "xorg:"))
+ {
+ devName = strdup(dev + strlen("xorg"));
+ tell(0, "Create X renderer at display '%s'", devName);
+ renderer = new XRenderer(GraphTFTSetup.xOffset, GraphTFTSetup.yOffset,
+ GraphTFTSetup.width, GraphTFTSetup.height,
+ GraphTFTSetup.configPath, GraphTFTSetup.Iso2Utf,
+ Thms::theTheme->getDir());
+ }
+#endif
+
+ else
+ {
+ tell(1, "No device configured, only graphtft-fe supported");
+
+ renderer = new DummyRenderer(GraphTFTSetup.xOffset, GraphTFTSetup.yOffset,
+ GraphTFTSetup.width, GraphTFTSetup.height,
+ GraphTFTSetup.configPath, GraphTFTSetup.Iso2Utf,
+ Thms::theTheme->getDir());
+ }
+
+ // apply renderer settings
+
+ renderer->setBorder(GraphTFTSetup.xBorder, GraphTFTSetup.yBorder);
+ renderer->setProperties(GraphTFTSetup.xOffset, GraphTFTSetup.yOffset,
+ Thms::theTheme->getWidth(), Thms::theTheme->getHeight(),
+ GraphTFTSetup.Iso2Utf, Thms::theTheme->getDir());
+
+ renderer->setDevName(devName);
+
+ // detached start only for xorg supported
+
+ if (!startDetached || !strstr(dev, "xorg:"))
+ {
+ if (renderer->init(/*lazy*/ yes) != success)
+ return fail;
+ }
+ else
+ {
+ tell(0, "Starting detached!");
+ }
+
+
+#ifdef WITH_TCPCOM
+ // for comthread use theme width/height instead of display width/height
+
+ comThread = new ComThread(this, Thms::theTheme->getWidth(), Thms::theTheme->getHeight());
+
+ if (comThread->init(renderer, port) != success)
+ {
+ tell(0, "Can't establish tcp listener at port %d, aborting", port);
+
+ return fail;
+ }
+
+ comThread->Start();
+#endif
+
+#ifdef WITH_TOUCH
+
+ // touch thread ..
+
+ touchThread = new cTouchThread(this);
+ touchThread->setDevice(GraphTFTSetup.touchDevice);
+
+ if (touchThread->open() != success)
+ {
+ tell(0, "Can't establish touch thread, touch panel not available!");
+ delete touchThread;
+ touchThread = 0;
+ }
+ else
+ {
+ touchThread->setSetting(&GraphTFTSetup.touchSettings);
+ touchThread->Start();
+ }
+
+#endif
+
+ // Show the start image
+
+ cDisplayItem::setRenderer(renderer);
+ cDisplayItem::setVdrStatus(this);
+ setupChanged();
+
+ Start();
+
+ return success;
+}
+
+//***************************************************************************
+// Broadcast
+//***************************************************************************
+
+void cGraphTFTDisplay::broadcast(int force)
+{
+ forceNextDraw = force ? force : forceNextDraw;
+ wakeup = yes;
+ _doUpdate.Broadcast();
+}
+
+//***************************************************************************
+// Set Mode
+//***************************************************************************
+
+int cGraphTFTDisplay::setMode(DisplayMode mode, const char* menuName, int force)
+{
+ if (_mode == ModeCalibration && !force)
+ return done;
+
+ if (_mode != mode || (menuName && _sectionName != menuName))
+ {
+ _mode = mode;
+ _sectionName = menuName ? menuName : "Menu";
+
+ tell(0, "Mode is set to (0x%X), menu section to '%s'",
+ _mode, _sectionName.c_str());
+
+ broadcast();
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Setup Changed
+//***************************************************************************
+
+void cGraphTFTDisplay::setupChanged(int w, int h)
+{
+ logDevice = GraphTFTSetup.LogDevice;
+ logLevel = GraphTFTSetup.Level;
+
+ if (comThread)
+ comThread->setJpegQuality(GraphTFTSetup.JpegQuality);
+
+ if (touchThread)
+ touchThread->setDevice(GraphTFTSetup.touchDevice, yes);
+
+ if (renderer)
+ {
+ renderer->flushCache();
+ renderer->setBorder(GraphTFTSetup.xBorder, GraphTFTSetup.yBorder);
+ renderer->setProperties(GraphTFTSetup.xOffset, GraphTFTSetup.yOffset,
+ Thms::theTheme->getWidth(), Thms::theTheme->getHeight(),
+ GraphTFTSetup.Iso2Utf,
+ Thms::theTheme->getDir());
+ renderer->setFontPath(Thms::theTheme->getFontPath());
+
+ // for X renderer set display size too, ignored by FB renderer!
+
+ renderer->setDisplaySize(w ? w : GraphTFTSetup.width,
+ h ? h : GraphTFTSetup.height);
+ }
+}
+
+//***************************************************************************
+// Switch/Set Calibrate
+//***************************************************************************
+
+void cGraphTFTDisplay::switchCalibrate(int state)
+{
+ if (isMode(ModeCalibration))
+ setCalibrate(off);
+ else
+ setCalibrate(on, state);
+}
+
+void cGraphTFTDisplay::setCalibrate(int active, int state)
+{
+ static int lastActive = no;
+ static cGraphTFTService::DisplayMode lastMode = cGraphTFTDisplay::NormalView;
+ static string lastSection = "";
+
+ if (active != lastActive)
+ {
+ lastActive = active;
+ tell(0, "Info: %s calibration mode", active ? "starting" : "stopping");
+ }
+
+ calibration.settings = GraphTFTSetup.touchSettings;
+
+ if (state == csUnknown && touchThread)
+ touchThread->setCalibrate(active);
+
+ if (active)
+ {
+ // store actual mode
+
+ lastMode = _mode;
+ lastSection = _sectionName;
+ setMode(ModeCalibration, 0, /*force*/ true);
+
+ calibration.state = state;
+
+ if (state == csUnknown)
+ {
+ calibration.settings.swapXY = no;
+ calibration.info = "calibration started";
+
+ if (touchThread)
+ touchThread->resetSetting();
+ }
+ else
+ {
+ calibration.instruction = "verify by touching ...";
+ calibration.info = "testing calibration";
+ }
+
+ calibrateTouch(0, 0);
+ }
+ else
+ {
+ setMode(lastMode, lastSection.c_str(), /*force*/ true);
+ }
+
+ broadcast(yes);
+}
+
+//***************************************************************************
+// Music Plugin Interface
+//***************************************************************************
+
+void cGraphTFTDisplay::musicAddPlaylistItem(const char* item, int index)
+{
+ if (index == 0)
+ _music.tracks.clear();
+
+ _music.tracks.push_back(item);
+}
+
+void cGraphTFTDisplay::setMusicPlayerState(cTftCS::MusicServicePlayerInfo* p)
+{
+ if (!p) return;
+
+ _music.filename = p->filename;
+ _music.artist = p->artist;
+ _music.album = p->album;
+ _music.genre = p->genre;
+ _music.comment = p->comment;
+ _music.year = p->year > 0 ? Str::toStr(p->year) : "--";
+ _music.frequence = p->frequence;
+ _music.bitrate = p->bitrate;
+ _music.smode = p->smode;
+ _music.index = p->index;
+ _music.cnt = p->count;
+ _music.status = p->status;
+ _music.currentTrack = p->currentTrack;
+ _music.loop = p->loop;
+ _music.shuffle = p->shuffle;
+ _music.shutdown = p->shutdown;
+ _music.recording = p->recording;
+ _music.rating = p->rating;
+ _music.lyrics = no; // TODO
+ _music.copy = no; // TODO
+ _music.timer = no; // TODO
+}
+
+void cGraphTFTDisplay::setMusicPlayerHelpButtons(cTftCS::MusicServiceHelpButtons* p)
+{
+ _music.red = p->red;
+ _music.green = p->green;
+ _music.yellow = p->yellow;
+ _music.blue = p->blue;
+}
+
+//***************************************************************************
+// Stop
+//***************************************************************************
+
+void cGraphTFTDisplay::Stop()
+{
+ if (_active)
+ {
+ _active = no;
+ broadcast();
+ Cancel(3);
+ }
+}
+
+//***************************************************************************
+// Get Tabbed Text
+//***************************************************************************
+
+const char* cGraphTFTDisplay::getTabbedText(const char* str, int index)
+{
+ static char buffer[1000+TB];
+ const char* a = str;
+ const char* b = strchrnul(a, '\t');
+
+ while (*b && index-- > 0)
+ {
+ a = b + 1;
+ b = strchrnul(a, '\t');
+ }
+
+ if (!*b)
+ return index <= 0 ? a : 0;
+
+ unsigned int n = b - a;
+
+ if (n >= sizeof(buffer))
+ n = sizeof(buffer) - 1;
+
+ strncpy(buffer, a, n);
+ buffer[n] = 0;
+
+ return buffer;
+}
+
+//***************************************************************************
+// Action
+//***************************************************************************
+
+void cGraphTFTDisplay::Action()
+{
+ uint64_t updateIn;
+ int n;
+
+ tell(0,"GraphTFT plugin display thread started (pid=%d)", getpid());
+
+ // display the start image
+
+ renderer->image(Thms::theTheme->getStartImage().c_str(), 0,0,0,0);
+ renderer->refresh();
+
+ // update the timer list
+
+ updateTimers();
+
+ // and give the plugin time to collect some data AND give vdr some time to
+ // finish initialization before acquiring the lock
+
+ sleep(3);
+
+ while (!Thms::theTheme->isInitialized())
+ usleep(100000);
+
+ _mutex.Lock(); // mutex gets ONLY unlocked when sleeping
+ _active = yes;
+
+ // main loop
+
+ while (_active)
+ {
+ tell(3, "action loop");
+
+ if (touchMenu)
+ {
+ if (touchMenuHideAt && msNow() > touchMenuHideAt-100)
+ touchMenu = 0;
+
+ forceNextDraw = yes;
+ }
+
+ if (isModeNormal(_mode))
+ {
+ // do the work ...
+
+ if (GraphTFTSetup.normalMode == "Standard")
+ n = display(channelType == ctTv ? "NormalTV" : "NormalRadio");
+ else
+ n = display("Normal" + GraphTFTSetup.normalMode);
+ }
+ else
+ {
+ switch (_mode)
+ {
+ case ReplayNormal: n = display("ReplayNormal"); break;
+ case ReplayMP3: n = display("ReplayMP3"); break;
+ case ReplayDVD: n = display("ReplayDVD"); break;
+ case ReplayImage: n = display("ReplayImage"); break;
+ case ModeCalibration: n = display("Calibration"); break;
+ case ModeMenu: n = display(_sectionName); break;
+ default: n = display("NormalTV");
+ }
+ }
+
+ updateIn = SECONDS(60); // the default
+
+ if (currentSection)
+ {
+ updateIn = currentSection->getNextUpdateTime() - msNow();
+
+ // auto hide of touch menu
+
+ if (touchMenu && touchMenuHideAt)
+ {
+ if ((touchMenuHideAt - msNow()) < updateIn)
+ updateIn = touchMenuHideAt - msNow();
+
+ tell(3, "Autohide scheduled in %ldms", updateIn);
+ }
+ }
+
+ if (updateIn < 10) // 10ms -> the minimum
+ updateIn = 10;
+
+ // can't calc this inline, due to a format string problem ... ?
+
+ int s = updateIn/1000; int us = updateIn%1000;
+
+ tell(2, "Displayed %d Items, schedule next "
+ "update in %d,%03d seconds", n, s, us);
+
+ wait(updateIn);
+
+ // some data update requests ...
+
+ if (triggerTimerUpdate)
+ updateTimers();
+
+ if (triggerChannelUpdate)
+ updateChannel();
+
+ if (triggerFinalizeItemList)
+ finalizeItemList();
+
+ // snapshot
+
+ if (snapshotPending)
+ takeSnapshot();
+ }
+
+ isyslog("GraphTFT plugin display thread ended (pid=%d)", getpid());
+}
+
+//***************************************************************************
+// Wait
+//***************************************************************************
+
+int cGraphTFTDisplay::wait(uint64_t updateIn)
+{
+ uint64_t waitStep = updateIn > 100 ? 100 : updateIn;
+ uint64_t waitUntil = updateIn + msNow();
+
+ wakeup = no;
+
+ while (msNow() < waitUntil && !wakeup)
+ {
+ _doUpdate.TimedWait(_mutex, waitStep);
+
+ if (waitUntil-msNow() < waitStep)
+ waitStep = waitUntil-msNow();
+
+ meanwhile();
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Meanwhile
+//***************************************************************************
+
+int cGraphTFTDisplay::meanwhile()
+{
+ static bool play = false, forward = false;
+ static int speed = 0;
+
+ bool aPlay, aForward;
+ int aSpeed;
+
+ // check some VDR states on changes
+
+ if (_replay.control && _replay.control->GetReplayMode(aPlay, aForward, aSpeed))
+ {
+ if (aPlay != play || aSpeed != speed || aForward != forward)
+ {
+ play = aPlay; forward = aForward; speed = aSpeed;
+
+ tell(3, "Trigger update of replay group (replay mode changed)");
+ updateGroup(groupReplay);
+ broadcast();
+ }
+ }
+/*
+ // check inotify
+
+ if (fdInotify != na)
+ {
+ const int sizeNameMax = 255;
+ const int sizeBuf = 10 * (sizeof(inotify_event) + sizeNameMax);
+
+ char buffer[sizeBuf];
+ inotify_event* event = 0;
+
+ int bytes = read(fdInotify, buffer, sizeBuf);
+
+ for (int pos = 0; pos < bytes; pos += sizeof(inotify_event) + event->len)
+ {
+ event = (inotify_event*)&buffer[pos];
+
+ tell(0, "got %d bytes fron inotify, mask %d, len %d, name '%s'",
+ bytes, event->mask, event->len, event->name ? event->name : "<null>");
+
+ if (event->mask & IN_CREATE || event->mask & IN_MODIFY)
+ {
+ cDisplayItem* p = Thms::theTheme->inotifies[event->wd];
+
+ tell(0, "inotify check event");
+
+ if (p)
+ {
+ p->scheduleDrawIn(10);
+
+ tell(0, "Got notification for '%s'", p->Path().c_str());
+ }
+ }
+ }
+ }
+*/
+ // process X events
+
+ renderer->xPending();
+
+ return done;
+}
+
+//***************************************************************************
+// Take Snapshot
+//***************************************************************************
+
+void cGraphTFTDisplay::takeSnapshot()
+{
+ char* path = 0;
+ char* file = 0;
+
+ snapshotPending = no;
+
+ // it's better to wait half a second, give the menu a chance to close ..
+
+ _doUpdate.TimedWait(_mutex, 500);
+
+ // tv view or replay running
+
+ if (_replay.fileName.length())
+ {
+ const cRecording* replay;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ tell(0, "lock for takeSnapshot()");
+ LOCK_RECORDINGS_READ;
+ const cRecordings* recordings = Recordings;
+#else
+ cRecordings* recordings = &Recordings;
+#endif
+
+ if ((replay = recordings->GetByName(_replay.fileName.c_str())) && replay->Info())
+ asprintf(&file, "%s", replay->Info()->Title());
+ }
+ else
+ {
+ if (!_presentEvent.isEmpty())
+ asprintf(&file, "%s", _presentEvent.Title());
+ }
+
+ if (!file)
+ {
+ Skins.Message(mtInfo, tr("Can't save snapshot, missing event information"));
+ return ;
+ }
+
+ strreplace(file, '/', ' ');
+ asprintf(&path, "%s/%s.jpg", GraphTFTSetup.snapshotPath, file);
+
+ if (cDevice::PrimaryDevice()->GrabImageFile(path, yes,
+ GraphTFTSetup.snapshotQuality,
+ GraphTFTSetup.snapshotWidth,
+ GraphTFTSetup.snapshotHeight))
+ Skins.Message(mtInfo, tr("Snapshot saved"));
+ else
+ Skins.Message(mtInfo, tr("Error saving snapshot"));
+
+ renderer->flushCache();
+ free(path);
+ free(file);
+}
+
+//***************************************************************************
+// update Timers
+//***************************************************************************
+
+#ifdef WITH_EPG2VDR
+# include "../vdr-plugin-epg2vdr/service.h"
+#endif
+
+void cGraphTFTDisplay::updateTimers()
+{
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ tell(3, "Clearing internal timer list.");
+ _timers.clear();
+
+#ifdef WITH_EPG2VDR
+
+ std::list<cEpgTimer_Interface_V1*>::iterator it;
+ cPlugin* pEpg2Vdr = cPluginManager::GetPlugin("epg2vdr");
+ cEpgTimer_Service_V1 data;
+
+ if (!pEpg2Vdr)
+ return;
+
+ if (pEpg2Vdr->Service(EPG2VDR_TIMER_SERVICE, &data))
+ {
+ tell(0, "Got list with %ld timers from epg2vdr", data.epgTimers.size());
+
+ for (it = data.epgTimers.begin(); it != data.epgTimers.end(); ++it)
+ {
+ tell(0, "Adding '%s' timer '%s' to list %s",
+ (*it)->isLocal() ? "local" : "remote",
+ (*it)->File(),
+ (*it)->hasState('R') ? "timer is recording" : "");
+
+ _timers.append(*it);
+
+ delete (*it);
+ }
+ }
+
+#else // WITH_EPG2VDR
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ tell(0, "lock for updateTimers()");
+ LOCK_TIMERS_READ;
+ const cTimers* timers = Timers;
+#else
+ const cTimers* timers = &Timers;
+#endif
+
+ for (const cTimer* timer = timers->First(); timer; timer = timers->Next(timer))
+ {
+ tell(3, "Adding timer '%s' to list %s",
+ timer->File(), timer->Recording() ? "timer is regording" : "");
+
+ _timers.append(timer);
+ }
+
+#endif // WITH_EPG2VDR
+
+ _timers.sort();
+
+ triggerTimerUpdate = no;
+ updateGroup(groupRecording);
+ broadcast();
+}
+
+//***************************************************************************
+// Update Channel
+//***************************************************************************
+
+void cGraphTFTDisplay::updateChannel()
+{
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ tell(0, "lock for updateChannel()");
+ LOCK_CHANNELS_READ;
+ _presentChannel = Channels->GetByNumber(_channel);
+#else
+ _presentChannel = Channels.GetByNumber(_channel);
+#endif
+
+ if (_presentChannel)
+ {
+ if (!isModeMenu(_mode))
+ {
+ switch (_presentChannel->Vpid())
+ {
+ case 0:
+ case 1:
+ case 0x1fff: channelType = ctRadio; break;
+ default: channelType = ctTv; break;
+ }
+
+ setMode(NormalView);
+ }
+ }
+
+ triggerChannelUpdate = no;
+}
+
+//***************************************************************************
+// Finalize Item List
+//***************************************************************************
+
+void cGraphTFTDisplay::finalizeItemList()
+{
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ const cChannels* channels = 0;
+ const cRecordings* recordings = 0;
+ cStateKey stateKeyChannels;
+ cStateKey stateKeyRecordings;
+
+ tell(0, "trylock for finalizeItemList(CHANNELS)");
+
+ if (!(channels = cChannels::GetChannelsRead(stateKeyChannels, 500)))
+ {
+ tell(0, "can't get lock for finalizeItemList(CHANNELS), retrying later");
+ return ;
+ }
+
+ tell(0, "trylock for finalizeItemList(REGORDINGS)");
+
+ if (!(recordings = cRecordings::GetRecordingsRead(stateKeyRecordings, 500)))
+ {
+ stateKeyChannels.Remove();
+ tell(0, "can't get lock for finalizeItemList(CHANNELS), retrying later");
+ return ;
+ }
+#else
+ cChannels* channels = &Channels;
+ cRecordings* recordings = &Recordings;
+#endif
+
+ for (string::size_type i = 0; i < _menu.items.size(); i++)
+ {
+ if (!_menu.items[i].event.isEmpty())
+ _menu.items[i].channel = channels->GetByChannelID(_menu.items[i].event.ChannelID());
+
+ if (!_menu.items[i].recording && _menu.items[i].recordingName.size())
+ _menu.items[i].recording = recordings->GetByName(_menu.items[i].recordingName.c_str());
+ }
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ stateKeyChannels.Remove();
+ stateKeyRecordings.Remove();
+#endif
+
+ _eventsReady = yes;
+ triggerFinalizeItemList = no;
+}
+
+//***************************************************************************
+// Item At
+//***************************************************************************
+
+cDisplayItem* cGraphTFTDisplay::getItemAt(int x, int y)
+{
+ // first check foreground items ..
+
+ for (cDisplayItem* p = currentSection->getItems()->First(); p;
+ p = currentSection->getItems()->Next(p))
+ {
+ if (!p->Foreground() || !p->evaluateCondition())
+ continue;
+
+ if (p->OnClick() == "" && p->OnDblClick() == "" && p->OnUp() == "" && p->OnDown() == "")
+ continue;
+
+ if (x >= p->X() && x <= p->X()+p->Width() &&
+ y >= p->Y() && y <= p->Y()+p->Height())
+ {
+ return p;
+ }
+ }
+
+ // now the other ..
+
+ for (cDisplayItem* p = currentSection->getItems()->First(); p;
+ p = currentSection->getItems()->Next(p))
+ {
+ if (p->Foreground() || !p->evaluateCondition())
+ continue;
+
+ if (p->OnClick() == "" && p->OnDblClick() == "" && p->OnUp() == "" && p->OnDown() == "")
+ continue;
+
+ if (x >= p->X() && x <= p->X()+p->Width() &&
+ y >= p->Y() && y <= p->Y()+p->Height())
+ {
+ return p;
+ }
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Mouse Event
+//***************************************************************************
+
+void cGraphTFTDisplay::mouseEvent(int x, int y, int button, int flag, int data)
+{
+ static int whipeDiff = 0;
+
+ if (!currentSection)
+ return ;
+
+ mouseX = x;
+ mouseY = y;
+ mouseKey = button;
+
+ cMutexLock lock(&_mutex);
+
+ cDisplayItem* p = getItemAt(x, y);
+
+ if (p)
+ tell(4, "Mouse action on item %d [%s] at (%d/%d)", p->Item(),
+ p->Text().c_str(), x, y);
+
+ if (isMode(ModeCalibration))
+ {
+ calibrateTouch(x, y);
+ updateGroup(groupCalibrate);
+ broadcast(yes);
+
+ if (calibration.state < csTest)
+ return ;
+ }
+
+ if (button == cGraphTftComService::mbWheelUp)
+ {
+ if (!p || p->OnUp() == "")
+ cRemote::Put(cKey::FromString("Up"));
+ else
+ processAction(p, p->OnUp());
+ }
+
+ else if (button == cGraphTftComService::mbWheelDown)
+ {
+ if (!p || p->OnDown() == "")
+ cRemote::Put(cKey::FromString("Down"));
+ else
+ processAction(p, p->OnDown());
+ }
+
+ else if (button == cGraphTftComService::mbRight)
+ {
+ cRemote::Put(cKey::FromString("Back"));
+ }
+
+ else if (button == cGraphTftComService::mbLeft)
+ {
+ if (!p)
+ return ;
+
+ if (flag & cGraphTftComService::efVWhipe)
+ {
+ tell(3, "vertical whipe of (%d) pixel", data);
+
+ whipeDiff += data;
+
+ int step = abs(whipeDiff) / p->WhipeRes();
+
+ if (step)
+ {
+ tell(3, "do step of (%d)", step);
+
+ if (whipeDiff < 0)
+ {
+ if (p->OnDown() != "")
+ processAction(p, p->OnDown(), step);
+ }
+ else
+ {
+ if (p->OnUp() != "")
+ processAction(p, p->OnUp(), step);
+ }
+
+ whipeDiff = whipeDiff % p->WhipeRes();
+ }
+
+ return ;
+ }
+
+ whipeDiff = 0;
+
+ // menue navigation area ?
+
+ if (p->Item() == itemMenuNavigationArea && !touchMenu &&
+ _menu.lineHeight > 0)
+ {
+ int clickRow;
+ int yOff = y - p->Y();
+ int currentRowY = (_menu.currentRow - _menu.topRow) * _menu.lineHeight;
+
+ if (yOff < currentRowY)
+ clickRow = yOff / _menu.lineHeight + _menu.topRow;
+ else if (yOff < currentRowY + _menu.lineHeightSelected)
+ clickRow = _menu.currentRow;
+ else
+ clickRow = (yOff-_menu.lineHeightSelected) / _menu.lineHeight + _menu.topRow+1;
+
+ if (_menu.currentRow < clickRow) // down
+ for (int i = _menu.currentRow; i < clickRow; i++)
+ cRemote::Put(cKey::FromString("Down"));
+ else if (_menu.currentRow > clickRow) // up
+ for (int i = _menu.currentRow; i > clickRow; i--)
+ cRemote::Put(cKey::FromString("Up"));
+ }
+
+ // check if item with defined action
+
+ if (p->OnClick() != "" && flag == cGraphTftComService::efNone)
+ {
+ if (p->OnClick().find("touchMenu") == 1)
+ {
+ char* val;
+ char* val2;
+ char* click;
+
+ asprintf(&click, "%s", p->OnClick().c_str());
+
+ // touch menu handling
+
+ if ((val = strchr(click, ':')) && *(val++))
+ {
+ if ((val2 = strchr(val, ':')) && *(val2++)
+ && atoi(val) == touchMenu)
+ touchMenu = atoi(val2);
+ else
+ touchMenu = atoi(val);
+ }
+ else
+ touchMenu = !touchMenu;
+
+ if (p->Delay())
+ touchMenuHideTime = p->Delay();
+
+ if (touchMenuHideTime)
+ touchMenuHideAt = msNow() + touchMenuHideTime;
+
+ tell(4, "touch menu switched to (%d) hide in (%ld) seconds",
+ touchMenu, touchMenuHideTime);
+
+ free(click);
+ broadcast(yes);
+ }
+ else
+ {
+ // no key, and not the 'touchMenu' command
+
+ processAction(p, p->OnClick());
+ }
+ }
+
+ if (p->OnDblClick() != "" && (flag & cGraphTftComService::efDoubleClick))
+ {
+ processAction(p, p->OnDblClick());
+ }
+ }
+}
+
+//***************************************************************************
+// Process Action
+//***************************************************************************
+
+int cGraphTFTDisplay::processAction(cDisplayItem* p, string action, int step)
+{
+ string v;
+ Scan scan(action.c_str());
+ string name;
+ eKeys key;
+ int value = 0;
+ string str;
+
+ scan.eat();
+ key = cKey::FromString(scan.lastIdent());
+
+ tell(4, "Performing mouse action (%d) times, key '%s', %sfound in VDRs keytab",
+ step, scan.all(), key != kNone ? "" : "not ");
+
+ // first check if 'normal' key actions
+
+ if (key != kNone)
+ {
+ for (int i = 0; i < abs(step); i++)
+ {
+ scan.reset();
+
+ while (scan.eat() == success)
+ {
+ if ((key = cKey::FromString(scan.lastIdent())) != kNone)
+ cRemote::Put(key);
+ }
+ }
+
+ return success;
+ }
+
+ // perform special action on theme variable
+
+ if (!scan.isIdent() || p->lookupVariable(scan.lastIdent(), v) != success)
+ {
+ tell(0, "Error: Invalid variable '%s' in '%s'",
+ scan.lastIdent(), scan.all());
+ return fail;
+ }
+
+ name = scan.lastIdent();
+
+ if (scan.eat() != success || !scan.isOperator())
+ {
+ tell(0, "Error: Invalid operator '%s' in '%s'",
+ scan.lastIdent(), scan.all());
+ return fail;
+ }
+
+ // get the actual value of the variable
+
+ value = atoi(v.c_str());
+
+ // perform action on the value
+
+ if (scan.hasValue("++"))
+ value += step;
+ else if (scan.hasValue("--"))
+ value -= step;
+ else if (scan.hasValue(":"))
+ {
+ int v1;
+
+ if (scan.eat() != success || !scan.isNum())
+ {
+ tell(0, "Missing int value in '%s', ignoring", scan.all());
+ return fail;
+ }
+
+ v1 = scan.lastInt();
+
+ if (value == v1 && scan.eat() == success && scan.isOperator() && scan.hasValue(":"))
+ {
+ if (scan.eat() != success || !scan.isNum())
+ {
+ tell(0, "Missing second int value in '%s', ignoring", scan.all());
+ return fail;
+ }
+
+ value = scan.lastInt();
+ }
+ else
+ {
+ value = v1;
+ }
+ }
+ else
+ {
+ tell(0, "Unexpected operation in '%s', ignoring", scan.all());
+ return fail;
+ }
+
+ tell(6, "Setting '%s' from (%s) to (%d)", name.c_str(), v.c_str(), value);
+ p->setVariable(name.c_str(), value);
+ broadcast(yes);
+
+ return done;
+}
+
+//***************************************************************************
+// Calibrate Touch Device
+//***************************************************************************
+
+int cGraphTFTDisplay::calibrateTouch(int x, int y)
+{
+ static double upperLeftX;
+ static double upperLeftY;
+
+ string s;
+ int offset = 20;
+
+ if (Thms::theTheme->lookupVar("calibrationFrameOffset", s) == success)
+ offset = atoi(s.c_str());
+
+ calibration.state++;
+
+ switch (calibration.state)
+ {
+ case csUpperLeft:
+ {
+ calibration.instruction = "Click upper left corner";
+ calibration.cursorX = offset;
+ calibration.cursorY = offset;
+ calibration.lastX = 0;
+ calibration.lastY = 0;
+ break;
+ }
+ case csUpperRight:
+ {
+ upperLeftX = x;
+ upperLeftY = y;
+
+ calibration.instruction = "Click upper right corner";
+ calibration.cursorX = Thms::theTheme->getWidth() - offset;
+ calibration.cursorY = offset;
+ break;
+ }
+ case csLowerLeft:
+ {
+ // check for swap
+
+ if (abs(calibration.lastY - y) > abs(calibration.lastX - x))
+ {
+ calibration.settings.swapXY = yes;
+ if (touchThread) touchThread->resetSetting(yes);
+
+ calibration.info = "detected flags (swapXY)";
+
+ // restart calibration due to XY swap!
+
+ calibration.state = csUnknown;
+ calibrateTouch(0, 0);
+ break;
+ }
+
+ calibration.settings.scaleX = ((double)(Thms::theTheme->getWidth() - 2*offset))
+ / ((double)(x-calibration.lastX));
+
+ calibration.instruction = "Click lower left corner";
+ calibration.cursorX = offset;
+ calibration.cursorY = Thms::theTheme->getHeight() - offset;
+
+ break;
+ }
+ case csLowerRight:
+ {
+ calibration.settings.scaleY = ((double)(Thms::theTheme->getHeight()- 2*offset))
+ / ((double)(y-calibration.lastY)); // upperLeftY
+
+ calibration.settings.offsetX = (int)((((double)offset)/calibration.settings.scaleX) - upperLeftX);
+ calibration.settings.offsetY = (int)((((double)offset)/calibration.settings.scaleY) - upperLeftY);
+ calibration.settings.scaleWidth = Thms::theTheme->getWidth();
+ calibration.settings.scaleHeight = Thms::theTheme->getHeight();
+
+ calibration.instruction = "Click lower right corner";
+ calibration.cursorX = Thms::theTheme->getWidth() - offset;
+ calibration.cursorY = Thms::theTheme->getHeight() - offset;
+ break;
+ }
+ case csDone:
+ {
+ if (touchThread)
+ {
+ touchThread->setSetting(&calibration.settings);
+ touchThread->setCalibrate(off);
+ GraphTFTSetup.touchSettings = calibration.settings;
+ GraphTFTSetup.Store(yes);
+ }
+
+ tell(0, "Calibration done, offset (%d/%d), scale (%f/%f)",
+ calibration.settings.offsetX, calibration.settings.offsetY,
+ calibration.settings.scaleX, calibration.settings.scaleY);
+
+ calibration.info = "verify by touching ...";
+ calibration.instruction = "Calibration done";
+ calibration.cursorX = x;
+ calibration.cursorY = y;
+ }
+ case csTest:
+ {
+ calibration.info = "verify by touching ...";
+ calibration.instruction = "Calibration done";
+ calibration.cursorX = x;
+ calibration.cursorY = y;
+
+ break;
+ }
+ default:
+ {
+ calibration.cursorX = x;
+ calibration.cursorY = y;
+ }
+ }
+
+ tell(0, "Callibration step '%s'", calibration.instruction.c_str());
+
+ calibration.lastX = x;
+ calibration.lastY = y;
+
+ return done;
+}
+
+//***************************************************************************
+// Clear
+//***************************************************************************
+
+void cGraphTFTDisplay::clear()
+{
+ renderer->clear();
+}
+
+//***************************************************************************
+// Display
+//***************************************************************************
+
+int cGraphTFTDisplay::display(string sectionName)
+{
+ int count = 0;
+
+ LogDuration ld("cGraphTFTDisplay::display()");
+
+ if (!displayActive)
+ return 0;
+
+ if (isModeNormal(_mode))
+ updateProgram();
+
+ else if (isModeMenu(_mode) && !Thms::theTheme->getSection(sectionName))
+ {
+ tell(0, "Info: Section faked to '%s' due to section '%s' not defined!",
+ "Menu", sectionName.c_str());
+
+ sectionName = "Menu";
+ }
+
+ if (!(currentSection = Thms::theTheme->getSection(sectionName)))
+ return 0;
+
+ // set/reset force flag
+
+ cDisplayItem::setForce(forceNextDraw);
+ forceNextDraw = no;
+
+ if (cDisplayItem::getForce())
+ tell(1, "Force draw of all items now");
+
+ // section changed
+
+ if (currentSection != lastSection)
+ {
+ tell(0, "Section changed from '%s' to '%s'",
+ lastSection ? lastSection->getName().c_str() : "<none>",
+ currentSection->getName().c_str());
+
+ lastSection = currentSection;
+
+ _menu.topRow = na;
+ _menu.lineHeight = na;
+ cDisplayItem::clearSelectedItem();
+
+ clear();
+ cDisplayItem::setForce(yes);
+ }
+
+ // for now ...
+
+ if (isModeMenu(_mode))
+ {
+ cDisplayItem::setForce(yes);
+ tell(3, "force due to menu section!");
+
+ // reset x of menu items
+
+ for (string::size_type i = 0; i < _menu.items.size(); i++)
+ _menu.items[i].nextX = 0;
+ }
+
+ needLock = no;
+
+ currentSection->updateVariables();
+ updateGroup(groupVarFile);
+
+ // draw items
+
+ for (cDisplayItem* p = currentSection->getItems()->First();
+ !needLock && p; p = currentSection->getItems()->Next(p))
+ {
+ if (!p->isForegroundItem() && !p->Foreground())
+ count += p->refresh();
+ }
+
+ for (cDisplayItem* p = currentSection->getItems()->First();
+ !needLock && p; p = currentSection->getItems()->Next(p))
+ {
+ if (p->isForegroundItem() || p->Foreground())
+ count += p->refresh();
+ }
+
+ // refresh changed areas (only supported by X renderer)
+
+ if (!isModeMenu(_mode))
+ {
+ for (cDisplayItem* p = currentSection->getItems()->First();
+ !needLock && p; p = currentSection->getItems()->Next(p))
+ {
+ if (p->Changed())
+ renderer->refreshArea(p->X(), p->Y(), p->Width(), p->Height());
+ }
+ }
+
+ // update display
+
+ if (!needLock && count)
+ refresh();
+
+ if (needLock)
+ forceNextDraw = yes;
+
+ return count;
+}
+
+//***************************************************************************
+// Refresh
+//***************************************************************************
+
+void cGraphTFTDisplay::refresh()
+{
+ // LogDuration ld("cGraphTFTDisplay::refresh()");
+
+ // refresh local display
+
+ // renderer->refresh(cDisplayItem::getForce());
+
+ renderer->refresh(isModeMenu(_mode));
+
+ // refresh tcp client
+
+ if (comThread)
+ comThread->refresh();
+
+ // dump to file ...
+
+ if (GraphTFTSetup.DumpImage)
+ {
+ static int lastDumpAt = msNow();
+ int lastDumpDiff = lastDumpAt ? msNow() - lastDumpAt : 0;
+
+ if (lastDumpDiff > (GraphTFTSetup.DumpRefresh * 1000))
+ {
+ // dump
+
+ lastDumpAt = msNow();
+ renderer->dumpImage2File("/graphtftng.png", GraphTFTSetup.width, GraphTFTSetup.height);
+ }
+ }
+
+ if (userDumpFile)
+ {
+ renderer->dumpImage2File(userDumpFile, userDumpWidth, userDumpHeight);
+ free(userDumpFile);
+ userDumpFile = 0;
+ }
+}
+
+//***************************************************************************
+// Update Programme
+//***************************************************************************
+
+void cGraphTFTDisplay::updateProgram()
+{
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ tell(0, "lock for updateProgram(CHANNELS)");
+ LOCK_CHANNELS_READ;
+ const cChannel* channel = Channels->GetByNumber(_channel);
+#else
+ const cChannel* channel = Channels.GetByNumber(_channel);
+#endif
+
+ if (channel)
+ {
+ tell(5, "updateProgram for channel '%s'", channel->Name());
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+// const cEvent* present = _presentEvent;
+// const cEvent* following = _followingEvent;
+
+// _presentEvent = _followingEvent = 0;
+
+ _presentEvent.reset();
+ _followingEvent.reset();
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ tell(0, "lock for updateProgram(SCHEDULES)");
+ LOCK_SCHEDULES_READ;
+ const cSchedules* schedules = Schedules;
+#else
+ cSchedulesLock schedulesLock;
+ const cSchedules* schedules = (cSchedules*)cSchedules::Schedules(schedulesLock);
+#endif
+
+ if (schedules)
+ {
+ const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID());
+
+ if (schedule)
+ {
+ _presentEvent.set(schedule->GetPresentEvent());
+ _followingEvent.set(schedule->GetFollowingEvent());
+ }
+ }
+
+// if (present != _presentEvent || following != _followingEvent)
+ {
+ updateGroup(groupChannel);
+ broadcast();
+ }
+ }
+}
+
+//***************************************************************************
+// Trigger Dump
+//***************************************************************************
+
+void cGraphTFTDisplay::triggerDump(const char* file, int width, int height)
+{
+ userDumpWidth = width == na ? GraphTFTSetup.width : width;
+ userDumpHeight = height == na ? GraphTFTSetup.height : height;
+
+ free(userDumpFile);
+ userDumpFile = strdup(file);
+
+ wakeup = yes;
+ _doUpdate.Broadcast();
+}
+
+int cGraphTFTDisplay::updateGroup(int group)
+{
+ if (!currentSection)
+ return ignore;
+
+ return currentSection->updateGroup(group);
+}
diff --git a/display.h b/display.h
new file mode 100644
index 0000000..da11e3a
--- /dev/null
+++ b/display.h
@@ -0,0 +1,638 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * display.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2014 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 __GTFT_DISPLAY_H
+#define __GTFT_DISPLAY_H
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+#include <vdr/status.h>
+#include <vdr/skins.h>
+
+#include <common.h>
+#include <comthread.h>
+#include <touchthread.h>
+#include <renderer.h>
+#include <theme.h>
+
+#ifdef WITH_EPG2VDR
+# include "../vdr-plugin-epg2vdr/service.h"
+#endif
+
+using std::string;
+using std::vector;
+
+//***************************************************************************
+// Class cGraphTFTService
+//***************************************************************************
+
+class cGraphTFTService : public cThemeService
+{
+ public:
+
+ enum ChannelType
+ {
+ ctRadio = 0,
+ ctTv
+ };
+
+ enum { MaxTabs = 10 };
+
+ enum CalibrationStart
+ {
+ csUnknown = na,
+ csUpperLeft,
+ csUpperRight,
+ csLowerLeft,
+ csLowerRight,
+ csDone,
+ csTest
+ };
+
+ enum DisplayMode
+ {
+ ModeUnknown = 0x00,
+
+ ModeMask = 0xF0,
+ DisplayMask = 0x0F,
+
+ ModeNormal = 0x10,
+ ModeReplay = 0x20,
+ ModeMenu = 0x30,
+
+ NormalView = 0x11,
+
+ ReplayNormal = 0x21,
+ ReplayMP3 = 0x22,
+ ReplayDVD = 0x23,
+ ReplayImage = 0x24,
+
+ MenuDefault = 0x31,
+ MenuMain = 0x32,
+ MenuSchedule = 0x33,
+ MenuChannels = 0x34,
+ MenuTimers = 0x35,
+ MenuRecordings = 0x36,
+ MenuSetup = 0x37,
+ MenuCommands = 0x38,
+ MenuWhatsOnElse = 0x39,
+ MenuWhatsOnNow = 0x3a,
+ MenuWhatsOnNext = 0x3b,
+ MenuSetupPage = 0x3c,
+
+ ModeCalibration = 0x41,
+ ModeCalibrationTest = 0x42
+ };
+
+ static int isModeNormal(DisplayMode mode) { return (mode & ModeMask) == ModeNormal; }
+ static int isModeReplay(DisplayMode mode) { return (mode & ModeMask) == ModeReplay; }
+ static int isModeMenu(DisplayMode mode) { return (mode & ModeMask) == ModeMenu; }
+};
+
+//***************************************************************************
+// Class cEventCopy
+//***************************************************************************
+
+class cEventCopy
+{
+ public:
+
+ cEventCopy() { initialized = no; title = 0; description = 0; shortText = 0; }
+ ~cEventCopy() { free(title); free(description); free(shortText); }
+
+ void set(const cEvent* event)
+ {
+ if (!event)
+ {
+ initialized = no;
+ return;
+ }
+
+ setEventID(event->EventID());
+ setChannelID(event->ChannelID());
+
+ setSeen(event->Seen());
+ setTableId(event->TableID());
+ setVersion(event->Version());
+ setRunningStatus(event->RunningStatus());
+ setTitle(event->Title());
+ setShortText(event->ShortText());
+ setDescription(event->Description());
+ setParentalRating(event->ParentalRating());
+ setStartTime(event->StartTime());
+ setDuration(event->Duration());
+ setVps(event->Vps());
+
+ initialized = yes;
+ }
+
+ void set(const cEventCopy* event)
+ {
+ if (!event || event->isEmpty())
+ {
+ initialized = no;
+ return;
+ }
+
+ setEventID(event->EventID());
+ setChannelID(event->ChannelID());
+
+ setSeen(event->Seen());
+ setTableId(event->TableID());
+ setVersion(event->Version());
+ setRunningStatus(event->RunningStatus());
+ setTitle(event->Title());
+ setShortText(event->ShortText());
+ setDescription(event->Description());
+ setParentalRating(event->ParentalRating());
+ setStartTime(event->StartTime());
+ setDuration(event->Duration());
+ setVps(event->Vps());
+
+ initialized = yes;
+ }
+
+ int isEmpty() const { return !initialized; }
+ void reset() { initialized = no; }
+
+ tEventID EventID() const { return eventId; }
+ tChannelID ChannelID() const { return channelId; }
+ time_t StartTime() const { return startTime; }
+ time_t EndTime() const { return startTime + Duration(); }
+ time_t Seen() const { return seen; }
+ uchar TableID() const { return tableId; }
+ bool IsRunning() const { return isRunning; }
+ bool SeenWithin(int Seconds) const { return time(0) - seen < Seconds; }
+ time_t Vps() const { return vps; }
+ uchar Version() const { return version; }
+ uchar RunningStatus() const { return runningStatus; }
+ uchar ParentalRating() const { return parentalRating; }
+ int Duration() const { return duration; }
+ const char* Title() const { return title; }
+ const char* Description() const { return description; }
+ const char* ShortText() const { return shortText; }
+
+ protected:
+
+ void setEventID(tEventID EventId) { eventId = EventId; }
+ void setChannelID(tChannelID ChannelId) { channelId = ChannelId; }
+ void setStartTime(time_t StartTime) { startTime = StartTime; }
+ void setRunning(bool IsRunning) { isRunning = IsRunning; }
+ void setSeen(time_t Seen) { seen = Seen; }
+ void setVps(time_t Vps) { vps = Vps; }
+ void setTableId(uchar tid) { tableId = tid; }
+ void setVersion(uchar Version) { version = Version; }
+ void setRunningStatus(uchar RunningStatus) { runningStatus = RunningStatus; }
+ void setTitle(const char* Title) { free(title); title = strdup(Str::notNull(Title)); }
+ void setDescription(const char* Description) { free(description); description = strdup(Str::notNull(Description)); }
+ void setParentalRating(uchar ParentalRating) { parentalRating = ParentalRating; }
+ void setShortText(const char* ShortText) { free(shortText); shortText = strdup(Str::notNull(ShortText)); }
+ void setDuration(int Duration) { duration = Duration; }
+
+ int initialized;
+
+ tChannelID channelId;
+ tEventID eventId;
+ time_t startTime;
+ bool isRunning;
+ time_t seen;
+ time_t vps;
+ uchar tableId;
+ uchar version;
+ uchar runningStatus;
+ uchar parentalRating;
+ int duration;
+
+ char* title;
+ char* description;
+ char* shortText;
+};
+
+//***************************************************************************
+// Class GraphTFT Display
+//***************************************************************************
+
+class cGraphTFTDisplay : public cStatus, cThread, public cGraphTFTService
+{
+ public:
+
+ // object
+
+ cGraphTFTDisplay(const char* aSyntaxVersion);
+ ~cGraphTFTDisplay();
+
+ // interface
+
+ int init(const char* dev, int port, int startDetached);
+ void setupChanged(int w = 0, int h = 0);
+ void setCalibrate(int active, int state = csUnknown);
+ void switchCalibrate(int state = csUnknown);
+ int calibrateTouch(int x, int y);
+ int setMode(DisplayMode mode, const char* menuName = 0, int force = false);
+ int isMode(DisplayMode mode) { return mode == _mode; }
+ void setCoverPath(const char* path) { _coverPath = Str::notNull(path); }
+ void musicAddPlaylistItem(const char* item, int index = na);
+ void setMusicPlayerState(cTftCS::MusicServicePlayerInfo* state);
+ void setMusicPlayerHelpButtons(cTftCS::MusicServiceHelpButtons* buttons);
+ cTouchThread* getTouchThread() { return touchThread; }
+
+ Renderer* getRenderer() { return renderer; }
+ int attachXorg(const char* disp = 0) { if (!renderer) return fail; return renderer->attach(disp); }
+ int detachXorg() { if (!renderer) return fail; return renderer->detach(); }
+
+ // thread stuff
+
+ bool Start() { return cThread::Start(); }
+ void Stop();
+ bool Active() const { return _active; }
+ cMutex* getMutex() { return &_mutex; }
+
+ void themesReloaded() { lastSection = 0; currentSection = 0; }
+
+ // due to comThread destructor is called by vdr (inherited by cRemote)
+ // don't delete comThread here!
+
+ void clearComThread() { comThread = 0; }
+ void clearTouchThread() { touchThread = 0; }
+
+ // event
+
+ void mouseEvent(int x, int y, int button, int flag = ComThread::efNone, int data = 0);
+ cDisplayItem* getItemAt(int x, int y);
+ int processAction(cDisplayItem* p, string action, int step = 1);
+
+ // display stuff
+
+ void broadcast(int force = no);
+ void triggerDump(const char* file, int width = na, int height = na);
+ int display(string sectionName);
+ void refresh();
+ void clear();
+ int updateGroup(int group);
+
+ int getUnseenMails()
+ {
+ unsigned long mailCount = 0;
+
+ if (cPluginManager::CallFirstService("MailBox-GetTotalUnseen-1.0", &mailCount))
+ return mailCount;
+
+ return na;
+ }
+
+ int hasNewMail()
+ {
+ int mailFlag = no;
+
+ if (cPluginManager::CallFirstService("MailBox-HasNewMail-1.0", &mailFlag))
+ return mailFlag;
+
+ return no;
+ }
+
+ // some structures and types
+
+ struct RdsInfo
+ {
+ string text;
+ string title;
+ string artist;
+ };
+
+ class cTextList
+ {
+ public:
+
+ cTextList() { it = 0; }
+
+ virtual int count() = 0;
+
+ virtual void clear() { reset(); };
+ virtual void reset() { it = 0; }
+ virtual void inc() { it++; }
+ virtual int iter() { return it; }
+
+ int isValid(int i = na)
+ {
+ i = i != na ? i : it;
+ return count() > 0 && i < count();
+ }
+
+ private:
+
+ unsigned int it;
+ };
+
+ class TimerList : public cTextList
+ {
+ public:
+
+ TimerList() : cTextList() { runningCnt = 0; }
+
+ void append(const cTimer* timer)
+ {
+ GtftTimerInfo info;
+
+#ifdef WITH_EPG2VDR
+ const cEpgTimer_Interface_V1* t = dynamic_cast<const cEpgTimer_Interface_V1*>(timer);
+ info.running = t->hasState('R');
+#else
+ info.running = timer->Recording();
+#endif
+
+ info.title = timer->File(); // as default
+ info.file = timer->File();
+ info.start = timer->StartTime();
+ info.stop = timer->StopTime();
+
+ if (timer->Event())
+ info.title = timer->Event()->Title();
+
+ timers.push_back(info);
+
+ if (info.running) runningCnt++;
+ }
+
+ const char* firstRunning()
+ {
+ for (int i = 0; i < count(); i++)
+ if (timers[i].running)
+ return timers[i].title.c_str();
+
+ return "";
+ }
+
+ time_t start() { return !isValid() ? 0 : timers[iter()].start; }
+ time_t stop() { return !isValid() ? 0 : timers[iter()].stop; }
+ const char* title() { return !isValid() ? "" : timers[iter()].title.c_str(); }
+ const char* file() { return !isValid() ? "" : timers[iter()].file.c_str(); }
+ int running() { return !isValid() ? 0 : timers[iter()].running; }
+
+ int count() { return timers.size(); }
+ int countRunning() { return runningCnt; }
+
+ void clear()
+ {
+ cTextList::clear();
+ timers.clear();
+ runningCnt = 0;
+ }
+
+ void sort() { std::sort(timers.begin(), timers.end()); }
+
+ protected:
+
+ class GtftTimerInfo
+ {
+ public:
+ string title;
+ string file;
+ time_t start;
+ time_t stop;
+ int running;
+
+ bool operator < (const GtftTimerInfo rhs) const { return start < rhs.start; }
+ };
+
+ int runningCnt;
+ vector<GtftTimerInfo> timers;
+ };
+
+ class MusicPlayerInfo : public cTextList
+ {
+ public:
+
+ MusicPlayerInfo() : cTextList() {}
+
+ string filename;
+ string artist;
+ string album;
+ string genre;
+ string comment;
+ string year;
+ double frequence;
+ int bitrate;
+ string smode;
+ bool loop;
+ bool shuffle;
+ bool shutdown;
+ bool recording;
+ int rating;
+ int index; // current index in tracklist
+ int cnt; // items in tracklist
+ bool lyrics;
+ bool copy;
+ bool timer;
+ string status; // player status
+ string currentTrack;
+ vector<string> tracks; // tracklist (only the unplayed part!)
+
+ // help buttons
+
+ string red;
+ string green;
+ string yellow;
+ string blue;
+
+ int count() { return tracks.size(); }
+
+ const char* track() { return tracks[iter()].c_str(); }
+
+ void clear()
+ {
+ cTextList::clear();
+ tracks.clear();
+ }
+ };
+
+ // actual replay
+
+ struct ReplayInfo
+ {
+ DisplayMode lastMode;
+ string name;
+ string fileName;
+ cControl* control;
+ };
+
+ struct Calibration
+ {
+ string instruction;
+ string info;
+ int state;
+ int cursorX;
+ int cursorY;
+ int lastX;
+ int lastY;
+
+ cTouchThread::CalibrationSetting settings;
+ };
+
+ struct MenuItem
+ {
+ string text;
+ string tabs[MaxTabs];
+ int tabCount;
+ int nextX;
+ Ts::MenuItemType type;
+ cEventCopy event;
+ const cChannel* channel;
+ const cRecording* recording;
+ string recordingName;
+ };
+
+ struct MenuInfo
+ {
+ DisplayMode lastMode;
+ int currentRow;
+ int currentRowLast;
+ int visibleRows;
+ int topRow;
+ int lineHeight;
+ int lineHeightSelected;
+ string title;
+ string current;
+ string buttons[4];
+ string text;
+ int drawingRow; // current drawing index
+ vector<MenuItem> items;
+ int charInTabs[MaxTabs+1];
+ };
+
+ // display contents
+
+ cThemeSection* currentSection;
+ cThemeSection* lastSection;
+ ChannelType channelType;
+ DisplayMode _mode;
+ string _sectionName;
+
+ int _eventsReady;
+ bool _mute;
+ int _volume;
+
+ int _channel;
+ const cChannel* _presentChannel;
+ string _channelGroup;
+ cEventCopy _presentEvent;
+ cEventCopy _followingEvent;
+ cEventCopy _event;
+ string _coverPath;
+
+ string _recording;
+ ReplayInfo _replay;
+
+ string _message;
+ TimerList _timers; // the timers
+
+ MenuInfo _menu;
+ MusicPlayerInfo _music; // special for Music Plugin
+ RdsInfo _rds; // special for Radio Plugin
+ Calibration calibration;
+ int mouseX;
+ int mouseY;
+ int mouseKey;
+ int touchMenu;
+ uint64_t touchMenuHideAt;
+ uint64_t touchMenuHideTime;
+
+ //
+
+ int displayActive;
+ int snapshotPending;
+ int fdInotify;
+
+ protected:
+
+ // thread
+
+ virtual void Action();
+
+ // helper functions
+
+ int wait(uint64_t updateIn);
+ int meanwhile();
+ void takeSnapshot();
+ void updateProgram();
+ void updateTimers();
+ void updateChannel();
+ void finalizeItemList();
+
+ const char* getTabbedText(const char* s, int Tab);
+
+ // status interface
+
+ virtual void ChannelSwitch(const cDevice* Device, int ChannelNumber, bool LiveView);
+ virtual void OsdSetEvent(const cEvent* event);
+ virtual void OsdSetRecording(const cRecording* recording);
+ virtual void OsdProgramme(time_t PresentTime, const char* PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+ virtual void OsdChannel(const char* text);
+
+ virtual void SetVolume(int Volume, bool Absolute);
+ virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On);
+ virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+ virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
+ virtual void OsdStatusMessage(const char *Message);
+ virtual void OsdTitle(const char* Title);
+ virtual void OsdItem(const char* Text, int Index);
+ virtual void OsdEventItem(const cEvent* Event, const char* Text, int Index, int Count);
+ virtual void OsdCurrentItem(const char *Text);
+ virtual void OsdClear();
+ virtual void OsdHelpKeys(const char* Red, const char* Green, const char* Yellow, const char* Blue);
+ virtual void OsdTextItem(const char* Text, bool Scroll);
+ virtual void OsdMenuDestroy();
+
+#if defined _OLD_PATCH
+ virtual void OsdMenuDisplay(const char* kind);
+#else
+ virtual void OsdMenuDisplay(eMenuCategory category);
+#endif
+
+ private:
+
+ // renderer
+
+ Renderer* renderer;
+ ComThread* comThread;
+ cTouchThread* touchThread;
+
+ // thread control
+
+ bool _active;
+ cMutex _mutex;
+ int needLock;
+ cCondVar _doUpdate;
+ int wakeup;
+ char* userDumpFile;
+ int userDumpWidth;
+ int userDumpHeight;
+ int triggerTimerUpdate;
+ int triggerChannelUpdate;
+ int triggerFinalizeItemList;
+
+ //
+
+ int forceNextDraw;
+ int startedAt;
+};
+
+//***************************************************************************
+#endif // __GTFT_DISPLAY_H
diff --git a/dspitems.c b/dspitems.c
new file mode 100644
index 0000000..29c4b95
--- /dev/null
+++ b/dspitems.c
@@ -0,0 +1,2614 @@
+/*
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * dspitems.c
+ *
+ * (c) 2007-2015 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <sstream>
+
+#include <sysinfo.h>
+#include <theme.h>
+#include <display.h>
+#include <scan.h>
+#include <span.h>
+#include <setup.h>
+
+#include <libexif/exif-data.h>
+
+const int maxGranularity = 100;
+
+//***************************************************************************
+// Init Statics
+//***************************************************************************
+
+Renderer* cDisplayItem::render = 0;
+cGraphTFTDisplay* cDisplayItem::vdrStatus = 0;
+int cDisplayItem::forceDraw = yes;
+uint64_t cDisplayItem::nextForce = 0;
+cDisplayItem* cDisplayItem::selectedItem = 0;
+
+//***************************************************************************
+// cDisplayItem
+//***************************************************************************
+
+void cDisplayItem::scheduleForce(uint64_t aTime)
+{
+ if (!nextForce || nextForce > aTime)
+ {
+ nextForce = aTime;
+
+ tell(1, "schedule force in (%ldms)",
+ nextForce - msNow());
+ }
+}
+
+void cDisplayItem::scheduleDrawAt(uint64_t aTime)
+{
+ // if (aTime < nextDraw || nextDraw < msNow())
+ {
+ nextDraw = aTime;
+
+ tell(2, "schedule next draw of '%s'[%s] in (%ldms)",
+ nameOf(), Debug().c_str(),
+ nextDraw - msNow());
+ }
+}
+
+void cDisplayItem::scheduleDrawIn(int aTime)
+{
+ uint64_t at = aTime + msNow();
+
+ // the maximal redraw granularity is 500ms (maxGranularity), due to this
+ // adjust to next full step
+
+ at = round((double)((double)at / maxGranularity)) * maxGranularity;
+
+ scheduleDrawAt(at);
+}
+
+void cDisplayItem::scheduleDrawNextFullMinute()
+{
+ uint64_t ms = SECONDS(((time(0)/60 +1) * 60) - time(0));
+
+ scheduleDrawAt(ms+msNow());
+}
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cDisplayItem::cDisplayItem()
+ : cThemeItem()
+{
+ changed = no;
+ nextDraw = 0;
+ section = 0;
+ marquee_active = no;
+ backgroundItem = 0;
+ visible = yes;
+ nextAnimationAt = msNow();
+ actLineCount = na;
+ lastConditionState = true;
+
+ lastX = 0;
+ lastY = 0;
+ lastWidth = 0;
+ lastHeight = 0;
+}
+
+cDisplayItem::~cDisplayItem()
+{
+}
+
+//***************************************************************************
+// Evaluate Color
+//***************************************************************************
+
+p_rgba cDisplayItem::evaluateColor(const char* var, p_rgba rgba)
+{
+ string p = "";
+
+ if (evaluate(p, var) != success)
+ memset(rgba, 255, sizeof(t_rgba)); // fallback white
+ else
+ str2rgba(p.c_str(), rgba);
+
+ tell(3, "evaluated color '%s' to %d/%d/%d/%d (%s)",
+ var, rgba[0], rgba[1], rgba[2], rgba[3], p.c_str());
+
+ return rgba;
+}
+
+//***************************************************************************
+// Evaluate Path
+//***************************************************************************
+
+string cDisplayItem::evaluatePath()
+{
+ string p = "";
+
+ // iterate over path
+
+ for (int i = 0; i < pathCount; i++)
+ {
+ if (evaluate(p, pathList[i].configured.c_str()) != success)
+ continue;
+
+ tell(5, "check path '%s'", p.c_str());
+
+ if (p == "")
+ {
+ tell(1, "path '%s' empty, skipping",
+ pathList[i].configured.c_str());
+
+ continue;
+ }
+
+ // append plugin config path
+
+ if (p[0] != '/')
+ {
+ p = string(GraphTFTSetup.themesPath)
+ + string(Thms::theTheme->getDir())
+ + "/" + p;
+
+ tell(4, "%d path [%s] converted to '%s'", i,
+ pathList[i].configured.c_str(), p.c_str());
+ }
+
+ if (fileExists(p.c_str()))
+ return p;
+
+ // time for next image?
+ // -> else we don't need to check for range
+
+ if (nextAnimationAt > msNow())
+ return pathList[i].last;
+
+ // check for range
+
+ unsigned int s = p.find_last_of('(');
+ unsigned int e = p.find_last_of(')');
+ int rangeSize = e-s-1;
+ int cur;
+
+ if (s != string::npos && e != string::npos && rangeSize >= 3)
+ {
+ Scan scan(p.substr(s+1, rangeSize).c_str(), no);
+ int minNum, maxNum;
+ string path = "";
+
+ scan.eat();
+
+ if (!scan.isNum())
+ continue; // range parsing error
+
+ minNum = scan.lastInt();
+ scan.eat();
+
+ if (!scan.isOperator() || *scan.lastIdent() != '-')
+ continue; // range parsing error
+
+ scan.eat();
+
+ if (!scan.isNum())
+ continue; // range parsing error
+
+ maxNum = scan.lastInt();
+
+ if (scan.eat() == success)
+ continue; // range parsing error (waste behind second int)
+
+ // get actual number
+
+ cur = pathList[i].curNum;
+
+ // reset ...
+
+ if (cur < minNum)
+ {
+ pathList[i].curNum = minNum;
+ cur = minNum-1;
+ }
+
+ do
+ {
+ if (++cur > maxNum)
+ cur = minNum;
+
+ path = p.substr(0, s) + Str::toStr(cur) + p.substr(e+1);
+ tell(2, "Checking for '%s'", path.c_str());
+
+ } while (!fileExists(path.c_str()) && cur != pathList[i].curNum);
+
+ if (fileExists(path.c_str()))
+ {
+ tell(4, "Animated image '%s'", path.c_str());
+ pathList[i].last = path;
+ pathList[i].curNum = cur;
+ nextAnimationAt = msNow() + _delay;
+
+ return pathList[i].last;
+ }
+ }
+ }
+
+ tell(2, "Image for '%s' with %d elements not found :(", _path.c_str(), pathCount);
+
+ return "";
+}
+
+//***************************************************************************
+// Replay Mode Value
+//***************************************************************************
+
+int cDisplayItem::replayModeValue(ReplayMode rm)
+{
+ bool play, forward;
+ int speed;
+
+ if (!vdrStatus->_replay.control
+ || !vdrStatus->_replay.control->GetReplayMode(play, forward, speed))
+ return -1;
+
+ switch (rm)
+ {
+ case rmSpeed: return speed;
+ case rmForward: return forward;
+ case rmPlay: return play;
+ default: return na;
+ }
+
+ return na;
+}
+
+//***************************************************************************
+// Evaluate Condition
+//***************************************************************************
+
+int cDisplayItem::evaluateCondition(int recurse)
+{
+ static Scan* scan = 0;
+
+ int result;
+ int state;
+
+ int rightType = catUnknown;
+ int leftType = catUnknown;
+ int leftInt = 0;
+ int rightInt = 0;
+ string leftStr = "";
+ string rightStr = "";
+
+ char op[100]; *op = 0;
+ char logicalOp[100]; *logicalOp = 0;
+ string expression;
+
+ if (!recurse || !scan)
+ {
+ if (_condition.size() <= 0)
+ return yes;
+
+ // beim Fehler erst mal 'no' ... ?
+
+ if (evaluate(expression, _condition.c_str()) != success)
+ return no;
+
+ tell(3, "evaluating condition '%s' with expression '%s'",
+ _condition.c_str(), expression.c_str());
+
+ // ...
+
+ if (scan) delete scan;
+ scan = new Scan(expression.c_str());
+ }
+
+ // left expression
+
+ scan->eat();
+
+ if (scan->isNum())
+ {
+ leftInt = scan->lastInt();
+ leftType = catInteger;
+ }
+ else if (scan->isString())
+ {
+ leftStr = scan->lastString();
+ leftType = catString;
+ }
+ else
+ {
+ tell(0, "Error: Invalid left '%s' expression in '%s'",
+ scan->lastIdent(), expression.c_str());
+ return no;
+ }
+
+ // operator ?
+
+ if ((state = scan->eat()) == success && scan->isOperator() && !scan->isLogical())
+ {
+ strcpy(op, scan->lastIdent());
+
+ // right expression
+
+ scan->eat();
+
+ if (scan->isNum())
+ {
+ rightInt = scan->lastInt();
+ rightType = catInteger;
+ }
+ else if (scan->isString())
+ {
+ rightStr = scan->lastString();
+ rightType = catString;
+ }
+ else
+ {
+ tell(0, "Error: Invalid right '%s' expression in '%s'",
+ scan->lastIdent(), expression.c_str());
+ return no;
+ }
+
+ // check the condition
+
+ if (leftType != rightType)
+ {
+ tell(0, "Error: Argument types of left and right "
+ "agrument don't match in (%d/%d) '%s'",
+ leftType, rightType, expression.c_str());
+ return no;
+ }
+
+ if (leftType == catInteger)
+ result = condition(leftInt, rightInt, op);
+ else
+ result = condition(&leftStr, &rightStr, op);
+
+ state = scan->eat();
+ }
+ else if (leftType == catInteger)
+ {
+ result = leftInt ? true : false;
+ }
+ else
+ {
+ result = leftStr != "" ? true : false;
+ }
+
+ // any more expressions in here?
+
+ tell(4, "check for further condition at '%s'", Str::notNull(scan->next()));
+
+ if (state == success)
+ {
+ tell(4, "further condition found");
+
+ if (!scan->isLogical())
+ {
+ tell(0, "Error: Invalid logical operator '%s' expression in '%s'",
+ scan->lastIdent(), expression.c_str());
+ return no;
+ }
+
+ strcpy(logicalOp, scan->lastIdent());
+
+ // start a recursion ...
+
+ if (strncmp(logicalOp, "&", 1) == 0)
+ result = result && evaluateCondition(yes);
+ else if (strncmp(logicalOp, "|", 1) == 0)
+ result = result || evaluateCondition(yes);
+ }
+
+ tell(3, "condition is '%s'; evaluated condition is '%s'; result is '%s'",
+ _condition.c_str(), expression.c_str(), result ? "match" : "don't match");
+
+ return result;
+}
+
+//***************************************************************************
+// evaluate the condition
+//***************************************************************************
+
+int cDisplayItem::condition(int left, int right, const char* op)
+{
+ tell(4, "evaluate condition '%d' '%s' '%d'", left, op, right);
+
+ if (strcmp(op, ">") == 0)
+ return left > right;
+
+ if (strcmp(op, "<") == 0)
+ return left < right;
+
+ if (strcmp(op, ">=") == 0)
+ return left >= right;
+
+ if (strcmp(op, "<=") == 0)
+ return left <= right;
+
+ if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0)
+ return left == right;
+
+ if (strcmp(op, "!=") == 0 || strcmp(op, "<>") == 0)
+ return left != right;
+
+ tell(0, "Unexpected operator '%s'", op);
+
+ return no;
+}
+
+int cDisplayItem::condition(string* left, string* right, const char* op)
+{
+ tell(4, "evaluate condition '%s' '%s' '%s'",
+ left->c_str(), op, right->c_str());
+
+ if (strcmp(op, ">") == 0)
+ return *left > *right;
+
+ if (strcmp(op, "<") == 0)
+ return *left < *right;
+
+ if (strcmp(op, ">=") == 0)
+ return *left >= *right;
+
+ if (strcmp(op, "<=") == 0)
+ return *left <= *right;
+
+ if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0)
+ return *left == *right;
+
+ if (strcmp(op, "!=") == 0 || strcmp(op, "<>") == 0)
+ return *left != *right;
+
+ tell(0, "Unexpected operator '%s'", op);
+
+ return no;
+}
+
+//***************************************************************************
+// Interface
+//***************************************************************************
+
+int cDisplayItem::reset()
+{
+ if (_scroll)
+ {
+ marquee_active = yes;
+ marquee_left = no;
+ marquee_idx = na;
+ marquee_count = 0;
+ marquee_strip = 0;
+ // scheduleDrawIn(0);
+ }
+
+ nextAnimationAt = msNow();
+ lastWidth = 0;
+
+ return done;
+}
+
+int cDisplayItem::draw()
+{
+ int status = success;
+ int cond = true;
+
+ // check condition
+
+ if (!isOfGroup(groupMenu) && !isOfGroup(groupTextList))
+ {
+ cond = evaluateCondition();
+
+ if (!cond)
+ {
+ tell(4, "Ignore drawing of '%s' due to condition '%s'",
+ nameOf(), _condition.c_str());
+
+ status = ignore;
+ }
+ }
+
+ // schedule due to the configured delay ..
+
+ if (cond && _delay > 0 && visible && msNow() > nextDraw && !_scroll)
+ scheduleDrawIn(_delay);
+
+ // condition state changed force immediate redraw !
+
+ if (lastConditionState != cond)
+ {
+ tell(4, "Condition '%s' of '%s' [%s] changed from (%d) to (%d), force draw",
+ _condition.c_str(), nameOf(),
+ Text() != "" ? Text().c_str() : Path().c_str(),
+ lastConditionState, cond);
+
+ lastConditionState = cond;
+ scheduleForce(msNow() + 10);
+ }
+
+ return status;
+}
+
+int cDisplayItem::refresh()
+{
+ tell(6, "timeMs::Now() (%ldms);", msNow());
+ tell(6, "nextForce at (%ldms)", nextForce);
+ tell(6, "forceDraw '%s', nextDraw (%ldms), isForegroundItem(%d), Foreground(%d)",
+ forceDraw ? "yes" : "no", nextDraw, isForegroundItem(), Foreground());
+
+ changed = no;
+
+ // LogDuration ld("cDisplayItem::refresh()");
+
+ // force required ? (volume, animating, osd-message, ...)
+
+ if (nextForce && msNow() >= nextForce)
+ {
+ forceDraw = yes;
+ nextForce = 0;
+ }
+
+ // respect the maximal redraw granularity
+
+ if ((nextDraw && (msNow() >= nextDraw-(maxGranularity/2-1)))
+ || isForegroundItem() || forceDraw || Foreground())
+ {
+ nextDraw = 0;
+ int res = draw() == success ? 1 : 0;
+
+ changed = res > 0;
+
+ tell(2, "draw '%s', %s", nameOf(), res ? "done" : "skipped due to condition");
+
+ if (res > 0 && logLevel >= 3)
+ {
+ if (isForegroundItem() || forceDraw || Foreground())
+ {
+ tell(3, "forceDraw(%d), isForegroundItem(%d), Foreground(%d)",
+ forceDraw, isForegroundItem(), Foreground());
+ tell(3, "'%s' - '%s'", nameOf(),
+ Text().size() ? Text().c_str() : Path().c_str());
+ }
+ }
+
+ return res;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Painters
+//***************************************************************************
+
+int cDisplayItem::drawText(const char* text, int y,
+ int height, int clear, int skipLines)
+{
+ int width;
+ unsigned int viewLength = clen(text);
+ unsigned int textLen = clen(text); // character count (real chars)
+ int lineHeight = 0;
+
+ y = y ? y : Y();
+ width = Width() ? Width() : Thms::theTheme->getWidth() - X();
+ height = height != na? height : Height();
+ height = height != na ? height : Thms::theTheme->getHeight() - y;
+ skipLines = skipLines ? skipLines : StartLine();
+
+ if (!height)
+ return done;
+
+ // draw background
+
+ if (clear)
+ drawBackRect(y, _bg_height ? _bg_height : height);
+
+ if (!textLen || Str::isEmpty(text))
+ return done;
+
+ // text width in pixel
+
+ int textWidth = render->textWidthOf(text, _font.c_str(), _size, lineHeight);
+ lineHeight = !lineHeight ? 1 :lineHeight;
+
+ // respect max height for line count
+
+ int lines = height / lineHeight > 0 ? height / lineHeight : 1;
+
+ if (_lines > 0)
+ lines = min(lines, _lines);
+
+ int visibleWidth = width*lines;
+
+ if (textWidth <= 0)
+ {
+ textWidth = 22 * textLen;
+
+ tell(1, "Info: Can't detect text with of '%s'[%s](%d) witch font %s/%d. Assuming %dpx",
+ text, _debug.c_str(), textLen, _font.c_str(), _size, textWidth);
+ }
+
+ int charWidth = textWidth / textLen > 0 ? textWidth / textLen : 1; // at least one pixel :p
+ viewLength = visibleWidth / charWidth; // calc max visible chars
+
+ tell(4, "[%s] drawing text '%s' at %d/%d (%d/%d), '%s'(%d) lines (%d)!",
+ _debug.c_str(), text, X(), y, width, height, _font.c_str(), _size, lines);
+
+ tell(3, "[%s] textLen %d, viewLength = %d, textWidth = %dpx, visibleWidth = %dpx, lines = %d, font %s/%d",
+ _debug.c_str(), textLen, viewLength, textWidth, visibleWidth, lines, _font.c_str(), _size);
+
+ tell(5, "[%s] scroll is (%d) and marquee_active is (%d) for text '%s'",
+ _debug.c_str(), _scroll, marquee_active, text);
+
+ // get line count of the actual text
+
+ actLineCount = render->lineCount(text, _font.c_str(), _size, width);
+
+ // ...
+
+ // exclude item fom scrolling if more than one line displayed,
+ // multiline srcolle is prepared but dont work cause of the word warp featute in ImlibRenderer::text(...)
+ // -> to hard to calculate this here ...
+
+ if (!_scroll || textWidth < visibleWidth || lines > 1)
+ {
+ t_rgba rgba;
+
+ // normal and 'dots' mode
+
+ render->text(text,
+ _font.c_str(), _size, _align,
+ X(), y,
+ evaluateColor(_color.c_str(), rgba),
+ width, height, lines,
+ _dots, skipLines);
+ }
+
+ else
+ {
+ // marquee and ticker mode
+
+ // viewLength -= 3;
+
+ if (msNow() > nextAnimationAt)
+ {
+ if (_scroll == 1 && marquee_idx + viewLength > textLen)
+ marquee_left = yes;
+
+ else if (_scroll == 2 && marquee_idx + viewLength > textLen)
+ marquee_idx = na;
+
+ if (marquee_left)
+ marquee_idx--;
+ else
+ marquee_idx++;
+
+ if (marquee_idx == 0)
+ {
+ marquee_left = no;
+ marquee_count++;
+ }
+
+ if (_scroll_count && marquee_count > _scroll_count)
+ {
+ marquee_active = no;
+ marquee_idx = 0;
+ }
+
+ if (marquee_active)
+ {
+ if (_delay < 200)
+ _delay = 200;
+
+ if (marquee_idx == 0 || marquee_idx > (int)(textLen - viewLength))
+ scheduleDrawIn(_delay*3);
+ else
+ scheduleDrawIn(_delay);
+
+ nextAnimationAt = nextDraw;
+ }
+ }
+
+ int blen = strlen(text);
+ int cs, ps;
+ int i = 0;
+ t_rgba rgba;
+
+ for (ps = 0; ps < blen; ps += cs)
+ {
+ i++;
+ cs = max(mblen(&text[ps], blen-ps), 1);
+
+ if (i >= marquee_idx)
+ break;
+ }
+
+ tell(3, "drawing text in scroll mode '%s' (%d/%d), nextDraw is %ld; idx is %d/%d",
+ text, textLen, viewLength, nextDraw/1000, marquee_idx, ps);
+
+ render->text(text + ps,
+ _font.c_str(), _size, _align,
+ X(), y,
+ evaluateColor(_color.c_str(), rgba),
+ width, height,
+ lines, marquee_active ? no : _dots);
+ }
+
+ return done;
+}
+
+int cDisplayItem::drawRectangle()
+{
+ t_rgba rgba;
+
+ render->rectangle(X(), Y(), Width(), Height(),
+ evaluateColor(_color.c_str(), rgba));
+
+ return done;
+}
+
+int cDisplayItem::drawBackRect(int y, int height)
+{
+ int x = _bg_x != na ? _bg_x : X();
+ int width = _bg_width > 0 ? _bg_width : Width();
+
+ y = y ? y : _bg_y != na ? _bg_y : Y();
+
+ if (!height)
+ height = _bg_height > 0 ? _bg_height : Height();
+
+ if (!Overlay())
+ {
+ t_rgba bg_rgba;
+
+ evaluateColor(_bg_color.c_str(), bg_rgba);
+
+ if (haveBackgroundItem())
+ {
+ string p;
+
+ // fill with part of the backround image
+
+ evaluate(p, backgroundItem->Path().c_str());
+
+ tell(3, "Drawing backround area of '%s' for '%s'",
+ p.c_str(), _text.c_str());
+
+ render->imagePart(p.c_str(), x, y, width, height);
+ }
+
+ if (bg_rgba[rgbA])
+ {
+ // fill with solid color, respect alpha channel
+
+ render->rectangle(x, y, width, height, bg_rgba);
+ }
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Get Jpeg Orientation
+//***************************************************************************
+
+int getJpegOrientation(const char* file)
+{
+ int orientation = 1; // 1 => 'normal'
+ ExifData* exifData = exif_data_new_from_file(file);
+
+ if (exifData)
+ {
+ ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
+ ExifEntry* exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
+
+ if (exifEntry)
+ orientation = exif_get_short(exifEntry->data, byteOrder);
+
+ exif_data_free(exifData);
+ }
+
+ return orientation;
+}
+
+int cDisplayItem::drawImage(const char* path, int fit, int aspectRatio, int noBack)
+{
+ int orientation = 1; // 1 => 'normal'
+
+ if (!path) path = _path.c_str();
+ if (fit == na) fit = _fit;
+ if (aspectRatio == na) aspectRatio = _aspect_ratio;
+
+ tell(3, "drawing image '%s' at %d/%d (%d/%d); fit = %d; aspectRatio = %d)",
+ path, X(), Y(), Width(), Height(), _fit, aspectRatio);
+
+ if (BgWidth() && !noBack)
+ drawBackRect();
+
+ if (strcasestr(path, "JPEG") || strcasestr(path, "JPG"))
+ orientation = getJpegOrientation(path);
+
+ render->image(path,
+ X(), Y(),
+ Width(), Height(),
+ fit, aspectRatio, orientation);
+
+ return done;
+}
+
+//***************************************************************************
+// Format String
+//***************************************************************************
+
+const char* cDisplayItem::formatString(const char* str, const char* fmt,
+ char* buffer, int len)
+{
+ if (Str::isEmpty(fmt) || Str::isEmpty(str))
+ return str;
+
+ sprintf(buffer, "%.*s", len, str);
+
+ if (strcasecmp(fmt, "upper") == 0)
+ Str::toCase(Str::cUpper, buffer);
+ if (strcasecmp(fmt, "lower") == 0)
+ Str::toCase(Str::cLower, buffer);
+
+ return buffer;
+}
+
+//***************************************************************************
+// Format Date Time
+//***************************************************************************
+
+const char* cDisplayItem::formatDateTime(time_t theTime, const char* fmt,
+ char* date, int len, int absolut)
+{
+ struct tm tim = {0};
+ tm* tmp;
+ int res;
+
+ *date = 0;
+
+ string format = fmt && *fmt ? fmt :
+ (_format.length() ? _format : "%a %d.%m %H:%M");
+
+ // %s seems to be absolut as default ...
+
+ if (absolut && format.find("%s") == string::npos)
+ {
+ localtime_r(&theTime, &tim);
+ theTime += timezone;
+ }
+
+ tmp = localtime_r(&theTime, &tim);
+
+ if (!tmp)
+ {
+ tell(0, "Error: Can't get localtime!");
+ return 0;
+ }
+
+ res = strftime(date, len, format.c_str(), tmp);
+
+ if (!res)
+ {
+ tell(0, "Error: Can't convert time, maybe "
+ "invalid format string '%s'!", format.c_str());
+
+ return 0;
+ }
+
+ if (format.find("%s") != string::npos
+ || format.find("%S") != string::npos
+ || format.find("%T") != string::npos)
+ {
+ // refresh in 1 second
+
+ if (!_delay)
+ scheduleDrawIn(1000);
+ }
+ else
+ {
+ // refresh at next full minute
+
+ scheduleDrawNextFullMinute();
+ }
+
+ return date;
+}
+
+//***************************************************************************
+// Draw Image on Background Coordinates
+//***************************************************************************
+
+int cDisplayItem::drawImageOnBack(const char* path, int fit, int height)
+{
+ if (!path)
+ return done;
+
+ int x = _bg_x != na ? _bg_x : X();
+ int width = _bg_width > 0 ? _bg_width : Width();
+ int y = _bg_y != na ? _bg_y : Y();
+
+ if (height == na)
+ height = _bg_height > 0 ? _bg_height : Height();
+
+ tell(0, "drawing image '%s' at %d/%d (%d/%d)", path, x, y, width, height);
+
+ render->image(path, x, y, width, height, fit);
+
+ return done;
+}
+
+int cDisplayItem::drawProgressBar(double current, double total,
+ string path, int y, int height,
+ int withFrame, int clear)
+{
+ t_rgba rgba;
+ int xDone;
+ char tmp[50];
+
+ int bgX = _bg_x != na ? _bg_x : X();
+ int bgWidth = _bg_width ? _bg_width : Width();
+
+ int bgY = y != na ? y : _bg_y != na ? _bg_y : Y();
+ int bgHeight = height != na ? height : _bg_height ? _bg_height : Height();
+
+ int red, green, blue, alpha;
+
+ rgba2int(str2rgba(_color.c_str(), rgba), red, green, blue, alpha);
+
+ current = current < 0 ? 0 : current ;
+ bgHeight = bgHeight ? bgHeight : Height();
+ height = height == na ? Height() : height;
+ y = y == na ? Y() : y;
+
+ if (!total) total = 1;
+ xDone = (int)((current/total) * (float)Width());
+
+ tell(4, "bar, %f/%f xDone=%d", current, total, xDone);
+
+ // background
+
+ if (clear)
+ drawBackRect(y, height);
+
+ if (_bg_x && withFrame)
+ render->rectangle(bgX, bgY,
+ bgWidth, bgHeight,
+ evaluateColor(_bg_color.c_str(), rgba));
+
+ if (path != "")
+ render->image(path.c_str(),
+ X(), y,
+ Width(), height,
+ true);
+ else // if (_bg_x <= 0)
+ render->rectangle(X(), y, Width(), height,
+ evaluateColor(_bg_color.c_str(), rgba));
+
+ // foreground
+
+ if (path != "")
+ {
+ // with image
+
+ render->rectangle(X() + xDone, y,
+ Width() - xDone, height,
+ evaluateColor(_bg_color.c_str(), rgba));
+ }
+ else
+ {
+ // without image
+
+ if (_switch)
+ {
+ // colorchanging bar
+
+ red = green = 255;
+ blue = 0;
+
+ double percent = current / total;
+
+ if (percent < 0.5f)
+ red = (int)(255.0f * percent * 2.0f);
+ else
+ green = (int)(255.0f * (1-percent) * 2.0f);
+ }
+
+ render->rectangle(X(), y, xDone, height,
+ int2rgba(red, green, blue, alpha, rgba));
+ }
+
+ // draw optional text
+
+ if (_text == "percent")
+ {
+ int textHeight = _size * 5/3;
+ t_rgba rgba;
+ sprintf(tmp, "%3.0f%%", current / (total/100));
+
+ render->text(tmp,
+ _font.c_str(), _size, _align,
+ X(),
+ y + (height-textHeight)/2,
+ int2rgba(255-red, 255-green, 255-blue, 0, rgba),
+ bgWidth, height, 1, no);
+ }
+ else if (_text == "value")
+ {
+ int textHeight = _size * 5/3;
+ t_rgba rgba;
+
+ sprintf(tmp, "%d%s / %d%s",
+ (int)current, _unit.c_str(),
+ (int)total, _unit.c_str());
+
+ render->text(tmp,
+ _font.c_str(), _size, _align,
+ X(),
+ y + (height-textHeight)/2,
+ int2rgba(255-red, 255-green, 255-blue, 0, rgba),
+ bgWidth, height, 1, no);
+ }
+
+ return success;
+}
+
+int cDisplayItem::drawPartingLine(string text, int y, int height)
+{
+ string p;
+ int barHeight = BarHeight();
+
+ if (BarHeightUnit() == iuPercent) // BarHeight in % of row
+ barHeight = (int)(((double)height/100) * (double)BarHeight());
+
+ int offset = (height - barHeight) / 2;
+
+ tell(5, "drawing parting line at %d/%d (%d/%d)",
+ X(), y+offset, Width(), barHeight);
+
+ if (_path != "" && text != "")
+ {
+ evaluate(p, _path.c_str());
+ render->image(p.c_str(),
+ X(), y+offset,
+ Width(), barHeight,
+ Fit(), AspectRatio());
+ }
+
+ if (_path2 != "" && text == "")
+ {
+ evaluate(p, _path2.c_str());
+ render->image(p.c_str(),
+ X(), y+offset,
+ Width(), barHeight,
+ Fit(), AspectRatio());
+ }
+
+ if (text != "")
+ {
+ t_rgba rgba;
+ int alignOff = 0;
+ int textOff = 0;
+
+ if (AlignV())
+ alignOff = (barHeight-(_size * 5/3))/2;
+
+ if (strncmp(text.c_str(), "->", 2) == 0)
+ textOff = 2;
+
+ render->text(text.c_str() + textOff,
+ _font.c_str(), _size, _align,
+ X(), y + offset + alignOff,
+ evaluateColor(_color.c_str(), rgba),
+ Width(), barHeight, 1);
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Item Classes
+//***************************************************************************
+
+int cDisplayText::draw()
+{
+ string p;
+
+ evaluate(p, _text.c_str());
+
+ int vLines = Height() / (_size * 5/3);
+ int width = Width() ? Width() : Thms::theTheme->getWidth() - X();
+
+ // trim
+
+ p = trim(p);
+
+ // user scrolling
+
+ if (p != lastText || StartLine() < 0)
+ {
+ lastText = p;
+ setStartLine(0);
+ }
+
+ if (StartLine() >= lineCount() - vLines)
+ setStartLine(lineCount() - vLines);
+
+ // first ... calc text width and real needed height
+
+ int lineHeight = 0;
+ lastHeight = 0;
+ lastWidth = render->textWidthOf(p.c_str(), _font.c_str(), _size, lineHeight);
+ lastY = Y();
+ lastX = X();
+
+ int neededLines = 0;
+
+ if (lastWidth && p.length())
+ {
+ neededLines = render->lineCount(p.c_str(), _font.c_str(), _size, width);
+ lastHeight = neededLines * lineHeight;
+ lastHeight = min(lastHeight, Height()); // respect configured max height
+ }
+
+ tell(3, "Dimension of '%s' %d/%d now %d/%d; position is %d/%d, need %d lines [%d,%d,%d]",
+ _id.c_str(), Width(), Height(),
+ lastWidth, lastHeight,
+ X(), Y(), neededLines,
+ lastWidth, width, lineHeight);
+
+ // second ... check condition
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ // third ... draw text
+
+ return drawText(p.c_str(), lastY, lastHeight);
+}
+
+int cDisplayRectangle::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ return drawRectangle();
+}
+
+int cDisplayImage::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ string path = evaluatePath().c_str();
+
+ tell(3, "Looking for file '%s'", path.c_str());
+
+ if (!Str::isEmpty(path.c_str()))
+ {
+ if (_path2 != "")
+ {
+ string p;
+ evaluate(p, _path2.c_str());
+ drawImageOnBack(p.c_str(), _fit);
+ }
+
+ return drawImage(path.c_str(), na, na, _path2 != "");
+ }
+
+ drawBackRect();
+
+ return done;
+}
+
+int cDisplayImageFile::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ FILE* fp;
+ char line[1000+TB]; *line = 0;
+ char* c;
+
+ fp = fopen(_path.c_str(), "r");
+
+ if (fp)
+ {
+ c = fgets(line, 1000, fp);
+
+ if (c) line[strlen(line)] = 0;
+ Str::allTrim(line);
+
+ fclose(fp);
+ }
+
+ if (!_fit || _aspect_ratio)
+ drawBackRect();
+
+ // info
+
+ tell(5, "Looking for file '%s'", line);
+
+ if (!Str::isEmpty(line) && fileExists(line))
+ {
+ drawImage(line);
+ }
+ else
+ {
+ tell(4, "Info: Image '%s' not found, falling back to '%s'",
+ line, _path2.c_str());
+
+ drawImage(_path2.c_str());
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// cDisplayImageDir
+//***************************************************************************
+
+int cDisplayImageDir::init()
+{
+ if (cDisplayItem::init() != success)
+ return fail;
+
+ images.clear();
+
+ scanDir(_path.c_str());
+
+ tell(0, "Info: Added %ld images of path '%s'", images.size(), _path.c_str());
+ current = images.end();
+
+ return success;
+}
+
+int cDisplayImageDir::scanDir(const char* path, int level)
+{
+ const char* extensions = "jpeg:jpg";
+ const char* ext;
+ DIR* dir;
+ struct dirent *entry, *res;
+ int nameMax, direntSize;
+
+ // calculate dirent size
+
+ nameMax = pathconf(path, _PC_NAME_MAX);
+ nameMax = nameMax > 0 ? nameMax : 256;
+ direntSize = offsetof(struct dirent, d_name) + nameMax + TB;
+
+ // open directory
+
+ if (!(dir = opendir(path)))
+ {
+ tell(1, "Can't open directory '%s', '%s'", path, strerror(errno));
+ return done;
+ }
+
+ entry = (struct dirent*)malloc(direntSize);
+
+ // iterate ..
+
+ tell(0, "Info: Scanning %sdirectory '%s' for images", level ? "sub-" : "", path);
+
+ while (readdir_r(dir, entry, &res) == 0 && res)
+ {
+ ImageFile f;
+
+ if (entry->d_type == DT_DIR)
+ {
+ char* subPath;
+
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ asprintf(&subPath, "%s/%s", path, entry->d_name);
+ scanDir(subPath, level+1);
+
+ free(subPath);
+ }
+
+ // check extension
+
+ if ((ext = strrchr(res->d_name, '.')))
+ ext++;
+
+ if (Str::isEmpty(ext))
+ {
+ tell(0, "skipping file '%s' without extension", res->d_name);
+ continue;
+ }
+
+ if (!strcasestr(extensions, ext))
+ {
+ tell(0, "skipping file '%s' with extension '%s'", res->d_name, ext);
+ continue;
+ }
+
+ // fill image infos
+
+ f.path = path + string("/") + res->d_name;
+ f.initialized = no;
+ f.orientation = 1; // 1 => 'normal'
+ f.landscape = yes;
+ f.width = 0;
+ f.height = 0;
+
+ images.push_back(f);
+
+ tell(3, "Info: Added '%s'", f.path.c_str());
+ }
+
+ free(entry);
+ closedir(dir);
+
+ return success;
+}
+
+int cDisplayImageDir::getNext(std::vector<ImageFile>::iterator& it, ImageFile*& file)
+{
+ if (it != images.end())
+ it++;
+
+ if (it == images.end())
+ it = images.begin();
+
+ if (it == images.end())
+ return fail;
+
+ file = &(*it);
+
+ if (!file->initialized)
+ {
+ file->orientation = getJpegOrientation(file->path.c_str());
+ jpegDimensions(file->path.c_str(), file->width, file->height);
+
+ file->landscape = (file->orientation < 5 && file->width < file->height) || (file->orientation >= 5 && file->width > file->height) ? no : yes;
+ }
+
+ return success;
+}
+
+int cDisplayImageDir::draw()
+{
+ ImageFile* file;
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (!_fit || _aspect_ratio)
+ drawBackRect();
+
+ if (getNext(current, file) != success)
+ return done;
+
+ if (!file->landscape)
+ {
+ static int n = 0;
+
+ unsigned int width = Width() / 2;
+ std::vector<ImageFile>::iterator next = current;
+
+ // hochkant -> show one image at the left side of the screen
+
+ render->image(file->path.c_str(), 0, Y(), width, Height(), yes, yes, _rotate ? file->orientation : 1);
+
+ // now fill the right halfe of the screen
+
+ do
+ {
+ getNext(next, file);
+
+ } while ((n%2 && file->landscape) || (!(n%2) && !file->landscape));
+
+ n++;
+
+ if (!file->landscape)
+ {
+ // one 'hochkant' image at the right side of the screen
+
+ render->image(file->path.c_str(), width, Y(), width, Height(), yes, yes, _rotate ? file->orientation : 1);
+ }
+ else
+ {
+ const unsigned int offset = 20;
+ unsigned int height = (Height()-30) / 2;
+
+ // two 'quer' images on the right side of the screen
+
+ render->image(file->path.c_str(), width, 10, width, height, yes, yes, _rotate ? file->orientation : 1);
+
+ do
+ {
+ getNext(next, file);
+
+ } while (!file->landscape);
+
+ render->image(file->path.c_str(), width+offset, height+20, width-offset, height, yes, yes, _rotate ? file->orientation : 1);
+ }
+
+ current++;
+ }
+ else
+ {
+ // quer -> show one image on screen
+
+ drawImage(file->path.c_str());
+ }
+
+ current++;
+
+ return success;
+}
+
+//***************************************************************************
+// cDisplayCalibrationCursor
+//***************************************************************************
+
+int cDisplayCalibrationCursor::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ t_rgba rgba;
+ int width = Width() ? Width() : 20;
+ int height = Height() ? Height() : 20;
+
+ if (_path != "")
+ render->image(_path.c_str(),
+ vdrStatus->calibration.cursorX - width/2,
+ vdrStatus->calibration.cursorY - height/2,
+ width, height, yes);
+ else
+ render->rectangle(vdrStatus->calibration.cursorX - width/2,
+ vdrStatus->calibration.cursorY - height/2,
+ width, height,
+ evaluateColor(_color.c_str(), rgba));
+
+ return success;
+}
+
+int cDisplayMenuButton::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ int index = _item - itemMenuButton;
+
+ if (index >= 0 && index <= 3)
+ return drawText(vdrStatus->_menu.buttons[index].c_str(),
+ 0, na, no /*clear*/);
+
+ return fail;
+}
+
+int cDisplayMenuButtonBackground::draw()
+{
+ string p;
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ int index = _item - itemMenuButtonBackground;
+
+ if (index < 0 || index > 3)
+ return fail;
+
+ if (vdrStatus->_menu.buttons[index].length())
+ {
+ if (_path != "")
+ {
+ evaluate(p, _path.c_str());
+ return drawImage(p.c_str());
+ }
+ }
+ else if (_path2 != "")
+ {
+ evaluate(p, _path2.c_str());
+ return drawImage(p.c_str());
+ }
+
+ return done;
+}
+
+int cDisplayMessage::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (vdrStatus->_message != "")
+ {
+ drawBackRect();
+
+ if (_path != "")
+ {
+ string p;
+ evaluate(p, _path.c_str());
+ drawImageOnBack(p.c_str(), _fit);
+ }
+
+ tell(3, "draw message '%s'", vdrStatus->_message.c_str());
+ drawText(vdrStatus->_message.c_str(), 0, na, no /*clear*/);
+
+ return success;
+ }
+
+ return ignore;
+}
+
+int cDisplayVolumeMuteSymbol::draw()
+{
+ static int lastMute = vdrStatus->_mute;
+ static uint64_t showUntil = msNow();
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ visible = no;
+
+ if (!_permanent && lastMute != vdrStatus->_mute)
+ {
+ lastMute = vdrStatus->_mute;
+ showUntil = msNow() + _delay;
+ }
+
+ if (_permanent || msNow() < showUntil)
+ {
+ string p;
+
+ visible = yes;
+
+ if (!_permanent)
+ scheduleForce(showUntil);
+
+ if (vdrStatus->_mute)
+ {
+ evaluate(p, _path.c_str());
+ return drawImage(p.c_str()); // is muted
+ }
+ else if (_path2 != "")
+ {
+ evaluate(p, _path2.c_str());
+ return drawImage(p.c_str());
+ }
+ }
+
+ return ignore;
+}
+
+int cDisplayVolumebar::draw()
+{
+ static int lastVolume = vdrStatus->_volume;
+ static uint64_t showUntil = msNow();
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ visible = no;
+
+ if (!_permanent && lastVolume != vdrStatus->_volume)
+ {
+ lastVolume = vdrStatus->_volume;
+ showUntil = msNow() + _delay;
+ }
+
+ if (_permanent || msNow() < showUntil)
+ {
+ string p;
+
+ if (!_permanent)
+ scheduleForce(showUntil);
+
+ visible = yes;
+
+ if (_path2 != "")
+ {
+ evaluate(p, _path2.c_str());
+ drawImageOnBack(p.c_str());
+ }
+
+ evaluate(p, _path.c_str());
+
+ return drawProgressBar(vdrStatus->_volume, 255, p,
+ Y(), Height(), no /*withFrame*/, no /*clear*/);
+ }
+
+ return ignore;
+}
+
+int cDisplayTimebar::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (!vdrStatus->_presentEvent.isEmpty() && !vdrStatus->_followingEvent.isEmpty())
+ {
+ string path = evaluatePath().c_str();
+
+ drawProgressBar(time(0) - vdrStatus->_presentEvent.StartTime(),
+ vdrStatus->_followingEvent.StartTime()
+ - vdrStatus->_presentEvent.StartTime(),
+ path);
+ }
+ else
+ drawBackRect();
+
+ if (!_delay)
+ scheduleDrawIn(SECONDS(30));
+
+ return done;
+}
+
+int cDisplayProgressBar::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ string cur;
+ string tot;
+
+ if (evaluate(cur, _value.c_str()) == success
+ && evaluate(tot, _total.c_str()) == success)
+ {
+ tell(3, "Progress: '%s'/'%s' %d/%d",
+ cur.c_str(), tot.c_str(),
+ atoi(cur.c_str()), atoi(tot.c_str()));
+
+ string path = evaluatePath().c_str();
+
+ return drawProgressBar(atoi(cur.c_str()), atoi(tot.c_str()), path);
+ }
+
+ return ignore;
+}
+
+int cDisplaySysinfo::draw()
+{
+ int status = ignore;
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ string path = evaluatePath().c_str();
+
+ if (_type.find("cpu") == 0)
+ {
+ int load = Sysinfo::cpuLoad();
+
+ // if (forceDraw || abs(load - lastCpuLoad) > 2)
+ {
+ lastCpuLoad = load;
+
+ if (_type == "cpuload")
+ status = drawProgressBar(load, 100, path);
+ else if (_type == "cpuidle")
+ status = drawProgressBar(100-load, 100, path);
+ }
+ }
+
+ else if (_type.find("mem") == 0)
+ {
+ unsigned long total, used, free, cached;
+
+ Sysinfo::memInfoMb(total, used, free, cached);
+
+ if (forceDraw || used != lastUsedMem)
+ {
+ int f = _factor / (1024*1024); // due to memInfoMb already return MB
+
+ lastUsedMem = used;
+
+ // scale to given factor
+
+ total /= f;
+ used /= f;
+ free /= f;
+
+ if (_type == "memused")
+ status = drawProgressBar(used, total, path);
+ else if (_type == "memfree")
+ status = drawProgressBar(free, total, path);
+ else if (_type == "memcached")
+ status = drawProgressBar(cached, total, path);
+ }
+ }
+
+ else if (_type == "disk" && _reference != "")
+ {
+ unsigned long freeM = 0, usedM = 0;
+ char* dir = strdup(_reference.c_str());
+ char* c;
+
+ if ((c = strchr(dir, '?')))
+ {
+ int u;
+
+ for (int i = 0; i < 10; i++)
+ {
+ *c = '0' + i;
+
+ if (fileExists(dir))
+ {
+ tell(6, "adding size of '%s'", dir);
+ freeM += FreeDiskSpaceMB(dir, &u);
+ usedM += u;
+ }
+ }
+ }
+ else
+ {
+ freeM = FreeDiskSpaceMB(dir, (int*)&usedM);
+ }
+
+ free(dir);
+
+ // trick, at least 1 due to divide by zero error
+
+ usedM = usedM ? usedM : 1;
+ freeM = freeM ? freeM : 1;
+
+ if (forceDraw || usedM != lastUsedDisk)
+ {
+ int f = _factor / (1024*1024); // due to FreeDiskSpaceMB return MB
+
+ lastUsedDisk = usedM;
+
+ // scale to given factor
+
+ usedM /= f;
+ freeM /= f;
+
+ status = drawProgressBar(usedM, freeM + usedM, path);
+ }
+ }
+ else
+ {
+ // return without scheduling next draw !
+
+ tell(0, "Ignoring sysinfo item of unexpected type '%s'",
+ _type.c_str());
+
+ return fail;
+ }
+
+ return status;
+}
+
+int cDisplayBackground::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ // #TODO - implement a force of all other items?
+
+ if (_path != "")
+ return cDisplayImage::draw();
+
+ return drawRectangle();
+}
+
+int cDisplayTextList::draw()
+{
+ cGraphTFTDisplay::cTextList* list = 0;
+
+ if (!_lines)
+ _lines = 1;
+
+ string p;
+ int visible = 0;
+ int y = 0;
+ int lineHeight = _size * 5/3;
+ int maxRows = Height() / (lineHeight * _lines);
+
+ cDisplayItem::draw();
+
+ // music or timer list ?
+
+ if (strstr(_text.c_str(), "actTimers") || strstr(_text.c_str(), "actRunning") || strstr(_text.c_str(), "actPending"))
+ list = &vdrStatus->_timers;
+ else
+ list = &vdrStatus->_music;
+
+ // first count visible rows
+
+ lastHeight = 0;
+ list->reset();
+
+ // first clear background, event if path is set (image may be transparent!)
+
+ drawBackRect();
+
+ // draw optional background image
+
+ if (_path != "")
+ drawImageOnBack(_path.c_str(), yes);
+
+ while (visible < maxRows && list->isValid())
+ {
+ if (evaluateCondition())
+ visible++;
+
+ list->inc();
+ }
+
+ if (visible == 0)
+ return success;
+
+ tell(3, "draw list with %d visible items, top pos %d", visible, Y());
+
+ // draw list
+
+ list->reset();
+
+ for (int r = 0; r < visible && list->isValid(); list->inc())
+ {
+ if (!evaluateCondition())
+ continue;
+
+ evaluate(p, _text.c_str());
+
+ y = Y() + lineHeight * r * _lines;
+
+ tell(4, "TextList, row %d '%s' at (%d/%d) with (%d) lines [%s]",
+ r, p.c_str(), X(), y, _lines, _text.c_str());
+
+ drawText(p.c_str(), y, lineHeight * _lines, no);
+
+ lastHeight += lineHeight * _lines;
+ r++;
+ }
+
+ return success;
+}
+
+int cDisplayMenuSelected::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ selectedItem = this;
+
+ return done;
+}
+
+int cDisplayMenuColumnSelected::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ selectedItem = this;
+
+ return done;
+}
+
+int cDisplayMenuEventColumnSelected::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ selectedItem = this;
+
+ return done;
+}
+
+//***************************************************************************
+// Menu - old style
+//***************************************************************************
+
+int cDisplayMenu::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (!selectedItem)
+ return fail;
+
+ int total;
+ int tabPercent[cGraphTFTService::MaxTabs] = {0, 0, 0, 0, 0, 0};
+ int afterSelect = 0;
+ int lineHeight = _size * 5/3;
+ int lineHeightSelect = selectedItem->Size() * 5/3;
+ int count = ((Height()-lineHeightSelect) / lineHeight) + 1;
+ int step = vdrStatus->_menu.currentRow - vdrStatus->_menu.currentRowLast; // step since last refresh
+
+ vdrStatus->_menu.visibleRows = count;
+ vdrStatus->_menu.lineHeight = lineHeight;
+ vdrStatus->_menu.lineHeightSelected = lineHeightSelect;
+
+ if (vdrStatus->_menu.topRow < 0)
+ vdrStatus->_menu.topRow = max(0, vdrStatus->_menu.currentRow - count/2); // initial
+ else if (vdrStatus->_menu.currentRow == vdrStatus->_menu.topRow-1)
+ vdrStatus->_menu.topRow = vdrStatus->_menu.currentRow; // up
+ else if (vdrStatus->_menu.currentRow == vdrStatus->_menu.topRow+count)
+ vdrStatus->_menu.topRow++; // down
+ else if (vdrStatus->_menu.currentRow < vdrStatus->_menu.topRow
+ || vdrStatus->_menu.currentRow > vdrStatus->_menu.topRow+count)
+ vdrStatus->_menu.topRow += step; // page up / page down
+
+ if (vdrStatus->_menu.topRow > (int)vdrStatus->_menu.items.size()-count)
+ vdrStatus->_menu.topRow = vdrStatus->_menu.items.size()-count;
+
+ if (vdrStatus->_menu.topRow < 0)
+ vdrStatus->_menu.topRow = 0;
+
+ vdrStatus->_menu.currentRowLast = vdrStatus->_menu.currentRow;
+
+ // calculate column width
+
+ total = vdrStatus->_menu.charInTabs[0] + vdrStatus->_menu.charInTabs[1] + vdrStatus->_menu.charInTabs[2]
+ + vdrStatus->_menu.charInTabs[3] + vdrStatus->_menu.charInTabs[4] + vdrStatus->_menu.charInTabs[5];
+
+ if (!total)
+ return done;
+
+ for (int i = 0; vdrStatus->_menu.charInTabs[i] != 0; i++)
+ tabPercent[i] = (int)((100L / (double)total) * (double)vdrStatus->_menu.charInTabs[i]);
+
+ tell(4, "Debug: tabs set to - %d:%d:%d:%d:%d",
+ tabPercent[0], tabPercent[1], tabPercent[2],
+ tabPercent[3], tabPercent[4]);
+
+ // loop over visible rows ...
+
+ for (int i = vdrStatus->_menu.topRow; i < min((int)vdrStatus->_menu.items.size(),
+ vdrStatus->_menu.topRow + count); ++i)
+ {
+ cDisplayItem* p = i == vdrStatus->_menu.currentRow ? selectedItem : this;
+ int y = p->Y() + ((i - vdrStatus->_menu.topRow) * lineHeight);
+
+ if (i == vdrStatus->_menu.currentRow)
+ {
+ afterSelect = lineHeightSelect - lineHeight;
+
+ // draw the selected backround
+
+ if (selectedItem->Focus() != "")
+ {
+ string p;
+
+ evaluate(p, selectedItem->Focus().c_str());
+ render->image(p.c_str(), X(),
+ Y() + (i - vdrStatus->_menu.topRow) * lineHeight,
+ Width(), Height());
+ }
+
+ // draw the columns
+
+ int x = selectedItem->X();
+
+ for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t)
+ {
+ if (vdrStatus->_menu.items[i].tabs[t] != "")
+ {
+ int width = (Width() * tabPercent[t]) / 100;
+ t_rgba rgba;
+
+ render->text(vdrStatus->_menu.items[i].tabs[t].c_str(),
+ selectedItem->Font().c_str(), selectedItem->Size(), selectedItem->Align(),
+ x,
+ Y() + ((i - vdrStatus->_menu.topRow) * lineHeight),
+ evaluateColor(selectedItem->Color().c_str(), rgba),
+ width,
+ lineHeightSelect, 1);
+
+ x += width;
+ }
+ }
+
+ if (selectedItem->Path() != "")
+ render->image(selectedItem->Path().c_str(),
+ X(), Y() + (i - vdrStatus->_menu.topRow) * lineHeight,
+ Width(), Height());
+
+ if (p->StaticPicture())
+ {
+ // for the selected item we optionaly draw a additional picture (ImageMap)
+ // as image name use the text after the number in the last column ...
+
+ string path = Thms::theTheme->getPathFromImageMap(vdrStatus->_menu.items[i].tabs[vdrStatus->_menu.items[i].tabCount-1].c_str());
+
+ if (path != "")
+ {
+ if (p->StaticX() && p->StaticY())
+ render->image(path.c_str(), p->StaticX(), p->StaticY(),
+ p->StaticWidth(), p->StaticHeight(), yes, yes);
+ else if (p->StaticX())
+ render->image(path.c_str(),
+ p->StaticX(),
+ Y() + (i - vdrStatus->_menu.topRow) * lineHeight
+ - ((p->StaticHeight() - lineHeightSelect) /2),
+ p->StaticWidth(), p->StaticHeight(), yes, yes);
+ }
+ }
+ }
+
+ else
+ {
+ if (_path != "")
+ render->image(_path.c_str(), X(), y + afterSelect, Width(), Height());
+
+ int x = X();
+
+ for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t)
+ {
+ if (vdrStatus->_menu.items[i].tabs[t] != "")
+ {
+ int width = (Width() * tabPercent[t]) / 100;
+ t_rgba rgba;
+
+ render->text(vdrStatus->_menu.items[i].tabs[t].c_str(), // test,
+ _font.c_str(), _size, _align, // font, font-size, align
+ x, y + afterSelect,
+ evaluateColor(_color.c_str(), rgba),
+ width, lineHeight, 1);
+
+ x += width;
+ }
+ }
+ }
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Menu Column - new 'column' style
+//***************************************************************************
+
+int cDisplayMenuColumn::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ cGraphTFTDisplay::MenuInfo* _menu = &vdrStatus->_menu;
+ int count;
+ int lineHeight;
+ int lineHeightSelect;
+
+ int step = _menu->currentRow - _menu->currentRowLast; // step since last refresh
+ int afterSelect = 0;
+
+ if (!_menu->items.size())
+ return done;
+
+ // calc height and row count ...
+
+ lineHeight = Height() ? Height() : Size() * 5/3;
+
+ if (selectedItem)
+ lineHeightSelect = selectedItem->Height() ? selectedItem->Height() : selectedItem->Size() * 5/3;
+ else
+ lineHeightSelect = lineHeight;
+
+ count = ((_menu_height-lineHeightSelect) / lineHeight) + 1;
+
+ // tell(0, "_menu->items.size(%d), _menu->topRow(%d), _menu->currentRow(%d),"
+ // "_menu->currentRowLast(%d), count(%d)",
+ // _menu->items.size(), _menu->topRow, _menu->currentRow, _menu->currentRowLast, count);
+
+ _menu->visibleRows = count;
+ _menu->lineHeight = lineHeight;
+ _menu->lineHeightSelected = lineHeightSelect;
+
+ if (_menu->topRow < 0)
+ _menu->topRow = max(0, _menu->currentRow - count/2); // initial
+ else if (_menu->currentRow == _menu->topRow-1)
+ _menu->topRow = _menu->currentRow; // up
+ else if (_menu->currentRow == _menu->topRow+count)
+ _menu->topRow++; // down
+ else if (_menu->currentRow < _menu->topRow
+ || _menu->currentRow > _menu->topRow+count)
+ _menu->topRow += step; // page up / page down
+
+ if (_menu->topRow > (int)_menu->items.size()-count)
+ _menu->topRow = _menu->items.size()-count;
+
+ if (_menu->topRow < _menu->currentRow-count)
+ _menu->topRow = _menu->currentRow-count;
+
+ if (_menu->topRow < 0)
+ _menu->topRow = 0;
+
+ if (step)
+ marquee_active = no;
+
+ _menu->currentRowLast = _menu->currentRow;
+
+ // paint the visible rows for this column
+
+ for (int i = _menu->topRow; i < min((int)_menu->items.size(), _menu->topRow + count); ++i)
+ {
+ cDisplayItem* p = this;
+
+ if (i == _menu->currentRow)
+ {
+ afterSelect = lineHeightSelect - lineHeight;
+
+ if (!selectedItem || selectedItem->Number() == na)
+ continue;
+
+ p = selectedItem;
+ }
+
+ int y = p->MenuY() + ((i - _menu->topRow) * lineHeight) + (p == selectedItem ? 0 : afterSelect);
+ _menu->drawingRow = i; // the row
+
+ tell(4, "colcount of row (%d) is (%d)", i, _menu->items[i].tabCount);
+
+ if (p->Number() > vdrStatus->_menu.items[i].tabCount)
+ {
+ tell(0, "Warning: Skipping column number %d, only (%d) columns for row (%d) send by vdr",
+ Number(), vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].tabCount, vdrStatus->_menu.drawingRow);
+
+ continue;
+ }
+
+ if (!p->evaluateCondition())
+ continue;
+
+ // calc pos an width ...
+
+ if (p->X() == na)
+ p->setX(_menu->items[i].nextX);
+
+ if (!p->Width())
+ p->setWidth(Thms::theTheme->getWidth()-p->X());
+
+ _menu->items[i].nextX = p->X() + p->Width() + p->Spacing();
+
+ if (i == _menu->currentRow)
+ {
+ if (p->ImageMap() && Number())
+ {
+ // for the selected item we optionaly draw a additional picture (ImageMap)
+ // as image name use the text of the current column ...
+
+ string path = Thms::theTheme->getPathFromImageMap(_menu->items[i].tabs[p->Number()-1].c_str());
+
+ if (path != "" && p->StaticX())
+ render->image(path.c_str(), p->StaticX(),
+ p->StaticY() ? p->StaticY() : y - ((p->StaticHeight() - lineHeightSelect) /2),
+ p->StaticWidth(), p->StaticHeight(), yes, yes);
+ }
+
+ if (p->StaticPicture() && Number())
+ {
+ // Image with static position for this column
+
+ if (_menu->items[i].tabs[p->Number()-1].c_str()[0] && p->StaticX())
+ render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str(), Format().c_str()).c_str(),
+ p->StaticX(), p->StaticY() ? p->StaticY() : y, p->StaticWidth(), p->StaticHeight(), yes, yes);
+ }
+
+ if (p->StaticText() && Number())
+ {
+ t_rgba rgba;
+
+ // Text with static position for this column
+
+ if (_menu->items[i].tabs[p->Number()-1].c_str()[0])
+ {
+ render->text(_menu->items[i].tabs[p->Number()-1].c_str(), // text
+ p->Font().c_str(), p->Size(), p->Align(), // font, font-size, align
+ p->StaticX(), // x-pos
+ p->StaticY(), // y-pos
+ evaluateColor(p->Color().c_str(), rgba), // color
+ p->StaticWidth(), // width
+ lineHeight, 1); // height, line-count
+ }
+ }
+ }
+
+ if (p->Width() == na)
+ continue;
+
+ if (_menu->items[i].type == itPartingLine)
+ {
+ cDisplayItem* partingLine = section->getItemByKind(itemPartingLine);
+
+ if (partingLine)
+ partingLine->drawPartingLine(_menu->items[i].tabs[0], y, lineHeight);
+
+ continue;
+ }
+
+ // draw image
+
+ if (p->Focus() != "")
+ {
+ string f;
+ evaluate(f, p->Focus().c_str());
+ render->image(f.c_str(),
+ p->X(), y,
+ p->Width(), lineHeight);
+ }
+
+ if (p->Type() == "progress")
+ {
+ int current = 0;
+ int n = 0;
+ int barHeight = p->BarHeight();
+
+ if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row
+ barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight());
+
+ // calc progress
+
+ while (_menu->items[i].tabs[p->Number()-1][n])
+ {
+ if (_menu->items[i].tabs[p->Number()-1][n] == '|')
+ current++;
+ n++;
+ }
+
+ // draw column's progress-bar
+
+ tell(5, "progress [%d] of (%d%%) at position y = %d [%s]",
+ i, current, y, _menu->items[i].tabs[p->Number()-1].c_str());
+
+ if (current && p->Path() != "")
+ {
+ string path = evaluatePath().c_str();
+
+ p->drawProgressBar(current, 8, path, y + (lineHeight-barHeight)/2, barHeight);
+ }
+ }
+
+ else if (p->Type() == "image")
+ {
+ string path;
+
+ if (p->Path() != "")
+ {
+ if (p->Path() == "{imageMap}")
+ path = Thms::theTheme->getPathFromImageMap(_menu->items[i].tabs[p->Number()-1].c_str());
+ else
+ path = p->evaluatePath();
+ }
+ else
+ path = p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str(), Format().c_str());
+
+ // draw column's image
+
+ if (path.length())
+ {
+ int barHeight = p->BarHeight();
+
+ if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row
+ barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight());
+
+ int offset = (lineHeight - barHeight) / 2;
+
+ render->image(path.c_str(), p->X(), y+offset,
+ p->Width(), barHeight,
+ p->Fit(), p->AspectRatio());
+ }
+ }
+
+ else
+ {
+ // draw column's text
+
+ string text;
+
+ if (p->Text() != "")
+ {
+ if (evaluate(text, p->Text().c_str()) != success)
+ text = "";
+ }
+ else
+ text = _menu->items[i].tabs[p->Number()-1].c_str();
+
+ tell(5, "draw '%s'", text.c_str());
+
+ p->drawText(text.c_str(), y, lineHeight, no);
+ }
+ }
+
+ selectedItem = 0;
+
+ return done;
+}
+
+//***************************************************************************
+// Menu Event Column
+//***************************************************************************
+
+int cDisplayMenuEventColumn::draw()
+{
+ // static int selectedOffset = 0;
+
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (!vdrStatus->_eventsReady)
+ return fail;
+
+ cGraphTFTDisplay::MenuInfo* _menu = &vdrStatus->_menu;
+
+ string path;
+ int count;
+ int lineHeight;
+ int lineHeightSelect = 0;
+
+ int step = _menu->currentRow - _menu->currentRowLast; // step since last refresh
+ int afterSelect = 0;
+
+ // calc height and row count ...
+
+ lineHeight = Height() ? Height() : Size() * 5/3;
+
+ if (selectedItem)
+ lineHeightSelect = selectedItem->Height() ? selectedItem->Height() : selectedItem->Size() * 5/3;
+ else
+ lineHeightSelect = lineHeight; // assume normal height this time
+
+ count = ((_menu_height-lineHeightSelect) / lineHeight) + 1;
+
+ _menu->visibleRows = count;
+ _menu->lineHeight = lineHeight;
+ _menu->lineHeightSelected = lineHeightSelect;
+
+ if (_menu->topRow < 0)
+ _menu->topRow = max(0, _menu->currentRow - count/2); // initial
+ else if (_menu->currentRow == _menu->topRow-1)
+ _menu->topRow = _menu->currentRow; // up
+ else if (_menu->currentRow == _menu->topRow+count)
+ _menu->topRow++; // down
+ else if (_menu->currentRow < _menu->topRow
+ || _menu->currentRow > _menu->topRow+count)
+ _menu->topRow += step; // page up / page down
+
+ if (_menu->topRow > (int)_menu->items.size()-count)
+ _menu->topRow = _menu->items.size()-count;
+
+ if (_menu->topRow < _menu->currentRow-count)
+ _menu->topRow = _menu->currentRow-count;
+
+ if (_menu->topRow < 0)
+ _menu->topRow = 0;
+
+ if (step)
+ marquee_active = no;
+
+ _menu->currentRowLast = _menu->currentRow;
+
+ // paint the visible rows for this column
+
+ for (int i = _menu->topRow; vdrStatus->_eventsReady && i < min((int)_menu->items.size(), _menu->topRow + count); ++i)
+ {
+ if (i == na)
+ tell(0, "XXXXXXXXXXXXXXXXXXXXXXXXX");
+
+ _menu->drawingRow = i; // the row
+
+ cDisplayItem* p = i == _menu->currentRow ? selectedItem : this;
+
+ if (!p)
+ continue;
+
+ int y = p->MenuY() + ((i - _menu->topRow) * lineHeight) + afterSelect;
+
+ tell(4, "colcount of row (%d) is (%d)", i, _menu->items[i].tabCount);
+
+ if (i == _menu->currentRow)
+ afterSelect = lineHeightSelect - lineHeight;
+
+ if (_menu->items[i].type == itPartingLine)
+ {
+ cDisplayItem* partingLine = section->getItemByKind(itemPartingLine);
+
+ // nur für die erste Spalte zeichnen, partingLine
+ // soll alle Spalten abdecken!
+
+ if (partingLine && Number() == 0)
+ partingLine->drawPartingLine(_menu->items[i].tabs[0], y, lineHeight);
+
+ continue;
+ }
+
+ // don't check condition for itPartingLine
+
+ if (!p->evaluateCondition())
+ continue;
+
+ // draw focus image
+
+ if (i == _menu->currentRow && p->Focus() != "")
+ {
+ string f;
+ evaluate(f, p->Focus().c_str());
+ render->image(f.c_str(),
+ p->X(), y, // + selectedOffset,
+ p->Width(), lineHeight);
+ }
+
+ if (p->Type() == "progress")
+ {
+ int barHeight = p->BarHeight();
+ string value;
+ int current;
+ string path = "";
+
+ lookupVariable("rowEventProgress", value);
+
+ current = atoi(value.c_str());
+
+ if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row
+ barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight());
+
+ // draw column's progress-bar
+
+ tell(5, "progress [%d] of (%d%%)", i, current);
+
+ if (current && p->Path() != "")
+ {
+ string path = evaluatePath().c_str();
+ p->drawProgressBar(current, 100, path, y + (lineHeight-barHeight)/2, barHeight);
+ }
+ }
+
+ else if (p->Type() == "image")
+ {
+ path = p->evaluatePath();
+
+ // draw column's image
+
+ if (path.length())
+ {
+ int barHeight = p->BarHeight();
+
+ if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row
+ barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight());
+
+ int offset = (lineHeight - barHeight) / 2;
+
+ render->image(path.c_str(), p->X(), y+offset,
+ p->Width(), barHeight,
+ p->Fit(), p->AspectRatio());
+ }
+ }
+
+ else
+ {
+ // draw column's text
+
+ string text;
+ int textHeight = p->Size() * 5/3;
+ textHeight -= textHeight/10; // 10% weniger
+
+ if (evaluate(text, p->Text().c_str()) != success)
+ text = "";
+
+ tell(5, "draw '%s'", text.c_str());
+
+ if (!p->Line())
+ p->drawText(text.c_str(),
+ y + (p->AlignV() ? (lineHeight-textHeight)/2 : 0),
+ lineHeight, no);
+ else
+ p->drawText(text.c_str(),
+ y + ((p->Line()-1)*textHeight),
+ textHeight, no);
+ }
+ }
+
+ selectedItem = 0;
+
+ return done;
+}
+
+//***************************************************************************
+// Spectrum Analyzer
+//***************************************************************************
+
+int cDisplaySpectrumAnalyzer::draw()
+{
+ if (cDisplayItem::draw() != success)
+ return fail;
+
+ if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, 0))
+ {
+ // tell(0, "draw SpectrumAnalyzer II");
+
+ cSpanService::Span_GetBarHeights_v1_0 GetBarHeights;
+
+ int bandsSA = 20;
+ int falloffSA = 8;
+
+ unsigned int* barHeights = new unsigned int[bandsSA];
+ unsigned int* barHeightsLeftChannel = new unsigned int[bandsSA];
+ unsigned int* barHeightsRightChannel = new unsigned int[bandsSA];
+ unsigned int* barPeaksBothChannels = new unsigned int[bandsSA];
+ unsigned int* barPeaksLeftChannel = new unsigned int[bandsSA];
+ unsigned int* barPeaksRightChannel = new unsigned int[bandsSA];
+ unsigned int volumeLeftChannel;
+ unsigned int volumeRightChannel;
+ unsigned int volumeBothChannels;
+
+ GetBarHeights.bands = bandsSA;
+ GetBarHeights.barHeights = barHeights;
+ GetBarHeights.barHeightsLeftChannel = barHeightsLeftChannel;
+ GetBarHeights.barHeightsRightChannel = barHeightsRightChannel;
+ GetBarHeights.volumeLeftChannel = &volumeLeftChannel;
+ GetBarHeights.volumeRightChannel = &volumeRightChannel;
+ GetBarHeights.volumeBothChannels = &volumeBothChannels;
+
+ GetBarHeights.name = "graphtft";
+ GetBarHeights.falloff = falloffSA;
+ GetBarHeights.barPeaksBothChannels = barPeaksBothChannels;
+ GetBarHeights.barPeaksLeftChannel = barPeaksLeftChannel;
+ GetBarHeights.barPeaksRightChannel = barPeaksRightChannel;
+
+ if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, &GetBarHeights))
+ {
+ int i;
+ t_rgba rgba;
+ int barWidth = Width() / (2 * bandsSA);
+ int width = barWidth * 2 * bandsSA;
+
+ tell(4, "width = %d; barWidth = %d; ", width, barWidth);
+
+ if (_path != "")
+ render->image(_path.c_str(),
+ X(), Y(), width, Height(), true);
+ else
+ render->rectangle(X(), Y(),
+ width, Height(),
+ evaluateColor(_color.c_str(), rgba));
+
+ for (i = 0; i < bandsSA; i++)
+ {
+ int2rgba(0, 0, 0, 255, rgba);
+
+ render->rectangle(X() + 2 * barWidth * i,
+ Y(),
+ barWidth,
+ Height() - (barHeightsLeftChannel[i] * Height() / 100),
+ rgba);
+
+
+ render->rectangle(X() + 2 * barWidth * i + barWidth,
+ Y(),
+ barWidth,
+ Height() - (barHeightsRightChannel[i] * Height() / 100),
+ rgba);
+
+ // the peak
+
+ // height = barPeaksBothChannels[i] * item->Height() / 100;
+
+ // if (height > 0)
+ // {
+ // render->rectangle(item->X() + barWidth*2*i+ barWidth + 1,
+ // item->Y(),
+ // item->X() + barWidth*2*i + barWidth+ barWidth + 1,
+ // height,
+ // item->Red(), item->Green(), item->Blue(),
+ // item->Transparent());
+ // }
+ }
+ }
+
+ delete[] barHeights;
+ delete[] barHeightsLeftChannel;
+ delete[] barHeightsRightChannel;
+ delete[] barPeaksBothChannels;
+ delete[] barPeaksLeftChannel;
+ delete[] barPeaksRightChannel;
+ }
+
+ return done;
+}
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
diff --git a/graphtftng.c b/graphtftng.c
new file mode 100644
index 0000000..13a1793
--- /dev/null
+++ b/graphtftng.c
@@ -0,0 +1,792 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * graphtftng.c
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2015 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 <vdr/config.h>
+#include <theme.h>
+#include <getopt.h>
+
+#include <service.h>
+#include <graphtftng.h>
+#include <span.h>
+
+//***************************************************************************
+// cGraphTFTMenu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cGraphTFTMenu::cGraphTFTMenu(const char* title, cPluginGraphTFT* aPlugin)
+{
+ int i = 0;
+ char* buf = 0;
+ char** normalModes = Thms::theTheme->getNormalSections();
+
+ SetMenuCategory(mcPluginSetup);
+ SetTitle(title ? title : "");
+
+ plugin = aPlugin;
+ defMode = 0;
+ dspActive = plugin->getDisplay()->displayActive;
+
+ while (normalModes[i])
+ {
+ if (normalModes[i] == GraphTFTSetup.normalMode)
+ {
+ defMode = i;
+ break;
+ }
+
+ i++;
+ }
+ originalDefMode = defMode;
+
+ Clear();
+
+ cOsdMenu::Add(new cOsdItem(tr("Reload themes")));
+ cOsdMenu::Add(new cMenuEditBoolItem(tr("Refresh Display"), &dspActive));
+ cOsdMenu::Add(new cMenuEditStraItem(tr("Normal Display"),
+ &defMode,
+ Thms::theTheme->getNormalSectionsCount(),
+ normalModes));
+
+ // user theme variables
+
+ if (!Thms::theTheme->menuVariables.empty())
+ {
+ asprintf(&buf, "------ \t %s -----------------------------------------",
+ Thms::theTheme->getName().c_str());
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+
+ map<string,string>::iterator iter;
+
+ for (i = 0, iter = Thms::theTheme->menuVariables.begin();
+ i < 50 && iter != Thms::theTheme->menuVariables.end();
+ iter++, i++)
+ {
+ strncpy(variableValues[i], iter->second.c_str(), sizeVarMax);
+ variableValues[i][sizeVarMax] = 0;
+
+ cOsdMenu::Add(new cMenuEditStrItem(iter->first.c_str(),
+ variableValues[i],
+ sizeVarMax, FileNameChars));
+ }
+ }
+
+ SetHelp(tr("Snapshot"), 0, 0,0);
+
+ Display();
+}
+
+cGraphTFTMenu::~cGraphTFTMenu()
+{
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cGraphTFTMenu::ProcessKey(eKeys key)
+{
+ eOSState state = cOsdMenu::ProcessKey(key);
+
+ // RefreshCurrent();
+
+ if (state != osUnknown)
+ return state;
+
+ switch (key)
+ {
+ case kRed:
+ {
+ plugin->getDisplay()->snapshotPending = yes;
+ return osEnd;
+ }
+ case kOk:
+ {
+ Store();
+
+ if (Current() == 0)
+ plugin->loadThemes();
+
+ return osEnd;
+ }
+
+ default:
+ break;
+ }
+
+ return state;
+}
+
+void cGraphTFTMenu::Store()
+{
+ int i;
+
+ if (defMode != originalDefMode)
+ {
+ GraphTFTSetup.normalMode = Thms::theTheme->getNormalSections()[defMode];
+ GraphTFTSetup.storeNormalMode = true;
+ GraphTFTSetup.originalNormalMode = "";
+ plugin->SetupStore("normalMode", GraphTFTSetup.normalMode.c_str());
+ Setup.Save();
+ }
+
+ plugin->getDisplay()->displayActive = dspActive;
+
+ map<string,string>::iterator iter;
+
+ for (i = 0, iter = Thms::theTheme->menuVariables.begin();
+ i < 50 && iter != Thms::theTheme->menuVariables.end();
+ iter++, i++)
+ {
+ iter->second = variableValues[i];
+ }
+}
+
+//***************************************************************************
+// cPluginGraphTFT
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cPluginGraphTFT::cPluginGraphTFT()
+{
+ display = 0;
+ device = 0;
+ port = 2039;
+ startDetached = no;
+}
+
+cPluginGraphTFT::~cPluginGraphTFT()
+{
+ if (display) delete display;
+
+ free(device);
+ Thms::theTheme = 0;
+ themes.Clear();
+}
+
+//***************************************************************************
+// Command Line Help
+//***************************************************************************
+
+const char *cPluginGraphTFT::CommandLineHelp()
+{
+ return " -d <device>, --device=<device> set the output device\n"
+ " /dev/fb0\n"
+ " xorg:1.1\n"
+ " (default: none)\n"
+ " -p <port>, --port=<port> set the output port\n"
+ " (default: 2039)\n"
+ " -o, --detached start detached (offline) only supported for xorg device\n"
+ ;
+}
+
+//***************************************************************************
+// Process Args
+//***************************************************************************
+
+bool cPluginGraphTFT::ProcessArgs(int argc, char* argv[])
+{
+ int c;
+
+ static option long_options[] =
+ {
+ { "device", required_argument, 0, 'd' },
+ { "port", required_argument, 0, 'p' },
+ { "detached", no_argument, 0, 'o' },
+ { 0, 0, 0, 0 }
+ };
+
+ // check the arguments
+
+ while ((c = getopt_long(argc, argv, "d:p:o", long_options, 0)) != -1)
+ {
+ switch (c)
+ {
+ case 'd': device = strdup(optarg); break;
+ case 'p': port = atoi(optarg); break;
+ case 'o': startDetached = yes; break;
+ default: tell(0, "Ignoring unknown argument '%s'", optarg);
+ }
+ }
+
+ return true;
+}
+
+//***************************************************************************
+// Initialize
+//***************************************************************************
+
+bool cPluginGraphTFT::Initialize()
+{
+ return true;
+}
+
+//***************************************************************************
+// Start
+//***************************************************************************
+
+bool cPluginGraphTFT::Start()
+{
+ if (!device)
+ asprintf(&device, "none");
+
+ // init
+
+ GraphTFTSetup.setClient(this);
+
+ display = new cGraphTFTDisplay(THEMEVERSION);
+
+ logDevice = GraphTFTSetup.LogDevice;
+ logLevel = GraphTFTSetup.Level;
+
+ GraphTFTSetup.themesPath = ConfigDirectory() + string("/graphtftng/themes/");
+ GraphTFTSetup.configPath = ConfigDirectory() + string("/graphtftng/");
+
+ // load the thems
+
+ if (loadThemes() != success)
+ return false;
+
+ if (display->init(device, port, startDetached) != success)
+ {
+ tell(0, "Error: Initializing failed, aborting!");
+
+ return 0;
+ }
+
+ return 1;
+}
+
+//***************************************************************************
+// Store
+//***************************************************************************
+
+void cPluginGraphTFT::Store()
+{
+ GraphTFTSetup.Store();
+}
+
+//***************************************************************************
+// Load Themes
+//***************************************************************************
+
+int cPluginGraphTFT::loadThemes()
+{
+ char* buffer;
+ FILE* p;
+ cMutexLock lock(display->getMutex());
+
+ // look for the themes in the config directory
+
+ asprintf(&buffer, "find %s -follow -type f -name '*.theme' | sort",
+ GraphTFTSetup.themesPath.c_str());
+
+ p = popen(buffer, "r");
+
+ free(buffer);
+
+ // first forget loaded themes
+
+ themes.Clear();
+
+ tell(0, "Loading themes");
+
+ if (p)
+ {
+ char* s;
+ cReadLine ReadLine;
+
+ while ((s = ReadLine.Read(p)))
+ {
+ // Try to load the themes
+
+ Thms::theTheme = new cGraphTFTTheme();
+
+ tell(0, "Try loading theme '%s'", s);
+
+ if (Thms::theTheme->load(s) != success)
+ {
+ delete Thms::theTheme;
+ Thms::theTheme = 0;
+ tell(0, "Ignoring invalid theme '%s'", s);
+
+ continue;
+ }
+
+ tell(0, "Theme '%s' loaded", s);
+
+ Thms::theTheme->check(THEMEVERSION);
+ themes.Add(Thms::theTheme);
+ }
+ }
+
+ pclose(p);
+
+ // Have we found themes?
+
+ if (!themes.Count())
+ {
+ tell(0, "Fatal: No themes found, aborting!");
+ return fail;
+ }
+
+ Thms::theTheme = themes.getTheme(GraphTFTSetup.Theme);
+
+ if (!Thms::theTheme)
+ Thms::theTheme = themes.Get(0);
+
+ tell(0, "Loaded %d themes", themes.Count());
+
+ Thms::theTheme->activate(na); // display->fdInotify);
+
+ tell(0, "Activated theme '%s'", Thms::theTheme->getName().c_str());
+
+ display->setupChanged();
+ display->themesReloaded();
+
+ return success;
+}
+
+//***************************************************************************
+// Main Menu Action
+//***************************************************************************
+
+cOsdObject* cPluginGraphTFT::MainMenuAction()
+{
+ return new cGraphTFTMenu(MainMenuEntry(), this);
+}
+
+//***************************************************************************
+// Setup Menu
+//***************************************************************************
+
+cMenuSetupPage* cPluginGraphTFT::SetupMenu()
+{
+ return new cMenuSetupGraphTFT(display);
+}
+
+//***************************************************************************
+// Setup Parse
+//***************************************************************************
+
+bool cPluginGraphTFT::SetupParse(const char* Name, const char* Value)
+{
+ return GraphTFTSetup.SetupParse(Name, Value);
+}
+
+//***************************************************************************
+// Service
+//***************************************************************************
+
+bool cPluginGraphTFT::Service(const char* id, void* data)
+{
+ if (strcmp(id, SPAN_CLIENT_CHECK_ID) == 0)
+ {
+ if (GraphTFTSetup.enableSpectrumAnalyzer && data)
+ *((cSpanService::Span_Client_Check_1_0*)data)->isActive = true;
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_TOUCH_EVENT_ID) == 0)
+ {
+ cTftCS::GraphTftTouchEvent* event;
+ event = (cTftCS::GraphTftTouchEvent*)data;
+
+ display->mouseEvent(event->x, event->y, event->button, event->flag);
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_CALIBRATION_ID) == 0)
+ {
+ display->setCalibrate(((cTftCS::GraphTftCalibration*)data)->activate);
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_COVERNAME_ID) == 0)
+ {
+ if (data)
+ {
+ display->setCoverPath(((cTftCS::MusicServiceCovername*)data)->name);
+
+ if (display->isMode(cGraphTFTService::ReplayMP3))
+ display->broadcast(yes);
+ }
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_PLAYLIST_ID) == 0)
+ {
+ if (data)
+ {
+ cTftCS::MusicServicePlaylist* p;
+ p = (cTftCS::MusicServicePlaylist*)data;
+
+ if (strcmp(p->item, "-EOL-") != 0)
+ display->musicAddPlaylistItem(p->item, p->index);
+ else
+ display->musicAddPlaylistItem(tr("--- end of playlist ---"), p->index);
+
+ if (p->index == p->count && display->isMode(cGraphTFTService::ReplayMP3))
+ display->broadcast(yes);
+ }
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_STATUS_ID) == 0)
+ {
+ if (data)
+ {
+ cTftCS::MusicServicePlayerInfo* p;
+ p = (cTftCS::MusicServicePlayerInfo*)data;
+
+ tell(2, "Got state update from music plugin");
+ display->setMusicPlayerState(p);
+
+ if (display->isMode(cGraphTFTService::ReplayMP3))
+ display->broadcast(yes);
+ }
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_HELPBUTTONS_ID) == 0)
+ {
+ if (data)
+ {
+ cTftCS::MusicServiceHelpButtons* p;
+ p = (cTftCS::MusicServiceHelpButtons*)data;
+
+ tell(0, "Got help buttons from music plugin");
+ display->setMusicPlayerHelpButtons(p);
+
+ if (display->isMode(cGraphTFTService::ReplayMP3))
+ display->broadcast(yes);
+ }
+
+ return true;
+ }
+
+ else if (strcmp(id, GRAPHTFT_INFO_ID) == 0)
+ {
+ if (data)
+ {
+ cTftCS::MusicServiceInfo* p;
+ p = (cTftCS::MusicServiceInfo*)data;
+
+ tell(0, "Got display info from music plugin");
+ display->_music.currentTrack = p->info;
+
+ if (display->isMode(cGraphTFTService::ReplayMP3))
+ display->broadcast(yes);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//***************************************************************************
+// SVDRP Help Pages
+//***************************************************************************
+
+const char** cPluginGraphTFT::SVDRPHelpPages()
+{
+ static const char* HelpPages[] =
+ {
+ "ACTIVE \n"
+ " Activate/Deactivate display refresh {ON|OFF}\n",
+ "VIEW \n"
+ " Set the normal view to the specified section\n",
+ "PREV \n"
+ " Set the normal view to the previous section\n",
+ "NEXT \n"
+ " Set the normal view to the next section\n",
+ "TVIEW \n"
+ " Set the normal view temporary to the specified section\n",
+ "RVIEW \n"
+ " Reset the temporary 'normal view'\n",
+ "REFRESH \n"
+ " force display refresh\n",
+ "RELOAD \n"
+ " reload the actual theme\n",
+ "ATTA \n"
+ " attach to xorg (if xorg is the selected output device)\n",
+ "DETA \n"
+ " detach from xorg (if xorg is the selected output device)\n",
+ "DISP <display> [width:height]\n"
+ " switch xorg display and optional the resolution (if xorg is the selected output device)\n",
+ "DUMP <file> [<width> <height>]\n"
+ " dump the actual frame to <file> (specify a path with access rights for the vdr process)\n",
+ 0
+ };
+
+ return HelpPages;
+}
+
+//***************************************************************************
+// SVDRP Command
+//***************************************************************************
+
+cString cPluginGraphTFT::SVDRPCommand(const char* Command,
+ const char* Option,
+ int& ReplyCode)
+{
+ const char* m = 0;
+
+ if (!strcasecmp(Command, "ACTIVE"))
+ {
+ if (Option && strcasecmp(Option, "ON") == 0)
+ {
+ display->displayActive = yes;
+ ReplyCode = 550;
+ return "graphTFT display activated";
+ }
+ else if (Option && strcasecmp(Option, "OFF") == 0)
+ {
+ display->displayActive = no;
+ ReplyCode = 550;
+ return "graphTFT display deactivated!";
+ }
+ else
+ {
+ ReplyCode = 901;
+ return "Error: Unexpected option";
+ }
+ }
+
+ else if (!strcasecmp(Command, "DETA"))
+ {
+ if (!display->getRenderer())
+ return "Can't detach, no xorg device configured (use -d to setup the device)";
+
+ if (display->detachXorg() != success)
+ return "detache failed!";
+
+ return "detached from xorg";
+ }
+
+ else if (!strcasecmp(Command, "ATTA"))
+ {
+ if (!display->getRenderer())
+ return "Can't attach, no xorg device configured (use -d to setup the device)";
+
+ if (display->attachXorg() != success)
+ return "attach failed!";
+
+ display->broadcast(yes);
+
+ return "attached to xorg";
+ }
+
+ else if (!strcasecmp(Command, "DISP"))
+ {
+ char* opt = Option ? strdup(Option) : 0;
+ int w = 0;
+ int h = 0;
+ char* p;
+
+ if (!display->getRenderer())
+ return "Can't set display, no xorg device configured (use -d to setup the device)";
+
+ if (Str::isEmpty(opt))
+ return "Missing option";
+
+ if ((p = strchr(opt, ' ')))
+ {
+ *p = 0;
+ p++;
+ w = atoi(p);
+
+ if ((p = strchr(p, ':')))
+ {
+ *p = 0;
+ p++;
+ h = atoi(p);
+ }
+
+ if (w && h)
+ display->setupChanged(w, h);
+ }
+
+ if (display->detachXorg() != success)
+ return "detache for display switch failed!";
+
+ if (display->attachXorg(opt) != success)
+ return "attach failed!";
+
+ display->broadcast(yes);
+
+ return "reattached to xorg";
+ }
+
+ else if (!strcasecmp(Command, "RELOAD"))
+ {
+ loadThemes();
+ display->broadcast(yes);
+ ReplyCode = 550;
+ return "theme reloaded";
+ }
+
+ else if (!strcasecmp(Command, "REFRESH"))
+ {
+ display->broadcast(yes);
+ ReplyCode = 550;
+ return "display refreshed";
+ }
+
+ else if (!strcasecmp(Command, "PREV") || !strcasecmp(Command, "NEXT"))
+ {
+ if (!strcasecmp(Command, "NEXT"))
+ GraphTFTSetup.normalMode = Thms::theTheme->nextNormalMode(GraphTFTSetup.normalMode.c_str());
+ else
+ GraphTFTSetup.normalMode = Thms::theTheme->prevNormalMode(GraphTFTSetup.normalMode.c_str());
+
+ GraphTFTSetup.storeNormalMode = true;
+ GraphTFTSetup.originalNormalMode = "";
+ SetupStore("normalMode", GraphTFTSetup.normalMode.c_str());
+ Setup.Save();
+ display->broadcast(yes);
+
+ ReplyCode = 550;
+ return "Normal view changed";
+ }
+ else if (!strcasecmp(Command, "VIEW"))
+ {
+ if (Str::isEmpty(Option))
+ {
+ ReplyCode = 901;
+ return "Error: Missing option";
+ }
+
+ if (!strcasecmp(Option, "TV") || !strcasecmp(Option, "RADIO"))
+ Option = "Standard";
+
+ if ((m = Thms::theTheme->getNormalMode(Option)))
+ {
+ GraphTFTSetup.normalMode = m;
+ GraphTFTSetup.storeNormalMode = true;
+ GraphTFTSetup.originalNormalMode = "";
+ }
+ else
+ {
+ ReplyCode = 901;
+ return "Error: Unexpected option";
+ }
+
+ SetupStore("normalMode", GraphTFTSetup.normalMode.c_str());
+ Setup.Save();
+ display->broadcast(yes);
+
+ tell(0, "Normal view now '%s'", GraphTFTSetup.normalMode.c_str());
+
+ ReplyCode = 550;
+ return "Normal view changed";
+ }
+
+ else if (!strcasecmp(Command, "TVIEW"))
+ {
+ if (!Str::isEmpty(Option) && (m = Thms::theTheme->getNormalMode(Option)))
+ {
+ if (GraphTFTSetup.originalNormalMode.empty())
+ GraphTFTSetup.originalNormalMode = GraphTFTSetup.normalMode;
+
+ GraphTFTSetup.normalMode = m;
+ GraphTFTSetup.storeNormalMode = false;
+ }
+ else
+ {
+ ReplyCode = 901;
+ return "Error: Unexpected option";
+ }
+
+ display->broadcast(yes);
+
+ ReplyCode = 550;
+ return "Normal view changed temporarily";
+ }
+
+ else if (!strcasecmp(Command, "RVIEW"))
+ {
+ if (!GraphTFTSetup.storeNormalMode && !GraphTFTSetup.originalNormalMode.empty())
+ {
+ GraphTFTSetup.normalMode = GraphTFTSetup.originalNormalMode;
+ GraphTFTSetup.storeNormalMode = true;
+ GraphTFTSetup.originalNormalMode = "";
+ }
+ else
+ {
+ ReplyCode = 901;
+ return "Error: Unexpected option";
+ }
+
+ SetupStore("normalMode", GraphTFTSetup.normalMode.c_str());
+ Setup.Save();
+ display->broadcast(yes);
+
+ ReplyCode = 550;
+ return "Normal view reset";
+ }
+
+ else if (!strcasecmp(Command, "DUMP"))
+ {
+ char* file = strdup("/tmp/graphTFT.jpg");
+ int width = na;
+ int height = na;
+
+ if (*Option)
+ {
+ char* p;
+
+ free(file);
+ file = strdup(Option);
+ p = file;
+
+ if ((p = strchr(p, ' ')))
+ {
+ *p = 0;
+ width = atoi(++p);
+ }
+
+ if (p && (p = strchr(p, ' ')))
+ {
+ *p = 0;
+ height = atoi(++p);
+ }
+ }
+
+ display->triggerDump(file, width, height);
+
+ free(file);
+
+ ReplyCode = 550;
+ return "Dump of image triggered";
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+
+VDRPLUGINCREATOR(cPluginGraphTFT)
diff --git a/graphtftng.h b/graphtftng.h
new file mode 100644
index 0000000..828ad57
--- /dev/null
+++ b/graphtftng.h
@@ -0,0 +1,124 @@
+/*
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * graphtftng.h
+ *
+ * (c) 2006-2016 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 __GTFT__H__
+#define __GTFT__H__
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/plugin.h>
+#include <vdr/config.h>
+
+#include "common.h"
+#include "setup.h"
+#include "display.h"
+#include "HISTORY.h"
+
+//***************************************************************************
+// General
+//***************************************************************************
+
+static const char* DESCRIPTION = trNOOP("VDR OSD on TFT");
+static const char* MAINMENUENTRY = trNOOP("Graph-TFTng");
+
+class cPluginGraphTFT;
+
+//***************************************************************************
+// Class GraphTFTMenuItem
+//***************************************************************************
+
+class cGraphTFTMenuItem : public cOsdItem
+{
+ public:
+
+ cGraphTFTMenuItem(const char* aTitle)
+ : cOsdItem(aTitle) {}
+};
+
+//***************************************************************************
+// Class GraphTFTMenu
+//***************************************************************************
+
+class cGraphTFTMenu : public cMenuSetupPage
+{
+ public:
+
+ enum Size
+ {
+ sizeVarMax = 100
+ };
+
+ cGraphTFTMenu(const char* title, cPluginGraphTFT* aPlugin);
+ virtual ~cGraphTFTMenu();
+
+ virtual eOSState ProcessKey(eKeys key);
+
+ const char* MenuKind() { return "MenuSetupPage"; }
+
+ protected:
+
+ void Store();
+
+ cPluginGraphTFT* plugin;
+ int defMode;
+ int originalDefMode;
+ int dspActive;
+ char variableValues[50][sizeVarMax+TB];
+};
+
+//***************************************************************************
+// Class cPluginGraphTFT
+//***************************************************************************
+
+class cPluginGraphTFT : public cPlugin, cThemeService
+{
+ public:
+
+ cPluginGraphTFT();
+ ~cPluginGraphTFT();
+
+ const char* Version() { return VERSION; }
+ const char* Description() { return tr(DESCRIPTION); }
+ const char* CommandLineHelp();
+
+ bool ProcessArgs(int argc, char* argv[]);
+ bool Initialize();
+ bool Start();
+ void Store();
+ void Housekeeping() {}
+
+ const char* MainMenuEntry()
+ { return GraphTFTSetup.HideMainMenu ? 0 : tr(MAINMENUENTRY); }
+
+ cOsdObject* MainMenuAction();
+
+ cMenuSetupPage* SetupMenu();
+ bool SetupParse(const char* Name, const char* Value);
+ cGraphTFTDisplay* getDisplay() { return display; }
+
+ bool Service(const char* Id, void* Data);
+ const char** SVDRPHelpPages();
+ cString SVDRPCommand(const char* Command, const char* Option, int& ReplyCode);
+
+ int loadThemes();
+
+ private:
+
+ cGraphTFTDisplay* display;
+ char* device;
+ int startDetached;
+ int port;
+};
+
+//***************************************************************************
+#endif // __GTFT__H__
diff --git a/imlibrenderer/Makefile b/imlibrenderer/Makefile
new file mode 100644
index 0000000..68e7227
--- /dev/null
+++ b/imlibrenderer/Makefile
@@ -0,0 +1,5 @@
+
+LIBS += $(shell imlib2-config --libs) -ljpeg
+
+all:
+ g++ -ggdb -I ../ imlib.cc $(LIBS) -o imlibtest
diff --git a/imlibrenderer/dmyrenderer/dmyrenderer.h b/imlibrenderer/dmyrenderer/dmyrenderer.h
new file mode 100644
index 0000000..d95fd1c
--- /dev/null
+++ b/imlibrenderer/dmyrenderer/dmyrenderer.h
@@ -0,0 +1,28 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File dmyrenderer.c
+// Date 31.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 DummyRenderer
+//***************************************************************************
+
+#ifndef __GTFT_DMYRENDERER_HPP__
+#define __GTFT_DMYRENDERER_HPP__
+
+#include "imlibrenderer.h"
+
+class DummyRenderer : public ImlibRenderer
+{
+ public:
+
+ DummyRenderer(int x, int y, int width, int height, string cfgPath, int utf, string thmPath)
+ : ImlibRenderer(x, y, width, height, cfgPath, utf, thmPath) { }
+
+ int init(int lazy) { return ImlibRenderer::init(lazy); }
+ void deinit() { }
+};
+
+//***************************************************************************
+#endif // __GTFT_DMYRENDERER_HPP__
diff --git a/imlibrenderer/fbrenderer/fbrenderer.c b/imlibrenderer/fbrenderer/fbrenderer.c
new file mode 100644
index 0000000..87e2cd5
--- /dev/null
+++ b/imlibrenderer/fbrenderer/fbrenderer.c
@@ -0,0 +1,375 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * fbrenderer.c - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: fbrenderer.c,v 1.4 2012/09/27 13:07:12 wendel Exp $
+ *
+ **/
+
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include <fbrenderer.h>
+
+#include <libavcodec/avcodec.h>
+
+#include <common.h>
+#include <setup.h>
+
+static unsigned char* frame_buffer;
+static struct fb_var_screeninfo fb_vinfo;
+
+typedef unsigned char UINT8;
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+FbRenderer::FbRenderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath)
+ : ImlibRenderer(x, y, width, height, cfgPath, utf, thmPath)
+{
+ fb_dev_name = 0;
+ initialized = no;
+}
+
+FbRenderer::~FbRenderer()
+{
+ deinit();
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void FbRenderer::deinit()
+{
+ if (!initialized)
+ return;
+
+ fb_orig_vinfo.xoffset = fb_vinfo.xoffset;
+ fb_orig_vinfo.yoffset = fb_vinfo.yoffset;
+
+ if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_orig_vinfo))
+ tell(4, "Can't reset original fb_var_screeninfo: %s", strerror(errno));
+
+ ::close(fb_dev_fd);
+
+ if (frame_buffer)
+ munmap(frame_buffer, fb_size);
+
+ initialized = no;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+#ifndef AV_PIX_FMT_RGB32
+# define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
+# define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+# define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
+#endif
+
+int FbRenderer::init(int lazy)
+{
+ asprintf(&fb_dev_name, "%s", devname);
+
+ // open framebuffer
+
+ tell(4 , "Using framebuffer device '%s'", fb_dev_name);
+
+ if ((fb_dev_fd = open(fb_dev_name, O_RDWR)) == -1)
+ {
+ tell(0, "Opening framebuffer device '%s' faild, error was '%s'",
+ fb_dev_name, strerror(errno));
+ return fail;
+ }
+
+ // read VScreen info from fb
+
+ if (ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo))
+ {
+ tell(0, "Can't get VSCREENINFO, %s", strerror(errno));
+ return fail;
+ }
+
+ // Save VScreen info and try to set virtual image
+
+ fb_orig_vinfo = fb_vinfo;
+ fb_vinfo.xres_virtual = fb_vinfo.xres;
+ fb_vinfo.yres_virtual = fb_vinfo.yres;
+
+ fb_vinfo.xoffset = 0;
+ fb_vinfo.yoffset = 0;
+
+ // fb_vinfo.bits_per_pixel = 32;
+
+ // write VScreen info
+
+ if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo))
+ tell(0, "Can't put VSCREENINFO, %s", strerror(errno));
+
+ // read VScreen info from fb (again) this will be the 'accepted' resolutions from the fb
+
+ if (ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo))
+ {
+ tell(0, "Can't get VSCREENINFO, %s", strerror(errno));
+ return fail;
+ }
+
+ // read VScreen info from fb
+
+ if (ioctl(fb_dev_fd, FBIOGET_FSCREENINFO, &fb_finfo))
+ {
+ tell(0, "Can't get FSCREENINFO, %s", strerror(errno));
+ return fail;
+ }
+
+ tell(0, "fb settings are (%d/%d) with a color depth of (%d)",
+ fb_vinfo.xres, fb_vinfo.yres, fb_vinfo.bits_per_pixel);
+
+ dspWidth = fb_vinfo.xres;
+ dspHeight = fb_vinfo.yres;
+
+ y_offset = fb_vinfo.yoffset;
+ fb_line_len = fb_finfo.line_length;
+ fb_size = fb_finfo.smem_len;
+ frame_buffer = 0;
+
+ switch (fb_vinfo.bits_per_pixel)
+ {
+ case 32: tell(4, "FB using 32 bit depth"); fb_type = AV_PIX_FMT_RGB32; break;
+ case 24: tell(4, "FB using 24 bit depth"); fb_type = AV_PIX_FMT_RGB24; break;
+ case 16: tell(4, "FB using 16 bit depth"); fb_type = AV_PIX_FMT_RGB565; break;
+ default: tell(4, "FB color depth not supported -> %i bits per pixel",
+ fb_vinfo.bits_per_pixel);
+ }
+
+ if ((frame_buffer = (unsigned char*)mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_dev_fd, 0)) == (unsigned char*)-1)
+ {
+ tell(0, "FB Can't mmap %s: %s", fb_dev_name, strerror(errno));
+ return fail;
+ }
+
+ initialized = yes;
+
+ ImlibRenderer::init(lazy);
+
+ return success;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+#ifndef use_asm
+
+void FbRenderer::fbdev_draw_32(unsigned char* frame, int force)
+{
+ memcpy(frame_buffer, frame, 4*fb_vinfo.yres*fb_vinfo.xres);
+}
+
+#else
+
+/* FIXME evil hack */
+
+static void fbdev32(unsigned char * frame)
+{
+ __asm__ __volatile__(
+
+ " pushl %%esi \n\t"
+ " pushl %%edi \n\t"
+ " pushl %%eax \n\t"
+ " pushl %%ecx \n\t"
+
+ " movl fb_vinfo,%%eax \n\t" // Height
+ " movl fb_vinfo+4,%%ecx \n\t" // width
+ " imul %%eax,%%ecx \n\t" // mul
+ " movl frame_buffer,%%edi \n\t" // fbdev mmap'd buffer
+ " movl 8(%%ebp), %%esi \n\t" // Imlib2 buffer (frame)
+ " rep movsl \n\t" // move all longs at a time (4 bytes)
+
+ " popl %%ecx \n\t"
+ " popl %%eax \n\t"
+ " popl %%edi \n\t"
+ " popl %%esi \n\t"
+
+ :/*no output*/:/*no input*/:"memory","cc");
+}
+
+void FbRenderer::fbdev_draw_32(unsigned char* frame, int force)
+{
+ fbdev32(frame);
+}
+
+#endif
+
+//***************************************************************************
+//
+//***************************************************************************
+
+#ifndef use_asm
+
+void FbRenderer::fbdev_draw_24(unsigned char* frame, int force)
+{
+ unsigned int i,a,b,c,x, out_offset = 0, in_offset = 0;
+
+ x = fb_vinfo.xres*4;
+
+ for (i = 0; i < fb_vinfo.yres; ++i)
+ {
+ for (a=0, b=0, c=0; a < fb_vinfo.xres; ++a, b+=3, c+=4)
+ {
+ frame_buffer[out_offset + b +0] = frame[in_offset + c +0];
+ frame_buffer[out_offset + b +1] = frame[in_offset + c +1];
+ frame_buffer[out_offset + b +2] = frame[in_offset + c +2];
+ }
+
+ out_offset += fb_line_len;
+ in_offset += x;
+ }
+}
+
+#else
+
+/* FIXME evil hack */
+
+static void fbdev24(unsigned char* frame)
+{
+ __asm__ __volatile__(
+
+ " pushl %%esi \n\t"
+ " pushl %%edi \n\t"
+ " pushl %%eax \n\t"
+ " pushl %%ebx \n\t"
+ " pushl %%ecx \n\t"
+ " pushl %%edx \n\t"
+
+ " movl fb_vinfo,%%eax \n\t" // fbdev mmap'd buffer
+ " movl fb_vinfo+4,%%edx \n\t" // fbdev mmap'd buffer
+ " imul %%eax,%%edx \n\t" // fbdev mmap'd buffer
+ " movl 8(%%ebp), %%esi \n\t" // Imlib2 buffer (frame)
+ " movl frame_buffer,%%edi \n\t" // fbdev mmap'd buffer
+
+ " .lop: \n\t"
+ " leal 3,%%ecx \n\t" // fbdev mmap'd buffer
+ " rep movsb \n\t" // move 3 bytes at a time
+ " inc %%esi \n\t" // increment one byte, bypass Alpha
+ " dec %%edx \n\t" // dec counter
+ " jnz .lop \n\t" // loop :)
+
+ " popl %%edx \n\t"
+ " popl %%ecx \n\t"
+ " popl %%ebx \n\t"
+ " popl %%eax \n\t"
+ " popl %%edi \n\t"
+ " popl %%esi \n\t"
+
+ :/*no output*/:/*no input*/:"memory","cc");
+}
+
+void FbRenderer::fbdev_draw_24(unsigned char* frame, int force)
+{
+ fbdev24(frame);
+}
+
+#endif
+
+//***************************************************************************
+// fbdev draw 16
+//***************************************************************************
+
+void FbRenderer::fbdev_draw_16(unsigned char* frame, int force)
+{
+ static unsigned short* tmp = 0;
+ static unsigned int size = fb_vinfo.yres * fb_vinfo.xres;
+ static unsigned short* fb = (unsigned short*)frame_buffer;
+
+ if (!tmp)
+ tmp = (unsigned short*)calloc(sizeof(unsigned short), size);
+
+ LogDuration ld("FbRenderer::fbdev_draw_16()");
+
+ unsigned char B, G, R;
+ unsigned int x, y;
+ unsigned short v;
+ unsigned int out_offset = 0, in_offset = 0;
+
+ for (y = 0; y < fb_vinfo.yres; y++)
+ {
+ for (x = 0; x < fb_vinfo.xres; x++, out_offset++, in_offset+=4)
+ {
+ R = (frame[in_offset + 2] >> 3) & 0x1f;
+ G = (frame[in_offset + 1] >> 2) & 0x3f;
+ B = (frame[in_offset + 0] >> 3) & 0x1f;
+
+ v = ((G << 5) | B) | ((R << 3) | (G >> 3)) << 8;
+
+ if (force || tmp[out_offset] != v)
+ {
+ tmp[out_offset] = v;
+ fb[out_offset] = v;
+ }
+ }
+ }
+}
+
+//***************************************************************************
+// Refresh
+//***************************************************************************
+
+void FbRenderer::refresh(int force)
+{
+ LogDuration ld("FbRenderer::refresh()");
+
+ // refresh
+
+ ImlibRenderer::refresh(force);
+
+ // copy to buffer
+
+ imlib_context_set_image(*pImageToDisplay);
+
+ if (GraphTFTSetup.flipOSD)
+ {
+ imlib_image_flip_vertical();
+ imlib_image_flip_horizontal();
+ }
+
+ UINT8* dataptr = (UINT8*)imlib_image_get_data_for_reading_only();
+
+ tell(4, "copy image with a depth of (%d) to framebuffer",
+ fb_vinfo.bits_per_pixel);
+
+ switch (fb_vinfo.bits_per_pixel)
+ {
+ case 16 : fbdev_draw_16(dataptr, force); break;
+ case 24 : fbdev_draw_24(dataptr, force); break;
+ case 32 : fbdev_draw_32(dataptr, force); break;
+
+ default : tell(0, "fbdevout.c: color depth not supported "
+ "-> %i bits per pixel", fb_vinfo.bits_per_pixel);
+ }
+
+#ifdef PVRFB
+
+ struct ivtvfb_ioctl_dma_host_to_ivtv_args prep;
+ prep.source = frame_buffer;
+ prep.dest_offset = 0;
+ prep.count = width * height * 4;
+ ioctl(fb_dev_fd, IVTVFB_IOCTL_PREP_FRAME, &prep);
+
+#endif
+}
+
+void FbRenderer::clear()
+{
+ ImlibRenderer::clear();
+}
diff --git a/imlibrenderer/fbrenderer/fbrenderer.h b/imlibrenderer/fbrenderer/fbrenderer.h
new file mode 100644
index 0000000..9de636e
--- /dev/null
+++ b/imlibrenderer/fbrenderer/fbrenderer.h
@@ -0,0 +1,65 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * fbrenderer.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: fbrenderer.h,v 1.4 2012/09/27 13:07:12 wendel Exp $
+ *
+ **/
+
+//The most part of this Code is from the MMS V.2 Project:
+
+#ifndef __GTFT_FBRENDERER_HPP
+#define __GTFT_FBRENDERER_HPP
+
+#include <linux/fb.h>
+
+#include <imlibrenderer.h>
+
+//***************************************************************************
+//
+//***************************************************************************
+
+class FbRenderer : public ImlibRenderer
+{
+
+ public:
+
+ FbRenderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath);
+ ~FbRenderer();
+
+ int init(int lazy);
+ void deinit();
+
+ void refresh(int force = no);
+ void clear();
+
+ private:
+
+ void fbdev_draw_32(unsigned char* frame, int force);
+ void fbdev_draw_24(unsigned char* frame, int force);
+ void fbdev_draw_16(unsigned char* frame, int force);
+
+ // data
+
+ char* fb_dev_name;
+ int initialized;
+ Imlib_Image _resized;
+
+ int fb_dev_fd;
+ int fb_type;
+ size_t fb_size;
+ int fb_line_len;
+ int y_offset;
+
+ struct fb_var_screeninfo fb_orig_vinfo;
+ struct fb_fix_screeninfo fb_finfo;
+};
+
+#endif // __GTFT_FBRENDERER_H
diff --git a/imlibrenderer/imlib.cc b/imlibrenderer/imlib.cc
new file mode 100644
index 0000000..15148a4
--- /dev/null
+++ b/imlibrenderer/imlib.cc
@@ -0,0 +1,81 @@
+//
+// Test
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <Imlib2.h>
+
+int text(int x, int y, const char* text, const char* fontName, int size);
+Imlib_Image theImage;
+
+//-----------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------
+
+int main(int argc, char** argv)
+{
+ if (argc != 4)
+ {
+ printf("Usage: imlib <text> <font-with-path> <fontsize>\n");
+ return -1;
+ }
+
+ theImage = imlib_create_image(600, 100);
+ imlib_context_set_image(theImage);
+
+ text(10, 10, argv[1], argv[2], atoi(argv[3]));
+
+ text(10, 50, "Test1 \n Test2", argv[2], atoi(argv[3]));
+
+ imlib_image_set_format("png");
+ imlib_save_image("test.png");
+
+ imlib_free_image();
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------
+
+int text(int x, int y, const char* text, const char* fontName, int size)
+{
+ char* font;
+ Imlib_Font theFont;
+ int height, width;
+
+ if (!text || !fontName || !size)
+ return -1;
+
+ asprintf(&font, "%s/%d", fontName, size);
+
+ if (!(theFont = imlib_load_font(font)))
+ {
+ printf("Loading font '%s' failed\n", font);
+ free(font);
+
+ return -1;
+ }
+
+ imlib_context_set_font(theFont);
+ imlib_context_set_image(theImage);
+ imlib_context_set_color(150, 150, 150, 255);
+
+ imlib_get_text_size(text, &width, &height);
+
+ printf("width (%d); height (%d)\n", width, height);
+ printf("Ascent=%d/%d Descent=%d/%d\n", imlib_get_font_ascent(),
+ imlib_get_maximum_font_ascent(), imlib_get_font_descent(),
+ imlib_get_maximum_font_descent());
+
+ imlib_text_draw(x, y, text);
+
+ imlib_free_font();
+ free(font);
+
+ return 0;
+}
+
diff --git a/imlibrenderer/imlibrenderer.c b/imlibrenderer/imlibrenderer.c
new file mode 100644
index 0000000..7720594
--- /dev/null
+++ b/imlibrenderer/imlibrenderer.c
@@ -0,0 +1,906 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * imlibrenderer.c - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2013 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: imlibrenderer.c,v 1.10 2012/09/27 13:07:11 wendel Exp $
+ *
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include <string>
+#include <utility>
+#include <iostream>
+
+#include <jpeglib.h>
+
+#include "imlibrenderer.h"
+#include "theme.h"
+#include "common.h"
+
+using std::string;
+using std::cout;
+using std::endl;
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+ImlibRenderer::ImlibRenderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath)
+ : Renderer(x, y, width, height, cfgPath, utf, thmPath)
+{
+// Imlib_Context ctx = imlib_context_new();
+// imlib_context_push(ctx);
+
+ _render_image = 0;
+ _cur_image = 0;
+ pImageToDisplay = 0;
+}
+
+ImlibRenderer::~ImlibRenderer()
+{
+ deinit();
+}
+
+//***************************************************************************
+// init / deinit
+//***************************************************************************
+
+int ImlibRenderer::init(int lazy)
+{
+ if (_render_image || _cur_image)
+ deinit();
+
+ // new image
+
+ imlib_set_color_usage(256);
+
+ _cur_image = imlib_create_image(themeWidth, themeHeight);
+ _render_image = imlib_create_image(dspWidth, dspHeight);
+ imlib_context_set_image(_cur_image);
+
+ // cache
+
+ imlib_set_cache_size(16 * 1024 * 1024);
+ imlib_set_font_cache_size(4 * 1024 * 1024);
+
+ return success;
+}
+
+void ImlibRenderer::deinit()
+{
+ pImageToDisplay = 0;
+
+ if (_render_image)
+ {
+ imlib_context_set_image(_render_image);
+ imlib_free_image();
+ _render_image = 0;
+ }
+
+ if (_cur_image)
+ {
+ imlib_context_set_image(_cur_image);
+ imlib_free_image();
+ _cur_image = 0;
+ }
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void ImlibRenderer::flushCache()
+{
+ // flush the font cache
+
+ imlib_flush_font_cache();
+
+ // and the image cache
+
+ imlib_set_cache_size(0);
+ imlib_set_cache_size(16 * 1024 * 1024);
+}
+
+//***************************************************************************
+// Set Font Path
+//***************************************************************************
+
+void ImlibRenderer::setFontPath(string fntPath)
+{
+ int e, s;
+ e = s = 0;
+ string p;
+ int count;
+ char* path;
+ const char** list;
+
+ // first clear imlibs font path
+
+ list = (const char**)imlib_list_font_path(&count);
+
+ for (int i = 0; i < count; i++)
+ {
+ tell(3, "Info: Removing '%s' from font path", list[i]);
+ imlib_remove_path_from_font_path(list[i]);
+ }
+
+ imlib_list_font_path(&count);
+
+ // add all configured font paths
+
+ tell(0, "Info: Font path configured to '%s'", fntPath.c_str());
+
+ do
+ {
+ e = fntPath.find(':', s);
+ p = fntPath.substr(s, e == na ? fntPath.length()-s : e-s);
+
+ if (p[0] != '/')
+ {
+ // make path relative to the themes directory
+
+ asprintf(&path, "%s/themes/%s/%s",
+ confPath.c_str(), themePath.c_str(), p.c_str());
+ }
+ else
+ {
+ asprintf(&path, "%s", p.c_str());
+ }
+
+ tell(0, "Info: Adding font path '%s'", path);
+
+ if (*path && fileExists(path))
+ imlib_add_path_to_font_path(path);
+ else
+ tell(0, "Info: Font path '%s' not found, ignoring", path);
+
+ free(path);
+ s = e+1;
+
+ } while (s > 0);
+
+
+ // at least add the default path
+
+ asprintf(&path, "%s/graphtftng/fonts/", confPath.c_str());
+ tell(0, "Info: Adding font path '%s'", path);
+ imlib_add_path_to_font_path(path);
+ free(path);
+}
+
+//***************************************************************************
+// Refresh
+//***************************************************************************
+
+void ImlibRenderer::refresh(int force)
+{
+ LogDuration ld("ImlibRenderer::refresh()");
+
+ if (!_cur_image)
+ return ;
+
+ // resize only if needed !
+
+ if (xOffset || yOffset || xBorder || yBorder
+ || themeWidth != dspWidth || themeHeight != dspHeight)
+ {
+ tell(2, "scale image from (%d/%d) to (%d/%d)",
+ themeWidth, themeHeight,
+ dspWidth - xOffset - (2 * xBorder),
+ dspHeight - yOffset - (2 * yBorder));
+
+ imlib_context_set_image(_render_image);
+
+ imlib_blend_image_onto_image(_cur_image, 0,
+ 0, 0, themeWidth, themeHeight,
+ xOffset + xBorder, yOffset + yBorder,
+ dspWidth - xOffset - (2 * xBorder),
+ dspHeight - yOffset - (2 * yBorder));
+
+ pImageToDisplay = &_render_image;
+ }
+ else
+ {
+ pImageToDisplay = &_cur_image;
+ }
+}
+
+void ImlibRenderer::clear()
+{
+ if (!_cur_image)
+ return ;
+
+ // clear the current image
+
+ imlib_context_set_image(_cur_image);
+ imlib_free_image();
+ _cur_image = imlib_create_image(themeWidth, themeHeight);
+ imlib_context_set_image(_cur_image);
+}
+
+//***************************************************************************
+// Image
+//***************************************************************************
+
+void ImlibRenderer::image(const char* fname,
+ int x, int y,
+ int width, int height,
+ bool fit, bool aspectRatio,
+ int orientation)
+{
+ Imlib_Image new_image;
+ int imgWidth = 0;
+ int imgHeight = 0;
+ std::ostringstream path;
+ int areaWidth = width;
+ int areaHeight = height;
+ Imlib_Load_Error err;
+ int rotate = na;
+ int hflip = no;
+
+ if (!_cur_image)
+ return ;
+
+ if (x == na) x = 0;
+ if (y == na) y = 0;
+
+ if (fname[0] == '/')
+ path << fname;
+ else
+ path << confPath << "/themes/" << themePath << "/" << fname;
+
+ if (!fileExists(path.str().c_str()))
+ {
+ tell(0, "Image '%s' not found", path.str().c_str());
+ return ;
+ }
+
+ new_image = imlib_load_image_with_error_return(path.str().c_str(), &err);
+
+ if (!new_image)
+ {
+ tell(0, "The image '%s' could not be loaded, error was '%s'",
+ path.str().c_str(), strerror(err));
+
+ return ;
+ }
+
+ imlib_context_set_image(new_image);
+
+ switch (orientation)
+ {
+ case 0: rotate = na; hflip = no; break; // 0: unknon
+ case 1: rotate = na; hflip = no; break; // 1: okay
+ case 2: rotate = na; hflip = yes; break; // 2: gespiegelt (not implemented yet)
+ case 3: rotate = 2; hflip = no; break; // 3: auf dem Kopf
+ case 4: rotate = 2; hflip = yes; break; // 4: auf dem Kopf (und gespiegelt -> not implemented yet)
+ case 5: rotate = 1; hflip = yes; break; // 5: 90° links (und gespiegelt -> not implemented yet)
+ case 6: rotate = 1; hflip = no; break; // 6: 90° links
+ case 7: rotate = 3; hflip = yes; break; // 7: 90° rechts (und gespiegelt -> not implemented yet)
+ case 8: rotate = 3; hflip = no; break; // 8: 90° rechts
+ }
+
+ if (rotate != na)
+ imlib_image_orientate(rotate);
+
+ if (hflip)
+ imlib_image_flip_horizontal();
+
+ imgWidth = imlib_image_get_width();
+ imgHeight = imlib_image_get_height();
+
+ if (strstr(fname, "chg_"))
+ imlib_image_set_changes_on_disk();
+
+ imlib_context_set_image(_cur_image);
+
+ if (fit)
+ {
+ if (aspectRatio)
+ {
+ double ratio = (double)imgWidth / (double)imgHeight;
+
+ if ((double)width/(double)imgWidth < (double)height/(double)imgHeight)
+ {
+ height = (int)((double)width / ratio);
+ y += (areaHeight-height) / 2;
+ }
+ else
+ {
+ width = (int)((double)height * ratio);
+ x += (areaWidth-width) / 2;
+ }
+ }
+
+ imlib_blend_image_onto_image(new_image, 0, 0, 0,
+ imgWidth, imgHeight, x, y,
+ width, height);
+ }
+ else
+ {
+ imlib_blend_image_onto_image(new_image, 0, 0, 0,
+ imgWidth, imgHeight, x, y,
+ imgWidth, imgHeight);
+ }
+
+ imlib_context_set_image(new_image);
+ imlib_free_image();
+ imlib_context_set_image(_cur_image);
+}
+
+//***************************************************************************
+// Image Part
+//***************************************************************************
+
+void ImlibRenderer::imagePart(const char* fname, int x, int y,
+ int width, int height)
+{
+ Imlib_Image new_image;
+ std::ostringstream path;
+ Imlib_Load_Error err;
+
+ if (!_cur_image)
+ return ;
+
+ if (x == na) x = 0;
+ if (y == na) y = 0;
+
+ if (fname[0] == '/')
+ path << fname;
+ else
+ path << confPath << "/themes/" << themePath << "/" << fname;
+
+ if (!fileExists(path.str().c_str()))
+ {
+ tell(0, "Image '%s' not found", path.str().c_str());
+ return ;
+ }
+
+ // new_image = imlib_load_image(path.str().c_str());
+
+ new_image = imlib_load_image_with_error_return(path.str().c_str(), &err);
+
+ if (!new_image)
+ {
+ tell(0, "The image '%s' could not be loaded, error was '%s'",
+ path.str().c_str(), strerror(err));
+
+ return ;
+ }
+
+ imlib_context_set_image(_cur_image);
+
+ imlib_blend_image_onto_image(new_image, 0,
+ x, y, width, height,
+ x, y, width, height);
+
+ imlib_context_set_image(new_image);
+ imlib_free_image();
+ imlib_context_set_image(_cur_image);
+}
+
+//***************************************************************************
+// Text Width Of
+//***************************************************************************
+
+int ImlibRenderer::textWidthOf(const char* text, const char* fontName,
+ int fontSize, int& height)
+{
+ Imlib_Font font;
+ int width = 20;
+ char* fontNameSize = 0;
+
+ fontSize = fontSize ? fontSize : 24;
+ height = fontSize * (5/3);
+
+ if (!_cur_image)
+ return 0;
+
+ if (!fontName || !text || !strlen(text))
+ return 0;
+
+ asprintf(&fontNameSize, "%s/%d", fontName, fontSize);
+ font = imlib_load_font(fontNameSize);
+ free(fontNameSize);
+
+ if (font)
+ {
+ imlib_context_set_font(font);
+ imlib_context_set_image(_cur_image);
+ imlib_get_text_size(text, &width, &height);
+ imlib_free_font();
+ }
+ else
+ tell(1, "The font '%s' could not be loaded.", fontName);
+
+ return width;
+}
+
+//***************************************************************************
+// Char Width Of
+//***************************************************************************
+
+int ImlibRenderer::charWidthOf(const char* fontName, int fontSize)
+{
+ Imlib_Font font;
+ int width = 20;
+ int height = 20;
+ char* fontNameSize = 0;
+
+ if (!_cur_image)
+ return 30;
+
+ const char* text = "We need here something like a "
+ "representive sentence WITH SOME UPPER CASE LETTERS";
+
+ if (!fontName)
+ {
+ imlib_get_text_size(text, &width, &height);
+ return width / strlen(text);
+ }
+
+ asprintf(&fontNameSize, "%s/%d", fontName, fontSize);
+ font = imlib_load_font(fontNameSize);
+ free(fontNameSize);
+
+ if (font)
+ {
+ imlib_context_set_font(font);
+ imlib_context_set_image(_cur_image);
+ imlib_get_text_size(text, &width, &height);
+ imlib_free_font();
+
+ width /= clen(text);
+ }
+ else
+ tell(1, "The font '%s' could not be loaded.", fontName);
+
+ return width;
+}
+
+//***************************************************************************
+// Line Count
+//***************************************************************************
+
+int ImlibRenderer::lineCount(const char* text, const char* font_name,
+ int size, int width)
+{
+ int count;
+ int lineHeight, textWidth, dummy;
+ string line;
+ string tmp = text;
+ Imlib_Font font;
+ int currentWidth;
+ int blankWidth, dotsWidth;
+ string::size_type pos = 0;
+ char* fontNameSize = 0;
+
+ if (!_cur_image)
+ return 1;
+
+ // load font
+
+ asprintf(&fontNameSize, "%s/%d", font_name, size);
+ font = imlib_load_font(fontNameSize);
+ free(fontNameSize);
+
+ if (width <= 0)
+ width = themeWidth; // ;)
+
+ imlib_context_set_font(font);
+ imlib_context_set_image(_cur_image);
+
+ imlib_get_text_size(tmp.c_str(), &textWidth, &lineHeight);
+ imlib_get_text_size(" ", &blankWidth, &dummy);
+ imlib_get_text_size("...", &dotsWidth, &dummy);
+
+ for (count = 1; pos < tmp.length(); count++)
+ {
+ string token;
+ line = "";
+
+ currentWidth = 0;
+
+ // fill next line ...
+
+ while (pos < tmp.length())
+ {
+ int tokenWidth;
+ int lf = no;
+
+ string::size_type pA = tmp.find_first_of(" \n", pos);
+
+ if (pA != string::npos && tmp[pA] == '\n')
+ {
+ lf = yes;
+ tmp[pA] = ' ';
+ }
+
+ token = tmp.substr(pos, pA - pos);
+
+ if (token == "")
+ {
+ line += " ";
+ pos++;
+
+ if (lf)
+ break;
+ else
+ continue;
+ }
+
+ imlib_get_text_size(token.c_str(), &tokenWidth, &dummy);
+
+ if (currentWidth + (currentWidth ? blankWidth : 0) + tokenWidth > width)
+ {
+ // passt nicht mehr ganz rein
+
+ if (!line.length())
+ {
+ // alleinstehendes Wort -> noch zur Zeile rechnen
+
+ pos += token.length();
+ }
+
+ break;
+ }
+ else
+ {
+ // passt noch rein
+
+ line = line + token;
+ imlib_get_text_size(line.c_str(), &currentWidth, &dummy);
+ pos += token.length();
+
+ if (lf)
+ break;
+ }
+ }
+ }
+
+ imlib_free_font();
+
+ return count-1;
+}
+
+//***************************************************************************
+// Draw Text
+//***************************************************************************
+
+int ImlibRenderer::text(const char* text,
+ const char* font_name, int size,
+ int align, int x, int y,
+ p_rgba rgba, // int r, int g, int b,
+ int width, int height,
+ int lines, int dots, int skipLines)
+{
+ string line;
+ string tmp;
+ int lineHeight, textWidth, dummy;
+ int blankWidth;
+ int dotsWidth;
+ string::size_type pos;
+ int currentWidth;
+ Imlib_Font font;
+ char* fontNameSize = 0;
+
+ if (x == na) x = 0;
+ if (y == na) y = 0;
+
+ if (!_cur_image)
+ return 0;
+
+ if (Str::isEmpty(text))
+ return 0;
+
+ if (utf8)
+ {
+ const int maxBuf = 10000;
+ char out[maxBuf+TB];
+
+ if (toUTF8(out, maxBuf, text) == success)
+ tmp = out;
+ else
+ tmp = text;
+ }
+ else
+ tmp = text;
+
+ // load font
+
+ asprintf(&fontNameSize, "%s/%d", font_name, size);
+ font = imlib_load_font(fontNameSize);
+ free(fontNameSize);
+
+ if (width <= 0)
+ width = themeWidth; // ;)
+
+ imlib_context_set_font(font);
+ imlib_context_set_image(_cur_image);
+ imlib_context_set_color(rgba[rgbR], rgba[rgbG], rgba[rgbB], 255);
+
+ imlib_get_text_size(tmp.c_str(), &textWidth, &lineHeight);
+ imlib_get_text_size(" ", &blankWidth, &dummy);
+ imlib_get_text_size("...", &dotsWidth, &dummy);
+
+ if (!lines)
+ lines = height / lineHeight;
+
+ if (lines <= 0)
+ lines = 1;
+
+ pos = 0;
+ int tl = 1;
+
+ for (int l = 1; l <= lines && pos < tmp.length(); l++, tl++)
+ {
+ int yPos = y + lineHeight * (l-1);
+ string token;
+ line = "";
+
+ currentWidth = 0;
+
+ while (pos < tmp.length())
+ {
+ int tokenWidth;
+ int lf = no;
+
+ string::size_type pA = tmp.find_first_of(" \n", pos);
+
+ if (pA != string::npos && tmp[pA] == '\n')
+ {
+ lf = yes;
+ tmp[pA] = ' ';
+ }
+
+ token = tmp.substr(pos, pA - pos);
+
+ if (token == "")
+ {
+ line += " ";
+ pos++;
+
+ if (lf)
+ break;
+ else
+ continue;
+ }
+
+ imlib_get_text_size(token.c_str(), &tokenWidth, &dummy);
+
+ if (currentWidth + (currentWidth ? blankWidth : 0) + tokenWidth > width)
+ {
+ // passt nicht mehr ganz rein
+
+ if (!line.length() || l == lines)
+ {
+ // alleinstehendes Wort oder letzte Zeile,
+ // daher Abgeschnitten anzeigen
+
+ unsigned int i = 0;
+
+ while (currentWidth + (dots ? dotsWidth : 0) < width && i <= token.length())
+ {
+ line += token.substr(i++, 1);
+ imlib_get_text_size(line.c_str(), &currentWidth, &dummy);
+ }
+
+ // einer zuviel !
+
+ line = line.substr(0, line.length()-1);
+
+ if (dots)
+ {
+ if (currentWidth + dotsWidth > width)
+ line = line.substr(0, line.length()-1);
+
+ line += "...";
+ }
+
+ pos += token.length();
+ }
+
+ break;
+ }
+ else
+ {
+ // passt noch rein
+
+ // currentWidth += blankWidth + tokenWidth; // nicht genau
+ // -> so ist's besser
+
+ line = line + token;
+ imlib_get_text_size(line.c_str(), &currentWidth, &dummy);
+ pos += token.length();
+
+ if (lf)
+ break;
+ }
+ }
+
+ if (skipLines && tl <= skipLines)
+ {
+ l--;
+ continue;
+ }
+
+ if (align != 0)
+ imlib_get_text_size(line.c_str(), &textWidth, &lineHeight);
+
+ if (align == 0) // left
+ imlib_text_draw(x, yPos, line.c_str());
+ else if (align == 1) // center
+ imlib_text_draw(x + (width - textWidth) / 2, yPos, line.c_str());
+ else // right
+ imlib_text_draw(x + width - textWidth -2, yPos, line.c_str());
+ }
+
+ imlib_free_font();
+
+ return 0;
+}
+
+//***************************************************************************
+// Draw Rectangle
+//***************************************************************************
+
+void ImlibRenderer::rectangle(int x, int y,
+ int width, int height,
+ p_rgba rgba) // int r, int g, int b, int alpha)
+{
+ if (x == na) x = 0;
+ if (y == na) y = 0;
+
+ if (!_cur_image)
+ return ;
+
+ imlib_context_set_image(_cur_image);
+ imlib_context_set_color(rgba[rgbR], rgba[rgbG], rgba[rgbB], rgba[rgbA]);
+ imlib_image_fill_rectangle(x, y, width, height);
+}
+
+//***************************************************************************
+// Dump Image To File
+//***************************************************************************
+
+void ImlibRenderer::dumpImage2File(const char* fname, int dumpWidth,
+ int dumpHeight, const char* aPath)
+{
+ std::ostringstream path;
+ const char* format = 0;
+
+ if (!_cur_image)
+ return ;
+
+ if (aPath && *aPath)
+ path << aPath;
+ else if (*fname != '.' && *fname != '/')
+ path << "/tmp";
+
+ if (strchr(fname, '.'))
+ format = strchr(fname, '.') + 1;
+
+ // check if local directory structure exist, else create it.
+
+ if (!fileExists(path.str().c_str()))
+ mkdir(path.str().c_str(), 0777);
+
+ // get width and heigt
+
+ imlib_context_set_image(_cur_image);
+ int width = imlib_image_get_width();
+ int height = imlib_image_get_height();
+
+ // create image
+
+ Imlib_Image new_image = imlib_create_image(dumpWidth, dumpHeight);
+
+ imlib_context_set_image(new_image);
+ imlib_blend_image_onto_image(_cur_image, 0,
+ 0, 0, width, height,
+ 0, 0, dumpWidth, dumpHeight);
+
+ tell(1, "DUMP: From (%d/%d) to (%d/%d) '%s'",
+ width, height, dumpWidth, dumpHeight, fname);
+
+ // save the image
+
+ imlib_image_set_format(format && *format ? format : "png");
+ path << "/" << fname;
+ imlib_save_image(path.str().c_str());
+
+ imlib_free_image();
+ imlib_context_set_image(_cur_image);
+}
+
+//***************************************************************************
+// To JPEG
+//***************************************************************************
+
+#ifndef WITH_TCPCOM
+
+long ImlibRenderer::toJpeg(unsigned char*& buffer, int quality)
+{
+ return 0;
+}
+
+#else
+
+long ImlibRenderer::toJpeg(unsigned char*& buffer, int quality)
+{
+ struct jpeg_compress_struct cinfo = { 0 };
+ struct jpeg_error_mgr jerr;
+ DATA32* ptr;
+ DATA8* buf;
+ unsigned long size;
+
+ if (!_cur_image)
+ return 0;
+
+ buffer = 0;
+ size = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jpeg_create_compress(&cinfo);
+ jpeg_mem_dest(&cinfo, &buffer, &size);
+
+ cinfo.image_width = imlib_image_get_width();
+ cinfo.image_height = imlib_image_get_height();
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // get data pointer
+
+ if (!(ptr = imlib_image_get_data_for_reading_only()))
+ return 0;
+
+ // allocate a small buffer to convert image data */
+
+ buf = (DATA8*)malloc(imlib_image_get_width() * 3 * sizeof(DATA8));
+
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ // convert scanline from ARGB to RGB packed
+
+ for (int j = 0, i = 0; i < imlib_image_get_width(); i++)
+ {
+ buf[j++] = ((*ptr) >> 16) & 0xff;
+ buf[j++] = ((*ptr) >> 8) & 0xff;
+ buf[j++] = ((*ptr)) & 0xff;
+
+ ptr++;
+ }
+
+ // write scanline
+
+ jpeg_write_scanlines(&cinfo, (JSAMPROW*)(&buf), 1);
+ }
+
+ free(buf);
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ return size;
+}
+#endif
diff --git a/imlibrenderer/imlibrenderer.h b/imlibrenderer/imlibrenderer.h
new file mode 100644
index 0000000..5ef79ab
--- /dev/null
+++ b/imlibrenderer/imlibrenderer.h
@@ -0,0 +1,76 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * imlibrenderer.h
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2014 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 __GTFT_IMLIBRENDERER_HPP
+#define __GTFT_IMLIBRENDERER_HPP
+
+#include "renderer.h"
+
+#ifndef WITH_X
+# define X_DISPLAY_MISSING
+#endif
+
+#include <Imlib2.h>
+
+#undef Status
+
+//***************************************************************************
+// Class ImlibRenderer
+//***************************************************************************
+
+class ImlibRenderer : public Renderer
+{
+ public:
+
+ ImlibRenderer(int x, int y, int width, int height, string cfgPath, int utf, string thmPath);
+ virtual ~ImlibRenderer();
+
+ int init(int lazy);
+ void deinit();
+ void flushCache();
+ void setFontPath(string fntPath);
+
+ long toJpeg(unsigned char*& buffer, int quality);
+
+ virtual void refresh(int force = no);
+ virtual void clear();
+
+ int textWidthOf(const char* text, const char* fontName, int fontSize, int& height);
+ int charWidthOf(const char* fontName = 0, int fontSize = 0);
+
+ void image(const char* fname, int x, int y,
+ int coverwidth, int coverheight,
+ bool fit = no, bool aspectRatio = no,
+ int orientation = 1);
+ void imagePart(const char* fname, int x, int y, int width, int height);
+
+ int text(const char* text, const char* font_name, int size, int align,
+ int x, int y,
+ p_rgba rgba, // int r, int g, int b,
+ int width, int height,int lines, int dots = 0, int skipLines = 0);
+
+ int lineCount(const char* text, const char* font_name, int size, int width);
+
+ void rectangle(int x, int y, int width, int height, p_rgba rgba);
+
+ void dumpImage2File(const char* fname, int dumpWidth, int dumpHeight, const char* aPath = 0);
+
+ protected:
+
+ Imlib_Image _cur_image; // the image you're working on
+ Imlib_Image _render_image; // the image buffer for scaling
+ Imlib_Image* pImageToDisplay; // pointer to the image for displaying
+};
+
+//***************************************************************************
+#endif // __GTFT_IMLIBRENDERER_H
diff --git a/imlibrenderer/xrenderer/xrenderer.c b/imlibrenderer/xrenderer/xrenderer.c
new file mode 100644
index 0000000..7dde32b
--- /dev/null
+++ b/imlibrenderer/xrenderer/xrenderer.c
@@ -0,0 +1,296 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File xrenderer.c
+// Date 16.02.14 - 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 XRenderer
+//***************************************************************************
+
+#include <stdio.h>
+
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include <common.h>
+#include "xrenderer.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+XRenderer::XRenderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath)
+ : ImlibRenderer(x, y, width, height, cfgPath, utf, thmPath)
+{
+ disp = 0;
+ win = 0;
+ pix = 0;
+ screen = 0;
+ initialized = no;
+}
+
+XRenderer::~XRenderer()
+{
+ deinit();
+}
+
+void XRenderer::setDisplaySize(int width, int height)
+{
+ if (dspWidth != width || dspHeight != height)
+ {
+ cMutexLock lock(&mutex);
+
+ tell(0, "Changing display size to %dx%d", width, height);
+ deinit();
+ dspWidth = width;
+ dspHeight = height;
+ init(no);
+ }
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int XRenderer::init(int lazy)
+{
+ const char* appName = "graphtft-fe";
+
+ Visual* vis;
+ Colormap cm;
+ int depth;
+ XClassHint* classHint;
+
+ cMutexLock lock(&mutex);
+
+ ImlibRenderer::init(lazy);
+
+ if (Str::isEmpty(devname))
+ {
+ tell(0, "Can't open display 'null' continue in detached mode!");
+ return fail;
+ }
+
+ // init X
+
+ if (!(disp = XOpenDisplay(devname)))
+ {
+ tell(0, "Can't open display '%s' continue in detached mode", devname);
+ return lazy ? success : fail;
+ }
+
+ if (!XInitThreads())
+ tell(0, "Can't initialize X11 thread support");
+
+ // init display
+
+ screen = DefaultScreen(disp);
+ vis = DefaultVisual(disp, screen);
+ depth = DefaultDepth(disp, screen);
+ cm = DefaultColormap(disp, screen);
+
+ // create simple window
+
+ win = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
+ 0, depth, dspWidth, dspHeight, 0, 0, 0);
+
+ XSetStandardProperties(disp, win, appName, appName, None, 0, 0, 0);
+ XStoreName(disp, win, appName);
+
+ /* set the name and class hints for the window manager to use */
+
+ classHint = XAllocClassHint();
+ classHint->res_name = (char*)appName;
+ classHint->res_class = (char*)appName;
+ XSetClassHint(disp, win, classHint);
+ XFree(classHint);
+
+ 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, dspWidth, dspHeight, DefaultDepthOfScreen(scn));
+
+ imlib_context_set_dither(1); // 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(pix); // and drawable we are using
+
+// {
+// XWindowAttributes windowAttributes;
+// XGetWindowAttributes(disp, win, &windowAttributes);
+// int w = windowAttributes.width;
+// int h = windowAttributes.height;
+
+// tell(0, "Created window with (%d/%d) got (%d/%d)", dspWidth, dspHeight, w, h);
+// }
+
+ tell(0, "Connection to '%s' established", devname);
+ initialized = yes;
+
+ return success;
+}
+
+//***************************************************************************
+// Deinit
+//***************************************************************************
+
+void XRenderer::deinit()
+{
+ cMutexLock lock(&mutex);
+
+ ImlibRenderer::deinit();
+
+ if (initialized)
+ {
+ imlib_context_disconnect_display();
+
+ if (win) XDestroyWindow(disp, win);
+ if (pix) XFreePixmap(disp, pix);
+
+ XFlush(disp);
+
+ if (XCloseDisplay(disp))
+ tell(0, "Error closing display");
+
+ disp = 0;
+ win = 0;
+ pix = 0;
+ screen = 0;
+ initialized = no;
+ }
+
+ tell(0, "Connection to '%s' closed, now detached", devname);
+}
+
+//***************************************************************************
+// attach / detach
+//***************************************************************************
+
+int XRenderer::attach(const char* disp)
+{
+ if (initialized)
+ {
+ tell(0, "Already attached, trying detach first");
+ detach();
+ }
+
+ if (!Str::isEmpty(disp))
+ setDevName(disp);
+
+ tell(0, "Try to attach to '%s'", devname);
+
+ return init(no);
+}
+
+int XRenderer::detach()
+{
+ if (!initialized)
+ {
+ tell(0, "Already detached");
+ return done;
+ }
+
+ tell(0, "Detach from '%s'", devname);
+ deinit();
+
+ return success;
+}
+
+//***************************************************************************
+// X Pending
+//***************************************************************************
+
+int XRenderer::xPending()
+{
+ cMutexLock lock(&mutex);
+ XEvent ev;
+
+ if (!initialized)
+ return success;
+
+ while (XPending(disp))
+ XNextEvent(disp, &ev);
+
+ return success;
+}
+
+//***************************************************************************
+// Refresh
+//***************************************************************************
+
+void XRenderer::refresh(int force)
+{
+ cMutexLock lock(&mutex);
+
+ if (!force || !initialized) // don't need 'total' refresh every time since we refresh the areas
+ return;
+
+ tell(2, "refresh all");
+
+ refreshPixmap();
+
+ XClearWindow(disp, win);
+}
+
+void XRenderer::refreshArea(int x, int y, int width, int height)
+{
+ cMutexLock lock(&mutex);
+
+ if (!initialized)
+ return ;
+
+ refreshPixmap();
+
+ // scale coordinates from theme to display size
+
+ double xScale = (double)dspWidth / (double)themeWidth;
+ double yScale = (double)dspHeight / (double)themeHeight;
+
+ int xDsp = x * xScale;
+ int yDsp = y * yScale;
+ int widthDsp = width * xScale;
+ int heightDsp = height * yScale;
+
+ tell(3, "Refresh area at %d/%d (%d/%d); scaled to %d/%d (%d/%d); scale %.2f/%.2f; dspSize (%d/%d)",
+ x, y, width, height, xDsp, yDsp, widthDsp, heightDsp,
+ xScale, yScale, dspWidth, dspHeight);
+
+ XClearArea(disp, win, xDsp, yDsp, widthDsp, heightDsp, false);
+}
+
+void XRenderer::refreshPixmap()
+{
+ // XWindowAttributes windowAttributes;
+ // XGetWindowAttributes(disp, win, &windowAttributes);
+ // int width = windowAttributes.width;
+ // int height = windowAttributes.height;
+
+ imlib_context_set_image(_cur_image);
+ imlib_render_image_on_drawable_at_size(0, 0, dspWidth, dspHeight);
+
+ XSetWindowBackgroundPixmap(disp, win, pix);
+}
+
+void XRenderer::clear()
+{
+ if (!initialized)
+ return ;
+
+ cMutexLock lock(&mutex);
+ ImlibRenderer::clear();
+}
diff --git a/imlibrenderer/xrenderer/xrenderer.h b/imlibrenderer/xrenderer/xrenderer.h
new file mode 100644
index 0000000..1add4d1
--- /dev/null
+++ b/imlibrenderer/xrenderer/xrenderer.h
@@ -0,0 +1,54 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File xrenderer.h
+// Date 09.11.14 - 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 XRenderer
+//***************************************************************************
+
+#ifndef __DXRENDERER_H__
+#define __DXRENDERER_H__
+
+#include <X11/Xlib.h>
+
+#include <vdr/thread.h>
+#include "imlibrenderer.h"
+
+//***************************************************************************
+// Class X Renderer
+//***************************************************************************
+
+class XRenderer : public ImlibRenderer
+{
+ public:
+
+ XRenderer(int x, int y, int width, int height, string cfgPath, int utf, string thmPath);
+ ~XRenderer();
+
+ void setDisplaySize(int width, int height);
+ int init(int lazy);
+ void deinit();
+ int xPending();
+ void refresh(int force = no);
+ void refreshArea(int x, int y, int width, int height);
+ void clear();
+
+ virtual int attach(const char* disp = 0);
+ virtual int detach();
+
+ protected:
+
+ void refreshPixmap();
+
+ Window win;
+ Display* disp;
+ int screen;
+ Pixmap pix;
+ cMutex mutex;
+ int initialized;
+};
+
+//***************************************************************************
+#endif // __DXRENDERER_H__
diff --git a/patch/epgsearch-0.9.24.diff b/patch/epgsearch-0.9.24.diff
new file mode 100644
index 0000000..b1e2f25
--- /dev/null
+++ b/patch/epgsearch-0.9.24.diff
@@ -0,0 +1,71 @@
+--- ../plain.0/epgsearch-0.9.24//menu_main.c 2008-04-13 20:53:44.000000000 +0200
++++ menu_main.c 2008-09-27 14:05:23.000000000 +0200
+@@ -73,6 +73,23 @@
+ }
+ }
+
++#ifdef USE_GRAPHTFT
++void cMenuSearchMain::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0)
++ {
++ int i = 0;
++
++ for (cOsdItem *item = First(); item; item = Next(item))
++ cStatus::MsgOsdEventItem(!item->Selectable() ? 0 :
++ ((cMenuMyScheduleItem*)item)->event,
++ item->Text(), i++, Count());
++ }
++}
++#endif /* GRAPHTFT */
++
+ cMenuSearchMain::~cMenuSearchMain()
+ {
+ cMenuWhatsOnSearch::ScheduleChannel(); // makes sure any posted data is cleared
+--- ../plain.0/epgsearch-0.9.24//menu_main.h 2008-04-13 20:53:43.000000000 +0200
++++ menu_main.h 2008-09-09 21:43:40.000000000 +0200
+@@ -57,6 +57,7 @@
+ void UpdateCurrent();
+ #ifdef USE_GRAPHTFT
+ virtual const char* MenuKind() { return "MenuEpgsSchedule"; }
++ virtual void Display(void);
+ #endif
+ };
+
+--- ../plain.0/epgsearch-0.9.24//menu_whatson.c 2008-04-22 19:18:24.000000000 +0200
++++ menu_whatson.c 2008-09-27 14:05:47.000000000 +0200
+@@ -296,7 +296,22 @@
+ if (currentShowMode > showNext) return "MenuEpgsWhatsOnElse";
+ else return "MenuWhatsOnElse";
+ }
+-#endif
++
++void cMenuWhatsOnSearch::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0)
++ {
++ int i = 0;
++
++ for (cOsdItem *item = First(); item; item = Next(item))
++ cStatus::MsgOsdEventItem(!item->Selectable() ? 0 :
++ ((cMenuMyScheduleItem*)item)->event,
++ item->Text(), i++, Count());
++ }
++}
++#endif /* GRAPHTFT */
+
+ int cMenuWhatsOnSearch::GetTab(int Tab)
+ {
+--- ../plain.0/epgsearch-0.9.24//menu_whatson.h 2008-04-13 20:53:42.000000000 +0200
++++ menu_whatson.h 2008-09-09 21:43:12.000000000 +0200
+@@ -79,6 +79,7 @@
+ void UpdateCurrent();
+ #ifdef USE_GRAPHTFT
+ virtual const char* MenuKind();
++ virtual void Display(void);
+ #endif
+ };
diff --git a/patch/extrecmenu-1.1.diff b/patch/extrecmenu-1.1.diff
new file mode 100644
index 0000000..fb71096
--- /dev/null
+++ b/patch/extrecmenu-1.1.diff
@@ -0,0 +1,20 @@
+--- ../plain.0/extrecmenu-1.1//./mymenurecordings.c 2007-10-24 19:29:46.000000000 +0200
++++ ./mymenurecordings.c 2007-11-18 18:14:50.000000000 +0100
+@@ -51,6 +54,10 @@
+ {
+ cOsdMenu::Display();
+
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetRecording(recording);
++#endif
++
+ if(mysetup.UseVDRsRecInfoMenu)
+ {
+ DisplayMenu()->SetRecording(recording);
+@@ -93,6 +100,7 @@
+ text << tr("Lifetime") << ": " << recording->lifetime << "\n";
+
+ DisplayMenu()->SetText(text.str().c_str(),false);
++ cStatus::MsgOsdTextItem(text.str().c_str());
+ }
+ }
diff --git a/patch/mailbox-0.5.0-mailcount.diff b/patch/mailbox-0.5.0-mailcount.diff
new file mode 100644
index 0000000..0f13dab
--- /dev/null
+++ b/patch/mailbox-0.5.0-mailcount.diff
@@ -0,0 +1,78 @@
+--- ../plain.0/mailbox-0.5.0/AxMailBoxServiceTypes.h 2006-03-03 15:52:16.000000000 +0100
++++ AxMailBoxServiceTypes.h 2007-11-17 15:43:24.000000000 +0100
+@@ -31,6 +31,7 @@
+ // Service-Names (ID)
+ //=============================================================================
+ #define MailBox_HasNewMail_v1_0_NAME "MailBox-HasNewMail-1.0"
++#define MailBox_GetTotalUnseen_v1_0_NAME "MailBox-GetTotalUnseen-1.0"
+
+ //=============================================================================
+ // service MailBox-HasNewMail-1.0
+--- ../plain.0/mailbox-0.5.0/AxMailChecker.cpp 2006-06-09 19:16:00.000000000 +0200
++++ AxMailChecker.cpp 2007-11-17 15:40:47.000000000 +0100
+@@ -209,6 +209,7 @@
+ unsigned long aSumUnread = 0;
+ unsigned long aSumSIF = 0;
+ bool fAbort = false;
++ int unseen = 0;
+
+ //----- iterate over Mail-Accounts -----
+ for (Mail::MailBoxList::const_iterator anIter = myPlugin->getMailBoxCltn().begin();
+@@ -230,6 +231,7 @@
+ anAppendix = anAppendix + std::string("/");
+ } // if
+ anAppendix = anAppendix + std::string(itoa(aMB->getCurrentFolder()->getCountUnseen()));
++ unseen += aMB->getCurrentFolder()->getCountUnseen();
+ } // if
+
+ if ((aMB->getSetup().getBGCheckMode() & Mail::BG_SIF_MASK) == Mail::BG_SIF_ON)
+@@ -262,6 +264,7 @@
+ } // if
+
+ myPlugin->setMainMenuAppendix(aSumSIF != 0, anAppendix);
++ myPlugin->setTotalUnseen(unseen);
+
+ fFirst = false;
+ aTimeOutMS = myPluginSettings.BGCheckDelay * 60 * 1000;
+--- ../plain.0/mailbox-0.5.0/AxPluginMailBox.cpp 2007-10-21 10:43:27.000000000 +0200
++++ AxPluginMailBox.cpp 2007-11-17 16:30:27.000000000 +0100
+@@ -108,6 +108,7 @@
+ , myInitialMailBox(0)
+ , myHasNewMail (false)
+ , myMainMenuEntryAppendix("")
++ , totalUnseen(0)
+ {
+ #if HAVE_AXLIB
+ Ax::Tools::Trace::setLogFunc(syslog_with_tid);
+@@ -426,6 +427,11 @@
+ wsdebug(("- AxPluginMailBox::Service() Id: '%s' supported", Id));
+ } // if
+ }
++ else if (strcmp(Id, MailBox_GetTotalUnseen_v1_0_NAME) == 0)
++ {
++ if (Data != 0)
++ *((int*)Data) = totalUnseen;
++ }
+ else
+ {
+ fHandled = false;
+--- ../plain.0/mailbox-0.5.0/AxPluginMailBox.h 2006-05-01 18:49:21.000000000 +0200
++++ AxPluginMailBox.h 2007-11-17 15:41:51.000000000 +0100
+@@ -497,6 +497,8 @@
+ */
+ void setMainMenuAppendix(bool fHasNewMail = false, const std::string &theMenuText = std::string(""));
+
++ void setTotalUnseen(int i) { totalUnseen = i; }
++
+ //-------------------------------------------------------------------------
+ // copyMainMenuEntry()
+ //-------------------------------------------------------------------------
+@@ -552,7 +554,7 @@
+ bool myHasNewMail;
+ std::string myMainMenuEntryAppendix;
+ cMutex myMainMenuEntryAppendixMutex;
+-
++ int totalUnseen;
+ }; // class AxPluginMailBox
+
+ #endif // __AxPluginMaiBox_H__
diff --git a/patch/mp3-0.9.15pre14_graphtft-cover-file.diff b/patch/mp3-0.9.15pre14_graphtft-cover-file.diff
new file mode 100644
index 0000000..7e22852
--- /dev/null
+++ b/patch/mp3-0.9.15pre14_graphtft-cover-file.diff
@@ -0,0 +1,119 @@
+--- ../mp3-0.9.15pre14.save//player-mp3.c 2006-11-04 15:44:33.000000000 +0100
++++ player-mp3.c 2007-07-01 16:53:27.000000000 +0200
+@@ -1583,6 +1583,7 @@
+ cNormalize norm;
+ bool haslevel=false;
+ const unsigned char *p=0;
++ const char *coverName=0;
+ int pc=0, readindex=0;
+ bool imageValid=true;
+ int imageCheck=0;
+@@ -1751,6 +1752,16 @@
+ if(si->HasInfo())
+ total=SecondsToFrames(si->Total);
+ }
++ // First remove the old cover, when exist
++ RemoveOldCover();
++ coverName=GetCover(playing->FullPath());
++ if(coverName) {
++ // if a cover exist, copy it to the /tmp directory
++ if(CopyCover(coverName)){
++ d(isyslog("mp3: found and copy cover %s to /tmp/graphTFT.cover",coverName));
++ }
++ }
++
+ d(printf("mp3: isStream=%d levelgood=%d haslevel=%d\n",isStream,levelgood,haslevel))
+ output->Init();
+ level.Init();
+@@ -2027,3 +2038,79 @@
+ Speed=-1;
+ return true;
+ }
++
++char *cMP3Player::GetCover(const char* fullname)
++{
++ static char imageFile[1024];
++ FILE* fp;
++
++ // track cover file ?
++
++ strcpy(imageFile, fullname);
++ strcpy(strrchr(imageFile, '.'), ".jpg");
++
++ d(isyslog("cov: Checking '%s' for track cover file", imageFile));
++
++ if ((fp = fopen(imageFile, "rb")))
++ {
++ fclose(fp);
++ d(isyslog("cov: Track specific cover file '%s' found", basename(imageFile)));
++
++ return imageFile;
++ }
++
++ // directory cover file ?
++
++ strcpy(strrchr(imageFile, '/'), "/Cover.jpg");
++
++ d(isyslog("cov: Checking '%s' for directory cover file ", imageFile));
++
++ if ((fp = fopen(imageFile, "rb")))
++ {
++ fclose (fp);
++ d(isyslog("cov: Cover file Cover.jpg found"));
++
++ return imageFile;
++ }
++
++ strcpy(strrchr(imageFile, '/'), "/cover.jpg");
++
++ d(isyslog("cov: Checking '%s' for directory cover file ", imageFile));
++
++ if ((fp = fopen(imageFile, "rb")))
++ {
++ fclose (fp);
++ d(isyslog("cov: Cover file cover.jpg found"));
++
++ return imageFile;
++ }
++
++ d(isyslog("cov: No cover found :("));
++
++ return 0;
++}
++
++bool cMP3Player::CopyCover(const char* coverName)
++{
++ char commandString[1024];
++ bool result = false;
++ int ret = false;
++
++ sprintf((char*)commandString, "echo %s > /tmp/graphTFT.cover", coverName);
++ ret = system((const char*)commandString);
++
++ if (ret) result = true;
++
++ return result;
++}
++
++void cMP3Player::RemoveOldCover(void)
++{
++ FILE* fp;
++
++ if ((fp = fopen("/tmp/graphTFT.cover", "rb")))
++ {
++ fclose (fp);
++ system("rm /tmp/graphTFT.cover");
++ }
++}
+--- ../mp3-0.9.15pre14.save//player-mp3.h 2006-11-04 15:44:33.000000000 +0100
++++ player-mp3.h 2007-07-01 16:05:48.000000000 +0200
+@@ -177,6 +177,9 @@
+ virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
+ bool Active(void) { return active; }
+ bool IsStream(void) { return isStream; }
++ char *GetCover(const char *maskFilename);
++ bool CopyCover(const char *coverName);
++ void RemoveOldCover(void);
+ };
+
+ #endif //___DVB_MP3_H
diff --git a/patch/muggle-0.0.8_graphtft_cover_file.diff b/patch/muggle-0.0.8_graphtft_cover_file.diff
new file mode 100644
index 0000000..22f8302
--- /dev/null
+++ b/patch/muggle-0.0.8_graphtft_cover_file.diff
@@ -0,0 +1,103 @@
+diff -Nru muggle-0.0.8-orig/vdr_player.c muggle-0.0.8/vdr_player.c
+--- muggle-0.0.8-orig/vdr_player.c 2004-11-28 18:07:59.403969080 +0100
++++ muggle-0.0.8/vdr_player.c 2004-11-28 18:09:47.448543808 +0100
+@@ -194,6 +194,10 @@
+ void Play();
+ void Forward();
+ void Backward();
++ char * GetCover(const char *fullname);
++ bool CopyCover(const char *coverName);
++ void RemoveOldCover(void);
++
+
+ void Goto(int Index, bool Still=false);
+ void SkipSeconds(int secs);
+@@ -326,6 +330,63 @@
+ }
+ }
+
++char *mgPCMPlayer::GetCover(const char *fullname)
++{
++ static char imageFile[1024];
++ char *result = NULL;
++ FILE *fp;
++
++ printf( "cov: checking %s for specific cover\n", fullname);
++ strcpy (imageFile, fullname);
++
++ strcpy (strrchr (imageFile, '.'), ".jpg");
++ if ((fp=fopen(imageFile, "rb")))
++ {
++ // found specific cover
++ printf( "cov: specific cover file %s found\n", basename(imageFile));
++ fclose (fp);
++ result = imageFile;
++ }
++ else
++ {
++ strcpy (strrchr (imageFile, '/'), "/Cover.jpg");
++ if ((fp = fopen (imageFile, "rb")))
++ {
++ fclose (fp);
++ result = imageFile;
++ printf( "cov: cover file Cover.jpg found\n");
++ } else {
++ printf( "cov: no cover found\n" );
++ }
++ }
++ return result;
++}
++
++bool mgPCMPlayer::CopyCover(const char *coverName)
++{
++ char commandString[1024];
++ bool result=false;
++ int ret = false;
++
++ sprintf((char*) commandString, "echo %s > /tmp/graphTFT.cover", coverName );
++ ret=system((const char*)commandString);
++
++ if (ret) result=true;
++ return result;
++}
++
++void mgPCMPlayer::RemoveOldCover(void)
++{
++ FILE *fp;
++
++ if ((fp=fopen("/tmp/graphTFT.cover", "rb")))
++ {
++ fclose (fp);
++ system( "rm /tmp/graphTFT.cover");
++ printf( "cov: old cover removed\n" );
++ }
++}
++
+ void mgPCMPlayer::Action(void)
+ {
+ MGLOG( "mgPCMPlayer::Action" );
+@@ -335,6 +396,7 @@
+ cResample resample[2];
+ unsigned int nsamples[2];
+ const mad_fixed_t *data[2];
++ const char *coverName=0;
+ cScale scale;
+ cLevel level;
+ cNormalize norm;
+@@ -388,6 +450,16 @@
+ {
+ std::string filename = m_playing->getSourceFile();
+ // mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() );
++ // First remove the old cover, when exist
++ RemoveOldCover();
++ coverName=GetCover(filename.c_str());
++ if(coverName) {
++ // if a cover exist, copy it to the /tmp directory
++ if(CopyCover(coverName)){
++ printf( "cov: found and copy cover %s to /tmp/graphTFT.cover\n",coverName );
++ }
++ }
++
+
+ if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() )
+ {
diff --git a/patch/remotetimers.patch b/patch/remotetimers.patch
new file mode 100644
index 0000000..5d4073c
--- /dev/null
+++ b/patch/remotetimers.patch
@@ -0,0 +1,52 @@
+Description: <short summary of the patch>
+ TODO: Put a short summary on the line above and replace this paragraph
+ with a longer explanation of this change. Complete the meta-information
+ with other relevant fields (see below for details). To make it easier, the
+ information below has been extracted from the changelog. Adjust it or drop
+ it.
+ .
+ vdr-plugin-remotetimers (1.0.1-2yavdr1~precise) precise; urgency=medium
+ .
+ * automatic rebuild
+Author: yavdr package builder <release@yavdr.org>
+
+---
+The information above should follow the Patch Tagging Guidelines, please
+checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
+are templates for supplementary fields that you might want to add:
+
+Origin: <vendor|upstream|other>, <url of original patch>
+Bug: <url in upstream bugtracker>
+Bug-Debian: http://bugs.debian.org/<bugnumber>
+Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
+Forwarded: <no|not-needed|url proving that it has been forwarded>
+Reviewed-By: <name and email of someone who approved the patch>
+Last-Update: <YYYY-MM-DD>
+
+--- vdr-plugin-remotetimers-1.0.1.orig/Makefile
++++ vdr-plugin-remotetimers-1.0.1/Makefile
+@@ -51,6 +51,9 @@ INCLUDES +=
+
+ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
++# uncomment this to enable support for the graphtft plugin.
++#DEFINES += -DUSE_GRAPHTFT
++
+ ### The object files (add further files here):
+
+ OBJS = $(PLUGIN).o svdrp.o conflict.o menu.o menuitems.o setup.o moverec.o watcher.o
+--- vdr-plugin-remotetimers-1.0.1.orig/menu.h
++++ vdr-plugin-remotetimers-1.0.1/menu.h
+--- vdr-plugin-remotetimers-1.0.1.orig/menu.c
++++ vdr-plugin-remotetimers-1.0.1/menu.c
+@@ -877,6 +877,10 @@ void cMenuEvent::Display(void)
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetEvent(event);
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetEvent(event);
++#endif
++
+ if (event->Description())
+ cStatus::MsgOsdTextItem(event->Description());
+ }
diff --git a/patch/vdr-2.2.0_graphtftng.diff b/patch/vdr-2.2.0_graphtftng.diff
new file mode 100644
index 0000000..186b553
--- /dev/null
+++ b/patch/vdr-2.2.0_graphtftng.diff
@@ -0,0 +1,157 @@
+--- vdr-2.2.0.plain/menu.c 2015-02-10 13:37:06.000000000 +0100
++++ vdr-2.2.0.gtft/menu.c 2016-02-01 10:35:04.592774218 +0100
+@@ -1302,6 +1302,7 @@
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetEvent(event);
++ cStatus::MsgOsdSetEvent(event);
+ if (event->Description())
+ cStatus::MsgOsdTextItem(event->Description());
+ }
+@@ -1432,6 +1433,7 @@
+ static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
+ static const cEvent *ScheduleEvent(void);
+ virtual eOSState ProcessKey(eKeys Key);
++ virtual void Display(void);
+ };
+
+ int cMenuWhatsOn::currentChannel = 0;
+@@ -1461,6 +1463,18 @@
+ SetHelpKeys();
+ }
+
++void cMenuWhatsOn::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++
+ bool cMenuWhatsOn::Update(void)
+ {
+ bool result = false;
+@@ -1614,6 +1628,7 @@
+ cMenuSchedule(void);
+ virtual ~cMenuSchedule();
+ virtual eOSState ProcessKey(eKeys Key);
++ virtual void Display(void);
+ };
+
+ cMenuSchedule::cMenuSchedule(void)
+@@ -1640,6 +1655,18 @@
+ cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
+ }
+
++void cMenuSchedule::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++
+ void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
+ {
+ Clear();
+@@ -2561,6 +2588,7 @@
+ }
+ cOsdMenu::Display();
+ DisplayMenu()->SetRecording(recording);
++ cStatus::MsgOsdSetRecording(recording);
+ if (recording->Info()->Description())
+ cStatus::MsgOsdTextItem(recording->Info()->Description());
+ }
+--- vdr-2.2.0.plain/osdbase.c 2015-01-15 11:11:11.000000000 +0100
++++ vdr-2.2.0.gtft/osdbase.c 2015-10-16 14:48:22.000000000 +0200
+@@ -106,6 +106,7 @@
+ free(status);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++ cStatus::MsgOsdMenuDestroy();
+ if (!--displayMenuCount)
+ DELETENULL(displayMenu);
+ }
+@@ -234,6 +235,7 @@
+ displayMenuItems = displayMenu->MaxItems();
+ displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
+ displayMenu->SetTitle(title);
++ cStatus::MsgOsdMenuDisplay(menuCategory);
+ cStatus::MsgOsdTitle(title);
+ DisplayHelp(true);
+ int count = Count();
+--- vdr-2.2.0.plain/status.c 2014-01-25 11:47:39.000000000 +0100
++++ vdr-2.2.0.gtft/status.c 2015-10-16 14:46:28.000000000 +0200
+@@ -130,3 +130,32 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++void cStatus::MsgOsdSetEvent(const cEvent* event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetEvent(event);
++}
++
++void cStatus::MsgOsdSetRecording(const cRecording* recording)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetRecording(recording);
++}
++
++void cStatus::MsgOsdMenuDisplay(eMenuCategory menuCategory)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDisplay(menuCategory);
++}
++
++void cStatus::MsgOsdMenuDestroy()
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDestroy();
++}
++void cStatus::MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdEventItem(Event, Text, Index, Count);
++}
+--- vdr-2.2.0.plain/status.h 2014-01-25 11:47:39.000000000 +0100
++++ vdr-2.2.0.gtft/status.h 2015-10-16 14:46:28.000000000 +0200
+@@ -84,6 +84,17 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual void OsdSetRecording(const cRecording* recording) {}
++ // The OSD displays the recording information.
++ virtual void OsdSetEvent(const cEvent* event) {}
++ // The OSD displays the event information.
++ virtual void OsdMenuDisplay(eMenuCategory menuCategory) {}
++ // report menu creation
++ virtual void OsdMenuDestroy() {}
++ // report menu destruvtion
++ virtual void OsdEventItem(const cEvent* Event, const char *Text, int Index, int Count) {}
++ // The OSD displays the given single line Event as menu item at Index.
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -106,6 +117,11 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static void MsgOsdSetEvent(const cEvent* event);
++ static void MsgOsdSetRecording(const cRecording* recording);
++ static void MsgOsdMenuDisplay(eMenuCategory menuCategory);
++ static void MsgOsdMenuDestroy();
++ static void MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count);
+ };
+
+ #endif //__STATUS_H
diff --git a/patch/vdr-2.3.1_graphtftng.diff b/patch/vdr-2.3.1_graphtftng.diff
new file mode 100644
index 0000000..b60c885
--- /dev/null
+++ b/patch/vdr-2.3.1_graphtftng.diff
@@ -0,0 +1,157 @@
+--- vdr-2.3.1.plain//menu.c 2015-09-14 15:22:49.000000000 +0200
++++ vdr-2.3.1.gtft//menu.c 2016-02-01 10:37:00.688774779 +0100
+@@ -1465,6 +1465,7 @@
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetEvent(event);
++ cStatus::MsgOsdSetEvent(event);
+ if (event->Description())
+ cStatus::MsgOsdTextItem(event->Description());
+ }
+@@ -1594,6 +1595,7 @@
+ static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
+ static const cEvent *ScheduleEvent(void);
+ virtual eOSState ProcessKey(eKeys Key);
++ virtual void Display(void);
+ };
+
+ int cMenuWhatsOn::currentChannel = 0;
+@@ -1619,6 +1621,18 @@
+ SetHelpKeys();
+ }
+
++void cMenuWhatsOn::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++
+ bool cMenuWhatsOn::Update(void)
+ {
+ bool result = false;
+@@ -1788,6 +1802,7 @@
+ cMenuSchedule(void);
+ virtual ~cMenuSchedule();
+ virtual eOSState ProcessKey(eKeys Key);
++ virtual void Display(void);
+ };
+
+ cMenuSchedule::cMenuSchedule(void)
+@@ -1846,6 +1861,18 @@
+ }
+ }
+
++void cMenuSchedule::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++
+ bool cMenuSchedule::PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
+ {
+ if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
+@@ -2793,6 +2820,7 @@
+ }
+ cOsdMenu::Display();
+ DisplayMenu()->SetRecording(recording);
++ cStatus::MsgOsdSetRecording(recording);
+ if (recording->Info()->Description())
+ cStatus::MsgOsdTextItem(recording->Info()->Description());
+ }
+--- vdr-2.3.1.plain//osdbase.c 2015-09-10 13:23:07.000000000 +0200
++++ vdr-2.3.1.gtft//osdbase.c 2015-10-23 08:30:06.000000000 +0200
+@@ -107,6 +107,7 @@
+ free(status);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++ cStatus::MsgOsdMenuDestroy();
+ if (!--displayMenuCount)
+ DELETENULL(displayMenu);
+ }
+@@ -236,6 +237,7 @@
+ displayMenuItems = displayMenu->MaxItems();
+ displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
+ displayMenu->SetTitle(title);
++ cStatus::MsgOsdMenuDisplay(menuCategory);
+ cStatus::MsgOsdTitle(title);
+ DisplayHelp(true);
+ int count = Count();
+--- vdr-2.3.1.plain//status.c 2014-01-25 11:47:39.000000000 +0100
++++ vdr-2.3.1.gtft//status.c 2015-10-23 08:30:06.000000000 +0200
+@@ -130,3 +130,32 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++void cStatus::MsgOsdSetEvent(const cEvent* event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetEvent(event);
++}
++
++void cStatus::MsgOsdSetRecording(const cRecording* recording)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetRecording(recording);
++}
++
++void cStatus::MsgOsdMenuDisplay(eMenuCategory menuCategory)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDisplay(menuCategory);
++}
++
++void cStatus::MsgOsdMenuDestroy()
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDestroy();
++}
++void cStatus::MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdEventItem(Event, Text, Index, Count);
++}
+--- vdr-2.3.1.plain//status.h 2015-08-02 12:34:44.000000000 +0200
++++ vdr-2.3.1.gtft//status.h 2015-10-23 08:30:06.000000000 +0200
+@@ -81,6 +81,17 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++ virtual void OsdSetRecording(const cRecording* recording) {}
++ // The OSD displays the recording information.
++ virtual void OsdSetEvent(const cEvent* event) {}
++ // The OSD displays the event information.
++ virtual void OsdMenuDisplay(eMenuCategory menuCategory) {}
++ // report menu creation
++ virtual void OsdMenuDestroy() {}
++ // report menu destruvtion
++ virtual void OsdEventItem(const cEvent* Event, const char *Text, int Index, int Count) {}
++ // The OSD displays the given single line Event as menu item at Index.
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -103,6 +114,11 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++ static void MsgOsdSetEvent(const cEvent* event);
++ static void MsgOsdSetRecording(const cRecording* recording);
++ static void MsgOsdMenuDisplay(eMenuCategory menuCategory);
++ static void MsgOsdMenuDestroy();
++ static void MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count);
+ };
+
+ #endif //__STATUS_H
diff --git a/patch/xsnow-gtftng.patch b/patch/xsnow-gtftng.patch
new file mode 100644
index 0000000..bfd70bb
--- /dev/null
+++ b/patch/xsnow-gtftng.patch
@@ -0,0 +1,66 @@
+--- ../../xsnow.org/xsnow-1.42/toon_root.c 2001-12-16 00:09:39.000000000 +0100
++++ ./toon_root.c 2013-11-08 16:37:09.075742684 +0100
+@@ -224,6 +224,45 @@
+ return winreturn;
+ }
+
++static Window __ToonGetWindowByName(Display *display, int screen, Window window, int depth, const char* useName)
++{
++ Window rootReturn, parentReturn, *children;
++ Window winreturn = (Window)0;
++ unsigned int nChildren;
++ char* name = NULL;
++
++ if (depth > 5)
++ return (Window)0;
++
++ if (XQueryTree(display, window, &rootReturn, &parentReturn, &children, &nChildren))
++ {
++ int i;
++
++ for (i = 0; i < nChildren; ++i)
++ {
++ XWindowAttributes attributes;
++
++ if (XFetchName(display, children[i], &name))
++ {
++ if (strcasecmp(name, useName) == 0)
++ {
++ winreturn = children[i];
++ break;
++ }
++ }
++
++ winreturn = __ToonGetWindowByName(display, screen, children[i], depth+1, useName);
++
++ if (winreturn)
++ break;
++ }
++
++ XFree((char *) children);
++ }
++
++ return winreturn;
++}
++
+
+ /*
+ * Returns the window ID of the `background' window on to which the
+@@ -253,7 +292,16 @@
+
+ *clientparent = root;
+
+- if (XGetWindowProperty(display, root,
++ if (background = __ToonGetWindowByName(display, screen, root, 0, "graphtft-fe"))
++ {
++ *clientparent = background;
++ snprintf(toon_message, TOON_MESSAGE_LENGTH,
++ _("Drawing to graphtft-fe"));
++ toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
++ }
++
++ if (!background
++ && XGetWindowProperty(display, root,
+ NAUTILUS_DESKTOP_WINDOW_ID,
+ 0, 1, False, XA_WINDOW,
+ &actual_type, &actual_format,
diff --git a/plasma/CMakeLists.txt b/plasma/CMakeLists.txt
new file mode 100644
index 0000000..237a68f
--- /dev/null
+++ b/plasma/CMakeLists.txt
@@ -0,0 +1,27 @@
+project(plasma-gtft)
+
+find_package(KDE4 REQUIRED)
+include(KDE4Defaults)
+include(MacroOptionalAddSubdirectory)
+# find_package(Plasma REQUIRED)
+
+add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
+include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${PLASMA_INCLUDE_DIR})
+
+set(gtft_SRCS
+ configdialog.cc
+ gtft.cpp
+ comthread.cc
+ common.cc
+ tcpchannel.cc)
+
+kde4_add_ui_files(gtft_SRCS config.ui)
+macro_bool_to_01(KEXIV2_FOUND HAVE_KEXIV2)
+kde4_add_plugin(plasma_applet_gtft ${gtft_SRCS})
+
+target_link_libraries(plasma_applet_gtft ${KDE4_PLASMA_LIBS} ${KDE4_KDEUI_LIBS} )
+
+install(TARGETS plasma_applet_gtft DESTINATION ${PLUGIN_INSTALL_DIR})
+install(FILES plasma-applet-gtft.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+
diff --git a/plasma/README b/plasma/README
new file mode 100644
index 0000000..1fbdf2a
--- /dev/null
+++ b/plasma/README
@@ -0,0 +1,12 @@
+
+# Install like all plasmoids
+
+mkdir build
+cd build
+cmake ../ -DCMAKE_INSTALL_PREFIX=`kde4-config --prefix`
+make -s
+sudo make -s install
+
+# After an update restart plasma
+kquitapp plasma && plasma
+
diff --git a/plasma/common.cc b/plasma/common.cc
new file mode 100644
index 0000000..13cea6b
--- /dev/null
+++ b/plasma/common.cc
@@ -0,0 +1,53 @@
+//***************************************************************************
+// 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 <plasma/applet.h>
+
+//***************************************************************************
+// 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;
+
+ if (eloquence < 2)
+ {
+ va_start(ap, format);
+
+ 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] = ' ';
+
+ kDebug() << buf;
+
+ va_end(ap);
+ }
+
+ return 0;
+}
diff --git a/plasma/common.hpp b/plasma/common.hpp
new file mode 100644
index 0000000..b9c9765
--- /dev/null
+++ b/plasma/common.hpp
@@ -0,0 +1,15 @@
+//***************************************************************************
+// 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/plasma/comthread.cc b/plasma/comthread.cc
new file mode 100644
index 0000000..52ef1b8
--- /dev/null
+++ b/plasma/comthread.cc
@@ -0,0 +1,226 @@
+//***************************************************************************
+// 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 "comthread.hpp"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+ComThread::ComThread()
+ : QThread()
+{
+ line = new TcpChannel();
+
+ bufferSize = maxBuffer;
+
+ buffer = new char[bufferSize+1];
+ header = new TcpChannel::Header;
+
+ timeout = 1;
+ port = -1;
+ *host = 0;
+}
+
+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;
+}
+
+//***************************************************************************
+// Run
+//***************************************************************************
+
+void ComThread::run()
+{
+ const int checkTimeout = 2;
+
+ int status;
+ time_t lastCheck = time(0);
+
+ running = true;
+
+ while (running)
+ {
+ if (!line->isConnected())
+ {
+ tell(eloAlways, "Trying connecting to '%s' at port (%d)", host, port);
+
+ if (line->open(host, port) == success)
+ tell(eloAlways, "Connection to '%s' established", host);
+ 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)
+ {
+ line->close();
+ tell(eloAlways, "Error: Communication problems, "
+ "look failed, status was (%d)", status);
+
+ break;
+ }
+
+ continue;
+ }
+
+ if ((status = read()) != success)
+ {
+ line->close();
+ tell(eloAlways, "Error: Communication problems, "
+ "read failed, status was (%d)", status);
+
+ break;
+ }
+ }
+
+ 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 = x;
+ m.y = y;
+ m.button = button;
+ m.flag = flag;
+ m.data = data;
+ line->write(cGraphTftComService::cmdMouseEvent, (char*)&m, sizeof(GraphTftTouchEvent));
+
+ return 0;
+}
+
+//***************************************************************************
+// ...
+//***************************************************************************
+
+int ComThread::keyEvent(int key, int flag)
+{
+ GraphTftTouchEvent m;
+
+ m.x = 0;
+ m.y = 0;
+ m.button = key;
+ m.flag = 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: Received %d bytes", header->size);
+
+ bufferLock.lockForWrite();
+ status = line->read(buffer, header->size);
+ bufferLock.unlock();
+
+ if (status == success)
+ update((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;
+}
+
+//***************************************************************************
+// Update Image
+//***************************************************************************
+
+void ComThread::update(unsigned char* buffer, int size)
+{
+ emit updateImage(buffer, size);
+}
diff --git a/plasma/comthread.hpp b/plasma/comthread.hpp
new file mode 100644
index 0000000..ccec05e
--- /dev/null
+++ b/plasma/comthread.hpp
@@ -0,0 +1,153 @@
+//***************************************************************************
+// 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 ComThread
+// Class TcpChannel
+//***************************************************************************
+
+#ifndef __COMTHREAD_HPP__
+#define __COMTHREAD_HPP__
+
+#include <QObject>
+#include <QThread>
+#include <QReadWriteLock>
+
+#define __FRONTEND
+#include <../common.h>
+
+#include <common.hpp>
+#include <../service.h>
+
+#include <unistd.h>
+
+//***************************************************************************
+// 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
+ };
+
+ struct Header
+ {
+ int command;
+ int size;
+ };
+
+ // object
+
+ TcpChannel();
+ ~TcpChannel();
+
+ // api function
+
+ int open(const char* aHost, int aPort);
+ int close();
+ 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;
+};
+
+//***************************************************************************
+// Communication Thread
+//***************************************************************************
+
+class ComThread : public QThread, public cGraphTftComService
+{
+ Q_OBJECT
+
+ public:
+
+ enum Misc
+ {
+ maxBuffer = 1024*1024
+ };
+
+ ComThread();
+ virtual ~ComThread();
+
+ void stop() { running = false; }
+ 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; }
+ QReadWriteLock* getBufferLock() { return &bufferLock; }
+
+ signals:
+
+ void updateImage(unsigned char* buffer, int size);
+
+ protected:
+
+ void update(unsigned char* buffer, int size);
+ void run();
+ int read();
+
+ TcpChannel* line;
+
+ char* buffer;
+ int bufferSize;
+ QReadWriteLock bufferLock;
+
+ long timeout;
+ int running;
+ TcpChannel::Header* header;
+ unsigned short port;
+ char host[100];
+};
+
+//***************************************************************************
+#endif // __COMTHREAD_HPP__
diff --git a/plasma/config.ui b/plasma/config.ui
new file mode 100644
index 0000000..98c9479
--- /dev/null
+++ b/plasma/config.ui
@@ -0,0 +1,101 @@
+<ui version="4.0" >
+ <class>config</class>
+ <widget class="QWidget" name="config" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>250</height>
+ </rect>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>0</width>
+ <height>250</height>
+ </size>
+ </property>
+ <property name="styleSheet" >
+ <string notr="true" />
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>VDR</string>
+ </property>
+ <property name="flat" >
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Host</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="lineEditHost" >
+ <property name="text" >
+ <string>localhost</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="lineEditPort" >
+ <property name="inputMask" >
+ <string>990000; </string>
+ </property>
+ <property name="text" >
+ <string>2039</string>
+ </property>
+ <property name="maxLength" >
+ <number>6</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Appearance</string>
+ </property>
+ <property name="flat" >
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <layout class="QVBoxLayout" >
+ <item>
+ <widget class="QCheckBox" name="smoothScaling" >
+ <property name="toolTip" >
+ <string>Use smooth scaling when resizing pictures. Usually pictures look better this way, but it takes more time to load them.</string>
+ </property>
+ <property name="text" >
+ <string>Smooth scaling</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plasma/configdialog.cc b/plasma/configdialog.cc
new file mode 100644
index 0000000..f791870
--- /dev/null
+++ b/plasma/configdialog.cc
@@ -0,0 +1,42 @@
+
+#include "configdialog.h"
+
+ConfigDialog::ConfigDialog(QWidget* parent)
+ : QWidget(parent)
+{
+ ui.setupUi(this);
+}
+
+ConfigDialog::~ConfigDialog()
+{
+}
+
+void ConfigDialog::setSmoothScaling(bool smooth)
+{
+ ui.smoothScaling->setChecked(smooth);
+}
+
+bool ConfigDialog::smoothScaling() const
+{
+ return ui.smoothScaling->isChecked();
+}
+
+void ConfigDialog::setHost(QString host)
+{
+ ui.lineEditHost->setText(host);
+}
+
+QString ConfigDialog::host() const
+{
+ return ui.lineEditHost->text();
+}
+
+void ConfigDialog::setPort(unsigned int port)
+{
+ ui.lineEditPort->setText(QString::number(port));
+}
+
+unsigned int ConfigDialog::port() const
+{
+ return ui.lineEditPort->text().toInt();
+}
diff --git a/plasma/configdialog.h b/plasma/configdialog.h
new file mode 100644
index 0000000..ea0d7a5
--- /dev/null
+++ b/plasma/configdialog.h
@@ -0,0 +1,28 @@
+
+
+#ifndef CONFIGDIALOG_H
+#define CONFIGDIALOG_H
+
+#include "ui_config.h"
+
+class ConfigDialog : public QWidget
+{
+ Q_OBJECT
+
+ public:
+
+ ConfigDialog(QWidget* parent);
+ ~ConfigDialog();
+
+ void setSmoothScaling(bool smooth);
+ bool smoothScaling() const;
+ void setHost(QString host);
+ QString host() const;
+ void setPort(unsigned int port);
+ unsigned int port() const;
+
+ Ui::config ui;
+
+};
+
+#endif // CONFIGDIALOG_H
diff --git a/plasma/gtft.cpp b/plasma/gtft.cpp
new file mode 100644
index 0000000..7ae993a
--- /dev/null
+++ b/plasma/gtft.cpp
@@ -0,0 +1,216 @@
+
+#include <KConfigDialog>
+#include <KSharedConfig>
+#include <KServiceTypeTrader>
+#include <kglobalsettings.h>
+
+#include <gtft.h>
+#include <configdialog.h>
+
+GtftApplet::GtftApplet(QObject* parent, const QVariantList& args)
+ : Plasma::Applet(parent, args)
+{
+ configDialog = 0;
+ vdrWidth = 720;
+ vdrHeight = 576;
+
+ thread = new ComThread();
+
+ setHasConfigurationInterface(true);
+ setCacheMode(QGraphicsItem::NoCache);
+ setBackgroundHints(Plasma::Applet::NoBackground);
+ setAspectRatioMode(Plasma::KeepAspectRatio); // Plasma::IgnoreAspectRatio
+
+ resize(400, 300);
+}
+
+GtftApplet::~GtftApplet()
+{
+ if (thread)
+ {
+ tell(eloAlways, "Stopping thread");
+
+ thread->stop();
+
+ if (!thread->wait(1000))
+ tell(eloAlways, "Thread would not end!");
+ else
+ tell(eloAlways, "Thread ended regularly");
+
+ delete thread;
+ }
+}
+
+void GtftApplet::init()
+{
+ // picture.load("/home/wendel/test.jpg");
+
+ // read config
+
+ KConfigGroup cg = config();
+
+ smoothScaling = cg.readEntry("smoothScaling", true);
+ host = cg.readEntry("vdr host", "localhost");
+ port = cg.readEntry("vdr port", 2039);
+
+ connect(thread, SIGNAL(updateImage(unsigned char*, int)),
+ this, SLOT(updateImage(unsigned char*, int)));
+
+ tell(eloAlways, "Starting thread");
+
+ thread->setHost(host.toAscii());
+ thread->setPort(port);
+
+ thread->start();
+}
+
+//***************************************************************************
+// Configuration
+//***************************************************************************
+
+void GtftApplet::createConfigurationInterface(KConfigDialog* parent)
+{
+ configDialog = new ConfigDialog(parent);
+
+ parent->addPage(configDialog, i18n("General"), "configure");
+ parent->setDefaultButton(KDialog::Ok);
+ parent->showButtonSeparator(true);
+
+ connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
+ connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
+
+ configDialog->setSmoothScaling(smoothScaling);
+ configDialog->setHost(host);
+ configDialog->setPort(port);
+}
+
+void GtftApplet::configAccepted()
+{
+ KConfigGroup cg = config();
+
+ // connection parameter changed ?
+
+ if (host != configDialog->host() ||
+ port != configDialog->port())
+ {
+ // reconnect ..
+
+ thread->stop();
+ host = configDialog->host();
+ port = configDialog->port();
+ thread->setHost(host.toAscii());
+ thread->setPort(port);
+ thread->start();
+ }
+
+ smoothScaling = configDialog->smoothScaling();
+
+ cg.writeEntry("smoothScaling", smoothScaling);
+ cg.writeEntry("vdr host", host);
+ cg.writeEntry("vdr port", port);
+
+ emit configNeedsSaving();
+}
+
+void GtftApplet::updateImage(unsigned char* buffer, int size)
+{
+ thread->getBufferLock()->lockForRead();
+ picture.loadFromData(buffer, size);
+ thread->getBufferLock()->unlock();
+
+ update();
+}
+
+void GtftApplet::paintInterface(QPainter* p, const QStyleOptionGraphicsItem* option,
+ const QRect& rect)
+{
+ Q_UNUSED(option);
+ QPixmap* pixmap;
+
+ Qt::TransformationMode tm = smoothScaling ? Qt::SmoothTransformation : Qt::FastTransformation;
+
+ if (!picture.isNull())
+ pixmap = &picture;
+ else
+ return ;
+
+ QPixmap scaledImage = pixmap->scaled(rect.size(), Qt::KeepAspectRatio, tm);
+ p->drawPixmap(rect, scaledImage);
+
+ // notice some data for 'scaling' the mouse cooridinates ...
+
+ paintRect = rect;
+ vdrWidth = pixmap->width();
+ vdrHeight = pixmap->height();
+}
+
+//***************************************************************************
+// User Action
+//***************************************************************************
+
+void GtftApplet::keyPressEvent(QKeyEvent* keyEvent)
+{
+ thread->mouseEvent(0, 0, keyEvent->nativeScanCode(), ComThread::efKeyboard);
+}
+
+void GtftApplet::mouseMoveEvent(QGraphicsSceneMouseEvent* /*event*/)
+{
+ // don't drag the widget ...
+}
+
+void GtftApplet::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
+{
+ QPoint p = clickCoordinate(event->screenPos());
+
+ thread->mouseEvent(p.x(), p.y(), cGraphTftComService::mbLeft,
+ cGraphTftComService::efDoubleClick);
+}
+
+void GtftApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent* /*event*/)
+{
+ // #TODO
+ // Mouse gestures
+}
+
+void GtftApplet::wheelEvent(QGraphicsSceneWheelEvent* wheelEvent)
+{
+ QPoint p = clickCoordinate(wheelEvent->screenPos());
+
+ if (wheelEvent->delta() > 0)
+ thread->mouseEvent(p.x(), p.y(), cGraphTftComService::mbWheelUp, 0);
+ else
+ thread->mouseEvent(p.x(), p.y(), cGraphTftComService::mbWheelDown, 0);
+}
+
+void GtftApplet::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+ QPoint p = clickCoordinate(event->screenPos());
+
+ tell(4, "mousePressEvent %d/%d", p.x(), p.y());
+
+ if (event->buttons() == Qt::LeftButton)
+ thread->mouseEvent(p.x(), p.y(), cGraphTftComService::mbLeft, 0);
+ else if (event->buttons() == Qt::MidButton)
+ thread->mouseEvent(p.x(), p.y(), cGraphTftComService::mbRight, 0);
+}
+
+QPoint GtftApplet::clickCoordinate(QPoint screenPos)
+{
+ QPoint p;
+
+ // calc position relative to the draw area (the image)
+
+ p.setX(screenPos.x() - (geometry().x() + paintRect.x()));
+ p.setY(screenPos.y() - (geometry().y() + paintRect.y()));
+
+ // scale
+
+ p.setX((int)(((double)p.x() / (double)paintRect.width()) * (double)vdrWidth));
+ p.setY((int)(((double)p.y() / (double)paintRect.height()) * (double)vdrHeight));
+
+ return p;
+}
+
+//***************************************************************************
+
+#include "gtft.moc"
diff --git a/plasma/gtft.h b/plasma/gtft.h
new file mode 100644
index 0000000..e2daa21
--- /dev/null
+++ b/plasma/gtft.h
@@ -0,0 +1,66 @@
+
+
+#ifndef _GTFT_H
+#define _GTFT_H
+
+#include <QPainter>
+#include <QGraphicsSceneMouseEvent>
+#include <Plasma/Applet>
+
+#include <comthread.hpp>
+
+class ConfigDialog;
+
+// using namespace Plasma;
+
+class GtftApplet : public Plasma::Applet
+{
+ Q_OBJECT
+
+ public:
+
+ GtftApplet(QObject* parent, const QVariantList& args);
+ ~GtftApplet();
+
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent);
+ void wheelEvent(QGraphicsSceneWheelEvent* wheelEvent);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void keyPressEvent(QKeyEvent* keyEvent);
+
+ void paintInterface(QPainter* painter, const QStyleOptionGraphicsItem* option,
+ const QRect& contentsRect);
+ void init();
+
+ public slots:
+
+ void updateImage(unsigned char* buffer, int size);
+ void createConfigurationInterface(KConfigDialog *parent);
+
+ protected Q_SLOTS:
+
+ void configAccepted();
+
+ protected:
+
+ QPoint clickCoordinate(QPoint screenPos);
+
+ private:
+
+ ConfigDialog* configDialog;
+ bool smoothScaling;
+ QString host;
+ unsigned int port;
+ QRect paintRect;
+
+ QPixmap picture;
+ ComThread* thread;
+
+ int vdrWidth;
+ int vdrHeight;
+};
+
+K_EXPORT_PLASMA_APPLET(gtft, GtftApplet)
+
+#endif // _GTFT_H
diff --git a/plasma/install.sh b/plasma/install.sh
new file mode 100755
index 0000000..16841a9
--- /dev/null
+++ b/plasma/install.sh
@@ -0,0 +1,12 @@
+
+if [ ! -d build ]; then
+ mkdir build
+fi
+
+cd build
+cmake ../ -DCMAKE_INSTALL_PREFIX=`kde4-config --prefix`
+make -s
+sudo make -s install
+
+# kquitapp plasma && plasma
+
diff --git a/plasma/plasma-applet-gtft.desktop b/plasma/plasma-applet-gtft.desktop
new file mode 100644
index 0000000..cd61c18
--- /dev/null
+++ b/plasma/plasma-applet-gtft.desktop
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Name=GraphTFT
+Comment=Frontend for VDR graphTFT plugin
+Type=Service
+Icon=system-run
+X-KDE-ServiceTypes=Plasma/Applet
+X-KDE-Library=plasma_applet_gtft
+X-KDE-PluginInfo-Author=Jörg Wendel
+X-KDE-PluginInfo-Email=vdr@jwendel.de
+X-KDE-PluginInfo-Name=graphtft
+X-KDE-PluginInfo-Version=0.0.1
+X-KDE-PluginInfo-Website=
+X-KDE-PluginInfo-Category=Multimedia
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
diff --git a/plasma/tcpchannel.cc b/plasma/tcpchannel.cc
new file mode 100644
index 0000000..1f76940
--- /dev/null
+++ b/plasma/tcpchannel.cc
@@ -0,0 +1,387 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File tcpchannel.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 TcpChannel
+//***************************************************************************
+
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "comthread.hpp"
+
+//***************************************************************************
+// Class TcpChannel
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+TcpChannel::TcpChannel()
+{
+ handle = 0;
+ port = 0;
+ *localHost = 0;
+ *remoteHost = 0;
+ localAddr = 0;
+ remoteAddr = 0;
+ timeout = 30;
+ lookAheadChar = false;
+ lookAhead = 0;
+ nTtlSent = 0;
+ nTtlReceived = 0;
+}
+
+TcpChannel::~TcpChannel()
+{
+ // nothing yet !
+}
+
+//***************************************************************************
+// Open
+//***************************************************************************
+
+int TcpChannel::open(const char* aHost, int aPort)
+{
+ const char* hostName;
+ struct sockaddr_in localSockAddr, remoteSockAddr;
+ struct hostent* hostInfo;
+ int aHandle;
+
+ if (!aHost || !*aHost)
+ return -1;
+
+ 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;
+ }
+ }
+
+ // none blocking
+
+ if (fcntl(handle, F_SETFL, O_NONBLOCK) < 0)
+ return fail;
+
+ // 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;
+
+ 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 timeout;
+ fd_set readFD, writeFD, exceptFD;
+ int n;
+
+ // time-out for select
+
+ timeout.tv_sec = aTimeout;
+ timeout.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, &timeout);
+
+ 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;
+}
+
+//***************************************************************************
+// 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;
+
+ //cMutexLock lock(&_mutex);
+
+ if (buf && !bufLen)
+ bufLen = strlen(buf);
+
+ tell(eloDebug, "Writing (%d) header bytes, command (%d), size (%ld)",
+ 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 0;
+
+ tell(eloDebug, "Writing (%ld) 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)
+ return success;
+
+ ::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/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..67a2e66
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,176 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Klaus Schmidinger <kls@cadsoft.de>, 2000
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <vdr@jwendel.de>\n"
+"POT-Creation-Date: 2015-12-22 11:18+0100\n"
+"PO-Revision-Date: 2008-12-05 19:18+0100\n"
+"Last-Translator: Klaus Schmidinger <kls@cadsoft.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Can't save snapshot, missing event information"
+msgstr "Kann Snapdhot nicht speichern, es fehlen Event Informationen"
+
+msgid "Snapshot saved"
+msgstr "Snapshot gespeichert"
+
+msgid "Error saving snapshot"
+msgstr "Fehler beim Snapshot speichern"
+
+msgid "Reload themes"
+msgstr "Themes neu laden"
+
+msgid "Refresh Display"
+msgstr "Anzeige aktualisieren"
+
+msgid "Normal Display"
+msgstr "Standardanzeige"
+
+msgid "Snapshot"
+msgstr ""
+
+msgid "--- end of playlist ---"
+msgstr "--- Ende der Abspielliste ---"
+
+msgid "GraphTFT"
+msgstr "GraphTFT"
+
+msgid "Theme"
+msgstr "Aussehen"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Hauptmenueintrag verstecken"
+
+msgid "VDR use iso charset"
+msgstr ""
+
+msgid "Spectrum Analyzer (music)"
+msgstr ""
+
+msgid "Dump Image"
+msgstr "Dump Bild"
+
+msgid "Dump image to '/tmp/graphtftng.png'"
+msgstr ""
+
+msgid "Dump every [sec]"
+msgstr ""
+
+msgid "Snapshot width"
+msgstr "Snapshot Breite"
+
+msgid "Snapshot height"
+msgstr "Snapshot Hoehe"
+
+msgid "Snapshot JPEG Quality"
+msgstr ""
+
+msgid "Snapshot path"
+msgstr "Snapshot Ablagepfad"
+
+msgid "FB/X Device"
+msgstr ""
+
+msgid "Width"
+msgstr "Breite"
+
+msgid "Height"
+msgstr "Hoehe"
+
+msgid "FB Device"
+msgstr ""
+
+msgid "Flip OSD"
+msgstr "Tausche OSD"
+
+msgid "X Offset"
+msgstr "X Koordinaten Abweichung"
+
+msgid "Y Offset"
+msgstr "Y Koordinaten Abweichung"
+
+msgid "Border to Width"
+msgstr "Rahmen zur Hoehe"
+
+msgid "Border to Height"
+msgstr "Rahmen zur Breite"
+
+msgid "TCP Connection"
+msgstr "TCP Verbindung"
+
+msgid "JPEG Quality"
+msgstr ""
+
+msgid "touch Device"
+msgstr ""
+
+msgid "Device"
+msgstr ""
+
+msgid "Log"
+msgstr ""
+
+msgid "Log Device"
+msgstr ""
+
+msgid "Log Level"
+msgstr ""
+
+msgid "Test"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Calibrate"
+msgstr "Kalibrieren"
+
+msgid "Unknown title"
+msgstr "Unbekannter Title"
+
+msgid "No EPG data available."
+msgstr "Keine EPG Daten vorhanden."
+
+msgid "Title"
+msgstr "Titel"
+
+msgid "Artist"
+msgstr "Künstler"
+
+#~ msgid "Dump Refresh"
+#~ msgstr "Dump alle [Sek] erneuern"
+
+#~ msgid "Dump image width"
+#~ msgstr "Dump Bild Breite"
+
+#~ msgid "Dump image height"
+#~ msgstr "Dump Bild Hoehe"
+
+#~ msgid "Snapshot Jpeg Quality"
+#~ msgstr "Snapshot JPEG Qualitaet"
+
+#~ msgid "Jpeg Quality"
+#~ msgstr "JPEG Qualitaet"
+
+#~ msgid "Convert Iso to UTF-8"
+#~ msgstr "Konvertiere ISO zu UTF-8"
+
+#~ msgid "Dump image to file"
+#~ msgstr "Dump Bild in Datei speichern"
+
+#~ msgid "Force redraw every [sec]"
+#~ msgstr "Erzwungenes Neuzeichnen alle [Sek]"
+
+#~ msgid "Use StillPicture"
+#~ msgstr "Benutze StillPicture"
+
+#~ msgid "Refresh"
+#~ msgstr "Aktualisieren"
diff --git a/po/fi_FI.po b/po/fi_FI.po
new file mode 100644
index 0000000..47774a8
--- /dev/null
+++ b/po/fi_FI.po
@@ -0,0 +1,179 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Hannu Savolainen <hannu@opensound.com>, 2002
+# Jaakko Hyvätti <jaakko@hyvatti.iki.fi>, 2002
+# Niko Tarnanen <niko.tarnanen@hut.fi>, 2003
+# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2003
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <vdr@jwendel.de>\n"
+"POT-Creation-Date: 2015-12-22 11:18+0100\n"
+"PO-Revision-Date: 2008-12-05 19:18+0100\n"
+"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Can't save snapshot, missing event information"
+msgstr ""
+
+msgid "Snapshot saved"
+msgstr ""
+
+msgid "Error saving snapshot"
+msgstr ""
+
+msgid "Reload themes"
+msgstr ""
+
+msgid "Refresh Display"
+msgstr ""
+
+msgid "Normal Display"
+msgstr ""
+
+msgid "Snapshot"
+msgstr ""
+
+msgid "--- end of playlist ---"
+msgstr ""
+
+msgid "GraphTFT"
+msgstr "GraphTFT"
+
+msgid "Theme"
+msgstr "Ulkoasu"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Piilota valinta päävalikosta"
+
+msgid "VDR use iso charset"
+msgstr ""
+
+msgid "Spectrum Analyzer (music)"
+msgstr ""
+
+msgid "Dump Image"
+msgstr ""
+
+msgid "Dump image to '/tmp/graphtftng.png'"
+msgstr ""
+
+msgid "Dump every [sec]"
+msgstr ""
+
+msgid "Snapshot width"
+msgstr ""
+
+msgid "Snapshot height"
+msgstr ""
+
+msgid "Snapshot JPEG Quality"
+msgstr ""
+
+msgid "Snapshot path"
+msgstr ""
+
+msgid "FB/X Device"
+msgstr ""
+
+msgid "Width"
+msgstr ""
+
+msgid "Height"
+msgstr ""
+
+msgid "FB Device"
+msgstr ""
+
+msgid "Flip OSD"
+msgstr ""
+
+msgid "X Offset"
+msgstr "Vaakakeskitys"
+
+msgid "Y Offset"
+msgstr "Pystykeskitys"
+
+msgid "Border to Width"
+msgstr "Leveysraja"
+
+msgid "Border to Height"
+msgstr "Korkeusraja"
+
+msgid "TCP Connection"
+msgstr ""
+
+msgid "JPEG Quality"
+msgstr ""
+
+msgid "touch Device"
+msgstr ""
+
+msgid "Device"
+msgstr ""
+
+msgid "Log"
+msgstr ""
+
+msgid "Log Device"
+msgstr "Debug-loki"
+
+msgid "Log Level"
+msgstr ""
+
+msgid "Test"
+msgstr ""
+
+msgid "Stop"
+msgstr ""
+
+msgid "Calibrate"
+msgstr ""
+
+msgid "Unknown title"
+msgstr ""
+
+msgid "No EPG data available."
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "Artist"
+msgstr ""
+
+#~ msgid "Debug"
+#~ msgstr "Debug"
+
+#~ msgid "Debug Level"
+#~ msgstr "Debug-taso"
+
+#~ msgid "Dump image width"
+#~ msgstr "Tallennettavan kuvan leveys"
+
+#~ msgid "Dump image height"
+#~ msgstr "Tallennettavan kuvan korkeus"
+
+#~ msgid "Convert Iso to UTF-8"
+#~ msgstr "Muunna ISO UTF-8:ksi"
+
+#~ msgid "Dump image to file"
+#~ msgstr "Tallenna kuva tiedostoon"
+
+#~ msgid "DVB Device"
+#~ msgstr "DVB-laite"
+
+#~ msgid "Use StillPicture"
+#~ msgstr "Käytä StillPicture-toimintoa"
+
+#~ msgid "Refresh"
+#~ msgstr "Päivitä"
+
+#~ msgid "DVB/FB Device"
+#~ msgstr "DVB/FB-laite"
diff --git a/po/it_IT.po b/po/it_IT.po
new file mode 100644
index 0000000..bed924b
--- /dev/null
+++ b/po/it_IT.po
@@ -0,0 +1,196 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de>
+# This file is distributed under the same license as the VDR package.
+# Alberto Carraro <bertocar@tin.it>, 2001
+# Antonio Ospite <ospite@studenti.unina.it>, 2003
+# Sean Carlos <seanc@libero.it>, 2005
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.7\n"
+"Report-Msgid-Bugs-To: <vdr@jwendel.de>\n"
+"POT-Creation-Date: 2015-12-22 11:18+0100\n"
+"PO-Revision-Date: 2009-02-08 20:23+0100\n"
+"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Italian\n"
+"X-Poedit-Country: ITALY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Can't save snapshot, missing event information"
+msgstr "Impossibile salvare schermata, info evento mancante"
+
+msgid "Snapshot saved"
+msgstr "Schermata salvata"
+
+msgid "Error saving snapshot"
+msgstr "Errore salvataggio schermata"
+
+msgid "Reload themes"
+msgstr "Ricarica temi"
+
+msgid "Refresh Display"
+msgstr "Aggiorna Display"
+
+msgid "Normal Display"
+msgstr "Display normale"
+
+msgid "Snapshot"
+msgstr "Schermata"
+
+msgid "--- end of playlist ---"
+msgstr "--- fine lista esec. ---"
+
+msgid "GraphTFT"
+msgstr "GraphTFT"
+
+msgid "Theme"
+msgstr "Tema"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Nascondi voce menu principale"
+
+msgid "VDR use iso charset"
+msgstr ""
+
+msgid "Spectrum Analyzer (music)"
+msgstr ""
+
+msgid "Dump Image"
+msgstr "Estrai immagine"
+
+msgid "Dump image to '/tmp/graphtftng.png'"
+msgstr ""
+
+msgid "Dump every [sec]"
+msgstr ""
+
+msgid "Snapshot width"
+msgstr "Larghezza schermata"
+
+msgid "Snapshot height"
+msgstr "Altezza schermata"
+
+msgid "Snapshot JPEG Quality"
+msgstr ""
+
+msgid "Snapshot path"
+msgstr "Percorso schermata"
+
+msgid "FB/X Device"
+msgstr ""
+
+msgid "Width"
+msgstr "Larghezza"
+
+msgid "Height"
+msgstr "Altezza"
+
+msgid "FB Device"
+msgstr ""
+
+msgid "Flip OSD"
+msgstr "Vibrazione OSD"
+
+msgid "X Offset"
+msgstr "Limite X"
+
+msgid "Y Offset"
+msgstr "Limite Y"
+
+msgid "Border to Width"
+msgstr "Larghezza bordo"
+
+msgid "Border to Height"
+msgstr "Altezza bordo"
+
+msgid "TCP Connection"
+msgstr "Connessione TCP"
+
+msgid "JPEG Quality"
+msgstr ""
+
+msgid "touch Device"
+msgstr "tocca dispositivo"
+
+msgid "Device"
+msgstr "Dispositivo"
+
+msgid "Log"
+msgstr ""
+
+msgid "Log Device"
+msgstr "Log dispositivo"
+
+msgid "Log Level"
+msgstr ""
+
+msgid "Test"
+msgstr "Prova"
+
+msgid "Stop"
+msgstr "Ferma"
+
+msgid "Calibrate"
+msgstr "Calibra"
+
+msgid "Unknown title"
+msgstr "Titolo sconosciuto"
+
+msgid "No EPG data available."
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "Artist"
+msgstr ""
+
+#~ msgid "Debug"
+#~ msgstr "Debug"
+
+#~ msgid "Debug Level"
+#~ msgstr "Livello debug"
+
+#~ msgid "Dump Refresh"
+#~ msgstr "Estrai aggiornamento"
+
+#~ msgid "Dump image width"
+#~ msgstr "Estrai larghezza immagine"
+
+#~ msgid "Dump image height"
+#~ msgstr "Estrai altezza immagine"
+
+#~ msgid "Snapshot Jpeg Quality"
+#~ msgstr "Qualità schermata Jpeg"
+
+#~ msgid "Jpeg Quality"
+#~ msgstr "Qualità Jpeg"
+
+#~ msgid "Convert Iso to UTF-8"
+#~ msgstr "Converti ISO in UTF-8"
+
+#~ msgid "Spectrum Analyzer"
+#~ msgstr "Analizzatore di spettro"
+
+#~ msgid "Dump image to file"
+#~ msgstr "Estrai immagine in un file"
+
+#~ msgid "graphTFT: The syntax version of the theme file don't match!"
+#~ msgstr "graphTFT: la versione della sintassi del file del tema non corrisponde!"
+
+#~ msgid "DVB Device"
+#~ msgstr "Scheda DVB"
+
+#~ msgid "Use StillPicture"
+#~ msgstr "Utilizza adatta immagine"
+
+#~ msgid "Refresh"
+#~ msgstr "Aggiorna"
+
+#~ msgid "DVB/FB Device"
+#~ msgstr "Scheda DVB/FB"
diff --git a/renderer.c b/renderer.c
new file mode 100644
index 0000000..764d726
--- /dev/null
+++ b/renderer.c
@@ -0,0 +1,81 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File renderer.c
+// 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-2008 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class Renderer
+//***************************************************************************
+
+#include <string.h>
+
+#include "renderer.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+Renderer::Renderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath)
+{
+ confPath = cfgPath;
+
+ xOffset = x;
+ yOffset = y;
+ themeWidth = width;
+ themeHeight = height;
+ utf8 = utf;
+ themePath = thmPath;
+
+ dspWidth = width;
+ dspHeight = height;
+ xBorder = 0;
+ yBorder = 0;
+ devname = 0;
+}
+
+Renderer::~Renderer()
+{
+ free(devname);
+}
+
+//***************************************************************************
+// Set Device Name
+//***************************************************************************
+
+void Renderer::setDevName(const char* _devname)
+{
+ if (!Str::isEmpty(_devname))
+ {
+ free(devname);
+ devname = strdup(_devname);
+ tell(0, "Set display to '%s'", devname);
+ }
+}
+
+//***************************************************************************
+// Set Properties
+//***************************************************************************
+
+void Renderer::setProperties(int x, int y, int width, int height,
+ int utf, string thmPath)
+{
+ xOffset = x;
+ yOffset = y;
+ themeWidth = width;
+ themeHeight = height;
+ utf8 = utf;
+ themePath = thmPath;
+}
+
+//***************************************************************************
+// Set Border
+//***************************************************************************
+
+void Renderer::setBorder(int widthBorder, int heightBorder)
+{
+ xBorder = widthBorder;
+ yBorder = widthBorder;
+}
diff --git a/renderer.h b/renderer.h
new file mode 100644
index 0000000..3cd456d
--- /dev/null
+++ b/renderer.h
@@ -0,0 +1,102 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * renderer.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2005-2013 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 __GTFT_RENDERER_H
+#define __GTFT_RENDERER_H
+
+#include <string>
+
+#include <common.h>
+
+using std::string;
+
+//***************************************************************************
+// used as base class of the renderes
+//***************************************************************************
+
+class Renderer
+{
+ public:
+
+ Renderer(int x, int y, int width, int height,
+ string cfgPath, int utf, string thmPath);
+ virtual ~Renderer();
+
+ virtual void setDevName(const char* _devname);
+ virtual void setProperties(int x, int y,
+ int width, int height,
+ int utf, string thmPath);
+ virtual void setBorder(int widthBorder,
+ int heightBorder);
+ virtual void setDisplaySize(int width, int height) { }
+ virtual void setFontPath(string fntPath) = 0;
+ virtual int init(int lazy) = 0;
+ virtual void flushCache() {};
+ virtual void deinit() = 0;
+
+ virtual long toJpeg(unsigned char*& buffer, int quality) = 0;
+
+ virtual void refresh(int force = no) = 0;
+ virtual void refreshArea(int x, int y, int width, int height) {}
+ virtual void clear() = 0;
+
+ virtual int textWidthOf(const char* text, const char* fontName, int fontSize, int& height) = 0;
+ virtual int charWidthOf(const char* fontName = 0,
+ int fontSize = 0) = 0;
+ virtual void image(const char* fname, int x, int y,
+ int width, int height,
+ bool fit = no, bool aspectRatio = no,
+ int orientation = 1) = 0;
+
+ virtual void imagePart(const char* fname, int x, int y,
+ int width, int height) = 0;
+
+ virtual int text(const char *text, const char *font_name,
+ int size, int align, int x, int y,
+ p_rgba rgba, // int r, int g, int b,
+ int width, int height,
+ int lines, int dots = 0, int skipLines = 0) = 0;
+
+ virtual int lineCount(const char* text, const char* font_name,
+ int size, int width) = 0;
+
+ virtual void rectangle(int x, int y, int width, int height,
+ p_rgba rgba) = 0;
+ // int r, int g, int b, int alpha) = 0;
+
+ virtual void dumpImage2File(const char* fname,
+ int dumpWidth, int dumpHeight,
+ const char* aPath = 0) = 0;
+
+ virtual int xPending() { return done; }
+ virtual int attach(const char* disp = 0) { return done; }
+ virtual int detach() { return done; }
+
+ protected:
+
+ string confPath;
+ string themePath;
+ int utf8;
+ int xOffset;
+ int yOffset;
+ int themeWidth;
+ int themeHeight;
+ int xBorder;
+ int yBorder;
+
+ int dspWidth;
+ int dspHeight;
+ char* devname;
+};
+
+//***************************************************************************
+#endif // __GTFT_RENDERER_H
diff --git a/scan.c b/scan.c
new file mode 100644
index 0000000..3b00554
--- /dev/null
+++ b/scan.c
@@ -0,0 +1,90 @@
+/*
+ * scan.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ * (c) 2007-2013 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <scan.h>
+
+//***************************************************************************
+// Scanner
+//***************************************************************************
+
+const char* Scan::delimiters = " ;,(){}[]\"";
+const char* Scan::operators = " +-*:<>!=";
+const char* Scan::whitespace = " ()";
+const char* Scan::logicalOps = "|&";
+const char* Scan::numprefix = "+-";
+
+//***************************************************************************
+// Scanner
+//***************************************************************************
+
+int Scan::eat(int aANP)
+{
+ int i = 0;
+ int anp = aANP != na ? aANP : allowNumPrefix;
+
+ _last[0] = 0;
+ isStr = no;
+
+ skipWs();
+
+ while (p && *p && i < 1000)
+ {
+ if (*p == '"')
+ {
+ if (isStr)
+ {
+ p++;
+ break;
+ }
+
+ isStr = yes;
+ p++;
+
+ continue;
+ }
+
+ if (!isStr && isDelimiter(*p))
+ break;
+
+ if (isStr && *p == '"')
+ break;
+
+ if (i)
+ {
+ if (isOperator(*_last) && !isOperator(*p) && !isStr)
+ {
+ if (!isNum(*p))
+ break;
+ if (!anp || !isNumPrefix(*_last))
+ break;
+ }
+
+ if (!isOperator(*_last) && isOperator(*p) && !isStr)
+ break;
+
+ if (isNum(*_last) && !isNum(*p) && !isStr)
+ break;
+ }
+
+ if (*p != '"')
+ _last[i++] = *p;
+
+ p++;
+ }
+
+ _last[i] = 0;
+
+ return i == 0 ? fail : success;
+}
diff --git a/scan.h b/scan.h
new file mode 100644
index 0000000..a780ed6
--- /dev/null
+++ b/scan.h
@@ -0,0 +1,75 @@
+/*
+ * scan.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ * (c) 2007-2013 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <common.h>
+
+//***************************************************************************
+// Token Scanner
+//***************************************************************************
+
+class Scan
+{
+ public:
+
+ Scan(const char* buf, int np = yes) { asprintf(&buffer, "%s", buf); reset(); allowNumPrefix = np; }
+ ~Scan() { free(buffer); }
+
+ void reset() { p = buffer; isStr = no; }
+ const char* all() { return buffer; }
+ const char* lastString() { return _last; }
+ const char* lastIdent() { return _last; }
+ int lastInt() { return atoi(_last); }
+ int isEmpty() { return _last[0] == 0; }
+ int isString() { return isStr; }
+ int isIdent() { return !isStr && isIdent(*_last); }
+ int isNum() { return !isStr && isNum(*_last); }
+ int isLogical() { return !isStr && isLogical(*_last); }
+ int isOperator() { return !isStr && isOperator(*_last); }
+ int hasValue(const char* v) { return strcmp(Str::notNull(v), Str::notNull(_last)) == 0; }
+
+ const char* next() { return p; }
+
+ int eat(int aANP = na);
+
+ private:
+
+ void skipWs() { while (*p && isWhiteSpace(*p)) p++; }
+
+ int isDelimiter(char c) { return strchr(delimiters, c) != 0; }
+ int isWhiteSpace(char c) { return strchr(whitespace, c) != 0; }
+ int isIdent(char c) { return isalpha(c); }
+ int isNum(char c) { return isdigit(c) || isNumPrefix(c); }
+ int isOperator(char c) { return strchr(operators, c) != 0; }
+ int isLogical(char c) { return strchr(logicalOps, c) != 0; }
+ int isNumPrefix(char c) { return strchr(numprefix, c) != 0; }
+
+ // data
+
+ int isStr;
+ const char* p;
+ char* buffer;
+ char _last[1000+TB];
+ int allowNumPrefix;
+
+ static const char* delimiters;
+ static const char* operators;
+ static const char* logicalOps;
+ static const char* whitespace;
+ static const char* numprefix;
+};
diff --git a/scraper2vdr.c b/scraper2vdr.c
new file mode 100644
index 0000000..26fd9db
--- /dev/null
+++ b/scraper2vdr.c
@@ -0,0 +1,106 @@
+/**
+ * GraphTFTng plugin for the Video Disk Recorder
+ *
+ * scraper2vdr.c
+ *
+ * (c) 2006-2015 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ **/
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <vdr/plugin.h>
+
+#include <common.h>
+#include <scraper2vdr.h>
+
+//***************************************************************************
+// Get Scraper Plugin
+//***************************************************************************
+
+cPlugin* GetScraperPlugin()
+{
+ static cPlugin* pScraper = cPluginManager::GetPlugin("scraper2vdr");
+
+ if (!pScraper)
+ pScraper = cPluginManager::GetPlugin("tvscraper");
+
+ return pScraper;
+}
+
+//***************************************************************************
+// Get Media Path
+//***************************************************************************
+
+int getScraperMediaPath(const cEventCopy* event, const cRecording* recording,
+ std::string& bannerPath, std::string& posterPath)
+{
+ static cPlugin* pScraper = GetScraperPlugin();
+ const cEvent* evt = 0;
+ const cSchedule* s = 0;
+ ScraperGetPosterBannerV2 call;
+
+ bannerPath = "";
+ posterPath = "";
+
+ if (!pScraper)
+ {
+ tell(0, "Warning: Plugin scraper2vdr not found");
+ return fail;
+ }
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ LOCK_SCHEDULES_READ;
+ const cSchedules* schedules = Schedules;
+#else
+ cSchedulesLock schedulesLock;
+ const cSchedules* schedules = (cSchedules*)cSchedules::Schedules(schedulesLock);
+#endif
+
+ if (recording)
+ {
+ event = 0;
+ call.recording = recording;
+ }
+ else if (event)
+ {
+ // lock und schedule (channel) holen
+
+ if (!schedules ||
+ !(s = (cSchedule*)schedules->GetSchedule(event->ChannelID())) ||
+ !(evt = s->GetEvent(event->EventID())))
+ {
+ tell(0, "Error, can't get lock on schedules or cant find event, aborting!");
+ return fail;
+ }
+
+ call.event = evt;
+ }
+
+ if (pScraper->Service("GetPosterBannerV2", &call))
+ {
+ if (call.type == tSeries && call.banner.path.size() > 0)
+ {
+ ScraperGetPoster callPoster;
+
+ bannerPath = call.banner.path;
+
+ callPoster.event = evt; // only one is set
+ callPoster.recording = recording; // " " " "
+
+ if (pScraper->Service("GetPoster", &callPoster))
+ posterPath = callPoster.poster.path;
+ }
+ else if (call.type == tMovie && call.poster.path.size() > 0 && call.poster.height > 0)
+ {
+ posterPath = call.poster.path;
+ }
+ }
+
+ return bannerPath.size() || posterPath.size() ? success : fail;
+}
diff --git a/scraper2vdr.h b/scraper2vdr.h
new file mode 100644
index 0000000..411ec8f
--- /dev/null
+++ b/scraper2vdr.h
@@ -0,0 +1,293 @@
+
+#ifndef __SCRAPER2VDRSERVICES_H
+#define __SCRAPER2VDRSERVICES_H
+
+#include <string>
+#include <vector>
+#include <vdr/epg.h>
+#include <vdr/recording.h>
+
+#include <display.h>
+
+//***************************************************************************
+// Functions
+//***************************************************************************
+
+int getScraperMediaPath(const cEventCopy* event, const cRecording* recording,
+ std::string& mediaPath, std::string& posterPath);
+
+//***************************************************************************
+// Types
+//***************************************************************************
+
+enum tvType
+{
+ tSeries,
+ tMovie,
+ tNone
+};
+
+/*********************************************************************
+* Helper Structures
+*********************************************************************/
+
+class cTvMedia
+{
+ public:
+
+ cTvMedia(void)
+ {
+ path = "";
+ width = height = 0;
+ };
+
+ std::string path;
+ int width;
+ int height;
+};
+
+class cEpisode
+{
+ public:
+
+ cEpisode(void)
+ {
+ number = 0;
+ season = 0;
+ name = "";
+ firstAired = "";
+ guestStars = "";
+ overview = "";
+ rating = 0.0;
+ };
+
+ int number;
+ int season;
+ std::string name;
+ std::string firstAired;
+ std::string guestStars;
+ std::string overview;
+ float rating;
+ cTvMedia episodeImage;
+};
+
+class cActor
+{
+ public:
+
+ cActor(void)
+ {
+ name = "";
+ role = "";
+ };
+
+ std::string name;
+ std::string role;
+ cTvMedia actorThumb;
+};
+
+/*********************************************************************
+* Data Structures for Service Calls
+*********************************************************************/
+
+// Data structure for service "GetEventType"
+
+class ScraperGetEventType
+{
+ public:
+
+ ScraperGetEventType(void)
+ {
+ event = NULL;
+ recording = NULL;
+ type = tNone;
+ movieId = 0;
+ seriesId = 0;
+ episodeId = 0;
+ };
+
+ // in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+
+ //out
+
+ tvType type; // typeSeries or typeMovie
+ int movieId;
+ int seriesId;
+ int episodeId;
+};
+
+// Data structure for full series and episode information
+
+class cMovie
+{
+ public:
+
+ cMovie(void)
+ {
+ title = "";
+ originalTitle = "";
+ tagline = "";
+ overview = "";
+ adult = false;
+ collectionName = "";
+ budget = 0;
+ revenue = 0;
+ genres = "";
+ homepage = "";
+ releaseDate = "";
+ runtime = 0;
+ popularity = 0.0;
+ voteAverage = 0.0;
+ };
+
+ // in
+
+ int movieId; // movieId fetched from ScraperGetEventType
+
+ // out
+
+ std::string title;
+ std::string originalTitle;
+ std::string tagline;
+ std::string overview;
+ bool adult;
+ std::string collectionName;
+ int budget;
+ int revenue;
+ std::string genres;
+ std::string homepage;
+ std::string releaseDate;
+ int runtime;
+ float popularity;
+ float voteAverage;
+ cTvMedia poster;
+ cTvMedia fanart;
+ cTvMedia collectionPoster;
+ cTvMedia collectionFanart;
+ std::vector<cActor> actors;
+};
+
+// Data structure for full series and episode information
+
+class cSeries
+{
+ public:
+
+ cSeries(void)
+ {
+ seriesId = 0;
+ episodeId = 0;
+ name = "";
+ overview = "";
+ firstAired = "";
+ network = "";
+ genre = "";
+ rating = 0.0;
+ status = "";
+ };
+
+ // in
+
+ int seriesId; // seriesId fetched from ScraperGetEventType
+ int episodeId; // episodeId fetched from ScraperGetEventType
+
+ // out
+
+ std::string name;
+ std::string overview;
+ std::string firstAired;
+ std::string network;
+ std::string genre;
+ float rating;
+ std::string status;
+ cEpisode episode;
+ std::vector<cActor> actors;
+ std::vector<cTvMedia> posters;
+ std::vector<cTvMedia> banners;
+ std::vector<cTvMedia> fanarts;
+ cTvMedia seasonPoster;
+};
+
+// Data structure for service "GetPosterBanner"
+
+class ScraperGetPosterBanner
+{
+ public:
+
+ ScraperGetPosterBanner(void)
+ {
+ type = tNone;
+ event = NULL;
+ };
+
+ // in
+
+ const cEvent *event; // check type for this event
+
+ // out
+
+ tvType type; // typeSeries or typeMovie
+ cTvMedia poster;
+ cTvMedia banner;
+};
+
+// Data structure for service "GetPosterBannerV2"
+
+class ScraperGetPosterBannerV2
+{
+ public:
+
+ ScraperGetPosterBannerV2(void)
+ {
+ type = tNone;
+ event = NULL;
+ recording = NULL;
+ };
+
+ // in
+
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // check type for this recording
+
+ // out
+
+ tvType type; // typeSeries or typeMovie
+ cTvMedia poster;
+ cTvMedia banner;
+};
+
+// Data structure for service "GetPoster"
+
+class ScraperGetPoster
+{
+ public:
+
+ // in
+
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+
+ //out
+
+ cTvMedia poster;
+};
+
+// Data structure for service "GetPosterThumb"
+
+class ScraperGetPosterThumb
+{
+ public:
+
+ // in
+
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+
+ // out
+
+ cTvMedia poster;
+};
+
+#endif //__SCRAPER2VDRSERVICES_H
diff --git a/scripts/create-channlelogo-symlinks.pl b/scripts/create-channlelogo-symlinks.pl
new file mode 100755
index 0000000..c788759
--- /dev/null
+++ b/scripts/create-channlelogo-symlinks.pl
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+
+if (not defined $ARGV[0]) {die "No parameter given! \ncreate-symlinks.pl <channels.conf> <logos> <link-dir>\n"};
+if (not defined $ARGV[1]) {die "No parameter given! \ncreate-symlinks.pl <channels.conf> <logos> <link-dir>\n"};
+if (not defined $ARGV[2]) {die "No parameter given! \ncreate-symlinks.pl <channels.conf> <logos> <link-dir>\n"};
+
+my $inpath0 = "$ARGV[1]";
+my $linkpath = "$ARGV[2]";
+
+%png0 = %png1 = ();
+
+# png folder einlesen
+opendir DIR, "$inpath0" or {die " cant open $inpath0" };
+
+while(my $file = readdir DIR) {
+ if( $file =~ /\.png$/) {
+ $value = $file;
+ $file =~ /(.*).png/;
+ $key = $1;
+ $key =~ s/\W//g;
+ $png0{$key} = $value;
+ }
+}
+closedir DIR;
+
+
+open LOG, ">>translate.log" or die "Can't open log file!\n";
+
+# channels.conf einlesen
+open (FILE, "< $ARGV[0]") or die "Can't open file\n";
+while (<FILE>) {
+ $channame = $shortname = '';
+ $line = $_;
+ $line =~ s/\r\n//;
+ if ($line =~ /^:/ or $line =~ /^@/ ) { next; }
+
+ @line = split(/:/, $line);
+ $line[0] =~ s/\'//;
+ $line[0] =~ s/\///;
+ if ($line[0] =~ m/;/) { $line[0] =~ /(.*);.*/; $line[0] = $1 }
+
+ if ($line[0] =~ m/,/) {
+ @names = split(/,/, $line[0]);
+ $channame = $names[0]; $shortname = $names[1];
+ }
+ else { $channame = $line[0]; $shortname = ''; }
+
+ if ($channame eq '' or $channame eq '.') { next; }
+
+ $searchname = $channame;
+ $searchname =~ s/\W//g;
+ $searchname =~ tr/[A-Z]/[a-z]/;
+
+ if ($png0{$searchname}) {
+ $cnt++;
+ $status = symlink("./../$inpath0/$png0{$searchname}","$linkpath/$channame.png");
+ if ($status == 1) { print LOG "$channame => ./../$inpath0/$png0{$searchname}"; }
+ else { print LOG "$channame => failed"; }
+ if ($shortname and $shortname ne '') {
+ $status = symlink("./../$inpath0/$png0{$searchname}","$linkpath/$shortname.png");
+ if ($status == 1) { print LOG "\t$shortname"; }
+ else { print LOG "\t$shortname => failed"; }
+ }
+ print LOG "\n"; next;
+ }
+ elsif ($shortname and $shortname ne '') {
+
+ $searchname = $shortname;
+ $searchname =~ s/\W//g;
+ $searchname =~ tr/[A-Z]/[a-z]/;
+
+ if ($png0{$searchname}) {
+ $cnt++;
+ $status = symlink("./../$inpath0/$png0{$searchname}","$linkpath/$shortname.png");
+ if ($status == 1) { print LOG "$channame => ./../$inpath0/$png0{$searchname}"; }
+ else { print LOG "$shortname => failed"; }
+ if ($channame and $channame ne '') {
+ $status = symlink("./../$inpath0/$png0{$searchname}","$linkpath/$shortname.png");
+ if ($status == 1) { print LOG "\t$channame"; }
+ else { print LOG "\t$channame => failed"; }
+ }
+ print LOG "\n"; next;
+ }
+ }
+
+ $searchname = $channame;
+ $searchname =~ s/\W//g;
+ $searchname =~ tr/[A-Z]/[a-z]/;
+
+ if ($png1{$searchname}) {
+ $cnt++;
+ $status = symlink("./../png1/$png1{$searchname}","$linkpath/$channame.png");
+ if ($status == 1) { print LOG "$channame => ./../png1/$png1{$searchname}"; }
+ else { print LOG "$channame => failed"; }
+ if ($shortname and $shortname ne '') {
+ $status = symlink("./../png1/$png1{$searchname}","$linkpath/$shortname.png");
+ if ($status == 1) { print LOG "\t$shortname"; }
+ else { print LOG "\t$shortname => failed"; }
+ }
+ print LOG "\n"; next;
+ }
+ elsif ($shortname and $shortname ne '') {
+
+ $searchname = $shortname;
+ $searchname =~ s/\W//g;
+ $searchname =~ tr/[A-Z]/[a-z]/;
+
+ if ($png1{$searchname}) {
+ $cnt++;
+ $status = symlink("./../png1/$png1{$searchname}","$linkpath/$shortname.png");
+ if ($status == 1) { print LOG "$channame => ./../png1/$png1{$searchname}"; }
+ else { print LOG "$shortname => failed"; }
+ if ($channame and $channame ne '') {
+ $status = symlink("./../png1/$png1{$searchname}","$linkpath/$channame.png");
+ if ($status == 1) { print LOG "\t$shortname"; }
+ else { print LOG "\t$channame => failed"; }
+ }
+ print LOG "\n"; next ;
+ }
+ }
+}
+close(FILE) or die "Can't close file\n";
+close(LOG) or die "Can't close file\n";
+
+print $cnt, "\n";
diff --git a/scripts/dia.sh b/scripts/dia.sh
new file mode 100755
index 0000000..e6e0fe5
--- /dev/null
+++ b/scripts/dia.sh
@@ -0,0 +1,84 @@
+
+INTERVAL=10
+IMAGE_DIR="/ablage/Pictures/"
+REPEAD=yes
+# MOUNTPOINT=/pub/images/Pictures
+
+FILE="/tmp/dia.file"
+LIST="/tmp/dia.list"
+
+# ---------------------------------------------------
+# stop ??
+# ---------------------------------------------------
+
+if [ "$1" == "stop" ]; then
+ svdrpsend.pl plug graphtft VIEW Standard
+ rm "$LIST"
+ exit 0
+fi
+
+# ---------------------------------------------------
+# check mount point (if required)
+# ---------------------------------------------------
+
+if [ -n "$MOUNTPOINT" ]; then
+ check-mount.sh "$MOUNTPOINT"
+
+ if [ "$?" == 1 ]; then
+ echo mount failed aborting diashow
+ exit 1
+ fi
+fi
+
+# ---------------------------------------------------
+# create list
+# ---------------------------------------------------
+
+echo creating image list
+find $IMAGE_DIR -name *.jpg -o -name *.JPG > "$LIST"
+
+# ---------------------------------------------------
+# switching mode
+# ---------------------------------------------------
+
+svdrpsend.pl plug graphtft VIEW Dia
+
+# ---------------------------------------------------
+# loop
+# ---------------------------------------------------
+
+echo starting show ...
+
+while [ -f "$LIST" ]; do
+
+ cat "$LIST" | while read i; do
+
+ # skip thumbnails
+
+ echo $i | grep "/\.pics/"
+
+ if [ "$?" == 1 ]; then
+ echo $i
+ echo $i > "$FILE"
+ svdrpsend.pl PLUG graphtft REFRESH 2>&1 > /dev/null
+
+ sleep $INTERVAL
+ else
+ echo skipping $i
+ fi
+
+ if [ ! -f "$LIST" ]; then
+ break
+ fi
+
+ done
+
+ if [ "$REPEAD" != "yes" ]; then
+ break
+ fi
+
+done
+
+rm -f "$LIST" "$FILE"
+exit 0
+
diff --git a/scripts/noadcall.sh b/scripts/noadcall.sh
new file mode 100755
index 0000000..814ece2
--- /dev/null
+++ b/scripts/noadcall.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+# this is an example-script for a noad-call with
+# different parameters for a call before or after
+# a recording is done
+# this script should be called from inside vdr via '-r '
+# e.g. vdr '-r /usr/local/sbin/noadcall.sh'
+
+# set the noad-binary here
+NOAD="/usr/bin/noad"
+
+# set the online-mode here
+# 1 means online for live-recording only
+# 2 means online for every recording
+ONLINEMODE="--online=1"
+
+# set additional args for every call here here
+ADDOPTS="--ac3 --overlap --jumplogo --comments"
+
+# set special args for a call with 'before' here
+# e.g. set a specail statistikfile
+BEFOREOPTS="--statisticfile=/video0/noadonlinestat"
+
+# set special args for a call with 'after' here
+# e.g. backup the marks from the online-call before
+# so you can compare the marks and see
+# how the marks vary between online-mode
+# and normal scan (backup-marks are in marks0.vdr)
+AFTEROPTS="--backupmarks --statisticfile=/video0/noadstat"
+
+# set the dir of the epg images
+EPGIMG_DIR="/video0/epgimages"
+
+echo "noadcall.sh $*" >> /tmp/noad.log
+echo "$NOAD $* $ONLINEMODE $ADDOPTS $AFTEROPTS" >> /tmp/noad.log
+
+case "$1" in
+ before)
+ $NOAD $* $ONLINEMODE $ADDOPTS $BEFOREOPTS
+ ;;
+ after)
+ # Try to copy the EPG-Image to Recording subdir
+
+ EVENTID=`cat $2/info.vdr | sed -n -e 's/^E //p'| awk -F " " '{print $1 }'`
+ echo "--------------------" >> /tmp/noadcall.log
+
+ if [ -f $EPGIMG_DIR/$EVENTID.png ]; then
+ cp $EPGIMG_DIR/$EVENTID.png $2/thumbnail_0.png
+ fi
+
+ n=1
+
+ for i in $EPGIMG_DIR/${EVENTID}_*.png; do
+ if [ -f $i ]; then
+ THUMB=thumbnail_$n.png
+ cp $i $2/$THUMB
+ n=`expr $n + 1`
+ fi
+ done
+
+ # .. other Noad/sharemarks stuff
+
+ $NOAD $* $ONLINEMODE $ADDOPTS $AFTEROPTS
+ ;;
+ edited)
+ # FSK protection beim verschieben uebernehmen
+
+ fsk=`echo $2 | sed s/"%"/""/ `
+ fsk=$fsk/protection.fsk
+ fsknew=$2/protection.fsk
+
+ if [ -f $fsk ]; then
+ touch $fsknew
+ fi
+
+ # Hier muss das Bildchen verschoben werden, VDR macht nicht
+
+ OLDREC=`echo $2 | sed s/"%"/""/ `
+
+ if [ -f $OLDREC/thumbnail.png ]; then
+ cp $OLDREC/thumbnail.png $2/
+ fi
+ ;;
+ rename)
+ # Nothing to do.
+ ;;
+ move)
+ # Nothing to do.
+ ;;
+ delete)
+ # Nothing to do.
+ ;;
+ *)
+ # Nothing to do.
+ ;;
+esac
+
diff --git a/service.h b/service.h
new file mode 100644
index 0000000..0107f6d
--- /dev/null
+++ b/service.h
@@ -0,0 +1,139 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File service.h
+// Date 21.11.06
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+// (c) 2006-2008 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class cGraphTftService
+//***************************************************************************
+
+#ifndef __GTFT_SERVICE_H__
+#define __GTFT_SERVICE_H__
+
+#define GRAPHTFT_TOUCH_EVENT_ID "GraphTftTouchEvent-v1.0"
+#define GRAPHTFT_CALIBRATION_ID "GraphTftCalibration-v1.0"
+
+#define GRAPHTFT_COVERNAME_ID "GraphTftCovername-v1.0"
+#define GRAPHTFT_PLAYLIST_ID "GraphTftPlaylist-v1.0"
+#define GRAPHTFT_STATUS_ID "GraphTftStatus-v1.0"
+#define GRAPHTFT_HELPBUTTONS_ID "GraphTftHelpButtons-v1.0"
+#define GRAPHTFT_INFO_ID "GraphTftInfo-v1.0"
+
+//***************************************************************************
+// cGraphTftService
+//***************************************************************************
+
+class cGraphTftComService
+{
+ public:
+
+ // Touch TFT Interface
+
+ enum Command
+ {
+ cmdUnknown = na,
+
+ cmdWelcome,
+ cmdData,
+ cmdMouseEvent,
+ cmdLogout,
+ cmdStartCalibration,
+ cmdStopCalibration,
+ cmdCheck,
+ cmdJpegQuality
+ };
+
+ enum MouseButton
+ {
+ mbLeft = 1,
+ mbMiddle,
+ mbRight,
+ mbWheelUp,
+ mbWheelDown
+ };
+
+ enum EventFlags
+ {
+ efNone = 0x00,
+
+ efDoubleClick = 0x01,
+ efShift = 0x02,
+ efAlt = 0x04,
+ efStrg = 0x08,
+ efKeyboard = 0x10,
+ efHWhipe = 0x20,
+ efVWhipe = 0x40
+ };
+
+#pragma pack(1)
+ struct GraphTftTouchEvent
+ {
+ int x;
+ int y;
+ int button;
+ int flag;
+ int data;
+ };
+#pragma pack()
+
+ struct GraphTftCalibration
+ {
+ int activate;
+ };
+
+ // Music Plugins
+
+ struct MusicServiceCovername
+ {
+ const char* name;
+ };
+
+ struct MusicServicePlaylist
+ {
+ int index;
+ int count;
+ const char* item;
+ };
+
+ struct MusicServicePlayerInfo
+ {
+ const char* filename;
+ const char* artist;
+ const char* album;
+ const char* genre;
+ const char* comment;
+ int year;
+ double frequence;
+ int bitrate;
+ const char* smode;
+ int index; // current index in tracklist
+ int count; // total items in tracklist
+ const char* status; // player status
+ const char* currentTrack;
+ bool loop;
+ bool shuffle;
+ bool shutdown;
+ bool recording;
+ int rating;
+ };
+
+ struct MusicServiceHelpButtons
+ {
+ const char* red;
+ const char* green;
+ const char* yellow;
+ const char* blue;
+ };
+
+ struct MusicServiceInfo
+ {
+ const char* info;
+ };
+};
+
+typedef cGraphTftComService cTftCS;
+
+//***************************************************************************
+#endif // __GTFT_SERVICE_H__
diff --git a/setup.c b/setup.c
new file mode 100644
index 0000000..c89064c
--- /dev/null
+++ b/setup.c
@@ -0,0 +1,335 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * setup.c - A plugin for the Video Disk Recorder
+ *
+ * (c) 2006-2013 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 "common.h"
+#include "setup.h"
+
+//***************************************************************************
+// Log Devices
+//***************************************************************************
+
+static const char* logDevices[] =
+{
+ "none",
+ "stdout",
+ "syslog",
+ "file"
+};
+
+cGraphTFTSetup GraphTFTSetup;
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cGraphTFTSetup::cGraphTFTSetup()
+{
+ Theme = "";
+ index = 0;
+ Level=0;
+ LogDevice=0;
+ Iso2Utf = yes;
+ HideMainMenu = 0;
+ yBorder = 5;
+ xBorder = 25;
+ xOffset = 0;
+ yOffset = 0;
+ DumpImage = 0;
+ DumpRefresh = 5;
+ JpegQuality = 60;
+ enableSpectrumAnalyzer = 0;
+ normalMode = "Standard";
+ storeNormalMode = true;
+ originalNormalMode = "";
+ width = 800;
+ height = 600;
+ themesPath = "";
+ configPath = "";
+ snapshotWidth = 240;
+ snapshotHeight = 180;
+ snapshotQuality = 90;
+ strcpy(snapshotPath, "/tmp");
+ strcpy(touchDevice, "/dev/input/event3");
+}
+
+//***************************************************************************
+// Setup Parse
+//***************************************************************************
+
+bool cGraphTFTSetup::SetupParse(const char* Name, const char* Value)
+{
+ if (!strcasecmp(Name, "Theme")) Theme = Value;
+ else if (!strcasecmp(Name, "HideMainMenu")) HideMainMenu = atoi(Value);
+ else if (!strcasecmp(Name, "Iso2Utf")) Iso2Utf = atoi(Value);
+ else if (!strcasecmp(Name, "SpectrumAnalyzer")) enableSpectrumAnalyzer = atoi(Value);
+ else if (!strcasecmp(Name, "XBorder")) xBorder = atoi(Value);
+ else if (!strcasecmp(Name, "YBorder")) yBorder = atoi(Value);
+ else if (!strcasecmp(Name, "XOffset")) xOffset = atoi(Value);
+ else if (!strcasecmp(Name, "YOffset")) yOffset = atoi(Value);
+ else if (!strcasecmp(Name, "DumpImage")) DumpImage = atoi(Value);
+ else if (!strcasecmp(Name, "DumpRefresh")) DumpRefresh = atoi(Value);
+ else if (!strcasecmp(Name, "LogDevice")) LogDevice = atoi(Value);
+ else if (!strcasecmp(Name, "Level")) Level = atoi(Value);
+ else if (!strcasecmp(Name, "JpegQuality")) JpegQuality = atoi(Value);
+ else if (!strcasecmp(Name, "flipOSD")) flipOSD = atoi(Value);
+ else if (!strcasecmp(Name, "normalMode")) normalMode = Value;
+ else if (!strcasecmp(Name, "width")) width = atoi(Value);
+ else if (!strcasecmp(Name, "height")) height = atoi(Value);
+
+ else if (!strcasecmp(Name, "snapshotWidth")) snapshotWidth = atoi(Value);
+ else if (!strcasecmp(Name, "snapshotHeight")) snapshotHeight = atoi(Value);
+ else if (!strcasecmp(Name, "snapshotQuality")) snapshotQuality = atoi(Value);
+ else if (!strcasecmp(Name, "snapshotPath")) strcpy(snapshotPath, Value);
+
+ else if (!strcasecmp(Name, "touchDevice")) strcpy(touchDevice, Value);
+ else if (!strcasecmp(Name, "touchYOffset")) touchSettings.offsetY = atoi(Value);
+ else if (!strcasecmp(Name, "touchXOffset")) touchSettings.offsetX = atoi(Value);
+ else if (!strcasecmp(Name, "touchSwapXY")) touchSettings.swapXY = atoi(Value);
+ else if (!strcasecmp(Name, "touchXScale")) touchSettings.scaleX = atof(Value);
+ else if (!strcasecmp(Name, "touchYScale")) touchSettings.scaleY = atof(Value);
+ else if (!strcasecmp(Name, "touchScaleWidth")) touchSettings.scaleWidth = atoi(Value);
+ else if (!strcasecmp(Name, "touchScaleHeight")) touchSettings.scaleHeight = atoi(Value);
+
+ else return false;
+
+ return true;
+}
+
+//***************************************************************************
+// Store
+//***************************************************************************
+
+void cGraphTFTSetup::Store(int force)
+{
+ if (Thms::theTheme != themes.Get(index))
+ {
+ Thms::theTheme = themes.Get(index);
+ Thms::theTheme->checkViewMode();
+ }
+
+ Theme = Thms::theTheme->getName();
+
+ plugin->SetupStore("Theme", Theme.c_str());
+ plugin->SetupStore("HideMainMenu", HideMainMenu);
+ plugin->SetupStore("Iso2Utf", Iso2Utf);
+ plugin->SetupStore("SpectrumAnalyzer", enableSpectrumAnalyzer);
+ plugin->SetupStore("XBorder", xBorder);
+ plugin->SetupStore("YBorder", yBorder);
+ plugin->SetupStore("XOffset", xOffset);
+ plugin->SetupStore("YOffset", yOffset);
+ plugin->SetupStore("DumpImage", DumpImage);
+ plugin->SetupStore("DumpRefresh", DumpRefresh);
+ plugin->SetupStore("LogDevice", LogDevice);
+ plugin->SetupStore("Level", Level);
+ plugin->SetupStore("JpegQuality", JpegQuality);
+ plugin->SetupStore("flipOSD", flipOSD);
+ plugin->SetupStore("Width", width);
+ plugin->SetupStore("Height", height);
+
+ if (storeNormalMode)
+ plugin->SetupStore("normalMode", normalMode.c_str());
+ else if (!originalNormalMode.empty())
+ plugin->SetupStore("normalMode", originalNormalMode.c_str());
+
+ plugin->SetupStore("snapshotWidth", snapshotWidth);
+ plugin->SetupStore("snapshotHeight", snapshotHeight);
+ plugin->SetupStore("snapshotQuality", snapshotQuality);
+ plugin->SetupStore("snapshotPath", snapshotPath);
+
+ plugin->SetupStore("touchDevice", touchDevice);
+ plugin->SetupStore("touchSwapXY", touchSettings.swapXY);
+ plugin->SetupStore("touchXOffset", touchSettings.offsetX);
+ plugin->SetupStore("touchYOffset", touchSettings.offsetY);
+ plugin->SetupStore("touchScaleWidth", touchSettings.scaleWidth);
+ plugin->SetupStore("touchScaleHeight", touchSettings.scaleHeight);
+
+ char* tmp;
+
+ asprintf(&tmp, "%f", touchSettings.scaleX);
+ plugin->SetupStore("touchXScale", tmp);
+ free(tmp);
+
+ asprintf(&tmp, "%f", touchSettings.scaleY);
+ plugin->SetupStore("touchYScale", tmp);
+ free(tmp);
+
+ if (force)
+ Setup.Save();
+}
+
+//***************************************************************************
+// cMenuSetupGraphTFT
+//***************************************************************************
+
+cMenuSetupGraphTFT::cMenuSetupGraphTFT(cGraphTFTDisplay* aDisplay)
+{
+ cGraphTFTTheme* t;
+ char* buf = 0;
+ int i;
+
+ for (int i = 0; i < 100; i++)
+ themeNames[i] = 0;
+
+ display = aDisplay;
+
+ SetSection(tr("GraphTFT"));
+
+ GraphTFTSetup.index = Thms::theTheme->Index();
+ tell(0, "Theme index is (%d)", GraphTFTSetup.index);
+
+ for (i = 0, t = themes.First(); t && i < 100; t = themes.Next(t), i++)
+ asprintf(&themeNames[i], "%s", t->getName().c_str());
+
+ if (themes.Count())
+ Add(new cMenuEditStraItem(tr("Theme"), &GraphTFTSetup.index, themes.Count(), themeNames));
+
+ Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &GraphTFTSetup.HideMainMenu));
+ Add(new cMenuEditBoolItem(tr("VDR use iso charset"), &GraphTFTSetup.Iso2Utf));
+ Add(new cMenuEditBoolItem(tr("Spectrum Analyzer (music)"), &GraphTFTSetup.enableSpectrumAnalyzer));
+
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("Dump Image"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditBoolItem(tr("Dump image to '/tmp/graphtftng.png'"),
+ &GraphTFTSetup.DumpImage));
+ Add(new cMenuEditIntItem(tr("Dump every [sec]"), &GraphTFTSetup.DumpRefresh,0,600));
+
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("Snapshot"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditIntItem(tr("Snapshot width"), &GraphTFTSetup.snapshotWidth, 1, 1920));
+ Add(new cMenuEditIntItem(tr("Snapshot height"), &GraphTFTSetup.snapshotHeight, 1, 1080));
+ Add(new cMenuEditIntItem(tr("Snapshot JPEG Quality"), &GraphTFTSetup.snapshotQuality, 0, 100));
+ Add(new cMenuEditStrItem(tr("Snapshot path"), GraphTFTSetup.snapshotPath,
+ cGraphTFTSetup::sizePath, trVDR(FileNameChars)));
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("FB/X Device"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditIntItem(tr("Width"), &GraphTFTSetup.width,1,1920));
+ Add(new cMenuEditIntItem(tr("Height"), &GraphTFTSetup.height,1,1080));
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("FB Device"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditBoolItem(tr("Flip OSD"), &GraphTFTSetup.flipOSD));
+ Add(new cMenuEditIntItem(tr("X Offset"), &GraphTFTSetup.xOffset,0,719));
+ Add(new cMenuEditIntItem(tr("Y Offset"), &GraphTFTSetup.yOffset,0,575));
+ Add(new cMenuEditIntItem(tr("Border to Width"), &GraphTFTSetup.xBorder,0,575));
+ Add(new cMenuEditIntItem(tr("Border to Height"), &GraphTFTSetup.yBorder,0,719));
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("TCP Connection"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditIntItem(tr("JPEG Quality"), &GraphTFTSetup.JpegQuality, 0, 100));
+
+#ifdef WITH_TOUCH
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("touch Device"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditStrItem(tr("Device"), GraphTFTSetup.touchDevice,
+ cGraphTFTSetup::sizePath, trVDR(FileNameChars)));
+#endif
+
+ asprintf(&buf, "---------------- %s ---------------------------------", tr("Log"));
+ Add(new cOsdItem(buf));
+ free(buf);
+ cList<cOsdItem>::Last()->SetSelectable(false);
+ Add(new cMenuEditStraItem(tr("Log Device"), &GraphTFTSetup.LogDevice, 4, logDevices));
+ Add(new cMenuEditIntItem(tr("Log Level"), &GraphTFTSetup.Level,0,10));
+
+#ifdef WITH_TOUCH
+ setHelp();
+#endif
+}
+
+cMenuSetupGraphTFT::~cMenuSetupGraphTFT()
+{
+ for (int i = 0; themeNames[i] && i < 100; i++)
+ free(themeNames[i]);
+
+ display->setCalibrate(off);
+}
+
+//***************************************************************************
+// Set help Keys
+//***************************************************************************
+
+void cMenuSetupGraphTFT::setHelp()
+{
+ SetHelp(0,
+ display->isMode(cGraphTFTService::ModeCalibration) ? 0 : tr("Test"),
+ display->isMode(cGraphTFTService::ModeCalibration) ? tr("Stop") : tr("Calibrate"),
+ 0);
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cMenuSetupGraphTFT::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown)
+ {
+ switch (Key)
+ {
+ case kOk:
+ {
+ Store();
+ return osBack;
+ }
+ case kGreen:
+ {
+ // activate/deactivate callibration test mode
+
+ display->switchCalibrate(cGraphTFTService::csTest);
+ setHelp();
+
+ return osContinue;
+ }
+ case kYellow:
+ {
+ // activate/deactivate callibration mode
+
+ display->switchCalibrate();
+ setHelp();
+
+ return osContinue;
+ }
+
+ default: break;
+ }
+ }
+ else if (state == osBack)
+ {
+ display->setCalibrate(off);
+ }
+
+ return state;
+}
+
+//***************************************************************************
+// Store
+//***************************************************************************
+
+void cMenuSetupGraphTFT::Store()
+{
+ GraphTFTSetup.Store();
+
+ if (display)
+ display->setupChanged();
+}
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..a145fcb
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,104 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * setup.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004-2013 Lars Tegeler, Sascha Volkenandt, 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 __GTFT_SETUP_H
+#define __GTFT_SETUP_H
+
+#include "display.h"
+
+//***************************************************************************
+// Class Graph TFT Setup
+//***************************************************************************
+
+class cGraphTFTSetup
+{
+ public:
+
+ // definitions
+
+ enum Size
+ {
+ sizePath = 255
+ };
+
+ cGraphTFTSetup();
+ ~cGraphTFTSetup() {}
+
+ string Theme; // name of the actual theme
+ int HideMainMenu;
+ int xBorder;
+ int yBorder;
+ int xOffset;
+ int yOffset;
+ int DumpImage;
+ int DumpRefresh;
+ int Level;
+ int LogDevice;
+ int Iso2Utf;
+
+ int enableSpectrumAnalyzer;
+ int index;
+ int JpegQuality;
+ int flipOSD;
+ int width;
+ int height;
+ // int redrawEvery;
+ string normalMode;
+ bool storeNormalMode;
+ string originalNormalMode;
+ string themesPath;
+ string configPath;
+ int snapshotWidth;
+ int snapshotHeight;
+ int snapshotQuality;
+ char snapshotPath[sizePath+TB];
+ char touchDevice[sizePath+TB];
+ cTouchThread::CalibrationSetting touchSettings;
+
+ bool SetupParse(const char* Name, const char* Value);
+ void Store(int force = no);
+ void setClient(cPlugin* aClient) { plugin = aClient; }
+
+ protected:
+
+ cPlugin* plugin;
+};
+
+//***************************************************************************
+// Class Menu Setup GraphTFT
+//***************************************************************************
+
+class cMenuSetupGraphTFT : public cMenuSetupPage
+{
+ public:
+
+ cMenuSetupGraphTFT(cGraphTFTDisplay* aDisplay);
+ virtual ~cMenuSetupGraphTFT();
+
+ virtual eOSState ProcessKey(eKeys Key);
+ void Store();
+ void setHelp();
+
+ protected:
+
+ cGraphTFTDisplay* display;
+
+ // data
+
+ char* themeNames[cGraphTFTSetup::sizePath+TB];
+};
+
+//***************************************************************************
+
+extern cGraphTFTSetup GraphTFTSetup;
+
+//***************************************************************************
+#endif // __GTFT_SETUP_H
diff --git a/span.h b/span.h
new file mode 100644
index 0000000..cf5507d
--- /dev/null
+++ b/span.h
@@ -0,0 +1,65 @@
+/*
+ * dspitems.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 26.05.07
+ */
+
+//***************************************************************************
+// Class Span Service
+//***************************************************************************
+
+#define SPAN_PROVIDER_CHECK_ID "Span-ProviderCheck-v1.0"
+#define SPAN_CLIENT_CHECK_ID "Span-ClientCheck-v1.0"
+#define SPAN_SET_PCM_DATA_ID "Span-SetPcmData-v1.0"
+#define SPAN_GET_BAR_HEIGHTS_ID "Span-GetBarHeights-v1.0"
+
+//***************************************************************************
+// Calss cSpanService
+//***************************************************************************
+
+class cSpanService
+{
+ public:
+
+ // Span requests to collect possible providers / clients
+
+ struct Span_Provider_Check_1_0
+ {
+ bool* isActive;
+ bool* isRunning;
+ };
+
+ struct Span_Client_Check_1_0
+ {
+ bool* isActive;
+ bool* isRunning;
+ };
+
+ // Span data
+
+ struct Span_SetPcmData_1_0
+ {
+ unsigned int length; // the length of the PCM-data
+ int* data; // the PCM-Data as 32-bit int, however only the lower 16-bit are used
+ // and you have to take care to hand in such data!
+ };
+
+ struct Span_GetBarHeights_v1_0
+ {
+ unsigned int bands; // number of bands to compute
+ unsigned int* barHeights; // the heights of the bars of the two channels combined
+ unsigned int* barHeightsLeftChannel; // the heights of the bars of the left channel
+ unsigned int* barHeightsRightChannel; // the heights of the bars of the right channel
+ unsigned int* volumeLeftChannel; // the volume of the left channels
+ unsigned int* volumeRightChannel; // the volume of the right channels
+ unsigned int* volumeBothChannels; // the combined volume of the two channels
+ const char* name; // name of the plugin that wants to get the data
+ // (must be unique for each client!)
+ unsigned int falloff; // bar falloff value
+ unsigned int* barPeaksBothChannels; // bar peaks of the two channels combined
+ unsigned int* barPeaksLeftChannel; // bar peaks of the left channel
+ unsigned int* barPeaksRightChannel; // bar peaks of the right channel
+ };
+};
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..3cef744
--- /dev/null
+++ b/status.c
@@ -0,0 +1,715 @@
+/**
+ * GraphTFTng plugin for the Video Disk Recorder
+ *
+ * status.c
+ *
+ * (c) 2013-2013 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ **/
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <display.h>
+
+//***************************************************************************
+// Osd Channel Switch
+//***************************************************************************
+
+void cGraphTFTDisplay::ChannelSwitch(const cDevice* Device, int ChannelNumber, bool LiveView)
+{
+ tell(5, "ChannelSwitch on %p: %d", Device, ChannelNumber);
+
+ if (LiveView
+ && _channel != ChannelNumber
+ && cDevice::CurrentChannel() != _channel)
+ {
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _rds.title.clear();
+ _rds.artist.clear();
+ _rds.text.clear();
+ _channel = ChannelNumber;
+ _channelGroup = "";
+
+ _presentEvent.reset();
+ _followingEvent.reset();
+ _event.reset();
+ _presentChannel = 0;
+
+ triggerChannelUpdate = yes;
+
+ updateGroup(groupChannel);
+ broadcast();
+ }
+}
+
+//***************************************************************************
+// Osd Channel
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdChannel(const char* text)
+{
+ tell(5, "OsdChannel: '%s'", text);
+
+ _channelGroup = text;
+
+ updateGroup(groupChannel);
+ broadcast();
+}
+
+//***************************************************************************
+// Osd Set Event
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdSetEvent(const cEvent* event)
+{
+ tell(5, "OsdSetEvent: '%s'", event ? event->Title() : "");
+
+ _event.set(event);
+ updateGroup(groupChannel);
+}
+
+//***************************************************************************
+// Osd Set Recording
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdSetRecording(const cRecording* recording)
+{
+ tell(5, "OsdSetRecording: '%s'", recording ? recording->Title() : "");
+
+ _recording = recording ? recording->FileName() : "";
+}
+
+//***************************************************************************
+// Osd Programme
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdProgramme(time_t PresentTime, const char* PresentTitle,
+ const char* PresentSubtitle, time_t FollowingTime,
+ const char* FollowingTitle, const char* FollowingSubtitle)
+{
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ if (PresentTitle)
+ _rds.title = PresentTitle;
+ else
+ _rds.title = "";
+
+ if (PresentSubtitle)
+ _rds.artist = PresentSubtitle;
+ else
+ _rds.artist = "";
+
+ broadcast();
+}
+
+//***************************************************************************
+// Set Volume
+//***************************************************************************
+
+void cGraphTFTDisplay::SetVolume(int, bool)
+{
+ tell(5, "SetVolume to %d, muted is %s",
+ cDevice::CurrentVolume(),
+ cDevice::PrimaryDevice()->IsMute() ? "true" : "false");
+
+ if (_volume != cDevice::CurrentVolume()
+ || _mute != cDevice::PrimaryDevice()->IsMute())
+ {
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _volume = cDevice::CurrentVolume();
+ _mute = cDevice::PrimaryDevice()->IsMute();
+
+ updateGroup(groupVolume);
+ broadcast(yes);
+ }
+}
+
+//***************************************************************************
+// Recording
+//***************************************************************************
+
+void cGraphTFTDisplay::Recording(const cDevice* Device, const char* Name,
+ const char* FileName, bool On)
+{
+ tell(5, "Recording %s to %p", Name, Device);
+
+ triggerTimerUpdate = yes;
+ broadcast();
+}
+
+//***************************************************************************
+// Timer Change
+//***************************************************************************
+
+void cGraphTFTDisplay::TimerChange(const cTimer *Timer, eTimerChange Change)
+{
+ tell(5, "TimerChange %s - %d ", Timer ? Timer->File() : "", Change);
+
+ triggerTimerUpdate = yes;
+ broadcast();
+}
+
+//***************************************************************************
+// Replaying
+//***************************************************************************
+
+void cGraphTFTDisplay::Replaying(const cControl* Control, const char* Name,
+ const char* FileName, bool On)
+{
+ static const char* suffixes[] = { ".ogg", ".mpg", ".mpeg", ".mp3", ".avi", 0 };
+
+ tell(5, "Replaying '%s' of '%s' file '%s'; control* is (%p)",
+ On ? "start" : "stop",
+ Name, Str::notNull(FileName),
+ Control);
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ if (On)
+ {
+ string::size_type i, n;
+
+ if (!Name) Name = "";
+
+ _replay.control = (cControl*)Control;
+ _replay.lastMode = _mode;
+ setMode(ReplayNormal);
+ _replay.fileName = Str::notNull(FileName);
+ // OsdSetRecording(Recordings.GetByName(Str::notNull(FileName)));
+
+ if (strlen(Name) > 6 && Name[0]=='[' && Name[3]==']' && Name[5]=='(')
+ {
+ for (i = 6; Name[i]; ++i)
+ {
+ if (Name[i] == ' ' && Name[i-1] == ')')
+ break;
+ }
+
+ if (Name[i])
+ {
+ // replaying mp3
+
+ const char* name = skipspace(Name + i);
+ _replay.name = *name ? name : tr("Unknown title");
+ setMode(ReplayMP3);
+ }
+ }
+ else if (strcmp(Name, "image") == 0)
+ {
+ if (!FileName)
+ _replay.name = Name;
+ else
+ _replay.name = basename(FileName);
+
+ setMode(ReplayImage);
+ }
+ else if (strcmp(Name, "DVD") == 0)
+ {
+ _replay.name = Name;
+ setMode(ReplayDVD);
+ }
+ else if (strlen(Name) > 7)
+ {
+ for (i = 1, n = 0; Name[i]; ++i)
+ {
+ if (Name[i] == ' ' && Name[i-1] == ',' && ++n == 4)
+ break;
+ }
+
+ if (Name[i])
+ {
+ // replaying DVD
+
+ const char *name = skipspace(Name + i);
+ _replay.name = *name ? name : tr("Unknown title");
+ replace(_replay.name.begin(), _replay.name.end(), '_', ' ');
+ setMode(ReplayDVD);
+ }
+ }
+
+ if (_mode == ReplayNormal)
+ {
+ _replay.name = Name;
+
+ if ((i = _replay.name.rfind('~')) != string::npos)
+ _replay.name.erase(0, i + 1);
+ }
+
+ char* tmp;
+ asprintf(&tmp, "%s", _replay.name.c_str());
+ _replay.name = Str::allTrim(tmp);
+ free(tmp);
+
+ for (int l = 0; suffixes[l]; l++)
+ {
+ if ((i = _replay.name.rfind(suffixes[l])) != string::npos)
+ _replay.name.erase(i);
+ }
+
+ tell(5, "reformatted name is '%s'", _replay.name.c_str());
+ updateGroup(groupReplay);
+ broadcast();
+ }
+
+ else
+ {
+ _replay.control = 0;
+ _replay.name = "";
+ _replay.fileName = "";
+
+ setMode(NormalView);
+ broadcast();
+ }
+}
+
+//***************************************************************************
+// Osd Status Message
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdStatusMessage(const char* Message)
+{
+ tell(5, "OsdStatusMessage %s", Message);
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ int force = _message != "";
+
+ _message = Message ? Message : "";
+
+ updateGroup(groupMessage);
+ broadcast(force);
+}
+
+//***************************************************************************
+// Osd Title
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdTitle(const char* Title)
+{
+ tell(5, "OsdTitle %s", Title);
+
+ if (Title)
+ {
+ string::size_type pos;
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _message = "";
+ _menu.title = Title;
+
+ if ((pos = _menu.title.find('\t')) != string::npos)
+ _menu.title.erase(pos);
+ }
+}
+
+//***************************************************************************
+// Osd Event Item (called after corresponding OsdItem!)
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdEventItem(const cEvent* Event, const char* Text,
+ int Index, int Count)
+{
+ tell(5, "OsdEventItem '%s' at index (%d), count (%d)",
+ Event ? Event->Title() : "<null>", Index, Count);
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _eventsReady = no;
+
+ if (_menu.items.size() <= (unsigned int)Index)
+ {
+ tell(1, "Got unexpected index %d, count is %ld", Index, _menu.items.size());
+ return;
+ }
+
+ if (_menu.items[Index].text != Text)
+ tell(0, "Fatal: Item index don't match!");
+ else if (Event)
+ _menu.items[Index].event.set(Event);
+
+ if (Index == Count-1)
+ {
+ tell(3, "OsdEventItem() Force update due to index/count (%d/%d)", Index, Count);
+
+ triggerFinalizeItemList = yes;
+ updateGroup(groupMenu);
+ broadcast();
+ }
+}
+
+//***************************************************************************
+// Osd Item
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdItem(const char* Text, int Index)
+{
+ MenuItem item;
+
+ tell(5, "OsdItem '%s' at index (%d)", Text, Index);
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _eventsReady = no;
+
+ if (!Text)
+ Text = "";
+
+ item.text = Text;
+ item.type = itNormal;
+ item.event.reset();
+ item.channel = 0;
+ item.tabCount = 0;
+ int count = 1;
+ char* pp;
+ _menu.currentRow = na;
+
+ const char* p = Text;
+
+ while (*p)
+ {
+ if (*p == '\t')
+ count++;
+ p++;
+ }
+
+ _message = "";
+
+ for (int i = 0; i < MaxTabs; ++i)
+ {
+ const char* tp = getTabbedText(Text, i);
+
+ if (tp)
+ {
+ char* tab;
+ int len;
+
+ tab = strdup(tp);
+ len = strlen(Str::allTrim(tab));
+
+ // bigger than last row ?
+
+ if (_menu.charInTabs[i] < len)
+ _menu.charInTabs[i] = len;
+
+ // detect partingLine
+
+ if (i == 0 && strstr(tab, "-----") && (pp = strchr(tab, ' ')))
+ {
+ char* e;
+ *pp = 0;
+ pp++;
+
+ item.type = itPartingLine;
+
+ if ((e = strchr(pp, ' ')))
+ *e = 0;
+
+ item.tabs[0] = Str::allTrim(pp);
+ }
+
+ else if (strstr(tab, "-----") && count < 2)
+ {
+ // arghdirector
+
+ item.type = itPartingLine;
+ item.tabs[0] = "";
+ }
+ else if (i == 1 && strstr(item.tabs[0].c_str(), "-----"))
+ {
+ char* p;
+
+ // some special handling for lines of epgsearch plugin like
+ // ---------------------------------- \t Die 24.10.2006 ------------------------
+
+ item.type = itPartingLine;
+
+ if ((p = strstr(tab, " ---")))
+ *p = 0;
+
+ if (!strstr(tab, "-----"))
+ item.tabs[0] = Str::allTrim(tab);
+ else
+ item.tabs[0] = "";
+
+ tell(5, "Detected parting line '%s'", item.tabs[0].c_str());
+ }
+ else
+ {
+ item.tabs[i] = tab;
+ item.recordingName = tab; // vermeintlicher name ...
+ }
+
+ tell(5, "tab (%d) '%s'", i, tp);
+ item.tabCount++;
+ free(tab);
+ }
+ else
+ break;
+ }
+
+ tell(4, "adding item with (%d) tabs", item.tabCount);
+ _menu.items.push_back(item);
+}
+
+//***************************************************************************
+// Osd Current Item
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdCurrentItem(const char* Text)
+{
+ if (!Text)
+ Text = "";
+
+ tell(3, "OsdCurrentItem '%s'", Text);
+
+ string text = Text;
+
+ if (text != _menu.current)
+ {
+ int i;
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _message = "";
+ _menu.current = text;
+
+ // lookup the item in the list and set the new current position
+
+ for (i = 0; i < (int)_menu.items.size(); i++)
+ {
+ if (_menu.items[i].text == _menu.current)
+ {
+ _menu.currentRow = i;
+
+ updateGroup(groupMenu);
+ broadcast();
+ return ;
+ }
+ }
+
+ // We don't have found the item, resume
+ // old one with changed values ...
+
+ if (_menu.currentRow < 0 || _menu.currentRow >= (int)_menu.items.size())
+ return ;
+
+ tell(5, "OsdCurrentItem: Text of current item changed to %s", Text);
+
+// #if __GNUC__ < 3
+// MenuItem* item = _menu.items[_menu.currentRow];
+// #else
+// MenuItem* item = _menu.items.at(_menu.currentRow);
+// #endif
+
+ _menu.items[_menu.currentRow].text = Text;
+
+ for (int i = 0; i < MaxTabs; ++i)
+ {
+ const char* tab = getTabbedText(Text, i);
+
+ if (tab)
+ _menu.items[_menu.currentRow].tabs[i] = tab;
+
+ if (getTabbedText(Text, i+1) == 0)
+ break;
+ }
+
+ updateGroup(groupMenu);
+ broadcast();
+ }
+}
+
+//***************************************************************************
+// Osd Clear
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdClear()
+{
+ string cg = _channelGroup;
+
+ tell(0, "OsdClear");
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _menu.title = "";
+ _menu.items.clear();
+ _eventsReady = no;
+ _menu.text = "";
+ _menu.current = "";
+ _channelGroup = "";
+
+ _menu.charInTabs[0] = _menu.charInTabs[1] = _menu.charInTabs[2] =
+ _menu.charInTabs[3] = _menu.charInTabs[4] = _menu.charInTabs[5] = 0;
+
+ if (_message != "" || cg != "")
+ {
+ _message = "";
+ updateGroup(groupMessage);
+ broadcast(yes);
+ }
+}
+
+//***************************************************************************
+// Osd Help Keys
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdHelpKeys(const char* Red, const char* Green,
+ const char* Yellow, const char* Blue)
+{
+ tell(5, "OsdHelpKeys: '%s', '%s', '%s', '%s'", Red, Green, Yellow, Blue);
+
+ string red = Red ? Red : "";
+ string green = Green ? Green : "";
+ string yellow = Yellow ? Yellow : "";
+ string blue = Blue ? Blue : "";
+
+ if (_menu.buttons[0] != red ||
+ _menu.buttons[1] != green ||
+ _menu.buttons[2] != yellow ||
+ _menu.buttons[3] != blue)
+ {
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _menu.buttons[0] = red;
+ _menu.buttons[1] = green;
+ _menu.buttons[2] = yellow;
+ _menu.buttons[3] = blue;
+
+ updateGroup(groupButton);
+ broadcast();
+ }
+}
+
+//***************************************************************************
+// Osd Text Item
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdTextItem(const char* Text, bool Scroll)
+{
+ tell(5, "OsdTextItem: '%s' scroll: %d", Text, Scroll);
+
+ if (!Text)
+ Text = "";
+
+ needLock = yes;
+ cMutexLock lock(&_mutex);
+
+ _rds.text = Text;
+ _menu.text = Text;
+ broadcast();
+}
+
+//***************************************************************************
+// Osd Menu Display
+//***************************************************************************
+
+#if defined _OLD_PATCH
+void cGraphTFTDisplay::OsdMenuDisplay(const char* kind)
+{
+ tell(5, "OsdMenuDisplay - kind '%s'", kind);
+
+ if (!isModeMenu(_mode))
+ _menu.lastMode = _mode;
+
+ setMode(ModeMenu, kind);
+}
+#else
+void cGraphTFTDisplay::OsdMenuDisplay(eMenuCategory category)
+{
+ struct cCategorie
+ {
+ eMenuCategory category;
+ const char* name;
+ };
+
+ cCategorie categories[] =
+ {
+ { mcUnknown, "Menu" },
+
+ { mcMain, "MenuMain" },
+
+ { mcSchedule, "MenuSchedule" },
+ { mcScheduleNow, "MenuWhatsOnNow" },
+ { mcScheduleNext, "MenuWhatsOnNext" },
+
+ { mcChannel, "MenuChannels" },
+ { mcChannelEdit, "MenuEditChannel" } ,
+
+ { mcTimer, "MenuTimers" },
+ { mcTimerEdit, "MneuTimerEdit" },
+
+ { mcRecording, "MenuRecordings" },
+ { mcRecordingInfo, "MenuRecording" },
+ { mcRecordingEdit, "MenuRecordingEdit" },
+
+ { mcPlugin, "MenuPlugin" },
+ { mcPluginSetup, "MenuPluginSetup" },
+
+ { mcSetup, "MenuSetup" },
+ { mcSetupOsd, "MenuSetupOsd" },
+ { mcSetupEpg, "MenuSetupEpg" },
+ { mcSetupDvb, "MenuSetupDvb" },
+ { mcSetupLnb, "MenuSetupLnb" },
+ { mcSetupCam, "MenuSetupCam" },
+ { mcSetupRecord, "MenuSetupRecord" },
+ { mcSetupReplay, "MenuSetupReplay" },
+ { mcSetupMisc, "MenuSetupMisc" },
+ { mcSetupPlugins, "MenuSetupPlugins" },
+
+ { mcCommand, "MenuCommand" },
+ { mcEvent, "MenuEvent" },
+ { mcText, "MenuText" },
+ { mcFolder, "MenuFolder" },
+ { mcCam, "MenuCam" },
+
+ { mcUndefined, "Menu" }
+ };
+
+ const char* categoryName = "MenuUnknown";
+
+ if (category > mcUndefined && category <= mcCam)
+ categoryName = categories[category].name;
+
+ tell(0, "OsdMenuDisplay - set category (%d) to '%s'", category, categoryName);
+
+ if (!isModeMenu(_mode))
+ _menu.lastMode = _mode;
+
+ setMode(ModeMenu, categoryName);
+}
+#endif
+
+//***************************************************************************
+// Osd Menu Destroy
+//***************************************************************************
+
+void cGraphTFTDisplay::OsdMenuDestroy()
+{
+ tell(5, "OsdMenuDestroy");
+
+ triggerTimerUpdate = yes;
+
+ // all menues closed !
+
+ if (isModeMenu(_mode)) // 'if' is needed for replay via extrecmenu
+ setMode(_menu.lastMode);
+
+ broadcast();
+}
diff --git a/sysinfo.c b/sysinfo.c
new file mode 100644
index 0000000..8de6e34
--- /dev/null
+++ b/sysinfo.c
@@ -0,0 +1,158 @@
+/*
+ * sysinfo.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 03.06.07
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glibtop.h>
+#include <glibtop/cpu.h>
+#include <glibtop/mem.h>
+
+#ifndef WITHOUT_VDR
+# include <vdr/tools.h>
+#endif
+
+#include <sysinfo.h>
+
+int Sysinfo::initialized = no;
+double Sysinfo::lastIdle = 0;
+double Sysinfo::lastTotal = 0;
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int Sysinfo::init()
+{
+ if (initialized)
+ return done;
+
+#if LIBGTOP_MAJOR_VERSION > 2 || (LIBGTOP_MAJOR_VERSION == 2 && LIBGTOP_MINOR_VERSION >= 14)
+ tell(0, "init libgtop (%d.%d)",
+ LIBGTOP_MAJOR_VERSION, LIBGTOP_MINOR_VERSION);
+ glibtop_init();
+#endif
+
+ initialized = yes;
+
+ return done;
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int Sysinfo::exit()
+{
+ if (!initialized)
+ return done;
+
+#if LIBGTOP_MAJOR_VERSION > 2 || (LIBGTOP_MAJOR_VERSION == 2 && LIBGTOP_MINOR_VERSION >= 14)
+ glibtop_close();
+#endif
+
+ initialized = no;
+
+ return done;
+
+}
+
+//***************************************************************************
+// Get CPU Load
+//***************************************************************************
+
+int Sysinfo::cpuLoad()
+{
+ static uint64_t lastCall = 0;
+ static int lastLoad = na;
+
+ if (!initialized)
+ init();
+
+ int load;
+ glibtop_cpu cpu;
+
+#ifndef WITHOUT_VDR
+ if (lastCall && lastLoad != na && cTimeMs::Now() - lastCall < 200)
+ return lastLoad;
+#endif
+
+ glibtop_get_cpu(&cpu);
+
+ double total = ((unsigned long)cpu.total) ? ((double)cpu.total) : 1.0;
+ double idle = ((unsigned long)cpu.idle) ? ((double)cpu.idle) : 1.0;
+
+ total /= (double)cpu.frequency;
+ idle /= (double)cpu.frequency;
+
+ // calc tic deltas (from last)
+
+ double loadTics = total- idle - lastTotal;
+ double idleTics = idle - lastIdle;
+
+ // calc load
+
+ load = (int) (loadTics * 100 / (loadTics + idleTics));
+
+ // remember
+
+ lastTotal = total - idle;
+ lastIdle = idle;
+ lastLoad = load;
+
+#ifndef WITHOUT_VDR
+ lastCall = cTimeMs::Now();
+#endif
+
+ return load;
+}
+
+//***************************************************************************
+// Get Memory Info
+//***************************************************************************
+
+int Sysinfo::memInfoMb(unsigned long& total,
+ unsigned long& used,
+ unsigned long& free,
+ unsigned long& cached)
+{
+ glibtop_mem memory;
+
+ if (!initialized)
+ init();
+
+ glibtop_get_mem(&memory);
+
+ total = (unsigned long)memory.total/(1024*1024);
+ used = (unsigned long)memory.used/(1024*1024);
+ free = (unsigned long)memory.free/(1024*1024);
+ cached = (unsigned long)memory.cached/(1024*1024);
+
+ /*
+ tell(4, "\nMEMORY USING\n\n"
+ "Memory Total : %ld MB\n"
+ "Memory Used : %ld MB\n"
+ "Memory Free : %ld MB\n"
+ "Memory Buffered : %ld MB\n"
+ "Memory Cached : %ld MB\n"
+ "Memory user : %ld MB\n"
+ "Memory Locked : %ld MB\n",
+ total, used, free,
+ (unsigned long)memory.shared/(1024*1024),
+ (unsigned long)memory.buffer/(1024*1024),
+ (unsigned long)memory.cached/(1024*1024),
+ (unsigned long)memory.user/(1024*1024),
+ (unsigned long)memory.locked/(1024*1024));
+ */
+
+ return done;
+}
+
+//***************************************************************************
+
diff --git a/sysinfo.h b/sysinfo.h
new file mode 100644
index 0000000..fdc3325
--- /dev/null
+++ b/sysinfo.h
@@ -0,0 +1,39 @@
+/*
+ * sysinfo.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * Date: 03.06.07
+ *
+ */
+
+#ifndef __SYSINFO_H
+#define __SYSINFO_H
+
+#include <common.h>
+
+//***************************************************************************
+// Sysinfo
+//***************************************************************************
+
+class Sysinfo
+{
+ public:
+
+ // interface
+
+ static int init();
+ static int exit();
+ static int cpuLoad();
+ static int memInfoMb(unsigned long& total, unsigned long& used,
+ unsigned long& free, unsigned long& cached);
+
+ private:
+
+ static double lastIdle;
+ static double lastTotal;
+ static int initialized;
+};
+
+//***************************************************************************
+#endif // __SYSINFO_H
diff --git a/tcpchannel.c b/tcpchannel.c
new file mode 100644
index 0000000..178d5fa
--- /dev/null
+++ b/tcpchannel.c
@@ -0,0 +1,422 @@
+//***************************************************************************
+// Group VDR/GraphTFT
+// File tcpchannel.c
+// 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-2013 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 "tcpchannel.h"
+#include "common.h"
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+TcpChannel::TcpChannel(int aTimeout, int aHandle)
+{
+ handle = aHandle;
+ timeout = aTimeout;
+
+ localAddr = 0;
+ port = 0;
+
+ nTtlSent = 0;
+ nTtlReceived = 0;
+
+ lookAheadChar = false;
+ lookAhead = 0;
+}
+
+TcpChannel::~TcpChannel()
+{
+ close();
+}
+
+//***************************************************************************
+// Open -> Start Listener
+//***************************************************************************
+
+int TcpChannel::open(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;
+}
+
+//***************************************************************************
+// Close
+//***************************************************************************
+
+int TcpChannel::close()
+{
+ if (handle)
+ {
+ ::close(handle);
+ handle = 0;
+ }
+
+ 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;
+
+ cMutexLock lock(&_mutex);
+
+ if (buf && !bufLen)
+ bufLen = strlen(buf);
+
+ tell(3, "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(3, "Writing (%uld) 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 = 1;
+
+ // 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;
+}
+
+//***************************************************************************
+// Look
+//***************************************************************************
+
+int TcpChannel::look(int aTimeout)
+{
+ struct timeval tv;
+ fd_set readFD, writeFD;
+ 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_SET(handle, &readFD);
+ FD_SET(handle, &writeFD);
+
+ // look event
+
+ n = ::select(handle+1, &readFD, (aTimeout ? 0 : &writeFD) , 0, &tv);
+
+ if (n < 0)
+ return checkErrno();
+
+// // 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;
+}
+
+//***************************************************************************
+// 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 = 1;
+
+ // 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;
+ tell(3, "Debug: Received %d bytes\n", nReceived);
+
+ 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/tcpchannel.h b/tcpchannel.h
new file mode 100644
index 0000000..bf7ca1c
--- /dev/null
+++ b/tcpchannel.h
@@ -0,0 +1,92 @@
+//***************************************************************************
+// 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-2008 Jörg Wendel
+//--------------------------------------------------------------------------
+// Class TcpChannel
+//***************************************************************************
+
+#ifndef __GTFT_TCPCHANNEL_H__
+#define __GTFT_TCPCHANNEL_H__
+
+#include <vdr/thread.h>
+
+//***************************************************************************
+// Class TcpChannel
+//***************************************************************************
+
+class TcpChannel
+{
+ public:
+
+ enum Errors
+ {
+ errChannel = -100,
+
+ errUnknownHostname, // 99
+ errBindAddressFailed, // 98
+ errAcceptFailed,
+ errListenFailed,
+ errConnectFailed, // 95
+ errIOError, // 94
+ errConnectionClosed, // 93
+ errInvalidEndpoint,
+ errOpenEndpointFailed, // 91
+
+ // Warnungen
+
+ wrnNoEventPending, // 90
+ errUnexpectedEvent, // 89
+ wrnChannelBlocked, // 88
+ wrnNoConnectIndication, // 87
+ wrnNoResponseFromServer, // 86
+ wrnNoDataAvaileble, // 85
+ wrnSysInterrupt, // 84
+ wrnTimeout // 83
+ };
+
+#pragma pack(1)
+ struct Header
+ {
+ int command;
+ int size;
+ };
+#pragma pack()
+
+ TcpChannel(int aTimeout = 2, int aHandle = 0);
+ ~TcpChannel();
+
+ int open(unsigned short aPort, const char* aLocalHost = 0);
+ int close();
+ int listen(TcpChannel*& child);
+ int look(int aTimeout = 0);
+ int read(char* buf, int bufLen);
+ int write(int command, const char* buf = 0, int bufLen = 0);
+
+ int isConnected() { return handle != 0; }
+ int getHandle() { return handle; }
+
+ private:
+
+ int checkErrno();
+
+ // data
+
+ int lookAheadChar;
+ int lookAhead;
+
+ int handle;
+ unsigned short port;
+ long localAddr;
+ long nTtlSent;
+ long nTtlReceived;
+ long timeout; // for read/write
+
+ cMutex _mutex;
+};
+
+//***************************************************************************
+#endif // __GTFT_TCPCHANNEL_H__
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..f3e6338
--- /dev/null
+++ b/test.c
@@ -0,0 +1,127 @@
+
+#define __USE_POSIX199309
+
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string>
+
+#include <scan.h>
+
+using std::string;
+
+#define success 0
+#define TB 1
+
+//***************************************************************************
+// Variable Provider
+//***************************************************************************
+
+class VariableProvider
+{
+ public:
+
+ VariableProvider() {};
+ virtual ~VariableProvider() {};
+
+ int calcExpression(const char* expression);
+ int calc(const char* op, int left, int right);
+};
+
+//***************************************************************************
+// Calculate Expression
+// calc expressions like: "2 + 4"
+//***************************************************************************
+
+int VariableProvider::calcExpression(const char* expression)
+{
+ Scan* scan;
+ int result = 0;
+ char op[100]; *op = 0;
+
+ if (!expression || !*expression)
+ return 0;
+
+ scan = new Scan(expression, no);
+
+ // left expression
+
+ if (scan->eat(yes) != success || !scan->isNum())
+ {
+ delete scan;
+ return result;
+ }
+
+ result = scan->lastInt();
+
+ while (scan->eat() == success && scan->isOperator())
+ {
+ strcpy(op, scan->lastIdent());
+
+ if (scan->eat(yes) != success || !scan->isNum())
+ {
+ delete scan;
+ return result;
+ }
+
+ result = calc(op, result, scan->lastInt());
+ }
+
+ delete scan;
+
+ return result;
+}
+
+int VariableProvider::calc(const char* op, int left, int right)
+{
+ int result = left;
+
+ if (*op == '+')
+ result += right;
+ else if (*op == '-')
+ result -= right;
+ else if (*op == '*')
+ result *= right;
+ else if (*op == ':')
+ result /= right;
+
+ return result;
+}
+
+//***************************************************************************
+// tescht
+//***************************************************************************
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char** argv)
+{
+ int res = 0;
+ VariableProvider v;
+
+ // res = v.calcExpression(argv[1]);
+ // printf("result = %d\n", res);
+
+ int x = 250;
+ int width = 400;
+ int dspWidth = 1360;
+ int themeWidth = 1600;
+
+ double xScale = (double)dspWidth / (double)themeWidth;
+
+ int xDsp = x * xScale;
+ int widthDsp = width * xScale;
+
+ printf("%d / %d = %.2f \n", dspWidth, themeWidth, xScale);
+ printf("%d / %.2f = %d \n", width, xScale, widthDsp);
+
+ return 0;
+}
diff --git a/theme.c b/theme.c
new file mode 100644
index 0000000..67f6bc6
--- /dev/null
+++ b/theme.c
@@ -0,0 +1,2012 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * theme.c - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2013 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/inotify.h>
+
+#include <theme.h>
+#include <common.h>
+#include <scan.h>
+
+#include "setup.h"
+
+//***************************************************************************
+// The Theme
+//***************************************************************************
+
+cGraphTFTTheme* Thms::theTheme = 0;
+cGraphTFTThemes themes;
+
+//***************************************************************************
+// Convert Channel-Name in Logo Path
+//***************************************************************************
+
+string VariableProvider::channelLogoPath(const char* channel,
+ const char* format, int classic)
+{
+ unsigned int s = 0;
+ unsigned int d = 0;
+ unsigned int len = strlen(channel);
+ char* file = (char *)malloc(len+TB);
+
+ // remove '\/:*?"<>| character
+
+ while (channel[s])
+ {
+ if (!strchr("/':*?\"<>\\", channel[s]))
+ file[d++] = channel[s];
+
+ s++;
+ }
+
+ file[d] = 0;
+
+ // special handling fore some channels
+
+ if (!rep(file, "D [0-9] \\- ...."))
+ {
+ free(file);
+ file = strdup("DIREKT");
+ }
+
+ string path;
+
+ if (classic)
+ path = "columnimages/" + string(file) + "."
+ + ((format && *format) ? string(format) : "png");
+ else
+ path = string(file);
+
+ free(file);
+
+ tell(5, "converted logo name is '%s'", path.c_str());
+
+ return path;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+const char* VariableProvider::splitFormatValue(const char* data,
+ char* value, char* format)
+{
+ const char* f = strchr(data, '/');
+
+ if (f)
+ {
+ strncpy(value, data, f-data);
+ value[f-data] = 0;
+ strcpy(format, f+1);
+
+ return value;
+ }
+
+ strcpy(value, data);
+ *format = 0;
+
+ return value;
+}
+
+//***************************************************************************
+// Variable Of
+// - returns the first variable name in expession
+// "{varTest} xx {varHallo}" -> varTest
+//***************************************************************************
+
+int VariableProvider::variableOf(string& name, const char* expression, char*& e)
+{
+ const char* s;
+
+ tell(4, "variableOf '%s'", expression);
+
+ if ((s = strchr(expression, '{')))
+ {
+ s++;
+
+ if (!(e = (char *)strchr(s, '}')))
+ {
+ tell(0, "Parsing of [%s] failed, missing bracket '}'", expression);
+ return fail;
+ }
+
+ // found expression
+
+ name = string(expression, s-expression, e-s);
+ tell(4, "variable '%s' found", name.c_str());
+
+ return success;
+ }
+
+ return fail;
+}
+
+//***************************************************************************
+// Evaluate Variable
+//***************************************************************************
+
+int VariableProvider::evaluate(string& buf, const char* var)
+{
+ const char* s;
+ const char* e;
+ string v;
+ const char* p = var;
+ char exp[1000+TB];
+ int len;
+ char tmp[1+TB];
+
+ buf = "";
+
+ tell(4, "evaluating '%s'", var);
+
+ while ((s = strchr(p, '{')))
+ {
+ if (s > p)
+ buf.append(p, s-p);
+
+ if (!(e = strchr(p, '}')))
+ {
+ tell(0, "Parsing of [%s] failed, missing bracket '}'", var);
+ return fail;
+ }
+
+ // found expression
+
+ e--;
+ len = min((int)(e-s), 1000);
+
+ if (len < 1)
+ {
+ tell(0, "Parsing of [%s] failed, bracket missmatch", var);
+ return fail;
+ }
+
+ strncpy(exp, s+1, len);
+ exp[len] = 0;
+
+ tell(4, "variable '%s' found", exp);
+
+ if (*exp == '\\' && strlen(exp) > 1)
+ {
+ tmp[0] = (char)atoi(exp+1);
+ tmp[1] = 0;
+ v = tmp;
+
+ tell(4, "found character expression '%s'", v.c_str());
+ }
+ else
+ {
+ char fmt[100+TB];
+ char val[255+TB];
+
+ // split if expression contais format string
+ // in a expression a '/' always start a format string or special function
+
+ splitFormatValue(exp, val, fmt);
+
+ tell(4, "expression '%s' with variable '%s' and format '%s'",
+ exp, val, fmt);
+
+ // lookup variable
+
+ if (lookupVariable(val, v, fmt) != success)
+ return fail;
+
+ tell(4, "I Variable '%s' evaluated, value is '%s'", val, v.c_str());
+
+ string vv;
+
+ if (strchr(v.c_str(), '{') && evaluate(vv, v.c_str()) == success)
+ {
+ tell(4, "II Variable '%s' evaluated, value is '%s'", v.c_str(), vv.c_str());
+ v = vv;
+ }
+
+ // do the format stuff
+
+ if (!Str::isEmpty(fmt) && strcasecmp(fmt, "toLogo") == 0)
+ v = channelLogoPath(v.c_str(), 0, no);
+ }
+
+ buf.append(v);
+
+ p = e+2;
+ }
+
+ buf.append(p);
+
+ return success;
+}
+
+//***************************************************************************
+// Calculate Expression
+// calc expressions like: "2 + 4"
+//***************************************************************************
+
+int VariableProvider::calcExpression(const char* expression)
+{
+ Scan* scan;
+ int result = 0;
+ char op[100]; *op = 0;
+
+ if (!expression || !*expression)
+ return 0;
+
+ scan = new Scan(expression, no);
+
+ // left expression
+
+ if (scan->eat(yes) != success || !scan->isNum())
+ {
+ delete scan;
+ return result;
+ }
+
+ result = scan->lastInt();
+
+ while (scan->eat() == success && scan->isOperator())
+ {
+ strcpy(op, scan->lastIdent());
+
+ if (scan->eat(yes) != success || !scan->isNum())
+ {
+ delete scan;
+ return result;
+ }
+
+ result = calc(op, result, scan->lastInt());
+ }
+
+ delete scan;
+
+ return result;
+}
+
+int VariableProvider::calc(const char* op, int left, int right)
+{
+ int result = left;
+
+ if (*op == '+')
+ result += right;
+ else if (*op == '-')
+ result -= right;
+ else if (*op == '*')
+ result *= right;
+ else if (*op == ':')
+ result /= right;
+
+ return result;
+}
+
+//***************************************************************************
+// cThemeService
+//***************************************************************************
+
+const char* cThemeService::items[] =
+{
+ "Include",
+ "Theme",
+
+ "Text",
+ "Image",
+ "ImageFile",
+ "ImageDir",
+ "Rectangle",
+ "Timebar",
+
+ "Message",
+ "VolumeMuteSymbol",
+ "Volumebar",
+
+ "Menu",
+ "MenuSelected",
+
+ "MenuButtonRed",
+ "MenuButtonGreen",
+ "MenuButtonYellow",
+ "MenuButtonBlue",
+
+ "MenuButtonBackgroundRed",
+ "MenuButtonBackgroundGreen",
+ "MenuButtonBackgroundYellow",
+ "MenuButtonBackgroundBlue",
+
+ "MenuImageMap",
+
+ "SpectrumAnalyzer",
+ "PartingLine",
+ "Sysinfo",
+ "Background",
+ "TextList",
+ "Progressbar",
+
+ "ClickArea",
+ "MenuNavigationArea",
+ "CalibrationCursor",
+
+ "Column",
+ "ColumnSelected",
+
+ "EventColumn",
+ "EventColumnSelected",
+
+ "Defaults",
+ "VariableFile",
+
+ 0
+};
+
+const char* cThemeService::toName(ItemKind aItem)
+{
+ if (!isValid(aItem))
+ return "unknown";
+
+ return items[aItem];
+}
+
+cThemeService::ItemKind cThemeService::toItem(const char* aName)
+{
+ for (int i = 0; items[i]; i++)
+ if (strcasecmp(items[i], aName) == 0)
+ return (ItemKind)i;
+
+ return itemUnknown;
+}
+
+//***************************************************************************
+// Translations
+//***************************************************************************
+
+cThemeService::Translation cThemeService::alignments[] =
+{
+ { algLeft, "left" },
+ { algCenter, "center" },
+ { algRight, "right" },
+ { 0, 0 }
+};
+
+cThemeService::Translation cThemeService::scrollmodes[] =
+{
+ { smOff, "off" },
+ { smMarquee, "marquee" },
+ { smTicker, "ticker" },
+ { 0, 0 }
+};
+
+int cThemeService::toDenum(Translation t[], const char* value)
+{
+ for (int i = 0; t[i].name; i++)
+ {
+ if (strcasecmp(t[i].name, value) == 0)
+ return t[i].denum;
+ }
+
+ return na;
+}
+
+//***************************************************************************
+// Class cThemeItem
+//***************************************************************************
+
+cThemeSection* cThemeItem::currentSection = 0;
+string cThemeItem::lineBuffer = "";
+string cThemeItem::condition = "";
+
+cThemeItem::cThemeItem()
+{
+ _item = itemUnknown;
+
+ _x = "-1";
+ _y = "-1";
+ _width = "0";
+ _height = "0";
+
+ _bg_x = na;
+ _bg_y = na;
+ _image_map = 0;
+ _stat_pic = 0;
+ _stat_text=0;
+ _stat_width = 0;
+ _stat_height = 0;
+ _stat_x = 0;
+ _stat_y = 0;
+ _bg_width = 0;
+ _bg_height = 0;
+ _overlay = no;
+ _lines = 0;
+ _start_line = "0";
+ _line = 0;
+ _size = 32;
+ _switch = no;
+ _align = Ts::algLeft;
+ _align_v = 0;
+ _delay=0;
+ _foreground = no;
+ _count=0;
+ _spacing = 0;
+ _yspacing = 5;
+ _bar_height = 100;
+ _bar_height_unit = iuPercent;
+ _scroll = smOff;
+ _scroll_count = 0;
+ _dots = no;
+ _permanent = no;
+ _factor = 1;
+ _aspect_ratio = no;
+ _rotate = 0; // { 0 - off; 1 - auto; }
+ _fit = no;
+ _value = "";
+ _total = "";
+
+ _menu_x = 0;
+ _menu_y = 0;
+ _menu_width = 0;
+ _menu_height = 0;
+
+ _color = "255:255:255:255";
+ _bg_color = "0:0:0:255";
+ _unit = "";
+ _reference = "";
+ _font = "graphTFT";
+ _type = "";
+ _format = "";
+ _path = "";
+ _path2 = "";
+ _focus = "";
+ _text = "";
+ _number = na;
+ _index = 0;
+ _whipe_res = 20;
+ _onClick = "";
+ _onDblClick = "";
+ _onUp = "";
+ _onDown = "";
+ _sectionInclude = "";
+ _condition = "";
+ _debug = "-";
+ _id = "";
+ _area = "";
+
+ section = 0;
+ pathCount = 0;
+}
+
+cThemeItem::~cThemeItem()
+{
+}
+
+//***************************************************************************
+// Parse one line => one Item
+//***************************************************************************
+
+bool cThemeItem::Parse(const char* s)
+{
+ cDisplayItem* dspItem;
+ string tmp;
+ string::size_type posA;
+ string::size_type posB;
+ int value;
+ string toParse;
+
+ // skip whitespace
+
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+
+ // skip empty lines
+
+ if (Str::isEmpty(s))
+ return true;
+
+ // append
+
+ lineBuffer.append(s);
+
+ // skip line and inline comments
+
+ if ((posA = lineBuffer.find("//")) != string::npos)
+ {
+ lineBuffer.erase(posA);
+
+ if (Str::isBlank(lineBuffer.c_str()))
+ return true;
+ }
+
+ // parse directives
+
+ if ((posA = lineBuffer.find("#")) == 0)
+ {
+ parseDirectives(lineBuffer);
+ lineBuffer.clear();
+ return true;
+ }
+
+ if (Thms::theTheme->isSkipContent())
+ {
+ lineBuffer.clear();
+ tell(2, "Skipping line due to directive [%.50s%s]",
+ s, strlen(s) > 50 ? ".." : "");
+ return true;
+ }
+
+ // parse variables
+
+ if ((posA = lineBuffer.find("var ")) == 0)
+ {
+ string tmp = lineBuffer.substr(posA+4);
+ parseVariable(tmp, currentSection);
+ lineBuffer.clear();
+
+ return true;
+ }
+
+ // parse condition
+
+ if ((posA = lineBuffer.find("if ")) == 0)
+ {
+ condition = lineBuffer.substr(posA+3);
+ lineBuffer.clear();
+
+ return true;
+ }
+ else if (lineBuffer.find("endif") == 0)
+ {
+ condition = "";
+ lineBuffer.clear();
+ return true;
+ }
+
+ // check if it is a section start
+
+ posA = lineBuffer.find("[");
+ posB = lineBuffer.find("]", posA);
+
+ if (posA == 0 && posB > 0 && posB != string::npos)
+ {
+ // section ...
+
+ tmp = lineBuffer.substr(posA+1, posB-1);
+
+ // append section
+
+ currentSection = new cThemeSection(tmp);
+ Thms::theTheme->getSections()->Add(currentSection);
+ Thms::theTheme->addNormalSection(currentSection->getName());
+ lineBuffer.clear();
+
+ return true;
+ }
+
+ // line completed ?
+
+ if (lineBuffer.find(";") == string::npos)
+ {
+ tell(5, "line buffer now [%s]", lineBuffer.c_str());
+ return true;
+ }
+
+ // ----------------------------
+ //
+
+ tell(4, "line completed, processing [%s]", toParse.c_str());
+ toParse = lineBuffer;
+ lineBuffer.clear();
+
+ if (!currentSection)
+ return true;
+
+ // include section ...
+
+ if (ParseVar(toParse, "Include", &tmp) == success)
+ {
+ if ((dspItem = newDisplayItem(itemSectionInclude)))
+ {
+ currentSection->Add(dspItem);
+ dspItem->_sectionInclude = tmp;
+ }
+
+ return true;
+ }
+
+ // item ...
+
+ if ((posA = toParse.find(" ")) == string::npos)
+ {
+ tell(0, "Ignoring invalid theme line [%.50s%s]",
+ s, strlen(s) > 50 ? ".." : "");
+
+ return true;
+ }
+
+ // parse item attributes ..
+
+ _item = toItem(toParse.substr(0, posA).c_str());
+
+ switch (_item)
+ {
+ case itemVarFile:
+ {
+ if ((dspItem = newDisplayItem(itemVarFile)))
+ {
+ dspItem->ParseText(toParse);
+ dspItem->_item = _item;
+ dspItem->setSection(currentSection);
+ currentSection->addVarFile(dspItem);
+ tell(2, "added variable file '%s', namespace '%s'",
+ dspItem->Path2().c_str(), dspItem->Path().c_str());
+ }
+
+ return true;
+ }
+
+ case itemTheme:
+ {
+ ParseVarExt(toParse, "name", &tmp);
+ Thms::theTheme->setName(tmp);
+ ParseVarExt(toParse, "themeVersion", &tmp);
+ Thms::theTheme->setThemeVersion(tmp);
+ ParseVarExt(toParse, "syntaxVersion", &tmp);
+ Thms::theTheme->setSyntaxVersion(tmp);
+ ParseVarExt(toParse, "dir", &tmp);
+ Thms::theTheme->setDir(tmp);
+ ParseVarExt(toParse, "startImage", &tmp);
+ Thms::theTheme->setStartImage(tmp);
+ ParseVarExt(toParse, "endImage", &tmp);
+ Thms::theTheme->setEndImage(tmp);
+
+ if (ParseVarExt(toParse, "width", &value) == success)
+ Thms::theTheme->setWidth(value);
+ if (ParseVarExt(toParse, "height", &value) == success)
+ Thms::theTheme->setHeight(value);
+ if (ParseVarExt(toParse, "fontPath", &tmp) == success)
+ Thms::theTheme->setFontPath(tmp);
+
+ return true;
+ }
+
+ case itemDefaults:
+ {
+ currentSection->setDefaultsItem(newDisplayItem(_item));
+ currentSection->getDefaultsItem()->ParseText(toParse);
+
+ return true;
+ }
+
+ case itemUnknown:
+ {
+ tell(0, "Warning: Ignoring unknown theme item = [%s]",
+ toParse.substr(0, posA).c_str());
+
+ return true;
+ }
+ }
+
+ dspItem = newDisplayItem(_item);
+
+ if (dspItem)
+ {
+ if (_item == itemMenuImageMap)
+ Thms::theTheme->AddMapItem(dspItem);
+ else
+ currentSection->Add(dspItem);
+
+ // copy default values
+
+ if (currentSection->getDefaultsItem())
+ *dspItem = *currentSection->getDefaultsItem(); // copy constructor
+ else
+ tell(2, "Info: No defaults for section '%s' defined",
+ currentSection->getName().c_str());
+
+ // set/parse item properties
+
+ dspItem->_item = _item;
+ dspItem->setSection(currentSection);
+ dspItem->ParseText(toParse);
+
+ if (condition.size())
+ {
+ if (dspItem->_condition.size())
+ dspItem->_condition += " && ";
+
+ dspItem->_condition += condition;
+
+ tell(3, "Attach condition '%s' to '%s', condition now '%s'",
+ condition.c_str(), dspItem->nameOf(), dspItem->_condition.c_str());
+ }
+ }
+
+ return true;
+}
+
+//***************************************************************************
+// Parse Variable
+//***************************************************************************
+
+int cThemeItem::parseVariable(string& toParse, cThemeSection* section)
+{
+ int menu = no;
+ string name, value;
+ Scan scan(toParse.c_str());
+
+ scan.eat();
+
+ if (!scan.isIdent())
+ {
+ tell(0, "Error: Invalid left value '%s' in '%s'",
+ scan.lastIdent(), toParse.c_str());
+ return fail;
+ }
+
+ // parse optional 'menu' key word
+
+ if (strcmp(scan.lastIdent(), "menu") == 0)
+ {
+ menu = yes;
+ scan.eat();
+
+ if (!scan.isIdent())
+ {
+ tell(0, "Error: Invalid left value '%s' in '%s'",
+ scan.lastIdent(), toParse.c_str());
+ return fail;
+ }
+ }
+
+ // name
+
+ name = scan.lastIdent();
+
+ scan.eat();
+
+ // assignment
+
+ if (!scan.isOperator() || scan.lastIdent()[0] != '=')
+ {
+ tell(0, "Error: Invalid operator '%s' in '%s', '=' expected",
+ scan.lastIdent(), toParse.c_str());
+ return fail;
+ }
+
+ scan.eat();
+
+ if (!scan.isString() && scan.isIdent())
+ {
+ tell(0, "Error: Invalid right value '%s' in '%s'",
+ scan.lastIdent(), toParse.c_str());
+ return fail;
+ }
+
+// evaluate(value, scan.lastIdent()); // #jw value = scan.lastIdent();
+ value = scan.lastIdent();
+
+ if (section)
+ {
+ tell(3, "adding section variable '%s' with '%s' to '%s'",
+ name.c_str(), value.c_str(), section->getName().c_str());
+ section->variables[name] = value;
+ }
+ else
+ {
+ tell(3, "adding theme variable '%s' with '%s' %s",
+ name.c_str(), value.c_str(), menu ? "mode menu" : "");
+
+ if (menu)
+ Thms::theTheme->menuVariables[name] = value;
+ else
+ Thms::theTheme->variables[name] = value;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Parse Directive
+//***************************************************************************
+
+int cThemeItem::parseDirectives(string& toParse)
+{
+ string exeption;
+
+ if (ParseDirective(toParse, "#define", &exeption) == success
+ && exeption.length())
+ {
+ tell(1, "Adding define '%s'", exeption.c_str());
+ Thms::theTheme->defines[Thms::theTheme->defineCount] = exeption;
+ Thms::theTheme->defineCount++;
+
+ return success;
+ }
+
+ else if (ParseDirective(toParse, "#endif", &exeption) == success)
+ {
+ if (Thms::theTheme->skipContent.empty())
+ tell(0, "Warning: Ignoring unexpected '#endif'");
+ else
+ Thms::theTheme->skipContent.pop();
+
+ tell(2, "Swiching parser '%s' ('#endif' found)",
+ Thms::theTheme->isSkipContent() ? "off" : "on");
+
+ return success;
+ }
+
+ else if (ParseDirective(toParse, "#else", &exeption) == success)
+ {
+ int actualSkip = Thms::theTheme->isSkipContent();
+
+ if (Thms::theTheme->skipContent.empty())
+ tell(0, "Warning: Ignoring unexpected '#else'");
+ else
+ Thms::theTheme->skipContent.top() = !actualSkip;
+
+ tell(2, "Swiching parser '%s' [from '%s'] ('#else' found)",
+ Thms::theTheme->isSkipContent() ? "off" : "on",
+ actualSkip ? "off" : "on");
+
+ return success;
+ }
+
+ else if ((ParseDirective(toParse, "#ifdef", &exeption) == success
+ || ParseDirective(toParse, "#ifndef", &exeption) == success)
+ && exeption.length())
+ {
+ int actualSkip = Thms::theTheme->isSkipContent();
+ int negate = no;
+
+ if (toParse.find("#ifndef") == 0)
+ negate = yes;
+
+ tell(4, "Found '%s %s'", negate ? "#ifndef" : "#ifdef", exeption.c_str());
+
+ int skip = negate ? no : yes;
+
+ for (int i = 0; i < Thms::theTheme->defineCount; i++)
+ {
+ if (Thms::theTheme->defines[i] == exeption)
+ {
+ tell(4, "Define '%s' is set, switching parser '%s'",
+ exeption.c_str(), negate ? "off" : "on");
+
+ skip = negate ? yes : no;
+
+ break;
+ }
+ }
+
+ // aus bleibt aus (bei verschachtelten ifdef)
+
+ skip = skip || actualSkip;
+
+ Thms::theTheme->skipContent.push(skip);
+
+ tell(2, "Parser now '%s' due to '%s' '%s'",
+ Thms::theTheme->isSkipContent() ? "off" : "on",
+ negate ? "#ifndef" : "#ifdef",
+ exeption.c_str());
+
+ return success;
+ }
+
+ return ignore;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+cDisplayItem* cThemeItem::newDisplayItem(int item)
+{
+ cDisplayItem* newItem = 0;
+
+ switch (item)
+ {
+ case itemMenuImageMap:
+ case itemDefaults:
+ case itemMenuNavigationArea:
+ case itemSectionInclude: newItem = new cDisplayItem(); break;
+
+ case itemText: newItem = new cDisplayText(); break;
+ case itemTextList: newItem = new cDisplayTextList(); break;
+ case itemProgressbar: newItem = new cDisplayProgressBar(); break;
+ case itemRectangle: newItem = new cDisplayRectangle(); break;
+ case itemImage: newItem = new cDisplayImage(); break;
+ case itemImageFile: newItem = new cDisplayImageFile(); break;
+ case itemImageDir: newItem = new cDisplayImageDir(); break;
+ case itemCalibrationCursor: newItem = new cDisplayCalibrationCursor(); break;
+ case itemMessage: newItem = new cDisplayMessage(); break;
+
+ case itemMenu: newItem = new cDisplayMenu(); break;
+ case itemMenuSelected: newItem = new cDisplayMenuSelected(); break;
+ case itemColumn: newItem = new cDisplayMenuColumn(); break;
+ case itemColumnSelected: newItem = new cDisplayMenuColumnSelected(); break;
+ case itemEventColumn: newItem = new cDisplayMenuEventColumn(); break;
+ case itemEventColumnSelected: newItem = new cDisplayMenuEventColumnSelected(); break;
+
+ case itemSpectrumAnalyzer: newItem = new cDisplaySpectrumAnalyzer(); break;
+ case itemPartingLine: newItem = new cDisplayPartingLine(); break;
+ case itemSysinfo: newItem = new cDisplaySysinfo(); break;
+ case itemBackground: newItem = new cDisplayBackground(); break;
+
+ case itemVolumeMuteSymbol: newItem = new cDisplayVolumeMuteSymbol(); break;
+ case itemVolumebar: newItem = new cDisplayVolumebar(); break;
+
+ case itemTimebar: newItem = new cDisplayTimebar(); break;
+ case itemVarFile: newItem = new cVariableFile(); break;
+
+ case itemMenuButtonRed:
+ case itemMenuButtonGreen:
+ case itemMenuButtonYellow:
+ case itemMenuButtonBlue:
+ newItem = new cDisplayMenuButton();
+ break;
+
+ case itemMenuButtonBackgroundRed:
+ case itemMenuButtonBackgroundGreen:
+ case itemMenuButtonBackgroundYellow:
+ case itemMenuButtonBackgroundBlue:
+ newItem = new cDisplayMenuButtonBackground();
+ break;
+ };
+
+ if (newItem)
+ newItem->_item = item;
+
+ return newItem;
+}
+
+//***************************************************************************
+// Parse Properties
+//***************************************************************************
+
+int cThemeItem::ParseText(string toParse)
+{
+ ParseVar(toParse, "id", &_id);
+ ParseVar(toParse, "area", &_area);
+
+ ParseVarColor(toParse, "color", &_color);
+ ParseVarColor(toParse, "bg_color", &_bg_color);
+
+ ParseVar(toParse, "x", &_x);
+ ParseVar(toParse, "y", &_y);
+ ParseVar(toParse, "width", &_width);
+ ParseVar(toParse, "height", &_height);
+
+ ParseVar(toParse, "overlay", &_overlay);
+ ParseVar(toParse, "lines", &_lines);
+ ParseVar(toParse, "start_line", &_start_line);
+ ParseVar(toParse, "line", &_line);
+ ParseVar(toParse, "font", &_font);
+ ParseVar(toParse, "size", &_size);
+ ParseVar(toParse, "text", &_text);
+ ParseVar(toParse, "switch", &_switch); // bool
+ ParseVar(toParse, "align_v", &_align_v);
+ ParseVar(toParse, "align", &_align, alignments);
+ ParseVar(toParse, "focus", &_focus);
+ ParseVar(toParse, "type", &_type);
+ ParseVar(toParse, "format", &_format);
+ ParseVarTime(toParse, "delay", &_delay);
+ ParseVar(toParse, "foreground", &_foreground);
+ ParseVar(toParse, "count", &_count);
+ ParseVar(toParse, "image_map", &_image_map); // bool
+ ParseVar(toParse, "stat_pic", &_stat_pic); // bool
+ ParseVar(toParse, "stat_text", &_stat_text); // bool
+ ParseVar(toParse, "stat_width", &_stat_width);
+ ParseVar(toParse, "stat_height", &_stat_height);
+ ParseVar(toParse, "stat_x", &_stat_x);
+ ParseVar(toParse, "stat_y", &_stat_y);
+ ParseVar(toParse, "bg_x", &_bg_x);
+ ParseVar(toParse, "bg_y", &_bg_y);
+ ParseVar(toParse, "bg_width", &_bg_width);
+ ParseVar(toParse, "bg_height", &_bg_height);
+ ParseVar(toParse, "number", &_number);
+ ParseVar(toParse, "spacing", &_spacing);
+ ParseVar(toParse, "yspacing", &_yspacing);
+ ParseVar(toParse, "scroll", &_scroll, scrollmodes);
+ ParseVar(toParse, "factor", &_factor);
+ ParseVar(toParse, "unit", &_unit);
+ ParseVar(toParse, "reference", &_reference);
+ ParseVar(toParse, "scroll_count", &_scroll_count);
+ ParseVar(toParse, "dots", &_dots);
+ ParseVar(toParse, "permanent", &_permanent);
+ ParseVar(toParse, "value", &_value);
+ ParseVar(toParse, "total", &_total);
+
+ ParseVar(toParse, "menu_x", &_menu_x);
+ ParseVar(toParse, "menu_y", &_menu_y);
+ ParseVar(toParse, "menu_width", &_menu_width);
+ ParseVar(toParse, "menu_height", &_menu_height);
+
+ ParseVar(toParse, "fit", &_fit);
+ ParseVar(toParse, "aspect_ratio", &_aspect_ratio);
+ ParseVar(toParse, "rotate", &_rotate);
+
+ ParseVar(toParse, "whipe_res", &_whipe_res);
+ ParseVar(toParse, "on_click", &_onClick);
+ ParseVar(toParse, "on_dblclick", &_onDblClick);
+ ParseVar(toParse, "on_up", &_onUp);
+ ParseVar(toParse, "on_down", &_onDown);
+
+ ParseVar(toParse, "name", &_path);
+ ParseVar(toParse, "file", &_path2);
+ ParseVar(toParse, "path2", &_path2);
+ ParseVar(toParse, "pathON", &_path);
+ ParseVar(toParse, "pathOFF", &_path2);
+
+ ParseVar(toParse, "condition", &_condition);
+
+ ParseVar(toParse, "debug", &_debug);
+
+ // some properties with special handling
+
+ string tmp;
+
+ if (ParseVar(toParse,"bar_height", &tmp) == success)
+ {
+ _bar_height = atoi(tmp.c_str());
+
+ if (strchr(tmp.c_str(), '%'))
+ _bar_height_unit = iuPercent;
+ else
+ _bar_height_unit = iuAbsolute;
+ }
+
+ if (ParseVar(toParse, "path", &_path) == success)
+ {
+ string::size_type e, s;
+ e = s = 0;
+
+ do
+ {
+ e = _path.find(':', s);
+
+ pathList[pathCount].configured = _path.substr(s, e == string::npos ? _path.length()-s : e-s);
+ pathList[pathCount].curNum = na;
+ pathList[pathCount].last = "";
+ pathCount++;
+
+ s = e+1;
+
+ } while (s > 0 && pathCount < maxPathCount);
+ }
+
+ _text = replaceChar(_text, '@', '\n');
+
+ return success;
+}
+
+//***************************************************************************
+// Search via translation list
+//***************************************************************************
+
+int cThemeItem::ParseVar(string toParse, string name, int* value, Translation* t)
+{
+ string val;
+ int status;
+ int denum;
+
+ if ((status = ParseVar(toParse, name, &val)) == success)
+ {
+ denum = cThemeService::toDenum(t, val.c_str());
+
+ if (denum == na)
+ {
+ tell(0, "Error: Unexpected value '%s' for '%s'",
+ val.c_str(), name.c_str());
+
+ denum = 0; // 0 -> always the default
+ }
+
+ *value = denum;
+ }
+
+ return status;
+}
+
+//***************************************************************************
+// Search the int parameter
+//***************************************************************************
+
+int cThemeItem::ParseVar(string toParse, string name, int* value)
+{
+ string val;
+ int status;
+
+ if ((status = ParseVar(toParse, name, &val)) == success)
+ {
+ if (val == "yes" || val == "true")
+ *value = 1;
+ else if (val == "no" || val == "false")
+ *value = 0;
+ else
+ *value = atoi(val.c_str());
+ }
+
+ return status;
+}
+
+//***************************************************************************
+// Search the time parameter
+//***************************************************************************
+
+int cThemeItem::ParseVarTime(string toParse, string name, uint64_t* value)
+{
+ string val;
+ int status;
+
+ if ((status = ParseVar(toParse, name, &val)) == success)
+ {
+ if (val.find("ms") != string::npos)
+ *value = atoi(val.c_str());
+ else
+ *value = atoi(val.c_str()) * 1000;
+ }
+
+ return status;
+}
+
+//***************************************************************************
+// Lookup Variable
+//***************************************************************************
+
+int cThemeItem::lookupVariable(const char* name, string& value, const char* fmt)
+{
+ int status = fail;
+
+ tell(4, "lookup variable '%s' in '%s'", name,
+ section ? section->getName().c_str() : "theme");
+
+ if (section)
+ status = section->lookupVar(name, value);
+
+ if (status != success)
+ status = Thms::theTheme->lookupVar(name, value);
+
+ if (status == success)
+ tell(4, "Found variable '%s' with value '%s' in '%s'",
+ name, value.c_str(), section ? section->getName().c_str() : "theme");
+
+ return status;
+}
+
+//***************************************************************************
+// Set Variable
+//***************************************************************************
+
+int cThemeItem::setVariable(const char* name, int value)
+{
+ char v[50];
+ string tmp;
+
+ sprintf(v, "%d", value);
+
+ if (section && section->lookupVar(name, tmp) == success)
+ {
+ section->variables[name] = v;
+ return success;
+ }
+ else if (Thms::theTheme->lookupVar(name, tmp) == success)
+ {
+ Thms::theTheme->variables[name] = v;
+ return success;
+ }
+
+ return fail;
+}
+
+//***************************************************************************
+// Pase Variables
+//***************************************************************************
+
+int cThemeItem::ParseVarExt(string toParse, string name, int* value)
+{
+ string v, p;
+
+ if (ParseVar(toParse, name, &v) != success)
+ return fail;
+
+ if (evaluate(p, v.c_str()) != success)
+ return fail;
+
+ *value = atoi(p.c_str());
+
+ return success;
+}
+
+int cThemeItem::ParseVarExt(string toParse, string name, string* value)
+{
+ string v;
+
+ if (ParseVar(toParse, name, &v) != success)
+ return fail;
+
+ if (evaluate(*value, v.c_str()) != success)
+ return fail;
+
+ tell(4, "found var '%s' with value '%s'",
+ name.c_str(), value->c_str());
+
+ return success;
+}
+
+int cThemeItem::ParseVarColor(string toParse, string name, string* value)
+{
+ char* temp;
+ int red, green, blue, alpha = na;
+
+ int bg = strncmp(name.c_str(), "bg_", 3) == 0; // background color expected?
+
+ // parse alpha channel
+
+ ParseVar(toParse, bg ? "bg_transparent" : "transparent", &alpha);
+ ParseVar(toParse, bg ? "bg_alpha" : "alpha", &alpha);
+
+ // parse new style color parameter
+
+ if (ParseVar(toParse, name, value) != success)
+ {
+ // parse old style color parameter
+
+ if (ParseVar(toParse, bg ? "bg_red" : "red", &red) +
+ ParseVar(toParse, bg ? "bg_green" : "green", &green) +
+ ParseVar(toParse, bg ? "bg_blue" : "blue", &blue) == success)
+ {
+ asprintf(&temp, "%d:%d:%d:%d", red, green, blue, alpha);
+ *value = temp;
+ free(temp);
+
+ return success;
+ }
+ }
+
+ // it's allowed to configure separate alpha value
+
+ if (alpha != na)
+ {
+ t_rgba rgba;
+
+ str2rgba(value->c_str(), rgba);
+ asprintf(&temp, "%d:%d:%d:%d", rgba[rgbR], rgba[rgbG], rgba[rgbB], alpha);
+ *value = temp;
+ free(temp);
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Search the string parameter
+//***************************************************************************
+
+int cThemeItem::ParseVar(string toParse, string name, string* value)
+{
+ string::size_type posA, posB, end = 0;
+
+ name += "=";
+
+ if ((posA = toParse.find("," + name)) == string::npos)
+ if ((posA = toParse.find(" " + name)) == string::npos)
+ if ((posA = toParse.find(name)) != 0)
+ return fail;
+
+ if (posA)
+ posA++;
+
+ posB = posA;
+
+ while (posB < toParse.length())
+ {
+ if ((end = toParse.find(",", posB)) == string::npos)
+ if ((end = toParse.find(";", posB)) == string::npos)
+ return fail;
+
+ // if at first pos or without mask sign,
+ // then we have found the end of the item
+ // -> break the loop
+
+ if (end == 0) // wenn "," -> fertig
+ break;
+
+ if (toParse[end-1] != '\\') // wenn "x," -> fertig
+ break;
+
+ if (end > 1 && toParse[end-2] == '\\') // wenn "\\," -> fertig
+ break;
+
+ // => "\," -> nicht fertig
+ // => search again behind the ',' or ';' sign
+
+ posB = end+1;
+ }
+
+ *value = toParse.substr(posA + name.size(), end-posA-name.size());
+
+ // de mask '\' sign
+
+ if ((value->find("\\", 0)) != string::npos)
+ {
+ char* buf; char* s; char* d;
+
+ asprintf(&buf, "%s", value->c_str());
+
+ s = d = buf;
+
+ while (*s)
+ {
+ if (*s != '\\' || *(s+1) == '\\')
+ *d++ = *s;
+
+ s++;
+ }
+
+ *d = 0;
+
+ tell(5, "got '%s' build '%s'", value->c_str(), buf);
+
+ *value = buf;
+ free(buf);
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Parse Directive
+//***************************************************************************
+
+int cThemeItem::ParseDirective(string toParse, string name, string* value)
+{
+ string::size_type posA;
+
+ *value = "";
+
+ if ((posA = toParse.find(name)) != 0)
+ return fail;
+
+ if (toParse.length() > posA + name.size() + 1)
+ *value = toParse.substr(posA + name.size() + 1);
+
+ return success;
+}
+
+//***************************************************************************
+// Class cThemeSections
+//***************************************************************************
+
+//***************************************************************************
+// Get Section By Name
+//***************************************************************************
+
+cThemeSection* cThemeSections::getSection(string name)
+{
+ for (cThemeSection* p = First(); p; p = Next(p))
+ if (p->getName() == name)
+ return p;
+
+ return 0;
+}
+
+//***************************************************************************
+// Class cThemeSection
+//***************************************************************************
+
+uint64_t cThemeSection::getNextUpdateTime()
+{
+ uint64_t next = msNow() + SECONDS(300); // 5 minutes
+ cDisplayItem* pNext = 0;
+
+ // search next (earlyast) drawing time
+
+ for (cDisplayItem* p = First(); p; p = Next(p))
+ {
+ if (p->getNextDraw() && p->getNextDraw() < next)
+ {
+ uint64_t drawIn = p->getNextDraw() - msNow();
+
+ tell(2, "setting next for '%s' in (%ld ms) [%s/%s]",
+ p->nameOf(), drawIn, p->Debug().c_str(), p->Text().c_str());
+
+ pNext = p;
+ next = p->getNextDraw();
+ }
+ }
+
+ if (pNext)
+ {
+ int64_t updateIn = next - msNow();
+
+ if (updateIn < 0)
+ updateIn = 0;
+
+ int64_t s = updateIn/1000;
+ int64_t us = updateIn%1000;
+
+ tell(2, "schedule next, nearest item is '%s'[%s] in %ld,%03ld seconds",
+ pNext->nameOf(), pNext->Debug().c_str(), s, us);
+ }
+
+ return next < msNow() ? msNow() : next;
+}
+
+int cThemeSection::updateGroup(int group)
+{
+ tell(3, "update item group (%d)", group);
+
+ for (cDisplayItem* p = First(); p; p = Next(p))
+ {
+ if (p->groupOf() & group)
+ {
+ p->reset();
+ p->setNextDraw();
+
+ // schedule all of my area also
+
+ if (p->Area() != "")
+ {
+ for (cDisplayItem* pa = First(); pa; pa = Next(pa))
+ {
+ if (pa->Area() == p->Area())
+ {
+ pa->reset();
+ pa->setNextDraw();
+ }
+ }
+ }
+ }
+ }
+
+ return done;
+}
+
+int cThemeSection::reset()
+{
+ tell(3, "reset items");
+
+ for (cDisplayItem* p = First(); p; p = Next(p))
+ p->reset();
+
+ return done;
+}
+
+int cThemeSection::updateVariables()
+{
+ map<string,cVariableFile*>::iterator it;
+
+ for (it = varFiles.begin(); it != varFiles.end(); it++)
+ it->second->parse();
+
+ return done;
+}
+
+//***************************************************************************
+// Class cDisplayItems
+//***************************************************************************
+//***************************************************************************
+// Get Item By Kind
+//***************************************************************************
+
+cDisplayItem* cDisplayItems::getItemByKind(Ts::ItemKind type)
+{
+ for (cDisplayItem* p = First(); p; p = Next(p))
+ {
+ if (p->Item() == type)
+ return p;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Get Item By ID
+// id is configured in theme file, otherwise it is na
+//***************************************************************************
+
+cDisplayItem* cDisplayItems::getItemById(const char* id)
+{
+ for (cDisplayItem* p = First(); p; p = Next(p))
+ {
+ if (p->Id() == id)
+ return p;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Class GraphTFTTheme
+//***************************************************************************
+
+cGraphTFTTheme::cGraphTFTTheme()
+{
+ initialized = no;
+
+ width = 720;
+ height = 576;
+ memset(normalModes, 0, sizeof(normalModes));
+ normalModesCount = 0;
+ fontPath = "";
+ variables.clear();
+ menuVariables.clear();
+
+ cThemeItem::currentSection = 0;
+ cThemeItem::lineBuffer = "";
+ cThemeItem::condition = "";
+
+ resetDefines();
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int cGraphTFTTheme::init()
+{
+ cThemeSection* s;
+ cThemeSection* sec;
+ cDisplayItem* newItem;
+ cDisplayItem* item;
+ cDisplayItem* p;
+ cDisplayItem* background;
+
+ if (initialized)
+ exit();
+
+ // loop over sections
+
+ for (s = FirstSection(); s; s = NextSection(s))
+ {
+ // evaluate includes ...
+
+ // loop over sections items
+
+ tell(4, "Section: '%s'", s->getName().c_str());
+
+ for (p = s->First(); p; p = s->Next(p))
+ {
+ // add items of 'included' sections
+
+ tell(4, "Item: '%s'", Ts::toName((cThemeService::ItemKind)p->Item()));
+
+ if (p->Item() == itemSectionInclude && (sec = getSection(p->SectionInclude())))
+ {
+ tell(4, "Icluding section: '%s'", sec->getName().c_str());
+
+ map<string,cVariableFile*>::iterator it;
+
+ for (it = sec->getVarFiles()->begin(); it != sec->getVarFiles()->end(); it++)
+ {
+ if (!s->hasVarFile(it->second->getName()))
+ s->addVarFile(it->second);
+ }
+
+ // include items
+
+ for (item = sec->First(); item; item = sec->Next(item))
+ {
+ tell(4, "Including Item: '%s'", Ts::toName((cThemeService::ItemKind)item->Item()));
+
+ if (item->Item() >= itemBegin
+ && (newItem = cThemeItem::newDisplayItem(item->Item())))
+ {
+ *newItem = *item; // copy constructor
+ s->Ins(newItem, p); // s->First());
+ }
+ }
+
+ // include variables
+
+ map<string,string>::iterator iter;
+
+ for (iter = sec->variables.begin(); iter != sec->variables.end(); ++iter)
+ s->variables[iter->first] = iter->second;
+ }
+ }
+
+ // assign background item ...
+
+ background = 0;
+
+ // loop over sections items
+
+ for (p = s->First(); p; p = s->Next(p))
+ {
+ // detect/assign background
+ // only working properly if background is the first
+ // item in the theme section!
+
+ if (p->Item() == itemBackground)
+ {
+ if (background)
+ tell(1, "Warning: in theme section '%s' cascading "
+ "background items detected", s->getName().c_str());
+
+ background = p;
+ }
+ else
+ p->setBackgroundItem(background);
+ }
+ }
+
+ int ex = 0, exs = 0;
+ int ey = 0;
+
+ // init image map
+ // init column positions
+ // init ...
+
+ // loop over all sections
+
+ for (s = FirstSection(); s; s = NextSection(s))
+ {
+ int colNumber = 0;
+
+ // loop over items of this section
+
+ ex = exs = 0;
+ ey = 0;
+
+ for (p = s->First(); p; p = s->Next(p))
+ {
+ cDisplayItem* dspItem = p;
+
+ if (!dspItem)
+ continue;
+
+ // calculate y position of the next text item
+
+ if (dspItem->Item() == itemText)
+ {
+ if (dspItem->Y() == na)
+ dspItem->setY(ey);
+ else
+ ey = dspItem->Y();
+
+ if (dspItem->Height())
+ ey += dspItem->Height() + dspItem->YSpacing();
+ }
+
+ // calculate x position of the column items
+
+ if (dspItem->Item() == itemEventColumn)
+ // || dspItem->Item() == itemColumn)
+ {
+ if (dspItem->Number() == na)
+ dspItem->Number(colNumber);
+
+ if (dspItem->X() == na)
+ dspItem->setX(ex); // not configured, take calculated
+ else if (dspItem->X() < na && ex + dspItem->X() > 0)
+ dspItem->setX(ex + dspItem->X()); // go back
+ else
+ ex = dspItem->X(); // take configured value
+
+ if (dspItem->Width())
+ ex += dspItem->Width() + dspItem->Spacing(); // calc next x
+ else
+ dspItem->setWidth(Thms::theTheme->getWidth() - dspItem->X());
+ }
+
+ if (dspItem->Item() == itemEventColumnSelected)
+ // || dspItem->Item() == itemColumnSelected)
+ {
+ if (dspItem->Number() == na)
+ dspItem->Number(colNumber);
+
+ if (dspItem->X() == na)
+ dspItem->setX(exs); // not configured, take calculated
+ else if (dspItem->X() < na && exs + dspItem->X() > 0)
+ dspItem->setX(exs + dspItem->X()); // go back
+ else
+ exs = dspItem->X(); // take configured value
+
+ if (dspItem->Width())
+ exs += dspItem->Width() + dspItem->Spacing(); // calc next x
+ else
+ dspItem->setWidth(Thms::theTheme->getWidth() - dspItem->X());
+ }
+
+ if (dspItem->Item() == itemEventColumn)
+ {
+ // || dspItem->Item() == itemColumn)
+ colNumber++;
+ }
+
+ dspItem->init();
+ }
+ }
+
+ // at leat reset all items
+
+ for (s = FirstSection(); s; s = NextSection(s))
+ s->reset();
+
+ initialized = yes;
+
+ return success;
+}
+
+int cGraphTFTTheme::activate(int fdInotify)
+{
+ cThemeSection* s;
+ cDisplayItem* p;
+
+ inotifies.clear();
+
+ if (fdInotify != na)
+ {
+ // loop over all sections
+
+ for (s = FirstSection(); s; s = NextSection(s))
+ {
+ // loop over items of this section
+
+ for (p = s->First(); p; p = s->Next(p))
+ {
+ // add inotify watch
+
+ if (p->Item() == itemImageFile)
+ {
+ if (p->Path().length())
+ {
+ int wd = inotify_add_watch(fdInotify, p->Path().c_str(), IN_CREATE | IN_MODIFY);
+
+ tell(0, "Adding inotify watch for '%s'", p->Path().c_str());
+
+ if ((wd = -1))
+ {
+ tell(0, "Adding inotify watch for '%s' failed, %m", p->Path().c_str());
+ continue;
+ }
+
+ inotifies[wd] = p;
+ }
+ }
+ }
+ }
+ }
+
+ return success;
+}
+
+int cGraphTFTTheme::deactivate(int fdInotify)
+{
+ map<int,cDisplayItem*>::iterator iter;
+
+ for (iter = inotifies.begin(); iter != inotifies.end(); iter++)
+ inotify_rm_watch(fdInotify, iter->first);
+
+ inotifies.clear();
+
+ return success;
+}
+
+int cGraphTFTTheme::checkViewMode()
+{
+ // check normal view
+
+ int i = 0;
+
+ while (normalModes[i])
+ {
+ if ((GraphTFTSetup.storeNormalMode && (normalModes[i] == GraphTFTSetup.normalMode))
+ || (!GraphTFTSetup.storeNormalMode && (normalModes[i] == GraphTFTSetup.originalNormalMode)))
+ break;
+
+ i++;
+ }
+
+ if (!normalModes[i])
+ {
+ GraphTFTSetup.normalMode = "Standard";
+ GraphTFTSetup.storeNormalMode = true;
+ GraphTFTSetup.originalNormalMode = "";
+ }
+
+ if (GraphTFTSetup.storeNormalMode)
+ tell(0, "normal mode now '%s'", GraphTFTSetup.normalMode.c_str());
+ else
+ tell(0, "normal mode now '%s'", GraphTFTSetup.originalNormalMode.c_str());
+
+ return done;
+}
+
+//***************************************************************************
+// Exit
+//***************************************************************************
+
+int cGraphTFTTheme::exit()
+{
+ int i = 0;
+ cThemeSection* s;
+
+ if (!initialized)
+ return done;
+
+ cThemeItem::currentSection = 0;
+ cThemeItem::lineBuffer = "";
+ cThemeItem::condition = "";
+
+ tell(3, "destroy theme '%s'", getName().c_str());
+
+ for (s = FirstSection(); s; s = NextSection(s))
+ s->getItems()->Clear();
+
+ sections.Clear();
+
+ while (normalModes[i] && i < 100)
+ free(normalModes[i++]);
+
+ // delete of this object's is done by
+ // cConfig before new Load() or in destructor!
+
+ if(Thms::theTheme)
+ {
+ Thms::theTheme->resetDefines();
+ }
+
+ initialized = no;
+
+ return done;
+}
+
+//***************************************************************************
+// Check
+//***************************************************************************
+
+int cGraphTFTTheme::check(const char* theVersion)
+{
+ // check if the theme fit to current version
+
+ if (strcmp(syntaxVersion.c_str(), theVersion) != 0)
+ {
+ tell(0, "Warning: Version of themefile syntax "
+ "does not match, found '%s' instead of '%s'",
+ syntaxVersion.c_str(), theVersion);
+ return fail;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Load
+//***************************************************************************
+
+int cGraphTFTTheme::load(const char* path)
+{
+ exit();
+ Load(path);
+
+ return init();
+}
+
+//***************************************************************************
+// Add Normal Section
+//***************************************************************************
+
+void cGraphTFTTheme::addNormalSection(string sectionName)
+{
+ if (normalModesCount >= 100)
+ return ;
+
+ if (sectionName.find("Normal") != 0 || sectionName.length() <= strlen("Normal"))
+ return ;
+
+ if (sectionName == "NormalRadio")
+ return ;
+
+ if (sectionName == "NormalTV")
+ asprintf(&normalModes[normalModesCount], "%s",
+ "Standard");
+ else
+ asprintf(&normalModes[normalModesCount], "%s",
+ sectionName.substr(strlen("Normal")).c_str());
+
+ normalModesCount++;
+}
+
+//***************************************************************************
+// Is Normal Mode Section
+//***************************************************************************
+
+const char* cGraphTFTTheme::getNormalMode(const char* modeName)
+{
+ for (int i = 0; i < normalModesCount; i++)
+ {
+ if (strcasecmp(normalModes[i], modeName) == 0)
+ return normalModes[i];
+ }
+
+ return 0;
+}
+
+const char* cGraphTFTTheme::nextNormalMode(const char* modeName)
+{
+ int i;
+
+ for (i = 0; i < normalModesCount; i++)
+ {
+ if (strcasecmp(normalModes[i], modeName) == 0)
+ break;
+ }
+
+ if (++i < normalModesCount)
+ return normalModes[i];
+
+ return normalModes[0];
+}
+
+const char* cGraphTFTTheme::prevNormalMode(const char* modeName)
+{
+ int i;
+
+ for (i = normalModesCount-1; i >= 0; i--)
+ {
+ if (strcasecmp(normalModes[i], modeName) == 0)
+ break;
+ }
+
+ if (--i >= 0)
+ return normalModes[i];
+
+ return normalModes[normalModesCount-1];
+}
+
+//***************************************************************************
+// Get Path From ImageMap
+//***************************************************************************
+
+string cGraphTFTTheme::getPathFromImageMap(const char* aName)
+{
+ const char* name = aName;
+
+ // skip leading blanks and numbers
+
+ while (*name && (isdigit(*name) || *name == ' '))
+ name++;
+
+ tell(3, "checking imagemap for menu entry '%s'", name);
+
+ for (cDisplayItem* p = mapSection.First(); p; p = mapSection.Next(p))
+ {
+ if (p->Item() == itemMenuImageMap)
+ {
+ if (strcmp(trVDR(p->Path().c_str()), name) == 0)
+ {
+ tell(3, "MenuImageMap: Picture for '%s' is '%s'", aName, p->Path2().c_str());
+ return p->Path2();
+ }
+ }
+ }
+
+ return "";
+}
+
+//***************************************************************************
+// Calss cGraphTFTThemes
+//***************************************************************************
+//***************************************************************************
+// Get Theme
+//***************************************************************************
+
+cGraphTFTTheme* cGraphTFTThemes::getTheme(string aTheme)
+{
+ cGraphTFTTheme* t;
+
+ tell(3, "looking for theme '%s'", aTheme.c_str());
+
+ for (t = First(); t; t = Next(t))
+ if (t->getName() == aTheme)
+ return t;
+
+ return 0;
+}
diff --git a/theme.h b/theme.h
new file mode 100644
index 0000000..93eb732
--- /dev/null
+++ b/theme.h
@@ -0,0 +1,1267 @@
+/**
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * theme.h - A plugin for the Video Disk Recorder
+ *
+ * (c) 2004 Lars Tegeler, Sascha Volkenandt
+ * (c) 2006-2013 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 __GTFT_THEME_H
+#define __GTFT_THEME_H
+
+#include <errno.h>
+
+#include <string>
+#include <map>
+#include <vector>
+#include <stack>
+
+#include <vdr/tools.h>
+#include <vdr/config.h>
+
+#include <common.h>
+#include <renderer.h>
+#include <string>
+
+#define SECONDS(x) (((uint64_t)x)*1000)
+
+using std::string;
+using std::map;
+
+class cDisplayItem;
+class cGraphTFTDisplay;
+class cThemeSection;
+
+//***************************************************************************
+// Variable Provider
+//***************************************************************************
+
+class VariableProvider
+{
+ public:
+
+ VariableProvider() {};
+ virtual ~VariableProvider() {};
+
+ virtual string channelLogoPath(const char* channel,
+ const char* format = 0, int classic = yes);
+
+ virtual int variableOf(string& name, const char* expression, char*& e);
+ virtual int evaluate(string& buf, const char* var);
+ virtual const char* splitFormatValue(const char* data,
+ char* value, char* format);
+
+ virtual int lookupVariable(const char* name, string& value,
+ const char* fmt = 0) = 0;
+
+ int calcExpression(const char* expression);
+ int calc(const char* op, int left, int right);
+};
+
+//***************************************************************************
+// Theme Service
+//***************************************************************************
+
+class cThemeService
+{
+ public:
+
+ enum ReplayMode
+ {
+ rmUnknown = na,
+ rmPlay,
+ rmForward,
+ rmSpeed
+ };
+
+ enum ItemUnit
+ {
+ iuUnknown = na,
+ iuAbsolute,
+ iuRelative,
+ iuPercent
+ };
+
+ enum ConditionArgumentType
+ {
+ catUnknown,
+ catInteger,
+ catString
+ };
+
+ enum MenuItemType
+ {
+ itNormal,
+ itPartingLine
+ };
+
+ // groups
+
+ enum eThmeGroup
+ {
+ groupUnknown = 1,
+
+ groupChannel = 2,
+ groupVolume = 4,
+ groupButton = 8,
+ groupRecording = 16,
+ groupReplay = 32,
+ groupMessage = 64,
+ groupMenu = 128,
+ groupMusic = 256,
+ groupTextList = 512,
+ groupCalibrate = 1024,
+ groupVarFile = 2048,
+
+ groupAll = 0xFFFF
+ };
+
+ // items
+
+ enum ItemKind
+ {
+ itemUnknown = na,
+
+ itemSectionInclude, // = 0
+ itemTheme,
+
+ itemBegin, // = 2
+ itemText = itemBegin, // = 2
+ itemImage, // = 3
+ itemImageFile,
+ itemImageDir,
+ itemRectangle, // = 5
+ itemTimebar,
+
+ itemMessage,
+ itemVolumeMuteSymbol,
+ itemVolumebar,
+
+ itemMenu,
+ itemMenuSelected,
+
+ itemMenuButton,
+ itemMenuButtonRed = itemMenuButton, // =
+ itemMenuButtonGreen,
+ itemMenuButtonYellow,
+ itemMenuButtonBlue,
+
+ itemMenuButtonBackground, // =
+ itemMenuButtonBackgroundRed = itemMenuButtonBackground, // =
+ itemMenuButtonBackgroundGreen,
+ itemMenuButtonBackgroundYellow,
+ itemMenuButtonBackgroundBlue,
+
+ itemMenuImageMap, // =
+
+ itemSpectrumAnalyzer,
+ itemPartingLine, // =
+ itemSysinfo,
+ itemBackground,
+ itemTextList,
+ itemProgressbar,
+
+ itemClickArea,
+ itemMenuNavigationArea,
+ itemCalibrationCursor,
+
+ itemColumn,
+ itemColumnSelected,
+
+ itemEventColumn,
+ itemEventColumnSelected,
+
+ itemDefaults,
+ itemVarFile,
+
+ itemCount
+ };
+
+ enum Misc
+ {
+ maxPathCount = 10
+ };
+
+ enum Align
+ {
+ algLeft,
+ algCenter,
+ algRight
+ };
+
+ enum ScrollModes
+ {
+ smOff,
+ smMarquee,
+ smTicker
+ };
+
+ struct Translation
+ {
+ int denum;
+ const char* name;
+ };
+
+ static int toDenum(Translation t[], const char* value);
+ static Translation alignments[];
+ static Translation scrollmodes[];
+
+ static const char* toName(ItemKind aItem);
+ static ItemKind toItem(const char* aName);
+ static int isValid(ItemKind aItem)
+ { return aItem > itemUnknown && aItem < itemCount; }
+
+ static const char* items[itemCount+1];
+};
+
+typedef cThemeService Ts;
+
+//***************************************************************************
+// Class cThemeItem
+// - represents a theme item
+//***************************************************************************
+
+class cThemeItem : public cListObject, public cThemeService, public VariableProvider
+{
+ public:
+
+ struct cPath
+ {
+ string configured; // configured path
+
+ // path can contain a range [0-9], [3-99], ...
+ // used to support images like thumbnail_1.jpg
+ // in this case the following members are set ...
+
+ int curNum;
+ string last; // last path
+ };
+
+ cThemeItem();
+ virtual ~cThemeItem();
+
+ bool Parse(const char *s);
+ int parseDirectives(string& toParse);
+ int parseVariable(string& toParse, cThemeSection* section = 0);
+ virtual int init() { return done; }
+
+ //
+
+ int lookupVariable(const char* name, string& value, const char* fmt = 0);
+ int setVariable(const char* name, int value);
+
+ // statics
+
+ static cDisplayItem* newDisplayItem(int item);
+
+ // temporary buffer used by the parser ::Parse()
+
+ static cThemeSection* currentSection;
+ static string lineBuffer;
+ static string condition;
+
+ protected:
+
+ // functions
+
+ int ParseText(string toParse);
+ int ParseVar(string toParse, string name, int* value);
+ int ParseVarTime(string toParse, string name, uint64_t* value);
+ int ParseVar(string toParse, string name, string* value);
+ int ParseVarExt(string toParse, string name, string* value);
+ int ParseVarExt(string toParse, string name, int* value);
+ int ParseDirective(string toParse, string name, string* value);
+ int ParseVar(string toParse, string name, int* value, Translation t[]);
+ int ParseVarColor(string toParse, string name, string* value);
+
+ // item properties
+
+ int _item;
+
+ string _id;
+ string _debug;
+ string _sectionInclude;
+ string _area;
+
+ string _x;
+ string _y;
+ string _width;
+ string _height;
+ string _start_line;
+
+ int _overlay, _lines, _line, _size, _switch;
+ int _menu_x, _menu_y, _menu_width, _menu_height;
+ int _bg_x, _bg_y, _bg_width, _bg_height;
+ int _stat_pic, _stat_x, _stat_y, _image_map;
+ int _stat_text, _stat_width, _stat_height;
+ int _align, _align_v, _count;
+ int _number;
+ int _index, _spacing, _yspacing, _bar_height, _bar_height_unit;
+ int _scroll, _scroll_count, _dots;
+ int _permanent, _factor, _aspect_ratio, _fit, _foreground;
+ int _rotate;
+ int _whipe_res;
+ uint64_t _delay; // delay in ms
+
+ string _color;
+ string _bg_color;
+ string _value;
+ string _total;
+ string _unit;
+ string _reference;
+ string _onClick;
+ string _onDblClick;
+ string _onUp;
+ string _onDown;
+ string _font;
+ string _path;
+ string _focus;
+ string _path2;
+ string _type;
+ string _format;
+ string _text;
+ string _condition;
+
+ int pathCount;
+ cPath pathList[maxPathCount];
+ cThemeSection* section; // my section
+};
+
+//***************************************************************************
+// Display Items
+//***************************************************************************
+
+class cDisplayItem : public cThemeItem
+{
+ public:
+
+ cDisplayItem();
+ virtual ~cDisplayItem();
+
+ // functions
+
+ virtual const char* nameOf() { return toName((ItemKind)_item); }
+ virtual int groupOf() { return groupUnknown; }
+ virtual int isOfGroup(int g) { return groupOf() & g; }
+
+ int useVarOf(const char* varPart)
+ { return strstr(_text.c_str(), varPart) || strstr(_path.c_str(), varPart) || strstr(_condition.c_str(), varPart); }
+
+ virtual int draw();
+ virtual int refresh();
+ virtual int reset();
+
+ // virtual string channelLogoPath(const char* channel, int classic = yes);
+
+ virtual int drawText(const char* text, int y = 0,
+ int height = na, int clear = yes,
+ int skipLines = 0);
+ virtual int drawRectangle();
+ virtual int drawBackRect(int y = 0, int height = 0);
+ virtual int drawImage(const char* path = 0, int fit = na,
+ int aspectRatio = na, int noBack = no);
+ virtual int drawImageOnBack(const char* path, int fit = no, int height = na);
+ virtual int drawProgressBar(double current, double total,
+ string path, int y = na,
+ int height = na,
+ int withFrame = yes, int clear = yes);
+ virtual int drawPartingLine(string text, int y, int height);
+
+ virtual void setBackgroundItem(cDisplayItem* p) { backgroundItem = p; }
+ virtual void setNextDraw() { nextDraw = msNow(); }
+
+ virtual uint64_t getNextDraw() { return nextDraw; }
+ virtual int isForegroundItem() { return no; }
+
+ void scheduleDrawAt(uint64_t aTime);
+ void scheduleDrawIn(int aTime);
+ void scheduleDrawNextFullMinute();
+
+ void setSection(cThemeSection* s) { section = s; }
+
+ // statics
+
+ static void scheduleForce(uint64_t aTime);
+
+ static void clearSelectedItem() { selectedItem = 0; }
+ static void setForce(int flag) { forceDraw = flag; }
+ static int getForce() { return forceDraw; }
+ static void setRenderer(Renderer* aRender) { render = aRender; }
+ static void setVdrStatus(cGraphTFTDisplay* aVdrStatus) { vdrStatus = aVdrStatus; }
+
+ // Item getter/setter
+
+ int Item() { return _item; }
+ string Id() { return _id; }
+ string Area() { return _area; }
+
+ int Index(int value = na) { if (value != na) _index = value; return _index; }
+ int Changed() { return changed; }
+
+ void setStartLine(int value) { _start_line = Str::toStr(value); }
+ void setX(int value) { _x = Str::toStr(value); }
+ void setY(int value) { _y = Str::toStr(value); }
+ void setWidth(int value) { _width = Str::toStr(value); }
+
+ int Number(int value = na) { if (value != na) _number = value; return _number; }
+
+ int StartLine() { return optionVariable(_start_line.c_str()); }
+ int X() { return optionVariable(_x.c_str()); }
+ int Y() { return optionVariable(_y.c_str()); }
+ int Width() { return optionVariable(_width.c_str()); }
+ int Height() { return optionVariable(_height.c_str()); }
+
+ // item options
+
+ int BgX() { return _bg_x; }
+ int BgY() { return _bg_y; }
+ int BgWidth() { return _bg_width; }
+ int BgHeight() { return _bg_height; }
+
+ int ImageMap() { return _image_map; }
+ int StaticPicture() { return _stat_pic; }
+ int StaticText() { return _stat_text; }
+ int StaticWidth() { return _stat_width; }
+ int StaticHeight() { return _stat_height; }
+ int StaticX() { return _stat_x; }
+ int StaticY() { return _stat_y; }
+ int Overlay() { return _overlay; }
+ int Lines() { return _lines; }
+ int Line() { return _line; }
+ int Size() { return _size; }
+ int Switch() { return _switch; }
+ int Align() { return _align; }
+ int AlignV() { return _align_v; }
+ uint64_t Delay() { return _delay; }
+ int Foreground() { return _foreground; }
+ int Count() { return _count; }
+ int Spacing() { return _spacing; }
+ int YSpacing() { return _yspacing; }
+ int BarHeight() { return _bar_height; }
+ int BarHeightUnit() { return _bar_height_unit; }
+ int Fit() { return _fit; }
+ int AspectRatio() { return _aspect_ratio; }
+ int Scroll() { return _scroll; }
+ int MenuX() { return _menu_x; }
+ int MenuY() { return _menu_y; }
+ int MenuWidth() { return _menu_width; }
+ int MenuHeight() { return _menu_height; }
+ int WhipeRes() { return _whipe_res; }
+
+ string Color() { return _color; }
+ string BgColor() { return _bg_color; }
+ string Value() { return _value; }
+ string Total() { return _total; }
+ string Font() { return _font; }
+ string Focus() { return _focus; }
+ string Path() { return _path; }
+ string Path2() { return _path2; }
+ string Type() { return _type; }
+ string Format() { return _format; }
+ string Text() { return _text; }
+ string OnClick() { return _onClick; }
+ string OnDblClick() { return _onDblClick; }
+ string OnUp() { return _onUp; }
+ string OnDown() { return _onDown; }
+ string Condition() { return _condition; }
+ string SectionInclude() { return _sectionInclude; }
+ string Debug() { return _debug; }
+
+ //
+
+ int optionVariable(const char* expression)
+ {
+ string p;
+ string name;
+ int min = -32000;
+ char* buffer = strdup(expression);
+ char* v;
+
+ // evaluate variable
+
+ if ((v = strchr(buffer, '/')))
+ {
+ *v = 0;
+ v++;
+ min = atoi(buffer);
+ }
+ else
+ v = buffer;
+
+ if (evaluate(p, v) == success)
+ {
+ int res = calcExpression(p.c_str());
+
+ if (logLevel > 2 && strchr(v, '{'))
+ tell(3, "evaluated '%s' to '%s', res is (%d)", v, p.c_str(), res);
+
+ free(buffer);
+
+ return max(min, res);
+ }
+
+ free(buffer);
+
+ return 0;
+ }
+
+ int evaluateCondition(int recurse = no);
+ p_rgba evaluateColor(const char* var, p_rgba rgba);
+ string evaluatePath();
+ int lookupVariable(const char* name, string& value, const char* fmt = 0);
+ int lineCount() { return actLineCount; }
+
+ protected:
+
+ const char* variable(const char* name, const char* fmt, int& status);
+ const char* musicVariable(const char* group, const char* var, const char* fmt, int& status);
+
+ const char* formatDateTime(time_t time, const char* fmt, char* date, int len, int absolut = no);
+ const char* formatString(const char* str, const char* fmt, char* buffer, int len);
+ int replayModeValue(ReplayMode rm);
+ int condition(int left, int right, const char* op);
+ int condition(string* left, string* right, const char* op);
+
+ int haveBackgroundItem() { return backgroundItem && backgroundItem->Path() != ""; }
+
+ // data
+
+ int changed;
+ uint64_t nextDraw;
+ cDisplayItem* backgroundItem;
+ int visible;
+
+ // scroll mode
+
+ int marquee_active;
+ int marquee_left;
+ int marquee_idx;
+ int marquee_count;
+ int marquee_strip;
+ uint64_t nextAnimationAt;
+ int actLineCount;
+ int lastConditionState;
+
+ int lastX;
+ int lastY;
+ int lastWidth;
+ int lastHeight;
+
+ // statics
+
+ static int forceDraw;
+ static Renderer* render;
+ static cGraphTFTDisplay* vdrStatus;
+ static cDisplayItem* selectedItem;
+ static uint64_t nextForce;
+};
+
+//***************************************************************************
+// Variable File
+//***************************************************************************
+
+class cVariableFile : public cDisplayItem
+{
+ public:
+
+ cVariableFile() : cDisplayItem() { lastUpdate = 0; _delay = 60 * 1000; }
+
+ int hasName(const char* name) { return _path == name; }
+ const char* getName() { return _path.c_str(); }
+
+ int lookupVar(string var, string& value)
+ {
+ string::size_type p;
+ string name, ns;
+
+ value = "";
+
+ if ((p = var.find(".")) == string::npos)
+ return fail;
+
+ ns = var.substr(0, p);
+ name = var.substr(p+1);
+
+ tell(5, "lookup file variable '%s.%s' in file '%s'",
+ ns.c_str(), name.c_str(), _path.c_str());
+
+ if (_path != ns)
+ return fail;
+
+ map<string,string>::iterator iter = variables.find(name);
+
+ if (iter != variables.end())
+ {
+ value = iter->second.c_str();
+ return success;
+ }
+
+ return fail;
+ }
+
+ int parse()
+ {
+ int status = ignore;
+ FILE* fp;
+ char line[500+TB]; *line = 0;
+ char *c, *p, *name;
+ char* value;
+
+ if (time(0) < lastUpdate + (time_t)_delay/1000)
+ return ignore;
+
+ variables.clear();
+
+ if (!(fp = fopen(_path2.c_str(), "r")))
+ {
+ tell(0, "Can't open '%s', error was '%s'", _path2.c_str(), strerror(errno));
+ return ignore;
+ }
+
+ while ((c = fgets(line, 500, fp)))
+ {
+ line[strlen(line)] = 0; // cut linefeed
+
+ if ((p = strstr(line, "//"))) // cut comments
+ *p = 0;
+
+ if ((p = strstr(line, ";"))) // cut line end
+ *p = 0;
+
+ // skip empty lines
+
+ Str::allTrim(line);
+
+ if (Str::isEmpty(line))
+ continue;
+
+ // check line, search value
+
+ if (!(name = strstr(line, "var ")) || !(value = strchr(line, '=')) || name >= value)
+ {
+ tell(0, "Info: Ignoring invalid line [%s] in '%s'", line, _path2.c_str());
+ continue;
+ }
+
+ name += strlen("var ");
+ *value = 0;
+ value++;
+
+ Str::allTrim(name);
+ Str::allTrim(value);
+
+ tell(4, "append variable '%s' with value '%s'", name, value);
+ variables[name] = value;
+ status = success;
+ }
+
+ fclose(fp);
+ lastUpdate = time(0);
+
+ return status;
+ }
+
+ protected:
+
+ map<string,string> variables;
+ time_t lastUpdate;
+};
+
+//***************************************************************************
+// Display Items
+//***************************************************************************
+
+class cDisplayText : public cDisplayItem
+{
+ public:
+
+ cDisplayText() { lastText = ""; }
+
+ int groupOf()
+ {
+ int group = groupUnknown;
+
+ if (useVarOf("{replay"))
+ group |= groupReplay;
+ if (useVarOf("{recording"))
+ group |= groupRecording;
+ if (useVarOf("{actRecording"))
+ group |= groupRecording;
+ if (useVarOf("{event"))
+ group |= groupChannel;
+ if (useVarOf("{present"))
+ group |= groupChannel;
+ if (useVarOf("{following"))
+ group |= groupChannel;
+ if (useVarOf("{videoSize"))
+ group |= groupChannel;
+ if (useVarOf("{channel"))
+ group |= groupChannel;
+ if (useVarOf("{volume"))
+ group |= groupVolume;
+ if (useVarOf("{calibration"))
+ group |= groupCalibrate;
+
+ group |= groupVarFile;
+
+ return group;
+ }
+
+ int draw();
+
+ protected:
+
+ string lastText;
+};
+
+class cDisplayImage : public cDisplayItem
+{
+ public:
+
+ int groupOf()
+ {
+ int group = groupUnknown;
+
+ if (useVarOf("{replay"))
+ group |= groupReplay;
+ if (useVarOf("{recording"))
+ group |= groupRecording;
+ if (useVarOf("{actRecording"))
+ group |= groupRecording;
+ if (useVarOf("{event"))
+ group |= groupChannel;
+ if (useVarOf("{present"))
+ group |= groupChannel;
+ if (useVarOf("{following"))
+ group |= groupChannel;
+ if (useVarOf("{videoSize"))
+ group |= groupChannel;
+ if (useVarOf("{channel"))
+ group |= groupChannel;
+ if (useVarOf("{volume"))
+ group |= groupVolume;
+ if (useVarOf("{calibration"))
+ group |= groupCalibrate;
+
+ return group;
+ }
+
+ int draw();
+};
+
+class cDisplayTextList : public cDisplayItem
+{
+ public:
+
+ int groupOf()
+ {
+ int group = groupTextList;
+
+ if (useVarOf("{music"))
+ group |= groupMusic;
+ if (useVarOf("{actTimers"))
+ group |= groupRecording;
+
+ return group;
+ }
+
+ int draw();
+};
+
+class cDisplayRectangle : public cDisplayItem
+{
+ public:
+
+ int draw();
+};
+
+
+class cDisplayBackground : public cDisplayImage
+{
+ public:
+
+ int groupOf() { return groupUnknown; }
+ int draw();
+};
+
+class cDisplayImageFile : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupReplay; }
+ int draw();
+};
+
+class cDisplayImageDir : public cDisplayItem
+{
+ public:
+
+ // int groupOf() { return groupReplay; }
+ int init();
+ int draw();
+
+ protected:
+
+ struct ImageFile
+ {
+ int initialized;
+ std::string path;
+ unsigned int width;
+ unsigned int height;
+ unsigned int orientation;
+ unsigned int landscape;
+ };
+
+ int getNext(std::vector<ImageFile>::iterator& it, ImageFile*& file);
+ int scanDir(const char* path, int level = 0);
+
+ std::vector<ImageFile> images;
+ std::vector<ImageFile>::iterator current;
+};
+
+class cDisplayCalibrationCursor : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupCalibrate; }
+ int draw();
+};
+
+class cDisplayMenuButton : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupButton; }
+ int draw();
+};
+
+class cDisplayMenuButtonBackground : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupButton; }
+ int draw();
+};
+
+class cDisplayMessage : public cDisplayItem
+{
+ public:
+
+ cDisplayMessage() : cDisplayItem() { visible = no; }
+ int groupOf() { return groupMessage; }
+ int isForegroundItem() { return yes; }
+
+ int draw();
+};
+
+class cDisplayVolumeMuteSymbol : public cDisplayItem
+{
+ public:
+
+ cDisplayVolumeMuteSymbol() : cDisplayItem() { visible = no; }
+ int groupOf() { return groupVolume; }
+ int isForegroundItem() { return yes; }
+
+ int draw();
+};
+
+class cDisplayVolumebar : public cDisplayItem
+{
+ public:
+
+ cDisplayVolumebar() : cDisplayItem() { visible = no; }
+ int groupOf() { return groupVolume; }
+ int isForegroundItem() { return yes; }
+
+ int draw();
+};
+
+class cDisplayTimebar : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupChannel; }
+ int draw();
+};
+
+class cDisplayProgressBar : public cDisplayItem
+{
+ public:
+
+ int draw();
+};
+
+class cDisplaySymbol : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupChannel; }
+ int draw();
+};
+
+class cDisplayMenu : public cDisplayItem
+{
+ public:
+
+ int draw();
+};
+
+class cDisplayMenuSelected : public cDisplayItem
+{
+ public:
+
+ int draw();
+};
+
+class cDisplayMenuColumn : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupMenu; }
+ int draw();
+};
+
+class cDisplayMenuColumnSelected : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupMenu; }
+ int draw();
+};
+
+class cDisplayMenuEventColumn : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupMenu; }
+ int draw();
+};
+
+class cDisplayMenuEventColumnSelected : public cDisplayItem
+{
+ public:
+
+ int groupOf() { return groupMenu; }
+ int draw();
+};
+
+class cDisplayPartingLine : public cDisplayItem
+{
+ public:
+
+ int draw() { return cDisplayItem::draw(); }
+};
+
+class cDisplaySpectrumAnalyzer : public cDisplayItem
+{
+ public:
+
+ int draw();
+};
+
+class cDisplaySysinfo : public cDisplayItem
+{
+ public:
+
+ int draw();
+
+ protected:
+
+ int lastCpuLoad;
+ unsigned long lastUsedMem;
+ unsigned long lastUsedDisk;
+};
+
+//***************************************************************************
+// Lists
+//***************************************************************************
+//***************************************************************************
+// Theme Items
+//***************************************************************************
+
+class cDisplayItems : public cList<cDisplayItem>
+{
+ public:
+
+ cDisplayItem* getItemByKind(Ts::ItemKind k);
+ cDisplayItem* getItemById(const char* id);
+};
+
+//***************************************************************************
+// Class cThemeSection
+// - represents a section of a theme
+//***************************************************************************
+
+class cThemeSection : public cListObject, cThemeService
+{
+ public:
+
+ cThemeSection(string aName = "unnamed") { name = aName; defaultsItem = 0; }
+ ~cThemeSection() { if (defaultsItem) delete defaultsItem; }
+
+ string getName() { return name; }
+ cDisplayItems* getItems() { return &items; }
+
+ cDisplayItem* getItemByKind(ItemKind k) { return items.getItemByKind(k); }
+ cDisplayItem* getItemById(const char* id) { return items.getItemById(id); }
+
+ cDisplayItem* First() { return items.First(); }
+ cDisplayItem* Next(cDisplayItem* item) { return items.Next(item); }
+
+ void Ins(cDisplayItem* item, cDisplayItem* before = 0)
+ { items.Ins(item, before); item->setSection(this); }
+
+ void Add(cDisplayItem* item, cDisplayItem* after = 0)
+ { items.Add(item, after); }
+
+ cDisplayItem* getDefaultsItem() { return defaultsItem; }
+
+ void setDefaultsItem(cDisplayItem* item)
+ {
+ if (defaultsItem)
+ delete defaultsItem;
+
+ defaultsItem = item;
+ }
+
+ int lookupVar(string name, string& value)
+ {
+ value = "";
+
+ map<string,string>::iterator iter = variables.find(name);
+
+ if (iter != variables.end())
+ {
+ value = iter->second.c_str();
+ return success;
+ }
+
+ if (varFiles.size())
+ {
+ string::size_type p;
+
+ if ((p = name.find(".")) == string::npos)
+ return fail;
+
+ map<string,cVariableFile*>::iterator itFile = varFiles.find(name.substr(0, p));
+
+ if (itFile != varFiles.end())
+ {
+ if (itFile->second->lookupVar(name, value) == success)
+ return success;
+ }
+ }
+
+ return fail;
+ }
+
+ uint64_t getNextUpdateTime();
+ int reset();
+ int updateGroup(int group);
+
+ void addVarFile(cDisplayItem* p)
+ {
+ cVariableFile* pp = (cVariableFile*)p;
+ varFiles[pp->getName()] = pp;
+ }
+
+ int hasVarFile(const char* name)
+ {
+ return varFiles.find(name) != varFiles.end();
+ }
+
+ map<string,cVariableFile*>* getVarFiles()
+ {
+ return &varFiles;
+ }
+
+ int updateVariables();
+
+ map<string,string> variables;
+
+ protected:
+
+ string name;
+ cDisplayItems items;
+ cDisplayItem* defaultsItem; // Item holding default values
+ map<string,cVariableFile*> varFiles;
+};
+
+//***************************************************************************
+// Class cThemeSections
+// - holding all sections
+//***************************************************************************
+
+class cThemeSections : public cList<cThemeSection>
+{
+ public:
+
+ cThemeSection* getSection(string name);
+};
+
+//***************************************************************************
+// Class cGraphTFTTheme
+// - holding all Items included in the theme configuration file
+//***************************************************************************
+
+class cGraphTFTTheme : public cConfig<cThemeItem>, public cListObject, cThemeService
+{
+ public:
+
+ cGraphTFTTheme();
+ virtual ~cGraphTFTTheme() { exit(); }
+
+ // functions
+
+ int init();
+ int exit();
+ int activate(int fdInotify);
+ int deactivate(int fdInotify);
+ int check(const char* theVersion);
+ int load(const char* path);
+ int checkViewMode();
+
+ // get
+
+ string getName() { return name; }
+ string getThemeVersion() { return themeVersion; }
+ string getSyntaxVersion() { return syntaxVersion; }
+ string getDir() { return dir; }
+ string getStartImage() { return startImage; }
+ string getEndImage() { return endImage; }
+ int getWidth() { return width; }
+ int getHeight() { return height; }
+ string getFontPath() { return fontPath; }
+ int isInitialized() { return initialized; }
+
+ // set
+
+ void setName(string aValue) { name = aValue; }
+
+ void setThemeVersion(string aValue) { themeVersion = aValue; }
+ void setSyntaxVersion(string aValue) { syntaxVersion = aValue; }
+ void setDir(string aValue) { dir = aValue; }
+ void setStartImage(string aValue) { startImage = aValue; }
+ void setEndImage(string aValue) { endImage = aValue; }
+ void setWidth(int aValue) { width = aValue; }
+ void setHeight(int aValue) { height = aValue; }
+ void setFontPath(string aValue) { fontPath = aValue; }
+
+ void AddMapItem(cDisplayItem* item, cDisplayItem* after = 0)
+ { mapSection.Add(item, after); }
+
+ //
+
+ void addNormalSection(string section);
+ const char* getNormalMode(const char* modeName);
+ const char* nextNormalMode(const char* modeName);
+ const char* prevNormalMode(const char* modeName);
+ char** getNormalSections() { return normalModes; }
+ int getNormalSectionsCount() { return normalModesCount; }
+
+ // the sections
+
+ cThemeSection* getSection(string name) { return sections.getSection(name); }
+ cThemeSections* getSections() { return &sections; }
+
+ cThemeSection* FirstSection()
+ { return sections.First(); }
+
+ cThemeSection* NextSection(cThemeSection* sect)
+ { return sections.Next(sect); }
+
+ string getPathFromImageMap(const char* name);
+
+ void resetDefines() { defineCount = 0; clearIfdefs();}
+ void clearIfdefs() { while (!skipContent.empty()) skipContent.pop(); }
+ int isSkipContent() { return !skipContent.empty() && skipContent.top(); }
+
+ int lookupVar(string name, string& value)
+ {
+ value = "";
+
+ map<string,string>::iterator iter;
+
+ iter = menuVariables.find(name);
+
+ if (iter != menuVariables.end())
+ {
+ value = iter->second.c_str();
+ return success;
+ }
+
+ iter = variables.find(name);
+
+ if (iter != variables.end())
+ {
+ value = iter->second.c_str();
+ return success;
+ }
+
+ return fail;
+ }
+
+ // data
+
+ string defines[100];
+ int defineCount;
+ std::stack<int> skipContent;
+
+ map<string, string> variables;
+ map<string, string> menuVariables;
+ map<int, cDisplayItem*> inotifies;
+
+ protected:
+
+ // data
+
+ int width;
+ int height;
+ string dir;
+ string name;
+ string themeVersion;
+ string syntaxVersion;
+ string startImage;
+ string endImage;
+ string fontPath;
+ int initialized;
+ cThemeSections sections;
+ cThemeSection mapSection;
+ char* normalModes[100];
+ int normalModesCount;
+};
+
+//***************************************************************************
+// Class cGraphTFTThemes
+// - holding all themes
+//***************************************************************************
+
+class cGraphTFTThemes : public cList<cGraphTFTTheme>
+{
+ public:
+
+ cGraphTFTTheme* getTheme(string aTheme);
+
+ static cGraphTFTTheme* theTheme;
+
+ protected:
+};
+
+//***************************************************************************
+//
+//***************************************************************************
+
+typedef cGraphTFTThemes Thms;
+
+//***************************************************************************
+// External
+//***************************************************************************
+
+extern cGraphTFTThemes themes;
+
+//***************************************************************************
+#endif //__GTFT_THEME_H
diff --git a/themes/DeepBlue.theme b/themes/DeepBlue.theme
new file mode 100644
index 0000000..7a8c7ca
--- /dev/null
+++ b/themes/DeepBlue.theme
@@ -0,0 +1,1469 @@
+//***************************************************************************
+// Theme - Informationen:
+//
+// DeepBlue graphTFT Theme from horchi
+//
+// Used graphTFT Font and graphTFT >= 0.3.0
+//
+// Dieses Theme ist optimiert für Displays mit 720x576 Auflösung
+//
+//***************************************************************************
+
+//***************************************************************************
+//***************************************************************************
+// Wird die folgende #define Zeile einkommentiert, erhält man
+// die Ausgabe von mouse Buttons (z.B. für Touch-Displays)
+// (Einkommentieren durch entfernen der // vor #define...)
+// Die benutzerbezogenen Maus-Icons sind wie folgt in der keymacros.conf vorkonfiguriert
+// User1 @osdteletext
+// User2 @mp3
+// User3 @mplayer
+// User4 @dvd
+// User5 @burn
+// User6 @osdpip
+// User8 @trayopen
+// User9 @mailbox
+//***************************************************************************
+#define MOUSE_BUTTONS
+
+
+//***************************************************************************
+// Auswahl der Lautstärke-Anzeige
+// Wird die folgende #define Zeile einkommentiert, erhält man eine alternative
+// Laustärkeanzeige
+// uncomment out for alternative volume display
+// (Einkommentieren durch entfernen von // vor #define...)
+//***************************************************************************
+//#define VOL_STYLE_BLUE
+
+//***************************************************************************
+// mp3/music Plugin Anzeigeauswahl
+//
+// Entferne die Doppelslashes bei der #define Zeile für die gewünschte Varinate
+// zur Anzeige der mp3/music Plugin Informationen
+// (immer nur eine Ziele einkommentieren)
+// uncomment if you like to use the music plugin
+// MP3_PLUGIN - Einfache Ausgabe für das mp3 und music Plugin
+// MUSIC_PLUGIN - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in AvP Style
+// MUSIC_PLUGIN_MORONE_STYLE - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in Morone Style
+//***************************************************************************
+//#define MP3_PLUGIN
+#define MUSIC_PLUGIN
+//#define MUSIC_PLUGIN_MORONE_STYLE
+
+
+
+//***************************************************************************
+// Beginn der Theme-Beschreibung
+//***************************************************************************
+//***************************************************************************
+//***************************************************************************
+
+
+//***************************************************************************
+// Themeweit nutzbare Variablen einrichten
+// Set global Vars
+//***************************************************************************
+//***************************************************************************
+// Informationen zum Theme und der benötigten GraphTFT version
+//***************************************************************************
+var varThemeName = "DeepBlue";
+var varThemeVersion = "0.3.1";
+var varSyntaxVersion = "0.4.1";
+
+//***************************************************************************
+// Suchpfade für die EPG Images
+//***************************************************************************
+var varEPGimagesPath_1 = "/ramdisk/epgimages/";
+var varEPGimagesPath_2 = "/media/epgimages/";
+var varEPGimagesPath_3 = "/video0/epgimages/";
+var varImagesPath_1 = "/video0/images/";
+
+
+
+//***************************************************************************
+// Initialisierung und Default Vorbelegeung für Mouse Variablen
+//***************************************************************************
+var varSwitchMouseNumber = 0;//Normaler Zehnerblock
+var varHideColorButtons = 0; //Farbtasten anzeigen
+var varHidePrevChannel = 0; //PrevChannel Taste anzeigen
+var varTouchMenu = 0; //Vorbelegung für Mausmenüanzeige
+
+
+
+//***************************************************************************
+// Allgemeine Einstellungen
+//***************************************************************************
+[Theme]
+
+//Theme name={varThemeName} {varThemeVersion},dir={varThemeName},fontPath=./fonts:../../fonts,width=800,height=600,,themeVersion={varThemeVersion},syntaxVersion={varSyntaxVersion},startImage=backgrounds/start-blue.jpg,endImage=backgrounds/end-blue.jpg;
+Theme name={varThemeName} {varThemeVersion},dir={varThemeName},
+ fontPath=./fonts:../../fonts,width=720,height=576,
+ themeVersion={varThemeVersion},
+ syntaxVersion={varSyntaxVersion},
+ startImage=backgrounds/start-blue.jpg,
+ endImage=backgrounds/end-blue.jpg;
+
+
+
+//***************************************************************************
+// Aufnahmesymbol & Anzeige des Aufnahmetitels
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+//***************************************************************************
+[RecSymbol]
+
+SymRecording x=426,y=520,pathON=symbols/recOn.png;
+Recording x=507,y=523,width=203,height=45,lines=1,size=24,red=0,green=0,blue=0,bg_red=50,bg_green=84,bg_blue=121,scroll=marquee,scroll_count=5;
+
+
+
+//***************************************************************************
+// Anzeige der Lautstärke, Lautstärkeregelung und Mute
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+//***************************************************************************
+[Volume]
+
+#ifdef VOL_STYLE_BLUE
+Volumebar x=142,y=262,width=520,height=40,switch=yes,bg_x=120,bg_y=238,bg_width=592,bg_height=100,path2=backgrounds/volume.png,permanent=no,delay=4;
+VolumeMuteSymbol x=10,y=238,pathON=symbols/mute_on.png,pathOFF=symbols/mute_off.png,permanent=no,delay=4;
+#else
+Volumebar x=217,y=310,width=265,height=40,red=250,green=155,blue=44,bg_red=50,bg_green=50,bg_blue=50,permanent=no,delay=4;
+Volumebar x=222,y=262,width=0,bg_x=200,bg_y=238,bg_width=308,bg_height=100,path2=backgrounds/volume2.png,permanent=no,delay=4;
+Volumebar text=percent,size=12,red=0,green=0,blue=0,x=450,y=250,width=0,height=40,alpha=0,permanent=no,delay=4;
+VolumeMuteSymbol x=220,y=365,pathON=symbols/mute_on2.png,pathOFF=symbols/mute_off2.png,permanent=no,delay=4;
+#endif
+
+
+
+//***************************************************************************
+// Anzeige der OSD Meldungen
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+//***************************************************************************
+[OSD-Messages]
+
+Message x=10,y=483,width=670,height=35,size=19,red=0,green=0,blue=0,bg_x=2,bg_y=481,bg_width=715,bg_height=30,bg_red=197,bg_green=129,bg_blue=24,delay=5;
+
+
+
+//***************************************************************************
+// Kalibrationsanzeige für Touch Displays
+//***************************************************************************
+[Calibration]
+
+var calibrationFrameOffset = 60; //Abstand von der Mitte des Cursors zum oberen und linken Bildschirmrand
+
+Defaults font=graphTFT,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=250,bg_alpha=0;
+Background x=0,y=0,width=720,height=576,red=0,green=0,blue=160,bg_red=0,bg_green=0,bg_blue=0;
+
+Text text=Calibration,x=0,y=20,size=44,width=720,height=70,align=center;
+//Kalibrierungsanweisungen
+Text text={calibrationInstruction},x=0,y=170,size=22,width=720,height=40,align=center;
+Text text={calibrationInfo},x=0,y=210,size=22,width=720,height=40,align=center;
+//Ausgabe der Kalibrationswerte
+Text text=touched: {calibrationTouchedX} / {calibrationTouchedY},x=0,y=300,size=22,width=720,height=40,align=center;
+Text text=Offset: {calibrationOffsetX} / {calibrationOffsetY},x=0,y=340,size=22,width=720,height=40,align=center;
+Text text=Scale: {calibrationScaleX} / {calibrationScaleY},x=0,y=380,size=22,width=720,height=40,align=center;
+//Anzeige Kalibrationscursor
+CalibrationCursor width=30,height=30,path=symbols/calibratecursor.png;
+
+//Testgrid zeichnen
+Rectangle x=0,y=100,width=720,height=3;
+Rectangle x=0,y=150,width=720,height=3;
+Rectangle x=0,y=426,width=720,height=3;
+Rectangle x=0,y=476,width=720,height=3;
+
+Rectangle x=100,y=0,width=3,height=720;
+Rectangle x=150,y=0,width=3,height=576;
+Rectangle x=570,y=0,width=3,height=576;
+Rectangle x=620,y=0,width=3,height=576;
+
+MenuButtonBackgroundYellow x=360,y=529,width=170,height=45,pathON=menu/button-yellow.png,bg_red=50,bg_green=84,bg_blue=121,on_click=Yellow;
+MenuButtonYellow x=360,y=534,width=170,height=45,red=0,green=0,blue=0,alpha=255,align=center;
+
+
+
+//***************************************************************************
+// Ausgabe eines Bildes oder, mittels Script, einer Bilderserie auf dem Display
+// Diese Funktion wird über das graphTFT OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Dia" aufgerufen
+//***************************************************************************
+[NormalDia]
+
+Background path=backgrounds/bg-sysinfo-blue.png;
+ImageFile x=0,y=0,width=720,height=576,path=/tmp/dia.file,path2=symbols/nocover.png,fit=yes,aspect_ratio=yes;
+
+Include=OSD-Messages;
+
+
+
+//***************************************************************************
+// Ausgabe von Systeminformationen
+// Diese Funktion wird über das graphTFT OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Sysinfo" aufgerufen
+//***************************************************************************
+[NormalSysinfo]
+
+Defaults font=graphTFT,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+Background path=backgrounds/bg-sysinfo-blue.png;
+
+// {...ChannelName/tologo} sorgt dafür, dass event. Sonderzeichen im Sendernamen entfernt werden
+// So wird z.B. beim Sender BR-alpha* das Logo BR-alpha.png gesich und nicht BR-alpha*.png
+Image x=5,y=2,width=60,height=35,fit=yes,aspect_ratio=yes,path=columnimages/{presentChannelName/tologo}.png;
+
+Text text={time/%d.%m %H:%M},x=520,y=27,width=200,height=40,red=52,green=162,blue=159;
+
+Text x=70,y=7,text={presentChannelName},align=center,width=420,height=30;
+
+Text text=Prozessor,size=24,x=25,y=47,width=200,height=30,red=52,green=162,blue=159;
+Text text=load,x=25,y=95,width=130,height=30;
+Sysinfo type=cpuload,text=percent,align=center,size=16,x=160,y=90,width=500,height=40,bg_x=160,delay=3,switch=yes,bg_red=150,bg_green=150,bg_blue=150;
+Text text=idle,x=25,y=155,width=130,height=30;
+Sysinfo type=cpuidle,text=percent,align=center,size=16,x=160,y=150,width=500,height=40,bg_x=160,delay=3,red=100,green=255,blue=100,bg_red=150,bg_green=150,bg_blue=150;
+
+Text text=Speicher,size=24,x=25,y=210,width=200,height=30,red=52,green=162,blue=159;
+Text text=used,x=25,y=270,width=130,height=30;
+Sysinfo type=memused,factor=1048576,text=value,align=center,unit=MB,size=16,x=160,y=260,width=500,height=40,bg_x=160,delay=3,switch=yes,red=100,green=255,blue=100,bg_red=150,bg_green=150,bg_blue=150;
+
+Text text=Festplatten,size=24,x=25,y=330,width=200,height=30,red=52,green=162,blue=159;
+Text text=video,x=25,y=390,width=130,height=30;
+Sysinfo type=disk,reference=/video?,factor=1073741824,align=center,text=value,unit=GB,size=16,x=160,y=385,width=500,height=40,bg_x=160,delay=3,switch=yes,red=100,green=255,blue=100,bg_red=150,bg_green=150,bg_blue=150;
+Text text=system,x=25,y=450,width=130,height=30;
+Sysinfo type=disk,reference=/,factor=1073741824,align=center,text=value,unit=GB,size=16,x=160,y=445,width=500,height=40,bg_x=160,delay=3,switch=yes,red=100,green=255,blue=100,bg_red=150,bg_green=150,bg_blue=150;
+
+#ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+#endif
+
+Include=Volume;
+Include=OSD-Messages;
+
+
+
+//***************************************************************************
+// Ausgabe der Programminformationen mit einer Uhr als zentraler Bestandteil der Anzeige
+// Diese Funktion wird über das graphTFT OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Clock" aufgerufen
+//***************************************************************************
+[NormalClock]
+
+Defaults font=graphTFT,red=50,green=130,blue=255,bg_red=0,bg_green=0,bg_blue=0;
+
+Background x=0,y=0,width=720,height=576,red=0,green=0,blue=0;
+
+Text text={time/%A\, %d. %B %G},x=25,y=10,width=500,height=35,size=20;
+Text text={time/%H:%M},x=10,y=140,width=690,height=250,size=180;
+Text text={presentTitle},x=25,y=450,height=120,width=450,size=28,red=150,green=150,blue=150,lines=2;
+Timebar x=485,y=500,width=215,height=18,path=symbols/progress.png,bg_red=230,bg_green=230,bg_blue=230,bg_alpha=255;
+Image x=620,y=15,width=80,height=50,fit=yes,aspect_ratio=yes,path=columnimages/{presentChannelName/tologo}.png;
+
+#ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+#endif
+
+Include=Volume;
+Include=OSD-Messages;
+
+//***************************************************************************
+// Non Live TV - shows actual recordings
+//***************************************************************************
+
+[NormalNonLiveTv]
+
+// TextList text={actRecordings},x=20,y=20,height=400,width=605,size=12;
+TextList text={actRunningRecordings},x=20,y=20,height=200,width=605,size=12,red=200,green=10,blue=10;
+TextList text={actPendingRecordings},x=20,y=230,height=200,width=605,size=12;
+
+Include=OSD-Messages;
+
+
+//***************************************************************************
+// Definition der Mouse Button Fenster
+//***************************************************************************
+[MouseButtons]
+Defaults bg_alpha=0,foreground=yes;
+
+//Anzeige für Keyboardaufruf
+Image x=000,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_keyb_white.png,on_click=varTouchMenu:1:0;
+
+//***************************************************************************
+//Basisblock
+//{varHideColorButtons} = 0 -> Farbtasten anzeigen
+//{varHideColorButtons} = 1 -> Farbtasten nicht anzeigen
+//{varHidePrevChannel} = 0 -> PrevChannel Taste anzeigen
+//{varHidePrevChannel} = 1 -> PrevChannel Taste nicht anzeigen
+
+if ({varTouchMenu})
+ //Hintergrund
+ Image x=114,y=343,width=408,height=159,path=menu/mouse_btn/btn_back_master.png;
+
+ //Schalterbuttons für Nummernblock (2) und Extended Block (3), beim zweiten Klick, Rückfall auf 1 (:2:1)
+ Image x=124,y=351,width=40,height=40,delay=150,path=menu/mouse_btn/btn_keyb_segment_trans_left.png,on_click=varTouchMenu:2:1,delay=15;
+ Image x=472,y=351,width=40,height=40,delay=150,path=menu/mouse_btn/btn_keyb_segment_trans_right.png,on_click=varTouchMenu:3:1,delay=15;
+
+ //Erste Zeile von oben
+ Image x=222,y=357,width=40,height=40,delay=150,path=menu/mouse_btn/btn_rec.png,on_click=Record;
+ Image x=376,y=357,width=40,height=40,delay=150,path=menu/mouse_btn/btn_power.png,on_click=Power;
+
+ //Zweite Zeile von oben
+ Image condition={varHidePrevChannel} <> 1,x=132,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevchannel.png,on_click=PrevChannel;
+ Image x=177,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_back.png,on_click=back;
+ Image x=222,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_up.png,on_click=Up;
+ Image x=267,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_ok.png,on_click=ok;
+
+ Image x=376,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_minus.png,on_click=Volume-;
+ Image x=421,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mute.png,on_click=Mute;
+ Image x=466,y=402,width=40,height=40,delay=150,path=menu/mouse_btn/btn_plus.png,on_click=Volume+;
+
+ //Dritte Zeile von oben
+ Image x=132,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_menu.png,on_click=Menu;
+ Image x=177,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_left.png,on_click=Left;
+ Image x=222,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_down.png,on_click=Down;
+ Image x=267,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_right.png,on_click=Right;
+
+ Image condition={varHideColorButtons} <> 1,x=331,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_red.png,on_click=Red;
+ Image condition={varHideColorButtons} <> 1,x=376,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_green.png,on_click=Green;
+ Image condition={varHideColorButtons} <> 1,x=421,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_yellow.png,on_click=Yellow;
+ Image condition={varHideColorButtons} <> 1,x=466,y=447,width=40,height=40,delay=150,path=menu/mouse_btn/btn_blue.png,on_click=Blue;
+endif
+
+//***************************************************************************
+//Nummernblock
+//{varSwitchMouseNumber} = 0 -> Normaler Zehnerblock
+//{varSwitchMouseNumber} = 1 -> Ändert Zehnerblock-Zahlen zu Aufnahme Icons
+//{varSwitchMouseNumber} = 2 -> Ändert Zehnerblock-Zahlen zu DVD Wiedergabe Icons
+
+if ({varTouchMenu} == 2)
+ //Hintergrund
+ Image x=009,y=192,width=158,height=205,path=menu/mouse_btn/btn_back_numberblock.png;
+
+ //Erste Zeile von oben
+ Image x=023,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_1.png,on_click=1;
+ Image condition={varSwitchMouseNumber} = 0,x=068,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_2.png,on_click=2;
+ Image condition={varSwitchMouseNumber} = 1,x=068,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cut.png,on_click=2;
+ Image condition={varSwitchMouseNumber} = 2,x=068,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_subtitle.png,on_click=2;
+ Image condition={varSwitchMouseNumber} <> 2,x=113,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_3.png,on_click=3;
+ Image condition={varSwitchMouseNumber} = 2,x=113,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdangle.png,on_click=3;
+
+ //Zweite Zeile von oben
+ Image condition={varSwitchMouseNumber} <> 1,x=023,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_4.png,on_click=4;
+ Image condition={varSwitchMouseNumber} = 1,x=023,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmarkleft.png,on_click=4;
+ Image condition={varSwitchMouseNumber} = 2,x=023,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prev.png,on_click=4;;
+
+ Image condition={varSwitchMouseNumber} <> 2,x=068,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_5.png,on_click=5;
+ Image condition={varSwitchMouseNumber} = 2,x=068,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdnavi.png,on_click=5;
+ Image condition={varSwitchMouseNumber} = 0,x=113,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_6.png,on_click=6;
+ Image condition={varSwitchMouseNumber} = 1,x=113,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmarkright.png,on_click=6;
+ Image condition={varSwitchMouseNumber} = 2,x=113,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_next.png,on_click=6;
+
+ //Dritte Zeile von oben
+ Image condition={varSwitchMouseNumber} = 0,x=023,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_7.png,on_click=7;
+ Image condition={varSwitchMouseNumber} = 1,x=023,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevtitle.png,on_click=7;
+ Image condition={varSwitchMouseNumber} = 2,x=023,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevtitle.png,on_click=7;
+ Image condition={varSwitchMouseNumber} <> 2,x=068,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_8.png,on_click=8;
+ Image condition={varSwitchMouseNumber} = 2,x=068,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdmenu.png,on_click=8;
+ Image condition={varSwitchMouseNumber} = 0,x=113,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_9.png,on_click=9;
+ Image condition={varSwitchMouseNumber} = 1,x=113,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_nexttitle.png,on_click=9;
+ Image condition={varSwitchMouseNumber} = 2,x=113,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_nexttitle.png,on_click=9;
+
+ //Vierte Zeile von oben
+ Image condition={varSwitchMouseNumber} = 0,x=068,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_0.png,on_click=0;
+ Image condition={varSwitchMouseNumber} = 1,x=068,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmark.png,on_click=0;
+ Image condition={varSwitchMouseNumber} = 2,x=068,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_audiomenu.png,on_click=0;
+endif
+
+//***************************************************************************
+//Extended Icon Block
+if ({varTouchMenu} == 3)
+ //Hintergrund
+ Image x=465,y=192,width=255,height=205,path=menu/mouse_btn/btn_back_extended.png;
+
+ //Erste Zeile von oben
+ Image x=483,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvd.png,on_click=User4;
+ Image x=573,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_audio.png,on_click=Audio;
+ Image x=618,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mp3.png,on_click=User2;
+ Image x=663,y=207,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mplayer.png,on_click=User3;
+
+ //Zweite Zeile von oben
+ Image x=483,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mail.png,on_click=User9;
+ Image x=528,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_videotext.png,on_click=User1;
+ Image x=573,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_pip.png,on_click=User6;
+ Image x=663,y=252,width=40,height=40,delay=150,path=menu/mouse_btn/btn_burn.png,on_click=User5;
+
+ //Dritte Zeile von oben
+ Image x=528,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_pause.png,on_click=Pause;
+ Image x=573,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_stop.png,on_click=Stop;
+ Image x=618,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_play.png,on_click=Play;
+ Image x=663,y=297,width=40,height=40,delay=150,path=menu/mouse_btn/btn_eject.png,on_click=User8;
+
+ //Vierte Zeile von oben
+ Image x=528,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prev.png,on_click=Prev;
+ Image x=573,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_frwd.png,on_click=FastRew;
+ Image x=618,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_ffwd.png,on_click=FastFwd;
+ Image x=663,y=342,width=40,height=40,delay=150,path=menu/mouse_btn/btn_next.png,on_click=Next;
+endif
+
+
+//***************************************************************************
+// Allgemeine Einstellungen zur Ausgabe der TV und RADIO Informationen
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+// Diese Funktion wird über das graphTFT OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Standard" aufgerufen
+//***************************************************************************
+[TV_Radio_Common]
+Defaults font=graphTFT,size=24,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// Senderlogo Logo oben links
+Image x=15,y=15,width=80,height=50,fit=yes,aspect_ratio=yes,path=columnimages/{presentChannelName/tologo}.png;
+
+//Datum/Uhr oben rechts
+Text text={time/%d.%m. %H:%M},x=510,y=55,width=210,height=40;
+//Textausgabe des Kanalnamens im Kopfbereich
+Text x=120,y=13,size=36,text={presentChannelName},align=center,width=360,height=60,lines=1,red=255,green=255,blue=255;
+
+// Themeversion
+Text text={varThemeName} {themeVersion}/{syntaxVersion},x=520,y=33,width=190,size=8,align=right,height=18,red=255,green=255,blue=255;
+
+//Kanalnummer unten links
+Text x=37,y=522,size=32,text={presentChannelNumber},align=center,width=80,height=50,lines=1,red=255,green=255,blue=255;
+
+// Verschiedene Möglichkeiten für die Timebar
+// some ways to display the timebar
+Timebar x=523,y=12,width=184,height=18,bg_x=520,bg_y=9,bg_width=190,bg_height=24,bg_red=0,bg_green=0,bg_blue=0,path=symbols/progress.png,bg_alpha=255;
+//Timebar x=525,y=12,width=180,height=21,bg_x=520,bg_y=8,bg_width=190,bg_height=29,bg_red=0,bg_green=0,bg_blue=0,switch=yes,bg_alpha=255;
+//Timebar x=525,y=12,width=180,height=21,bg_x=520,bg_y=8,bg_width=190,bg_height=29,bg_red=0,bg_green=0,bg_blue=0,switch=no,bg_alpha=255;
+
+//Aktuelle Sendung
+Text text={presentStartTime/%H:%M},x=5,y=110,width=120,height=45,size=30,red=255,green=255,blue=255;
+
+Image x=5,y=160,width=130,height=120,path={varEPGimagesPath_1}{presentID}.png:{varEPGimagesPath_2}{presentID}.png:{varEPGimagesPath_3}{presentID}.png:{varImagesPath_1}{presentTitle}.jpg,fit=yes,aspect_ratio=yes;
+Text condition="{presentDuration/%M}" != "",text={presentTitle} - {presentDuration/%M}',x=140,y=110,width=580,height=100,size=30,lines=2,red=255,green=255,blue=255,dots=yes; // Titelausgabe wenn Laufzeit ermittelbar
+Text condition="{presentDuration/%M}" = "",text={presentTitle},x=140,y=110,width=580,height=100,size=30,lines=2,red=255,green=255,blue=255,dots=yes; // Titelausgabe wenn Laufzeit NICHT ermittelbar
+Text text={presentSubtitle},x=140,y=210,width=580,height=80,size=24,lines=2;
+
+// Kommende Sendung
+Text text={followingStartTime/%H:%M},x=5,y=295,width=120,height=45,size=30,red=255,green=255,blue=255;
+Image x=5,y=345,width=130,height=120,path={varEPGimagesPath_1}{followingID}.png:{varEPGimagesPath_2}{followingID}.png:{varEPGimagesPath_3}{followingID}.png:{varImagesPath_1}{followingTitle}.jpg,fit=yes,aspect_ratio=yes;
+Text condition="{followingDuration/%M}" != "",text={followingTitle} - {followingDuration/%M}',x=140,y=295,width=580,height=100,size=30,lines=2,red=255,green=255,blue=255,dots=yes; // Titelausgabe wenn Laufzeit ermittelbar
+Text condition="{followingDuration/%M}" = "",text={followingTitle},x=140,y=295,width=580,height=100,size=30,lines=2,red=255,green=255,blue=255,dots=yes; // Titelausgabe wenn Laufzeit NICHT ermittelbar
+Text text={followingSubtitle},x=140,y=395,width=580,height=80,size=24,lines=2;
+
+// Iconleiste
+SymCrypt x=257,y=518,width=45,height=25,pathON=symbols/cryptOn.png;
+SymDD x=309,y=518,width=45,height=25,pathON=symbols/ddOn.png;
+Sym2ch x=361,y=518,width=45,height=25,pathON=symbols/ch2On.png;
+
+// E-Mail Symbol
+Image condition={hasNewMail} != 0,x=361,y=518,width=45,height=25,path=symbols/mail.png,delay=30;
+Text condition={unseenMailCount} > 0,text={unseenMailCount},
+ x=376,y=522,size=10,align=center,width=12,height=12,red=255,green=255,blue=255,delay=30,bg_alpha=0;
+
+#ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+#endif
+
+Include=Volume;
+Include=RecSymbol;
+Include=OSD-Messages;
+
+
+
+//***************************************************************************
+// Einstellungen zur Ausgabe der TV Informationen
+//***************************************************************************
+
+[NormalTV]
+// Hintergrund setzen
+Background path=backgrounds/bg-tv-blue.png;
+
+//Standardwerte laden
+Include=TV_Radio_Common;
+
+
+//Zusätzliches spezifisches Icon in der Iconleiste
+SymVTX x=205,y=518,width=45,height=25,pathON=symbols/vtxOn.png;
+
+//***************************************************************************
+// Einstellungen zur Ausgabe der RADIO Informationen
+//***************************************************************************
+[NormalRadio]
+// Hintergrund setzen
+Background path=backgrounds/bg-radio-blue.png;
+
+//Standardwerte laden
+Include=TV_Radio_Common;
+
+
+
+//***************************************************************************
+// Allgemeine Einstellungen für kommende Wiedergabe Abschnitte (Replay)
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+//***************************************************************************
+[ReplayCommon]
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// play
+Image condition={replayForward} == 1 && {replaySpeed} == -1 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/play.png,delay=1;
+// pause
+Image condition={replayForward} == 1 && {replaySpeed} == -1 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/pause.png,delay=1;
+// Fast Rewind
+Image condition={replayForward} == 0 && {replaySpeed} > 0 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/frew{replaySpeed}.png,delay=1;
+// Fast Forward
+Image condition={replayForward} == 1 && {replaySpeed} > 0 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/ffwd{replaySpeed}.png,delay=1;
+// Slow Rewind
+Image condition={replayForward} == 0 && {replaySpeed} > 0 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/srew.png,delay=1;
+// Slow Forward
+Image condition={replayForward} == 1 && {replaySpeed} > 0 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/sfwd.png,delay=1;
+
+Include=Volume;
+Include=OSD-Messages;
+
+
+
+//***************************************************************************
+// Anzeige der Informationen bei der Wiedergabe von Aufnahmen und media Dateien
+//***************************************************************************
+[ReplayNormal]
+//Standardwerte laden
+Include=ReplayCommon;
+
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// Hintergrund setzen
+Background path=backgrounds/bg-replay-blue.png;
+
+Text text={time/%d.%m. %H:%M},x=548,y=63,size=20,width=200,height=27;
+Text text={replayTitle},x=5,y=100,size=28,height=66,align=center,lines=2,red=255,green=255,blue=255;
+Rectangle x=5,y=195,width=710,height=3;
+Text text={replaySubtitle},x=10,y=208,size=20,height=80,lines=1,align=center;
+Image x=10,y=248,width=700,height=247,
+ path={replayPath}/thumbnail.png
+ :{varEPGimagesPath_1}{replayEventID}.png
+ :{varEPGimagesPath_2}{replayEventID}.png
+ :{varEPGimagesPath_3}{replayEventID}.png
+ :{varImagesPath_1}{replayTitle}.jpg
+ :{replayPath}/Cover-Enigma.jpg,fit=yes,aspect_ratio=yes;
+
+Text text={replayCurrent/%k:%M:%S},x=10,y=505,width=160,height=40,delay=1;
+Text text={replayTotal/%k:%M:%S},x=540,y=505,width=170,height=40,align=right,delay=1;
+
+// three different progressbar, uncomment which you like
+//Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=325,height=22,red=187,green=129,blue=22,bg_x=193,bg_y=550,bg_width=331,bg_height=28,bg_red=50,bg_green=84,bg_blue=121,bg_alpha=255;
+//Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=325,height=22,bg_x=193,bg_y=550,bg_width=331,bg_height=28,bg_red=0,bg_green=0,bg_blue=0,switch=yes,bg_alpha=255;
+Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=326,height=22,bg_x=193,bg_y=550,bg_width=332,bg_height=28,bg_red=0,bg_green=0,bg_blue=0,path=symbols/progress.png,bg_alpha=255,delay=3;
+
+#ifdef MOUSE_BUTTONS
+ var varSwitchMouseNumber = 1;
+ Include=MouseButtons;
+#endif
+
+
+//***************************************************************************
+// Anzeige der Informationen bei der Wiedergabe von DVDs
+//***************************************************************************
+[ReplayDVD]
+//Standardwerte laden
+Include=ReplayCommon;
+
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// Hintergrund setzen
+Background path=backgrounds/bg-dvd-blue.png;
+
+Text text={time/%H:%M},x=620,y=60,size=24,red=50,green=150,blue=150;
+
+Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=200,y=555,width=323,height=20,red=187,green=129,blue=22,bg_x=200,bg_y=555,bg_width=323,bg_height=20, bg_red=50, bg_green=84, bg_blue=121,path=symbols/progress.png,bg_alpha=255,delay=3;
+
+Text text={replayTitle},x=150,y=150,size=48,width=540,height=340,lines=4, red=255,green=255,blue=255;
+Text text={replayCurrent/%k:%M:%S},x=10,y=490,width=160,height=40,delay=1;
+Text text={replayTotal/%k:%M:%S},x=540,y=490,width=170,height=40,align=right,delay=1;
+
+#ifdef MOUSE_BUTTONS
+ var varSwitchMouseNumber = 2;
+ Include=MouseButtons;
+#endif
+
+
+//***************************************************************************
+// Anzeige der Informationen bei der Wiedergabe von mp3 Dateien
+// über das mp3 oder music Plugin.
+// Hier werden drei Versionen angeboten, die am Anfang dieses Themes
+// ausgewählt werden können.
+// MP3_PLUGIN - Einfache Ausgabe für das mp3 und music Plugin
+// MUSIC_PLUGIN - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in DeepBlue Style
+// MUSIC_PLUGIN_MORONE_STYLE - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in Morone Style
+//***************************************************************************
+[ReplayMP3]
+
+// ----------------------------------------
+#ifdef MP3_PLUGIN
+// ----------------------------------------
+
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+Background path=backgrounds/bg-mp3-blue.png;
+
+Text text={time/%d.%m. %H:%M},x=548,y=63,size=20,width=200,height=27;
+
+ImageFile x=195,y=180,width=330,height=247,path=/tmp/graphTFT.cover,path2=symbols/nocover.png,fit=yes,aspect_ratio=yes;
+
+// play
+Image condition={replayForward} == 1 && {replaySpeed} == -1 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/play.png,delay=1;
+// pause
+Image condition={replayForward} == 1 && {replaySpeed} == -1 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/pause.png,delay=1;
+// Fast Rewind
+Image condition={replayForward} == 0 && {replaySpeed} > 0 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/frew{replaySpeed}.png,delay=1;
+// Fast Forward
+Image condition={replayForward} == 1 && {replaySpeed} > 0 && {replayPlay} == 1,x=0,y=70,width=178,height=30,path=symbols/ffwd{replaySpeed}.png,delay=1;
+// Slow Rewind
+Image condition={replayForward} == 0 && {replaySpeed} > 0 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/srew.png,delay=1;
+// Slow Forward
+Image condition={replayForward} == 1 && {replaySpeed} > 0 && {replayPlay} < 1,x=0,y=70,width=178,height=30,path=symbols/sfwd.png,delay=1;
+
+Text text={replayTitle},x=10,y=5,size=26,height=40,width=700,align=center,lines=1,red=255,green=255,blue=255;
+Text text={replayCurrent/%k:%M:%S},x=10,y=505,width=160,height=40,delay=1;
+Text text={replayTotal/%k:%M:%S},x=540,y=505,width=170,height=40,align=right,delay=1;
+
+// three different progressbars, uncomment which you like
+//Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=325,height=22,red=187,green=129,blue=22,bg_x=193,bg_y=550,bg_width=331,bg_height=28,bg_red=50,bg_green=84,bg_blue=121;
+//Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=325,height=22,bg_x=193,bg_y=550,bg_width=331,bg_height=28,bg_red=0,bg_green=0,bg_blue=0,switch=yes;
+Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=196,y=553,width=326,height=22,bg_x=193,bg_y=550,bg_width=332,bg_height=28,bg_red=0,bg_green=0,bg_blue=0,path=symbols/progress.png,bg_alpha=255,delay=3;
+
+// Achtung delay beim SpectrumAnalyzer in ms
+//SpectrumAnalyzer x=500,y=350,width=200,height=100,delay=100,red=52,green=162,blue=159,path=backgrounds/spectrum.png;
+
+#ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+#endif
+
+Include=Volume;
+Include=OSD-Messages;
+
+#endif
+
+
+// ----------------------------------------
+#ifdef MUSIC_PLUGIN
+// ----------------------------------------
+
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+Background path=backgrounds/bg-music-blue.png;
+Text text={time/%d.%m. %H:%M},x=548,y=63,size=20,width=200,height=27;
+
+// Replay Name in Titlebar
+Text x=10,y=5,size=26,text={replayTitle},width=700,height=40,align=center,red=255,green=255,blue=255;
+
+// Cover
+Image x=22,y=124,width=190,height=190,fit=yes,aspect_ratio=yes,path={musicCoverName}:symbols/nocover.png;
+
+// Staus Info
+Text text={musicPlayStatus},x=490,y=119,height=28,width=200,size=20,align=center;
+Text text={musicFrequence} kHz\,{musicBitrate} kbps\, {musicStereoMode},x=477,y=170,height=20,width=220,size=12;
+Text condition={volumeMute} == 0,text={\\008},x=490,y=200,width=20,height=18,size=13,align=left,red=187,green=129,blue=22;
+Text condition={volumeMute} <> 0,text={\\009},x=490,y=200,width=20,height=18,size=13,align=left,red=187,green=129,blue=22;
+Volumebar x=520,y=205,width=170,height=10,red=187,green=129,blue=22,permanent=yes,bg_red=50,bg_green=50,bg_blue=50,bg_alpha=255;
+
+// Track Info
+Text text={musicArtist},x=245,y=122,height=20,width=200,size=12,red=255,green=248,blue=166;
+Text text=Album:,x=245,y=147,height=20,width=60,size=12,red=255,green=248,blue=166;
+Text text={musicAlbum},x=305,y=147,height=20,width=140,size=12,red=255,green=248,blue=166,scroll=ticker;
+Text text=Genre: {musicGenre},x=245,y=172,height=20,width=200,size=12,red=255,green=248,blue=166;
+Text text=Year: {musicYear},x=245,y=197,height=20,width=200,size=12,red=255,green=248,blue=166;
+
+// Playlist
+Text text={musicCurrentTrack},x=245,y=310,height=24,width=450,size=14,red=255,green=248,blue=166;
+TextList text={musicTrack},x=245,y=335,height=160,width=450,size=12;
+
+// Show some Details (commented out, totally missplaced ; ) only to show how it works)
+//Text text=File: {musicFilename},x=245,y=500,height=20,width=450,size=12,red=255,green=248,blue=166;
+//Text text=Comment: {musicComment},x=245,y=525,height=20,width=450,size=12,red=255,green=248,blue=166;
+
+// Status Icons
+Text condition={musicShuffle} == 1,text={\\001},x=20,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicShuffle} <> 1,text={\\001},x=20,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+Text condition={musicLoop} == 1,text={\\002},x=41,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicLoop} <> 1,text={\\002},x=41,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+Text condition={musicRecording} == 1,text={\\003},x=62,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicRecording} <> 1,text={\\003},x=62,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+// Variable musicLyrics wird noch nicht unterstützt
+Text condition={musicLyrics} == 1,text={\\004},x=83,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicLyrics} <> 1,text={\\004},x=83,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+// Variable musicCopy wird noch nicht unterstützt
+Text condition={musicCopy} == 1,text={\\005},x=104,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicCopy} <> 1,text={\\005},x=104,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+Text condition={musicShutdown} == 1,text={\\006},x=125,y=517,width=20,height=20,size=12,align=center,red=255,green=0,blue=0;
+Text condition={musicShutdown} <> 1,text={\\006},x=125,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+// Variable musicTimer wird noch nicht unterstützt
+Text condition={musicTimer} == 1,text={\\007},x=146,y=517,width=20,height=20,size=12,align=center,red=187,green=129,blue=22;
+Text condition={musicTimer} <> 1,text={\\007},x=146,y=517,width=20,height=20,size=12,align=center,red=64,green=64,blue=64;
+
+// to be continued ...
+
+// Progressbar
+Text text=Track {musicIndex} of {musicCount},x=250,y=242,height=20,width=200,size=12;
+Text text={replayCurrent/%k:%M:%S},x=250,y=265,width=70,height=18,size=12;
+Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=325,y=268,width=295,height=10,red=187,green=230,blue=22,bg_red=20,bg_green=50,bg_blue=50,bg_alpha=255;
+Text text={replayTotal/%k:%M:%S},x=625,y=265,width=70,height=18,size=12;
+
+Text text=Rating,x=460,y=242,height=20,width=100,size=12;
+Progressbar condition={musicRating} <> 3,total=255,value={musicRating},x=517,y=247,width=110,height=10,path=symbols/music-rating.png,bg_alpha=255;
+Text condition={musicRating} == 3,text=zum löschen,x=517,y=242,height=20,width=110,size=12,red=255,green=248,blue=166;
+
+// Delay beim SpectrumAnalyzer in ms!
+SpectrumAnalyzer x=18,y=340,width=200,height=140,delay=200,red=52,green=162,blue=159,path=backgrounds/spectrum.png;
+
+// Buttons Help
+Rectangle x=15,y=557,width=10,height=10,red=200,green=0,blue=0;
+Text text={musicButtonRed},x=30,y=552,height=22,width=150,size=14,bg_red=50,bg_green=84,bg_blue=121;
+Rectangle x=215,y=557,width=10,height=10,red=0,green=200,blue=0;
+Text text={musicButtonGreen},x=230,y=552,height=22,width=150,size=14,bg_red=50,bg_green=84,bg_blue=121;
+Rectangle x=415,y=557,width=10,height=10,red=250,green=250,blue=100;
+Text text={musicButtonYellow},x=430,y=552,height=22,width=150,size=14,bg_red=50,bg_green=84,bg_blue=121;
+Rectangle x=615,y=557,width=10,height=10,red=0,green=0,blue=200;
+Text text={musicButtonBlue},x=630,y=552,height=22,width=150,size=14,bg_red=50,bg_green=84,bg_blue=121;
+
+Include=OSD-Messages;
+
+#endif
+
+// ----------------------------------------
+#ifdef MUSIC_PLUGIN_MORONE_STYLE
+// ----------------------------------------
+
+Defaults font=graphTFT,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+Background path=backgrounds/bg-music-morone.png;
+
+// Cover
+Image x=42,y=78,width=148,height=142,fit=yes,aspect_ratio=no,path={musicCoverName}:symbols/nocover.png;
+
+// Status Info
+Text text={musicPlayStatus},x=42,y=270,height=20,width=148,size=12,align=center,red=255,green=248,blue=166;
+
+// Volume
+Text condition={volumeMute} == 0,text={\\008},x=470,y=485,width=30,height=30,size=18,align=left,red=187,green=129,blue=22;
+Text condition={volumeMute} <> 0,text={\\009},x=470,y=485,width=30,height=30,size=18,align=left,red=187,green=129,blue=22;
+Volumebar x=500,y=493,width=170,height=10,red=187,green=129,blue=22,permanent=yes,bg_red=50,bg_green=50,bg_blue=50,bg_alpha=255;
+
+// Track Info
+Text text={musicArtist},x=220,y=78,height=20,width=460,size=12,red=255,green=248,blue=166;
+Text text=Album:,x=220,y=105,height=20,width=60,size=12,red=255,green=248,blue=166;
+Text text={musicAlbum},x=285,y=105,height=20,width=395,size=12,red=255,green=248,blue=166;
+Text text=Genre:,x=220,y=130,height=20,width=60,size=12,red=255,green=248,blue=166;
+Text text={musicGenre},x=285,y=130,height=20,width=395,size=12,red=255,green=248,blue=166;
+Text text=Year:,x=220,y=155,height=20,width=60,size=12,red=255,green=248,blue=166;
+Text text={musicYear},x=285,y=155,height=20,width=395,size=12,red=255,green=248,blue=166;
+
+// Playlist
+Text text={musicCurrentTrack},x=42,y=354,height=22,width=635,size=14,red=255,green=248,blue=166;
+TextList text={musicTrack},x=42,y=376,height=90,width=635,size=12;
+
+// Show some Details (commented out, totally missplaced ; ) only to show how it works)
+//Text text=File: {musicFilename},x=245,y=500,height=20,width=450,size=12,red=255,green=248,blue=166;
+//Text text=Comment: {musicComment},x=245,y=525,height=20,width=450,size=12,red=255,green=248,blue=166;
+
+// Progressbar
+Text text={musicFrequence} kHz\,{musicBitrate} kbps\, {musicStereoMode},x=220,y=250,height=20,width=460,size=12;
+Text text=Track {musicIndex} of {musicCount},x=220,y=273,height=20,width=460,size=12;
+Text text={replayCurrent/%k:%M:%S},x=220,y=296,width=70,height=20,size=12;
+Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=300,y=300,width=300,height=10,red=187,green=230,blue=22,bg_red=20,bg_green=50,bg_blue=50,bg_alpha=255;
+Text text={replayTotal/%k:%M:%S},x=610,y=296,width=70,height=20,size=12;
+
+// Rating
+Text text=Rating:,x=220,y=200,height=20,width=60,size=12;
+Progressbar total=255,value={musicRating},x=285,y=205,width=110,height=10,path=symbols/music-rating.png,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255;
+
+// Delay beim SpectrumAnalyzer in ms!
+//SpectrumAnalyzer x=42,y=250,width=159,height=60,delay=0,red=52,green=162,blue=159,path=backgrounds/spectrum.png,fit=yes,aspect_ration=yes;
+//SpectrumAnalyzer x=295,y=480,width=160,height=40,delay=0,red=52,green=162,blue=159,path=backgrounds/spectrum.png,fit=yes,aspect_ration=yes;
+
+// Status Icons
+Text condition={musicShuffle} = 1,text={\\001},x=42,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicShuffle} <> 1,text={\\001},x=42,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+Text condition={musicLoop} = 1,text={\\002},x=77,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicLoop} <> 1,text={\\002},x=77,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+Text condition={musicRecording} = 1,text={\\003},x=112,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicRecording} <> 1,text={\\003},x=112,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+// Variable musicLyrics wird noch nicht unterstützt
+Text condition={musicLyrics} = 1,text={\\004},x=147,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicLyrics} <> 1,text={\\004},x=147,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+// Variable musicCopy wird noch nicht unterstützt
+Text condition={musicCopy} = 1,text={\\005},x=182,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicCopy} <> 1,text={\\005},x=182,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+Text condition={musicShutdown} = 1,text={\\006},x=217,y=485,width=30,height=30,size=20,align=center,red=255,green=0,blue=0;
+Text condition={musicShutdown} <> 1,text={\\006},x=217,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+// Variable musicTimer wird noch nicht unterstützt
+Text condition={musicTimer} = 1,text={\\007},x=252,y=485,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+Text condition={musicTimer} <> 1,text={\\007},x=252,y=485,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+// to be continued ...
+
+// Buttons Help
+Text text={musicButtonRed},x=40,y=547,height=20,width=130,size=12,red=255,green=248,blue=166,bg_alpha=0;
+Text text={musicButtonGreen},x=217,y=547,height=20,width=130,size=12,red=255,green=248,blue=166,bg_alpha=0;
+Text text={musicButtonYellow},x=400,y=547,height=20,width=130,size=12,red=255,green=248,blue=166,bg_alpha=0;
+Text text={musicButtonBlue},x=578,y=547,height=20,width=130,size=12,red=255,green=248,blue=166,bg_alpha=0;
+
+Include=OSD-Messages;
+
+#endif
+
+
+
+//***************************************************************************
+// Tracklist des Music Plugins
+//***************************************************************************
+[MenuMusicTrackList]
+
+Include=MenuCommon;
+
+Defaults menu_y=90,menu_height=425,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// Track Nr
+ColumnSelected number=1,width=80,focus=menu/focus-small.png;
+Column number=1,width=80;
+
+// Title
+ColumnSelected number=2,scroll=marquee,scroll_count=5,dots=1;
+Column number=2,dots=yes;
+
+
+
+//***************************************************************************
+// Standardwerte für alle Menüs
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+//***************************************************************************
+[MenuCommon]
+
+Defaults font=graphTFT,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+Background path=backgrounds/bg-menu-blue.png;
+
+MenuImageMap name=Schedule,file=menu/menuitems/schedule.png;
+MenuImageMap name=Channels,file=menu/menuitems/channels.png;
+MenuImageMap name=Timers,file=menu/menuitems/timers.png;
+MenuImageMap name=Timer...,file=menu/menuitems/timers.png;
+MenuImageMap name=Recordings,file=menu/menuitems/recordings.png;
+MenuImageMap name=ExtRecMenu,file=menu/menuitems/recordings.png;
+MenuImageMap name=Video,file=menu/menuitems/recordings.png;
+MenuImageMap name=Suche/Info...,file=menu/menuitems/info.png;
+MenuImageMap name=Infotools...,file=menu/menuitems/info.png;
+MenuImageMap name=System...,file=menu/menuitems/setup.png;
+MenuImageMap name=Setup,file=menu/menuitems/setup.png;
+MenuImageMap name=Minivdr-Setup,file=menu/menuitems/setup.png;
+MenuImageMap name=Commands,file=menu/menuitems/commands.png;
+MenuImageMap name=DVD,file=menu/menuitems/dvd.png;
+MenuImageMap name=VideoCD,file=menu/menuitems/vcd.png;
+MenuImageMap name=Disk abspielen,file=menu/menuitems/vdrcd.png;
+MenuImageMap name=DVDs erstellen,file=menu/menuitems/burn.png;
+MenuImageMap name=Create DVDs,file=menu/menuitems/burn.png;
+MenuImageMap name=DVD-Wechsler,file=menu/menuitems/dvd.png;
+MenuImageMap name=MP3,file=menu/menuitems/mp3.png;
+MenuImageMap name=MPlayer,file=menu/menuitems/mplayer.png;
+MenuImageMap name=Picture beside Picture,file=menu/menuitems/pbp.png;
+MenuImageMap name=Mldonkey,file=menu/menuitems/donkey.png;
+MenuImageMap name=Bilder,file=menu/menuitems/image.png;
+MenuImageMap name=Bilder...,file=menu/menuitems/image.png;
+MenuImageMap name=Postfach,file=menu/menuitems/Mail.png;
+MenuImageMap name=SysInfo,file=menu/menuitems/Sysinfo.png;
+MenuImageMap name=Signalinformationen,file=menu/menuitems/Signal.png;
+MenuImageMap name=Pilot,file=menu/menuitems/Pilot.png;
+MenuImageMap name=Graph-TFT,file=menu/menuitems/Pilot.png;
+MenuImageMap name=Konsolen,file=menu/menuitems/konsole.png;
+MenuImageMap name=Prefermenu,file=menu/menuitems/Prefermenu.png;
+MenuImageMap name=TV-OnScreen,file=menu/menuitems/tvonscreen.png;
+MenuImageMap name=Sleep-Timer,file=menu/menuitems/sleeptimer.png;
+MenuImageMap name=Wetter,file=menu/menuitems/Wetter.png;
+MenuImageMap name=Suche,file=menu/menuitems/suche.png;
+MenuImageMap name=Bild-in-Bild,file=menu/menuitems/osdpip.png;
+MenuImageMap name=Zeitleiste,file=menu/menuitems/timeline.png;
+MenuImageMap name=iPod,file=menu/menuitems/ipod.png;
+MenuImageMap name=Video/Audio...,file=menu/menuitems/recordings.png;
+MenuImageMap name=EPG...,file=menu/menuitems/yaepg.png;
+MenuImageMap name=Spiele...,file=menu/menuitems/gngb2vdr.png;
+MenuImageMap name=Sonstige Plugins...,file=menu/menuitems/content.png;
+MenuImageMap name=Videotext (OSD),file=menu/menuitems/osdteletext.png;
+MenuImageMap name=Fritz!Box Telefonbuch,file=menu/menuitems/vbox.png;
+MenuImageMap name=Aufnahmen wiederherstellen,file=menu/menuitems/stop.png;
+MenuImageMap name=VideoCD (/dev/dvd),file=menu/menuitems/vcd.png;
+MenuImageMap name=CD Player,file=menu/menuitems/vdrcd.png;
+
+Text text={menuTitle},x=70,y=15,width=420,height=40,lines=1,align=center,red=0,green=0,blue=0;
+
+Text text={time/%d.%m. %H:%M},x=520,y=39,height=30;
+
+MenuButtonBackgroundRed x=5,y=529,width=170,height=45,pathON=menu/button-red.png,bg_red=50,bg_green=84,bg_blue=121,on_click=Red;
+MenuButtonRed x=5,y=534,width=170,height=45,red=255,green=255,blue=255,alpha=255,align=center;
+MenuButtonBackgroundGreen x=180,y=529,width=170,height=45,pathON=menu/button-green.png,bg_red=50,bg_green=84,bg_blue=121,on_click=Green;
+MenuButtonGreen x=180,y=534,width=170,height=45,red=0,green=0,blue=0,alpha=255,align=center;
+MenuButtonBackgroundYellow x=360,y=529,width=170,height=45,pathON=menu/button-yellow.png,bg_red=50,bg_green=84,bg_blue=121,on_click=Yellow;
+MenuButtonYellow x=360,y=534,width=170,height=45,red=0,green=0,blue=0,alpha=255,align=center;
+MenuButtonBackgroundBlue x=540,y=529,width=170,height=45,pathON=menu/button-blue.png,bg_red=50,bg_green=84,bg_blue=121,on_click=Blue;
+MenuButtonBlue x=540,y=534,width=170,height=45,red=255,green=255,blue=255,alpha=255,align=center;
+
+PartingLine x=0,align=center,width=720,red=255,green=255,blue=255,path=menu/parting-line.png,path2=menu/parting-line-empty.png;
+
+SymRecording x=520,y=480,pathON=symbols/recOn.png;
+Recording x=601,y=485,width=120,height=38,lines=1,size=24,red=0,green=0,blue=0,bg_red=50,bg_green=84,bg_blue=121,scroll=marquee,scroll_count=5;
+
+#ifdef MOUSE_BUTTONS
+ var varHideColorButtons = 1;
+ var varHidePrevChannel = 1;
+ Include=MouseButtons;
+#endif
+
+// OSD Message
+Message x=10,y=483,width=480,height=35,size=19,red=0,green=0,blue=0,bg_x=2,bg_y=481,path=backgrounds/bg-message.png,delay=5;
+
+
+
+//***************************************************************************
+// Anzeige der Timer Daten
+//***************************************************************************
+[MenuTimers]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,menu_height=425,font=graphTFT,size=22,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=425,on_dblclick=Ok;
+
+// flag as text (or symbol via font)
+ColumnSelected number=1,width=65,red=255,green=248,blue=166,focus=menu/focus-small.png;
+Column number=1,width=65;
+
+// Channel Nr
+ColumnSelected number=2,width=50,red=255,green=248,blue=166;
+Column number=2,width=50,red=255,green=255,blue=255;
+
+//
+ColumnSelected number=3,width=120,red=255,green=248,blue=166;
+Column number=3,width=120,red=255,green=255,blue=255;
+
+// Start Time
+ColumnSelected number=4,width=100,red=255,green=248,blue=166;
+Column number=4,width=100;
+
+// End Time
+ColumnSelected number=5,width=100,red=255,green=248,blue=166;
+Column number=5,width=100,red=255,green=255,blue=255;
+
+// Event
+ColumnSelected number=6,red=255,green=248,blue=166,scroll=marquee,scroll_count=5,dots=1;
+Column number=6,red=255,green=255,blue=255,dots=yes;
+
+
+
+//***************************************************************************
+// Anzeige der Auswahlliste aller Plugins
+//***************************************************************************
+[MenuSetupPlugins]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,menu_height=400,font=graphTFT,size=22,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=400,on_dblclick=Ok;
+
+ColumnSelected number=0,red=255,green=248,blue=166,focus=menu/focus-small.png,scroll=off;
+Column number=0,red=255,green=255,blue=255;
+
+// ColumnSelected number=2,width=300,red=255,green=248,blue=166,scroll=off;
+// Column number=2,red=255,green=255,blue=255;
+
+
+
+//***************************************************************************
+// Menü zur Einstellung von Konfigurationsparametern
+//***************************************************************************
+[MenuSetupPage]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,menu_height=400,font=graphTFT,size=22,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=400,on_dblclick=Ok;
+
+ColumnSelected number=1,width=520,red=255,green=248,blue=166,focus=menu/focus-small.png,scroll=off;
+Column number=1,width=520,red=255,green=255,blue=255;
+
+ColumnSelected number=2,red=255,green=248,blue=166,scroll=off;
+Column number=2,red=255,green=255,blue=255;
+
+
+//***************************************************************************
+// Aufnahmemenü (VDR Standardversion)
+//***************************************************************************
+[MenuRecordings]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,menu_height=400,font=graphTFT,size=22,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=400,on_dblclick=Ok;
+
+// Date
+// conditon example
+// ColumnSelected condition={colCount} < 3,number=1,width=150,red=255,green=248,blue=166,focus=menu/focus-small.png;
+ColumnSelected number=1,width=150,red=255,green=248,blue=166,focus=menu/focus-small.png;
+Column number=1,width=150,red=255,green=255,blue=255;
+
+// Time
+ColumnSelected number=2,width=120,red=255,green=248,blue=166;
+Column number=2,width=120,red=255,green=255,blue=255;
+
+// length only if more than 3 columns
+ColumnSelected condition={colCount} > 3,number=3,width=120,red=255,green=248,blue=166;
+Column condition={colCount} > 3,number=3,width=120;
+
+// text
+ColumnSelected condition={colCount} > 3,number=4,red=255,green=248,blue=166,scroll=marquee,scroll_count=5,dots=1;
+Column condition={colCount} > 3,number=4,red=255,green=255,blue=255,dots=yes;
+
+ColumnSelected condition={colCount} = 3,number=3,red=255,green=248,blue=166,scroll=marquee,scroll_count=5,dots=1;
+Column condition={colCount} = 3,number=3,red=255,green=255,blue=255,dots=yes;
+
+
+
+//***************************************************************************
+// Aufnahmemenü (extRecMenu Plugin Version)
+//***************************************************************************
+[MenuExtRecordings]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,menu_height=400,font=graphTFT,size=22,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=400,on_dblclick=Ok;
+
+// flag (New,...) as text (or symbol via font)
+ColumnSelected number=1,width=40,red=255,green=248,blue=166,focus=menu/focus-small.png;
+Column number=1,width=40;
+
+// Date
+ColumnSelected number=2,width=150,red=255,green=248,blue=166;
+Column number=2,width=150,red=255,green=255,blue=255;
+
+// Time
+ColumnSelected number=3,width=105,red=255,green=248,blue=166;
+Column number=3,width=105,red=255,green=255,blue=255;
+
+// length
+ColumnSelected number=4,width=85,red=255,green=248,blue=166;
+Column number=4,width=85;
+
+// text
+ColumnSelected number=5,red=255,green=248,blue=166,scroll=marquee,scroll_count=5,dots=1;
+Column number=5,red=255,green=255,blue=255,dots=yes;
+
+
+
+//***************************************************************************
+// Kanalmenü (Menu->Kanäle)
+//***************************************************************************
+[MenuChannels]
+
+Include=MenuCommon;
+
+Defaults menu_y=90,menu_height=400,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=90,x=1,width=720,height=400,on_dblclick=Ok;
+
+// ChannelNumber
+ColumnSelected number=1,x=1,width=80,focus=menu/focus-small.png;
+Column number=1,x=1;
+
+// ChannelName
+ColumnSelected number=2,x=80;
+Column number=2,x=80;
+
+
+
+//***************************************************************************
+// Programmübersicht (Menu->Programme) (EPGSearch Plugin Version)
+//***************************************************************************
+[MenuEpgsSchedule]
+
+Include=MenuCommon;
+
+Defaults menu_y=170,menu_height=300,height=70,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=170,x=1,width=720,height=300,on_dblclick=Ok;
+
+// Senderlogo Logo oben links
+Image width=80,fit=yes,aspect_ratio=yes,x=10,y=-5,path=columnimages/{selectedRowEventChannelName/tologo}.png;
+
+// Info zum Event der selektierten Zeile über dem Menü
+
+// Start und Endezeit des gewählten Titels oben links
+Text text={selectedRowEventStartTime/%a %d.%m.},x=10,y=70,size=20,width=170,height=30,red=52,green=162,blue=159;
+Text text={selectedRowEventStartTime/%H:%M}-{selectedRowEventEndTime/%H:%M},x=10,y=110,size=20,width=170,height=30,red=52,green=162,blue=159;
+
+// EPG Detailinformationen des gewählten Titels im Kopfbereich anzeigen
+text text={selectedRowEventDescription},x=195,y=70,size=12,width=400,height=73,red=255,green=255,blue=255;
+Image x=613,y=70,width=97,height=73,path={varEPGimagesPath_1}{selectedRowEventID}.png:{varEPGimagesPath_2}{selectedRowEventID}.png:{varEPGimagesPath_3}{selectedRowEventID}.png:{varImagesPath_1}{selectedRowEventTitle}.jpg:menu/menuitems/logo.png,fit=yes,aspect_ratio=yes;
+
+//Alternativ: Gewählten Programmtitel und Subtitel im Kopfbereich anzeigen
+//text text={selectedRowEventTitle},x=195,y=70,width=450,height=40,red=255,green=255,blue=255;
+//text text={selectedRowEventSubtitle},x=195,y=110,size=18,width=450,height=30,lines=1,red=52,green=162,blue=159;
+
+Rectangle x=185,y=70,width=3,height=75,red=52,green=162,blue=159;
+Rectangle x=5,y=145,width=710,height=3,red=52,green=162,blue=159;
+
+
+//Das Menü...
+// Date
+EventColumnSelected text={rowEventStartTime/%a %d.%m. %H:%M},width=150,focus=menu/focus-big.png;
+EventColumn text={rowEventStartTime/%a %d.%m. %H:%M},width=150;
+
+// EPG Image
+EventColumnSelected type=image,bar_height=70%,fit=yes,aspect_ratio=yes,spaceing=10,width=80,spacing=10,path={varEPGimagesPath_1}{rowEventID}.png:{varEPGimagesPath_2}{rowEventID}.png:{varEPGimagesPath_3}{rowEventID}.png:{varImagesPath_1}{rowEventTitle}.jpg:menu/menuitems/logo.png;
+EventColumn type=image,bar_height=70%,fit=yes,aspect_ratio=yes,spaceing=10,width=80,spacing=10,path={varEPGimagesPath_1}{rowEventID}.png:{varEPGimagesPath_2}{rowEventID}.png:{varEPGimagesPath_3}{rowEventID}.png:{varImagesPath_1}{rowEventTitle}.jpg:menu/menuitems/logo.png;
+
+
+//////////////////
+// Symbol Column Beginn - flag (V,T,t,...) as text (or symbol via font)
+////
+// Partial Timer Before Record; Char 149 = Half Clock 1
+EventColumnSelected x=230,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+EventColumn x=230,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+
+// Timer & not Recording; Char 253 = Clock-Symbol
+EventColumnSelected x=230,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+EventColumn x=230,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+
+// Partial Timer After Record; Char 148 = Half Clock 2
+EventColumnSelected x=230,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+EventColumn x=230,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+
+// Recording Timer; Char 249 = REC Symbol
+EventColumnSelected x=230,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+EventColumn x=230,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+
+// Running Event; Char 251 = Runningman-Symbol
+EventColumnSelected x=230,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+EventColumn x=230,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+////
+// Symbol Column End
+////////////////////
+
+// event
+EventColumnSelected text={rowEventTitle},scroll=off,dots=1,line=1;
+EventColumn text={rowEventTitle},line=1,dots=yes;
+EventColumnSelected text={rowEventSubTitle},line=2,size=20,dots=yes,red=52,green=162,blue=159;
+EventColumn text={rowEventSubTitle},line=2,size=20,dots=yes,red=52,green=162,blue=159;
+
+
+
+//***************************************************************************
+// Programmübersicht (Menu->Programme) (VDR Standard Version)
+//***************************************************************************
+[MenuSchedule]
+
+Include=MenuEpgsSchedule;
+
+
+
+//***************************************************************************
+// Anzeige von Senderlogo und Startzeit
+// Dieser Abschnitt wird in anderen ...WhatsOn...-Sektionen eingebunden (include)
+//***************************************************************************
+[MenuEpgsWhatsOn_logo_time]
+
+Include=MenuCommon;
+
+Defaults menu_y=70,height=66,menu_height=400,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=70,x=1,width=720,height=400,on_dblclick=Ok;
+
+// Senderlogo Logo oben links
+Image width=80,height=70,fit=yes,aspect_ratio=yes,x=10,y=-5,path=columnimages/{selectedRowEventChannelName/tologo}.png;
+
+// ch-logo
+EventColumnSelected type=image,width=60,fit=yes,aspect_ratio=yes,bar_height=90%,spacing=10,path=columnimages/{rowEventChannelName/tologo}.png,focus=menu/focus-big.png;
+EventColumn type=image,width=60,fit=yes,aspect_ratio=yes,bar_height=90%,spacing=10,path=columnimages/{rowEventChannelName/tologo}.png;
+
+// time
+EventColumnSelected text={rowEventStartTime/%H:%M},align_v=1,width=100;
+EventColumn text={rowEventStartTime/%H:%M},align_v=1,width=100;
+
+
+
+//***************************************************************************
+// Anzeige der Event Daten (aktuelles Programm)
+// Dieser Abschnitt wird in anderen ...WhatsOn...-Sektionen eingebunden (include)
+//***************************************************************************
+[MenuEpgsWhatsOn_Event]
+
+Defaults menu_y=70,height=66,menu_height=400,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+EventColumnSelected text={rowEventTitle},line=1,scroll=marquee,scroll_count=5,dots=1;
+EventColumn text={rowEventTitle},line=1,dots=yes;
+
+EventColumnSelected text={rowEventSubTitle},line=2,size=20,scroll=marquee,scroll_count=5,dots=1,red=52,green=162,blue=159;
+EventColumn text={rowEventSubTitle},line=2,size=20,dots=yes,red=52,green=162,blue=159;
+
+
+
+//***************************************************************************
+// Anzeige eingerichteter persönlicher Zeitabschnitte in EPGSearch
+//***************************************************************************
+[MenuEpgsWhatsOnElse]
+
+Include=MenuEpgsWhatsOn_logo_time;
+
+Defaults menu_y=70,height=66,menu_height=400,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+//////////////////
+// Symbol Column Beginn - flag (V,T,t,...) as text (or symbol via font)
+////
+// Partial Timer Before Record; Char 149 = Half Clock 1
+EventColumnSelected x=170,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+EventColumn x=170,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+
+// Timer & not Recording; Char 253 = Clock-Symbol
+EventColumnSelected x=170,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+EventColumn x=170,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+
+// Partial Timer After Record; Char 148 = Half Clock 2
+EventColumnSelected x=170,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+EventColumn x=170,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+
+// Recording Timer; Char 249 = REC Symbol
+EventColumnSelected x=170,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+EventColumn x=170,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+
+// Running Event; Char 251 = Runningman-Symbol
+EventColumnSelected x=170,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+EventColumn x=170,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+////
+// Symbol Column End
+////////////////////
+
+Include=MenuEpgsWhatsOn_Event;
+
+
+
+//***************************************************************************
+// Was läuft JETZT (Menu->Programme-Jetzt) (EPGSearch Plugin Version)
+//***************************************************************************
+[MenuEpgsWhatsOnNow]
+
+Include=MenuEpgsWhatsOn_logo_time;
+
+Defaults menu_y=70,height=66,menu_height=400,font=graphTFT,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// Fortschrittsanzeige (progress)
+EventColumnSelected type=progress,width=70,spacing=10,bar_height=40%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+EventColumn type=progress,width=70,spacing=10,bar_height=40%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+
+//////////////////
+// Symbol Column Beginn - flag (V,T,t,...) as text (or symbol via font)
+////
+// Partial Timer Before Record; Char 149 = Half Clock 1
+EventColumnSelected x=250,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+EventColumn x=250,condition={rowEventHasPartialTimerBefore} == 1,text={\\149},width=40,align_v=1;
+
+// Timer & not Recording; Char 253 = Clock-Symbol
+EventColumnSelected x=250,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+EventColumn x=250,condition={rowEventHasTimer} == 1 && {rowEventIsRecording} == 0,text={\\253},width=40,align_v=1;
+
+// Partial Timer After Record; Char 148 = Half Clock 2
+EventColumnSelected x=250,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+EventColumn x=250,condition={rowEventHasPartialTimerAfter} == 1,text={\\148},width=40,align_v=1;
+
+// Recording Timer; Char 249 = REC Symbol
+EventColumnSelected x=250,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+EventColumn x=250,condition={rowEventIsRecording} == 1,text={\\249},width=40,align_v=1;
+
+// Running Event; Char 251 = Runningman-Symbol
+EventColumnSelected x=250,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+EventColumn x=250,condition={rowEventIsRunning} == 1,text={\\251},width=40,align_v=1;
+////
+// Symbol Column End
+////////////////////
+
+Include=MenuEpgsWhatsOn_Event;
+
+
+
+//***************************************************************************
+// Was läuft Jetzt (Menu->Programme-Jetzt) (VDR Standard Version)
+//***************************************************************************
+[MenuWhatsOnNow]
+Include=MenuEpgsWhatsOnNow;
+
+
+
+//***************************************************************************
+// Was läuft als NÄCHSTES (Menu->Programme-Nächste) (EPGSearch Plugin Version)
+//***************************************************************************
+[MenuEpgsWhatsOnNext]
+Include=MenuEpgsWhatsOnElse;
+
+
+
+//***************************************************************************
+// Was läuft als NÄCHSTES (Menu->Programme-Nächste) (VDR Standard Version)
+//***************************************************************************
+[MenuWhatsOnNext]
+Include=MenuEpgsWhatsOnElse;
+
+
+
+//***************************************************************************
+// Anzeige ArghDirector (benötigt ArghDirector Plugin)
+//***************************************************************************
+[MenuArghDirector]
+
+Include=MenuCommon;
+
+Defaults menu_y=90,menu_height=400,font=graphTFT,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=90,x=1,width=720,height=400,on_dblclick=Ok;
+
+// Overview - View
+// event
+ColumnSelected condition={colCount} = 1,number=1,focus=menu/focus-small.png;
+Column condition={colCount} = 1,number=1;
+
+// Detail - View (with times, ...)
+// event/ch-name
+ColumnSelected condition={colCount} > 1,number=1,width=350,focus=menu/focus-small.png;
+Column condition={colCount} > 1,number=1,width=350;
+
+// flag (V,T,t,...) as text (or symbol via font) ColumnSelected number=2,width=40;
+Column number=2,width=40;
+
+// progress graphical with image
+ColumnSelected number=3,type=progress,width=70,spacing=10,bar_height=50%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+Column number=3,type=progress,width=70,spacing=10,bar_height=50%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+
+// time
+ColumnSelected number=4;
+Column number=4;
+
+
+
+//***************************************************************************
+// Detailinfos des Programms in der Programmübersicht
+//***************************************************************************
+[MenuEvent]
+
+Include=MenuCommon;
+
+Defaults font=graphTFT,size=24,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=75,x=1,width=720,height=400,on_dblclick=Ok;
+
+// Image x=15,y=15,width=80,height=50,fit=yes,aspect_ratio=yes,path=columnimages/{presentChannelName/tologo}.png;
+Text text={eventStartTime/%a %d.%m},x=10,y=90,size=20,width=170,height=30;
+Text text={eventStartTime/%H:%M},x=10,y=130,size=20,width=80,height=30;
+Text text=-{eventEndTime/%H:%M},x=90,y=130,size=20,width=90,height=30;
+
+Rectangle x=185,y=70,width=3,height=115;
+
+text text={eventTitle},x=195,y=80,width=500,height=40,red=255,green=255,blue=255;
+text text={eventSubtitle},x=195,y=120,size=18,width=350,height=65,lines=2;
+Image x=540,y=75,width=190,height=105,path={varEPGimagesPath_1}{eventID}.png:{varEPGimagesPath_2}{eventID}.png:{varEPGimagesPath_3}{eventID}.png:{varImagesPath_1}{eventTitle}.jpg,fit=yes,aspect_ratio=yes;
+
+Rectangle x=5,y=185,width=710,height=3;
+
+MenuText x=10,y=200,width=680,height=270,size=20,red=255,blue=255,green=255;
+
+
+//***************************************************************************
+// Detailinfos der Aufnahmen
+//***************************************************************************
+[MenuRecording]
+
+Include=MenuCommon;
+
+Defaults font=graphTFT,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=75,x=1,width=720,height=400,on_dblclick=Ok;
+
+Text text={recordingTime/%a %d.%m},x=10,y=90,size=20,width=140,height=30;
+Text text={recordingTime/%H:%M},x=10,y=130,size=20,width=80,height=30;
+
+Rectangle x=185,y=70,width=3,height=115;
+
+Text text={recordingTitle},x=195,y=80,width=500,height=40,red=255,green=255,blue=255;
+Text text={recordingSubtitle},x=195,y=120,size=18,width=350,height=65,lines=2;
+Image x=540,y=75,width=190,height=105,path={recordingPath}/thumbnail.png:{varEPGimagesPath_1}{recordingEventID}.png:{varEPGimagesPath_2}{recordingEventID}.png:{varEPGimagesPath_3}{recordingEventID}.png:{varImagesPath_1}{recordingTitle}.jpg,fit=yes,aspect_ratio=yes;
+
+Rectangle x=5,y=185,width=710,height=3;
+
+MenuText x=10,y=200,width=680,height=270,size=20,red=255,blue=255,green=255;
+
+
+
+//***************************************************************************
+// Detailinfos der Aufnahmen (extRecMenu Plugin Version)
+//***************************************************************************
+[MenuExtRecording]
+
+Include=MenuRecording;
+Defaults font=graphTFT,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+
+
+//***************************************************************************
+// Anzeige Standardmenü
+//***************************************************************************
+[Menu]
+
+Include=MenuCommon;
+
+Defaults font=graphTFT,size=24,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+// define the MenuNavigationArea
+MenuNavigationArea y=75,x=1,width=720,height=400,on_dblclick=Ok;
+
+MenuText x=10,y=70,width=680,height=400,alpha=255,red=255,blue=255,green=255;
+
+// display menu icon in the upper left corner
+MenuSelected x=0,y=70,width=720,height=400,stat_pic=yes,stat_x=1,stat_y=1,
+ stat_width=60,stat_height=60,alpha=255,red=255,green=248,blue=166,focus=menu/focus-small.png;
+
+// OR display menu icon in the current menu line
+// MenuSelected stat_pic=yes,stat_x=600,stat_width=40,stat_height=40,alpha=255,
+ red=255,green=248,blue=166,focus=menu/focus-small.png;
+
+Menu x=0,y=70,width=720,height=400,alpha=255,red=255,green=255,blue=255;
+
+//***************************************************************************
+// Noch nicht weiter definierter Menüeintrag
+//***************************************************************************
+
+[MenuCommands]
+Include=Menu;
+
+[MenuMain]
+Include=Menu;
+
+//***************************************************************************
+// Anzeige bei nicht bekannten Menüstrukturen
+//***************************************************************************
+[MenuUnknown]
+Include=Menu;
diff --git a/themes/PearlHD.theme b/themes/PearlHD.theme
new file mode 100644
index 0000000..26a1d7c
--- /dev/null
+++ b/themes/PearlHD.theme
@@ -0,0 +1,1137 @@
+
+// GraphTFT-theme PearlHD by mapovi
+// Used VDRSymbolsSans Font and VDRSymbolsSans >= 0.3.0
+// This theme works best on 800x600 displays
+
+#define MOUSE_BUTTONS
+
+
+// mp3/music Plugin Anzeigeauswahl
+//
+// Entferne die Doppelslashes bei der #define Zeile für die gewünschte Varinate
+// zur Anzeige der mp3/music Plugin Informationen
+// (immer nur eine Ziele einkommentieren)
+// MP3_PLUGIN - Einfache Ausgabe für das mp3 und music Plugin
+// MUSIC_PLUGIN - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in AvP Style
+// MUSIC_PLUGIN_MORONE_STYLE - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in Morone Style
+
+//#define MP3_PLUGIN
+#define MUSIC_PLUGIN
+//#define MUSIC_PLUGIN_MORONE_STYLE
+
+// Informationen zum Theme und der benötigten VDRSymbolsSans version
+var varThemeName = "PearlHD"; // Name des Themes
+var varThemeVersion = "0.3.1"; // Version des Themes
+var varSyntaxVersion = "0.4.1"; //Benötigte VDRSymbolsSans-Engine Version
+
+// Suchpfade für die EPG Images
+var varEPGimagesPath_1 = "/ramdisk/epgimages/";
+var varEPGimagesPath_2 = "/media/epgimages/";
+var varEPGimagesPath_3 = "/video0/epgimages/";
+var varEPGimagesPath_4 = "/var/cache/vdr/epgimages/";
+var varImagesPath_1 = "/video0/images/";
+
+// Initialisierung und Default Vorbelegeung für Mouse Variablen
+var varSwitchMouseNumber = 0;//Normaler Zehnerblock
+var varHideColorButtons = 0; //Farbtasten anzeigen
+var varHidePrevChannel = 0; //PrevChannel Taste anzeigen
+var varTouchMenu = 0; //Vorbelegung für Mausmenüanzeige
+
+// Abstand von der Mitte des Kalibartionscursors zum oberen und linken Bildschirmrand
+var calibrationFrameOffset = 50;
+
+
+[Theme] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Theme name={varThemeName} {varThemeVersion},dir=PearlHD,
+ fontPath=./fonts:../../fonts,width=800,height=600,
+ themeVersion={varThemeVersion},
+ syntaxVersion={varSyntaxVersion},
+ startImage=backgrounds/start.png,
+ endImage=backgrounds/ende.png;
+
+
+[RecSymbol] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Image condition={actRecordingCount} > 0,x=690,y=100,path=symbols/recOn.png;
+ Image condition={actRecordingCount} < 1,x=690,y=100,path=symbols/recOff.png;
+
+
+[Volume] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //Volumebar size=14,red=57, green=120, blue=159,x=50,y=562,width=700,height=48,bg_alpha=128,alpha=0,permanent=no,delay=4;
+ Volumebar text=percent,red=57, green=120, blue=159,size=27,x=100,y=450,width=100,height=100,bg_alpha=0,alpha=0,permanent=no,delay=3;
+ Volumebar x=50,y=550,width=700,height=40,switch=yes,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,permanent=no,delay=3;
+ VolumeMuteSymbol x=0,y=550,pathON=symbols/mute_on_avp.png,pathOFF=symbols/mute_off_avp.png,permanent=no,delay=4;
+
+[OSD-Messages] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //Message x=135,y=173,width=530,height=280,size=44,lines=4,dots=yes,red=0,green=0,blue=255,bg_x=0,bg_y=0,path=backgrounds/bg-message.jpg,delay=5;
+ Message font=VDRSymbolsSans,x=135,y=173,width=530,height=280,size=44,lines=4,dots=yes,red=255,green=255,blue=255,bg_x=0,bg_y=0,path=backgrounds/bg-message.jpg,delay=5;
+
+
+[StandardHeader] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ Defaults font=VDRSymbolsSans,size=27,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Text x=75,y=88,size=30,dots=yes,text={presentChannelNumber}. {presentChannelName},align=left,width=395,height=53,lines=1;
+
+ Image condition={channelHasMultilang} = 0,x=490,y=100,path=symbols/ch2Off.png;
+ Image condition={channelHasMultilang} = 1,x=490,y=100,path=symbols/ch2On.png;
+
+ Image condition={channelHasDD} = 0,x=540,y=100,path=symbols/ddOff.png;
+ Image condition={channelHasDD} = 1,x=540,y=100,path=symbols/ddOn.png;
+
+ Image condition={channelIsEncrypted} = 0,x=640,y=100,path=symbols/cryptOff.png;
+ Image condition={channelIsEncrypted} = 1,x=640,y=100,path=symbols/cryptOn.png;
+
+//Image condition={actRecordingCount} = 0,x=675,y=100,path=symbols/recOff.png;
+// Image condition={actRecordingCount} > 0,x=675,y=100,path=symbols/recOn.png;
+
+ Include=RecSymbol;
+
+
+[StandardFooter] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=20,red=175,green=175,blue=175,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Image x=650,y=460,width=75,height=75,fit=yes,bg_alpha=100,aspect_ratio=yes,path=/var/lib/vdr/channellogos/{presentChannelName}.png,on_click=Menu;
+ Text text={time/%H:%M} Uhr,x=75,y=470,width=300,height=100,red=255,green=255,blue=255,size=34;
+ Text text={time/%A. %d.%m.},x=335,y=487,width=300,height=100,size=22;
+
+
+[StandardProgress] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Timebar x=50,y=150,width=700,height=20,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=255;
+
+
+// Ausgabe eines Bildes oder, mittels Script, einer Bilderserie auf dem Display
+// Diese Funktion wird über das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Dia" aufgerufen
+//Ein Bild anzeigen. Pfad und Dateiname unten angeben (Beispiel: /tmp/test.png)
+//Das Item ImageFile ermöglicht, im Gegensatz zum Image Item, das Update der Anzeige über svdrpsend REFRESH
+//Damit lässt sich z.B. eine Diashow einrichten. Siehe dazu das Beispielscript dia.sh im VDRSymbolsSans Sourcepaket.
+[NormalDia] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ImageFile x=0,y=0,width=800,height=600,path=/tmp/dia.file,path2=backgrounds/alien.png,fit=yes,aspect_ratio=yes;
+ Include=OSD-Messages;
+
+
+// Diese Funktion wird über das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Sysinfo" aufgerufen
+[NormalSysinfo] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-clock.jpg;
+ Text text=CPU load,x=75,y=180,height=50;
+ Sysinfo type=cpuload,text=percent,align=center,size=18,x=240,y=185,width=500,height=30,bg_x=240,delay=3,switch=no,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=90;
+ Text text=CPU idle,x=75,y=230,height=50;
+ Sysinfo type=cpuidle,text=percent,align=center,size=18,x=240,y=235,width=500,height=30,bg_x=240,delay=3,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=90;
+ Text text=MEM used,x=75,y=330,height=30;
+ Sysinfo type=memused,factor=1048576,text=value,align=center,unit=MB,size=18,x=240,y=335,width=500,height=30,bg_x=240,delay=3,switch=no,red=57,green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=90;
+ Text text=HDD video,x=75,y=430,height=30;
+ Sysinfo type=disk,reference=/video?,factor=1073741824,align=center,text=value,unit=GB,size=18,x=240,y=435,width=500,height=30,bg_x=240,delay=3,switch=no,red=57,green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=90;
+ Text text=HDD system,x=75,y=480,height=30;
+ Sysinfo type=disk,reference=/,factor=1073741824,align=center,text=value,unit=GB,size=18,x=240,y=485,width=500,height=30,bg_x=240,delay=3,switch=no,red=57,green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,bg_alpha=90;
+
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ #endif
+
+ Include=Volume;
+ Include=OSD-Messages;
+ Include=StandardHeader;
+
+
+// Ausgabe der Programminformationen mit einer Uhr als zentraler Bestandteil der Anzeige
+// Diese Funktion wird über das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Clock" aufgerufen
+[NormalClock] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-clock.jpg;
+ Text text={time/%A. %d. %b.%G},x=75,y=465,width=600,height=100,size=27,red=175,green=175,blue=175;
+ Text text={time/%H:%M},x=75,y=175,width=700,align=left,height=220,size=165;
+ //Aktuelle Sendung
+ //Text text={presentStartTime/%H:%M},x=90,y=75,width=175,height=70,size=44;
+ //Text text={presentTitle} > {followingTitle},x=90,y=75,height=140,width=617,size=27,red=180,green=180,blue=180,lines=1,on_click=Schedule Ok;
+ //Text text={presentTitle} ,x=90,y=75,height=140,width=617,size=27,red=255,green=255,blue=255;
+ //Nächste Sendung
+ //Text text={followingStartTime/%H:%M},x=5,y=465,width=150,height=70,size=38;
+ //Text text={followingTitle},x=180,y=465,height=120,width=617,size=38,red=180,green=180,blue=180,lines=2,on_click=Schedule Down Ok;
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ #endif
+ Include=Volume;
+ Include=OSD-Messages;
+ Include=StandardHeader;
+ Include=StandardProgress;
+// Detailierte Ausgabe der EPG-Programminformationen und EPG-Bild
+// Diese Funktion wird über das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Detail" aufgerufen
+[NormalDetail] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ var varStartLine = 0;
+ var varEventId = 0;
+ Defaults font=VDRSymbolsSans,size=27,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-tv.jpg;
+
+ Text text={presentTitle},x=75,y=175,width=600,height=80,size=37,lines=1,dots=yes;
+ Text condition="{presentSubtitle}" != "",text={presentSubtitle},x=75,y=242,width=600,height=20,size=20,lines=1,dots=yes;
+ Text condition="{presentSubtitle}",text={presentStartTime/%H:%M} - {followingStartTime/%H:%M},x=75,y=470,width=600,height=45,size=20;
+ Text condition="{presentSubtitle}" = "",text={presentStartTime/%H:%M} - {followingStartTime/%H:%M},x=75,y=425,width=600,height=45,size=20;
+ Text condition="{presentSubtitle}",text={presentDuration/%M}',x=75,y=506,width=75,height=20,size=16; //Dauer der Sendung - falls ermittelbar
+ Text condition="{presentSubtitle}" = "",text={presentDuration/%M}',x=75,y=461,width=75,height=20,size=16; //Dauer der Sendung - falls ermittelbar
+ Text condition="{presentSubtitle}" != "",text={presentDescription},start_line={varStartLine},x=310,y=290,width=430,height=230,size=16,dots=yes, whipe_res=20,on_up=varStartLine--,on_down=varStartLine++;
+Text condition="{presentSubtitle}" = "",text={presentDescription},start_line={varStartLine},x=300,y=250,width=430,height=280,size=16,dots=yes, whipe_res=20,on_up=varStartLine--,on_down=varStartLine++;
+ Rectangle condition="{presentSubtitle}", x=75,y=295,width=200,height=160;
+ Rectangle condition="{presentSubtitle}" = "", x=75,y=255,width=200,height=160;
+ Image condition="{presentSubtitle}",x=75,y=295,width=200,height=160,bg_width=200,
+ path=
+ {varEPGimagesPath_1}{presentID}_(0-9).png:
+ {varEPGimagesPath_2}{presentID}_(0-9).png:
+ {varEPGimagesPath_3}{presentID}_(0-9).png:
+ {varEPGimagesPath_4}{presentID]_(0-9).png:
+ {varImagesPath_1}{presentTitle}.jpg:
+ backgrounds/nopic.png,
+ fit=yes,aspect_ratio=yes,delay=5,
+ on_dblclick=varEventId:0:1;
+ Image condition="{presentSubtitle}" = "",x=75,y=255,width=200,height=160,bg_width=200,
+ path=
+ {varEPGimagesPath_1}{presentID}_(0-9).png:
+ {varEPGimagesPath_2}{presentID}_(0-9).png:
+ {varEPGimagesPath_3}{presentID}_(0-9).png:
+ {varEPGimagesPath_4}{presentID}_(0-9).png:
+ {varImagesPath_1}{presentTitle}.jpg:
+ backgrounds/nopic.png,
+ fit=yes,aspect_ratio=yes,delay=5,
+ on_dblclick=varEventId:0:1;
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ #endif
+ Include=Volume;
+ Include=RecSymbol;
+ Include=OSD-Messages;
+ Include=StandardHeader;
+ Include=StandardProgress;
+
+
+// Allgemeine Einstellungen zur Ausgabe der TV und RADIO Informationen
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+// Diese Funktion wird über das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Standard" aufgerufen
+
+[TV_Radio_Common] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ Defaults font=VDRSymbolsSans,size=27,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-tv.png;
+
+ Timebar x=50,y=150,width=700,height=20,bg_x=50,bg_y=150,bg_width=700,bg_height=20,bg_red=160, bg_green=196, bg_blue=218,red=57, green=120, blue=159,bg_alpha=255,alpha=255;
+
+ Text text={presentStartTime/%H:%M},x=90,y=193,width=130,height=30,size=22,red=255,green=255,blue=255;
+ Text text={presentTitle},x=223,y=183,width=460,height=50,size=30,lines=1,red=255,green=255,blue=255,scroll=marquee,scroll_count=5,dots=yes,on_click=Schedule Ok;
+ Text text={presentSubtitle},x=223,y=237,width=460,height=100,size=22,lines=2,red=175,green=175,blue=175,dots=yes;
+
+ //Text condition="{presentDuration/%M}" != "",text={presentDuration/%M}',x=5,y=150,width=200,height=50,size=27,align=left; //Dauer der Sendung - falls ermittelbar
+
+ //Image x=5,y=185,width=130,height=100,path={varEPGimagesPath_1}{presentID}.png
+ // :{varEPGimagesPath_2}{presentID}.png
+ // :{varEPGimagesPath_3}{presentID}.png
+ // :{varImagesPath_1}{presentTitle}.jpg,
+ // fit=yes,aspect_ratio=yes; //EPG Bild
+
+ Text text={followingStartTime/%H:%M},x=90,y=330,width=130,height=45,size=22,red=255,green=255,blue=255;
+ Text text={followingTitle},x=223,y=320,width=460,height=50,size=30,lines=2,red=255,green=255,blue=255,scroll=marquee,scroll_count=5,dots=yes,Text text={presentTitle},x=150,y=105,width=650,height=107,size=34,lines=2,red=255,green=255,blue=255,dots=yes,on_click=Schedule Down Ok;
+ Text text={followingSubtitle},x=223,y=374,width=460,height=100,size=22,lines=2,red=175,green=175,blue=175,dots=yes;
+ //Text condition="{followingDuration/%M}" != "",text={followingDuration/%M}',x=5,y=357,width=130,height=35,size=24,align=center; //Dauer der Sendung - falls ermittelbar
+ //Image x=5,y=392,width=130,height=100,
+ // path={varEPGimagesPath_1}{followingID}.png
+ // :{varEPGimagesPath_2}{followingID}.png
+ // :{varEPGimagesPath_3}{followingID}.png
+ // :{varImagesPath_1}{followingTitle}.jpg,fit=yes,aspect_ratio=yes;
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ //Image x=754,y=510,width=40,height=40,bg_width=40,delay=150,path=menu/mouse_btn/btn_xbmc_white.png,on_click=User2;
+ #endif
+ Include=Volume;
+ Include=StandardHeader;
+ Include=StandardFooter;
+ Include=OSD-Messages;
+
+
+// Ergänzende Einstellungen zur Ausgabe der TV Informationen
+[NormalTV] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=TV_Radio_Common;
+ //Image x=153,y=545,width=45,height=25,path=symbols/tv.png;
+ Image condition={channelHasVtx} = 1,x=590,y=100,path=symbols/vtxOn.png;
+ Image condition={channelHasVtx} = 0,x=590,y=100,path=symbols/vtxOff.png;
+
+
+// Ergänzende Einstellungen zur Ausgabe der RADIO Informationen
+[NormalRadio] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=TV_Radio_Common;
+ Image x=153,y=545,width=45,height=25,path=symbols/radio.png;
+
+// Diese Funktion wird <FC>ber das VDRSymbolsSans OSD-Menu oder "svdrpsend.pl plug graphtft VIEW NonLiveTv" aufgerufen
+[NormalNonLiveTv] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-clock.jpg;
+ Defaults font=VDRSymbolsSans,size=27,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Text x=75,y=88,size=30,dots=yes,text=Timer,align=left,width=395,height=53,lines=1;
+
+ Image condition={channelHasMultilang} = 0,x=490,y=100,path=symbols/ch2Off.png;
+ Image condition={channelHasMultilang} = 1,x=490,y=100,path=symbols/ch2On.png;
+
+ Image condition={channelHasDD} = 0,x=540,y=100,path=symbols/ddOff.png;
+ Image condition={channelHasDD} = 1,x=540,y=100,path=symbols/ddOn.png;
+
+ Image condition={channelIsEncrypted} = 0,x=640,y=100,path=symbols/cryptOff.png;
+ Image condition={channelIsEncrypted} = 1,x=640,y=100,path=symbols/cryptOn.png;
+
+ Image condition={actRecordingCount} = 0,x=675,y=100,path=symbols/recOff.png;
+ Image condition={actRecordingCount} > 0,x=675,y=100,path=symbols/recOn.png;
+
+ Include=RecSymbol;
+
+ //Timer Listen
+ TextList condition={actTimersRunning} = 1,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M} {actTimersTitle},
+ x=70,y=180,height=200,width=680,size=18;red=200,green=200,blue=200,bg_alpha=2;
+ TextList condition={actTimersRunning} = 0,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M} {actTimersTitle},
+ x=70,y=400,height=120,width=680,size=18,bg_alpha=2;
+
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ #endif
+
+ Include=Volume;
+ Include=OSD-Messages;
+ //Include=StandardHeader;
+
+
+
+// Allgemeine Einstellungen für kommende Wiedergabe Abschnitte (Replay)
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+[ReplayCommon] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=28,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-replay.jpg;
+ Image condition={replayForward} = 1 & {replaySpeed} = -1 & {replayPlay} < 1,x=650,y=460,width=75,height=75,path=symbols/pause.png,delay=1;
+ Image condition={replayForward} = 1 & {replaySpeed} = -1 & {replayPlay} = 1,x=650,y=460,width=75,height=75,path=symbols/play.png,delay=1;
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} < 1,x=650,y=460,width=75,height=75,path=symbols/slowForward.png,delay=1;
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} = 1,x=650,y=460,width=75,height=75,path=symbols/fastForward.png,delay=1;
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} < 1,x=650,y=460,width=75,height=75,path=symbols/slowRewind.png,delay=1;
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} = 1,x=650,y=460,width=75,height=75,path=symbols/fastRewind.png,delay=1;
+ Image condition={replaySpeed} > 1,x=99,y=483,path=symbols/{replaySpeed}.png,delay=1;
+
+ Text text={time/%H:%M} Uhr,x=75,y=470,width=300,height=100,red=255,green=255,blue=255,size=34;
+ Text text={time/%A. %d.%m.},x=335,y=487,width=300,height=100,size=22,red=175,green=175,blue=175;
+ //Laufzeiten links und rechts unten
+ Text text={replayCurrent/%k:%M:%S} von{replayTotal/%k:%M:%S},x=65,y=88,size=30,width=600,height=40,delay=1;
+
+ Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=50,y=150,width=700,height=20,bg_x=50,bg_y=150,bg_width=700,bg_height=20,bg_alpha=255,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,delay=3;
+ Include=Volume;
+ Include=OSD-Messages;
+
+
+// Anzeige der Informationen bei der Wiedergabe von Aufnahmen und media Dateien
+[ReplayNormal] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=ReplayCommon;
+ Include=RecSymbol;
+ Defaults font=VDRSymbolsSans,size=28,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+ Text text={replayTitle},x=90,y=175,width=600,height=80,size=37,lines=1,red=255,green=255,blue=255,dots=yes;
+ Text text={replaySubtitle},x=90,y=242,width=600,height=20,size=20,lines=1;
+ Image x=235,y=260,width=200,height=160,path={replayPath}/thumbnail.png:{varEPGimagesPath_1}{replayEventID}.png:{varEPGimagesPath_2}{replayEventID}.png:{varEPGimagesPath_3}{replayEventID}.png:{varImagesPath_1}{replayTitle}.jpg:{replayPath}/Cover-Enigma.jpg:symbols/video.png,fit=yes,aspect_ratio=no;
+ #ifdef MOUSE_BUTTONS
+ var varSwitchMouseNumber = 1;
+ Include=MouseButtons;
+ #endif
+
+
+// Anzeige der Informationen bei der Wiedergabe von DVDs
+[ReplayDVD] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=ReplayCommon;
+ Defaults font=VDRSymbolsSans,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Image x=297,y=5,path=symbols/dvd-header.png;
+ Image x=0,y=220,path=backgrounds/filmspule.png;
+
+ Text text={replayTitle},x=5,y=105,size=44,height=66,width=790,align=center,lines=1,red=255,green=255,blue=255;
+ Image x=235,y=260,width=330,height=247,path={varImagesPath_1}{replayTitle}.jpg:symbols/dvd.png,fit=yes,aspect_ratio=yes;
+ #ifdef MOUSE_BUTTONS
+ var varSwitchMouseNumber = 2;
+ Include=MouseButtons;
+ #endif
+
+
+// Anzeige der Informationen bei der Wiedergabe von mp3 Dateien
+// über das mp3 oder music Plugin.
+// Hier werden drei Versionen angeboten, die am Anfang dieses Themes
+// ausgewählt werden können.
+// MP3_PLUGIN - Einfache Ausgabe für das mp3 und music Plugin
+// MUSIC_PLUGIN - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in AvP Style
+// MUSIC_PLUGIN_MORONE_STYLE - Erweiterete Informationen für das music Plugin ab Version 0.4.0 in Morone Style
+[ReplayMP3] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ #ifdef MP3_PLUGIN
+ Include=ReplayCommon;
+ Defaults font=VDRSymbolsSans,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Image x=343,y=20,path=symbols/mp3-header.png;
+ Image x=0,y=220,width=170,height=160,path=backgrounds/note.png;
+ Text text={replayTitle},x=5,y=105,size=44,height=66,width=790,align=center,lines=1,red=255,green=255,blue=255;
+ ImageFile x=235,y=260,width=330,height=247,path=/tmp/VDRSymbolsSans.cover,path2=symbols/nocover.png,fit=yes,aspect_ratio=yes;
+ SpectrumAnalyzer x=600,y=445,width=200,height=100,delay=10ms,red=52,green=162,blue=159,path=backgrounds/spectrum_avp.png;
+ #ifdef MOUSE_BUTTONS
+ Include=MouseButtons;
+ #endif
+ #endif
+ #ifdef MUSIC_PLUGIN
+
+ Defaults menu_x=95,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ Background path=backgrounds/bg-replay.jpg;
+
+ Text text={replayCurrent/%k:%M:%S} von{replayTotal/%k:%M:%S},x=65,y=88,size=30,width=600,height=40,delay=1;
+
+ Text text={replayTitle},x=90,y=175,width=600,height=80,size=37,lines=1,red=255,green=255,blue=255,dots=yes,scroll=marquee,scroll_count=5;
+
+
+ Text text={musicPlayStatus},x=5,y=60,height=28,width=200,size=20,align=center;
+ SpectrumAnalyzer x=200,y=367,width=430,height=130,delay=10ms,red=52,green=162,blue=159,path=backgrounds/spectrum_avp.png,fit=yes,aspect_ration=yes;
+
+ Text text=Artist:,x=90,y=250,height=20,width=60,size=20,;
+ Text text={musicArtist},x=140,y=250,height=20,width=250,size=20,;
+ Text text=Album:,x=90,y=280,height=20,width=60,size=20,;
+ Text text={musicAlbum},x=140,y=280,height=20,width=250,size=20,;
+ Text text=Genre:,x=90,y=310,height=20,width=60,size=20,;
+ Text text={musicGenre},x=140,y=310,height=20,width=250,size=20,;
+ Text text=Year:,x=90,y=340,height=20,width=60,size=0;
+ Text text={musicYear},x=140,y=340,height=20,width=250,size=20,;
+
+
+ Text text={musicFrequence} kHz\,{musicBitrate} kbps\, {musicStereoMode},x=330,y=147,height=20,width=300,size=12;
+ Text text=Track {musicIndex} of {musicCount},x=330,y=172,height=20,width=300,size=12;
+ Text text=Rating:,x=330,y=197,height=20,width=60,size=12;
+ Progressbar condition={musicRating} <> 3,total=255,value={musicRating},x=390,y=202,width=170,height=10,path=symbols/music-rating.png,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255;
+ Text condition={musicRating} = 3,text=zum löschen,x=390,y=202,height=20,width=170,size=12,;
+
+ //Image x=10,y=250,width=180,height=180,fit=yes,aspect_ratio=yes,path={musicCoverName}:symbols/nocover.png;
+ Text text={musicCurrentTrack},x=200,y=250,height=24,width=430,size=14,;
+ TextList text={musicTrack},x=200,y=275,height=60,width=430,size=12;
+
+ //Text text=File: {musicFilename},x=245,y=500,height=20,width=450,size=12,;
+ //Text text=Comment: {musicComment},x=245,y=525,height=20,width=450,size=12,;
+ Text condition={musicShuffle} = 1,text={\\001},x=10,y=435,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ Text condition={musicShuffle} <> 1,text={\\001},x=10,y=435,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+ Text condition={musicLoop} = 1,text={\\002},x=60,y=435,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ Text condition={musicLoop} <> 1,text={\\002},x=60,y=435,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+ Text condition={musicRecording} = 1,text={\\003},x=110,y=435,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ Text condition={musicRecording} <> 1,text={\\003},x=110,y=435,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+ //Text condition={musicLyrics} = 1,text={\\004},x=160,y=435,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ //Text condition={musicLyrics} <> 1,text={\\004},x=160,y=435,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+ //Text condition={musicCopy} = 1,text={\\005},x=10,y=470,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ //Text condition={musicCopy} <> 1,text={\\005},x=10,y=470,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+ //Text condition={musicTimer} = 1,text={\\007},x=160,y=470,width=30,height=30,size=20,align=center,red=187,green=129,blue=22;
+ //Text condition={musicTimer} <> 1,text={\\007},x=160,y=470,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+
+ Text condition={musicShutdown} = 1,text={\\006},x=85,y=470,width=30,height=30,size=20,align=center,red=255,green=0,blue=0;
+ Text condition={musicShutdown} <> 1,text={\\006},x=85,y=470,width=30,height=30,size=20,align=center,red=64,green=64,blue=64;
+ Text condition={volumeMute} = 0,text={\\008},x=300,y=500,width=20,height=18,size=12,align=left,red=187,green=129,blue=22;
+ Text condition={volumeMute} <> 0,text={\\009},x=300,y=500,width=20,height=18,size=12,align=left,red=187,green=129,blue=22;
+ Volumebar x=330,y=505,width=170,height=10,red=187,green=129,blue=22,permanent=yes,bg_red=50,bg_green=50,bg_blue=50,bg_alpha=255;
+
+
+ Text text={time/%H:%M} Uhr,x=75,y=470,width=300,height=100,red=255,green=255,blue=255,size=34;
+ Text text={time/%A. %d.%m.},x=335,y=487,width=300,height=100,size=22,red=175,green=175,blue=175;
+ //Laufzeiten links und rechts unten
+
+
+
+ Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=50,y=150,width=700,height=20,bg_x=50,bg_y=150,bg_width=700,bg_height=20,bg_alpha=255,red=57, green=120, blue=159,bg_red=160, bg_green=196, bg_blue=218,delay=3;
+ Image x=007,y=555,width=192,height=43,path=menu/button-red.png,on_click=Red;
+ Text text={musicButtonRed},x=20,y=560,width=-1,height=43,size=22,red=255,green=255,blue=255;
+ Image x=205,y=555,width=192,height=43,path=menu/button-green.png,on_click=Green;
+ Text text={musicButtonGreen},x=218,y=560,width=-1,height=43,size=22,red=0,green=0,blue=0;
+ Image x=403,y=555,width=192,height=43,path=menu/button-yellow.png,on_click=Yellow;
+ Text text={musicButtonYellow},x=416,y=560,width=-1,height=43,size=22,red=0,green=0,blue=0;
+ Image x=601,y=555,width=192,height=43,path=menu/button-blue.png,on_click=Blue;
+ Text text={musicButtonBlue},x=614,y=560,width=-1,height=43,size=22,red=255,green=255,blue=255;
+ Include=OSD-Messages;
+ #endif
+
+ #ifdef MUSIC_PLUGIN_MORONE_STYLE
+ Defaults font=VDRSymbolsSans,size=28,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-music-morone_800x600.png;
+ Image x=41,y=75,width=175,height=160,fit=yes,aspect_ratio=no,path={musicCoverName}:symbols/nocover.png;
+ Text text=Artist:,x=250,y=78,height=20,width=60,size=12,;
+ Text text={musicArtist},x=315,y=78,height=20,width=430,size=12,;
+ Text text=Album:,x=250,y=105,height=20,width=60,size=12,;
+ Text text={musicAlbum},x=315,y=105,height=20,width=430,size=12,;
+ Text text=Genre:,x=250,y=130,height=20,width=60,size=12,;
+ Text text={musicGenre},x=315,y=130,height=20,width=430,size=12,;
+ Text text=Year:,x=250,y=155,height=20,width=60,size=12,;
+ Text text={musicYear},x=315,y=155,height=20,width=430,size=12,;
+ Text text=Rating:,x=250,y=200,height=20,width=65,size=12;
+ Progressbar total=255,value={musicRating},x=315,y=205,width=110,height=10,path=symbols/music-rating.png,bg_alpha=255;
+ Text text={musicPlayStatus},x=53,y=280,height=20,width=148,size=12,align=center,;
+ SpectrumAnalyzer x=48,y=297,width=175,height=40,delay=10ms,red=52,green=162,blue=159,path=backgrounds/spectrum.png,fit=yes,aspect_ration=yes;
+ Text text={musicFrequence} kHz\,{musicBitrate} kbps\, {musicStereoMode},x=250,y=260,height=20,width=495,size=12;
+ Text text=Track {musicIndex} of {musicCount},x=250,y=285,height=20,width=495,size=12;
+ Text text={replayCurrent/%k:%M:%S},x=250,y=308,width=70,height=20,size=12;
+ Text text={replayTotal/%k:%M:%S},x=690,y=308,width=70,height=20,size=12;
+ Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=330,y=312,width=340,height=10,red=187,green=230,blue=22,bg_red=20,bg_green=50,bg_blue=50,bg_alpha=255;
+ Text text={musicCurrentTrack},x=52,y=375,height=20,width=700,size=14,;
+ TextList text={musicTrack},x=52,y=400,height=80,width=700,size=12,;
+ Text condition={musicShuffle} = 1,text={\\001},x=52,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ Text condition={musicShuffle} <> 1,text={\\001},x=52,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ Text condition={musicLoop} = 1,text={\\002},x=97,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ Text condition={musicLoop} <> 1,text={\\002},x=97,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ Text condition={musicRecording} = 1,text={\\003},x=142,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ Text condition={musicRecording} <> 1,text={\\003},x=142,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ //Text condition={musicLyrics} = 1,text={\\004},x=187,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ //Text condition={musicLyrics} <> 1,text={\\004},x=187,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ //Text condition={musicCopy} = 1,text={\\005},x=232,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ //Text condition={musicCopy} <> 1,text={\\005},x=232,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ //Text condition={musicTimer} = 1,text={\\007},x=322,y=503,width=40,height=40,size=26,align=center,red=187,green=129,blue=22;
+ //Text condition={musicTimer} <> 1,text={\\007},x=322,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ Text condition={musicShutdown} = 1,text={\\006},x=277,y=503,width=40,height=40,size=26,align=center,red=255,green=0,blue=0;
+ Text condition={musicShutdown} <> 1,text={\\006},x=277,y=503,width=40,height=40,size=26,align=center,red=64,green=64,blue=64;
+ //Text text={time/%A\, der %d. %B - %H:%M},x=52,y=509,height=24,size=14,width=480,,height=12;
+ Text condition={volumeMute} = 0,text={\\008},x=545,y=504,width=30,height=30,size=20,align=left,red=187,green=129,blue=22;
+ Text condition={volumeMute} <> 0,text={\\009},x=545,y=504,width=30,height=30,size=20,align=left,red=187,green=129,blue=22;
+ Volumebar x=580,y=513,width=170,height=11,red=187,green=129,blue=22,permanent=yes,bg_red=50,bg_green=50,bg_blue=50,bg_alpha=255;
+ Text text={musicButtonRed},x=40,y=571,height=20,width=145,size=12,;
+ Text text={musicButtonGreen},x=237,y=570,height=20,width=145,size=12,;
+ Text text={musicButtonYellow},x=440,y=570,height=20,width=145,size=12,;
+ Text text={musicButtonBlue},x=637,y=570,height=20,width=145,size=12,;
+ Include=OSD-Messages;
+ #endif
+
+
+// Tracklist des Music Plugins
+[MenuMusicTrackList] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=95,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+
+ ColumnSelected number=1,width=500,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=1,width=500;
+ ColumnSelected number=2,width=110,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=2,width=110;
+
+
+// Standardwerte für alle Menüs
+[MenuCommon] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=30,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-menu.jpg;
+ //Text text={time/%d.%m. %H:%M},x=600,y=10,width=195,height=30,align=right;
+ //PartingLine x=0,align=center,width=800,red=255,green=255,blue=255,path=menu/parting-line-avp.png,path2=menu/parting-line-empty-avp.png;
+ Message x=10,y=520,width=470,height=25,size=14,red=0,green=0,blue=0,bg_x=0,bg_y=520,path=backgrounds/bg-message.png,delay=5,bg_alpha=255;
+ MenuButtonRed x=50,y=545,width=179,height=43,size=18,red=255,green=255,blue=255,alpha=255,align=center;
+ MenuButtonGreen x=224,y=545,width=179,height=43,size=18,red=0,green=0,blue=0,alpha=255,align=center;
+ MenuButtonYellow x=400,y=545,width=179,height=43,size=18,red=0,green=0,blue=0,alpha=255,align=center;
+ MenuButtonBlue x=574,y=545,width=179,height=43,size=18,red=255,green=255,blue=255,alpha=255,align=center;
+ #ifdef MOUSE_BUTTONS
+ var varHideColorButtons = 1;
+ var varHidePrevChannel = 1;
+ Include=MouseButtons;
+ #endif
+
+
+[MenuTitel] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255;
+
+
+// Anzeige Standardmenü
+[Menu] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=95,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea x=95,y=170,width=600,height=350,on_dblclick=Ok;
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+ // Kanal
+ ColumnSelected number=1,width=580,red=255,green=255,blue=255,;
+ Column number=1,width=580;
+ // Titel
+ Text text={menuText},x=75,y=0,width=600,height=50;
+
+
+[MenuTimers] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=90,x=50,width=800,height=425,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+ // Kanal Nr
+ ColumnSelected number=2,width=80,red=255,green=255,blue=255;
+ Column number=2,width=80;
+ //
+ ColumnSelected number=3,width=1,red=255,green=255,blue=255;
+ Column number=3,width=1;
+ // Start Time
+ ColumnSelected number=4,width=100,red=255,green=255,blue=255;
+ Column number=4,width=100;
+ // End Time
+ ColumnSelected number=5,width=1,size=14,x-50,red=255,green=255,blue=255;
+ Column number=5,width=1,size=14,x-50;
+ // Event
+ ColumnSelected number=6,scroll=marquee,scroll_count=5,size=22,dots=1,width=450,height=50,red=255,green=255,blue=255;
+ Column number=6,dots=yes,size=22,width=450,height=50;
+
+
+// Anzeige der Auswahlliste aller Plugins
+[MenuSetupPlugins] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=90,x=50,width=800,height=425,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ // Workaround
+ ColumnSelected number=0,width=1,spacing=78;
+ Column number=0,x=0,width=1,spacing=78;
+ ColumnSelected number=0,width=1,spacing=5,focus=menu/focus_2.png;
+ Column number=0,x=0,width=1,spacing=5;
+
+ ColumnSelected number=0,width=620,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=0,width=620;
+
+
+// Menü zur Einstellung von Konfigurationsparametern
+[MenuSetupPage] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=90,x=50,width=800,height=425,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+
+ ColumnSelected number=1,width=500,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=1,width=500;
+ ColumnSelected number=2,width=110,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=2,width=110;
+
+
+// Aufnahmemenü (VDR Standardversion)
+[MenuRecordings] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=175,x=75,width=600,height=430,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ // Workaround
+ ColumnSelected number=1,width=1,red=255,green=255,blue=255,spacing=78;
+ Column number=1,x=0,width=1,spacing=78;
+ ColumnSelected number=1,width=1,spacing=20,focus=menu/focus_2.png,red=255,green=255,blue=255;
+ Column number=1,x=0,width=1,spacing=20;
+
+ ColumnSelected number=1,size=22,red=255,green=255,blue=255,width=150;
+ Column number=1,width=150;
+ ColumnSelected number=2,size=22,red=255,green=255,blue=255,width=100;
+ Column number=2,width=100;
+ ColumnSelected condition={colCount} = 3,number=3,size=22,red=255,green=255,blue=255,width=310;
+ Column condition={colCount} = 3,number=3,width=310;
+ ColumnSelected condition={colCount} = 3,number=4,size=22,red=255,green=255,blue=255,width=60;
+ Column condition={colCount} = 3,number=4,width=60;
+ ColumnSelected condition={colCount} > 3,number=3,size=22,width=60,red=255,green=255,blue=255,dots=yes;
+ Column condition={colCount} > 3,number=3,width=60,dots=yes;
+ ColumnSelected condition={colCount} > 3,number=4,size=22,width=310,red=255,green=255,blue=255,dots=yes;
+ Column condition={colCount} > 3,number=4,width=310,dots=yes;
+
+
+// Aufnahmemenü (extRecMenu Plugin Version)
+[MenuExtRecordings] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=175,x=75,width=600,height=430,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+
+ ColumnSelected number=1,size=22,red=255,green=255,blue=255,width=50;
+ Column number=1,width=50;
+ ColumnSelected condition={colCount} = 3,number=2,size=22,red=255,green=255,blue=255,width=120;
+ Column condition={colCount} = 3,number=2,width=120;
+ ColumnSelected condition={colCount} = 3,number=3,size=22,red=255,green=255,blue=255,width=310;
+ Column condition={colCount} = 3,number=3,width=310;
+ ColumnSelected condition={colCount} > 3,number=2,size=22,red=255,green=255,blue=255,width=150;
+ Column condition={colCount} > 3,number=2,width=150;
+ ColumnSelected condition={colCount} > 3,number=3,size=22,red=255,green=255,blue=255,width=100,dots=yes;
+ Column condition={colCount} > 3,number=3,width=100,dots=yes;
+ ColumnSelected condition={colCount} > 3,number=4,size=22,red=255,green=255,blue=255,width=320,dots=yes;
+ Column condition={colCount} > 3,number=4,width=320,dots=yes;
+
+
+// Kanalmenü (Menu->Kanäle)
+[MenuChannels] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=90,x=50,width=800,height=425,on_dblclick=Ok;
+
+ Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ //HG
+ EventColumnSelected x=79,width=10,text={},focus=menu/focus_2.png;
+ EventColumn x=79,width=10,text={};
+ // Workaround
+ ColumnSelected number=6,width=1,spacing=78;
+ Column number=6,width=1,spacing=78;
+ ColumnSelected number=6,width=1,focus=menu/focus_2.png,spacing=20;
+ Column number=6,width=1,spacing=20;
+
+ //Nr
+ ColumnSelected number=1,width=75,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=1,width=75;
+ //Name
+ ColumnSelected number=2,width=500,red=255,green=255,blue=255,scroll=marquee,scroll_count=5;
+ Column number=2,width=500;
+
+
+
+
+
+
+// Übersicht Programm
+[MenuEpgsSchedule] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=170,x=1,width=800,height=365,on_dblclick=Ok;
+
+
+ Text x=75,y=70,size=16,width=620,height=90,text={selectedRowEventDescription},red=255,green=255,blue=255;
+
+
+ //HG
+ EventColumnSelected x=79,width=10,red=255,green=255,blue=255,text={},focus=menu/focus_2.png;
+ EventColumn x=79,width=10,text={};
+
+ //Tag
+ EventColumnSelected x=90,width=50,red=255,green=255,blue=255,text={rowEventStartTime/%a};
+ EventColumn x=90,width=50,text={rowEventStartTime/%a};
+ //Zeit
+ EventColumnSelected x=145,width=100,red=255,green=255,blue=255,text={rowEventStartTime/%H:%M};
+ EventColumn x=145,width=100,text={rowEventStartTime/%H:%M};
+
+
+
+ //Symbol
+ // Partial Timer Before Record; Char 149 = Half Clock 1
+ EventColumnSelected x=250,red=255,green=255,blue=255,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ EventColumn x=250,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ // Timer & not Recording; Char 253 = Clock-Symbol
+ EventColumnSelected x=250,red=255,green=255,blue=255,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ EventColumn x=250,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ // Partial Timer After Record; Char 148 = Half Clock 2
+ EventColumnSelected x=250,red=255,green=255,blue=255,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ EventColumn x=250,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ // Recording Timer; Char 249 = REC Symbol
+ EventColumnSelected x=250,red=255,green=255,blue=255,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ EventColumn x=250,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ // Running Event; Char 251 = Runningman-Symbol
+ EventColumnSelected x=250,red=255,green=255,blue=255,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+ EventColumn x=250,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+
+ //Titel
+ EventColumnSelected x=295,width=400,red=255,green=255,blue=255,text={rowEventTitle};
+ EventColumn x=295,width=400,text={rowEventTitle};
+
+
+// Programmübersicht (Menu->Programme) (VDR Standard Version)
+[MenuSchedule] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuEpgsSchedule;
+
+
+// Übersicht Nächste
+[MenuEpgsWhatsOnElse] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=175,x=75,width=600,height=430,on_dblclick=Ok;
+
+ //Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ Text x=75,y=70,size=16,width=620,height=90,text={selectedRowEventDescription},red=255,green=255,blue=255;
+
+ //HG
+ EventColumnSelected x=79,width=10,red=255,green=255,blue=255,text={},focus=menu/focus_2.png;
+ EventColumn x=79,width=10,text={};
+
+ //Kanal
+ EventColumnSelected x=90,width=100,red=255,green=255,blue=255,text={RowEventChannelName};
+ EventColumn x=90,width=100,text={RowEventChannelName};
+ //Zeit
+
+ EventColumnSelected x=195,width=100,red=255,green=255,blue=255,text={rowEventStartTime/%H:%M};
+ EventColumn x=195,width=100,text={rowEventStartTime/%H:%M};
+
+ //Symbol
+ // Partial Timer Before Record; Char 149 = Half Clock 1
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ // Timer & not Recording; Char 253 = Clock-Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ // Partial Timer After Record; Char 148 = Half Clock 2
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ // Recording Timer; Char 249 = REC Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ // Running Event; Char 251 = Runningman-Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+
+ //Titel
+ EventColumnSelected x=345,width=400,red=255,green=255,blue=255,text={rowEventTitle};
+ EventColumn x=345,width=400,text={rowEventTitle};
+
+
+
+
+// Übersicht Jetzt
+[MenuEpgsWhatsOnNow] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ MenuNavigationArea y=175,x=75,width=600,height=430,on_dblclick=Ok;
+
+ //Text x=75,y=88,dots=yes,text={menuTitle},width=600,height=53,lines=1,bg_alpha=0,red=255,green=255,blue=255,size=30;
+ Text x=75,y=70,size=16,width=620,height=90,text={selectedRowEventDescription},red=255,green=255,blue=255;
+ //HG
+ EventColumnSelected x=79,width=10,red=255,green=255,blue=255,text={},focus=menu/focus_2.png;
+ EventColumn x=79,width=10,text={};
+
+ //Kanal
+ EventColumnSelected x=90,width=100,red=255,green=255,blue=255,text={RowEventChannelName};
+ EventColumn x=90,width=100,text={RowEventChannelName};
+ //Zeit
+ EventColumnSelected x=195,width=100,red=255,green=255,blue=255,text={rowEventStartTime/%H:%M};
+ EventColumn x=195,width=100,text={rowEventStartTime/%H:%M};
+
+ //Progress
+ EventColumnSelected type=progress,x=330,width=70,spacing=10,bar_height=40%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+ EventColumn type=progress,x=330,width=70,spacing=10,bar_height=40%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+
+ //Symbol
+ // Partial Timer Before Record; Char 149 = Half Clock 1
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasPartialTimerBefore} = 1,text={\\149},width=40,align_v=1;
+ // Timer & not Recording; Char 253 = Clock-Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasTimer} = 1 & {rowEventIsRecording} = 0,text={\\253},width=40,align_v=1;
+ // Partial Timer After Record; Char 148 = Half Clock 2
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventHasPartialTimerAfter} = 1,text={\\148},width=40,align_v=1;
+ // Recording Timer; Char 249 = REC Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventIsRecording} = 1,text={\\249},width=40,align_v=1;
+ // Running Event; Char 251 = Runningman-Symbol
+ EventColumnSelected x=300,red=255,green=255,blue=255,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+ EventColumn x=300,condition={rowEventIsRunning} = 1,text={\\251},width=40,align_v=1;
+
+ //Titel
+ EventColumnSelected x=415,width=320,red=255,green=255,blue=255,text={rowEventTitle};
+ EventColumn x=415,width=320,text={rowEventTitle};
+
+// Was läuft Jetzt (Menu->Programme-Jetzt) (VDR Standard Version)
+[MenuWhatsOnNow] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuEpgsWhatsOnNow;
+
+
+// Was läuft als NÄCHSTES (Menu->Programme-Nächste) (EPGSearch Plugin Version)
+[MenuEpgsWhatsOnNext] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuEpgsWhatsOnElse;
+
+
+// Was läuft als NÄCHSTES (Menu->Programme-Nächste) (VDR Standard Version)
+[MenuWhatsOnNext] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuEpgsWhatsOnElse;
+
+
+// Anzeige ArghDirector (benötigt ArghDirector Plugin)
+[MenuArghDirector] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=430,font=VDRSymbolsSans,size=20,red=255,green=255,blue=255,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ // define the MenuNavigationArea
+ MenuNavigationArea y=90,x=1,width=800,height=430,on_dblclick=Ok;
+
+ // Workaround
+ ColumnSelected number=0,width=1,red=255,green=255,blue=255,spacing=78;
+ Column number=0,width=1,spacing=78;
+ ColumnSelected number=0,width=1,red=255,green=255,blue=255,focus=menu/focus_2.png,spacing=20;
+ Column number=0,width=1,spacing=20;
+
+ ColumnSelected condition={colCount} = 1,number=1;
+ Column condition={colCount} = 1,number=1;
+
+ ColumnSelected condition={colCount} > 1,number=1,width=350;
+ Column condition={colCount} > 1,number=1,width=350;
+
+ ColumnSelected number=2,width=40;
+ Column number=2,width=40;
+
+ ColumnSelected number=3,type=progress,width=70,spacing=10,bar_height=50%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+ Column number=3,type=progress,width=70,spacing=10,bar_height=50%,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255,path=symbols/progress.png;
+
+ // Zeit
+ ColumnSelected number=4;
+ Column number=4;
+
+
+// Standardwerte für die Sektionen MenuEvent & MenuRecording
+// Dieser Abschnitt wird in anderen Sektionen eingebunden (include)
+[MenuDetailCommon] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+ Text text={time/%d.%m. %H:%M},x=600,y=10,width=195,height=30,align=right;
+ MenuButtonBackgroundRed x=7,y=555,width=192,height=43,pathON=menu/button-red.png,on_click=Red;
+ MenuButtonRed x=10,y=560,width=179,height=43,red=255,green=255,blue=255,alpha=255,align=center;
+ MenuButtonBackgroundGreen x=205,y=555,width=192,height=43,pathON=menu/button-green.png,on_click=Green;
+ MenuButtonGreen x=208,y=560,width=179,height=43,red=0,green=0,blue=0,alpha=255,align=center;
+ MenuButtonBackgroundYellow x=403,y=555,width=192,height=43,pathON=menu/button-yellow.png,on_click=Yellow;
+ MenuButtonYellow x=406,y=560,width=179,height=43,red=0,green=0,blue=0,alpha=255,align=center;
+ MenuButtonBackgroundBlue x=601,y=555,width=192,height=43,pathON=menu/button-blue.png,on_click=Blue;
+ MenuButtonBlue x=604,y=560,width=179,height=43,red=255,green=255,blue=255,alpha=255,align=center;
+
+ // {actRecordingCount} = Anzahl der gerade laufenen Aufnahmen
+ Image condition={actRecordingCount} > 0,x=587,y=520,path=symbols/animation/recOn_(0-9).png,delay=300ms;
+ // {actRecordingName} = Name der gerade laufenen Aufnahmen
+ Text condition={actRecordingCount} > 0, text={actRecordingName},x=665,y=530,width=130,height=25,lines=1,size=18,red=0,green=0,blue=0,scroll=marquee,scroll_count=5,bg_alpha=0;
+ // Spezial OSD Message
+ Message x=10,y=520,width=470,height=25,size=14,red=0,green=0,blue=0,bg_x=0,bg_y=520,path=backgrounds/bg-message.png,delay=5,bg_alpha=255;
+
+
+// Detailinfos des Programms in der Programmübersicht
+[MenuEvent] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuDetailCommon;
+ Defaults menu_x=90,menu_y=173,menu_height=375,menu_width=600,font=VDRSymbolsSans,size=22,red=57, green=120, blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0,height=50;
+ Background path=backgrounds/bg-detail.png;
+ var varStartLine = 0;
+
+ //Text text={menuTitle},x=90,y=15,width=420,height=40,size=22,lines=1,align=center,red=255,green=255,blue=255;
+ Image x=10,y=5,width=80,height=70,fit=yes,aspect_ratio=yes,path=columnimages/{eventChannelName/tologo}.png,on_click=Menu;
+ Text text={eventStartTime/%a %d.%m},x=90,y=60,size=20,width=170,height=30,red=255,green=255,blue=255;
+ Text text={eventStartTime/%H:%M},x=90,y=100,size=20,width=80,height=30,red=255,green=255,blue=255;
+ Text text=-{eventEndTime/%H:%M},x=175,y=100,size=20,width=90,height=30,red=255,green=255,blue=255;
+ Text text={eventTitle},x=285,y=60,width=450,height=40,lines=1,dots=yes,red=255,green=255,blue=255;
+ Text text={eventSubtitle},x=285,y=100,size=18,width=450,height=40,lines=1,red=255,green=255,blue=255;
+ //Text text={eventTitle},x=285,y=60,width=500,height=40,red=255,green=255,blue=255;
+ //Text text={eventSubtitle},x=285,y=100,size=18,width=450,height=65,lines=2,red=255,green=255,blue=255;
+ //Image x=600,y=60,width=200,height=125, path={varEPGimagesPath_1}{eventID}.png: {varEPGimagesPath_2}{eventID}.png: {varEPGimagesPath_3}{eventID}.png: {varImagesPath_1}{eventTitle}.jpg: backgrounds/filmspule_tr.png, fit=yes,aspect_ratio=yes;
+ Text text={eventDescription},start_line={varStartLine},x=90,y=165,width=600,height=360,size=20,red=255,green=255,blue=255,whipe_res=28,on_up=varStartLine--,on_down=varStartLine++;
+ #ifdef MOUSE_BUTTONS
+ var varHideColorButtons = 1;
+ var varHidePrevChannel = 1;
+ Include=MouseButtons;
+ #endif
+
+
+// Detailinfos der Aufnahmen
+[MenuRecording] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuDetailCommon;
+ Defaults font=VDRSymbolsSans,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+ Background path=backgrounds/bg-detail.png;
+ var varStartLine = 0;
+
+ Text text={recordingTime/%a %d.%m},x=90,y=60,size=20,width=170,height=30,red=255,green=255,blue=255;
+ Text text={recordingTime/%H:%M},x=90,y=100,size=20,width=80,height=30,red=255,green=255,blue=255;
+ Text text={recordingTitle},x=285,y=60,width=500,height=40,red=255,green=255,blue=255;
+ Text text={recordingSubtitle},x=285,y=100,size=18,width=450,height=65,lines=2,red=255,green=255,blue=255;
+ Image x=600,y=60,width=200,height=125,path={recordingPath}/thumbnail.png:
+ {varEPGimagesPath_1}{recordingEventID}.png:
+ {varEPGimagesPath_2}{recordingEventID}.png:
+ {varEPGimagesPath_3}{recordingEventID}.png:
+ {varImagesPath_1}{recordingTitle}.jpg:
+ backgrounds/filmspule_tr.png,
+ fit=yes,aspect_ratio=yes;
+ Text text={recordingDescription},start_line={varStartLine},x=90,y=165,width=600,height=360,size=20,red=255,green=255,blue=255,whipe_res=28,on_up=varStartLine--,on_down=varStartLine++;
+ #ifdef MOUSE_BUTTONS
+ var varHideColorButtons = 1;
+ var varHidePrevChannel = 1;
+ Include=MouseButtons;
+ #endif
+
+
+// Detailinfos der Aufnahmen (extRecMenu Plugin Version)
+[MenuExtRecording] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=MenuRecording;
+ Defaults font=VDRSymbolsSans,size=22,red=52,green=162,blue=159,bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+
+// Noch nicht weiter definierter Menüeintrag
+[MenuCommands] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=Menu;
+
+
+[MenuMain] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=Menu;
+
+
+// Anzeige bei nicht bekannten Menüstrukturen
+[MenuUnknown] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Include=Menu;
+
+
+// Definition der Mouse Button Fenster
+[MouseButtons] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults bg_alpha=0,foreground=yes,red=57, green=120, blue=159;
+
+ Image x=003,y=510,width=40,height=40,bg_width=40,delay=150,path=menu/mouse_btn/btn_keyb_white.png,on_click=varTouchMenu:0:1;
+ Image x=003,y=460,width=40,height=40,bg_width=40,delay=150,path=menu/mouse_btn/btn_menu_white.png,on_click=Menu;
+ MenuButtonBackgroundRed condition={varHideColorButtons} = 1,x=7,y=555,width=192,height=43,pathON=menu/button-red.png,on_click=Red;
+ MenuButtonBackgroundGreen condition={varHideColorButtons} = 1,x=205,y=555,width=192,height=43,pathON=menu/button-green.png,on_click=Green;
+ MenuButtonBackgroundYellow condition={varHideColorButtons} = 1,x=403,y=555,width=192,height=43,pathON=menu/button-yellow.png,on_click=Yellow;
+ MenuButtonBackgroundBlue condition={varHideColorButtons} = 1,x=601,y=555,width=192,height=43,pathON=menu/button-blue.png,on_click=Blue;
+
+ //Basisblock
+ //{varHideColorButtons} = 0 -> Farbtasten anzeigen
+ //{varHideColorButtons} = 1 -> Farbtasten nicht anzeigen
+ //{varHidePrevChannel} = 0 -> PrevChannel Taste anzeigen
+ //{varHidePrevChannel} = 1 -> PrevChannel Taste nicht anzeigen
+
+ if ({varTouchMenu})
+ //Hintergrund
+ Image x=198,y=376,width=404,height=159,path=menu/mouse_btn/btn_back_master.png;
+
+ //Schalterbuttons für Nummernblock (2) und Extended Block (3), beim zweiten Klick, Rückfall auf 1 (:2:1)
+ Image x=205,y=383,width=40,height=40,delay=150,path=menu/mouse_btn/btn_keyb_segment_trans_left.png,on_click=varTouchMenu:2:1,delay=15;
+ Image x=554,y=383,width=40,height=40,delay=150,path=menu/mouse_btn/btn_keyb_segment_trans_right.png,on_click=varTouchMenu:3:1,delay=15;
+
+ //Erste Zeile von oben
+ Image x=303,y=390,width=40,height=40,delay=150,path=menu/mouse_btn/btn_rec.png,on_click=Record;
+ Image x=457,y=390,width=40,height=40,delay=150,path=menu/mouse_btn/btn_power.png,on_click=Power;
+
+ //Zweite Zeile von oben
+ Image condition={varHidePrevChannel} <> 1,x=213,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevchannel.png,on_click=PrevChannel;
+ Image x=258,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_back.png,on_click=back;
+ Image x=303,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_up.png,on_click=Up;
+ Image x=348,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_ok.png,on_click=ok;
+
+ Image x=457,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_minus.png,on_click="Volume-";
+ Image x=502,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mute.png,on_click=Mute;
+ Image x=547,y=435,width=40,height=40,delay=150,path=menu/mouse_btn/btn_plus.png,on_click="Volume+";
+
+ //Dritte Zeile von oben
+ Image x=213,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_menu.png,on_click=Menu;
+ Image x=258,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_left.png,on_click=Left;
+ Image x=303,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_down.png,on_click=Down;
+ Image x=348,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_right.png,on_click=Right;
+
+ Image condition={varHideColorButtons} <> 1,x=412,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_red.png,on_click=Red;
+ Image condition={varHideColorButtons} <> 1,x=457,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_green.png,on_click=Green;
+ Image condition={varHideColorButtons} <> 1,x=502,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_yellow.png,on_click=Yellow;
+ Image condition={varHideColorButtons} <> 1,x=547,y=480,width=40,height=40,delay=150,path=menu/mouse_btn/btn_blue.png,on_click=Blue;
+ endif
+
+
+ //Nummernblock
+ //{varSwitchMouseNumber} = 0 -> Normaler Zehnerblock
+ //{varSwitchMouseNumber} = 1 -> Ändert Zehnerblock-Zahlen zu Aufnahme Icons
+ //{varSwitchMouseNumber} = 2 -> Ändert Zehnerblock-Zahlen zu DVD Wiedergabe Icons
+
+ if ({varTouchMenu} == 2)
+ //Hintergrund
+ Image x=089,y=225,width=164,height=206,path=menu/mouse_btn/btn_back_numberblock.png;
+
+ //Erste Zeile von oben
+ Image x=106,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_1.png,on_click=1;
+ Image condition={varSwitchMouseNumber} = 0,x=151,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_2.png,on_click=2;
+ Image condition={varSwitchMouseNumber} = 1,x=151,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cut.png,on_click=2;
+ Image condition={varSwitchMouseNumber} = 2,x=151,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_subtitle.png,on_click=2;
+ Image condition={varSwitchMouseNumber} <> 2,x=196,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_3.png,on_click=3;
+ Image condition={varSwitchMouseNumber} = 2,x=196,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdangle.png,on_click=3;
+
+ //Zweite Zeile von oben
+ Image condition={varSwitchMouseNumber} <> 1,x=106,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_4.png,on_click=4;
+ Image condition={varSwitchMouseNumber} = 1,x=106,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmarkleft.png,on_click=4;
+ Image condition={varSwitchMouseNumber} = 2,x=106,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prev.png,on_click=4;;
+
+ Image condition={varSwitchMouseNumber} <> 2,x=151,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_5.png,on_click=5;
+ Image condition={varSwitchMouseNumber} = 2,x=151,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdnavi.png,on_click=5;
+ Image condition={varSwitchMouseNumber} = 0,x=196,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_6.png,on_click=6;
+ Image condition={varSwitchMouseNumber} = 1,x=196,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmarkright.png,on_click=6;
+ Image condition={varSwitchMouseNumber} = 2,x=196,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_next.png,on_click=6;
+
+ //Dritte Zeile von oben
+ Image condition={varSwitchMouseNumber} = 0,x=106,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_7.png,on_click=7;
+ Image condition={varSwitchMouseNumber} = 1,x=106,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevtitle.png,on_click=7;
+ Image condition={varSwitchMouseNumber} = 2,x=106,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prevtitle.png,on_click=7;
+ Image condition={varSwitchMouseNumber} <> 2,x=151,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_8.png,on_click=8;
+ Image condition={varSwitchMouseNumber} = 2,x=151,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvdmenu.png,on_click=8;
+ Image condition={varSwitchMouseNumber} = 0,x=196,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_9.png,on_click=9;
+ Image condition={varSwitchMouseNumber} = 1,x=196,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_nexttitle.png,on_click=9;
+ Image condition={varSwitchMouseNumber} = 2,x=196,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_nexttitle.png,on_click=9;
+
+ //Vierte Zeile von oben
+ Image condition={varSwitchMouseNumber} = 0,x=151,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_0.png,on_click=0;
+ Image condition={varSwitchMouseNumber} = 1,x=151,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_cropmark.png,on_click=0;
+ Image condition={varSwitchMouseNumber} = 2,x=151,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_audiomenu.png,on_click=0;
+ endif
+
+
+ //Extended Icon Block
+ if ({varTouchMenu} == 3)
+ //Hintergrund
+ Image x=547,y=225,width=248,height=206,path=menu/mouse_btn/btn_back_extended.png;
+
+ //Erste Zeile von oben
+ Image x=564,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_dvd.png,on_click=User4;
+ Image x=654,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_audio.png,on_click=Audio;
+ Image x=699,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mp3.png,on_click=User2;
+ Image x=744,y=240,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mplayer.png,on_click=User3;
+
+ //Zweite Zeile von oben
+ Image x=564,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_mail.png,on_click=User9;
+ Image x=609,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_videotext.png,on_click=User1;
+ Image x=654,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_pip.png,on_click=User6;
+ Image x=744,y=285,width=40,height=40,delay=150,path=menu/mouse_btn/btn_burn.png,on_click=User5;
+
+ //Dritte Zeile von oben
+ Image x=609,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_pause.png,on_click=Pause;
+ Image x=654,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_stop.png,on_click=Stop;
+ Image x=699,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_play.png,on_click=Play;
+ Image x=744,y=330,width=40,height=40,delay=150,path=menu/mouse_btn/btn_eject.png,on_click=User8;
+
+ //Vierte Zeile von oben
+ Image x=609,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_prev.png,on_click=Prev;
+ Image x=654,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_frwd.png,on_click=FastRew;
+ Image x=699,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_ffwd.png,on_click=FastFwd;
+ Image x=744,y=375,width=40,height=40,delay=150,path=menu/mouse_btn/btn_next.png,on_click=Next;
+ endif
+
+
+// Kalibrationsanzeige für Touch Displays
+[Calibration] //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Defaults font=VDRSymbolsSans,size=20,red=180,green=180,blue=180,bg_red=0,bg_green=0,bg_blue=250,bg_alpha=0;
+ Background path=backgrounds/calibrate-avp.png;
+
+ //Kalibrierungsanweisungen
+ Text text={calibrationInstruction},x=430,y=80,size=22,width=370,height=40,red=255,green=255,blue=255;
+ Text text={calibrationInfo},x=430,y=120,size=22,width=370,height=40,red=255,green=255,blue=255;
+ //Ausgabe der Kalibrationswerte
+ Text text=touched: {calibrationTouchedX} / {calibrationTouchedY},x=470,y=250,size=22,width=330,height=40;
+ Text text=Offset: {calibrationOffsetX} / {calibrationOffsetY},x=470,y=290,size=22,width=330,height=40;
+ Text text=Scale: {calibrationScaleX} / {calibrationScaleY},x=470,y=330,size=22,width=330,height=40;
+ //Anzeige Kalibrationscursor
+ CalibrationCursor width=30,height=30,path=symbols/calibratecursor.png;
+
+ #ifdef CHECKGRID
+ // Checkgrid unten rechts zeichnen
+ Rectangle x=600,y=450,width=800,height=3; //Obere waagerechte Linie
+ Rectangle x=580,y=500,width=800,height=2; //Mittlere waagerechte Linie
+ Rectangle x=560,y=550,width=800,height=1; //Untere waagerechte Linie
+
+ Rectangle x=650,y=410,width=3,height=600; //Linke senkrechte Linie
+ Rectangle x=700,y=390,width=2,height=600; //Mittlere senkrechte Linie
+ Rectangle x=750,y=370,width=1,height=600; //Rechte senkrechte Linie
+
+ // Checkgrid oben links zeichnen
+ Rectangle x=0,y=010,width=800,height=1; //Obere waagerechte Linie
+ Rectangle x=0,y=050,width=600,height=2; //Mittlere waagerechte Linie
+ Rectangle x=0,y=100,width=400,height=3; //Untere waagerechte Linie
+
+ Rectangle x=010,y=0,width=1,height=600; //Linke senkrechte Linie
+ Rectangle x=050,y=0,width=2,height=500; //Mittlere senkrechte Linie
+ Rectangle x=100,y=0,width=3,height=400; //Rechte senkrechte Linie
+ #endif
+
+
+ MenuButtonBackgroundYellow x=403,y=555,width=192,height=43,pathON=menu/button-yellow.png,on_click=Yellow;
+ MenuButtonYellow x=406,y=560,width=179,height=43,red=0,green=0,blue=0,alpha=255,align=center;
diff --git a/themes/anthraize.theme b/themes/anthraize.theme
new file mode 100644
index 0000000..9cd721d
--- /dev/null
+++ b/themes/anthraize.theme
@@ -0,0 +1,591 @@
+//***************************************************************************
+// Suchpfade fuer die EPG Images
+//***************************************************************************
+ var varEPGimagesPath_1 = "/var/cache/vdr/epgimages/";
+ var varEPGimagesPath_2 = "/media/epgimages/";
+ var varEPGimagesPath_3 = "/video0/epgimages/";
+ var varEPGimagesPath_4 = "/ramdisk/epgimages/";
+ var varImagesPath_1 = "/video0/images/";
+
+//***************************************************************************
+[Theme]
+//***************************************************************************
+
+ Theme name=anthraize-800x480,dir=anthraize,
+ fontPath=./fonts:../../fonts,width=800,height=480,
+ themeVersion=0.0.2,
+ syntaxVersion=0.4.1;
+
+// startImage=backgrounds/start.png,
+// endImage=backgrounds/end.png;
+
+ #define MP3_PLUGIN
+// #define MUSIC_PLUGIN
+// #define MUSIC_PLUGIN_MORONE_STYLE
+
+// start.png,end.png = (c) yaVDR
+// based on the fabulous work "AVP" (c) data - THANK YOU!!
+
+//***************************************************************************
+[OSD-Messages]
+//***************************************************************************
+
+ Message x=0,y=180,bg_x=5,bg_y=120,bg_width=790,bg_height=280,bg_color=20/20/20,align=center,
+ width=800,height=280,font=graphTFT,size=48,color=250/250/250,delay=5;
+
+//***************************************************************************
+[Volume]
+//***************************************************************************
+
+ Defaults font=graphTFT;
+// ohne bg-dummy.png blendet volume.png nicht aus!!
+// Background path=backgrounds/bg-dummy.png,permanent=no,delay=3,
+ Volumebar x=197,y=230,width=1,height=1,switch=yes,bg_x=198,bg_y=239,bg_width=1,bg_height=1,alpha=1,permanent=no,delay=3;
+ VolumeMuteSymbol x=350,y=305,bg_x=5,bg_y=120,bg_width=790,bg_height=280,bg_color=20/20/20,pathON=symbols/mute_on.png,pathOFF=symbols/mute_off.png,permanent=no,delay=3;
+ Volumebar x=197,y=230,width=403,height=60,switch=yes,color=51/51/51,bg_color=51/51/51,permanent=no,delay=3;
+ Volumebar x=191,y=180,width=403,bg_x=191,bg_y=180,bg_width=416,bg_height=118,path2=backgrounds/volume.png,permanent=no,delay=3;
+ Volumebar text=percent,bg_x=5,bg_y=120,bg_width=790,bg_height=280,bg_color=20/20/20,size=26,color=0/0/0,x=344,y=208,height=40,permanent=no,delay=3;
+
+
+//***************************************************************************
+[NormalClock]
+//***************************************************************************
+
+ Defaults font=graphTFT,size=40,color=200/200/200,bg_color=0/0/0/0;
+ Background path=backgrounds/bg-tv.png;
+
+// Datumzeile
+ Text text={time/%A\, %d. %b.%G},x=20,y=7,width=650,height=65,size=36,bg_alpha=0,align=left;
+
+// Senderlogo Logo oben rechts
+ Image x=635,y=90,width=160,height=96,fit=yes,aspect_ratio=yes,path=logos/{presentChannelName}.png;
+
+// Email-Symbol
+ Image condition={hasNewMail} != 0,x=702,y=136,path=symbols/mail.png;
+
+// Aufnahmesymbol
+ Image condition={actRecordingCount} > 0,
+ x=656,y=5,path=symbols/recOn-clock.png,permanent=no,delay=5;
+
+// grosse Uhr
+ Text text={time/%H:%M},x=10,y=70,width=600,align=left,height=200,size=150;
+
+// Aktuelle Sendung
+ Text text={presentStartTime/%H:%M},x=15,y=280,width=150,height=70,color=255/255/255,size=38;
+ Text text={presentTitle},x=180,y=280,height=70,width=610,size=38,color=255/255/255,lines=1;
+
+// Naechste Sendung
+ Text text={followingStartTime/%H:%M},x=15,y=340,width=150,height=70,size=38;
+ Text text={followingTitle},x=180,y=340,height=70,width=610,size=38,color=180/180/180,lines=1;
+
+// Timebar
+// Timebar x=20,y=425,width=760,height=30,path=symbols/progress.png,bg_alpha=150;
+ Timebar x=20,y=425,width=760,height=30,path=symbols/progress_yellow.png,bg_alpha=150;
+
+// Includes
+ Include=Volume;
+ Include=OSD-Messages;
+
+//***************************************************************************
+[ReplayCommon]
+//***************************************************************************
+
+ Include=Volume;
+ Include=OSD-Messages;
+
+// play
+ Image condition={replayForward} = 1 & {replaySpeed}= -1 & {replayPlay} = 1,x=10,y=7,width=60,height=60,path=symbols/play.png,delay=1;
+// pause
+ Image condition={replayForward} = 1 & {replaySpeed} = -1 & {replayPlay} <1,x=10,y=7,width=60,height=60,path=symbols/pause.png,delay=1;
+// Fast Rewind
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} = 1,x=10,y=7,width=60,height=60,path=symbols/frew.png,delay=1;
+// Fast Forward
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} = 1,x=10,y=7,width=60,height=60,path=symbols/ffwd.png,delay=1;
+// Slow Rewind
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} < 1,x=10,y=7,width=60,height=60,path=symbols/srew.png,delay=1;
+// Slow Forward
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} < 1,x=10,y=7,width=60,height=60,path=symbols/sfwd.png,delay=1;
+
+
+//***************************************************************************
+[ReplayNormal]
+//***************************************************************************
+
+ Include=ReplayCommon;
+
+ Defaults font=graphTFT,size=40,color=200/200/200,bg_color=0/0/0/0;
+ Background path=backgrounds/bg-tv.png;
+
+// Datum/Uhr oben rechts
+ Text text={time/%A %d.%m} - {time/%H:%M},x=100,y=7,size=40,align=right,width=680,align=right,height=60;
+
+// Ausgabe recording info
+ Text text={replayTitle},x=10,y=85,size=40,width=720,height=70,lines=1,color=250/250/250,dots=1;
+ Text text={replaySubtitle},x=10,y=150,size=28,width=770,height=40,color=250/250/250;
+ Text text={replayDescription},x=10,y=195,width=770,height=200,size=20,color=200/200/200,dots=1;
+
+
+// Laufzeiten links und rechts oben
+ Text text={replayCurrent/%k:%M:%S},x=10,y=420,width=170,size=30,height=40,align=left,delay=1;
+ Text text={replayTotal/%k:%M:%S},x=610,y=420,width=170,size=30,height=40,align=right,delay=1;
+
+// Progressbar
+// Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=200,y=430,path=symbols/progress.png,width=380,height=30,bg_alpha=200;
+ Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=200,y=430,path=symbols/progress_yellow.png,width=380,height=30,bg_alpha=200;
+
+//***************************************************************************
+[TV_Radio_Common]
+//***************************************************************************
+
+ Background path=backgrounds/bg-tv.png;
+ Defaults font=graphTFT,size=24,color=200/200/200,bg_color=0/0/0/0;
+
+// Timebar
+// Timebar x=10,y=85,width=630,height=25,path=symbols/progress.png,bg_alpha=150;
+ Timebar x=10,y=85,width=630,height=25,path=symbols/progress_yellow.png,bg_alpha=150;
+
+// Senderlogo Logo oben rechts
+ Image x=635,y=15,width=160,height=96,fit=yes,aspect_ratio=yes,path=logos/{presentChannelName}.png;
+
+
+// Textausgabe des Kanalnamens im Kopfbereich
+ Text x=20,y=7,text={presentChannelName},size=40,align=left,width=620,height=65;
+
+// Aktuelle Sendung
+ Text text={presentStartTime/%H:%M},x=10,y=115,width=160,height=70,size=40,color=255/255/255;
+ Text text={presentTitle},x=175,y=115,bg_x=175,bg_y=115,bg_width=650,bg_height=80,width=590,height=80,size=40,dots=yes,color=255/255/255;
+ Text text={presentSubtitle},x=175,y=180,bg_x=175,bg_y=180,bg_width=650,bg_height=77,width=600,height=77,lines=2,size=24,dots=yes,color=255/255/255;
+
+// Kommende Sendung
+ Text text={followingStartTime/%H:%M},x=10,y=260,width=160,height=60,size=40;
+ Text text={followingTitle},x=175,y=260,bg_x=175,bg_y=260,bg_width=650,bg_height=80,width=590,height=80,size=40,dots=yes;
+ Text text={followingSubtitle},x=175,y=325,bg_x=175,bg_y=325,bg_width=650,bg_height=77,width=600,height=77,lines=2,size=24,dots=yes;
+
+// Iconleiste
+ Image x=0,y=400,width=800,height=78,path=backgrounds/button-bg-grey.png;
+
+ Image condition={channelHasVtx} = 0,x=30,y=400,width=80,height=78,path=symbols/vtxOff.png,fit=yes;
+ Image condition={channelHasVtx},x=30,y=400,width=80,height=78,path=symbols/vtxOn.png,fit=yes;
+ Image condition={channelHasDD} = 0, x=127,y=400,width=110,height=78,path=symbols/ddOff.png,fit=yes;
+ Image condition={channelHasDD},x=127,y=400,width=110,height=78,path=symbols/ddOn.png,fit=yes;
+ Image condition={channelIsEncrypted} = 0,x=390,y=400,width=110,height=78,path=symbols/cryptOff.png,fit=yes;
+ Image condition={channelIsEncrypted} = 1,x=390,y=400,width=110,height=78,path=symbols/cryptOn.png,fit=yes;
+ Image condition={actRecordingCount} = 0, x=530,y=400,width=110,height=78,path=symbols/recOff.png,fit=yes;
+ Image condition={actRecordingCount} > 0,x=530,y=400,width=110,height=78,path=symbols/recOn.png,fit=yes;
+ Image condition={channelHasMultilang} = 0, x=250,y=400,width=130,height=78,path=symbols/ch2Off.png,fit=yes;
+ Image condition={channelHasMultilang},x=250,y=400,width=130,height=78,path=symbols/ch2On.png,fit=yes;
+
+// Datum/Uhr unten rechts
+ Text text={time/%H:%M},x=630,y=409,size=40,color=255/255/255,align=left,width=160,height=60,bg_alpha=2;
+
+// Includes
+ Include=Volume;
+ Include=OSD-Messages;
+
+//***************************************************************************
+[NormalTV]
+//***************************************************************************
+
+ Include=TV_Radio_Common;
+
+
+//***************************************************************************
+[NormalRadio]
+//***************************************************************************
+
+ Include=TV_Radio_Common;
+
+//***************************************************************************
+[NormalNonLiveTv]
+//***************************************************************************
+
+ Defaults font=graphTFT,size=40,color=200/200/200,bg_color=0/0/0/0;
+ Background path=backgrounds/bg-tv.png;
+
+// Titel
+ Text condition={actRecordingCount} > 0, text={actRecordingCount} Timer(s),x=40,y=7,width=650,height=60,size=40,bg_alpha=2,color=200/20/20,align=left;
+ Text condition={actRecordingCount} = 0, text=Timer,x=40,y=7,width=650,height=60,size=40,bg_alpha=0,align=left;
+// Aufnahmesymbol
+ Image condition={actRecordingCount} > 0,
+ x=656,y=5,path=symbols/recOn-clock.png,permanent=no,delay=5;
+
+// TextList text={actRecordings},x=40,y=90,height=310,width=760,size=20;
+ TextList condition={actRecordingCount} > 0, text={actRunningRecordings},x=20,y=85,height=330,width=760,size=26,color=200/20/20,bg_alpha=2;
+ TextList condition={actRecordingCount} = 0, text={actPendingRecordings},x=20,y=85,height=330,width=760,size=26,bg_alpha=2;
+
+// Datum/Uhr unten rechts
+ Text text={time/%A %d.%m} - {time/%H:%M},x=40,y=409,size=40,color=255/255/255,align=left,width=800,height=60,bg_alpha=2;
+
+// Includes
+ Include=Volume;
+ Include=OSD-Messages;
+
+//***************************************************************************
+[MenuCommon]
+//***************************************************************************
+
+ Defaults font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+ Background path=backgrounds/bg-menu.png;
+ Text text={menuTitle},x=20,y=10,width=760,size=36,height=75,lines=1,align=left,dots=yes;
+
+// colorbuttons
+ MenuButtonBackgroundRed x=8,y=430,width=192,height=43,pathON=menu/button-red.png,on_click=Red;
+ MenuButtonRed x=14,y=438,width=179,size=22,height=43,color=200/200/200,alpha=255,align=center;
+ MenuButtonBackgroundGreen x=205,y=430,width=192,height=43,pathON=menu/button-green.png,on_click=Green;
+ MenuButtonGreen x=212,y=438,width=179,size=22,height=43,color=200/200/200,alpha=255,align=center;
+ MenuButtonBackgroundYellow x=401,y=430,width=192,height=43,pathON=menu/button-yellow.png,on_click=Yellow;
+ MenuButtonYellow x=408,y=438,width=179,size=22,height=43,color=200/200/200,alpha=255,align=center;
+ MenuButtonBackgroundBlue x=597,y=430,width=192,height=43,pathON=menu/button-blue.png,on_click=Blue;
+ MenuButtonBlue x=604,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+
+// special OSD message
+ Message x=0,y=438,bg_x=0,bg_y=430,width=800,align=center,height=55,size=22,color=200/200/200,path=backgrounds/bg-message-yellow.png,delay=5,bg_alpha=255;
+
+// parting line
+ PartingLine x=0,align=center,width=800,size=26,color=250/250/250,path=menu/parting-line.png;
+
+//***************************************************************************
+[Menu]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_x=0,menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+ ColumnSelected number=5,width=1,focus=menu/menuCurrent.png,spacing=20;
+ Column number=5,width=1,spacing=20;
+
+ MenuSelected x=10,y=90,width=760,height=330,size=30,color=255/255/255;
+ Menu x=10,y=90,width=760,height=330,color=200/200/200;
+
+//***************************************************************************
+[MenuSetupPage]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+ ColumnSelected number=1,width=600,size=30,color=250/250/250,focus=menu/menuCurrent.png,scroll=off;
+ Column number=1,width=600;
+
+ ColumnSelected number=2,size=30,color=250/250/250,scroll=off;
+ Column number=2;
+
+
+//***************************************************************************
+[MenuSetupPlugins]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+ ColumnSelected number=1,width=760,size=30,color=250/250/250,focus=menu/menuCurrent.png,dots=yes;
+ Column number=1,width=760;
+
+ ColumnSelected number=2,size=30,color=250/250/250,scroll=off;
+ Column number=2;
+
+// parting line
+// PartingLine x=0,align=center,width=800,size=26,color=250/250/250,path=menu/parting-line.png,path2=menu/parting-line-empty.png;
+
+
+//***************************************************************************
+[MenuTimers]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+ ColumnSelected number=1,width=35,size=30,color=255/255/255,focus=menu/menuCurrent.png,spacing=5;
+ Column number=1,width=30,spacing=5;
+ ColumnSelected number=2,width=1,size=30,size=30,color=255/255/255;
+ Column number=2,width=70;
+ ColumnSelected number=3,width=165,size=30,color=255/255/255;
+ Column number=3,width=120;
+ ColumnSelected number=4,width=125,size=30,color=255/255/255;
+ Column number=4,width=110;
+ ColumnSelected number=5,width=130,size=30,color=255/255/255;
+ Column number=5,width=110;
+ ColumnSelected number=6,width=350,size=30,color=255/255/255,dots=1;
+ Column number=6,dots=yes;
+
+//***************************************************************************
+[MenuExtRecordings]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+
+// spacer
+ ColumnSelected number=5,width=1,focus=menu/menuCurrent.png;
+ Column number=5,width=1;
+// symbol
+ ColumnSelected number=1,size=30,color=255/255/255,width=40;
+ Column number=1,width=40;
+//
+ ColumnSelected condition={colCount} = 3,number=2,size=30,color=255/255/255,width=200;
+ Column condition={colCount} = 3,number=2,width=200;
+ ColumnSelected condition={colCount} = 3,number=3,size=30,color=255/255/255,width=150;
+ Column condition={colCount} = 3,number=3,width=150;
+// dir-count
+ ColumnSelected condition={colCount} > 3,number=2,size=30,color=255/255/255,width=190,spacing=20;
+ Column condition={colCount} > 3,number=2,width=190,spacing=20;
+// name
+ ColumnSelected condition={colCount} > 3,number=5,size=30,color=255/255/255,width=520,dots=1;
+ Column condition={colCount} > 3,number=5,width=520,dots=yes;
+
+
+//***************************************************************************
+[MenuExtRecording] // Detailed-Information Recording (extRecMenu Plugin Version)
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+ Background path=backgrounds/bg-menu.png;
+
+// Output MenuTitle - Recording-Date
+ Text text={menuTitle},x=20,y=10,width=330,height=60,size=36,lines=1,align=left,color=200/200/200,dots=1;
+ Text text={recordingTime/%a %d.%m - %H:%M },x=350,y=10,width=440,size=36,height=60,align=right,color=200/200/200;
+
+// Output Detailed-Information
+ Text text={recordingTitle},x=10,y=85,size=36,width=720,height=60,lines=1,color=250/250/250,dots=1;
+ Text text={recordingSubtitle},x=10,y=150,size=28,width=770,height=40,color=250/250/250;
+ Text text={recordingDescription},x=10,y=195,width=770,height=239,size=20,color=200/200/200,dots=1;
+
+// colorbuttons
+ MenuButtonBackgroundRed x=8,y=430,width=192,height=43,pathON=menu/button-red.png,on_click=Red;
+ MenuButtonRed x=14,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+ MenuButtonBackgroundGreen x=205,y=430,width=192,height=43,pathON=menu/button-green.png,on_click=Green;
+ MenuButtonGreen x=212,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+ MenuButtonBackgroundYellow x=401,y=430,width=192,height=43,pathON=menu/button-yellow.png,on_click=Yellow;
+ MenuButtonYellow x=408,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+ MenuButtonBackgroundBlue x=597,y=430,width=192,height=43,pathON=menu/button-blue.png,on_click=Blue;
+ MenuButtonBlue x=604,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+
+//***************************************************************************
+[MenuRecordings]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// spacer
+ ColumnSelected number=4,width=1,focus=menu/menuCurrent.png;
+ Column number=4,width=1;
+
+// Date
+ ColumnSelected number=1,width=180,size=30,spacing=20,color=255/255/255;
+ Column number=1,width=160;
+//
+ ColumnSelected condition={colCount} = 3,number=6,size=30,color=255/255/255;
+// Column condition={colCount} = 3,number=6,width=100;
+ ColumnSelected condition={colCount} = 3,number=3,size=30,color=255/255/255,width=150;
+// Column condition={colCount} = 3,number=3,width=150;
+// dir-count
+ ColumnSelected condition={colCount} > 3,number=2,,width=160,size=30,color=255/255/255,spacing=10;
+ Column condition={colCount} > 3,number=2,width=140,spacing=10;
+// Name
+ ColumnSelected condition={colCount} > 3,number=4,size=30,color=255/255/255,dots=1;
+ Column condition={colCount} > 3,number=4,width=400,dots=yes;
+
+
+//***************************************************************************
+[MenuRecording]
+//***************************************************************************
+
+ Include=MenuExtRecording;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+ MenuButtonBackgroundBlue x=597,y=430,width=192,height=43,pathON=menu/button-blue.png,on_click=Blue;
+ MenuButtonBlue x=604,y=438,width=179,size=22,height=43,color=200/200/200/255,align=center;
+
+//***************************************************************************
+[MenuChannels]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=330,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// spacer
+ ColumnSelected number=5,width=1,focus=menu/menuCurrent.png,spacing=20;
+ Column number=5,width=1,spacing=20;
+
+ ColumnSelected number=1,x=5,size=30,width=120,color=255/255/255;
+ Column number=1,x=5,width=120;
+ ColumnSelected number=2,x=85,size=30,color=255/255/255,scroll=marquee,scroll_count=2;
+ Column number=2,x=75;
+
+
+//***************************************************************************
+[MenuEvent]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=350,font=graphTFT,size=24,color=255/255/255,bg_alpha=0;
+
+// Senderlogo Logo oben rechts
+ Image x=635,y=15,width=160,height=96,fit=yes,aspect_ratio=yes,path=logos/{presentChannelName}.png;
+
+// Start und Endezeit des gewaehlten Titels oben links
+ text text={EventTitle} - {EventSubtitle},x=10,y=115,width=770,height=40,dots=1;
+ text text={EventStartTime/%a %d.%m} - {EventStartTime/%H:%M}-{EventEndTime/%H:%M},x=10,y=155,width=780,height=40;
+
+// Progressbar
+// Timebar x=10,y=170,width=620,height=16,path=symbols/progress.png,bg_alpha=150;
+ Timebar x=10,y=85,width=630,height=25,path=symbols/progress_yellow.png,bg_alpha=150;
+
+// EPG Detailinformationen
+ text text={EventDescription},x=10,y=195,size=20,width=770,height=230,color=200/200/200,dots=1;
+
+
+//***************************************************************************
+[MenuEpgsSchedule] // Epgsearch
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=350,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// Date
+ EventColumnSelected text={rowEventStartTime/%H:%M},x=5,width=120,size=30,spacing=20,focus=menu/menuCurrent_1.png,spacing=20,color=250/250/250;
+ EventColumn text={rowEventStartTime/%H:%M},x=10,width=120;
+
+// event
+ EventColumnSelected text={rowEventTitle},width=500,size=30,spacing=10,color=250/250/250,dots=1;
+ EventColumn text={rowEventTitle},spacing=10,dots=1;
+
+
+//***************************************************************************
+[MenuSchedule] // Standard
+//***************************************************************************
+
+ Include=MenuEpgsSchedule;
+
+
+//***************************************************************************
+[MenuEpgsWhatsOn_logo_time]
+//***************************************************************************
+
+ Include=MenuCommon;
+ Defaults menu_y=90,menu_height=350,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// Balken
+ EventColumnSelected text={},width=1,color=250/250/250,spacing=5;
+ EventColumn text={},width=1,spacing=5;
+
+// Balken
+ EventColumnSelected text={},width=1,color=250/250/250,focus=menu/menuCurrent_1.png,spacing=5;
+ EventColumn text={},width=1,spacing=5;
+
+// Sendername
+ EventColumnSelected text={rowEventChannelname},width=150,size=30,,spacing=20,color=250/250/250;
+ EventColumn text={rowEventChannelname},spacing=10,width=150;
+
+// Starttime
+ EventColumnSelected text={rowEventStartTime/%H:%M},width=110,size=30,,spacing=20,color=250/250/250;
+ EventColumn text={rowEventStartTime/%H:%M},width=110;
+
+
+//***************************************************************************
+[MenuEpgsWhatsOn_Event]
+//***************************************************************************
+
+ Defaults menu_y=90,menu_height=350,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// Eventtitel
+ EventColumnSelected text={rowEventTitle},x=400,size=30,width=395,spacing=20,color=250/250/250,dots=1;
+ EventColumn text={rowEventTitle},width=420,spacing=20,dots=1;
+
+
+//***************************************************************************
+[MenuEpgsWhatsOnElse]
+//***************************************************************************
+
+ Include=MenuEpgsWhatsOn_logo_time;
+
+ Defaults menu_y=70,height=66,menu_height=400,font=graphTFT,size=24,color=255/255/255,bg_color=0/0/0/0;
+
+ Include=MenuEpgsWhatsOn_Event;
+
+
+
+//***************************************************************************
+[MenuEpgsWhatsOnNow]
+//***************************************************************************
+
+ Include=MenuEpgsWhatsOn_logo_time;
+ Defaults menu_y=90,menu_height=350,font=graphTFT,size=24,color=200/200/200,bg_alpha=0;
+
+// Progressbar Short
+ EventColumnSelected type=progress,width=70,spacing=10,bar_height=40%,bg_color=0/0/0/255,path2=symbols/progress_yellow.png;
+ EventColumn type=progress,width=70,spacing=10,bar_height=40%,bg_color=0/0/0/255,path2=symbols/progress_yellow.png;
+
+
+ Include=MenuEpgsWhatsOn_Event;
+
+
+//***************************************************************************
+[MenuWhatsOnNow]
+//***************************************************************************
+
+ Include=MenuEpgsWhatsOnNow;
+
+
+//***************************************************************************
+[MenuEpgsWhatsOnNext]
+//***************************************************************************
+
+ Include=MenuEpgsWhatsOnElse;
+
+
+//***************************************************************************
+[MenuWhatsOnNext]
+//***************************************************************************
+
+ Include=MenuEpgsWhatsOnElse;
+
+//***************************************************************************
+// Dia VIEW
+//***************************************************************************
+[NormalDia]
+
+ Include=Volume;
+
+ Background path=backgrounds/bg-tv-images.png;
+ Defaults font=graphTFT,size=24,color=200/200/200,bg_color=0/0/0/0;
+
+// Textausgabe des Kanalnamens im Kopfbereich
+ Text text={presentChannelName},x=20,y=7,width=620,height=65,size=40,bg_alpha=2,align=left;
+
+// Timebar
+ Timebar x=10,y=85,width=630,height=25,path=symbols/progress_yellow.png,bg_alpha=150;
+
+// Aktuelle Sendung
+ Text text={presentStartTime/%H:%M},x=20,y=117,width=160,height=70,size=40,color=255/255/255;
+ Text text={presentTitle},x=195,y=117,bg_x=195,bg_y=117,bg_width=650,bg_height=80,width=590,height=80,size=40,dots=yes,color=255/255/255;
+ Image x=265,y=190,width=270,height=200,
+ path={varEPGimagesPath_1}{presentID}.jpg
+ :{varEPGimagesPath_2}{presentID}.jpg
+ :{varEPGimagesPath_3}{presentID}.jpg
+ :{varEPGimagesPath_4}{presentID}.jpg
+ :{varImagesPath_1}{presentTitle}.jpg,
+ fit=yes,aspect_ratio=yes; //EPG Bild
+
+// Iconleiste
+
+ Image x=0,y=400,width=800,height=78,path=backgrounds/button-bg-grey.png;
+
+ Image condition={channelHasVtx} = 0,x=30,y=400,width=80,height=78,path=symbols/vtxOff.png,fit=yes;
+ Image condition={channelHasVtx},x=30,y=400,width=80,height=78,path=symbols/vtxOn.png,fit=yes;
+ Image condition={channelHasDD} = 0, x=127,y=400,width=110,height=78,path=symbols/ddOff.png,fit=yes;
+ Image condition={channelHasDD},x=127,y=400,width=110,height=78,path=symbols/ddOn.png,fit=yes;
+ Image condition={channelIsEncrypted} = 0,x=390,y=400,width=110,height=78,path=symbols/cryptOff.png,fit=yes;
+ Image condition={channelIsEncrypted} = 1,x=390,y=400,width=110,height=78,path=symbols/cryptOn.png,fit=yes;
+ Image condition={actRecordingCount} = 0, x=530,y=400,width=110,height=78,path=symbols/recOff.png,fit=yes;
+ Image condition={actRecordingCount} > 0,x=530,y=400,width=110,height=78,path=symbols/recOn.png,fit=yes;
+ Image condition={channelHasMultilang} = 0, x=250,y=400,width=130,height=78,path=symbols/ch2Off.png,fit=yes;
+ Image condition={channelHasMultilang},x=250,y=400,width=130,height=78,path=symbols/ch2On.png,fit=yes;
+
+// Datum/Uhr unten rechts
+ Text text={time/%H:%M},x=630,y=409,size=40,color=255/255/255,align=left,width=160,height=60,bg_alpha=2;
+
+ Include=OSD-Messages;
diff --git a/themes/nOpacity.theme b/themes/nOpacity.theme
new file mode 100644
index 0000000..336a132
--- /dev/null
+++ b/themes/nOpacity.theme
@@ -0,0 +1,1276 @@
+//***************************************************************************
+// Rework of nOpacity Theme, based on the great work of Santos,Ckone!
+//
+// (c) 2014 Jörg Wendel
+//
+// This code is distributed under the terms and conditions of the
+// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+//***************************************************************************
+
+// -------------------------------------
+// set paths to fit your environment
+// -------------------------------------
+
+var chanLogos = "/usr/share/vdr-skins/logos4nOpacity";
+// var chanLogos = "/usr/share/vdr/plugins/skinnopacity/logos";
+
+var varEPGimagesPath_1 = "/var/cache/vdr/epgimages/";
+var varEPGimagesPath_2 = "/video0/epgimages/";
+
+// -------------------------------------
+// select color scheme
+// -------------------------------------
+
+// #define LIGHT_BLUE
+#define DARK_BLUE
+// #define DARK_BLUE_PLAIN
+
+// -------------------------------------
+// activate if you have the Hardware
+// -------------------------------------
+
+//#define WEATHER
+//#define P4
+//#define SYSINFO
+
+//***************************************************************************
+// Theme Style
+//***************************************************************************
+
+#ifdef DARK_BLUE
+
+ // ------------------------
+ // dark blue
+
+ var selColor = "255:255:255:255";
+ var unselColor = "200:200:200:255";
+ var menuTitleColor = "255:255:255:255";
+ var tdColor = "255:255:255:255";
+ var tdMainColor = "200:200:200:255";
+ var textColor = "180:180:180:255";
+ var lightColor = "255:255:255:255";
+ var signalColor = "255:55:55:255";
+ var boxColor = "160:160:160:255";
+ var ptLineColor = "100:150:250:255";
+ var titleColor = "255:255:255:255";
+ var subtitleColor = "255:255:255:255";
+ var descriptionColor = "238:232:180";
+
+ var symbols = "symbols";
+ var menuBackground = "menu.darkblue";
+ var backgrounds = "backgrounds.darkblue";
+
+#endif
+
+#ifdef DARK_BLUE_PLAIN
+
+ // ------------------------
+ // dark blue plain
+
+ var selColor = "255:255:255:255";
+ var unselColor = "200:200:200:255";
+ var menuTitleColor = "255:255:255:255";
+ var tdColor = "255:255:255:255";
+ var tdMainColor = "200:200:200:255";
+ var textColor = "180:180:180:255";
+ var lightColor = "255:255:255:255";
+ var signalColor = "255:55:55:255";
+ var boxColor = "160:160:160:255";
+ var ptLineColor = "100:150:250:255";
+ var titleColor = "255:255:255:255";
+ var subtitleColor = "255:255:255:255";
+ var descriptionColor = "200:200:200";
+
+ var symbols = "symbols";
+ var menuBackground = "menu.darkblue_plain";
+ var backgrounds = "backgrounds.darkblue_plain";
+
+#endif
+
+#ifdef LIGHT_BLUE
+
+ // ------------------------
+ // light gold-blue
+
+ var selColor = "0:0:0:255";
+ var unselColor = "200:200:200:255";
+ var menuTitleColor = "255:203:12:255";
+ var tdColor = "250:200:20:255";
+ var tdMainColor = "200:200:200:255";
+ var textColor = "200:200:200:255";
+ var lightColor = "255:203:12:255";'
+ var signalColor = "255:55:55:255";
+ var boxColor = "255:203:12:255";
+ var ptLineColor = "100:150:250:255";
+ var titleColor = "255:255:255:255";
+ var subtitleColor = "255:255:255:255";
+ var descriptionColor = "255:236:139";
+
+ var symbols = "symbols"
+ var backgrounds = "backgrounds.lightblue"
+ var menuBackground = "menu.lightblue"
+
+#endif
+
+[Theme]
+
+ Theme name=nOpacity,dir=nOpacity,
+ width=1360,height=768,
+ themeVersion=0.1.0,
+ syntaxVersion=0.4.1,
+ fontPath=./fonts,
+ startImage=backgrounds/start.png,
+ endImage=backgrounds/end.png;
+
+//***************************************************************************
+// OSD Message
+//***************************************************************************
+
+[OSDMessage]
+
+ Message y=625,height=80,bg_x=20,bg_y=620,bg_height=90,bg_width=1300,fit=yes,align=center,size=42,
+ path={backgrounds}/bg-message-green.png;
+
+//***************************************************************************
+// Volume
+//***************************************************************************
+
+[Volume]
+
+ Defaults font=graphTFT;
+
+ Volumebar condition={volumeMute} = 0,x=40,y=385,width=1280,height=110,switch=yes,bg_red=100,bg_green=100,bg_blue=100,permanent=no,delay=3;
+ Volumebar condition={volumeMute} = 0,x=30,y=400,width=1300,bg_x=30,bg_y=260,bg_width=1300,path2=backgrounds/volume.png,permanent=no,delay=3;
+ Volumebar condition={volumeMute} = 0,text=percent,size=50,color=0:0:0,x=500,y=365,permanent=no,delay=3;
+
+ VolumeMuteSymbol x=1077,y=673,width=90,height=90,fit=yes,pathON={symbols}/mute_on.png,permanent=yes;
+
+//***************************************************************************
+// Normal TV/Radio View (svdrpsend PLUG graphtft VIEW Standard)
+//***************************************************************************
+
+[TV_Radio_Common]
+
+ Background path=backgrounds/bg-tv.png;
+ Defaults font=graphTFT,size=24,color={textColor},bg_color=20:110:220:0;
+
+#ifdef WEATHER
+ VariableFile name=w,file=/tmp/weather.txt,delay=10;
+ Text text=Aussen {w.varTime/%d. %b %H:%M},size=18,color={lightColor},x=1070,y=125,width=260,height=30,delay=10;
+ Text text={w.varTempKombiValue}°C / {w.varHumKombiValue}%,color={lightColor},size=28,x=1070,width=260,height=40,delay=10;
+ Text text={w.varWindKombiValue} m/s,color={lightColor},size=28,x=1070,width=140,height=40,delay=10;
+#endif
+
+#ifdef P4
+ VariableFile name=p4,file=/tmp/p4.txt,delay=10;
+ Text text=Heizung,color={lightColor},size=18,x=1070,width=110,height=30,delay=10;
+ Text text={p4.varPuffertemperaturunten_0x78Value}°C / {p4.varPuffertemperaturoben_0x76Value}°C,color={lightColor},size=28,x=1070,width=285,height=40,delay=10;
+ Text text={p4.varStatusText},color={lightColor},size=30,height=45,x=1070,width=285,dots=yes,delay=10;
+#endif
+
+#ifdef SYSINFO
+ VariableFile name=sys,file=/tmp/sysinfo.txt,delay=10;
+ Text text=Sysinfo {sys.varTime/%d. %b %H:%M},size=18,color={lightColor},x=1070,y=125,width=260,height=30;
+ Text text=CPU Temp: {sys.varCPUTempKombiValue} °C,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=GPU Temp: {sys.varGPUTempKombiValue} °C,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=PCH Temp: {sys.varCHIPTempKombiValue} °C,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=CPU: {sys.varCPUBusyKombiValue} %,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=RAM frei: {sys.varMemFreeKombiValue} MB,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=Disk frei: {sys.varVideoFreeKombiValue} GB,color={lightColor},size=18,x=1070,width=260,height=28;
+ Text text=Kernel: {sys.varKernelKombiValue},color={lightColor},size=18,x=1070,width=260,height=28;
+#endif
+
+ // Kanalnummer Kanalnamen oben
+
+ Text debug=channelname_number,condition="{channelGroup}" = "",x=5,y=0,text={presentChannelNumber} {presentChannelName},size=40,align=left,width=1050,
+ height=60,color={menuTitleColor},on_click=Schedule Yellow;
+
+ Text debug=channelname_number,condition="{channelGroup}" != "",x=5,y=0,text={channelGroup},size=40,align=center,width=1050,
+ height=60,color={descriptionColor},on_click=Schedule Yellow;
+
+ // Timebar oben
+
+ Timebar x=6,y=66,width=1049,height=14,path={symbols}/progress.png,
+ bg_x=2,bg_y=64,bg_height=18,bg_width=1056,bg_color=105:105:105:255;
+
+ // Senderlogo Logo oben rechts
+
+ Image debug=channellogo,x=1152,y=0,width=190,height=115,bg_x=1140,bg_y=2,bg_width=205,bg_height=120,fit=yes,aspect_ratio=no,
+ path={chanLogos}/{presentChannelName/lower}.png,path2=symbols/channellogoback.png,on_click=Schedule;
+
+ // Aktuelle Sendung
+
+ Text debug=pstarttime,text={presentStartTime/%H:%M},x=5,y=85,width=165,height=65,size=42,color={lightColor};
+ Text debug=premaining,text={presentRemaining}',x=175,y=105,width=85,height=45,size=26,delay=60,color={lightColor};
+
+ Text debug=ptitle,text={presentTitle},id=pTitle,x=260,y=82,width=800,height=295,size=42,color={titleColor},on_click=Schedule Info;
+ Text debug=psubtitel,text={presentSubtitle},id=pSubTitle,x=260,y=82+{pTitle.height},height=295-{pTitle.height},color={subtitleColor},width=800,size=32;
+ Text debug=pdescription,text={presentDescription},id=pDesc,x=260,y=82+{pTitle.height}+{pSubTitle.height},
+ height=295-{pTitle.height}-{pSubTitle.height},color={descriptionColor},width=800,size=26,dots=yes;
+
+ // EPG Bild
+
+ Image debug=pepgimage,x=15,y=155,width=230,height=200,
+ path={varEPGimagesPath_1}{presentID}_(0-9).jpg:
+ {varEPGimagesPath_2}{presentID}.png,
+ fit=yes,aspect_ratio=yes,delay=5;
+
+ // Kommende Sendung
+
+ Text debug=fstarttime,text={followingStartTime/%H:%M},x=5,y=390,width=165,height=65,size=42,color={lightColor};
+ Text debug=fduration,text={followingDuration}',x=175,y=410,width=85,height=45,size=26,color={lightColor};
+
+ Text debug=ftitle,text={followingTitle},id=fTitle,x=260,y=385,width=800,height=295,size=42,color={titleColor},on_click=Schedule Yellow Yellow Info;
+ Text debug=sdubtitle,text={followingSubtitle},id=fSubTitle,x=260,y=385+{fTitle.height},height=295-{fTitle.height},width=800,size=32,color={subtitleColor};
+ Text debug=fdescription,text={followingDescription},id=fDesc,x=260,y=385+{fTitle.height}+{fSubTitle.height},
+ height=295-{fTitle.height}-{fSubTitle.height},color={descriptionColor},width=800,size=26,dots=yes;
+
+ // EPG Bild
+
+ Image debug=fepgimage,x=15,y=460,width=230,height=200,
+ path={varEPGimagesPath_1}{followingID}_(0-9).jpg:
+ {varEPGimagesPath_2}{followingID}.png,
+ fit=yes,aspect_ratio=yes,delay=5;
+
+ // Iconleiste
+
+ Image debug=iconbar,x=5,y=678,width=1060,height=90,fit=yes,aspect_ratio=no,path={backgrounds}/channelsymbols.png;
+
+ // Signal Quality
+
+ Text debug=strtext,text=STR,x=18,y=700,width=40,height=22,size=14,color={lightColor},overlay=yes;
+ Progressbar debug=strbar,total=100,value={STR/%},x=73,y=705,path={symbols}/progress.png,width=290,height=12,bg_x=68,bg_y=702,
+ bg_height=18,bg_width=298,bg_color=0:0:0:255;
+
+ Text debug=snrtext,text=SNR,x=18,y=727,width=40,height=22,size=14,color={lightColor},overlay=yes;
+ Progressbar debug=snrbar,total=100,value={SNR/%},x=73,y=733,path={symbols}/progress.png,width=290,height=12,bg_x=68,bg_y=730,
+ bg_height=18,bg_width=298,bg_color=0:0:0:255;
+
+ // Status Icons
+
+ Image debug=HasVtx,condition={channelHasVtx} = 0,x=391,y=688,width=75,height=70,path={symbols}/vtxOff.png,fit=yes,overlay=yes;
+ Image debug=HasVtx,condition={channelHasVtx},x=391,y=688,width=75,height=70,path={symbols}/vtxOn.png,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD} = 0, x=487,y=688,width=75,height=70,path={symbols}/ddOff.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD},x=487,y=688,width=75,height=70,path={symbols}/ddOn.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD} = 0, x=583,y=688,width=75,height=70,path={symbols}/ch2On.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD},x=583,y=688,width=75,height=70,path={symbols}/ch2Off.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasMultilang,condition={channelHasMultilang} = 0, x=679,y=688,width=75,height=70,path={symbols}/multilangOff.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasMultilang,condition={channelHasMultilang},x=679,y=688,width=75,height=70,path={symbols}/multilangOn.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=IsEncrypted,condition={channelIsEncrypted} = 0,x=775,y=688,width=75,height=70,path={symbols}/cryptOff.png,fit=yes,overlay=yes;
+ Image debug=IsEncrypted,condition={channelIsEncrypted} = 1,x=775,y=688,width=75,height=70,path={symbols}/cryptOn.png,fit=yes,overlay=yes;
+ Image debug=actRecordingCount,condition={actRecordingCount} = 0, x=871,y=688,width=75,height=70,path={symbols}/recOff.png,fit=yes,overlay=yes;
+ Image debug=actRecordingCount,condition={actRecordingCount} > 0,x=871,y=688,width=75,height=70,path={symbols}/rec_On.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} < 719,x=967,y=688,width=75,height=70,path={symbols}/sd576i.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} >= 719 & {videoSizeHeight} < 1080,x=967,y=688,width=75,height=70,path={symbols}/hd720p.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} >= 1080,x=967,y=688,width=75,height=70,path={symbols}/hd1080i.png,fit=yes,overlay=yes;
+
+ // running timer
+
+ TextList debug=runningTimer1,condition={actTimersRunning} = 1,id=rList,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M}@{actTimersTitle},lines=2,
+ x=1076,y=397,height=264,width=275, size=20,color={signalColor},path=backgrounds/listbg.png;
+
+ // pending timer
+
+ TextList debug=runningTimer2,condition={actTimersRunning} = 0,id=pList,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M}@{actTimersTitle},lines=2,
+ x=1076,y={rList.Height}+397,height=264-{rList.Height},width=275,size=20,overlay=yes;
+
+ // Datum/Uhr unten rechts
+
+ Text debug=date,text={time/%H:%M},x=1177,y=661,size=42,color={tdMainColor},align=left,width=165,height=60;
+ Text debug=timer,text={time/%a %d.%m},x=1180,y=724,size=26,color={tdMainColor},align=left,width=165,height=38;
+
+ // Separator
+
+ Rectangle debug=seperator,x=0,y=377,width=1060,height=4,color={boxColor};
+
+ Include=Volume;
+ Include=OSDMessage;
+
+//***************************************************************************
+// Non Live Tv
+//***************************************************************************
+
+[NormalNonLiveTv]
+
+ Background path={backgrounds}/bg-menu-epg-info.png;
+ Defaults font=graphTFT,size=40,color={textColor},bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+ // Titel
+
+ Text text=Timers,x=10,y=0,height=50,size=34,color={lightColor};
+
+ // Running Timer
+
+ Image x=0,y=60,width=1355,height=70,fit=yes,aspect_ratio=no,path={backgrounds}/channelsymbols.png;
+
+ Text text=Running,x=1,y=66,height=40,size=32,color={lightColor},align=center;
+
+ TextList condition={actTimersRunning} = 1,id=rList,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M} {actTimersTitle},
+ x=15,y=138,height=768 - 138 - 62 -5,width=1340,size=26,color={signalColor};
+
+ // Pending Timer
+
+ Image x=0,y={rList.Height} + 138 + 10,width=1355,height=70,fit=yes,aspect_ratio=no,path={backgrounds}/channelsymbols.png;
+
+ Text text=Pending,x=1,y={rList.Height} + 138 + 15,height=41,size=32,color={lightColor},align=center;
+
+ TextList condition={actTimersRunning} = 0,id=pList,bg_color=0:0:0:0,text={actTimersStart/%a %H:%M} - {actTimersStop/ %H:%M} {actTimersTitle},
+ x=15,y={rList.Height}+138+10+70+5,height=768-{rList.Height}-220-10-62-5,width=1340,size=26,color={textColor};
+
+ // Datum/Uhr unten rechts
+
+ Text text={time/%a %d.%m} {time/%H:%M} ,x=5,y=706,width=1350,size=40,color={tdColor},align=right;
+
+ // Aufnahmesymbol
+
+ Image condition={actRecordingCount} > 0, x=5,y=705,path={symbols}/rec_On.png,permanent=no,delay=5;
+
+ Include=Volume;
+ Include=OSDMessage;
+
+//***************************************************************************
+// TV
+//***************************************************************************
+
+[NormalTV]
+
+ Include=TV_Radio_Common;
+
+//***************************************************************************
+// Radio
+//***************************************************************************
+
+[NormalRadio]
+
+ Include=TV_Radio_Common;
+
+//***************************************************************************
+// TV
+//***************************************************************************
+
+[NormalTV2]
+
+ Background path=backgrounds/bg-tv.png;
+ Defaults font=graphTFT,size=24,color={textColor},bg_color=20:110:220:0;
+
+ // Kanalnummer Kanalnamen oben
+
+ Text debug=channelname_number,condition="{channelGroup}" = "",x=5,y=0,text={presentChannelNumber} {presentChannelName},size=40,align=left,width=1050,
+ height=60,color={menuTitleColor},on_click=Schedule Yellow;
+
+ Text debug=channelname_number,condition="{channelGroup}" != "",x=5,y=0,text={channelGroup},size=40,align=left,width=1050,
+ height=60,color={descriptionColor},on_click=Schedule Yellow;
+
+ // Timebar oben
+
+ Timebar x=6,y=66,width=1049,height=14,path={symbols}/progress.png,
+ bg_x=2,bg_y=64,bg_height=18,bg_width=1056,bg_color=105:105:105:255;
+
+ // Senderlogo Logo oben rechts
+
+ Image debug=channellogo,x=1152,y=0,width=190,height=115,bg_x=1140,bg_y=2,bg_width=205,bg_height=120,fit=yes,aspect_ratio=no,
+ path={chanLogos}/{presentChannelName/lower}.png,path2=symbols/channellogoback.png,on_click=Schedule;
+
+ // Aktuelle Sendung
+
+ Text debug=pstarttime,text={presentStartTime/%H:%M},x=5,y=85,width=165,height=65,size=42,color={lightColor};
+ Text debug=premaining,text={presentRemaining}',x=175,y=105,width=85,height=45,size=26,delay=60,color={lightColor};
+
+ Text debug=ptitle,text={presentTitle},id=pTitle,x=260,y=82,width=800,height=295,size=42,color={titleColor},on_click=Schedule Info;
+ Text debug=psubtitel,text={presentSubtitle},id=pSubTitle,x=260,y=82+{pTitle.height},height=295-{pTitle.height},color={subtitleColor},width=800,size=32;
+ Text debug=pdescription,text={presentDescription},id=pDesc,x=260,y=82+{pTitle.height}+{pSubTitle.height},
+ height=295-{pTitle.height}-{pSubTitle.height},color={descriptionColor},width=800,size=26,dots=yes;
+
+ // EPG Bild
+
+ Image debug=pepgimage,x=15,y=155,width=230,height=200,
+ path={varEPGimagesPath_1}{presentID}_(0-9).jpg:
+ {varEPGimagesPath_2}{presentID}.png,
+ fit=yes,aspect_ratio=yes,delay=5;
+
+ // Banner
+
+ Image debug=pepgimage,x=15,y=380,width=1300,height=290,
+ path={presentBanner},fit=yes,aspect_ratio=yes;
+
+ // Iconleiste
+
+ Image debug=iconbar,x=5,y=678,width=1060,height=90,fit=yes,aspect_ratio=no,path={backgrounds}/channelsymbols.png;
+
+ // Signal Quality
+
+ Text debug=strtext,text=STR,x=18,y=700,width=40,height=22,size=14,color={lightColor},overlay=yes;
+ Progressbar debug=strbar,total=100,value={STR/%},x=73,y=705,path={symbols}/progress.png,width=290,height=12,bg_x=68,bg_y=702,
+ bg_height=18,bg_width=298,bg_color=0:0:0:255;
+
+ Text debug=snrtext,text=SNR,x=18,y=727,width=40,height=22,size=14,color={lightColor},overlay=yes;
+ Progressbar debug=snrbar,total=100,value={SNR/%},x=73,y=733,path={symbols}/progress.png,width=290,height=12,bg_x=68,bg_y=730,
+ bg_height=18,bg_width=298,bg_color=0:0:0:255;
+
+ // Status Icons
+
+ Image debug=HasVtx,condition={channelHasVtx} = 0,x=391,y=688,width=75,height=70,path={symbols}/vtxOff.png,fit=yes,overlay=yes;
+ Image debug=HasVtx,condition={channelHasVtx},x=391,y=688,width=75,height=70,path={symbols}/vtxOn.png,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD} = 0, x=487,y=688,width=75,height=70,path={symbols}/ddOff.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD},x=487,y=688,width=75,height=70,path={symbols}/ddOn.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD} = 0, x=583,y=688,width=75,height=70,path={symbols}/ch2On.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasDD,condition={channelHasDD},x=583,y=688,width=75,height=70,path={symbols}/ch2Off.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasMultilang,condition={channelHasMultilang} = 0, x=679,y=688,width=75,height=70,path={symbols}/multilangOff.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=HasMultilang,condition={channelHasMultilang},x=679,y=688,width=75,height=70,path={symbols}/multilangOn.png,on_click=Audio,fit=yes,overlay=yes;
+ Image debug=IsEncrypted,condition={channelIsEncrypted} = 0,x=775,y=688,width=75,height=70,path={symbols}/cryptOff.png,fit=yes,overlay=yes;
+ Image debug=IsEncrypted,condition={channelIsEncrypted} = 1,x=775,y=688,width=75,height=70,path={symbols}/cryptOn.png,fit=yes,overlay=yes;
+ Image debug=actRecordingCount,condition={actRecordingCount} = 0, x=871,y=688,width=75,height=70,path={symbols}/recOff.png,fit=yes,overlay=yes;
+ Image debug=actRecordingCount,condition={actRecordingCount} > 0,x=871,y=688,width=75,height=70,path={symbols}/rec_On.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} < 719,x=967,y=688,width=75,height=70,path={symbols}/sd576i.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} >= 719 & {videoSizeHeight} < 1080,x=967,y=688,width=75,height=70,path={symbols}/hd720p.png,fit=yes,overlay=yes;
+ Image debug=videoSizeHeight,condition={videoSizeHeight} >= 1080,x=967,y=688,width=75,height=70,path={symbols}/hd1080i.png,fit=yes,overlay=yes;
+
+ // Datum/Uhr unten rechts
+
+ Text debug=date,text={time/%H:%M},x=1177,y=661,size=42,color={tdMainColor},align=left,width=165,height=60;
+ Text debug=timer,text={time/%a %d.%m},x=1180,y=724,size=26,color={tdMainColor},align=left,width=165,height=38;
+
+ // Separator
+
+ Rectangle debug=seperator,x=0,y=377,width=1060,height=4,color={boxColor};
+
+ Include=Volume;
+ Include=OSDMessage;
+
+//***************************************************************************
+// Normal Replay
+//***************************************************************************
+
+[ReplayNormal]
+
+ Background path=backgrounds/bg-replay.png;
+
+ Defaults font=graphTFT,size=40,color={textColor},bg_red=0,bg_green=0,bg_blue=0,bg_alpha=0;
+
+#ifdef WEATHER
+ VariableFile name=p4,file=/tmp/p4.txt,delay=10;
+ Text text=Aussen {w.varTime/%d. %b %H:%M},color={lightColor},size=18,x=1076,y=270,width=300,height=30,delay=10;
+ Text text={w.varTempKombiValue}°C / {w.varHumKombiValue}%,color={lightColor},size=28,x=1076,width=280,height=40,delay=10;
+ Text text={w.varWindKombiValue} m/s,color={lightColor},size=28,x=1076,width=275,height=40,delay=10;
+#endif
+
+#ifdef P4
+ VariableFile name=w,file=/tmp/weather.txt,delay=10;
+ Text text=Heizung,size=18,x=1076,color={lightColor},width=120,height=30,delay=10;
+ Text text={p4.varPuffertemperaturunten_0x78Value}°C / {p4.varPuffertemperaturoben_0x76Value}°C,color={lightColor},size=28,x=1076,width=300,height=40,delay=10;
+ Text text={p4.varStatusText},color={lightColor},size=28,height=40,x=1076,width=350,delay=10;
+#endif
+
+ // Mode symbol
+
+ Image condition={replayForward} = 1 & {replaySpeed} = -1 & {replayPlay} = 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/play.png; // play
+ Image condition={replayForward} = 1 & {replaySpeed} = -1 & {replayPlay} < 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/pause.png; // pause
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} = 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/frew.png; // Fast Rewind
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} = 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/ffwd.png; // Fast Forward
+ Image condition={replayForward} = 0 & {replaySpeed} > 0 & {replayPlay} < 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/srew.png; // Slow Rewind
+ Image condition={replayForward} = 1 & {replaySpeed} > 0 & {replayPlay} < 1,x=1220,y=590,width=62,height=62,fit=yes,path={symbols}/sfwd.png; // Slow Forward
+
+ // Ausgabe recording info
+
+ Text text={replayTitle},id=rTitle,x=10,y=5,size=40,width=1000,height=690,color={lightColor};
+ Text text={replaySubtitle},id=rSubTitle,x=10,width=1000,y=5+{rTitle.height},height=690-{rTitle.height},size=30;
+
+ var varStartLine = 0;
+ Text text={replayDescription},start_line={varStartLine},size=22,whipe_res=20,on_up=varStartLine--,on_down=varStartLine++,
+ x=10,width=1020,y=5+{rTitle.height}+{rSubTitle.height},height=690-{rTitle.height}-{rSubTitle.height},color={descriptionColor},dots=yes;
+
+ // Event. Bild ausgeben.
+
+ Image x=1045,y=10,width=300,height=250,
+ path={replayPath}/thumbnail_(0-9).jpg
+ :{replayPath}/{replayEventId}_(0-9).jpg,
+ fit=yes,delay=5,aspect_ratio=yes;
+
+ // Datum/Uhr unten rechts
+
+ Text text={time/%H:%M},x=1177,y=661,size=42,color={tdMainColor},align=left,width=165,height=60;
+ Text text={time/%a %d.%m},x=1180,y=724,size=26,color={tdMainColor},align=left,width=165,height=38;
+
+ // Laufzeiten links und rechts unten & Vortschrittbalken
+
+ Text text={replayCurrent/%k:%M:%S},x=5,y=710,color={lightColor},width=190,size=32,height=55,align=left,delay=1;
+ Text text={replayTotal/%k:%M:%S},x=805,y=710,color={lightColor},width=190,size=32,height=55,align=right;
+
+ // Progressbar
+
+ Progressbar total={replayTotal/%s},value={replayCurrent/%s},x=200,y=725,width=600,height=22,
+ path={symbols}/progress.png,bg_x=198,bg_y=721,bg_height=26,bg_width=602,
+ bg_color=105:105:105:255;
+
+ Include=Volume;
+ Include=OSDMessage;
+
+//***************************************************************************
+// Menu Common
+//***************************************************************************
+
+[MenuCommon]
+
+ Defaults font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ // Menü Titel
+
+ Text text={menuTitle},x=100,y=10,width=650,size=42,height=83,lines=1,align=left,dots=yes,on_click=Ok;
+
+ // Datum/Uhr oben rechts
+
+// Text text={time/%a %d.%m - %H:%M},x=750,y=15,color={tdColor},size=32,align=right,width=600,align=right,height=40;
+ Text text={time/%a %d.%m - %H:%M},x=750, y=2,color={tdColor}, align=right,width=600,align=right,height=40;
+
+ Include=ColorButtons;
+ Include=OSDMessage;
+
+[ColorButtons]
+
+ MenuButtonBackgroundRed x=25,y=710,width=300,height=50,pathON={symbols}/button-red.png,fit=yes,on_click=Red;
+ MenuButtonRed x=25,y=717,width=300,size=24,height=25,red=200,green=200,blue=200,alpha=255,align=center;
+ MenuButtonBackgroundGreen x=361,y=710,width=300,height=50,pathON={symbols}/button-green.png,fit=yes,on_click=Green;
+ MenuButtonGreen x=361,y=717,width=300,size=24,height=25,red=200,green=200,blue=200,alpha=255,align=center;
+ MenuButtonBackgroundYellow x=698,y=710,width=300,height=50,pathON={symbols}/button-yellow.png,fit=yes,on_click=Yellow;
+ MenuButtonYellow x=698,y=717,width=300,size=24,height=25,red=200,green=200,blue=200,alpha=255,align=center;
+ MenuButtonBackgroundBlue x=1035,y=710,width=300,height=50,pathON={symbols}/button-blue.png,fit=yes,on_click=Blue;
+ MenuButtonBlue x=1035,y=717,width=300,size=24,height=25,red=200,green=200,blue=200,alpha=255,align=center;
+
+[ImageMap]
+
+ MenuImageMap file=menulogos/epgsearch.png,name=Programm;
+ MenuImageMap file=menulogos/chanman.png,name=Kanäle;
+ MenuImageMap file=menulogos/timers.png,name=Timer;
+ MenuImageMap file=menulogos/extrecmenu.png,name=Video;
+ MenuImageMap file=menulogos/cdplayer.png,name=Audio;
+ MenuImageMap file=menulogos/audio.png,name=Squeezebox;
+ MenuImageMap file=menulogos/screenshot.png,name=Bildschirmfoto;
+ MenuImageMap file=menulogos/lcdproc.png,name=Applikationen;
+ MenuImageMap file=menulogos/systeminfo.png,name=System;
+ MenuImageMap file=menulogos/tvm2vdr.png,name=epg handler;
+ MenuImageMap file=menulogos/graphtft.png,name=Graph-TFTng;
+ MenuImageMap file=menulogos/seduatmo.png,name=Seduatmo;
+ MenuImageMap file=menulogos/remote.png,name=ZapPilot;
+ MenuImageMap file=menulogos/fritzbox.png,name=Fritz!Box;
+ MenuImageMap file=menulogos/tvguide.png,name=Tvguide;
+ MenuImageMap file=menulogos/extrecmenu.png,name=Aufzeichnungen;
+ MenuImageMap file=menulogos/markad.png,name=markad Status;
+ MenuImageMap file=menulogos/cdplayer.png,name=Musicplayer HD;
+ MenuImageMap file=menulogos/radio.png,name=Zeige RDS-Radiotext;
+ MenuImageMap file=menulogos/imonlcd.png,name=PIP;
+ MenuImageMap file=menulogos/iptv.png,name=Web;
+ MenuImageMap file=menulogos/cdplayer.png,name=Medien;
+ MenuImageMap file=menulogos/avahi4vdr.png,name=Spiele;
+ MenuImageMap file=menulogos/systeminfo.png,name=Dienstprogramme;
+ MenuImageMap file=menulogos/epgsync.png,name=Befehle;
+ MenuImageMap file=menulogos/systeminfo.png,name=Einstellungen;
+ MenuImageMap file=menulogos/targavfd.png,name=Vorläufig auf den zweiten Bildschirm schalten;
+ MenuImageMap file=menulogos/femon.png,name=Signalinformationen;
+ MenuImageMap file=menulogos/systeminfo.png,name=Systeminformationen;
+ MenuImageMap file=menulogos/block.png,name=Childlock;
+ MenuImageMap file=menulogos/sleeptimer.png,name=Remote wakeup;
+
+//***************************************************************************
+// EPG Menu Common Part
+//***************************************************************************
+
+[MenuCommonEpg]
+
+ Defaults font=graphTFT,size=30,color={unselColor},bg_alpha=0;
+
+ // Menüname oben links
+
+ Text text={menuTitle},x=100,y=2,width=650,height=50,lines=1,align=left,dots=yes,on_click=Ok;
+
+ // Datum/Uhr oben rechts
+
+ Text text={time/%a %d.%m - %H:%M},x=750,y=2,color={tdColor},align=right,width=600,align=right,height=40;
+
+ Include=ColorButtons;
+ Include=OSDMessage;
+
+//***************************************************************************
+// Menu
+//***************************************************************************
+
+[MenuMain]
+
+ Include=ImageMap;
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Defaults menu_y=110,menu_height=590,height=80,font=graphTFT,size=42,color={unselColor},bg_alpha=0;
+
+ // Menü Titel
+
+ Image x=2,y=2,height=95,width=100,fit=yes,path=menulogos/logo_vdr.png;
+ Text text={menuTitle} {vdrVersion},x=130,y=10,width=1200,height=83,lines=1,align=left,dots=yes,on_click=Ok;
+
+ // Datum/Uhr oben rechts
+
+ Text text={time/%a %d.%m - %H:%M},x=750,y=15,color={tdColor},size=32,align=right,width=600,align=right,height=40;
+
+ // Menü
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=95%,fit=yes,path={menuBackground}/menuSelected_0.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=95%,fit=yes,path={menuBackground}/menuUnselected_0.png;
+
+ ColumnSelected number=1,x=15,type=image,width=75,bar_height=90%,fit=yes,path={imageMap},spacing=10;
+ Column number=1,x=15,type=image,width=75,bar_height=90%,fit=yes,path={imageMap},spacing=10;
+
+ ColumnSelected number=1,type=text,color={selColor};
+ Column number=1,type=text;
+
+ Include=ColorButtons;
+ Include=OSDMessage;
+
+//***************************************************************************
+// Menu
+//***************************************************************************
+
+[Menu]
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Include=MenuCommon;
+ Include=ImageMap;
+
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=34,color={unselColor},bg_alpha=0;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ // Menülogos auf fester Position
+
+ MenuSelected x=10,y=110,width=1300,height=590,color={selColor},
+ stat_pic=yes,stat_y=2,stat_x=2,stat_width=70,stat_height=70;
+ Menu x=10,y=110,width=1280,height=590;
+
+//***************************************************************************
+// Plugin Setup
+//***************************************************************************
+
+[MenuPluginSetup]
+
+ Background path={backgrounds}/bg-menu.png;
+ Include=MenuCommon;
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=34,color={unselColor},bg_alpha=0;
+
+ PartingLine x=10,align=center,width=1340,color={ptLineColor},fit=yes,path={menuBackground}/menuUnselected.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ ColumnSelected number=1,x=10,width=780,color={selColor};
+ Column number=1,x=10,width=780;
+
+ ColumnSelected number=2,color={selColor};
+ Column number=2;
+
+//***************************************************************************
+// Plugin Setup Page
+//***************************************************************************
+
+[MenuSetupPlugins]
+
+ Background path={backgrounds}/bg-menu.png;
+ Include=MenuCommon;
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=34,color={unselColor},bg_alpha=0;
+
+ PartingLine x=10,align=center,width=1340,color={ptLineColor},fit=yes,path={menuBackground}/menuUnselected.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ ColumnSelected number=1,x=10,width=1350,color={selColor};
+ Column number=1,x=10,width=1350;
+
+//***************************************************************************
+// Recordings Menu
+//***************************************************************************
+
+[MenuExtRecordings]
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Include=MenuCommon;
+
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ Image x=2,y=1,width=100,height=100,fit=yes,aspect_ratio=yes,path=menulogos/recordings.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ // symbol
+
+ ColumnSelected number=1,x=10,size=34,color={selColor},width=50,spacing=20;
+ Column number=1,x=10,width=50,spacing=20;
+
+ //
+
+ ColumnSelected condition={colCount} = 3,number=2,size=34,color={selColor},width=210;
+ Column condition={colCount} = 3,number=2,width=210;
+
+ ColumnSelected condition={colCount} = 3,number=3,size=34,color={selColor},width=180;
+ Column condition={colCount} = 3,number=3,width=180;
+
+ // count/date
+
+ ColumnSelected condition={colCount} > 3,number=2,size=34,color={selColor},width=210,spacing=20,align=right;
+ Column condition={colCount} > 3,number=2,width=210,spacing=20,align=right;
+
+ // name
+
+ ColumnSelected condition={colCount} > 3,number=6,size=34,color={selColor},width=1010,scroll=ticker,delay=500ms;
+ Column condition={colCount} > 3,number=6,width=1050,dots=yes;
+
+
+//***************************************************************************
+// Recording Menu
+//***************************************************************************
+
+[MenuExtRecording]
+
+ Background path={backgrounds}/bg-extrec.png;
+
+ Include=MenuCommon;
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ // Output Detailed-Information
+
+ Image x=2,y=1,width=100,height=100,fit=yes,aspect_ratio=yes,path=menulogos/recordings.png;
+
+ Text text={recordingTime/%a %d. %b %Y %H:%M},id=rTime,x=10,y=105,size=22,width=1110,height=45;
+ Text text={recordingTitle},id=rTitle,x=10,y={rTime.y}+{rTime.height},size=36,width=1110,height=545;
+ Text text={recordingSubtitle},id=rSubtitle,x=10,y={rTitle.y}+{rTitle.height},size=30,width=1110,height=545-{rTitle.height};
+
+ var varStartLine = 0;
+ Text text={recordingDescription},start_line={varStartLine},
+ whipe_res=20,on_up=varStartLine--,on_down=varStartLine++,color={descriptionColor},
+ x=10,y={rSubtitle.y}+{rSubtitle.height}+5,width=1340,
+ height=545-{rTitle.height}-{rSubtitle.height},size=20,dots=1;
+
+ // EPG. Bild ausgeben.
+
+ Image x=1130,y=100,width=220,height=200,
+ path={recordingPath}/thumbnail_(0-9).jpg
+ :{recordingPath}/{replayEventId}_(0-9).jpg
+ :{symbols}/video.png,fit=yes,aspect_ratio=yes,delay=5;
+
+ Include=ColorButtons;
+
+//***************************************************************************
+// Recordings Menu
+//***************************************************************************
+
+[MenuRecordings]
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Include=MenuCommon;
+
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ Image x=2,y=1,width=100,height=100,fit=yes,aspect_ratio=yes,path=menulogos/recordings.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ // Count or Date
+
+ ColumnSelected number=1,x=10,size=34,color={selColor},width=220;
+ Column number=1,x=10,width=220;
+
+ // Time
+
+ ColumnSelected condition={colCount} > 3,number=2,size=34,color={selColor},width=160;
+ Column condition={colCount} > 3,number=2,width=160;
+
+ // unseen or duration
+
+ ColumnSelected number=3,size=34,color={selColor},width=160;
+ Column number=3,width=160;
+
+ // name
+
+ ColumnSelected number=4,size=34,color={selColor},width=810,dots=yes;
+ Column number=4,width=810,dots=yes;
+
+//***************************************************************************
+// Menu Recording
+//***************************************************************************
+
+[MenuRecording]
+
+ Include=MenuExtRecording;
+
+//***************************************************************************
+// Channels
+//***************************************************************************
+
+[MenuChannels]
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Include=MenuCommon;
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ Image x=2,y=1,width=100,height=100,fit=yes,aspect_ratio=yes,path=menulogos/channels.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ ColumnSelected number=1,x=10,size=32,width=120,color={selColor};
+ Column number=1,x=10,width=120;
+
+ ColumnSelected number=2,x=85,size=32,color={selColor},scroll=ticker,delay=500ms;
+ Column number=2,x=75;
+
+//***************************************************************************
+// Timers Menu
+//***************************************************************************
+
+[MenuTimers]
+
+ Background path={backgrounds}/bg-menu.png;
+
+ Include=MenuCommon;
+
+ Defaults menu_y=110,menu_height=590,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ Image x=2,y=1,width=100,height=100,fit=yes,aspect_ratio=yes,path=menulogos/timers.png;
+
+ ColumnSelected number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuSelected.png;
+ Column number=0,x=5,type=image,width=1350,bar_height=90%,fit=yes,path={menuBackground}/menuUnselected.png;
+
+ // symbol
+
+ ColumnSelected number=1,x=10,size=34,color={selColor},width=40;
+ Column number=1,x=10,width=40;
+
+ // vdr
+
+ ColumnSelected condition={colCount} = 7,number=2,size=34,color={selColor},width=140;
+ Column condition={colCount} = 7,number=2,width=140;
+
+ // channel
+
+ ColumnSelected condition={colCount} = 6,number=2,size=34,color={selColor},width=90;
+ Column condition={colCount} = 6,number=2,width=90;
+
+ ColumnSelected condition={colCount} = 7,number=3,size=34,color={selColor},width=90;
+ Column condition={colCount} = 7,number=3,width=90;
+
+ // day
+
+ ColumnSelected condition={colCount} = 6,number=3,size=34,color={selColor},width=180;
+ Column condition={colCount} = 6,number=3,width=180;
+
+ ColumnSelected condition={colCount} = 7,number=4,size=34,color={selColor},width=180;
+ Column condition={colCount} = 7,number=4,width=180;
+
+ // time start
+
+ ColumnSelected condition={colCount} = 6,number=4size=34,color={selColor},width=150;
+ Column condition={colCount} = 6,number=4,width=150;
+
+ ColumnSelected condition={colCount} = 7,number=5size=34,color={selColor},width=150;
+ Column condition={colCount} = 7,number=5,width=150;
+
+ // time stop
+
+ ColumnSelected condition={colCount} = 6,number=5,size=34,color={selColor},width=150;
+ Column condition={colCount} = 6,number=5,width=150;
+
+ // name
+
+ ColumnSelected condition={colCount} = 6,number=6,size=34,color={selColor},dots=yes;
+ Column condition={colCount} = 6,number=6,dots=yes;
+
+ ColumnSelected condition={colCount} = 7,number=7,size=34,color={selColor},dots=yes;
+ Column condition={colCount} = 7,number=7,dots=yes;
+
+//***************************************************************************
+// Invent Details
+//***************************************************************************
+
+[MenuEvent]
+
+ Background path={backgrounds}/bg-menu-epg-info.png;
+
+ Include=MenuCommonEpg;
+
+ Defaults menu_y=52,menu_height=645,font=graphTFT,size=32,color={unselColor},bg_alpha=0;
+
+ // Senderlogo Logo oben links
+
+ Image x=2,y=0,width=90,height=60,fit=yes,aspect_ratio=yes,path={chanLogos}/{eventChannelName/lower}.png;
+
+ // Title, Subtitle, Start und Endezeit
+
+ text text={EventTitle},id=eTitle,x=10,y=62,size=40,width=1120,height=210,color={lightColor};
+ text text={EventSubtitle},id=eSubTitle,x=10,y={eTitle.y}+{eTitle.height},width=1120,size=30,height=210-{eTitle.height},dots=yes;
+
+ text text={EventStartTime/%a %d.%m.%Y} {EventStartTime/%H:%M} - {EventEndTime/%H:%M},id=eTime,x=10,
+ y={eSubTitle.y}+{eSubTitle.height},size=28,width=600,height=210-{eTitle.height}-{eSubTitle.height};
+
+ Text text=Progress: {eventElapsed}' / {eventDuration}',id=ePro,x=10,y={eTime.y}+{eTime.height},width=1120,
+ height=65,size=26;
+
+ Rectangle x=10,y={ePro.y}+{ePro.height}+5,
+ width=1340,height=4,color={boxColor};
+
+ // EPG Bild
+
+ Image x=1130,y=62,width=210,height=200,
+ path={varEPGimagesPath_1}{eventID}_(0-9).jpg:
+ {varEPGimagesPath_2}{eventID}.png,
+ fit=yes,delay=5,aspect_ratio=yes;
+
+ // EPG Detailinformationen
+
+ var varStartLine = 0;
+
+ text text={EventDescription},color={descriptionColor},x=10,
+ y=62+5+{eTitle.height}+{eSubTitle.height}+{eTime.height}+{ePro.height}+10,
+ size=24,width=1340,height=420,
+ start_line={varStartLine},whipe_res=20,on_up=varStartLine--,on_down=varStartLine++,
+ dots=1;
+
+//***************************************************************************
+// Right Area of EPG Menu
+//***************************************************************************
+
+[MenuEpgsRight]
+
+ Defaults menu_x=0,menu_y=70,menu_height=635,height=87,font=graphTFT,size=19,color={unselColor},bg_alpha=150;
+
+ // Titel / Subtitel des 'selected' rechts ausgeben
+
+ Text text={selectedRowEventTitle},id=eiTitle,x=437,y=60,width=690,height=635,size=35,color={lightColor},on_click=Ok;
+ Text text={selectedRowEventSubtitle},id=eiSubtitle,x=437,y={eiTitle.y}+{eiTitle.height},width=690,height=635-{eiTitle.height},
+ size=28,color={lightColor};
+
+ // description
+
+ var varStartLine = 0;
+
+ Text text={selectedRowEventDescription},id=eiDesc,start_line={varStartLine},color={descriptionColor},whipe_res=20,on_up=varStartLine--,on_down=varStartLine++,
+ x=437,y={eiSubtitle.y}+{eiSubtitle.height},width=910,height=635-{eiTitle.height}-{eiSubtitle.height}-5,size=24,align=left;
+
+ // EPG Bild
+
+ Image x=1130,y=65,width=210,height=200,
+ path={varEPGimagesPath_1}{selectedRowEventID}_(0-9).jpg:
+ {varEPGimagesPath_2}{selectedRowEventID}.png,
+ fit=yes,aspect_ratio=yes;
+
+//***************************************************************************
+// Epgsearch - Laufendes Programm
+//***************************************************************************
+
+[MenuEpgsSchedule]
+
+ Background path={backgrounds}/bg-menu-epg.png;
+
+ Include=MenuCommonEpg;
+
+ Defaults menu_x=0,menu_y=70,menu_height=635,height=87,font=graphTFT,size=19,color={unselColor},bg_alpha=0;
+
+ // Menu Navigation Area
+
+ MenuNavigationArea y=110,x=0,width=425,on_dblclick=Ok;
+
+ // parting line
+
+ PartingLine x=2,width=423,bar_height=95%,fit=yes,size=34,color={ptLineColor},path={menuBackground}/menuUnselected_2.png,align=center,align_v=yes;
+
+ // Senderlogo Logo oben links
+
+ Image x=2,y=0,width=90,height=70,fit=yes,aspect_ratio=yes,path={chanLogos}/{rowEventChannelName/lower}.png;
+
+ // Image als Menü Hintergrund
+
+ EventColumnSelected type=image,x=2,bar_height=95%,width=423,fit=yes,path={menuBackground}/menuSelected_2.png,spacing=5;
+ EventColumn type=image,x=2,bar_height=95%,width=423,fit=yes,path={menuBackground}/menuUnselected_2.png,width=1,spacing=5;
+
+ // Date
+
+ EventColumnSelected text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},menu_y=74,x=8,width=420,lines=1,size=20,spacing=20,color={selColor};
+ EventColumn text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},menu_y=74,x=8,width=420,lines=1,spacing=20;
+
+ // Title
+
+ EventColumnSelected text={rowEventTitle},menu_y=99,x=8,width=420,size=20,spacing=10,lines=1,color={selColor},dots=yes;
+ EventColumn text={rowEventTitle},menu_y=99,x=8,spacing=10,width=420,lines=1,dots=yes;
+
+ // Subtitle
+
+ EventColumnSelected text={rowEventSubtitle},menu_y=126,x=8,width=420,size=16,lines=1,color={selColor},dots=yes;
+ EventColumn text={rowEventSubtitle},menu_y=126,x=8,width=420,size=16,width=410,lines=1,dots=yes;
+
+ Include=MenuEpgsRight;
+
+//***************************************************************************
+// Laufendes Programm
+//***************************************************************************
+
+[MenuSchedule]
+
+ Include=MenuEpgsSchedule;
+
+//***************************************************************************
+// EpgSearch - Next
+//***************************************************************************
+
+[MenuEpgsWhatsOnElse]
+
+ Background path={backgrounds}/bg-menu-epg.png;
+
+ Include=MenuCommonEpg;
+
+ Defaults menu_y=70,menu_height=635,height=87,font=graphTFT,size=19,color={unselColor},bg_alpha=0;
+
+ // Menu Navigation Area
+
+ MenuNavigationArea y=110,x=0,width=425,on_dblclick=Ok;
+
+ // parting line
+
+ PartingLine x=2,width=423,bar_height=95%,fit=yes,size=34,color={ptLineColor},path={menuBackground}/menuUnselected_2.png,align=center,align_v=yes;
+
+ // EPG Logo oben links
+
+ Image x=10,y=2,width=60,height=60,fit=yes,aspect_ratio=yes,path=menulogos/schedule.png,on_click=Menu;
+
+ // Image als Menü Hintergrund
+
+ EventColumnSelected type=image,x=2,width=423,bar_height=95%,fit=yes,color={selColor},path={menuBackground}/menuSelected_2.png;
+ EventColumn type=image,x=2,width=423,bar_height=95%,fit=yes,path={menuBackground}/menuUnselected_2.png;
+
+ // Senderlogo
+
+ EventColumnSelected type=image,width=85,x=8,fit=yes,bar_height=95%,spacing=10,path={chanLogos}/{rowEventChannelname/lower}.png;
+ EventColumn type=image,width=85,x=8,fit=yes,bar_height=95%,spacing=10,path={chanLogos}/{rowEventChannelname/lower}.png;
+
+ // Starttime - Endtime
+
+ EventColumnSelected text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},lines=1,menu_y=74,x=102,width=200,size=21,color={selColor};
+ EventColumn text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},lines=1,menu_y=74,x=102,width=200;
+
+ // Title
+
+ EventColumnSelected text={rowEventTitle},menu_y=100,x=102,lines=1,size=20,width=308,color={selColor},dots=yes;
+ EventColumn text={rowEventTitle},menu_y=100,x=102,lines=1,width=312,dots=1;
+
+ // Subtitle
+
+ EventColumnSelected text={rowEventSubtitle},menu_y=125,x=102,lines=1,width=308,size=16,lines=1,color={selColor},dots=yes;
+ EventColumn text={rowEventSubtitle},menu_y=125,x=102,lines=1,width=312,size=16,lines=1,dots=yes;
+
+ Include=MenuEpgsRight;
+
+//**************************************************************************
+// EpgSearch - Now
+//***************************************************************************
+
+[MenuEpgsWhatsOnNow]
+
+ Background path={backgrounds}/bg-menu-epg.png;
+
+ Include=MenuCommonEpg;
+
+ Defaults menu_y=70,menu_height=635,height=87,font=graphTFT,size=19,color={unselColor},bg_alpha=0;
+
+ // Menu Navigation Area
+
+ MenuNavigationArea y=110,x=0,width=425,on_dblclick=Ok;
+
+ // EPG Logo oben links
+
+ Image x=10,y=2,width=60,height=60,fit=yes,aspect_ratio=yes,path=menulogos/schedule.png,on_click=Menu;
+
+ // parting line
+
+ PartingLine x=2,width=423,bar_height=95%,menu_y=80,fit=yes,size=34,color={ptLineColor},path={menuBackground}/menuUnselected_2.png,align=center,align_v=yes;
+
+ // Image als Menü Hintergrund
+
+ EventColumnSelected type=image,x=2,width=423,bar_height=95%,fit=yes,color={selColor},path={menuBackground}/menuSelected_2.png;
+ EventColumn type=image,x=2,width=423,bar_height=95%,fit=yes,path={menuBackground}/menuUnselected_2.png;
+
+ // Senderlogo
+
+ EventColumnSelected type=image,width=94,x=4,fit=yes,bar_height=97%,spacing=10,path={chanLogos}/{rowEventChannelname/lower}.png,aspect_ratio=yes;
+ EventColumn type=image,width=94,x=4,fit=yes,bar_height=97%,spacing=10,path={chanLogos}/{rowEventChannelname/lower}.png,aspect_ratio=yes;
+
+ // Starttime - Endtime
+
+ EventColumnSelected text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},menu_y=74,lines=1,x=102,width=200,size=21,color={selColor};
+ EventColumn text={rowEventStartTime/%H:%M} - {rowEventEndTime/%H:%M},menu_y=74,lines=1,x=102,width=200;
+
+ // Title
+
+ EventColumnSelected text={rowEventTitle},menu_y=100,x=102,lines=1,size=20,width=308,color={selColor},dots=yes;
+ EventColumn text={rowEventTitle},menu_y=100,x=102,lines=1,width=312,dots=yes;
+
+ // Progressbar
+
+ EventColumnSelected type=progress,menu_y=95,x=102,width=317,bar_height=15%,path={symbols}/progress_grey.png;
+ EventColumn type=progress,menu_y=95,x=102,width=317,bar_height=15%,path={symbols}/progress_grey.png;
+
+ EventColumnSelected type=progress,menu_y=95,x=106,width=310,bar_height=10%,bg_x=106,bg_width=310,
+ bg_color=105:105:105:255,path={symbols}/progress.png;
+ EventColumn type=progress,menu_y=95,x=106,width=310,bar_height=10%,bg_x=102,bg_width=310,
+ bg_color=105:105:105:255,path={symbols}/progress.png;
+
+ Include=MenuEpgsRight;
+
+//***************************************************************************
+//
+//***************************************************************************
+
+[MenuWhatsOnNow]
+
+ Include=MenuEpgsWhatsOnNow;
+
+[MenuEpgsFavorites]
+
+ Include=MenuEpgsWhatsOnElse;
+
+[MenuEpgsWhatsOnNext]
+
+ Include=MenuEpgsWhatsOnElse;
+
+[MenuWhatsOnNext]
+
+ Include=MenuEpgsWhatsOnElse;
+
+//***************************************************************************
+// Dial Show
+//***************************************************************************
+
+[NormalDia]
+
+ // Ausgabe eines Bildes oder, mittels Script, einer Bilderserie auf dem Display
+ // Diese Funktion wird über das graphTFT OSD-Menu oder "svdrpsend.pl plug graphtft VIEW Dia" aufgerufen
+
+ ImageFile x=0,y=0,width=1360,height=768,path=/tmp/dia.file,path2={symbols}/nocover.png,fit=yes,aspect_ratio=yes,rotate=1,delay=3;
+ Include=OSDMessage;
+
+#ifdef WEATHER
+
+//***************************************************************************
+// Daten der Wetterstation
+//***************************************************************************
+
+[NormalWeather]
+
+ Defaults font=graphTFT,size=40,color={lightColor},bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255;
+
+ Text text={presentStartTime/%H:%M},x=0,y=0,width=160,height=65;
+ Text text={presentTitle},x=160,y=0,width=940,height=65,align=center,dots=yes;
+ Text text={time/%d. %H:%M},x=1100,y=0,align=right,width=260,height=65;
+
+ Image x=0,y=65,width=1360,height=702,fit=yes,path=/tmp/chg_wde1.jpg,delay=4;
+
+ Include=OSDMessage;
+
+#endif
+
+//***************************************************************************
+// Daten der P4 Heizung
+//***************************************************************************
+
+#ifdef P4
+
+[NormalP4]
+
+ Defaults font=graphTFT,size=40,color={lightColor},bg_red=0,bg_green=0,bg_blue=0,bg_alpha=255;
+
+ Text text={presentStartTime/%H:%M},x=0,y=0,width=160,height=65;
+ Text text={presentTitle},x=160,y=0,width=940,height=65,align=center,dots=yes;
+ Text text={time/%d. %H:%M},x=1100,y=0,align=right,width=260,height=65;
+
+ Image x=0,y=65,width=1360,height=702,fit=yes,path=/tmp/chg_p4.jpg;
+
+ Include=OSDMessage;
+
+[NormalP4Data]
+
+ Background path=p4/bg-p4.png;
+
+ Defaults font=graphTFT,size=20,height=30,red=0,green=0,blue=0,
+ bg_red=255,bg_green=255,bg_blue=255,bg_alpha=0;
+
+ VariableFile name=p4,file=/tmp/p4.txt,delay=10;
+
+ var varAussenTemp = 0;
+ var varPufferOben = 0;
+ var varPufferUnten = 0;
+ var varKesselTemp = 0;
+ var varAbgasTemp = 0;
+ var varPellets = 0;
+
+ // Kessel
+
+ Text text={p4.varAbgastemperatur_0x1Value} °C,x=20,y=240,width=120,on_click=varAbgasTemp:1:0;
+
+ Image x=146,y=335,width=20,height=20,path=p4/ID_10-on.gif;
+ Text text={p4.varSaugzugdrehzahl_0x7Value},x=20,y=325,width=120;
+ Text text=U/min,x=20,y=355,width=120,size=16;
+
+ Text text={p4.varStatusText},size=30,height=40,x=62,y=60,width=350;
+ Text condition={p4.varFehlerText} != "Kein Fehler",text={p4.varFehlerText} h,
+ size=30,height=40,color={signalColor},x=62,y=120,size=24,height=35,width=250;
+
+ Text text={p4.varKesselstellgre_0x12Value}%,x=120,y=440,width=120;
+ Text text={p4.varKesseltemperatur_0x0Value} °C,x=75,y=590,width=120,on_click=varKesselTemp:1:0;
+
+ Text text={p4.varBetriebsstunden_0x62Value} h,x=75,y=630,width=125;
+
+ // Pellet
+
+ Text text={p4.varFllstandimPelletsbehlter_0x71Value}%,x=240,y=360,width=130;
+ Progressbar total=100,value={p4.varFllstandimPelletsbehlter_0x71Value/%s},text=percent,x=220,y=350,
+ red=255,green=255,blue=255,
+ width=120,height=80,path=p4/p4-pellets.jpg,on_click=varPellets:1:0;
+
+ // Heizkreise
+
+ Image condition={p4.varHeizkreispumpe_0x0Value} = 1,x=675,y=241,width=20,height=20,path=p4/p4-pump-on.png;
+ Image condition={p4.varHeizkreispumpe_0x0Value} = 0,x=675,y=241,width=20,height=20,path=p4/p4-pump-off.png;
+ Text text={p4.varVorlaufIsttemperatur_0x15Value} °C,x=590,y=20,width=120;
+
+ Image condition={p4.varHeizkreispumpe_0x1Value} = 1,x=780,y=241,width=20,height=20,path=p4/p4-pump-on.png;
+ Image condition={p4.varHeizkreispumpe_0x1Value} = 0,x=780,y=241,width=20,height=20,path=p4/p4-pump-off.png;
+ Text text={p4.varVorlaufIsttemperatur_0x19Value} °C,x=910,y=20,width=120;
+
+ // Puffer
+
+ Text text={p4.varPuffertemperaturoben_0x76Value} °C,x=1125,y=230,width=120,on_click=varPufferOben:1:0;
+ Text text={p4.varPuffertemperaturunten_0x78Value} °C,x=1125,y=610,width=120,on_click=varPufferUnten:1:0;
+ Text text={p4.varPufferpumpenAnsteuerung_0x8cValue}%,x=620,y=590,width=110;
+ Image condition={p4.varPufferpumpenAnsteuerung_0x8cValue} > 0,x=625,y=620,width=20,height=20,path=p4/p4-pump-on.png;
+ Image condition={p4.varPufferpumpenAnsteuerung_0x8cValue} = 0,x=625,y=620,width=20,height=20,path=p4/p4-pump-off.png;
+
+ // Aussen
+
+ Text text=Außen,x=1225,y=15,width=110,size=16;
+ Text text={p4.varAuentemperatur_0x4Value} °C,x=1225,y=40,width=110,on_click=varAussenTemp:1:0;
+
+ // Detail Charts
+
+ Image condition={varPufferOben},x=900,y=200,fit=yes,width=400,height=250,path=/tmp/chg_p4-pufferoben.jpg,on_click=varPufferOben:1:0;
+ Image condition={varKesselTemp},x=60,y=480,fit=yes,width=400,height=250,path=/tmp/chg_p4-abgastemp.jpg,on_click=varKesselTemp:1:0;
+ Image condition={varAbgasTemp},x=10,y=200,fit=yes,width=400,height=250,path=/tmp/chg_p4-abgastemp.jpg,on_click=varAbgasTemp:1:0;
+ Image condition={varPufferUnten},x=900,y=500,fit=yes,width=400,height=250,path=/tmp/chg_p4-pufferunten.jpg,on_click=varPufferUnten:1:0;
+ Image condition={varAussenTemp},x=800,y=15,fit=yes,width=400,height=250,path=/tmp/chg_p4-aussentemp.jpg,on_click=varAussenTemp:1:0;
+ Image condition={varPellets},x=200,y=300,fit=yes,width=400,height=250,path=/tmp/chg_p4-pellets.jpg,on_click=varPellets:1:0;
+
+ Text text=Akualisiert: {p4.varTime/%d.%m %H:%M},size=16,x=10,y=743,width=275,height=25;
+
+ Include=OSDMessage;
+
+#endif
diff --git a/touchthread.c b/touchthread.c
new file mode 100644
index 0000000..179e4a6
--- /dev/null
+++ b/touchthread.c
@@ -0,0 +1,322 @@
+/*
+ * touchthread.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ * (c) 2007-2008 Jörg Wendel
+ *
+ * Date: 14.11.2008
+ *
+ * The touch device driver is taken from the
+ * touchTFT plugin written by (c) Frank Simon
+ *
+ */
+
+//***************************************************************************
+// Includes
+//***************************************************************************
+
+#include <linux/input.h>
+#include <sys/time.h>
+#include <string.h>
+#include <errno.h>
+
+#include <unistd.h>
+
+#include <vdr/remote.h>
+#include <vdr/tools.h>
+
+#include "touchthread.h"
+#include "setup.h"
+#include "display.h"
+
+//***************************************************************************
+// Class Touch Thread
+//***************************************************************************
+
+cTouchThread::cTouchThread(void* aDisplay)
+ : cThread("TouchTFT-Thread")
+{
+ display = aDisplay;
+ handle = na;
+ calibrate = no;
+ touchDevice = 0;
+};
+
+cTouchThread::~cTouchThread()
+{
+ Stop();
+ close();
+
+ if (touchDevice) free(touchDevice);
+}
+
+//***************************************************************************
+// Reset Settings
+//***************************************************************************
+
+void cTouchThread::resetSetting(int swap)
+{
+ settings.offsetX = 0;
+ settings.offsetY = 0;
+ settings.scaleX = 1.0;
+ settings.scaleY = 1.0;
+ settings.swapXY = swap;
+ settings.scaleWidth = Thms::theTheme->getWidth();
+ settings.scaleHeight = Thms::theTheme->getHeight();
+}
+
+//***************************************************************************
+// Stop the Thread
+//***************************************************************************
+
+void cTouchThread::Stop()
+{
+ Cancel(3);
+}
+
+//***************************************************************************
+// Device
+//***************************************************************************
+
+void cTouchThread::setDevice(const char* aDevice, int doOpen)
+{
+ if (Str::isEmpty(aDevice))
+ return ;
+
+ if (!touchDevice || strcmp(touchDevice, GraphTFTSetup.touchDevice) != 0)
+ {
+ if (touchDevice) free(touchDevice);
+
+ touchDevice = strdup(GraphTFTSetup.touchDevice);
+
+ if (doOpen)
+ {
+ Cancel(3);
+ close();
+
+ if (open() == success)
+ Start();
+ }
+ }
+}
+
+//***************************************************************************
+// Open / Close
+//***************************************************************************
+
+int cTouchThread::open()
+{
+ if (Str::isEmpty(touchDevice))
+ {
+ tell(0, "Can't open touch device, now device configured");
+ return fail;
+ }
+
+ // open the device ..
+
+ handle = ::open(touchDevice, O_RDWR);
+
+ if (handle < 0)
+ {
+ tell(0, "Error: Opening device '%s' failed, errno was (%d) '%s'",
+ touchDevice, errno, strerror(errno));
+
+ handle = na;
+
+ return fail;
+ }
+
+ tell(0, "Open touch device '%s'", touchDevice);
+
+ return success;
+}
+
+int cTouchThread::close()
+{
+ // wait for Thread exit
+
+ if (isOpen())
+ ::close(handle);
+
+ handle = na;
+ tell(0, "Device '%s' closed", touchDevice);
+
+ return done;
+}
+
+//***************************************************************************
+// Action
+//***************************************************************************
+
+void cTouchThread::Action(void)
+{
+ struct input_event touchdata[sizeBuffer];
+
+ int x = 0;
+ int y = 0;
+ int r = 0;
+ int num = 0;
+ bool touched = no;
+
+ open();
+
+ while (Running())
+ {
+ if (!cFile::FileReady(handle, 100))
+ continue ;
+
+ // read
+
+ r = safe_read(handle, touchdata, sizeof(input_event) * sizeBuffer);
+
+ if (r < 0)
+ {
+ tell(0, "Fatal: Touch device read failed, errno (%d) '%s'",
+ errno, strerror(errno));
+
+ sleep(10);
+ continue ;
+ }
+
+ num = r / sizeof(input_event);
+
+ for (int i = 0; i < num; i++)
+ {
+ switch (touchdata[i].type)
+ {
+ case EV_ABS:
+ {
+ // received a coordinate
+
+ if ((touchdata[i].code == 0 && !settings.swapXY)
+ || (touchdata[i].code != 0 && settings.swapXY))
+ y = touchdata[i].value;
+ else
+ x = touchdata[i].value;
+
+ tell(3, "ABS: (%d/%d)", x, y);
+
+ break;
+ }
+ case EV_KEY:
+ {
+ if (touchdata[i].code == BTN_TOUCH // BTN_TOUCHED
+ || touchdata[i].code == BTN_LEFT) // BTN_LEFT
+ {
+ tell(3, "KEY: value (%d) code (%d)",
+ touchdata[i].value, touchdata[i].code);
+
+ touched = touchdata[i].value != 0;
+ }
+
+ break;
+ }
+
+ case EV_SYN:
+ {
+ // data complete
+
+ if (touched)
+ tell(3, "SYN: (%d/%d) touched (%d)", x, y, touched);
+ else
+ tell(0, "SYN: (%d/%d) released (%d)", x, y, touched);
+
+ processEvent(touched, x, y);
+
+ break;
+ }
+ }
+ }
+ }
+
+ close();
+
+ isyslog("GraphTFT plugin touch-thread thread ended (pid=%d)", getpid());
+ tell(0, "Touch thread ended");
+}
+
+//***************************************************************************
+// Rescale to actual theme size
+//***************************************************************************
+
+void cTouchThread::rescale()
+{
+ if (settings.scaleWidth == Thms::theTheme->getWidth())
+ return ;
+
+ settings.scaleX = (settings.scaleX * (double)Thms::theTheme->getWidth())
+ / settings.scaleWidth;
+ settings.scaleY = (settings.scaleY * (double)Thms::theTheme->getHeight())
+ / settings.scaleHeight;
+
+ settings.scaleWidth = Thms::theTheme->getWidth();
+ settings.scaleHeight = Thms::theTheme->getHeight();
+
+ tell(0, "Rescaled touch calibration to scale values (%f/%f)",
+ settings.scaleX, settings.scaleY);
+
+ ((cGraphTFTDisplay*)display)->calibration.settings = settings;
+ GraphTFTSetup.touchSettings = settings;
+ GraphTFTSetup.Store();
+}
+
+//***************************************************************************
+// Process Event
+//***************************************************************************
+
+void cTouchThread::processEvent(bool touch, int x, int y)
+{
+ static int lastX = 0;
+ static int lastY = 0;
+ static uint64_t lastTouchRelease = 0;
+
+ int flag = 0;
+
+ // check if rescale is required ...
+
+ if (!calibrate && settings.scaleWidth != Thms::theTheme->getWidth())
+ rescale();
+
+ // ignore 'press' events, we triggerd only on 'release' events
+ // -> touch is 0 on release
+
+ if (!touch)
+ {
+ // bounce check
+
+ if (lastTouchRelease + bounceTime <= cTimeMs::Now())
+ {
+ // scale only if not in calibration mode !
+
+ if (!calibrate)
+ {
+ int _x = x;
+ int _y = y;
+
+ x = (int)(((double)(_x + settings.offsetX)) * settings.scaleX);
+ y = (int)(((double)(_y + settings.offsetY)) * settings.scaleY);
+
+ tell(2, "Touch scaled from (%d/%d) to (%d/%d)",
+ _x, _y, x, y);
+ }
+
+ if (cTimeMs::Now() - lastTouchRelease < doubleClickTime
+ && abs(lastX-x) < 20
+ && abs(lastY-y) < 20)
+ {
+ tell(0, "Assuming double-click");
+ flag |= cGraphTftComService::efDoubleClick;
+ }
+
+ ((cGraphTFTDisplay*)display)->mouseEvent(x, y,
+ cGraphTftComService::mbLeft,
+ flag);
+
+ // notice last values
+
+ lastX = x;
+ lastY = y;
+ lastTouchRelease = cTimeMs::Now();
+ }
+ }
+}
diff --git a/touchthread.h b/touchthread.h
new file mode 100644
index 0000000..8c24711
--- /dev/null
+++ b/touchthread.h
@@ -0,0 +1,87 @@
+/*
+ * touchthread.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ * (c) 2007-2008 Jörg Wendel
+ *
+ * Date: 14.11.2008
+ *
+ * The touch device driver is taken from the
+ * touchTFT plugin written by (c) Frank Simon
+ *
+ */
+
+#ifndef _TOUCHTHREAD_H_
+#define _TOUCHTHREAD_H_
+
+#include <vdr/thread.h>
+
+#include <common.h>
+
+//***************************************************************************
+// Class Touch Thread
+//***************************************************************************
+
+class cTouchThread : public cThread
+{
+ public:
+
+ // definitions
+
+ enum Misc
+ {
+ sizeBuffer = 32,
+ bounceTime = 30, // milli Seconds (0.03 sec)
+ doubleClickTime = 500 // milli Seconds (0.5 sec)
+ };
+
+ struct CalibrationSetting
+ {
+ int swapXY;
+ double scaleX;
+ double scaleY;
+ int offsetX;
+ int offsetY;
+ int scaleWidth;
+ int scaleHeight;
+ };
+
+ // object
+
+ cTouchThread(void* aDisplay);
+ virtual ~cTouchThread();
+
+ // frame
+
+ void Stop();
+ void Action(void);
+
+ // interface
+
+ void setCalibrate(int state) { calibrate = state; }
+ int getCalibrate() { return calibrate; }
+ void setSetting(CalibrationSetting* aSet) { settings = *aSet; }
+ void resetSetting(int swap = no);
+ CalibrationSetting* getSettings() { return &settings; }
+ void setDevice(const char* aDevice, int doOpen = no);
+
+ int open();
+ int close();
+ int isOpen() { return handle > 0; }
+
+ protected:
+
+ void processEvent(bool touch, int x, int y);
+ void rescale();
+
+ // data
+
+ CalibrationSetting settings;
+ int handle;
+ void* display;
+ int calibrate;
+ char* touchDevice;
+};
+
+//***************************************************************************
+#endif // _TOUCHTHREAD_H_
diff --git a/vlookup.c b/vlookup.c
new file mode 100644
index 0000000..0aa84a9
--- /dev/null
+++ b/vlookup.c
@@ -0,0 +1,798 @@
+/*
+ * GraphTFT plugin for the Video Disk Recorder
+ *
+ * vlookup.c
+ *
+ * (c) 2007-2015 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 <display.h>
+#include <scraper2vdr.h>
+
+//***************************************************************************
+// Copied vorm VDR code!
+//***************************************************************************
+
+#define FULLMATCH 1000
+
+eTimerMatch Matches(const cTimer* ti, const cEventCopy* Event)
+{
+ if (ti->HasFlags(tfActive) && ti->Channel()->GetChannelID() == Event->ChannelID())
+ {
+ int overlap = 0;
+ bool UseVps = ti->HasFlags(tfVps) && Event->Vps();
+
+ if (UseVps)
+ overlap = (ti->StartTime() == Event->Vps()) ? FULLMATCH + (Event->IsRunning() ? 200 : 100) : 0;
+
+ if (!overlap)
+ {
+ if (ti->StartTime() <= Event->StartTime() && Event->EndTime() <= ti->StopTime())
+ overlap = FULLMATCH;
+ else if (ti->StopTime() <= Event->StartTime() || Event->EndTime() <= ti->StartTime())
+ overlap = 0;
+ else
+ overlap = (min(ti->StopTime(), Event->EndTime()) - max(ti->StartTime(), Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
+ }
+
+ if (UseVps)
+ return overlap > FULLMATCH ? tmFull : tmNone;
+
+ return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
+ }
+
+ return tmNone;
+}
+
+#if VDRVERSNUM < 10733
+ enum eTimerMatch { tmNone, tmPartial, tmFull };
+#endif
+
+const cTimer* getTimerMatch(const cTimers* timers, const cEventCopy* event, eTimerMatch* Match)
+{
+ const cTimer* t = 0;
+ eTimerMatch m = tmNone;
+
+ for (const cTimer* ti = timers->First(); ti; ti = timers->Next(ti))
+ {
+ eTimerMatch tm = Matches(ti, event);
+
+ if (tm > m)
+ {
+ t = ti;
+ m = tm;
+
+ if (m == tmFull)
+ break;
+ }
+ }
+
+ if (Match)
+ *Match = m;
+
+ return t;
+}
+
+//***************************************************************************
+// Variable of Group
+//***************************************************************************
+
+const char* variableGroup(const char* value, const char* group, const char*& var)
+{
+ if (!group)
+ return 0;
+
+ if (strncasecmp(value, group, strlen(group)) == 0)
+ {
+ var = value + strlen(group);
+ return group;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+// Lookup Variable
+//***************************************************************************
+
+int cDisplayItem::lookupVariable(const char* name, string& value, const char* fmt)
+{
+ const char* p;
+ int status;
+
+ value = "";
+ p = variable(name, fmt, status);
+
+ if (p)
+ {
+ value = p;
+ return success;
+ }
+
+ if (status == success)
+ return fail;
+
+ // nothing found ... -> check theme for global variable
+
+ if (cThemeItem::lookupVariable(name, value, fmt) == success)
+ {
+ if (fmt && (strcasestr(name, "TIME") || strcasestr(name, "DATE")))
+ {
+ char buf[100];
+
+ if (formatDateTime(atol(value.c_str()), fmt, buf, sizeof(buf)))
+ value = buf;
+ }
+
+ return success;
+ }
+
+ // variable unknown :(
+
+ tell(0, "Unexpected variable '%s'", name);
+
+ return fail;
+}
+
+//***************************************************************************
+// Variable
+//***************************************************************************
+
+const char* cDisplayItem::variable(const char* name, const char* fmt, int& status)
+{
+ static char buf[1000];
+ const char* p;
+ int total, current;
+ const char* var;
+ const char* group;
+
+ status = success;
+
+ if ((var = strchr(name, '.')))
+ {
+ cDisplayItem* item = 0;
+ char id[50+TB];
+
+ snprintf(id, 50, "%.*s", (int)(var-name), name);
+ var++;
+
+ if (!(item = section->getItemById(id)))
+ {
+ // no item with this id found -> should be a reference to a variable file,
+ // will processed by 'cThemeItem::lookupVariable'
+
+ status = fail;
+
+ return 0;
+ }
+
+ tell(4, "found item '%s' (%s), lookup '%s'", item->Id().c_str(), name, var);
+
+ if (strcasecmp(var, "X") == 0)
+ return Str::toStr(item->lastX);
+ if (strcasecmp(var, "Y") == 0)
+ return Str::toStr(item->lastY);
+ if (strcasecmp(var, "Width") == 0)
+ return Str::toStr(item->lastWidth);
+ if (strcasecmp(var, "Height") == 0)
+ return Str::toStr(item->lastHeight);
+ else
+ tell(0, "Unexpected theme variable %s in %s", var, name);
+
+ return "0";
+ }
+
+ if ((group = variableGroup(name, "music", var)))
+ {
+ p = musicVariable(group, var, fmt, status);
+
+ if (status == success)
+ return p;
+ }
+
+ else if ((group = variableGroup(name, "recording", var))
+ || (group = variableGroup(name, "replay", var))
+ || (group = variableGroup(name, "rowRecording", var))
+ || (group = variableGroup(name, "selectedRowRecording", var)))
+ {
+ const cRecording* recording = 0;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cRecordingsLock recordingsLock(false);
+ const cRecordings* recordings = recordingsLock.Recordings();
+#else
+ cRecordings* recordings = &Recordings;
+#endif
+
+ if (strcmp(group, "recording") == 0)
+ recording = recordings->GetByName(vdrStatus->_recording.c_str());
+ else if (strcmp(group, "replay") == 0)
+ recording = recordings->GetByName(vdrStatus->_replay.fileName.c_str());
+ else if (strcmp(group, "rowRecording") == 0)
+ recording = vdrStatus->_menu.drawingRow == na ?
+ 0 : vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].recording;
+ else if (strcmp(group, "selectedRowRecording") == 0)
+ recording = vdrStatus->_menu.currentRow == na ?
+ 0 : vdrStatus->_menu.items[vdrStatus->_menu.currentRow].recording;
+
+ // recording variables
+
+ if (!recording && strcmp(group, "replay") != 0)
+ {
+ tell(0, "Missing '%s' info, can't lookup variable for '%s'",
+ name, vdrStatus->_recording.c_str());
+
+ return 0;
+ }
+
+ if (vdrStatus->_replay.control)
+ {
+ if (strcasecmp(var, "Speed") == 0)
+ return Str::toStr(replayModeValue(rmSpeed));
+ else if (strcasecmp(var, "Play") == 0)
+ return Str::toStr(replayModeValue(rmPlay));
+ else if (strcasecmp(var, "Forward") == 0)
+ return Str::toStr(replayModeValue(rmForward));
+ else if (strcasecmp(var, "Current") == 0)
+ {
+ vdrStatus->_replay.control->GetIndex(current, total);
+ return formatDateTime((current ? current : 1) / (int)vdrStatus->_replay.control->FramesPerSecond(),
+ fmt, buf, sizeof(buf), yes);
+ }
+ else if (strcasecmp(var, "Total") == 0)
+ {
+ vdrStatus->_replay.control->GetIndex(current, total);
+ return formatDateTime((total ? total : 1) / (int)vdrStatus->_replay.control->FramesPerSecond(),
+ fmt, buf, sizeof(buf), yes);
+ }
+ else if (strcasecmp(var, "RawCurrent") == 0)
+ {
+ vdrStatus->_replay.control->GetIndex(current, total);
+ return Str::toStr(current);
+ }
+ else if (strcasecmp(var, "RawTotal") == 0)
+ {
+ vdrStatus->_replay.control->GetIndex(current, total);
+ return Str::toStr(total);
+ }
+ }
+
+ if (recording)
+ {
+ if (strcasecmp(var, "Title") == 0)
+ return formatString(Str::notNull(recording->Info()->Title()), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Path") == 0)
+ return formatString(Str::notNull(recording->FileName()), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Time") == 0)
+ return formatDateTime(recording->Start(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "EventID") == 0)
+ return Str::toStr(!recording->Info() ? na : (int)recording->Info()->GetEvent()->EventID());
+ else if (strcasecmp(var, "SubTitle") == 0)
+ return Str::notNull(recording->Info()->ShortText());
+ else if (strcasecmp(var, "Description") == 0)
+ return Str::notNull(recording->Info()->Description());
+ else if (strcasecmp(var, "Channel") == 0)
+ return formatString(Str::notNull(recording->Info()->ChannelName()), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Banner") == 0)
+ {
+ string mediaPath;
+ string posterPath;
+
+ if (getScraperMediaPath(0, recording, mediaPath, posterPath) == success)
+ tell(5, "Got banner path '%s'", mediaPath.c_str());
+
+ return mediaPath.c_str();
+ }
+ else if (strcasecmp(var, "Poster") == 0)
+ {
+ string mediaPath;
+ string posterPath;
+
+ if (getScraperMediaPath(0, recording, mediaPath, posterPath) == success)
+ tell(5, "Got poster path '%s'", posterPath.c_str());
+
+ return posterPath.c_str();
+ }
+ }
+
+ else
+ {
+ if (strcasecmp(var, "Title") == 0)
+ return formatString(Str::notNull(vdrStatus->_replay.name.c_str()), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "EventID") == 0)
+ return Str::toStr(na);
+ else if (strcasecmp(var, "SubTitle") == 0)
+ return "";
+ else if (strcasecmp(var, "Channel") == 0)
+ return "";
+ else if (strcasecmp(var, "Path") == 0)
+ return formatString(Str::notNull(vdrStatus->_replay.fileName.c_str()), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Time") == 0)
+ return "";
+ else if (strcasecmp(var, "Description") == 0)
+ return "no details available";
+ }
+ }
+
+ else if ((group = variableGroup(name, "volume", var)))
+ {
+ if (strcasecmp(var, "Mute") == 0)
+ return Str::toStr(vdrStatus->_mute);
+ else if (strcasecmp(var, "Level") == 0)
+ return Str::toStr(vdrStatus->_volume);
+ }
+
+ else if ((group = variableGroup(name, "event", var))
+ || (group = variableGroup(name, "following", var))
+ || (group = variableGroup(name, "present", var))
+ || (group = variableGroup(name, "rowEvent", var))
+ || (group = variableGroup(name, "selectedRowEvent", var)))
+ {
+ tell(5, "lookup variable '%s' of group '%s'", var, group);
+
+ eTimerMatch timerMatch = tmNone;
+ cEventCopy* event = 0;
+ const cChannel* channel = 0;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cChannelsLock channelsLock(false);
+ const cChannels* channels = channelsLock.Channels();
+#else
+ cChannels* channels = &Channels;
+#endif
+
+ if (strcmp(group, "event") == 0)
+ {
+ if (!vdrStatus->_event.isEmpty())
+ {
+ event = &vdrStatus->_event;
+ channel = channels->GetByChannelID(event->ChannelID());
+ }
+ }
+ else if (strcmp(group, "present") == 0)
+ {
+ if (!vdrStatus->_presentEvent.isEmpty())
+ event = &vdrStatus->_presentEvent;
+
+ channel = vdrStatus->_presentChannel;
+ }
+ else if (strcmp(group, "following") == 0)
+ {
+ if (!vdrStatus->_followingEvent.isEmpty())
+ event = &vdrStatus->_followingEvent;
+
+ channel = vdrStatus->_presentChannel;
+ }
+ else if (strcmp(group, "rowEvent") == 0)
+ {
+ if (vdrStatus->_menu.drawingRow != na)
+ event = &vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].event;
+ channel = vdrStatus->_menu.drawingRow == na ?
+ 0 : vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].channel;
+ }
+ else if (strcmp(group, "selectedRowEvent") == 0)
+ {
+ if (vdrStatus->_menu.currentRow != na)
+ event = &vdrStatus->_menu.items[vdrStatus->_menu.currentRow].event;
+
+ channel = vdrStatus->_menu.currentRow == na ?
+ 0 : vdrStatus->_menu.items[vdrStatus->_menu.currentRow].channel;
+ }
+
+ // this items don't need a channel
+
+ *buf = 0;
+
+ if (strcasecmp(var, "Title") == 0)
+ {
+ if (!event)
+ {
+ // No EPG Data available
+ // RDS is available only for presentTitle
+
+ if ((vdrStatus->_rds.text.empty()) ||
+ (strcmp(group, "present") != 0))
+ {
+ if (!channel)
+ return buf;
+
+ return channel->Name();
+ }
+ return vdrStatus->_rds.text.c_str();
+ }
+
+ return Str::notNull(event->Title());
+ }
+ else if (strcasecmp(var, "SubTitle") == 0)
+ {
+ if (!event)
+ {
+ // No EPG Data available
+ // RDS is available only for presentSubTitle
+
+ if (strcmp(group, "present") == 0)
+ {
+ if (vdrStatus->_rds.title.empty() &&
+ vdrStatus->_rds.artist.empty())
+ {
+ strcpy(buf, tr("No EPG data available."));
+ return buf;
+ }
+
+ sprintf(buf, " %s : %s\n%s : %s", tr("Title"),
+ vdrStatus->_rds.title.c_str(), tr("Artist"),
+ vdrStatus->_rds.artist.c_str());
+ }
+ return buf;
+ }
+
+ return Str::notNull(event->ShortText());
+ }
+ else if (strcasecmp(var, "Description") == 0)
+ {
+ if (!event)
+ return buf; // No EPG Data available
+
+ return Str::notNull(event->Description());
+ }
+
+ if (!channel || !channel->Number())
+ {
+ tell(2, "Info: Can't find channel for '%s' in '%s' of row %d",
+ group, var, vdrStatus->_menu.drawingRow);
+
+ return 0;
+ }
+
+ // this items don't need a event
+
+ if (strcasecmp(var, "ChannelName") == 0)
+ return formatString(channel ? channel->Name() : "", fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "ChannelId") == 0)
+ return !channel ? "" : strcpy(buf, channel->GetChannelID().ToString());
+ else if (strcasecmp(var, "ChannelNumber") == 0)
+ return Str::toStr(channel ? channel->Number() : 0);
+
+ // check the event
+
+ if (!event)
+ {
+ tell(1, "Info: Missing event for '%s', can't lookup variable in '%s'",
+ group, var);
+
+ return 0;
+ }
+
+ // following items need a event
+
+ // --------------------------
+ // get timers lock
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cTimersLock timersLock(false);
+ const cTimers* timers = timersLock.Timers();
+#else
+ cTimers* timers = &Timers;
+#endif
+
+ const cTimer* timer = getTimerMatch(timers, event, &timerMatch);
+
+ if (strcasecmp(var, "ID") == 0)
+ return Str::toStr((int)event->EventID());
+ else if (strcasecmp(var, "StartTime") == 0)
+ return formatDateTime(event->StartTime(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "EndTime") == 0)
+ return formatDateTime(event->EndTime(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Duration") == 0)
+ return Str::toStr(event->Duration() / 60);
+ else if (strcasecmp(var, "HasTimer") == 0)
+ return timer && timerMatch == tmFull ? "1" : "0";
+ else if (strcasecmp(var, "HasPartialTimer") == 0)
+ return timer && timerMatch == tmPartial ? "1" : "0";
+ else if (strcasecmp(var, "HasPartialTimerBefore") == 0)
+ return timer && timerMatch == tmPartial &&
+ timer->StartTime() >= event->StartTime()
+ ? "1" : "0";
+ else if (strcasecmp(var, "HasPartialTimerAfter") == 0)
+ return timer && timerMatch == tmPartial &&
+ timer->StartTime() < event->StartTime()
+ ? "1" : "0";
+ else if (strcasecmp(var, "IsRunning") == 0)
+ return event->SeenWithin(30) && event->IsRunning() ? "1" : "0";
+ else if (strcasecmp(var, "Elapsed") == 0)
+ return Str::toStr(((int)(time(0)-event->StartTime()) / 60));
+ else if (strcasecmp(var, "Remaining") == 0)
+ {
+ if (time(0) < event->StartTime())
+ return Str::toStr(event->Duration() / 60);
+ else
+ return Str::toStr(((int)(event->EndTime()-time(0)) / 60));
+ }
+ else if (strcasecmp(var, "Progress") == 0)
+ {
+ if (time(0) > event->StartTime())
+ return Str::toStr((int)(100.0 / ((double)event->Duration() / (double)(time(0)-event->StartTime()))));
+ else
+ return Str::toStr((int)(100.0 / ((double)event->Duration() / (double)(event->StartTime()-time(0)))));
+ }
+ else if (strcasecmp(var, "IsRecording") == 0)
+ return timerMatch == tmFull && timer && timer->Recording() ? "1" : "0";
+ else if (strcasecmp(var, "Banner") == 0)
+ {
+ string mediaPath;
+ string posterPath;
+
+ if (getScraperMediaPath(event, 0, mediaPath, posterPath) == success)
+ tell(5, "Got banner path '%s'", mediaPath.c_str());
+
+ return mediaPath.c_str();
+ }
+ else if (strcasecmp(var, "Poster") == 0)
+ {
+ string mediaPath;
+ string posterPath;
+
+ if (getScraperMediaPath(event, 0, mediaPath, posterPath) == success)
+ tell(5, "Got poster path '%s'", posterPath.c_str());
+
+ return posterPath.c_str();
+ }
+ }
+
+ else
+ {
+ if (strcasecmp(name, "channelGroup") == 0)
+ return vdrStatus->_channelGroup.c_str();
+
+ if (strcasecmp(name, "menuTitle") == 0)
+ return vdrStatus->_menu.title.c_str();
+
+ if (strcasecmp(name, "colCount") == 0)
+ return Str::toStr(vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].tabCount);
+
+ if (strcasecmp(name, "rowCount") == 0)
+ return Str::toStr((int)vdrStatus->_menu.items.size());
+
+ if (strcasecmp(name, "visibleRows") == 0)
+ return Str::toStr(vdrStatus->_menu.visibleRows);
+
+ if (strcasecmp(name, "currentRow") == 0)
+ return Str::toStr(vdrStatus->_menu.currentRow);
+
+ if (strcasecmp(name, "touchMenu") == 0)
+ return Str::toStr(vdrStatus->touchMenu);
+
+ if (strcasecmp(name, "themeVersion") == 0)
+ return Thms::theTheme->getThemeVersion().c_str();
+
+ if (strcasecmp(name, "syntaxVersion") == 0)
+ return Thms::theTheme->getSyntaxVersion().c_str();
+
+ if (strcasecmp(name, "themeName") == 0)
+ return Thms::theTheme->getName().c_str();
+
+ if (strcasecmp(name, "vdrVersion") == 0)
+ return VDRVERSION;
+
+ if (strcasecmp(name, "mouseX") == 0)
+ return Str::toStr(vdrStatus->mouseX);
+
+ if (strcasecmp(name, "mouseY") == 0)
+ return Str::toStr(vdrStatus->mouseY);
+
+ if (strcasecmp(name, "mouseKey") == 0)
+ return Str::toStr(vdrStatus->mouseKey);
+
+ if (strcasecmp(name, "calibrationInstruction") == 0)
+ return vdrStatus->calibration.instruction.c_str();
+
+ if (strcasecmp(name, "calibrationInfo") == 0)
+ return vdrStatus->calibration.info.c_str();
+
+ if (strcasecmp(name, "calibrationCursorX") == 0)
+ return Str::toStr(vdrStatus->calibration.cursorX);
+
+ if (strcasecmp(name, "calibrationCursorY") == 0)
+ return Str::toStr(vdrStatus->calibration.cursorY);
+
+ if (strcasecmp(name, "calibrationTouchedX") == 0)
+ return Str::toStr(vdrStatus->calibration.lastX);
+
+ if (strcasecmp(name, "calibrationTouchedY") == 0)
+ return Str::toStr(vdrStatus->calibration.lastY);
+
+ if (strcasecmp(name, "calibrationOffsetX") == 0)
+ return Str::toStr(vdrStatus->calibration.settings.offsetX);
+
+ if (strcasecmp(name, "calibrationOffsetY") == 0)
+ return Str::toStr(vdrStatus->calibration.settings.offsetY);
+
+ if (strcasecmp(name, "calibrationScaleX") == 0)
+ return Str::toStr(vdrStatus->calibration.settings.scaleX, 4);
+
+ if (strcasecmp(name, "calibrationScaleY") == 0)
+ return Str::toStr(vdrStatus->calibration.settings.scaleY, 4);
+
+ if (strcasecmp(name, "actRecordingCount") == 0)
+ return Str::toStr(vdrStatus->_timers.countRunning());
+
+ if (strcasecmp(name, "actRecordingName") == 0)
+ return vdrStatus->_timers.firstRunning();
+
+ if (strcasecmp(name, "actTimersRunning") == 0)
+ return Str::toStr(vdrStatus->_timers.running());
+
+ if (strcasecmp(name, "actTimersTitle") == 0)
+ return vdrStatus->_timers.title();
+
+ if (strcasecmp(name, "actTimersFile") == 0)
+ return vdrStatus->_timers.file();
+
+ if (strcasecmp(name, "actTimersStart") == 0)
+ return formatDateTime(vdrStatus->_timers.start(), fmt, buf, sizeof(buf));
+
+ if (strcasecmp(name, "actTimersStop") == 0)
+ return formatDateTime(vdrStatus->_timers.stop(), fmt, buf, sizeof(buf));
+
+ if (strcasecmp(name, "menuText") == 0)
+ return vdrStatus->_menu.text.c_str();
+
+ if (strcasecmp(name, "STR") == 0)
+ return !cDevice::ActualDevice() ? "0" : Str::toStr(cDevice::ActualDevice()->SignalStrength());
+
+ if (strcasecmp(name, "SNR") == 0)
+ return !cDevice::ActualDevice() ? "0" : Str::toStr(cDevice::ActualDevice()->SignalQuality());
+
+ if (strcasecmp(name, "unseenMailCount") == 0)
+ return Str::toStr(vdrStatus->getUnseenMails());
+
+ if (strcasecmp(name, "hasNewMail") == 0)
+ return Str::toStr(vdrStatus->hasNewMail());
+
+ if (strcasecmp(name, "channelHasVtx") == 0)
+ return Str::toStr(vdrStatus->_presentChannel && vdrStatus->_presentChannel->Tpid());
+
+ if (strcasecmp(name, "channelHasMultilang") == 0)
+ return Str::toStr(vdrStatus->_presentChannel && vdrStatus->_presentChannel->Apid(1));
+
+ if (strcasecmp(name, "channelHasDD") == 0)
+ return Str::toStr(vdrStatus->_presentChannel && vdrStatus->_presentChannel->Dpid(0));
+
+ if (strcasecmp(name, "channelIsEncrypted") == 0)
+ return Str::toStr(vdrStatus->_presentChannel && vdrStatus->_presentChannel->Ca());
+
+ if (strcasecmp(name, "channelIsRadio") == 0)
+ {
+ int vp = vdrStatus->_presentChannel ? vdrStatus->_presentChannel->Vpid() : na;
+
+ return Str::toStr(vp == 0 || vp == 1 || vp == 0x1fff);
+ }
+
+ if (strcasecmp(name, "videoSizeHeight") == 0)
+ {
+ int width = 0, height = 0;
+ double aspect;
+
+ if (cDevice::PrimaryDevice())
+ cDevice::PrimaryDevice()->GetVideoSize(width, height, aspect);
+
+ return Str::toStr(height);
+ }
+
+ if (strcasecmp(name, "videoSizeWidth") == 0)
+ {
+ int width = 0, height = 0;
+ double aspect;
+
+ if (cDevice::PrimaryDevice())
+ cDevice::PrimaryDevice()->GetVideoSize(width, height, aspect);
+
+ return Str::toStr(width);
+ }
+
+ if (strcasecmp(name, "time") == 0)
+ {
+ if (strstr(fmt, "%s") || strstr(fmt, "%S") || strstr(fmt, "%T"))
+ {
+ // refresh every 1 second
+
+ if (!_delay)
+ scheduleDrawIn(1000);
+ }
+ else
+ {
+ // refresh at full minute
+
+ scheduleDrawNextFullMinute();
+ }
+
+ return formatDateTime(time(0), fmt, buf, sizeof(buf));
+ }
+
+ if (strcasecmp(name, "x") == 0)
+ return Str::toStr(X());
+ if (strcasecmp(name, "y") == 0)
+ return Str::toStr(Y());
+ if (strcasecmp(name, "width") == 0)
+ return Str::toStr(Width());
+ if (strcasecmp(name, "height") == 0)
+ return Str::toStr(Height());
+ }
+
+ status = fail;
+
+ return 0;
+}
+
+//***************************************************************************
+// Music Variable
+//***************************************************************************
+
+const char* cDisplayItem::musicVariable(const char* group, const char* var,
+ const char* fmt, int& status)
+{
+ static char buf[1000+TB];
+
+ if (strcasecmp(var, "Track") == 0)
+ return formatString(vdrStatus->_music.track(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Artist") == 0)
+ return formatString(vdrStatus->_music.artist.c_str(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Album") == 0)
+ return formatString(vdrStatus->_music.album.c_str(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Genre") == 0)
+ return vdrStatus->_music.genre.c_str();
+ else if (strcasecmp(var, "Year") == 0)
+ return vdrStatus->_music.year.c_str();
+ else if (strcasecmp(var, "Filename") == 0)
+ return formatString(vdrStatus->_music.filename.c_str(), fmt, buf, sizeof(buf));
+ else if (strcasecmp(var, "Comment") == 0)
+ return vdrStatus->_music.comment.c_str();
+ else if (strcasecmp(var, "Frequence") == 0)
+ return Str::toStr(vdrStatus->_music.frequence/1000);
+ else if (strcasecmp(var, "Bitrate") == 0)
+ return Str::toStr(vdrStatus->_music.bitrate/1000);
+ else if (strcasecmp(var, "StereoMode") == 0)
+ return vdrStatus->_music.smode.c_str();
+ else if (strcasecmp(var, "Index") == 0)
+ return Str::toStr(vdrStatus->_music.index);
+ else if (strcasecmp(var, "Count") == 0)
+ return Str::toStr(vdrStatus->_music.cnt);
+ else if (strcasecmp(var, "CurrentTrack") == 0)
+ return vdrStatus->_music.currentTrack.c_str();
+ else if (strcasecmp(var, "PlayStatus") == 0)
+ return vdrStatus->_music.status.c_str();
+ if (strcasecmp(var, "CoverName") == 0)
+ return vdrStatus->_coverPath.c_str();
+ else if (strcasecmp(var, "Rating") == 0)
+ return Str::toStr(vdrStatus->_music.rating);
+ else if (strcasecmp(var, "Loop") == 0)
+ return Str::toStr(vdrStatus->_music.loop);
+ else if (strcasecmp(var, "Timer") == 0)
+ return Str::toStr(vdrStatus->_music.timer);
+ else if (strcasecmp(var, "Copy") == 0)
+ return Str::toStr(vdrStatus->_music.copy);
+ else if (strcasecmp(var, "Lyrics") == 0)
+ return Str::toStr(vdrStatus->_music.lyrics);
+ else if (strcasecmp(var, "Shuffle") == 0)
+ return Str::toStr(vdrStatus->_music.shuffle);
+ else if (strcasecmp(var, "Shutdown") == 0)
+ return Str::toStr(vdrStatus->_music.shutdown);
+ else if (strcasecmp(var, "Recording") == 0)
+ return Str::toStr(vdrStatus->_music.recording);
+
+ else if (strcasecmp(var, "ButtonRed") == 0)
+ return vdrStatus->_music.red.c_str();
+ else if (strcasecmp(var, "ButtonGreen") == 0)
+ return vdrStatus->_music.green.c_str();
+ else if (strcasecmp(var, "ButtonYellow") == 0)
+ return vdrStatus->_music.yellow.c_str();
+ else if (strcasecmp(var, "ButtonBlue") == 0)
+ return vdrStatus->_music.blue.c_str();
+
+ status = fail;
+
+ return 0;
+}