diff options
author | Klaus Schmidinger <vdr@tvdr.de> | 2000-02-19 13:36:48 +0100 |
---|---|---|
committer | Klaus Schmidinger <vdr@tvdr.de> | 2000-02-19 13:36:48 +0100 |
commit | 4a9d9c5876cde9f21ccd165a7630727e6aca576a (patch) | |
tree | 84548734048499e913f200e1359acec4fa441fb0 | |
download | vdr-4a9d9c5876cde9f21ccd165a7630727e6aca576a.tar.gz vdr-4a9d9c5876cde9f21ccd165a7630727e6aca576a.tar.bz2 |
Initial revision0.0.1
-rw-r--r-- | Makefile | 37 | ||||
-rw-r--r-- | README | 122 | ||||
-rw-r--r-- | TODO | 13 | ||||
-rw-r--r-- | channels.conf | 109 | ||||
-rw-r--r-- | config.c | 333 | ||||
-rw-r--r-- | config.h | 163 | ||||
-rw-r--r-- | dvbapi.c | 166 | ||||
-rw-r--r-- | dvbapi.h | 65 | ||||
-rw-r--r-- | interface.c | 298 | ||||
-rw-r--r-- | interface.h | 41 | ||||
-rw-r--r-- | keys-pc.conf | bin | 0 -> 225 bytes | |||
-rw-r--r-- | keys.conf | 19 | ||||
-rw-r--r-- | menu.c | 745 | ||||
-rw-r--r-- | menu.h | 21 | ||||
-rw-r--r-- | osd.c | 196 | ||||
-rw-r--r-- | osd.h | 68 | ||||
-rw-r--r-- | osm.c | 119 | ||||
-rw-r--r-- | remote.c | 257 | ||||
-rw-r--r-- | remote.h | 43 | ||||
-rw-r--r-- | timers.conf | 9 | ||||
-rw-r--r-- | tools.c | 124 | ||||
-rw-r--r-- | tools.h | 54 |
22 files changed, 3002 insertions, 0 deletions
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 @@ -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. + @@ -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 <ctype.h> +#include <stdlib.h> +#include <time.h> +#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 <stdio.h> +#include <string.h> +#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 T> class cConfig : public cList<T> { +private: + char *fileName; + void Clear(void) + { + delete fileName; + cList<T>::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<cChannel> {}; +class cTimers : public cConfig<cTimer> {}; + +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 <fcntl.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <unistd.h> +#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 <ncurses.h> +#include <unistd.h> +#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 Binary files differnew file mode 100644 index 00000000..c57613f0 --- /dev/null +++ b/keys-pc.conf 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 @@ -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 <limits.h> +#include <stdio.h> +#include <string.h> +#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; +} + @@ -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 @@ -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 <assert.h> +#include <string.h> + +// --- 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<cOsdItem>::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; +} + @@ -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<cOsdItem> { +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 @@ -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 <fcntl.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <termios.h> +#include <unistd.h> +#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 <stdio.h> +#include <time.h> + +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 <stdlib.h> +#include <sys/time.h> + +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 <syslog.h> + +//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 T> 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 |