diff --git a/HISTORY b/HISTORY new file mode 100644 index 00000000..f7eeb65e --- /dev/null +++ b/HISTORY @@ -0,0 +1,12 @@ +Video Disk Recorder OSM Revision History +---------------------------------------- + +2000-02-19: Version 0.01 (Initial revision). + +2000-03-11: Version 0.02 + +- Fixed compilation with only DEBUG_REMOTE=1. +- Menus now use colors. +- Support for "Red", "Green", "Yellow", "Blue" buttons. +- Channels and Timers can now be added, deleted and moved. +- Basic record/play file handling support (no actual record/playback yet). diff --git a/Makefile b/Makefile index 6696dff1..b5153cfc 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,9 @@ # 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 $ +# $Id: Makefile 1.2 2000/03/05 13:51:57 kls Exp $ -OBJS = config.o dvbapi.o interface.o menu.o osd.o remote.o tools.o osm.o +OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o tools.o osm.o ifdef DEBUG_REMOTE DEFINES += -DDEBUG_REMOTE @@ -24,9 +24,10 @@ 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 +menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h +osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h +osm.o : osm.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h +recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h remote.o : remote.c remote.h tools.h tools.o : tools.c tools.h diff --git a/README b/README index e4fa836e..5c6928cb 100644 --- a/README +++ b/README @@ -36,10 +36,34 @@ 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. +In order for the menu colors to work correctly you may want +to replace the function RGB2YUV() in DVB/driver/dvb.c with + +static u32 RGB2YUV(u16 R, u16 G, u16 B) +{ + u16 y, u, v; + u16 Y, Cr, Cb; + + y = R * 77 + G * 150 + B * 29; // Luma=0.299R+0.587G+0.114B 0..65535 + u = 2048+B * 8 -(y>>5); // Cr 0..4095 + v = 2048+R * 8 -(y>>5); // Cb 0..4095 + + Y = y >> 8; + Cb= u >> 4; + Cr= v >> 4; + + return Cr|(Cb<<16)|(Y<<8); +} + +(this may no longer be necessary with driver versions after 0.03c). + 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. +installed. There may be several warnings about "implicit declaration +of function `int asprintf(...)'" during the compilation, which I was +unable to avoid (anybody know how to avoid them?). Just ignore them, +the program will work, anyway. There are two macros you can use to customize the 'osm' program at compile time. Adding "DEBUG_REMOTE=1" to the 'make' call @@ -75,10 +99,10 @@ 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, +the basic data transfer 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 +and a "Down" key, so that you can switch channels. The rest of the key definitions is optional, but the more keys you define, the more you will be able to navigate through the menus. @@ -97,9 +121,6 @@ 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. @@ -110,6 +131,9 @@ 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. +The "Red", "Green", "Yellow" and "Blue" buttons have special meanings +in the various menus and are listed at the bottom of the on-screen-display. + At any point in the menu system, pressing the "Menu" key again will immediately leave the menu system. diff --git a/TODO b/TODO index 32793469..35e3aa30 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,12 @@ 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). + Maybe we can do this with only a single card, if the card + driver/firmware allows simultaneuos record/playback? * 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 index 9daca0af..1b579636 100644 --- a/channels.conf +++ b/channels.conf @@ -42,7 +42,7 @@ 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 +test card:11798:h:1:27500:511:512 Mosaico:11934:v:1:27500:165:100 Andalucia TV:11934:v:1:27500:166:104 TVC Internacional:11934:v:1:27500:167:108 diff --git a/config.c b/config.c index 50315691..af97a5cb 100644 --- a/config.c +++ b/config.c @@ -4,37 +4,40 @@ * 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 $ + * $Id: config.c 1.2 2000/03/05 16:14:27 kls Exp $ */ #include "config.h" #include #include -#include #include "dvbapi.h" #include "interface.h" // -- cKeys ------------------------------------------------------------------ tKey keyTable[] = { // "Up" and "Down" must be the first two keys! - { kUp, "Up", 0 }, - { kDown, "Down", 0 }, - { kMenu, "Menu", 0 }, - { kOk, "Ok", 0 }, - { kBack, "Back", 0 }, - { kLeft, "Left", 0 }, - { kRight, "Right", 0 }, - { k0, "0", 0 }, - { k1, "1", 0 }, - { k2, "2", 0 }, - { k3, "3", 0 }, - { k4, "4", 0 }, - { k5, "5", 0 }, - { k6, "6", 0 }, - { k7, "7", 0 }, - { k8, "8", 0 }, - { k9, "9", 0 }, - { kNone, "", 0 }, + { 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 }, + { kRed, "Red", 0 }, + { kGreen, "Green", 0 }, + { kYellow, "Yellow", 0 }, + { kBlue, "Blue", 0 }, + { kNone, "", 0 }, }; cKeys::cKeys(void) @@ -160,6 +163,17 @@ cChannel::cChannel(void) *name = 0; } +cChannel::cChannel(const cChannel *Channel) +{ + strcpy(name, Channel ? Channel->name : "Pro7"); + frequency = Channel ? Channel->frequency : 12480; + polarization = Channel ? Channel->polarization : 'v'; + diseqc = Channel ? Channel->diseqc : 1; + srate = Channel ? Channel->srate : 27500; + vpid = Channel ? Channel->vpid : 255; + apid = Channel ? Channel->apid : 256; +} + bool cChannel::Parse(char *s) { char *buffer = NULL; @@ -203,6 +217,17 @@ bool cChannel::SwitchTo(int i) cTimer::cTimer(void) { + startTime = stopTime = 0; + recording = false; + active = 1; + channel = CurrentChannel + 1; + day = 1; //XXX today! + start = 0; //XXX now! + stop = 0; //XXX now + 2h! +//TODO VPS??? + quality = 'H'; + priority = 99; + lifetime = 99; *file = 0; } @@ -211,6 +236,13 @@ int cTimer::TimeToInt(int t) return (t / 100 * 60 + t % 100) * 60; } +time_t cTimer::Day(time_t t) +{ + struct tm d = *localtime(&t); + d.tm_hour = d.tm_min = d.tm_sec = 0; + return mktime(&d); +} + int cTimer::ParseDay(char *s) { char *tail; @@ -270,13 +302,17 @@ 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::IsSingleEvent(void) +{ + return (day & 0x80000000) == 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; + struct tm now = *localtime(&t); + int weekday = now.tm_wday == 0 ? 6 : now.tm_wday - 1; // we start with monday==0! int begin = TimeToInt(start); int end = TimeToInt(stop); bool twoDays = (end < begin); @@ -291,20 +327,44 @@ bool cTimer::Matches(void) yesterdayMatches = true; } } - else if (day == now->tm_mday) + else if (day == now.tm_mday) todayMatches = true; else if (twoDays) { - t -= 86400; - now = localtime(&t); - if (day == now->tm_mday) + time_t ty = t - SECSINDAY; + if (day == localtime(&ty)->tm_mday) yesterdayMatches = true; } - return (todayMatches && current >= begin && (current <= end || twoDays)) - || (twoDays && yesterdayMatches && current <= end); + if (todayMatches || (twoDays && yesterdayMatches)) { + startTime = Day(t - (yesterdayMatches ? SECSINDAY : 0)) + begin; + stopTime = startTime + (twoDays ? SECSINDAY - begin + end : end - begin); + } + else + startTime = stopTime = 0; + return startTime <= t && t <= stopTime; } return false; } +time_t cTimer::StartTime(void) +{ + if (!startTime) + Matches(); + return startTime; +} + +time_t cTimer::StopTime(void) +{ + if (!stopTime) + Matches(); + return stopTime; +} + +void cTimer::SetRecording(bool Recording) +{ + recording = Recording; + isyslog(LOG_INFO, "timer %d %s", Index() + 1, recording ? "start" : "stop"); +} + cTimer *cTimer::GetMatch(void) { cTimer *t = (cTimer *)Timers.First(); diff --git a/config.h b/config.h index 3abb7a1a..51fd22e0 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * 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 $ + * $Id: config.h 1.2 2000/03/05 14:58:23 kls Exp $ */ #ifndef __CONFIG_H @@ -12,6 +12,7 @@ #include #include +#include #include "tools.h" #define MaxBuffer 1000 @@ -25,6 +26,10 @@ enum eKeys { // "Up" and "Down" must be the first two keys! kLeft, kRight, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, + kRed, + kGreen, + kYellow, + kBlue, kNone }; @@ -60,6 +65,7 @@ public: int vpid; int apid; cChannel(void); + cChannel(const cChannel *Channel); bool Parse(char *s); bool Save(FILE *f); bool Switch(void); @@ -67,8 +73,11 @@ public: }; class cTimer : public cListObject { +private: + time_t startTime, stopTime; public: enum { MaxFileName = 256 }; + bool recording; int active; int channel; int day; @@ -82,9 +91,14 @@ public: cTimer(void); bool Parse(char *s); bool Save(FILE *f); + bool IsSingleEvent(void); bool Matches(void); + time_t StartTime(void); + time_t StopTime(void); + void SetRecording(bool Recording); static cTimer *GetMatch(void); static int TimeToInt(int t); + static time_t Day(time_t t); static int ParseDay(char *s); static char *PrintDay(int d); }; diff --git a/dvbapi.c b/dvbapi.c index 5597fbd5..484a22e5 100644 --- a/dvbapi.c +++ b/dvbapi.c @@ -4,20 +4,14 @@ * 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 $ + * $Id: dvbapi.c 1.2 2000/03/11 10:39:09 kls Exp $ */ -// FIXME: these should be defined in ../DVB/driver/dvb.h!!! -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; - #include "dvbapi.h" #include #include #include #include -#include "../DVB/driver/dvb.h" #include "interface.h" #include "tools.h" @@ -60,6 +54,7 @@ bool DvbSetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, i cDvbRecorder::cDvbRecorder(void) { + recording = false; } cDvbRecorder::~cDvbRecorder() @@ -67,18 +62,41 @@ cDvbRecorder::~cDvbRecorder() Stop(); } +bool cDvbRecorder::Recording(void) +{ + return recording; +} + bool cDvbRecorder::Record(const char *FileName, char Quality) { isyslog(LOG_INFO, "record %s (%c)", FileName, Quality); - return true; + if (MakeDirs(FileName)) { + FILE *f = fopen(FileName, "a"); + if (f) { + fprintf(f, "%s, %c\n", FileName, Quality); + fclose(f); + recording = true; + // TODO + Interface.Error("Recording not yet implemented!"); + return true; + } + else { + Interface.Error("Can't write to file!"); + return false; + } + } // TODO return false; } bool cDvbRecorder::Play(const char *FileName, int Frame) { - isyslog(LOG_INFO, "play %s (%d)", FileName, Frame); - // TODO + if (!recording) { + isyslog(LOG_INFO, "play %s (%d)", FileName, Frame); + // TODO + Interface.Error("Playback not yet implemented!"); + return true; + } return false; } @@ -106,6 +124,7 @@ bool cDvbRecorder::Pause(void) void cDvbRecorder::Stop(void) { isyslog(LOG_INFO, "stop"); + recording = false; // TODO } @@ -116,9 +135,57 @@ int cDvbRecorder::Frame(void) return 0; } -// --------------------------------------------------------------------------- +// --- cDvbOsd --------------------------------------------------------------- -static void DvbOsdCmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, void *data = NULL) +cDvbOsd::cDvbOsd(void) +{ + cols = rows = 0; +#ifdef DEBUG_OSD + memset(&colorPairs, 0, sizeof(colorPairs)); + initscr(); + start_color(); + keypad(stdscr, TRUE); + nonl(); + cbreak(); + noecho(); + timeout(1000); + leaveok(stdscr, TRUE); + window = stdscr; +#endif +#ifdef DEBUG_REMOTE + initscr(); + keypad(stdscr, TRUE); + nonl(); + cbreak(); + noecho(); + timeout(1000); +#endif +} + +cDvbOsd::~cDvbOsd() +{ + Close(); +} + +#ifdef DEBUG_OSD +void cDvbOsd::SetColor(eDvbColor colorFg, eDvbColor colorBg) +{ + int color = (colorBg << 16) | colorFg | 0x80000000; + for (int i = 0; i < MaxColorPairs; i++) { + if (!colorPairs[i]) { + colorPairs[i] = color; + init_pair(i + 1, colorFg, colorBg); + wattrset(window, COLOR_PAIR(i + 1)); + break; + } + else if (color == colorPairs[i]) { + wattrset(window, COLOR_PAIR(i + 1)); + break; + } + } +} +#else +void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data) { int v = open(VIDEODEVICE, O_RDWR); @@ -130,37 +197,88 @@ static void DvbOsdCmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, in dc.y0 = y0; dc.x1 = x1; dc.y1 = y1; - dc.data = data; + dc.data = (void *)data; ioctl(v, VIDIOCSOSDCOMMAND, &dc); close(v); } else Interface.Error("can't open VIDEODEVICE");//XXX } +#endif -void DvbOsdOpen(int x, int y, int w, int h) +void cDvbOsd::Open(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 + cols = w; + rows = h; +#ifdef DEBUG_OSD + //XXX size... + #define B2C(b) (((b) * 1000) / 255) + #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b)) +#else + w *= charWidth; + h *= lineHeight; + int x = (720 - w) / 2; //TODO PAL vs. NTSC??? + int y = (576 - h) / 2; + Cmd(OSD_Open, 4, x, y, x + w - 1, y + h - 1); + #define SETCOLOR(n, r, g, b, o) Cmd(OSD_SetColor, n, r, g, b, o) +#endif + SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray + SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255); + SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255); + SETCOLOR(clrGreen, 0x24, 0xFC, 0x24, 255); + SETCOLOR(clrYellow, 0xFC, 0xC0, 0x24, 255); + SETCOLOR(clrBlue, 0x00, 0x00, 0xFC, 255); + SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255); + SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255); + SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255); } -void DvbOsdClose(void) +void cDvbOsd::Close(void) { - DvbOsdCmd(OSD_Close); +#ifndef DEBUG_OSD + Cmd(OSD_Close); +#endif } -void DvbOsdClear(void) +void cDvbOsd::Clear(void) { - DvbOsdCmd(OSD_Clear); +#ifdef DEBUG_OSD + SetColor(clrBackground, clrBackground); + Fill(0, 0, cols, rows, clrBackground); +#else + Cmd(OSD_Clear); +#endif } -void DvbOsdClrEol(int x, int y) +void cDvbOsd::Fill(int x, int y, int w, int h, eDvbColor color) { - DvbOsdCmd(OSD_FillBlock, 0, x, y * DvbOsdLineHeight, x + 490, (y + 1) * DvbOsdLineHeight);//XXX + if (x < 0) x = cols + x; + if (y < 0) y = rows + y; +#ifdef DEBUG_OSD + SetColor(color, color); + for (int r = 0; r < h; r++) { + wmove(window, y + r, x); // ncurses wants 'y' before 'x'! + whline(window, ' ', w); + } +#else + Cmd(OSD_FillBlock, color, x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1); +#endif } -void DvbOsdText(int x, int y, char *s) +void cDvbOsd::ClrEol(int x, int y, eDvbColor color) { - DvbOsdCmd(OSD_Text, 1, x, y, 1, 0, s); + Fill(x, y, cols - x, 1, color); +} + +void cDvbOsd::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg) +{ + if (x < 0) x = cols + x; + if (y < 0) y = rows + y; +#ifdef DEBUG_OSD + SetColor(colorFg, colorBg); + wmove(window, y, x); // ncurses wants 'y' before 'x'! + waddstr(window, s); +#else + Cmd(OSD_Text, (int(colorBg) << 16) | colorFg, x * charWidth, y * lineHeight, 1, 0, s); +#endif } diff --git a/dvbapi.h b/dvbapi.h index 6a8f0400..2e8406ee 100644 --- a/dvbapi.h +++ b/dvbapi.h @@ -4,23 +4,51 @@ * 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 $ + * $Id: dvbapi.h 1.2 2000/03/06 19:47:20 kls Exp $ */ #ifndef __DVBAPI_H #define __DVBAPI_H -const int DvbOsdCharWidth = 12; //XXX -const int DvbOsdLineHeight = 25; +// FIXME: these should be defined in ../DVB/driver/dvb.h!!! +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +#if defined(DEBUG_OSD) || defined(DEBUG_REMOTE) +#include +#endif +#include "../DVB/driver/dvb.h" + +enum eDvbColor { clrBackground, +#ifndef DEBUG_OSD + clrOBSOLETE, //FIXME apparently color '1' can't be used as FgColor with e.g. clrRed as BgColor??? + clrBlack, +#else + clrBlack = clrBackground, +#endif + clrRed, + clrGreen, + clrYellow, + clrBlue, + clrMagenta, + clrCyan, + clrWhite, + }; + extern const char *DvbQuality; // Low, Medium, High bool DvbSetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid); class cDvbRecorder { +private: + bool recording; public: cDvbRecorder(void); ~cDvbRecorder(); + bool Recording(void); + // Returns true if this recorder is currently recording, false if it + // is playing back or does nothing. 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. @@ -30,8 +58,9 @@ public: 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. + // frame in the file (or if it is negative), playback will be positioned + // to the last frame in the file (or the frame with the absolute value of + // Frame) 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. @@ -56,10 +85,29 @@ public: // 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); - +class cDvbOsd { +private: + enum { charWidth = 12, // average character width + lineHeight = 27 // smallest text height + }; +#ifdef DEBUG_OSD + WINDOW *window; + enum { MaxColorPairs = 16 }; + int colorPairs[MaxColorPairs]; + void SetColor(eDvbColor colorFg, eDvbColor colorBg = clrBackground); +#else + void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); +#endif + int cols, rows; +public: + cDvbOsd(void); + ~cDvbOsd(); + void Open(int w, int h); + void Close(void); + void Clear(void); + void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground); + void ClrEol(int x, int y, eDvbColor color = clrBackground); + void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground); + }; + #endif //__DVBAPI_H diff --git a/interface.c b/interface.c index 58c28f1f..6a4b2a4e 100644 --- a/interface.c +++ b/interface.c @@ -4,20 +4,21 @@ * 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 $ + * $Id: interface.c 1.2 2000/03/06 19:45:03 kls Exp $ */ #include "interface.h" -#include #include -#include "dvbapi.h" #include "remote.h" +#define MenuLines 15 +#define MenuColumns 40 + #ifndef DEBUG_REMOTE cRcIo RcIo("/dev/ttyS1");//XXX #endif -WINDOW *window; +cDvbOsd DvbOsd; //XXX member of cInterface??? cInterface Interface; @@ -25,16 +26,6 @@ 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) @@ -46,24 +37,14 @@ void cInterface::Init(void) void cInterface::Open(void) { - if (!open++) { -#ifdef DEBUG_OSD -#else -//TODO - DvbOsdOpen(100, 100, 500, 400); -#endif - } + if (!open++) + DvbOsd.Open(MenuColumns, MenuLines); } void cInterface::Close(void) { - if (!--open) { -#ifdef DEBUG_OSD -#else -//TODO - DvbOsdClose(); -#endif - } + if (!--open) + DvbOsd.Close(); } unsigned int cInterface::GetCh(void) @@ -71,9 +52,9 @@ unsigned int cInterface::GetCh(void) #ifdef DEBUG_REMOTE return getch(); #else -#ifdef DEBUG_OSD - wrefresh(window);//XXX -#endif +//XXX #ifdef DEBUG_OSD +//XXX wrefresh(window);//XXX +//XXX #endif unsigned int Command; return RcIo.GetCommand(&Command) ? Command : 0; #endif @@ -84,16 +65,28 @@ eKeys cInterface::GetKey(void) return Keys.Get(GetCh()); } +eKeys cInterface::Wait(int Seconds) +{ + int t0 = time_ms(); + + while (time_ms() - t0 < Seconds * 1000) { + eKeys Key = GetKey(); + if (Key != kNone) + return Key; + } + return kNone; +} + void cInterface::Clear(void) { - if (open) { -#ifdef DEBUG_OSD - wclear(window); -#else -//TODO - DvbOsdClear(); -#endif - } + if (open) + DvbOsd.Clear(); +} + +void cInterface::ClearEol(int x, int y, eDvbColor Color) +{ + if (open) + DvbOsd.ClrEol(x, y, Color); } void cInterface::SetCols(int *c) @@ -105,34 +98,22 @@ void cInterface::SetCols(int *c) } } -void cInterface::Write(int x, int y, char *s) +void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor) { - 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 - } + if (open) + DvbOsd.Text(x, y, s, FgColor, BgColor); } -void cInterface::WriteText(int x, int y, char *s, bool Current) +void cInterface::WriteText(int x, int y, const 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++; + eDvbColor FgColor = Current ? clrBlack : clrWhite; + eDvbColor BgColor = Current ? clrCyan : clrBackground; + ClearEol(x, y, BgColor); int col = 0; for (;;) { - char *t = strchr(s, '\t'); - char *p = s; + const char *t = strchr(s, '\t'); + const char *p = s; char buf[1000]; if (t && col < MaxCols && cols[col] > 0) { unsigned int n = t - s; @@ -143,7 +124,7 @@ void cInterface::WriteText(int x, int y, char *s, bool Current) p = buf; s = t + 1; } - Write(x, y, p); + Write(x, y, p, FgColor, BgColor); if (p == s) break; x += cols[col++]; @@ -151,38 +132,74 @@ void cInterface::WriteText(int x, int y, char *s, bool Current) } } -void cInterface::Info(char *s) +void cInterface::Title(const char *s) +{ + int x = (MenuColumns - strlen(s)) / 2; + if (x < 0) + x = 0; + ClearEol(0, 0, clrCyan); + Write(x, 0, s, clrBlack, clrCyan); +} + +void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor) +{ + ClearEol(0, -3, s ? BgColor : clrBackground); + if (s) + Write(0, -3, s, FgColor, BgColor); +} + +void cInterface::Info(const 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 + isyslog(LOG_INFO, s); + Status(s, clrWhite, clrGreen); + Wait(); + Status(NULL); Close(); } -void cInterface::Error(char *s) +void cInterface::Error(const 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 + Status(s, clrWhite, clrRed); + Wait(); + Status(NULL); Close(); } +bool cInterface::Confirm(const char *s) +{ + Open(); + isyslog(LOG_INFO, "confirm: %s", s); + Status(s, clrBlack, clrGreen); + bool result = Wait(10) == kOk; + Status(NULL); + Close(); + isyslog(LOG_INFO, "%sconfirmed", result ? "" : "not "); + return result; +} + +void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor) +{ + if (open && Text) { + const int w = MenuColumns / 4; + int l = (w - strlen(Text)) / 2; + if (l < 0) + l = 0; + DvbOsd.Fill(Index * w, -1, w, 1, BgColor); + DvbOsd.Text(Index * w + l, -1, Text, FgColor, BgColor); + } +} + +void cInterface::Help(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + HelpButton(0, Red, clrBlack, clrRed); + HelpButton(1, Green, clrBlack, clrGreen); + HelpButton(2, Yellow, clrBlack, clrYellow); + HelpButton(3, Blue, clrWhite, clrBlue); +} + void cInterface::QueryKeys(void) { Keys.Clear(); @@ -205,8 +222,8 @@ void cInterface::QueryKeys(void) WriteText(1, 5, "RC code detected!"); WriteText(1, 6, "Do not press any key..."); RcIo.Flush(3); - WriteText(1, 5, ""); - WriteText(1, 6, ""); + ClearEol(0, 5); + ClearEol(0, 6); break; } #endif @@ -229,8 +246,8 @@ void cInterface::QueryKeys(void) 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, ""); + ClearEol(0, 7); + ClearEol(0, 8); for (;;) { eKeys key = GetKey(); if (key == kUp) { @@ -238,7 +255,7 @@ void cInterface::QueryKeys(void) return; } else if (key == kDown) { - WriteText(1, 6, ""); + ClearEol(0, 6); break; } } @@ -255,17 +272,18 @@ void cInterface::QueryKeys(void) if (k > Keys.keys) WriteText(1, 7, "(press 'Up' to go back)"); else - WriteText(1, 7, ""); + ClearEol(0, 7); if (k > Keys.keys + 1) WriteText(1, 8, "(press 'Down' to end key definition)"); else - WriteText(1, 8, ""); + ClearEol(0, 8); } } void cInterface::LearnKeys(void) { isyslog(LOG_INFO, "learning keys"); + Open(); for (;;) { Clear(); QueryKeys(); @@ -277,19 +295,19 @@ void cInterface::LearnKeys(void) eKeys key = GetKey(); if (key == kUp) { Keys.Save(); - Clear(); + Close(); return; } else if (key == kDown) { Keys.Load(); - Clear(); + Close(); return; } } } } -void cInterface::DisplayChannel(int Number, char *Name) +void cInterface::DisplayChannel(int Number, const char *Name) { //TODO #ifndef DEBUG_REMOTE diff --git a/interface.h b/interface.h index e387ab91..9322cf39 100644 --- a/interface.h +++ b/interface.h @@ -4,13 +4,14 @@ * 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 $ + * $Id: interface.h 1.2 2000/02/27 14:54:02 kls Exp $ */ #ifndef __INTERFACE_H #define __INTERFACE_H #include "config.h" +#include "dvbapi.h" class cInterface { public: @@ -20,7 +21,8 @@ private: int cols[MaxCols]; unsigned int GetCh(void); void QueryKeys(void); - void Write(int x, int y, char *s); + void HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor); + eKeys Wait(int Seconds = 1); public: cInterface(void); void Init(void); @@ -28,12 +30,18 @@ public: void Close(void); eKeys GetKey(void); void Clear(void); + void ClearEol(int x, int y, eDvbColor Color = clrBackground); void SetCols(int *c); - void WriteText(int x, int y, char *s, bool Current = false); - void Info(char *s); - void Error(char *s); + void Write(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); + void WriteText(int x, int y, const char *s, bool Current = false); + void Title(const char *s); + void Status(const char *s, eDvbColor FgColor = clrBlack, eDvbColor BgColor = clrCyan); + void Info(const char *s); + void Error(const char *s); + bool Confirm(const char *s); + void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); void LearnKeys(void); - void DisplayChannel(int Number, char *Name); + void DisplayChannel(int Number, const char *Name); }; extern cInterface Interface; diff --git a/keys-pc.conf b/keys-pc.conf index c57613f0..cfbe4587 100644 Binary files a/keys-pc.conf and b/keys-pc.conf differ diff --git a/keys.conf b/keys.conf index f56222c3..0e3c07bc 100644 --- a/keys.conf +++ b/keys.conf @@ -17,3 +17,7 @@ Right 000045E2 7 00000FE2 8 000077E2 9 000037E2 +Red 000025E2 +Green 00002AE2 +Yellow 00005AE2 +Blue 00000000 diff --git a/menu.c b/menu.c index 4f525ff7..45f25a46 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * 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 $ + * $Id: menu.c 1.2 2000/03/05 15:37:31 kls Exp $ */ #include "menu.h" @@ -13,8 +13,9 @@ #include #include "config.h" #include "dvbapi.h" +#include "recording.h" -const char *FileNameChars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789/-.# ";//TODO more? +const char *FileNameChars = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789-.# "; // --- cMenuEditItem --------------------------------------------------------- @@ -59,7 +60,7 @@ protected: virtual void Set(void); public: cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max) @@ -78,11 +79,11 @@ void cMenuEditIntItem::Set(void) SetValue(buf); } -eOSStatus cMenuEditIntItem::ProcessKey(eKeys Key) +eOSState cMenuEditIntItem::ProcessKey(eKeys Key) { - eOSStatus status = cMenuEditItem::ProcessKey(Key); + eOSState state = cMenuEditItem::ProcessKey(Key); - if (status == osUnknown) { + if (state == osUnknown) { int newValue; if (k0 <= Key && Key <= k9) { if (fresh) { @@ -100,14 +101,14 @@ eOSStatus cMenuEditIntItem::ProcessKey(eKeys Key) fresh = true; } else - return status; + return state; if ((!fresh || min <= newValue) && newValue <= max) { *value = newValue; Set(); } - status = osContinue; + state = osContinue; } - return status; + return state; } // --- cMenuEditBoolItem ----------------------------------------------------- @@ -167,7 +168,7 @@ protected: virtual void Set(void); public: cMenuEditDayItem(const char *Name, int *Value); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; int cMenuEditDayItem::days[] ={ cTimer::ParseDay("M------"), @@ -205,7 +206,7 @@ void cMenuEditDayItem::Set(void) SetValue(cTimer::PrintDay(*value)); } -eOSStatus cMenuEditDayItem::ProcessKey(eKeys Key) +eOSState cMenuEditDayItem::ProcessKey(eKeys Key) { switch (Key) { case kLeft: if (d > 0) @@ -252,7 +253,7 @@ protected: virtual void Set(void); public: cMenuEditTimeItem(const char *Name, int *Value); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value) @@ -272,11 +273,11 @@ void cMenuEditTimeItem::Set(void) SetValue(buf); } -eOSStatus cMenuEditTimeItem::ProcessKey(eKeys Key) +eOSState cMenuEditTimeItem::ProcessKey(eKeys Key) { - eOSStatus status = cMenuEditItem::ProcessKey(Key); + eOSState state = cMenuEditItem::ProcessKey(Key); - if (status == osUnknown) { + if (state == osUnknown) { if (k0 <= Key && Key <= k9) { if (fresh || pos > 3) { pos = 0; @@ -324,12 +325,12 @@ eOSStatus cMenuEditTimeItem::ProcessKey(eKeys Key) fresh = true; } else - return status; + return state; *value = hh * 100 + mm; Set(); - status = osContinue; + state = osContinue; } - return status; + return state; } // --- cMenuEditChrItem ------------------------------------------------------ @@ -343,7 +344,7 @@ private: public: cMenuEditChrItem(const char *Name, char *Value, const char *Allowed); ~cMenuEditChrItem(); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed) @@ -369,11 +370,11 @@ void cMenuEditChrItem::Set(void) SetValue(buf); } -eOSStatus cMenuEditChrItem::ProcessKey(eKeys Key) +eOSState cMenuEditChrItem::ProcessKey(eKeys Key) { - eOSStatus status = cMenuEditItem::ProcessKey(Key); + eOSState state = cMenuEditItem::ProcessKey(Key); - if (status == osUnknown) { + if (state == osUnknown) { if (Key == kLeft) { if (current > allowed) current--; @@ -383,12 +384,12 @@ eOSStatus cMenuEditChrItem::ProcessKey(eKeys Key) current++; } else - return status; + return state; *value = *current; Set(); - status = osContinue; + state = osContinue; } - return status; + return state; } // --- cMenuEditStrItem ------------------------------------------------------ @@ -404,7 +405,7 @@ private: public: cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed); ~cMenuEditStrItem(); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed) @@ -449,7 +450,7 @@ char cMenuEditStrItem::Inc(char c, bool Up) return *p; } -eOSStatus cMenuEditStrItem::ProcessKey(eKeys Key) +eOSState cMenuEditStrItem::ProcessKey(eKeys Key) { switch (Key) { case kLeft: if (pos > 0) { @@ -492,11 +493,11 @@ private: cChannel data; public: cMenuEditChannel(int Index); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditChannel::cMenuEditChannel(int Index) -:cOsdMenu("Edit channel", 14) +:cOsdMenu("Edit Channel", 14) { channel = Channels.Get(Index); if (channel) { @@ -511,19 +512,19 @@ cMenuEditChannel::cMenuEditChannel(int Index) } } -eOSStatus cMenuEditChannel::ProcessKey(eKeys Key) +eOSState cMenuEditChannel::ProcessKey(eKeys Key) { - eOSStatus status = cOsdMenu::ProcessKey(Key); + eOSState state = cOsdMenu::ProcessKey(Key); - if (status == osUnknown) { + if (state == osUnknown) { if (Key == kOk) { if (channel) *channel = data; Channels.Save(); - status = osBack; + state = osBack; } } - return status; + return state; } // --- cMenuChannelItem ------------------------------------------------------ @@ -535,6 +536,7 @@ private: public: cMenuChannelItem(int Index, cChannel *Channel); virtual void Set(void); + void SetIndex(int Index); }; cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel) @@ -551,12 +553,24 @@ void cMenuChannelItem::Set(void) SetText(buffer, false); } +void cMenuChannelItem::SetIndex(int Index) +{ + index = Index; + Set(); +} + // --- cMenuChannels --------------------------------------------------------- class cMenuChannels : public cOsdMenu { +protected: + eOSState Switch(void); + eOSState Edit(void); + eOSState New(void); + eOSState Del(void); + virtual void Move(int From, int To); public: cMenuChannels(void); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuChannels::cMenuChannels(void) @@ -570,26 +584,124 @@ cMenuChannels::cMenuChannels(void) Add(new cMenuChannelItem(i, channel), i == CurrentChannel); i++; } + SetHelp("Edit", "New", "Delete", "Mark"); } -eOSStatus cMenuChannels::ProcessKey(eKeys Key) +eOSState cMenuChannels::Switch(void) { - eOSStatus status = cOsdMenu::ProcessKey(Key); + cChannel *ch = Channels.Get(Current()); + if (ch) + ch->Switch(); + return osEnd; +} - if (status == osUnknown) { +eOSState cMenuChannels::Edit(void) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + isyslog(LOG_INFO, "editing timer %d", Current() + 1); + return AddSubMenu(new cMenuEditChannel(Current())); +} + +eOSState cMenuChannels::New(void) +{ + if (HasSubMenu()) + return osContinue; + cChannel *channel = new cChannel(Channels.Get(Current())); + Channels.Add(channel); + Add(new cMenuChannelItem(channel->Index()/*XXX*/, channel), true); + Channels.Save(); + isyslog(LOG_INFO, "channel %d added", channel->Index() + 1); + return AddSubMenu(new cMenuEditChannel(Current())); +} + +eOSState cMenuChannels::Del(void) +{ + if (Count() > 0) { + int Index = Current(); + // Check if there is a timer using this channel: + for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) { + if (ti->channel == Index + 1) { + Interface.Error("Channel is being used by a timer!"); + return osContinue; + } + } + if (Interface.Confirm("Delete Channel?")) { + // Move and renumber the channels: + Channels.Del(Channels.Get(Index)); + cOsdMenu::Del(Index); + int i = 0; + for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) + ci->SetIndex(i++); + Channels.Save(); + isyslog(LOG_INFO, "channel %d deleted", Index + 1); + // Fix the timers: + bool TimersModified = false; + Index++; // user visible channel numbers start with '1' + for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) { + int OldChannel = ti->channel; + if (ti->channel > Index) + ti->channel--; + if (ti->channel != OldChannel) { + TimersModified = true; + isyslog(LOG_INFO, "timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel); + } + } + if (TimersModified) + Timers.Save(); + Display(); + } + } + return osContinue; +} + +void cMenuChannels::Move(int From, int To) +{ + // Move and renumber the channels: + Channels.Move(From, To); + cOsdMenu::Move(From, To); + int i = 0; + for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) + ci->SetIndex(i++); + Channels.Save(); + isyslog(LOG_INFO, "channel %d moved to %d", From + 1, To + 1); + // Fix the timers: + bool TimersModified = false; + From++; // user visible channel numbers start with '1' + To++; + for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) { + int OldChannel = ti->channel; + if (ti->channel == From) + ti->channel = To; + else if (ti->channel > From && ti->channel <= To) + ti->channel--; + else if (ti->channel < From && ti->channel >= To) + ti->channel++; + if (ti->channel != OldChannel) { + TimersModified = true; + isyslog(LOG_INFO, "timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel); + } + } + if (TimersModified) + Timers.Save(); + Display(); +} + +eOSState cMenuChannels::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == 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; - } + case kOk: return Switch(); + case kRed: return Edit(); + case kGreen: return New(); + case kYellow: return Del(); + case kBlue: Mark(); break; default: break; } } - return status; + return state; } // --- cMenuEditTimer -------------------------------------------------------- @@ -600,11 +712,11 @@ private: cTimer data; public: cMenuEditTimer(int Index); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; cMenuEditTimer::cMenuEditTimer(int Index) -:cOsdMenu("Edit timer", 10) +:cOsdMenu("Edit Timer", 10) { timer = Timers.Get(Index); if (timer) { @@ -622,21 +734,23 @@ cMenuEditTimer::cMenuEditTimer(int Index) } } -eOSStatus cMenuEditTimer::ProcessKey(eKeys Key) +eOSState cMenuEditTimer::ProcessKey(eKeys Key) { - eOSStatus status = cOsdMenu::ProcessKey(Key); + eOSState state = cOsdMenu::ProcessKey(Key); - if (status == osUnknown) { + if (state == osUnknown) { if (Key == kOk) { + if (!*data.file) + strcpy(data.file, "unnamed"); 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; + state = osBack; } } - return status; + return state; } // --- cMenuTimerItem -------------------------------------------------------- @@ -660,27 +774,34 @@ cMenuTimerItem::cMenuTimerItem(int Index, cTimer *Timer) void cMenuTimerItem::Set(void) { char *buffer = NULL; - asprintf(&buffer, "%d\t%c\t%d\t%s\t%02d:%02d\t%02d:%02d", index + 1, + asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s", 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' + timer->stop % 100, + timer->file); SetText(buffer, false); } -// --- cMenuTimer ------------------------------------------------------------ +// --- cMenuTimers ----------------------------------------------------------- -class cMenuTimer : public cOsdMenu { +class cMenuTimers : public cOsdMenu { +private: + eOSState Activate(bool On); + eOSState Edit(void); + eOSState New(void); + eOSState Del(void); + virtual void Move(int From, int To); public: - cMenuTimer(void); - virtual eOSStatus ProcessKey(eKeys Key); + cMenuTimers(void); + virtual eOSState ProcessKey(eKeys Key); }; -cMenuTimer::cMenuTimer(void) -:cOsdMenu("Timer", 3, 2, 4, 10, 6) +cMenuTimers::cMenuTimers(void) +:cOsdMenu("Timer", 2, 4, 10, 6, 6) { int i = 0; cTimer *timer; @@ -689,34 +810,190 @@ cMenuTimer::cMenuTimer(void) Add(new cMenuTimerItem(i, timer)); i++; } + SetHelp("Edit", "New", "Delete", "Mark"); } -eOSStatus cMenuTimer::ProcessKey(eKeys Key) +eOSState cMenuTimers::Activate(bool On) { - eOSStatus status = cOsdMenu::ProcessKey(Key); + cTimer *timer = Timers.Get(Current()); + if (timer && timer->active != On) { + timer->active = On; + RefreshCurrent(); + DisplayCurrent(true); + isyslog(LOG_INFO, "timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de"); + Timers.Save(); + } + return osContinue; +} - if (status == osUnknown) { +eOSState cMenuTimers::Edit(void) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + isyslog(LOG_INFO, "editing timer %d", Current() + 1); + return AddSubMenu(new cMenuEditTimer(Current())); +} + +eOSState cMenuTimers::New(void) +{ + if (HasSubMenu()) + return osContinue; + cTimer *timer = new cTimer; + Timers.Add(timer); + Add(new cMenuTimerItem(timer->Index()/*XXX*/, timer), true); + Timers.Save(); + isyslog(LOG_INFO, "timer %d added", timer->Index() + 1); + return AddSubMenu(new cMenuEditTimer(Current())); +} + +eOSState cMenuTimers::Del(void) +{ + // Check if this timer is active: + int Index = Current(); + cTimer *ti = Timers.Get(Index); + if (ti) { + if (!ti->recording) { + if (Interface.Confirm("Delete Timer?")) { + Timers.Del(Timers.Get(Index)); + cOsdMenu::Del(Index); + Timers.Save(); + Display(); + isyslog(LOG_INFO, "timer %d deleted", Index + 1); + } + } + else + Interface.Error("Timer is recording!"); + } + return osContinue; +} + +void cMenuTimers::Move(int From, int To) +{ + Timers.Move(From, To); + cOsdMenu::Move(From, To); + Timers.Save(); + Display(); + isyslog(LOG_INFO, "timer %d moved to %d", From + 1, To + 1); +} + +eOSState cMenuTimers::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == 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(); - } - } + case kRight: return Activate(Key == kRight); + case kOk: + case kRed: return Edit(); + case kGreen: return New(); + case kYellow: return Del(); + case kBlue: Mark(); break; default: break; } } - return status; + return state; +} + +// --- cMenuRecordingItem ---------------------------------------------------- + +class cMenuRecordingItem : public cOsdItem { +public: + cRecording *recording; + cMenuRecordingItem(cRecording *Recording); + virtual void Set(void); + }; + +cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording) +{ + recording = Recording; + Set(); +} + +void cMenuRecordingItem::Set(void) +{ + char *buffer = NULL; + struct tm *t = localtime(&recording->start); + asprintf(&buffer, "%02d.%02d.%04d\t%02d:%02d\t%s", + t->tm_mday, + t->tm_mon + 1, + t->tm_year + 1900, + t->tm_hour, + t->tm_min, + recording->name); + SetText(buffer, false); +} + +// --- cMenuRecordings ------------------------------------------------------- + +class cMenuRecordings : public cOsdMenu { +private: + cRecordings Recordings; + eOSState Play(void); + eOSState Del(void); +public: + cMenuRecordings(void); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuRecordings::cMenuRecordings(void) +:cOsdMenu("Recordings", 11, 6) +{ + if (Recordings.Load()) { + cRecording *recording = Recordings.First(); + while (recording) { + Add(new cMenuRecordingItem(recording)); + recording = Recordings.Next(recording); + } + } + SetHelp("Play", NULL/*XXX"Resume"*/, "Delete"); +} + +eOSState cMenuRecordings::Play(void) +{ + cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); + if (ri) { +//XXX what if this recording's file is currently in use??? + if (ri->recording->Play()) + return osEnd; + } + return osContinue; +} + +eOSState cMenuRecordings::Del(void) +{ + cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); + if (ri) { +//XXX what if this recording's file is currently in use??? +//XXX if (!ti->recording) { + if (Interface.Confirm("Delete Recording?")) { + if (ri->recording->Delete()) { + cOsdMenu::Del(Current()); + Display(); + } + else + Interface.Error("Error while deleting recording!"); + } +//XXX } +//XXX else +//XXX Interface.Error("Timer is recording!"); + } + return osContinue; +} + +eOSState cMenuRecordings::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case kOk: + case kRed: return Play(); + case kYellow: return Del(); + default: break; + } + } + return state; } // --- cMenuMain ------------------------------------------------------------- @@ -724,22 +1001,21 @@ eOSStatus cMenuTimer::ProcessKey(eKeys Key) 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) +eOSState cMenuMain::ProcessKey(eKeys Key) { - eOSStatus status = cOsdMenu::ProcessKey(Key); + eOSState state = cOsdMenu::ProcessKey(Key); - switch (status) { - case osChannels: return AddSubMenu(new cMenuChannels); - case osTimer: return AddSubMenu(new cMenuTimer); - //TODO Replay + switch (state) { + case osChannels: return AddSubMenu(new cMenuChannels); + case osTimer: return AddSubMenu(new cMenuTimers); + case osRecordings: return AddSubMenu(new cMenuRecordings); default: break; } - return status; + return state; } diff --git a/menu.h b/menu.h index 849d8ee3..813beecd 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * 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 $ + * $Id: menu.h 1.2 2000/03/05 10:57:27 kls Exp $ */ #ifndef _MENU_H @@ -15,7 +15,7 @@ class cMenuMain : public cOsdMenu { public: cMenuMain(void); - virtual eOSStatus ProcessKey(eKeys Key); + virtual eOSState ProcessKey(eKeys Key); }; #endif //_MENU_H diff --git a/osd.c b/osd.c index d7ab6993..673d075c 100644 --- a/osd.c +++ b/osd.c @@ -4,7 +4,7 @@ * 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 $ + * $Id: osd.c 1.2 2000/02/27 17:23:07 kls Exp $ */ #include "osd.h" @@ -13,19 +13,19 @@ // --- cOsdItem -------------------------------------------------------------- -cOsdItem::cOsdItem(eOSStatus Status) +cOsdItem::cOsdItem(eOSState State) { text = NULL; offset = -1; - status = Status; + state = State; fresh = false; } -cOsdItem::cOsdItem(char *Text, eOSStatus Status) +cOsdItem::cOsdItem(char *Text, eOSState State) { text = NULL; offset = -1; - status = Status; + state = State; fresh = false; SetText(Text); } @@ -52,24 +52,27 @@ void cOsdItem::Display(int Offset, bool Current) Interface.WriteText(0, offset + 2, text, Current); } -eOSStatus cOsdItem::ProcessKey(eKeys Key) +eOSState cOsdItem::ProcessKey(eKeys Key) { - return Key == kOk ? status : osUnknown; + return Key == kOk ? state : osUnknown; } // --- cOsdMenu -------------------------------------------------------------- cOsdMenu::cOsdMenu(char *Title, int c0, int c1, int c2, int c3, int c4) { + visible = false; title = strdup(Title); cols[0] = c0; cols[1] = c1; cols[2] = c2; cols[3] = c3; cols[4] = c4; - first = count = 0; - current = -1; + first = 0; + current = marked = -1; subMenu = NULL; + helpRed = helpGreen = helpYellow = helpBlue = NULL; + status = NULL; Interface.Open(); } @@ -77,40 +80,72 @@ cOsdMenu::~cOsdMenu() { delete title; delete subMenu; + delete status; Interface.Clear(); Interface.Close(); } +void cOsdMenu::SetStatus(const char *s) +{ + delete status; + status = s ? strdup(s) : NULL; + if (visible) + Interface.Status(status); +} + +void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + // strings are NOT copied - must be constants!!! + helpRed = Red; + helpGreen = Green; + helpYellow = Yellow; + helpBlue = Blue; +} + +void cOsdMenu::Del(int Index) +{ + cList::Del(Get(Index)); + if (current == Count()) + current--; + if (Index == first && first > 0) + first--; +} + void cOsdMenu::Add(cOsdItem *Item, bool Current) { cList::Add(Item); - count++; - if (Current && current < 0) + if (Current) current = Item->Index(); } void cOsdMenu::Display(void) { + visible = true; 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; + Interface.Title(title); + Interface.Help(helpRed, helpGreen, helpYellow, helpBlue); + int count = Count(); + if (count > 0) { + if (current < 0) + 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; + } } - 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; - } + Interface.Status(status); } void cOsdMenu::RefreshCurrent(void) @@ -144,6 +179,7 @@ void cOsdMenu::CursorUp(void) void cOsdMenu::CursorDown(void) { + int count = Count(); if (current < count - 1) { DisplayCurrent(false); if (++current >= first + MAXOSDITEMS) { @@ -157,7 +193,15 @@ void cOsdMenu::CursorDown(void) } } -eOSStatus cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) +void cOsdMenu::Mark(void) +{ + if (Count() && marked < 0) { + marked = current; + SetStatus("Up/Dn for new location - OK to move"); + } +} + +eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) { delete subMenu; subMenu = SubMenu; @@ -165,31 +209,40 @@ eOSStatus cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) return osContinue; // convenience return value (see cMenuMain) } -eOSStatus cOsdMenu::ProcessKey(eKeys Key) +eOSState cOsdMenu::ProcessKey(eKeys Key) { if (subMenu) { - eOSStatus status = subMenu->ProcessKey(Key); - if (status == osBack) { + eOSState state = subMenu->ProcessKey(Key); + if (state == osBack) { delete subMenu; subMenu = NULL; RefreshCurrent(); Display(); - status = osContinue; + state = osContinue; } - return status; + return state; } cOsdItem *item = Get(current); - if (item) { - eOSStatus status = item->ProcessKey(Key); - if (status != osUnknown) - return status; + if (marked < 0 && item) { + eOSState state = item->ProcessKey(Key); + if (state != osUnknown) + return state; } switch (Key) { case kUp: CursorUp(); break; case kDown: CursorDown(); break; case kBack: return osBack; - default: return osUnknown; + case kOk: if (marked >= 0) { + SetStatus(NULL); + if (marked != current) + Move(marked, current); + marked = -1; + break; + } + // else run into default + default: if (marked < 0) + return osUnknown; } return osContinue; } diff --git a/osd.h b/osd.h index 392f1a75..5ec13349 100644 --- a/osd.h +++ b/osd.h @@ -4,7 +4,7 @@ * 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 $ + * $Id: osd.h 1.2 2000/03/05 11:33:11 kls Exp $ */ #ifndef __OSD_H @@ -16,53 +16,61 @@ #define MAXOSDITEMS 9 -enum eOSStatus { osUnknown, - osContinue, - osProcessed, - osChannels, - osTimer, - osRecordings, - osBack, - osEnd, - }; +enum eOSState { osUnknown, + osContinue, + osProcessed, + osChannels, + osTimer, + osRecordings, + osBack, + osEnd, + }; class cOsdItem : public cListObject { private: char *text; int offset; - eOSStatus status; + eOSState state; protected: bool fresh; public: - cOsdItem(eOSStatus Status = osUnknown); - cOsdItem(char *Text, eOSStatus Status = osUnknown); + cOsdItem(eOSState State = osUnknown); + cOsdItem(char *Text, eOSState State = 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); + virtual eOSState ProcessKey(eKeys Key); }; class cOsdMenu : public cList { private: char *title; int cols[cInterface::MaxCols]; - int first, current, count; + int first, current, marked; cOsdMenu *subMenu; + const char *helpRed, *helpGreen, *helpYellow, *helpBlue; + const char *status; protected: + bool visible; void RefreshCurrent(void); void DisplayCurrent(bool Current); void CursorUp(void); void CursorDown(void); - eOSStatus AddSubMenu(cOsdMenu *SubMenu); + void Mark(void); + eOSState AddSubMenu(cOsdMenu *SubMenu); + bool HasSubMenu(void) { return subMenu; } + void SetStatus(const char *s); + void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); + virtual void Del(int Index); 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); + virtual eOSState ProcessKey(eKeys Key); }; #endif //__OSD_H diff --git a/osm.c b/osm.c index cc017451..7031d689 100644 --- a/osm.c +++ b/osm.c @@ -22,13 +22,13 @@ * * 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 $ + * $Id: osm.c 1.2 2000/03/05 17:18:15 kls Exp $ */ #include "config.h" -#include "dvbapi.h" #include "interface.h" #include "menu.h" +#include "recording.h" #include "tools.h" #ifdef DEBUG_REMOTE @@ -52,39 +52,39 @@ int main(int argc, char *argv[]) cMenuMain *Menu = NULL; cTimer *Timer = NULL; - cDvbRecorder *Recorder = NULL; + cRecording *Recording = 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) { + AssertFreeDiskSpace(); + if (!Recording && !Timer && (Timer = cTimer::GetMatch()) != NULL) { + DELETENULL(Menu); + // make sure the timer won't be deleted: + Timer->SetRecording(true); // 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; - } + Recording = new cRecording(Timer); + if (!Recording->Record()) + DELETENULL(Recording); } - 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??? + if (Timer && !Timer->Matches()) { + // stop recording: + if (Recording) { + Recording->Stop(); + DELETENULL(Recording); } + // release channel and timer: + ChannelLocked = false; + Timer->SetRecording(false); + // clear single event timer: + if (Timer->IsSingleEvent()) { + DELETENULL(Menu); // must make sure no menu uses it + isyslog(LOG_INFO, "deleting timer %d", Timer->Index() + 1); + Timers.Del(Timer); + Timers.Save(); + } + Timer = NULL; } eKeys key = Interface.GetKey(); if (Menu) { @@ -92,8 +92,7 @@ int main(int argc, char *argv[]) default: if (key != kMenu) break; case osBack: - case osEnd: delete Menu; - Menu = NULL; + case osEnd: DELETENULL(Menu); break; } } diff --git a/recording.c b/recording.c new file mode 100644 index 00000000..afc2d8b3 --- /dev/null +++ b/recording.c @@ -0,0 +1,238 @@ +/* + * recording.h: Recording file handling + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: recording.c 1.1 2000/03/05 17:16:22 kls Exp $ + */ + +#include "recording.h" +#include +#include +#include +#include "interface.h" +#include "tools.h" + +#define RECEXT ".rec" +#define DELEXT ".del" +#define DATAFORMAT "%4d-%02d-%02d.%02d:%02d.%c.%02d.%02d" RECEXT +#define NAMEFORMAT "%s/%s/" DATAFORMAT + +#define FINDCMD "find %s -type f -name '%s'" + +#define DFCMD "df -m %s" +#define MINDISKSPACE 1024 // MB + +const char *BaseDir = "/video";//XXX + +cDvbRecorder *Recorder = NULL; + +static bool LowDiskSpace(void) +{ + //TODO Find a simpler way to determine the amount of free disk space! + bool result = true; + char *cmd = NULL; + asprintf(&cmd, DFCMD, BaseDir); + FILE *p = popen(cmd, "r"); + if (p) { + char *s; + while ((s = readline(p)) != NULL) { + if (*s == '/') { + int available; + sscanf(s, "%*s %*d %*d %d", &available); + result = available < MINDISKSPACE; + break; + } + } + pclose(p); + } + else + esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd); + delete cmd; + return result; +} + +void AssertFreeDiskSpace(void) +{ + // With every call to this function we try to actually remove + // a file, or mark a file for removal ("delete" it), so that + // it will get removed during the next call. + if (Recorder && Recorder->Recording() && LowDiskSpace()) { + // Remove the oldest file that has been "deleted": + cRecordings Recordings; + if (Recordings.Load(true)) { + cRecording *r = Recordings.First(); + cRecording *r0 = r; + while (r) { + if (r->start < r0->start) + r0 = r; + r = Recordings.Next(r); + } + if (r0 && r0->Remove()) + return; + } + // No "deleted" files to remove, so let's see if we can delete a recording: + if (Recordings.Load(false)) { + cRecording *r = Recordings.First(); + cRecording *r0 = NULL; + while (r) { + if ((time(NULL) - r->start) / SECSINDAY > r->lifetime) { + if (r0) { + if (r->priority < r0->priority) + r0 = r; + } + else + r0 = r; + } + r = Recordings.Next(r); + } + if (r0 && r0->Delete()) + return; + } + // Unable to free disk space, but there's nothing we can do about that... + //TODO maybe a log entry - but make sure it doesn't come too often + } +} + +// --- cRecording ------------------------------------------------------------ + +cRecording::cRecording(const char *Name, time_t Start, char Quality, int Priority, int LifeTime) +{ + fileName = NULL; + name = strdup(Name); + start = Start; + quality = Quality; + priority = Priority; + lifetime = LifeTime; +} + +cRecording::cRecording(cTimer *Timer) +{ + fileName = NULL; + name = strdup(Timer->file); + start = Timer->StartTime(); + quality = Timer->quality; + priority = Timer->priority; + lifetime = Timer->lifetime; +} + +cRecording::cRecording(const char *FileName) +{ + fileName = strdup(FileName); + FileName += strlen(BaseDir) + 1; + char *p = strrchr(FileName, '/'); + + name = NULL; + if (p) { + time_t now = time(NULL); + struct tm t = *localtime(&now); // this initializes the time zone in 't' + if (8 == sscanf(p + 1, DATAFORMAT, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &quality, &priority, &lifetime)) { + t.tm_year -= 1900; + t.tm_mon--; + t.tm_sec = 0; + start = mktime(&t); + name = new char[p - FileName + 1]; + strncpy(name, FileName, p - FileName); + name[p - FileName] = 0; + } + } +} + +cRecording::~cRecording() +{ + delete fileName; + delete name; +} + +const char *cRecording::FileName(void) +{ + if (!fileName) { + struct tm *t = localtime(&start); + asprintf(&fileName, NAMEFORMAT, BaseDir, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, quality, priority, lifetime); + } + return fileName; +} + +bool cRecording::Delete(void) +{ + bool result = true; + char *NewName = strdup(FileName()); + char *ext = strrchr(NewName, '.'); + if (strcmp(ext, RECEXT) == 0) { + strncpy(ext, DELEXT, strlen(ext)); + isyslog(LOG_INFO, "deleting recording %s", FileName()); + if (rename(FileName(), NewName) == -1) { + esyslog(LOG_ERR, "ERROR: %s", strerror(errno)); + result = false; + } + } + delete NewName; + return result; +} + +bool cRecording::Remove(void) +{ + bool result = true; + isyslog(LOG_INFO, "removing recording %s", FileName()); + if (remove(FileName()) == -1) { + esyslog(LOG_ERR, "ERROR: %s", strerror(errno)); + result = false; + } + return result; +} + +bool cRecording::AssertRecorder(void) +{ + if (!Recorder || !Recorder->Recording()) { + if (!Recorder) + Recorder = new cDvbRecorder; + return true; + } + Interface.Error("Recorder is in use!"); + return false; +} + +bool cRecording::Record(void) +{ + return AssertRecorder() && Recorder->Record(FileName(), quality); +} + +bool cRecording::Play(void) +{ + return AssertRecorder() && Recorder->Play(FileName()); +} + +void cRecording::Stop(void) +{ + if (Recorder) + Recorder->Stop(); +} + +// --- cRecordings ----------------------------------------------------------- + +bool cRecordings::Load(bool Deleted) +{ + Clear(); + bool result = false; + char *cmd = NULL; + asprintf(&cmd, FINDCMD, BaseDir, Deleted ? "*" DELEXT : "*" RECEXT); + FILE *p = popen(cmd, "r"); + if (p) { + char *s; + while ((s = readline(p)) != NULL) { + cRecording *r = new cRecording(s); + if (r->name) + Add(r); + else + delete r; + } + pclose(p); + result = Count() > 0; + } + else + Interface.Error("Error while opening pipe!"); + delete cmd; + return result; +} + diff --git a/recording.h b/recording.h new file mode 100644 index 00000000..5b093a04 --- /dev/null +++ b/recording.h @@ -0,0 +1,56 @@ +/* + * recording.h: Recording file handling + * + * See the main source file 'osm.c' for copyright information and + * how to reach the author. + * + * $Id: recording.h 1.1 2000/03/05 15:57:27 kls Exp $ + */ + +#ifndef __RECORDING_H +#define __RECORDING_H + +#include +#include "config.h" +#include "dvbapi.h" +#include "tools.h" + +extern cDvbRecorder *Recorder; + +void AssertFreeDiskSpace(void); + +class cRecording : public cListObject { +private: + bool AssertRecorder(void); +public: + char *name; + char *fileName; + time_t start; + char quality; + int priority; + int lifetime; + cRecording(const char *Name, time_t Start, char Quality, int Priority, int LifeTime); + cRecording(cTimer *Timer); + cRecording(const char *FileName); + ~cRecording(); + const char *FileName(void); + bool Delete(void); + // Changes the file name so that it will no longer be visible in the OSM + // Returns false in case of error + bool Remove(void); + // Actually removes the file from the disk + // Returns false in case of error + bool Record(void); + // Starts recording of the file + bool Play(void); + // Starts playback of the file + void Stop(void); + // Stops recording or playback of the file + }; + +class cRecordings : public cList { +public: + bool Load(bool Deleted = false); + }; + +#endif //__RECORDING_H diff --git a/timers.conf b/timers.conf index ac002a99..de4df9da 100644 --- a/timers.conf +++ b/timers.conf @@ -1,9 +1,6 @@ -1:2:-----S-:2210:2320:H:99:99:Wochenshow -1:3:M------:2125:2205:H:99:99:Neues +1:2:-----S-:2205:2320:H:99:99:Wochenshow +0:15: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:# +1:3:---T---:2215:2300:H:99:99:Switch +1:14:------S:2210:2255:H:99:99:Olli diff --git a/tools.c b/tools.c index e0eeaefd..5a21a930 100644 --- a/tools.c +++ b/tools.c @@ -4,13 +4,30 @@ * 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 $ + * $Id: tools.c 1.2 2000/03/05 14:33:58 kls Exp $ */ #include "tools.h" +#include #include +#include +#include #include +#define MaxBuffer 1000 + +char *readline(FILE *f) +{ + static char buffer[MaxBuffer]; + if (fgets(buffer, sizeof(buffer), f) > 0) { + int l = strlen(buffer) - 1; + if (l >= 0 && buffer[l] == '\n') + buffer[l] = 0; + return buffer; + } + return NULL; +} + int time_ms(void) { struct timeval t; @@ -19,6 +36,30 @@ int time_ms(void) return 0; } +bool MakeDirs(const char *FileName) +{ + bool result = true; + char *s = strdup(FileName); + char *p = s; + if (*p == '/') + p++; + while ((p = strchr(p, '/')) != NULL) { + *p = 0; + struct stat fs; + if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) { + isyslog(LOG_INFO, "creating directory %s", s); + if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) { + esyslog(LOG_ERR, "ERROR while creating directory %s: %s", s, strerror(errno)); + result = false; + break; + } + } + *p++ = '/'; + } + delete s; + return result; +} + // --- cListObject ----------------------------------------------------------- cListObject::cListObject(void) @@ -42,6 +83,7 @@ void cListObject::Unlink(void) next->prev = prev; if (prev) prev->next = next; + next = prev = NULL; } int cListObject::Index(void) @@ -92,6 +134,33 @@ void cListBase::Del(cListObject *Object) delete Object; } +void cListBase::Move(int From, int To) +{ + Move(Get(From), Get(To)); +} + +void cListBase::Move(cListObject *From, cListObject *To) +{ + if (From && To) { + if (From->Index() < To->Index()) + To = To->Next(); + if (From == objects) + objects = From->Next(); + if (From == lastObject) + lastObject = From->Prev(); + From->Unlink(); + if (To) { + if (To->Prev()) + To->Prev()->Append(From); + From->Append(To); + } + else + lastObject->Append(From); + if (!From->Prev()) + objects = From; + } +} + void cListBase::Clear(void) { while (objects) { diff --git a/tools.h b/tools.h index 43f85254..9f87bbde 100644 --- a/tools.h +++ b/tools.h @@ -4,12 +4,13 @@ * 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 $ + * $Id: tools.h 1.2 2000/03/05 16:14:05 kls Exp $ */ #ifndef __TOOLS_H #define __TOOLS_H +#include #include //TODO @@ -17,6 +18,14 @@ #define esyslog syslog #define isyslog syslog +#define SECSINDAY 86400 + +#define DELETENULL(p) (delete (p), p = NULL) + +char *readline(FILE *f); +int time_ms(void); +bool MakeDirs(const char *FileName); + class cListObject { private: cListObject *prev, *next; @@ -38,6 +47,8 @@ public: virtual ~cListBase(); void Add(cListObject *Object); void Del(cListObject *Object); + void Move(int From, int To); + void Move(cListObject *From, cListObject *To); void Clear(void); cListObject *Get(int Index); int Count(void); @@ -47,8 +58,7 @@ template class cList : public cListBase { public: T *Get(int Index) { return (T *)cListBase::Get(Index); } T *First(void) { return (T *)objects; } + T *Next(T *object) { return (T *)object->Next(); } }; -int time_ms(void); - #endif //__TOOLS_H