/* * 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.31 2002/07/14 10:57:45 kls Exp $ */ #include "osd.h" #include #include "device.h" #include "dvbosd.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) || 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 cOsdBase *cOsd::OpenRaw(int x, int y) { #ifdef DEBUG_OSD return NULL; #else return osd ? NULL : new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), 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, 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::Del(Get(Index)); if (current == Count()) current--; if (Index == first && first > 0) first--; } void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After) { cList::Add(Item, After); if (Current) current = Item->Index(); } void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before) { cList::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::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; }