vdr/osd.c
Klaus Schmidinger f06d2c27fc Version 1.1.3
- Improved the VDR Makefile to avoid a warning if the '.dependencies' file does
  not exist, and also using $(MAKE) to call recursive makes.
- Changed the name of the 'package' target in the plugin Makefiles to 'dist'
  (following the suggestions in the "GNU Make" manual). If you already have started
  a plugin project, you may want to change this in your Makefile accordingly.
- Improved the plugin Makefile to avoid a warning if the '.dependencies' file does
  not exist, and also using $(shell...) to get the version numbers. If you already have
  started a plugin project, you may want to change this in your Makefile accordingly.
- Fixed some function headers to make them compile with gcc 3.x (thanks to
  Gregoire Favre).
- Fixed the cutting mechanism to make it re-sync in case a frame is larger than the
  buffer (thanks to Sven Grothklags).
- Added an error message if the directory specified in the '-L' option can't be
  accessed (suggested by Stefan Huelswitt).
- Rearranged OSD class names to make 'cOsd' available for the main OSD interface.
- Completely moved OSD handling out of the cDvbApi class, into the new cOsd.
- Implemented cStatus to allow plugins to set up a status monitor.
  See PLUGINS.html for details.
- Moved the cEITScanner out of dvbapi.h/.c, into the new eitscan.h/.c.
- Added Swedish language texts (thanks to Tomas Prybil).
- Fixed parsing 'E' records in epg2html.pl (thanks to Matthias Fechner for pointing
  out this one).
- Removed compiler option '-m486' to make it work on non-Intel platforms. If you
  have already started a plugin project, you may want to make sure you remove this
  option from your existing Makefile.
- Completely rearranged the recording and replay functions to make them available
  to plugins.
- Replay is now done in a single thread (no more syncing between input and output
  thread necessary).
- It is now possible to record several channels on the same transponder with "budget
  cards". VDR automatically attaches a recording timer to a card that already
  records on the appropriate transponder. How many parallel recordings can actually
  be done depends on the computer's performance. Currently any number of recordings
  gets attached to a card, so you should carefully plan your timers to not exceed
  the limit. On a K6-II/450 it was possible to record three channels from transponder
  12480 with a single WinTV NOVA-S.
- Timers that record two successive shows on the same channel may now overlap and
  will use the same DVB card. During the time where both timers record the data
  is simply saved to both files.
- The following limitations apply to this version:
  + Transfer mode doesn't work yet.
  + The '-a' option (for Dolby Digital audio) doesn't work yet.
  + Switching between different language tracks doesn't work yet.
  + Cutting doesn't work yet.
2002-06-16 18:00:00 +02:00

643 lines
14 KiB
C

/*
* osd.c: Abstract On Screen Display layer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.c 1.29 2002/06/16 13:24:00 kls Exp $
*/
#include "osd.h"
#include <string.h>
#include "device.h"
#include "i18n.h"
#include "status.h"
// --- cOsd ------------------------------------------------------------------
#ifdef DEBUG_OSD
WINDOW *cOsd::window = NULL;
int cOsd::colorPairs[MaxColorPairs] = { 0 };
#else
cDvbOsd *cOsd::osd = NULL;
#endif
int cOsd::cols = 0;
int cOsd::rows = 0;
void cOsd::Initialize(void)
{
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
initscr();
keypad(stdscr, true);
nonl();
cbreak();
noecho();
timeout(10);
#endif
#if defined(DEBUG_OSD)
start_color();
leaveok(stdscr, true);
#endif
}
void cOsd::Shutdown(void)
{
Close();
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
endwin();
#endif
}
#ifdef DEBUG_OSD
void cOsd::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;
}
}
}
#endif
void cOsd::Open(int w, int h)
{
int d = (h < 0) ? Setup.OSDheight + h : 0;
h = abs(h);
cols = w;
rows = h;
#ifdef DEBUG_OSD
window = subwin(stdscr, h, w, d, (Setup.OSDwidth - w) / 2);
syncok(window, true);
#define B2C(b) (((b) * 1000) / 255)
#define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b))
//XXX
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);
#else
w *= charWidth;
h *= lineHeight;
d *= lineHeight;
int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC???
int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;
//XXX
osd = new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y);
//XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)
if (h / lineHeight == 5) { //XXX channel display
osd->Create(0, 0, w, h, 4);
}
else if (h / lineHeight == 1) { //XXX info display
osd->Create(0, 0, w, h, 4);
}
else if (d == 0) { //XXX full menu
osd->Create(0, 0, w, lineHeight, 2);
osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2);
osd->AddColor(clrBackground);
osd->AddColor(clrCyan);
osd->AddColor(clrWhite);
osd->AddColor(clrBlack);
osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4);
}
else { //XXX progress display
/*XXX
osd->Create(0, 0, w, lineHeight, 1);
osd->Create(0, lineHeight, w, lineHeight, 2, false);
osd->Create(0, 2 * lineHeight, w, lineHeight, 1);
XXX*///XXX some pixels are not drawn correctly with lower bpp values
osd->Create(0, 0, w, 3*lineHeight, 4);
}
#endif
}
void cOsd::Close(void)
{
#ifdef DEBUG_OSD
if (window) {
delwin(window);
window = 0;
}
#else
delete osd;
osd = NULL;
#endif
}
void cOsd::Clear(void)
{
#ifdef DEBUG_OSD
SetColor(clrBackground, clrBackground);
Fill(0, 0, cols, rows, clrBackground);
#else
osd->Clear();
#endif
}
void cOsd::Fill(int x, int y, int w, int h, eDvbColor color)
{
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);
}
wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work
#else
osd->Fill(x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1, color);
#endif
}
void cOsd::SetBitmap(int x, int y, const cBitmap &Bitmap)
{
#ifndef DEBUG_OSD
osd->SetBitmap(x, y, Bitmap);
#endif
}
void cOsd::ClrEol(int x, int y, eDvbColor color)
{
Fill(x, y, cols - x, 1, color);
}
int cOsd::CellWidth(void)
{
#ifdef DEBUG_OSD
return 1;
#else
return charWidth;
#endif
}
int cOsd::LineHeight(void)
{
#ifdef DEBUG_OSD
return 1;
#else
return lineHeight;
#endif
}
int cOsd::Width(unsigned char c)
{
#ifdef DEBUG_OSD
return 1;
#else
return osd->Width(c);
#endif
}
int cOsd::WidthInCells(const char *s)
{
#ifdef DEBUG_OSD
return strlen(s);
#else
return (osd->Width(s) + charWidth - 1) / charWidth;
#endif
}
eDvbFont cOsd::SetFont(eDvbFont Font)
{
#ifdef DEBUG_OSD
return Font;
#else
return osd->SetFont(Font);
#endif
}
void cOsd::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'!
waddnstr(window, s, cols - x);
#else
osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg);
#endif
}
void cOsd::Flush(void)
{
#ifdef DEBUG_OSD
refresh();
#else
if (osd)
osd->Flush();
#endif
}
// --- cOsdItem --------------------------------------------------------------
cOsdItem::cOsdItem(eOSState State)
{
text = NULL;
offset = -1;
state = State;
fresh = false;
userColor = false;
fgColor = clrWhite;
bgColor = clrBackground;
}
cOsdItem::cOsdItem(const char *Text, eOSState State)
{
text = NULL;
offset = -1;
state = State;
fresh = false;
userColor = false;
fgColor = clrWhite;
bgColor = clrBackground;
SetText(Text);
}
cOsdItem::~cOsdItem()
{
delete text;
}
void cOsdItem::SetText(const char *Text, bool Copy)
{
delete text;
text = Copy ? strdup(Text) : Text;
}
void cOsdItem::SetColor(eDvbColor FgColor, eDvbColor BgColor)
{
userColor = true;
fgColor = FgColor;
bgColor = BgColor;
}
void cOsdItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
{
if (Offset < 0) {
FgColor = clrBlack;
BgColor = clrCyan;
}
fresh |= Offset >= 0;
if (Offset >= 0)
offset = Offset;
if (offset >= 0)
Interface->WriteText(0, offset + 2, text, userColor ? fgColor : FgColor, userColor ? bgColor : BgColor);
}
eOSState cOsdItem::ProcessKey(eKeys Key)
{
return Key == kOk ? state : osUnknown;
}
// --- cOsdMenu --------------------------------------------------------------
cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
{
digit = 0;
hasHotkeys = false;
visible = false;
title = NULL;
SetTitle(Title);
cols[0] = c0;
cols[1] = c1;
cols[2] = c2;
cols[3] = c3;
cols[4] = c4;
first = 0;
current = marked = -1;
subMenu = NULL;
helpRed = helpGreen = helpYellow = helpBlue = NULL;
status = NULL;
Interface->Open();
}
cOsdMenu::~cOsdMenu()
{
delete title;
delete subMenu;
delete status;
Interface->Clear();
Interface->Close();
}
const char *cOsdMenu::hk(const char *s)
{
static char buffer[64];
if (s && hasHotkeys) {
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
digit = -1; // prevents automatic hotkeys - input already has them
if (digit >= 0) {
digit++;
snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s);
s = buffer;
}
}
return s;
}
void cOsdMenu::SetHasHotkeys(void)
{
hasHotkeys = true;
digit = 0;
}
void cOsdMenu::SetStatus(const char *s)
{
delete status;
status = s ? strdup(s) : NULL;
if (visible)
Interface->Status(status);
}
void cOsdMenu::SetTitle(const char *Title, bool ShowDate)
{
delete title;
if (ShowDate)
asprintf(&title, "%s\t%s", Title, DayDateTime(time(NULL)));
else
title = strdup(Title);
}
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;
if (visible)
Interface->Help(helpRed, helpGreen, helpYellow, helpBlue);
}
void cOsdMenu::Del(int Index)
{
cList<cOsdItem>::Del(Get(Index));
if (current == Count())
current--;
if (Index == first && first > 0)
first--;
}
void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
{
cList<cOsdItem>::Add(Item, After);
if (Current)
current = Item->Index();
}
void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
{
cList<cOsdItem>::Ins(Item, Before);
if (Current)
current = Item->Index();
}
void cOsdMenu::Display(void)
{
if (subMenu) {
subMenu->Display();
return;
}
visible = true;
Interface->Clear();
Interface->SetCols(cols);
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 ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground);
if (i == current)
cStatus::MsgOsdCurrentItem(item->Text());
}
if (++n == MAXOSDITEMS) //TODO get this from Interface!!!
break;
}
}
if (!isempty(status))
Interface->Status(status);
}
void cOsdMenu::SetCurrent(cOsdItem *Item)
{
current = Item ? Item->Index() : -1;
}
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 ? clrBlack : clrWhite, Current ? clrCyan : clrBackground);
if (Current)
cStatus::MsgOsdCurrentItem(item->Text());
}
}
void cOsdMenu::Clear(void)
{
first = 0;
current = marked = -1;
cList<cOsdItem>::Clear();
}
bool cOsdMenu::SpecialItem(int idx)
{
cOsdItem *item = Get(idx);
return item && item->HasUserColor();
}
void cOsdMenu::CursorUp(void)
{
if (current > 0) {
int tmpCurrent = current;
while (--tmpCurrent >= 0 && SpecialItem(tmpCurrent));
if (tmpCurrent < 0)
return;
if (tmpCurrent >= first)
DisplayCurrent(false);
current = tmpCurrent;
if (current < first) {
first = first > MAXOSDITEMS - 1 ? first - (MAXOSDITEMS - 1) : 0;
if (Setup.MenuScrollPage)
current = SpecialItem(first) ? first + 1 : first;
Display();
}
else
DisplayCurrent(true);
}
}
void cOsdMenu::CursorDown(void)
{
int last = Count() - 1;
int lastOnScreen = first + MAXOSDITEMS - 1;
if (current < last) {
int tmpCurrent = current;
while (++tmpCurrent <= last && SpecialItem(tmpCurrent));
if (tmpCurrent > last)
return;
if (tmpCurrent <= lastOnScreen)
DisplayCurrent(false);
current = tmpCurrent;
if (current > lastOnScreen) {
first += MAXOSDITEMS - 1;
lastOnScreen = first + MAXOSDITEMS - 1;
if (lastOnScreen > last) {
first = last - (MAXOSDITEMS - 1);
lastOnScreen = last;
}
if (Setup.MenuScrollPage)
current = SpecialItem(lastOnScreen) ? lastOnScreen - 1 : lastOnScreen;
Display();
}
else
DisplayCurrent(true);
}
}
void cOsdMenu::PageUp(void)
{
if (Count() <= MAXOSDITEMS)
return;
current -= MAXOSDITEMS;
first -= MAXOSDITEMS;
if (first < 0)
first = current = 0;
if (SpecialItem(current)) {
current -= (current > 0) ? 1 : -1;
first = min(first, current - 1);
}
Display();
DisplayCurrent(true);
}
void cOsdMenu::PageDown(void)
{
if (Count() <= MAXOSDITEMS)
return;
current += MAXOSDITEMS;
first += MAXOSDITEMS;
if (current > Count() - 1) {
current = Count() - 1;
first = Count() - MAXOSDITEMS;
}
if (SpecialItem(current)) {
current += (current < Count() - 1) ? 1 : -1;
first = max(first, current - MAXOSDITEMS);
}
Display();
DisplayCurrent(true);
}
void cOsdMenu::Mark(void)
{
if (Count() && marked < 0) {
marked = current;
SetStatus(tr("Up/Dn for new location - OK to move"));
}
}
eOSState cOsdMenu::HotKey(eKeys Key)
{
for (cOsdItem *item = First(); item; item = Next(item)) {
const char *s = item->Text();
if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') {
current = item->Index();
return ProcessKey(kOk);
}
}
}
return osContinue;
}
eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
{
delete subMenu;
subMenu = SubMenu;
subMenu->Display();
return osContinue; // convenience return value (see cMenuMain)
}
eOSState cOsdMenu::ProcessKey(eKeys Key)
{
if (subMenu) {
eOSState state = subMenu->ProcessKey(Key);
if (state == osBack) {
delete subMenu;
subMenu = NULL;
RefreshCurrent();
Display();
state = osContinue;
}
return state;
}
cOsdItem *item = Get(current);
if (marked < 0 && item) {
eOSState state = item->ProcessKey(Key);
if (state != osUnknown)
return state;
}
switch (Key) {
case k1...k9: if (hasHotkeys)
return HotKey(Key);
break;
case kUp|k_Repeat:
case kUp: CursorUp(); break;
case kDown|k_Repeat:
case kDown: CursorDown(); break;
case kLeft|k_Repeat:
case kLeft: PageUp(); break;
case kRight|k_Repeat:
case kRight: PageDown(); break;
case kBack: return osBack;
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;
}