From 4a9d9c5876cde9f21ccd165a7630727e6aca576a Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sat, 19 Feb 2000 13:36:48 +0100 Subject: Initial revision --- Makefile | 37 +++ README | 122 ++++++++++ TODO | 13 + channels.conf | 109 +++++++++ config.c | 333 ++++++++++++++++++++++++++ config.h | 163 +++++++++++++ dvbapi.c | 166 +++++++++++++ dvbapi.h | 65 +++++ interface.c | 298 +++++++++++++++++++++++ interface.h | 41 ++++ keys-pc.conf | Bin 0 -> 225 bytes keys.conf | 19 ++ menu.c | 745 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ menu.h | 21 ++ osd.c | 196 +++++++++++++++ osd.h | 68 ++++++ osm.c | 119 ++++++++++ remote.c | 257 ++++++++++++++++++++ remote.h | 43 ++++ timers.conf | 9 + tools.c | 124 ++++++++++ tools.h | 54 +++++ 22 files changed, 3002 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 channels.conf create mode 100644 config.c create mode 100644 config.h create mode 100644 dvbapi.c create mode 100644 dvbapi.h create mode 100644 interface.c create mode 100644 interface.h create mode 100644 keys-pc.conf create mode 100644 keys.conf create mode 100644 menu.c create mode 100644 menu.h create mode 100644 osd.c create mode 100644 osd.h create mode 100644 osm.c create mode 100644 remote.c create mode 100644 remote.h create mode 100644 timers.conf create mode 100644 tools.c create mode 100644 tools.h diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..6696dff1 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the On Screen Menu of the Video Disk Recorder +# +# See the main source file 'osm.c' for copyright information and +# how to reach the author. +# +# $Id: Makefile 1.1 2000/02/19 13:36:48 kls Exp $ + +OBJS = config.o dvbapi.o interface.o menu.o osd.o remote.o tools.o osm.o + +ifdef DEBUG_REMOTE +DEFINES += -DDEBUG_REMOTE +endif + +ifdef DEBUG_OSD +DEFINES += -DDEBUG_OSD +endif + +%.o: %.c + g++ -g -O2 -Wall -c $(DEFINES) $< + +all: osm + +config.o : config.c config.h dvbapi.h interface.h tools.h +dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h +interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h +menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h tools.h +osd.o : osd.c config.h interface.h osd.h tools.h +osm.o : osm.c config.h dvbapi.h interface.h menu.h osd.h tools.h +remote.o : remote.c remote.h tools.h +tools.o : tools.c tools.h + +osm: $(OBJS) + g++ -g -O2 $(OBJS) -lncurses -o osm + +clean: + -rm $(OBJS) osm diff --git a/README b/README new file mode 100644 index 00000000..e4fa836e --- /dev/null +++ b/README @@ -0,0 +1,122 @@ +On Screen Menu for the Video Disk Recorder +------------------------------------------ + +These files contain the source code of an on screen +menu for a video disk recorder based on the DVB driver +of the LinuxTV project (http://linuxtv.org). +For details about the "Video Disk Recorder" project please +refer to http://www.cadsoft.de/people/kls/vdr. + +The author can be contacted at kls@cadsoft.de. + +Yet another "set-top-box"? +-------------------------- + +The "set-top-boxes" available from commercial companies all have +one major drawback: they are not "open". This project's goal is +to build an "open" digital satellite receiver and timer controlled +video disk recorder, based upon open standards and freely available +driver software (of course, the hardware still has to be bought). + +The on screen menu system is simple, but shall provide all the +possibilites necessary to perform timer controlled recording, +file management and, maybe, even "on disk editing". The menus +of commercial set-top-boxes usually are a lot more fancy than +the ones in this system, but here we have the full source code +and can modify the menus in whatever way desired. + +Compiling and running the program: +---------------------------------- + +Make sure the files from this package are located in a +directory that is "parallel" to the DVB directory of the +driver source for the Siemens DVB-S PCI card (refer to +http://linuxtv.org/dvb/siemens_dvb.html for more information +about that driver). For example, if the DVB driver was +extracted into the directory /home/kls/vdr/DVB, then this +package should be extracted into /home/kls/vdr/OSM. + +After extracting the package, change into the OSM directory +and type 'make'. This should produce an executable file +named 'osm', which can be run after the DVB driver has been +installed. + +There are two macros you can use to customize the 'osm' program +at compile time. Adding "DEBUG_REMOTE=1" to the 'make' call +will use the PC's keyboard as input device instead of the "Remote +Control Unit" (see http://www.cadsoft.de/people/kls/vdr/remote.htm). +Adding "DEBUG_OSD=1" will use the PC screen (or current window) +to display texts instead of the DVB card's on-screen display +interface. These modes are useful when testing new menus if you +only have a remote connection to the VDR (which, in my case, is +located in the living room and has neither a monitor nor a keyboard). + +Configuration files: +-------------------- + +There are three configuration files that hold information about +channels, remote control keys and timers. These files are currrently +assumed to be located in the directory from which the 'osm' program +was started (this will become configurable later). The configuration +files can be edited with any text editor, or will be written by the +'osm' program if any changes are made inside the on-screen menus. +The meaning of the data entries may still vary in future releases, +so for the moment please look at the source code (config.c) to see +the meaning of the various fields. + +There is no way of adding or deleting channels or timers yet, this +will be implemented later. + +Learning the remote control keys: +--------------------------------- + +The remote control configuration file 'keys.conf' that comes with +this package contains the codes for the "d-box" remote control unit. +If you want to use a different remote control unit, simply delete +the file 'keys.conf' and restart the 'osm' program. The program will +then start a key learning session in which it first attempts to determine +the basic data tranfer mode and timing of your remote control unit, +and then will ask you to press one key after the other so that it can +learn the various key codes. You will at least need to provide an "Up" +and a "Down" key, so that you can switch channels. The rest os the key +definitions is optional, but the more keys you define, the more you +will be able to navigate through the menus. + +If the program has been built with "DEBUG_REMOTE=1", it will use the +key configuration file 'keys-pc.conf', so that you won't loose data +when switching between normal and debug mode. + +Navigating through the On Screen Menus: +--------------------------------------- + +The "Main" menu can be called up with the "Menu" key of your remote +control unit. The "Up" and "Down" keys are used to select a specific +item. The "Left" and "Right" keys can be used to change options, and +the numeric keys allow direct input of numeric data. The "Ok" key +confirms any changes (or switches to a channel in the "Channels" menu). +The "Back" key goes back one level in the menu structure, discarding +any changes that might have been made in the current menu. + +In the "Channels" menu, the current channel can be edited by pressing +the "Right" key. + +In the "Timers" menu, the current timer can be enabled or disabled with +the "Right" or "Left" key, respectively (enabled timers are marked with ">"). +"Ok" here opens the "Edit timer" menu. + +Textual options, like channel names or recording file names, can be edited +by pressing the "Right" button (which puts brackets around the current +character as in "[R]TL"), selecting the desired character position with +"Left" and "Right", and changing the character with the "Up" and "Down" +keys. "Ok" then confirms the changes. + +At any point in the menu system, pressing the "Menu" key again will +immediately leave the menu system. + +What do you think? +------------------ + +So, what do you think about this project? Does it make sense? Were you +able to use it? Do you have suggestions on how to improve it? +Please send email to kls@cadsoft.de if you'd like to comment on this. + diff --git a/TODO b/TODO new file mode 100644 index 00000000..32793469 --- /dev/null +++ b/TODO @@ -0,0 +1,13 @@ +TODO list for the Video Disk Recorder project +--------------------------------------------- + +* Implement a way to add and delete channels and timers. +* Implement recording to disk and playback from disk. +* Implement disk file management (delete old/viewed files to make + room for new recordings if necessary). +* Make it work with two DVB-S PCI cards to allow simultaneous + recording of one programme, while replaying another programme + (or maybe the same one, but time delayed). +* Implement "on-disk editing" to allow "cutting out" of certain + scenes in order to archive them (or, reversely, cut out + commercial breaks). diff --git a/channels.conf b/channels.conf new file mode 100644 index 00000000..9daca0af --- /dev/null +++ b/channels.conf @@ -0,0 +1,109 @@ +RTL:12188:h:1:27500:163:104 +Sat.1:12552:v:1:22000:163:104 +Pro 7:12480:v:1:27500:255:256 +RTL2:12188:h:1:27500:166:128 +ARD:11837:h:1:27500:101:102 +BR3:11837:h:1:27500:201:202 +Hessen 3:11837:h:1:27500:301:302 +N3:11837:h:1:27500:401:402 +SR3:11837:h:1:27500:501:502 +WDR:11837:h:1:27500:601:602 +BR alpha:11837:h:1:27500:701:702 +SWR BW:11837:h:1:27500:801:802 +Phoenix:11837:h:1:27500:901:902 +ZDF:11954:h:1:27500:110:120 +3sat:11954:h:1:27500:210:220 +Kinderkanal:11954:h:1:27500:310:320 +arte:11954:h:1:27500:360:370 +phoenix:11954:h:1:27500:410:420 +ORF Sat:11954:h:1:27500:506:507 +ZDF Infobox:11954:h:1:27500:610:620 +CNN:12168:v:1:27500:165:100 +Super RTL:12188:h:1:27500:165:120 +VOX:12188:h:1:27500:167:136 +DW TV:12363:v:1:27500:305:306 +Kabel 1:12480:v:1:27500:511:512 +TM3:12480:v:1:27500:767:768 +DSF:12480:v:1:27500:1023:1024 +HOT:12480:v:1:27500:1279:1280 +BloombergTV:12552:v:1:22000:162:99 +Sky News:12552:v:1:22000:305:306 +KinderNet:12574:h:1:22000:163:92 +Alice:12610:v:1:22000:162:96 +n-tv:12670:v:1:22000:162:96 +Grand Tour.:12670:v:1:22000:289:290 +TW1:12692:h:1:22000:166:167 +Eins Extra:12722:h:1:22000:101:102 +Eins Festival:12722:h:1:22000:201:202 +Eins MuXx:12722:h:1:22000:301:302 +MDR:12722:h:1:22000:401:402 +ORB:12722:h:1:22000:501:502 +B1:12722:h:1:22000:601:602 +ARD Online-Kanal:12722:h:1:22000:8191:701 +Premiere World Promo:11798:h:1:27500:255:256 +TV Niepokalanow:11876:h:1:27500:305:321 +test card:11876:h:1:27500:306:322 +Mosaico:11934:v:1:27500:165:100 +Andalucia TV:11934:v:1:27500:166:104 +TVC Internacional:11934:v:1:27500:167:108 +Nasza TV:11992:h:1:27500:165:98 +WishLine test:12012:v:1:27500:163:90 +Pro 7 Austria:12051:v:1:27500:161:84 +Kabel 1 Schweiz:12051:v:1:27500:162:163 +Kabel 1 Austria:12051:v:1:27500:166:167 +Pro 7 Schweiz:12051:v:1:27500:289:290 +Kiosque:12129:v:1:27500:160:80 +KTO:12129:v:1:27500:170:120 +TCM:12168:v:1:27500:160:80 +Cartoon Network France & Spain:12168:v:1:27500:161:84 +TVBS Europe:12168:v:1:27500:162:88 +TVBS Europe:12168:v:1:27500:162:89 +Travel:12168:v:1:27500:163:92 +TCM Espania:12168:v:1:27500:164:96 +MTV Spain:12168:v:1:27500:167:112 +TCM France:12168:v:1:27500:169:64 +RTL2 CH:12188:h:1:27500:164:112 +La Cinquieme:12207:v:1:27500:160:80 +ARTE:12207:v:1:27500:165:100 +Post Filial TV:12226:h:1:27500:255:256 +Canal Canaris:12246:v:1:27500:160:80 +Canal Canaris:12246:v:1:27500:160:81 +Canal Canaris:12246:v:1:27500:160:82 +Canal Canaris:12246:v:1:27500:160:83 +AB Sat Passion promo:12266:h:1:27500:160:80 +AB Channel 1:12266:h:1:27500:161:84 +Taquilla 0:12285:v:1:27500:165:100 +CSAT:12324:v:1:27500:160:80 +Mosaique:12324:v:1:27500:162:88 +Mosaique 2:12324:v:1:27500:163:92 +Mosaique 3:12324:v:1:27500:164:96 +Le Sesame C+:12324:v:1:27500:165:1965 +FEED:12344:h:1:27500:163:92 +RTM 1:12363:v:1:27500:162:96 +ESC 1:12363:v:1:27500:163:104 +TV5 Europe:12363:v:1:27500:164:112 +TV7 Tunisia:12363:v:1:27500:166:128 +ARTE:12363:v:1:27500:167:137 +RAI Uno:12363:v:1:27500:289:290 +RTP International:12363:v:1:27500:300:301 +Fashion TV:12402:v:1:27500:163:92 +VideoService:12422:h:1:27500:255:256 +Beta Research promo:12422:h:1:27500:1023:1024 +Canal Canarias:12441:v:1:27500:160:80 +TVC International:12441:v:1:27500:512:660 +Fitur:12441:v:1:27500:514:662 +Astra Info 1:12552:v:1:22000:164:112 +Astra Info 2:12552:v:1:22000:165:120 +Astra Vision 1:12552:v:1:22000:168:144 +Astra Vision 1:12552:v:1:22000:168:145 +Astra Vision 1:12552:v:1:22000:168:146 +Astra Vision 1:12552:v:1:22000:168:147 +Astra Vision 1:12552:v:1:22000:168:148 +Astra Vision 1:12552:v:1:22000:168:149 +Astra Vision 1:12552:v:1:22000:168:150 +RTL Tele Letzebuerg:12552:v:1:22000:168:144 +Astra Mosaic:12552:v:1:22000:175:176 +MHP test:12604:h:1:22000:5632:8191 +Bloomberg TV Spain:12610:v:1:22000:45:49 +Video Italia:12610:v:1:22000:121:122 +AC 3 promo:12670:v:1:22000:308:256 diff --git a/config.c b/config.c new file mode 100644 index 00000000..50315691 --- /dev/null +++ b/config.c @@ -0,0 +1,333 @@ +/* + * config.c: Configuration file handling + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: config.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "config.h" +#include +#include +#include +#include "dvbapi.h" +#include "interface.h" + +// -- cKeys ------------------------------------------------------------------ + +tKey keyTable[] = { // "Up" and "Down" must be the first two keys! + { kUp, "Up", 0 }, + { kDown, "Down", 0 }, + { kMenu, "Menu", 0 }, + { kOk, "Ok", 0 }, + { kBack, "Back", 0 }, + { kLeft, "Left", 0 }, + { kRight, "Right", 0 }, + { k0, "0", 0 }, + { k1, "1", 0 }, + { k2, "2", 0 }, + { k3, "3", 0 }, + { k4, "4", 0 }, + { k5, "5", 0 }, + { k6, "6", 0 }, + { k7, "7", 0 }, + { k8, "8", 0 }, + { k9, "9", 0 }, + { kNone, "", 0 }, + }; + +cKeys::cKeys(void) +{ + fileName = NULL; + code = 0; + address = 0; + keys = keyTable; +} + +void cKeys::Clear(void) +{ + for (tKey *k = keys; k->type != kNone; k++) + k->code = 0; +} + +bool cKeys::Load(char *FileName) +{ + isyslog(LOG_INFO, "loading %s", FileName); + bool result = false; + if (FileName) + fileName = strdup(FileName); + if (fileName) { + FILE *f = fopen(fileName, "r"); + if (f) { + int line = 0; + char buffer[MaxBuffer]; + result = true; + while (fgets(buffer, sizeof(buffer), f) > 0) { + line++; + char *Name = buffer; + char *p = strpbrk(Name, " \t"); + if (p) { + *p = 0; // terminates 'Name' + while (*++p && isspace(*p)) + ; + if (*p) { + if (strcasecmp(Name, "Code") == 0) + code = *p; + else if (strcasecmp(Name, "Address") == 0) + address = strtol(p, NULL, 16); + else { + for (tKey *k = keys; k->type != kNone; k++) { + if (strcasecmp(Name, k->name) == 0) { + k->code = strtol(p, NULL, 16); + Name = NULL; // to indicate that we found it + break; + } + } + if (Name) { + fprintf(stderr, "unknown key in %s, line %d\n", fileName, line); + result = false; + break; + } + } + } + continue; + } + fprintf(stderr, "error in %s, line %d\n", fileName, line); + result = false; + break; + } + fclose(f); + } + else + fprintf(stderr, "can't open '%s'\n", fileName); + } + else + fprintf(stderr, "no key configuration file name supplied!\n"); + return result; +} + +bool cKeys::Save(void) +{ + //TODO make backup copies??? + bool result = true; + FILE *f = fopen(fileName, "w"); + if (f) { + if (fprintf(f, "Code\t%c\nAddress\t%04X\n", code, address) > 0) { + for (tKey *k = keys; k->type != kNone; k++) { + if (fprintf(f, "%s\t%08X\n", k->name, k->code) <= 0) { + result = false; + break; + } + } + } + else + result = false; + fclose(f); + } + else + result = false; + return result; +} + +eKeys cKeys::Get(unsigned int Code) +{ + if (Code != 0) { + tKey *k; + for (k = keys; k->type != kNone; k++) { + if (k->code == Code) + break; + } + return k->type; + } + return kNone; +} + +void cKeys::Set(eKeys Key, unsigned int Code) +{ + for (tKey *k = keys; k->type != kNone; k++) { + if (k->type == Key) { + k->code = Code; + break; + } + } +} + +// -- cChannel --------------------------------------------------------------- + +cChannel::cChannel(void) +{ + *name = 0; +} + +bool cChannel::Parse(char *s) +{ + char *buffer = NULL; + if (7 == sscanf(s, "%a[^:]:%d:%c:%d:%d:%d:%d", &buffer, &frequency, &polarization, &diseqc, &srate, &vpid, &apid)) { + strncpy(name, buffer, MaxChannelName - 1); + name[strlen(buffer)] = 0; + delete buffer; + return true; + } + return false; +} + +bool cChannel::Save(FILE *f) +{ + return fprintf(f, "%s:%d:%c:%d:%d:%d:%d\n", name, frequency, polarization, diseqc, srate, vpid, apid) > 0; +} + +bool cChannel::Switch(void) +{ + if (!ChannelLocked) { + isyslog(LOG_INFO, "switching to channel %d", Index() + 1); + CurrentChannel = Index(); + Interface.DisplayChannel(CurrentChannel + 1, name); + for (int i = 3; --i;) { + if (DvbSetChannel(frequency, polarization, diseqc, srate, vpid, apid)) + return true; + esyslog(LOG_ERR, "retrying"); + } + } + Interface.Info("Channel locked (recording)!"); + return false; +} + +bool cChannel::SwitchTo(int i) +{ + cChannel *channel = Channels.Get(i); + return channel && channel->Switch(); +} + +// -- cTimer ----------------------------------------------------------------- + +cTimer::cTimer(void) +{ + *file = 0; +} + +int cTimer::TimeToInt(int t) +{ + return (t / 100 * 60 + t % 100) * 60; +} + +int cTimer::ParseDay(char *s) +{ + char *tail; + int d = strtol(s, &tail, 10); + if (tail && *tail) { + d = 0; + if (tail == s) { + if (strlen(s) == 7) { + for (char *p = s + 6; p >= s; p--) { + d <<= 1; + d |= (*p != '-'); + } + d |= 0x80000000; + } + } + } + else if (d < 1 || d > 31) + d = 0; + return d; +} + +char *cTimer::PrintDay(int d) +{ + static char buffer[8]; + if ((d & 0x80000000) != 0) { + char *b = buffer; + char *w = "MTWTFSS"; + *b = 0; + while (*w) { + *b++ = (d & 1) ? *w : '-'; + d >>= 1; + w++; + } + } + else + sprintf(buffer, "%d", d); + return buffer; +} + +bool cTimer::Parse(char *s) +{ + char *buffer1 = NULL; + char *buffer2 = NULL; + if (9 == sscanf(s, "%d:%d:%a[^:]:%d:%d:%c:%d:%d:%as", &active, &channel, &buffer1, &start, &stop, &quality, &priority, &lifetime, &buffer2)) { + day = ParseDay(buffer1); + strncpy(file, buffer2, MaxFileName - 1); + file[strlen(buffer2)] = 0; + delete buffer1; + delete buffer2; + return day != 0; + } + return false; +} + +bool cTimer::Save(FILE *f) +{ + return fprintf(f, "%d:%d:%s:%d:%d:%c:%d:%d:%s\n", active, channel, PrintDay(day), start, stop, quality, priority, lifetime, file) > 0; +} + +bool cTimer::Matches(void) +{ + if (active) { + time_t t = time(NULL); + struct tm *now = localtime(&t); + int weekday = now->tm_wday == 0 ? 6 : now->tm_wday - 1; // we start with monday==0! + int current = (now->tm_hour * 60 + now->tm_min) * 60 + now->tm_sec; + int begin = TimeToInt(start); + int end = TimeToInt(stop); + bool twoDays = (end < begin); + + bool todayMatches = false, yesterdayMatches = false; + if ((day & 0x80000000) != 0) { + if ((day & (1 << weekday)) != 0) + todayMatches = true; + else if (twoDays) { + int yesterday = weekday == 0 ? 6 : weekday - 1; + if ((day & (1 << yesterday)) != 0) + yesterdayMatches = true; + } + } + else if (day == now->tm_mday) + todayMatches = true; + else if (twoDays) { + t -= 86400; + now = localtime(&t); + if (day == now->tm_mday) + yesterdayMatches = true; + } + return (todayMatches && current >= begin && (current <= end || twoDays)) + || (twoDays && yesterdayMatches && current <= end); + } + return false; +} + +cTimer *cTimer::GetMatch(void) +{ + cTimer *t = (cTimer *)Timers.First(); + while (t) { + if (t->Matches()) + return t; + t = (cTimer *)t->Next(); + } + return NULL; +} + +// -- cKeys ------------------------------------------------------------------ + +cKeys Keys; + +// -- cChannels -------------------------------------------------------------- + +int CurrentChannel = 0; +bool ChannelLocked = false; + +cChannels Channels; + +// -- cTimers ---------------------------------------------------------------- + +cTimers Timers; + diff --git a/config.h b/config.h new file mode 100644 index 00000000..3abb7a1a --- /dev/null +++ b/config.h @@ -0,0 +1,163 @@ +/* + * config.h: Configuration file handling + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: config.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include +#include +#include "tools.h" + +#define MaxBuffer 1000 + +enum eKeys { // "Up" and "Down" must be the first two keys! + kUp, + kDown, + kMenu, + kOk, + kBack, + kLeft, + kRight, + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, + kNone + }; + +struct tKey { + eKeys type; + char *name; + unsigned int code; + }; + +class cKeys { +private: + char *fileName; +public: + unsigned char code; + unsigned short address; + tKey *keys; + cKeys(void); + void Clear(void); + bool Load(char *FileName = NULL); + bool Save(void); + eKeys Get(unsigned int Code); + void Set(eKeys Key, unsigned int Code); + }; + +class cChannel : public cListObject { +public: + enum { MaxChannelName = 32 }; // 31 chars + terminating 0! + char name[MaxChannelName]; + int frequency; // MHz + char polarization; + int diseqc; + int srate; + int vpid; + int apid; + cChannel(void); + bool Parse(char *s); + bool Save(FILE *f); + bool Switch(void); + static bool SwitchTo(int i); + }; + +class cTimer : public cListObject { +public: + enum { MaxFileName = 256 }; + int active; + int channel; + int day; + int start; + int stop; +//TODO VPS??? + char quality; + int priority; + int lifetime; + char file[MaxFileName]; + cTimer(void); + bool Parse(char *s); + bool Save(FILE *f); + bool Matches(void); + static cTimer *GetMatch(void); + static int TimeToInt(int t); + static int ParseDay(char *s); + static char *PrintDay(int d); + }; + +template class cConfig : public cList { +private: + char *fileName; + void Clear(void) + { + delete fileName; + cList::Clear(); + } +public: + bool Load(char *FileName) + { + isyslog(LOG_INFO, "loading %s", FileName); + bool result = true; + Clear(); + fileName = strdup(FileName); + FILE *f = fopen(fileName, "r"); + if (f) { + int line = 0; + char buffer[MaxBuffer]; + while (fgets(buffer, sizeof(buffer), f) > 0) { + line++; + T *l = new T; + if (l->Parse(buffer)) + Add(l); + else { + fprintf(stderr, "error in %s, line %d\n", fileName, line); + delete l; + result = false; + break; + } + } + fclose(f); + } + else { + fprintf(stderr, "can't open '%s'\n", fileName); + result = false; + } + return result; + } + bool Save(void) + { + //TODO make backup copies??? + bool result = true; + T *l = (T *)First(); + FILE *f = fopen(fileName, "w"); + if (f) { + while (l) { + if (!l->Save(f)) { + result = false; + break; + } + l = (T *)l->Next(); + } + fclose(f); + } + else + result = false; + return result; + } + }; + +class cChannels : public cConfig {}; +class cTimers : public cConfig {}; + +extern int CurrentChannel; +extern bool ChannelLocked; + +extern cChannels Channels; +extern cTimers Timers; +extern cKeys Keys; + +#endif //__CONFIG_H diff --git a/dvbapi.c b/dvbapi.c new file mode 100644 index 00000000..5597fbd5 --- /dev/null +++ b/dvbapi.c @@ -0,0 +1,166 @@ +/* + * dvbapi.c: Interface to the DVB driver + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: dvbapi.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +// FIXME: these should be defined in ../DVB/driver/dvb.h!!! +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; + +#include "dvbapi.h" +#include +#include +#include +#include +#include "../DVB/driver/dvb.h" +#include "interface.h" +#include "tools.h" + +#define VIDEODEVICE "/dev/video" + +const char *DvbQuality = "LMH"; // Low, Medium, High + +bool DvbSetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid) +{ + int v = open(VIDEODEVICE, O_RDWR); + + if (v >= 0) { + struct frontend front; + ioctl(v, VIDIOCGFRONTEND, &front); + unsigned int freq = FrequencyMHz; + front.ttk = (freq < 11800UL) ? 0 : 1; + if (freq < 11800UL) + freq -= 9750UL; + else + freq -= 10600UL; + front.freq = freq * 1000000UL; + front.diseqc = Diseqc; + front.srate = Srate * 1000; + front.volt = (Polarization == 'v') ? 0 : 1; + front.video_pid = Vpid; + front.audio_pid = Apid; + front.AFC = 1; + ioctl(v, VIDIOCSFRONTEND, &front); + close(v); + if (front.sync & 0x1F == 0x1F) + return true; + esyslog(LOG_ERR, "channel not sync'ed (front.sync=%X)!", front.sync); + } + else + Interface.Error("can't open VIDEODEVICE");//XXX + return false; +} + +// -- cDvbRecorder ----------------------------------------------------------- + +cDvbRecorder::cDvbRecorder(void) +{ +} + +cDvbRecorder::~cDvbRecorder() +{ + Stop(); +} + +bool cDvbRecorder::Record(const char *FileName, char Quality) +{ + isyslog(LOG_INFO, "record %s (%c)", FileName, Quality); + return true; + // TODO + return false; +} + +bool cDvbRecorder::Play(const char *FileName, int Frame) +{ + isyslog(LOG_INFO, "play %s (%d)", FileName, Frame); + // TODO + return false; +} + +bool cDvbRecorder::FastForward(void) +{ + isyslog(LOG_INFO, "fast forward"); + // TODO + return false; +} + +bool cDvbRecorder::FastRewind(void) +{ + isyslog(LOG_INFO, "fast rewind"); + // TODO + return false; +} + +bool cDvbRecorder::Pause(void) +{ + isyslog(LOG_INFO, "pause"); + // TODO + return false; +} + +void cDvbRecorder::Stop(void) +{ + isyslog(LOG_INFO, "stop"); + // TODO +} + +int cDvbRecorder::Frame(void) +{ + isyslog(LOG_INFO, "frame"); + // TODO + return 0; +} + +// --------------------------------------------------------------------------- + +static void DvbOsdCmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, void *data = NULL) +{ + int v = open(VIDEODEVICE, O_RDWR); + + if (v >= 0) { + struct drawcmd dc; + dc.cmd = cmd; + dc.color = color; + dc.x0 = x0; + dc.y0 = y0; + dc.x1 = x1; + dc.y1 = y1; + dc.data = data; + ioctl(v, VIDIOCSOSDCOMMAND, &dc); + close(v); + } + else + Interface.Error("can't open VIDEODEVICE");//XXX +} + +void DvbOsdOpen(int x, int y, int w, int h) +{ + DvbOsdCmd(OSD_Open, 1, x, y, x + w - 1, y + h - 1); + DvbOsdCmd(OSD_SetColor, 0, 0, 0, 0, 127); // background 50% gray + DvbOsdCmd(OSD_SetColor, 1, 255, 255, 255, 255); // text white +} + +void DvbOsdClose(void) +{ + DvbOsdCmd(OSD_Close); +} + +void DvbOsdClear(void) +{ + DvbOsdCmd(OSD_Clear); +} + +void DvbOsdClrEol(int x, int y) +{ + DvbOsdCmd(OSD_FillBlock, 0, x, y * DvbOsdLineHeight, x + 490, (y + 1) * DvbOsdLineHeight);//XXX +} + +void DvbOsdText(int x, int y, char *s) +{ + DvbOsdCmd(OSD_Text, 1, x, y, 1, 0, s); +} diff --git a/dvbapi.h b/dvbapi.h new file mode 100644 index 00000000..6a8f0400 --- /dev/null +++ b/dvbapi.h @@ -0,0 +1,65 @@ +/* + * dvbapi.h: Interface to the DVB driver + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: dvbapi.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __DVBAPI_H +#define __DVBAPI_H + +const int DvbOsdCharWidth = 12; //XXX +const int DvbOsdLineHeight = 25; + +extern const char *DvbQuality; // Low, Medium, High + +bool DvbSetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid); + +class cDvbRecorder { +public: + cDvbRecorder(void); + ~cDvbRecorder(); + bool Record(const char *FileName, char Quality); + // Starts recording the current channel into the given file, with the + // given quality level. Any existing file will be overwritten. + // Returns true if recording was started successfully. + // If there is already a recording session active, false will be + // returned. + bool Play(const char *FileName, int Frame = 0); + // Starts playback of the given file, at the optional Frame (default + // is the beginning of the file). If Frame is beyond the last recorded + // frame in the file, or if it is negative, playback will be positioned + // to the last frame in the file and will do an implicit Pause() there. + // If there is already a playback session active, it will be stopped + // and the new file or frame (which may be in the same file) will + // be played back. + bool FastForward(void); + // Runs the current playback session forward at a higher speed. + // TODO allow different fast forward speeds??? + bool FastRewind(void); + // Runs the current playback session backwards forward at a higher speed. + // TODO allow different fast rewind speeds??? + bool Pause(void); + // Pauses the current recording or playback session, or resumes a paused + // session. + // Returns true if there is actually a recording or playback session + // active that was paused/resumed. + void Stop(void); + // Stops the current recording or playback session. + int Frame(void); + // Returns the number of the current frame in the current recording or + // playback session, which can be used to start playback at a given position. + // The number returned is the actual number of frames counted from the + // beginning of the current file. + // The very first frame has the number 1. + }; + +void DvbOsdOpen(int x, int y, int w, int h); +void DvbOsdClose(void); +void DvbOsdClear(void); +void DvbOsdClrEol(int x, int y); +void DvbOsdText(int x, int y, char *s); + +#endif //__DVBAPI_H diff --git a/interface.c b/interface.c new file mode 100644 index 00000000..58c28f1f --- /dev/null +++ b/interface.c @@ -0,0 +1,298 @@ +/* + * interface.c: Abstract user interface layer + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: interface.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "interface.h" +#include +#include +#include "dvbapi.h" +#include "remote.h" + +#ifndef DEBUG_REMOTE +cRcIo RcIo("/dev/ttyS1");//XXX +#endif + +WINDOW *window; + +cInterface Interface; + +cInterface::cInterface(void) +{ + open = 0; + cols[0] = 0; +#ifdef DEBUG_OSD + initscr(); + keypad(stdscr, TRUE); + nonl(); + cbreak(); + noecho(); + leaveok(stdscr, TRUE); + window = stdscr; +#else +#endif +} + +void cInterface::Init(void) +{ +#ifndef DEBUG_REMOTE + RcIo.SetCode(Keys.code, Keys.address); +#endif +} + +void cInterface::Open(void) +{ + if (!open++) { +#ifdef DEBUG_OSD +#else +//TODO + DvbOsdOpen(100, 100, 500, 400); +#endif + } +} + +void cInterface::Close(void) +{ + if (!--open) { +#ifdef DEBUG_OSD +#else +//TODO + DvbOsdClose(); +#endif + } +} + +unsigned int cInterface::GetCh(void) +{ +#ifdef DEBUG_REMOTE + return getch(); +#else +#ifdef DEBUG_OSD + wrefresh(window);//XXX +#endif + unsigned int Command; + return RcIo.GetCommand(&Command) ? Command : 0; +#endif +} + +eKeys cInterface::GetKey(void) +{ + return Keys.Get(GetCh()); +} + +void cInterface::Clear(void) +{ + if (open) { +#ifdef DEBUG_OSD + wclear(window); +#else +//TODO + DvbOsdClear(); +#endif + } +} + +void cInterface::SetCols(int *c) +{ + for (int i = 0; i < MaxCols; i++) { + cols[i] = *c++; + if (cols[i] == 0) + break; + } +} + +void cInterface::Write(int x, int y, char *s) +{ + if (open) { +#ifdef DEBUG_OSD + wmove(window, y, x); // ncurses wants 'y' before 'x'! + waddstr(window, s); +#else + DvbOsdText(x * DvbOsdCharWidth, y * DvbOsdLineHeight, s); +#endif + } +} + +void cInterface::WriteText(int x, int y, char *s, bool Current) +{ + if (open) { +#ifdef DEBUG_OSD + wmove(window, y, x); // ncurses wants 'y' before 'x'! + wclrtoeol(window);//XXX +#else +//TODO + DvbOsdClrEol(x * DvbOsdCharWidth, y);//XXX +#endif + Write(x, y, Current ? "*" : " "); + x++; + int col = 0; + for (;;) { + char *t = strchr(s, '\t'); + char *p = s; + char buf[1000]; + if (t && col < MaxCols && cols[col] > 0) { + unsigned int n = t - s; + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, s, n); + buf[n] = 0; + p = buf; + s = t + 1; + } + Write(x, y, p); + if (p == s) + break; + x += cols[col++]; + } + } +} + +void cInterface::Info(char *s) +{ + Open(); + isyslog(LOG_ERR, s); + WriteText(0, 11, s);//TODO +#ifdef DEBUG_OSD + wrefresh(window);//XXX +#endif + sleep(1); + WriteText(0, 11, "");//TODO +#ifdef DEBUG_OSD + wrefresh(window);//XXX +#endif + Close(); +} + +void cInterface::Error(char *s) +{ + Open(); + esyslog(LOG_ERR, s); + WriteText(0, 12, s);//TODO +#ifdef DEBUG_OSD + wrefresh(window);//XXX +#endif + sleep(1); + WriteText(0, 12, "");//TODO +#ifdef DEBUG_OSD + wrefresh(window);//XXX +#endif + Close(); +} + +void cInterface::QueryKeys(void) +{ + Keys.Clear(); + WriteText(1, 1, "Learning Remote Control Keys"); + WriteText(1, 3, "Phase 1: Detecting RC code type"); + WriteText(1, 5, "Press any key on the RC unit"); +#ifndef DEBUG_REMOTE + unsigned char Code = 0; + unsigned short Address; +#endif + for (;;) { +#ifdef DEBUG_REMOTE + if (GetCh()) + break; +#else + //TODO on screen display... + if (RcIo.DetectCode(&Code, &Address)) { + Keys.code = Code; + Keys.address = Address; + WriteText(1, 5, "RC code detected!"); + WriteText(1, 6, "Do not press any key..."); + RcIo.Flush(3); + WriteText(1, 5, ""); + WriteText(1, 6, ""); + break; + } +#endif + } + WriteText(1, 3, "Phase 2: Learning specific key codes"); + tKey *k = Keys.keys; + while (k->type != kNone) { + char *Prompt; + asprintf(&Prompt, "Press key for '%s'", k->name); + WriteText(1, 5, Prompt); + delete Prompt; + for (;;) { + unsigned int ch = GetCh(); + if (ch != 0) { + switch (Keys.Get(ch)) { + case kUp: if (k > Keys.keys) { + k--; + break; + } + case kDown: if (k > Keys.keys + 1) { + WriteText(1, 5, "Press 'Up' to confirm"); + WriteText(1, 6, "Press 'Down' to continue"); + WriteText(1, 7, ""); + WriteText(1, 8, ""); + for (;;) { + eKeys key = GetKey(); + if (key == kUp) { + Clear(); + return; + } + else if (key == kDown) { + WriteText(1, 6, ""); + break; + } + } + break; + } + case kNone: k->code = ch; + k++; + break; + default: break; + } + break; + } + } + if (k > Keys.keys) + WriteText(1, 7, "(press 'Up' to go back)"); + else + WriteText(1, 7, ""); + if (k > Keys.keys + 1) + WriteText(1, 8, "(press 'Down' to end key definition)"); + else + WriteText(1, 8, ""); + } +} + +void cInterface::LearnKeys(void) +{ + isyslog(LOG_INFO, "learning keys"); + for (;;) { + Clear(); + QueryKeys(); + Clear(); + WriteText(1, 1, "Learning Remote Control Keys"); + WriteText(1, 3, "Phase 3: Saving key codes"); + WriteText(1, 5, "Press 'Up' to save, 'Down' to cancel"); + for (;;) { + eKeys key = GetKey(); + if (key == kUp) { + Keys.Save(); + Clear(); + return; + } + else if (key == kDown) { + Keys.Load(); + Clear(); + return; + } + } + } +} + +void cInterface::DisplayChannel(int Number, char *Name) +{ +//TODO +#ifndef DEBUG_REMOTE + RcIo.Number(Number); +#endif +} diff --git a/interface.h b/interface.h new file mode 100644 index 00000000..e387ab91 --- /dev/null +++ b/interface.h @@ -0,0 +1,41 @@ +/* + * interface.h: Abstract user interface layer + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: interface.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __INTERFACE_H +#define __INTERFACE_H + +#include "config.h" + +class cInterface { +public: + enum { MaxCols = 5 }; +private: + int open; + int cols[MaxCols]; + unsigned int GetCh(void); + void QueryKeys(void); + void Write(int x, int y, char *s); +public: + cInterface(void); + void Init(void); + void Open(void); + void Close(void); + eKeys GetKey(void); + void Clear(void); + void SetCols(int *c); + void WriteText(int x, int y, char *s, bool Current = false); + void Info(char *s); + void Error(char *s); + void LearnKeys(void); + void DisplayChannel(int Number, char *Name); + }; + +extern cInterface Interface; + +#endif //__INTERFACE_H diff --git a/keys-pc.conf b/keys-pc.conf new file mode 100644 index 00000000..c57613f0 Binary files /dev/null and b/keys-pc.conf differ diff --git a/keys.conf b/keys.conf new file mode 100644 index 00000000..f56222c3 --- /dev/null +++ b/keys.conf @@ -0,0 +1,19 @@ +Code B +Address 0000 +Up 000047E2 +Down 000007E2 +Menu 000011E2 +Ok 000079E2 +Back 00001AE2 +Left 000005E2 +Right 000045E2 +0 00007FE2 +1 00003FE2 +2 00005FE2 +3 00001FE2 +4 00006FE2 +5 00002FE2 +6 00004FE2 +7 00000FE2 +8 000077E2 +9 000037E2 diff --git a/menu.c b/menu.c new file mode 100644 index 00000000..4f525ff7 --- /dev/null +++ b/menu.c @@ -0,0 +1,745 @@ +/* + * menu.c: The actual menu implementations + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: menu.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "menu.h" +#include +#include +#include +#include "config.h" +#include "dvbapi.h" + +const char *FileNameChars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789/-.# ";//TODO more? + +// --- cMenuEditItem --------------------------------------------------------- + +class cMenuEditItem : public cOsdItem { +private: + const char *name; + const char *value; +public: + cMenuEditItem(const char *Name); + ~cMenuEditItem(); + void SetValue(const char *Value); + }; + +cMenuEditItem::cMenuEditItem(const char *Name) +{ + name = strdup(Name); + value = NULL; +} + +cMenuEditItem::~cMenuEditItem() +{ + delete name; + delete value; +} + +void cMenuEditItem::SetValue(const char *Value) +{ + delete value; + value = strdup(Value); + char *buffer = NULL; + asprintf(&buffer, "%s:\t%s", name, value); + SetText(buffer, false); + Display(); +} + +// --- cMenuEditIntItem ------------------------------------------------------ + +class cMenuEditIntItem : public cMenuEditItem { +protected: + int *value; + int min, max; + virtual void Set(void); +public: + cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max) +:cMenuEditItem(Name) +{ + value = Value; + min = Min; + max = Max; + Set(); +} + +void cMenuEditIntItem::Set(void) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%d", *value); + SetValue(buf); +} + +eOSStatus cMenuEditIntItem::ProcessKey(eKeys Key) +{ + eOSStatus status = cMenuEditItem::ProcessKey(Key); + + if (status == osUnknown) { + int newValue; + if (k0 <= Key && Key <= k9) { + if (fresh) { + *value = 0; + fresh = false; + } + newValue = *value * 10 + (Key - k0); + } + else if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly? + newValue = *value - 1; + fresh = true; + } + else if (Key == kRight) { + newValue = *value + 1; + fresh = true; + } + else + return status; + if ((!fresh || min <= newValue) && newValue <= max) { + *value = newValue; + Set(); + } + status = osContinue; + } + return status; +} + +// --- cMenuEditBoolItem ----------------------------------------------------- + +class cMenuEditBoolItem : public cMenuEditIntItem { +protected: + virtual void Set(void); +public: + cMenuEditBoolItem(const char *Name, int *Value); + }; + +cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value) +:cMenuEditIntItem(Name, Value, 0, 1) +{ + Set(); +} + +void cMenuEditBoolItem::Set(void) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%s", *value ? "yes" : "no"); + SetValue(buf); +} + +// --- cMenuEditChanItem ----------------------------------------------------- + +class cMenuEditChanItem : public cMenuEditIntItem { +protected: + virtual void Set(void); +public: + cMenuEditChanItem(const char *Name, int *Value); + }; + +cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value) +:cMenuEditIntItem(Name, Value, 1, Channels.Count()) +{ + Set(); +} + +void cMenuEditChanItem::Set(void) +{ + char buf[255]; + cChannel *channel = Channels.Get(*value - 1); + if (channel) + snprintf(buf, sizeof(buf), "%d %s", *value, channel->name); + else + *buf = 0; + SetValue(buf); +} + +// --- cMenuEditDayItem ------------------------------------------------------ + +class cMenuEditDayItem : public cMenuEditIntItem { +protected: + static int days[]; + int d; + virtual void Set(void); +public: + cMenuEditDayItem(const char *Name, int *Value); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +int cMenuEditDayItem::days[] ={ cTimer::ParseDay("M------"), + cTimer::ParseDay("-T-----"), + cTimer::ParseDay("--W----"), + cTimer::ParseDay("---T---"), + cTimer::ParseDay("----F--"), + cTimer::ParseDay("-----S-"), + cTimer::ParseDay("------S"), + cTimer::ParseDay("MTWTF--"), + cTimer::ParseDay("MTWTFS-"), + cTimer::ParseDay("MTWTFSS"), + cTimer::ParseDay("-----SS"), + 0 }; + +cMenuEditDayItem::cMenuEditDayItem(const char *Name, int *Value) +:cMenuEditIntItem(Name, Value, -INT_MAX, 31) +{ + d = -1; + if (*value < 0) { + int n = 0; + while (days[n]) { + if (days[n] == *value) { + d = n; + break; + } + n++; + } + } + Set(); +} + +void cMenuEditDayItem::Set(void) +{ + SetValue(cTimer::PrintDay(*value)); +} + +eOSStatus cMenuEditDayItem::ProcessKey(eKeys Key) +{ + switch (Key) { + case kLeft: if (d > 0) + *value = days[--d]; + else if (d == 0) { + *value = 31; + d = -1; + } + else if (*value == 1) { + d = sizeof(days) / sizeof(int) - 2; + *value = days[d]; + } + else + return cMenuEditIntItem::ProcessKey(Key); + Set(); + break; + case kRight: if (d >= 0) { + *value = days[++d]; + if (*value == 0) { + *value = 1; + d = -1; + } + } + else if (*value == 31) { + d = 0; + *value = days[d]; + } + else + return cMenuEditIntItem::ProcessKey(Key); + Set(); + break; + default : return cMenuEditIntItem::ProcessKey(Key); + } + return osContinue; +} + +// --- cMenuEditTimeItem ----------------------------------------------------- + +class cMenuEditTimeItem : public cMenuEditItem { +protected: + int *value; + int hh, mm; + int pos; + virtual void Set(void); +public: + cMenuEditTimeItem(const char *Name, int *Value); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value) +:cMenuEditItem(Name) +{ + value = Value; + hh = *value / 100; + mm = *value % 100; + pos = 0; + Set(); +} + +void cMenuEditTimeItem::Set(void) +{ + char buf[10]; + snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm); + SetValue(buf); +} + +eOSStatus cMenuEditTimeItem::ProcessKey(eKeys Key) +{ + eOSStatus status = cMenuEditItem::ProcessKey(Key); + + if (status == osUnknown) { + if (k0 <= Key && Key <= k9) { + if (fresh || pos > 3) { + pos = 0; + fresh = false; + } + int n = Key - k0; + switch (pos) { + case 0: if (n <= 2) { + hh = n * 10; + mm = 0; + pos++; + } + break; + case 1: if (hh + n <= 23) { + hh += n; + pos++; + } + break; + case 2: if (n <= 5) { + mm += n * 10; + pos++; + } + break; + case 3: if (mm + n <= 59) { + mm += n; + pos++; + } + break; + } + } + else if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly? + if (--mm < 0) { + mm = 59; + if (--hh < 0) + hh = 23; + } + fresh = true; + } + else if (Key == kRight) { + if (++mm > 59) { + mm = 0; + if (++hh > 23) + hh = 0; + } + fresh = true; + } + else + return status; + *value = hh * 100 + mm; + Set(); + status = osContinue; + } + return status; +} + +// --- cMenuEditChrItem ------------------------------------------------------ + +class cMenuEditChrItem : public cMenuEditItem { +private: + char *value; + const char *allowed; + const char *current; + virtual void Set(void); +public: + cMenuEditChrItem(const char *Name, char *Value, const char *Allowed); + ~cMenuEditChrItem(); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed) +:cMenuEditItem(Name) +{ + value = Value; + allowed = strdup(Allowed); + current = strchr(allowed, *Value); + if (!current) + current = allowed; + Set(); +} + +cMenuEditChrItem::~cMenuEditChrItem() +{ + delete allowed; +} + +void cMenuEditChrItem::Set(void) +{ + char buf[2]; + snprintf(buf, sizeof(buf), "%c", *value); + SetValue(buf); +} + +eOSStatus cMenuEditChrItem::ProcessKey(eKeys Key) +{ + eOSStatus status = cMenuEditItem::ProcessKey(Key); + + if (status == osUnknown) { + if (Key == kLeft) { + if (current > allowed) + current--; + } + else if (Key == kRight) { + if (*(current + 1)) + current++; + } + else + return status; + *value = *current; + Set(); + status = osContinue; + } + return status; +} + +// --- cMenuEditStrItem ------------------------------------------------------ + +class cMenuEditStrItem : public cMenuEditItem { +private: + char *value; + int length; + const char *allowed; + int pos; + virtual void Set(void); + char Inc(char c, bool Up); +public: + cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed); + ~cMenuEditStrItem(); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed) +:cMenuEditItem(Name) +{ + value = Value; + length = Length; + allowed = strdup(Allowed); + pos = -1; + Set(); +} + +cMenuEditStrItem::~cMenuEditStrItem() +{ + delete allowed; +} + +void cMenuEditStrItem::Set(void) +{ + char buf[1000]; + if (pos >= 0) { + strncpy(buf, value, pos); + char *s = value[pos] != ' ' ? value + pos + 1 : ""; + snprintf(buf + pos, sizeof(buf) - pos - 2, "[%c]%s", *(value + pos), s); + SetValue(buf); + } + else + SetValue(value); +} + +char cMenuEditStrItem::Inc(char c, bool Up) +{ + const char *p = strchr(allowed, c); + if (!p) + p = allowed; + if (Up) { + if (!*++p) + p = allowed; + } + else if (--p < allowed) + p = allowed + strlen(allowed) - 1; + return *p; +} + +eOSStatus cMenuEditStrItem::ProcessKey(eKeys Key) +{ + switch (Key) { + case kLeft: if (pos > 0) { + if (value[pos] == ' ') + value[pos] = 0; + pos--; + } + break; + case kRight: if (pos < length && value[pos] != ' ') { + if (++pos >= int(strlen(value))) { + value[pos] = ' '; + value[pos + 1] = 0; + } + } + break; + case kUp: + case kDown: if (pos >= 0) + value[pos] = Inc(value[pos], Key == kUp); + else + return cMenuEditItem::ProcessKey(Key); + break; + case kOk: if (pos >= 0) { + if (value[pos] == ' ') + value[pos] = 0; + pos = -1; + break; + } + // run into default + default: return cMenuEditItem::ProcessKey(Key); + } + Set(); + return osContinue; +} + +// --- cMenuEditChannel ------------------------------------------------------ + +class cMenuEditChannel : public cOsdMenu { +private: + cChannel *channel; + cChannel data; +public: + cMenuEditChannel(int Index); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditChannel::cMenuEditChannel(int Index) +:cOsdMenu("Edit channel", 14) +{ + channel = Channels.Get(Index); + if (channel) { + data = *channel; + Add(new cMenuEditStrItem("Name", data.name, sizeof(data.name), FileNameChars)); + Add(new cMenuEditIntItem("Frequency", &data.frequency, 10000, 13000)); //TODO exact limits??? + Add(new cMenuEditChrItem("Polarization", &data.polarization, "hv")); + Add(new cMenuEditIntItem("Diseqc", &data.diseqc, 0, 10)); //TODO exact limits??? + Add(new cMenuEditIntItem("Srate", &data.srate, 22000, 27500)); //TODO exact limits - toggle??? + Add(new cMenuEditIntItem("Vpid", &data.vpid, 0, 10000)); //TODO exact limits??? + Add(new cMenuEditIntItem("Apid", &data.apid, 0, 10000)); //TODO exact limits??? + } +} + +eOSStatus cMenuEditChannel::ProcessKey(eKeys Key) +{ + eOSStatus status = cOsdMenu::ProcessKey(Key); + + if (status == osUnknown) { + if (Key == kOk) { + if (channel) + *channel = data; + Channels.Save(); + status = osBack; + } + } + return status; +} + +// --- cMenuChannelItem ------------------------------------------------------ + +class cMenuChannelItem : public cOsdItem { +private: + int index; + cChannel *channel; +public: + cMenuChannelItem(int Index, cChannel *Channel); + virtual void Set(void); + }; + +cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel) +{ + index = Index; + channel = Channel; + Set(); +} + +void cMenuChannelItem::Set(void) +{ + char *buffer = NULL; + asprintf(&buffer, "%d\t%s", index + 1, channel->name); // user visible channel numbers start with '1' + SetText(buffer, false); +} + +// --- cMenuChannels --------------------------------------------------------- + +class cMenuChannels : public cOsdMenu { +public: + cMenuChannels(void); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuChannels::cMenuChannels(void) +:cOsdMenu("Channels", 4) +{ + //TODO + int i = 0; + cChannel *channel; + + while ((channel = Channels.Get(i)) != NULL) { + Add(new cMenuChannelItem(i, channel), i == CurrentChannel); + i++; + } +} + +eOSStatus cMenuChannels::ProcessKey(eKeys Key) +{ + eOSStatus status = cOsdMenu::ProcessKey(Key); + + if (status == osUnknown) { + switch (Key) { + //TODO need to block this if we are already editing a channel! + case kRight: return AddSubMenu(new cMenuEditChannel(Current())); + case kOk: { + cChannel *ch = Channels.Get(Current()); + if (ch) + ch->Switch(); + return osEnd; + } + default: break; + } + } + return status; +} + +// --- cMenuEditTimer -------------------------------------------------------- + +class cMenuEditTimer : public cOsdMenu { +private: + cTimer *timer; + cTimer data; +public: + cMenuEditTimer(int Index); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuEditTimer::cMenuEditTimer(int Index) +:cOsdMenu("Edit timer", 10) +{ + timer = Timers.Get(Index); + if (timer) { + data = *timer; + Add(new cMenuEditBoolItem("Active", &data.active)); + Add(new cMenuEditChanItem("Channel", &data.channel)); + Add(new cMenuEditDayItem( "Day", &data.day)); + Add(new cMenuEditTimeItem("Start", &data.start)); + Add(new cMenuEditTimeItem("Stop", &data.stop)); +//TODO VPS??? + Add(new cMenuEditChrItem( "Quality", &data.quality, DvbQuality)); + Add(new cMenuEditIntItem( "Priority", &data.priority, 0, 99)); + Add(new cMenuEditIntItem( "Lifetime", &data.lifetime, 0, 99)); + Add(new cMenuEditStrItem( "File", data.file, sizeof(data.file), FileNameChars)); + } +} + +eOSStatus cMenuEditTimer::ProcessKey(eKeys Key) +{ + eOSStatus status = cOsdMenu::ProcessKey(Key); + + if (status == osUnknown) { + if (Key == kOk) { + if (timer && memcmp(timer, &data, sizeof(data)) != 0) { + *timer = data; + Timers.Save(); + isyslog(LOG_INFO, "timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive"); + } + status = osBack; + } + } + return status; +} + +// --- cMenuTimerItem -------------------------------------------------------- + +class cMenuTimerItem : public cOsdItem { +private: + int index; + cTimer *timer; +public: + cMenuTimerItem(int Index, cTimer *Timer); + virtual void Set(void); + }; + +cMenuTimerItem::cMenuTimerItem(int Index, cTimer *Timer) +{ + index = Index; + timer = Timer; + Set(); +} + +void cMenuTimerItem::Set(void) +{ + char *buffer = NULL; + asprintf(&buffer, "%d\t%c\t%d\t%s\t%02d:%02d\t%02d:%02d", index + 1, + timer->active ? '>' : ' ', + timer->channel, + timer->PrintDay(timer->day), + timer->start / 100, + timer->start % 100, + timer->stop / 100, + timer->stop % 100); // user visible timer numbers start with '1' + SetText(buffer, false); +} + +// --- cMenuTimer ------------------------------------------------------------ + +class cMenuTimer : public cOsdMenu { +public: + cMenuTimer(void); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +cMenuTimer::cMenuTimer(void) +:cOsdMenu("Timer", 3, 2, 4, 10, 6) +{ + int i = 0; + cTimer *timer; + + while ((timer = Timers.Get(i)) != NULL) { + Add(new cMenuTimerItem(i, timer)); + i++; + } +} + +eOSStatus cMenuTimer::ProcessKey(eKeys Key) +{ + eOSStatus status = cOsdMenu::ProcessKey(Key); + + if (status == osUnknown) { + switch (Key) { + //TODO need to block this if we are already editing a channel! + case kOk: return AddSubMenu(new cMenuEditTimer(Current())); + //TODO new timer + //TODO delete timer + case kLeft: + case kRight: + { + cTimer *timer = Timers.Get(Current()); + if (timer) { + timer->active = (Key == kRight); + isyslog(LOG_INFO, "timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de"); + RefreshCurrent(); + DisplayCurrent(true); + Timers.Save(); + } + } + default: break; + } + } + return status; +} + +// --- cMenuMain ------------------------------------------------------------- + +cMenuMain::cMenuMain(void) +:cOsdMenu("Main") +{ + //TODO + Add(new cOsdItem("Channels", osChannels)); + Add(new cOsdItem("Timer", osTimer)); + Add(new cOsdItem("Recordings", osRecordings)); +} + +eOSStatus cMenuMain::ProcessKey(eKeys Key) +{ + eOSStatus status = cOsdMenu::ProcessKey(Key); + + switch (status) { + case osChannels: return AddSubMenu(new cMenuChannels); + case osTimer: return AddSubMenu(new cMenuTimer); + //TODO Replay + default: break; + } + return status; +} + diff --git a/menu.h b/menu.h new file mode 100644 index 00000000..849d8ee3 --- /dev/null +++ b/menu.h @@ -0,0 +1,21 @@ +/* + * menu.h: The actual menu implementations + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: menu.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef _MENU_H +#define _MENU_H + +#include "osd.h" + +class cMenuMain : public cOsdMenu { +public: + cMenuMain(void); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +#endif //_MENU_H diff --git a/osd.c b/osd.c new file mode 100644 index 00000000..d7ab6993 --- /dev/null +++ b/osd.c @@ -0,0 +1,196 @@ +/* + * osd.c: Abstract On Screen Display layer + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: osd.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "osd.h" +#include +#include + +// --- cOsdItem -------------------------------------------------------------- + +cOsdItem::cOsdItem(eOSStatus Status) +{ + text = NULL; + offset = -1; + status = Status; + fresh = false; +} + +cOsdItem::cOsdItem(char *Text, eOSStatus Status) +{ + text = NULL; + offset = -1; + status = Status; + fresh = false; + SetText(Text); +} + +cOsdItem::~cOsdItem() +{ + delete text; +} + +void cOsdItem::SetText(char *Text, bool Copy) +{ + delete text; + text = Copy ? strdup(Text) : Text; +} + +void cOsdItem::Display(int Offset, bool Current) +{ + fresh |= Offset >= 0; + Current |= Offset < 0; + if (Offset >= 0) + offset = Offset; + //TODO current if Offset == -1 ??? + if (offset >= 0) + Interface.WriteText(0, offset + 2, text, Current); +} + +eOSStatus cOsdItem::ProcessKey(eKeys Key) +{ + return Key == kOk ? status : osUnknown; +} + +// --- cOsdMenu -------------------------------------------------------------- + +cOsdMenu::cOsdMenu(char *Title, int c0, int c1, int c2, int c3, int c4) +{ + title = strdup(Title); + cols[0] = c0; + cols[1] = c1; + cols[2] = c2; + cols[3] = c3; + cols[4] = c4; + first = count = 0; + current = -1; + subMenu = NULL; + Interface.Open(); +} + +cOsdMenu::~cOsdMenu() +{ + delete title; + delete subMenu; + Interface.Clear(); + Interface.Close(); +} + +void cOsdMenu::Add(cOsdItem *Item, bool Current) +{ + cList::Add(Item); + count++; + if (Current && current < 0) + current = Item->Index(); +} + +void cOsdMenu::Display(void) +{ + Interface.Clear(); + Interface.SetCols(cols); + Interface.WriteText(0, 0, title); + if (current < 0 && count) + current = 0; // just for safety - there HAS to be a current item! + int n = 0; + if (current - first >= MAXOSDITEMS) { + first = current - MAXOSDITEMS / 2; + if (first + MAXOSDITEMS > count) + first = count - MAXOSDITEMS; + if (first < 0) + first = 0; + } + for (int i = first; i < count; i++) { + cOsdItem *item = Get(i); + if (item) + item->Display(i - first, i == current); + if (++n == MAXOSDITEMS) //TODO get this from Interface!!! + break; + } +} + +void cOsdMenu::RefreshCurrent(void) +{ + cOsdItem *item = Get(current); + if (item) + item->Set(); +} + +void cOsdMenu::DisplayCurrent(bool Current) +{ + cOsdItem *item = Get(current); + if (item) + item->Display(current - first, Current); +} + +void cOsdMenu::CursorUp(void) +{ + if (current > 0) { + DisplayCurrent(false); + if (--current < first) { + first -= MAXOSDITEMS; + if (first < 0) + first = 0; + Display(); + } + else + DisplayCurrent(true); + } +} + +void cOsdMenu::CursorDown(void) +{ + if (current < count - 1) { + DisplayCurrent(false); + if (++current >= first + MAXOSDITEMS) { + first += MAXOSDITEMS; + if (first > count - MAXOSDITEMS) + first = count - MAXOSDITEMS; + Display(); + } + else + DisplayCurrent(true); + } +} + +eOSStatus cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) +{ + delete subMenu; + subMenu = SubMenu; + subMenu->Display(); + return osContinue; // convenience return value (see cMenuMain) +} + +eOSStatus cOsdMenu::ProcessKey(eKeys Key) +{ + if (subMenu) { + eOSStatus status = subMenu->ProcessKey(Key); + if (status == osBack) { + delete subMenu; + subMenu = NULL; + RefreshCurrent(); + Display(); + status = osContinue; + } + return status; + } + + cOsdItem *item = Get(current); + if (item) { + eOSStatus status = item->ProcessKey(Key); + if (status != osUnknown) + return status; + } + switch (Key) { + case kUp: CursorUp(); break; + case kDown: CursorDown(); break; + case kBack: return osBack; + default: return osUnknown; + } + return osContinue; +} + diff --git a/osd.h b/osd.h new file mode 100644 index 00000000..392f1a75 --- /dev/null +++ b/osd.h @@ -0,0 +1,68 @@ +/* + * osd.h: Abstract On Screen Display layer + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: osd.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __OSD_H +#define __OSD_H + +#include "config.h" +#include "interface.h" +#include "tools.h" + +#define MAXOSDITEMS 9 + +enum eOSStatus { osUnknown, + osContinue, + osProcessed, + osChannels, + osTimer, + osRecordings, + osBack, + osEnd, + }; + +class cOsdItem : public cListObject { +private: + char *text; + int offset; + eOSStatus status; +protected: + bool fresh; +public: + cOsdItem(eOSStatus Status = osUnknown); + cOsdItem(char *Text, eOSStatus Status = osUnknown); + virtual ~cOsdItem(); + void SetText(char *Text, bool Copy = true); + char *Text(void) { return text; } + void Display(int Offset = -1, bool Current = false); + virtual void Set(void) {} + virtual eOSStatus ProcessKey(eKeys Key); + }; + +class cOsdMenu : public cList { +private: + char *title; + int cols[cInterface::MaxCols]; + int first, current, count; + cOsdMenu *subMenu; +protected: + void RefreshCurrent(void); + void DisplayCurrent(bool Current); + void CursorUp(void); + void CursorDown(void); + eOSStatus AddSubMenu(cOsdMenu *SubMenu); +public: + cOsdMenu(char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); + virtual ~cOsdMenu(); + int Current(void) { return current; } + void Add(cOsdItem *Item, bool Current = false); + void Display(void); + virtual eOSStatus ProcessKey(eKeys Key); + }; + +#endif //__OSD_H diff --git a/osm.c b/osm.c new file mode 100644 index 00000000..cc017451 --- /dev/null +++ b/osm.c @@ -0,0 +1,119 @@ +/* + * osm.c: On Screen Menu for the Video Disk Recorder + * + * Copyright (C) 2000 Klaus Schmidinger + * + * 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. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * The author can be reached at kls@cadsoft.de + * + * The project's page is at http://www.cadsoft.de/people/kls/vdr + * + * $Id: osm.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "config.h" +#include "dvbapi.h" +#include "interface.h" +#include "menu.h" +#include "tools.h" + +#ifdef DEBUG_REMOTE +#define KEYS_CONF "keys-pc.conf" +#else +#define KEYS_CONF "keys.conf" +#endif + +int main(int argc, char *argv[]) +{ + openlog("vdr", LOG_PID | LOG_CONS, LOG_USER); + isyslog(LOG_INFO, "started"); + + Channels.Load("channels.conf"); + Timers.Load("timers.conf"); + if (!Keys.Load(KEYS_CONF)) + Interface.LearnKeys(); + Interface.Init(); + + cChannel::SwitchTo(CurrentChannel); + + cMenuMain *Menu = NULL; + cTimer *Timer = NULL; + cDvbRecorder *Recorder = NULL; + + for (;;) { + //TODO check for free disk space and delete files if necessary/possible + // in case there is an ongoing recording + if (!Timer && (Timer = cTimer::GetMatch()) != NULL) { + // switch to channel: + isyslog(LOG_INFO, "timer %d start", Timer->Index() + 1); + delete Menu; + Menu = NULL; + cChannel::SwitchTo(Timer->channel - 1); + ChannelLocked = true; + // start recording: + delete Recorder; + Recorder = new cDvbRecorder; + //TODO special filename handling!!! + if (!Recorder->Record(Timer->file, Timer->quality)) { + delete Recorder; + Recorder = NULL; + } + } + if (Timer) { + if (!Timer->Matches()) { + // stop recording: + if (Recorder) + Recorder->Stop(); + // end timer: + ChannelLocked = false; + isyslog(LOG_INFO, "timer %d stop", Timer->Index() + 1); + Timer = NULL; + //TODO switch back to the previous channel??? + //TODO clear single event timer??? + } + } + eKeys key = Interface.GetKey(); + if (Menu) { + switch (Menu->ProcessKey(key)) { + default: if (key != kMenu) + break; + case osBack: + case osEnd: delete Menu; + Menu = NULL; + break; + } + } + else { + switch (key) { + case kMenu: Menu = new cMenuMain; + Menu->Display(); + break; + case kUp: + case kDown: { + int n = CurrentChannel + (key == kUp ? 1 : -1); + cChannel *channel = Channels.Get(n); + if (channel) + channel->Switch(); + } + break; + default: break; + } + } + } + closelog(); + return 1; +} diff --git a/remote.c b/remote.c new file mode 100644 index 00000000..c2fb36f6 --- /dev/null +++ b/remote.c @@ -0,0 +1,257 @@ +/* + * remote.c: Interface to the Remote Control Unit + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: remote.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "remote.h" +#include +#include +#include +#include +#include +#include +#include +#include "tools.h" + +cRcIo::cRcIo(char *DeviceName) +{ + dp = 0; + mode = modeB; + code = 0; + address = 0xFFFF; + t = 0; + firstTime = lastTime = 0; + minDelta = 0; + lastCommand = 0; + if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) { + struct termios t; + if (tcgetattr(f, &t) == 0) { + cfsetspeed(&t, B9600); + cfmakeraw(&t); + if (tcsetattr(f, TCSAFLUSH, &t) == 0) + return; + } + close(f); + } + f = -1; +} + +cRcIo::~cRcIo() +{ + if (f >= 0) + close(f); +} + +int cRcIo::ReceiveByte(bool Wait) +{ + // Returns the byte if one was received within 1 second, -1 otherwise + if (f >= 0) { + fd_set set; + struct timeval timeout; + timeout.tv_sec = Wait ? 1 : 0; + timeout.tv_usec = Wait ? 0 : 10000; + FD_ZERO(&set); + FD_SET(f, &set); + if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0) { + if (FD_ISSET(f, &set)) { + unsigned char b; + if (read(f, &b, 1) == 1) + return b; + } + } + } + return -1; +} + +bool cRcIo::SendByteHandshake(unsigned char c) +{ + if (f >= 0 && write(f, &c, 1) == 1) { + for (int reply = ReceiveByte(); reply >= 0;) { + if (reply == c) + return true; + else if (reply == 'X') { + // skip any incoming RC code - it will come again + for (int i = 6; i--;) { + if (ReceiveByte(false) < 0) + return false; + } + } + else + return false; + } + } + return false; +} + +bool cRcIo::SendByte(unsigned char c) +{ + for (int retry = 5; retry--;) { + if (SendByteHandshake(c)) + return true; + } + return false; +} + +void cRcIo::Flush(int WaitSeconds) +{ + time_t t0 = time(NULL); + + for (;;) { + while (ReceiveByte(false) >= 0) + t0 = time(NULL); + if (time(NULL) - t0 >= WaitSeconds) + break; + } +} + +bool cRcIo::SetCode(unsigned char Code, unsigned short Address) +{ + code = Code; + address = Address; + minDelta = 200; + return SendCommand(code); +} + +bool cRcIo::SetMode(unsigned char Mode) +{ + mode = Mode; + return SendCommand(mode); +} + +bool cRcIo::GetCommand(unsigned int *Command, unsigned short *Address) +{ +#pragma pack(1) + union { + struct { + unsigned short address; + unsigned int command; + } data; + unsigned char raw[6]; + } buffer; +#pragma pack() + + Flush(); + if (Command && ReceiveByte() == 'X') { + for (int i = 0; i < 6; i++) { + int b = ReceiveByte(false); + if (b >= 0) + buffer.raw[i] = b; + else + return false; + } + if (Address) + *Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order" + else if (address != ntohs(buffer.data.address)) + return false; + *Command = ntohl(buffer.data.command); + if (code == 'B' && address == 0x0000 && *Command == 0x00004000) + // Well, well, if it isn't the "d-box"... + // This remote control sends the above command before and after + // each keypress - let's just drop this: + return false; + if (*Command == lastCommand) { + // let's have a timeout to avoid getting overrun by commands + int now = time_ms(); + int delta = now - lastTime; + if (delta < minDelta) + minDelta = delta; // dynamically adjust to the smallest delta + lastTime = now; + if (delta < minDelta * 1.3) { // if commands come in rapidly... + if (now - firstTime < 250) + return false; // ...repeat function kicks in after 250ms + return true; + } + } + lastTime = firstTime = time_ms(); + lastCommand = *Command; + return true; + } + if (time(NULL) - t > 60) { + SendCommand(code); // in case the PIC listens to the wrong code + t = time(NULL); + } + return false; +} + +bool cRcIo::SendCommand(unsigned char Cmd) +{ + return SendByte(Cmd | 0x80); +} + +bool cRcIo::Digit(int n, int v) +{ + return SendByte(((n & 0x03) << 5) | (v & 0x0F) | (((dp >> n) & 0x01) << 4)); +} + +bool cRcIo::Number(int n, bool Hex) +{ + if (!Hex) { + char buf[8]; + sprintf(buf, "%4d", n & 0xFFFF); + n = 0; + for (char *d = buf; *d; d++) { + if (*d == ' ') + *d = 0xF; + n = (n << 4) | ((*d - '0') & 0x0F); + } + } + for (int i = 0; i < 4; i++) { + if (!Digit(i, n)) + return false; + n >>= 4; + } + return SendCommand(mode); +} + +bool cRcIo::String(char *s) +{ + char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP "; + int n = 0; + + for (int i = 0; *s && i < 4; s++, i++) { + n <<= 4; + for (char *c = chars; *c; c++) { + if (*c == *s) { + n |= c - chars; + break; + } + } + } + return Number(n, mode == modeH); +} + +bool cRcIo::DetectCode(unsigned char *Code, unsigned short *Address) +{ + // Caller should initialize 'Code' to 0 and call DetectCode() + // until it returns true. Whenever DetectCode() returns false + // and 'Code' is not 0, the caller can use 'Code' to display + // a message like "Trying code '%c'". If false is returned and + // 'Code' is 0, all possible codes have been tried and the caller + // can either stop calling DetectCode() (and give some error + // message), or start all over again. + if (*Code < 'A' || *Code > 'D') { + *Code = 'A'; + return false; + } + if (*Code <= 'D') { + SetMode(modeH); + char buf[5]; + sprintf(buf, "C0D%c", *Code); + String(buf); + SetCode(*Code, 0); + unsigned int Command; + if (GetCommand(&Command, Address)) + return true; + if (*Code < 'D') { + (*Code)++; + return false; + } + } + *Code = 0; + return false; +} + diff --git a/remote.h b/remote.h new file mode 100644 index 00000000..68279291 --- /dev/null +++ b/remote.h @@ -0,0 +1,43 @@ +/* + * remote.h: Interface to the Remote Control Unit + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: remote.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __REMOTE_H +#define __REMOTE_H + +#include +#include + +class cRcIo { +private: + int f; + unsigned char dp, code, mode; + unsigned short address; + time_t t; + int firstTime, lastTime, minDelta; + unsigned int lastCommand; + bool SendCommand(unsigned char Cmd); + int ReceiveByte(bool Wait = true); + bool SendByteHandshake(unsigned char c); + bool SendByte(unsigned char c); +public: + enum { modeH = 'h', modeB = 'b', modeS = 's' }; + cRcIo(char *DeviceName); + ~cRcIo(); + void Flush(int WaitSeconds = 0); + bool SetCode(unsigned char Code, unsigned short Address); + bool SetMode(unsigned char Mode); + bool GetCommand(unsigned int *Command, unsigned short *Address = NULL); + bool Digit(int n, int v); + bool Number(int n, bool Hex = false); + void Points(unsigned char Dp) { dp = Dp; } + bool String(char *s); + bool DetectCode(unsigned char *Code, unsigned short *Address); + }; + +#endif //__REMOTE_H diff --git a/timers.conf b/timers.conf new file mode 100644 index 00000000..ac002a99 --- /dev/null +++ b/timers.conf @@ -0,0 +1,9 @@ +1:2:-----S-:2210:2320:H:99:99:Wochenshow +1:3:M------:2125:2205:H:99:99:Neues +1:15:MTWTF--:1828:1901:M:10:5:nano +1:2:-----S-:1737:1827:H:99:99:kls/StarTrek/DS9 +1:3:M------:2110:2230:H:99:99:SevenDays +1:3:---T---:2215:2300:H:99:99:SwItch +0:1:1:0:0:H:99:99:# +0:1:1:0:0:H:99:99:# +0:1:1:0:0:L:0:5:# diff --git a/tools.c b/tools.c new file mode 100644 index 00000000..e0eeaefd --- /dev/null +++ b/tools.c @@ -0,0 +1,124 @@ +/* + * tools.c: Various tools + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: tools.c 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#include "tools.h" +#include +#include + +int time_ms(void) +{ + struct timeval t; + if (gettimeofday(&t, NULL) == 0) + return t.tv_sec * 1000 + t.tv_usec / 1000; + return 0; +} + +// --- cListObject ----------------------------------------------------------- + +cListObject::cListObject(void) +{ + prev = next = NULL; +} + +cListObject::~cListObject() +{ +} + +void cListObject::Append(cListObject *Object) +{ + next = Object; + Object->prev = this; +} + +void cListObject::Unlink(void) +{ + if (next) + next->prev = prev; + if (prev) + prev->next = next; +} + +int cListObject::Index(void) +{ + cListObject *p = prev; + int i = 0; + + while (p) { + i++; + p = p->prev; + } + return i; +} + +// --- cListBase ------------------------------------------------------------- + +cListBase::cListBase(void) +{ + objects = lastObject = NULL; +} + +cListBase::~cListBase() +{ + Clear(); + while (objects) { + cListObject *object = objects->Next(); + delete objects; + objects = object; + } +} + +void cListBase::Add(cListObject *Object) +{ + if (lastObject) + lastObject->Append(Object); + else + objects = Object; + lastObject = Object; +} + +void cListBase::Del(cListObject *Object) +{ + if (Object == objects) + objects = Object->Next(); + if (Object == lastObject) + lastObject = Object->Prev(); + Object->Unlink(); + delete Object; +} + +void cListBase::Clear(void) +{ + while (objects) { + cListObject *object = objects->Next(); + delete objects; + objects = object; + } + objects = lastObject = NULL; +} + +cListObject *cListBase::Get(int Index) +{ + cListObject *object = objects; + while (object && Index-- > 0) + object = object->Next(); + return object; +} + +int cListBase::Count(void) +{ + int n = 0; + cListObject *object = objects; + + while (object) { + n++; + object = object->Next(); + } + return n; +} + diff --git a/tools.h b/tools.h new file mode 100644 index 00000000..43f85254 --- /dev/null +++ b/tools.h @@ -0,0 +1,54 @@ +/* + * tools.h: Various tools + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: tools.h 1.1 2000/02/19 13:36:48 kls Exp $ + */ + +#ifndef __TOOLS_H +#define __TOOLS_H + +#include + +//TODO +#define dsyslog syslog +#define esyslog syslog +#define isyslog syslog + +class cListObject { +private: + cListObject *prev, *next; +public: + cListObject(void); + virtual ~cListObject(); + void Append(cListObject *Object); + void Unlink(void); + int Index(void); + cListObject *Prev(void) { return prev; } + cListObject *Next(void) { return next; } + }; + +class cListBase { +protected: + cListObject *objects, *lastObject; + cListBase(void); +public: + virtual ~cListBase(); + void Add(cListObject *Object); + void Del(cListObject *Object); + void Clear(void); + cListObject *Get(int Index); + int Count(void); + }; + +template class cList : public cListBase { +public: + T *Get(int Index) { return (T *)cListBase::Get(Index); } + T *First(void) { return (T *)objects; } + }; + +int time_ms(void); + +#endif //__TOOLS_H -- cgit v1.2.3