vdr/osd.c
Klaus Schmidinger 9f91980146 Version 1.1.27
- The CAM is now accessed only if the current channel actually has a non-zero Ca
  value, and CAM access is completely suppressed during replay, which avoids
  problems in case the CAM is attached to the primary DVB device.
- The "Left" and "Right" buttons now set the cursor to the first or last list item
  even if the list consist only of a single page, like, for instance, the Main menu
  (thanks to Oliver Endriss).
- Made the log message "OSD window width must be a multiple of 4..." a debug message
  instead of an error message, so it can be avoided by using a log level less than 3.
- Updated Greek language texts (thanks to Dimitrios Dimitrakos).
- Fixed faulty behaviour of the "Mute" key in case the channel display is visible
  (thanks to Florian Bartels for reporting this one and Sascha Volkenandt for
  helping to fix it).
- Modified LOF handling to allow for C-band reception (thanks to Malcolm Caldwell).
- Added some missing cAudio handling calls (thanks to Werner Fink).
- Replaced the 'for' loops in StripAudioPackets() with memset() calls (thanks to
  Werner Fink).
- Further increased the timeout until an index file is considerd no longer to be
  written.
- Fixed a crash in case the index file can't be accessed any more during replay
  (thanks to Stefan Huelswitt for reporting this one).
- Fixed displaying messages in the status line in case they exceed the OSD width
  (thanks to Gerhard Steiner for reporting this one).
- Avoiding high CPU load in case the connection to LIRC gets lost (thanks to
  Ludwig Nussel).
- Fixed handling repeat function with LIRC (thanks to Ludwig Nussel).
- Fixed handling min/max borders when entering integer values (thanks to Andy
  Grobb for reporting this one).
- Implemented a "resume ID" which allows several users to each have their own
  resume.vdr files (thanks to Martin Hammerschmid). This parameter can be set in
  the "Setup/Replay" menu (see MANUAL for details).
- Now using 'libdtv' version 0.0.5 (thanks to Rolf Hakenes for the new version
  and Stefan Huelswitt for adapting VDR to it).
- If no device with an MPEG decoder can be found at startup, the first device
  is now used as primary device (just to have some device).
- Adjusted some Premiere channels in 'channels.conf' (thanks to Thomas Koch).
- Updated 'channels.conf.cable' (thanks to Stefan Hußfeldt).
- The 'epg.data' file is now read after all plugins have been started (thanks
  to Sascha Volkenandt).
- The LIRC remote control no longer tries to learn keys if it can't connect to
  the LIRC daemon (thanks to Ludwig Nussel for reporting this one). The same
  applies to the RCU remote control in case of errors during startup.
- Fixed handling of Ca parameters with values <= MAXDEVICES, which don't indicate
  an actual encrypted channel (thanks to Stefan Huelswitt for reporting this one).
2003-04-13 18:00:00 +02:00

645 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.40 2003/03/23 15:41:54 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
cOsdBase *cOsd::osd = NULL;
#endif
int cOsd::cols = 0;
int cOsd::rows = 0;
void cOsd::Initialize(void)
{
#if defined(DEBUG_OSD)
initscr();
start_color();
leaveok(stdscr, true);
#endif
}
void cOsd::Shutdown(void)
{
Close();
#if defined(DEBUG_OSD)
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
cOsdBase *cOsd::OpenRaw(int x, int y)
{
#ifdef DEBUG_OSD
return NULL;
#else
return osd ? NULL : cDevice::PrimaryDevice()->NewOsd(x, y);
#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 = OpenRaw(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, h, 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);
refresh();
#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()
{
free(text);
}
void cOsdItem::SetText(const char *Text, bool Copy)
{
free(text);
text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership!
}
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)
{
isMenu = true;
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()
{
free(title);
delete subMenu;
free(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)
{
free(status);
status = s ? strdup(s) : NULL;
if (visible)
Interface->Status(status);
}
void cOsdMenu::SetTitle(const char *Title, bool ShowDate)
{
free(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)
{
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)
{
current += MAXOSDITEMS;
first += MAXOSDITEMS;
if (current > Count() - 1) {
current = Count() - 1;
first = max(0, 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
}
eOSState cOsdMenu::CloseSubMenu()
{
delete subMenu;
subMenu = NULL;
RefreshCurrent();
Display();
return osContinue; // convenience return value
}
eOSState cOsdMenu::ProcessKey(eKeys Key)
{
if (subMenu) {
eOSState state = subMenu->ProcessKey(Key);
if (state == osBack)
return CloseSubMenu();
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: return hasHotkeys ? HotKey(Key) : osUnknown;
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;
}