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.confBinary files differ new 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 | 
