diff options
Diffstat (limited to 'osd.c')
-rw-r--r-- | osd.c | 1109 |
1 files changed, 613 insertions, 496 deletions
@@ -4,651 +4,768 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.45 2004/03/14 10:33:20 kls Exp $ + * $Id: osd.c 1.46 2004/05/16 09:25:06 kls Exp $ */ #include "osd.h" -#include <string.h> -#include "device.h" -#include "i18n.h" -#include "status.h" +#include <math.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/unistd.h> +#include "tools.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; +// --- cPalette -------------------------------------------------------------- -void cOsd::Initialize(void) +cPalette::cPalette(int Bpp) { -#if defined(DEBUG_OSD) - initscr(); - start_color(); - leaveok(stdscr, true); -#endif + SetBpp(Bpp); } -void cOsd::Shutdown(void) +void cPalette::Reset(void) { - Close(); -#if defined(DEBUG_OSD) - endwin(); -#endif + numColors = 0; + modified = false; } -#ifdef DEBUG_OSD -void cOsd::SetColor(eDvbColor colorFg, eDvbColor colorBg) +int cPalette::Index(tColor Color) { - 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; - } + for (int i = 0; i < numColors; i++) { + if (color[i] == Color) + return i; } -} -#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 (!osd) - return; - 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); + if (numColors < maxColors) { + color[numColors++] = Color; + modified = true; + return numColors - 1; } - 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 + dsyslog("too many different colors used in palette"); + //TODO: return the index of the "closest" color? + return 0; } -void cOsd::Close(void) +void cPalette::SetBpp(int Bpp) { -#ifdef DEBUG_OSD - if (window) { - delwin(window); - window = 0; + bpp = Bpp; + maxColors = 1 << bpp; + Reset(); +} + +void cPalette::SetColor(int Index, tColor Color) +{ + if (Index < maxColors) { + if (numColors <= Index) { + numColors = Index + 1; + modified = true; + } + else + modified |= color[Index] != Color; + color[Index] = Color; } -#else - delete osd; - osd = NULL; -#endif } -void cOsd::Clear(void) +const tColor *cPalette::Colors(int &NumColors) { -#ifdef DEBUG_OSD - SetColor(clrBackground, clrBackground); - Fill(0, 0, cols, rows, clrBackground); - refresh(); -#else - if (osd) - osd->Clear(); -#endif + NumColors = numColors; + return numColors ? color : NULL; } -void cOsd::Fill(int x, int y, int w, int h, eDvbColor color) +void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg) { - 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); + for (int i = 0; i < Palette.numColors; i++) { + tColor Color = Palette.color[i]; + if (ColorFg || ColorBg) { + switch (i) { + case 0: Color = ColorBg; break; + case 1: Color = ColorFg; break; + } + } + int n = Index(Color); + if (Indexes) + (*Indexes)[i] = n; } - wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work -#else - if (osd) - 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) +// --- cBitmap --------------------------------------------------------------- + +cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0) +:cPalette(Bpp) { -#ifndef DEBUG_OSD - if (osd) - osd->SetBitmap(x, y, Bitmap); -#endif + bitmap = NULL; + x0 = X0; + y0 = Y0; + SetSize(Width, Height); } -void cOsd::ClrEol(int x, int y, eDvbColor color) +cBitmap::cBitmap(const char *FileName) { - Fill(x, y, cols - x, 1, color); + bitmap = NULL; + x0 = 0; + y0 = 0; + LoadXpm(FileName); } -int cOsd::CellWidth(void) +cBitmap::cBitmap(char *Xpm[]) { -#ifdef DEBUG_OSD - return 1; -#else - return charWidth; -#endif + bitmap = NULL; + x0 = 0; + y0 = 0; + SetXpm(Xpm); } -int cOsd::LineHeight(void) +cBitmap::~cBitmap() { -#ifdef DEBUG_OSD - return 1; -#else - return lineHeight; -#endif + free(bitmap); } -int cOsd::Width(unsigned char c) +void cBitmap::SetSize(int Width, int Height) { -#ifdef DEBUG_OSD - return 1; -#else - return osd ? osd->Width(c) : 1; -#endif + if (bitmap && Width == width && Height == height) + return; + width = Width; + height = Height; + free(bitmap); + bitmap = NULL; + dirtyX1 = 0; + dirtyY1 = 0; + dirtyX2 = width - 1; + dirtyY2 = height - 1; + if (width > 0 && height > 0) { + bitmap = MALLOC(tIndex, width * height); + if (bitmap) + memset(bitmap, 0x00, width * height); + else + esyslog("ERROR: can't allocate bitmap!"); + } + else + esyslog("ERROR: illegal bitmap parameters (%d, %d)!", width, height); } -int cOsd::WidthInCells(const char *s) +bool cBitmap::Contains(int x, int y) const { -#ifdef DEBUG_OSD - return strlen(s); -#else - return osd ? (osd->Width(s) + charWidth - 1) / charWidth : strlen(s); -#endif + x -= x0; + y -= y0; + return 0 <= x && x < width && 0 <= y && y < height; } -eDvbFont cOsd::SetFont(eDvbFont Font) +bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const { -#ifdef DEBUG_OSD - return Font; -#else - return osd ? osd->SetFont(Font) : Font; -#endif + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height); } -void cOsd::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg) +bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2) { - 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 - if (osd) - osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg); -#endif + if (dirtyX2 >= 0) { + x1 = dirtyX1; + y1 = dirtyY1; + x2 = dirtyX2; + y2 = dirtyY2; + return true; + } + return false; +} + +void cBitmap::Clean(void) +{ + dirtyX1 = width; + dirtyY1 = height; + dirtyX2 = -1; + dirtyY2 = -1; +} + +bool cBitmap::LoadXpm(const char *FileName) +{ + bool Result = false; + FILE *f = fopen(FileName, "r"); + if (f) { + char **Xpm = NULL; + bool isXpm = false; + int lines = 0; + int index = 0; + char *s; + while ((s = readline(f)) != NULL) { + s = skipspace(s); + if (!isXpm) { + if (strcmp(s, "/* XPM */") != 0) { + esyslog("ERROR: invalid header in XPM file '%s'", FileName); + break; + } + isXpm = true; + } + else if (*s++ == '"') { + if (!lines) { + int w, h, n, c; + if (4 != sscanf(s, "%d %d %d %d", &w, &h, &n, &c)) { + esyslog("ERROR: faulty 'values' line in XPM file '%s'", FileName); + break; + } + lines = h + n + 1; + Xpm = MALLOC(char *, lines); + } + char *q = strchr(s, '"'); + if (!q) { + esyslog("ERROR: missing quotes in XPM file '%s'", FileName); + break; + } + *q = 0; + if (index < lines) + Xpm[index++] = strdup(s); + else { + esyslog("ERROR: too many lines in XPM file '%s'", FileName); + break; + } + } + } + if (index == lines) + Result = SetXpm(Xpm); + else + esyslog("ERROR: too few lines in XPM file '%s'", FileName); + for (int i = 0; i < index; i++) + free(Xpm[i]); + free(Xpm); + fclose(f); + } + else + esyslog("ERROR: can't open XPM file '%s'", FileName); + return Result; } -void cOsd::Flush(void) +bool cBitmap::SetXpm(char *Xpm[]) { -#ifdef DEBUG_OSD - refresh(); -#else - if (osd) - osd->Flush(); -#endif + char **p = Xpm; + int w, h, n, c; + if (4 != sscanf(*p, "%d %d %d %d", &w, &h, &n, &c)) { + esyslog("ERROR: faulty 'values' line in XPM: '%s'", *p); + return false; + } + if (n > MAXNUMCOLORS) { + esyslog("ERROR: too many colors in XPM: %d", n); + return false; + } + int b = 0; + while (1 << (1 << b) < n) + b++; + SetBpp(1 << b); + SetSize(w, h); + for (int i = 0; i < n; i++) { + const char *s = *++p; + if (int(strlen(s)) < c) { + esyslog("ERROR: faulty 'colors' line in XPM: '%s'", s); + return false; + } + s = skipspace(s + c); + if (*s != 'c') { + esyslog("ERROR: unknown color key in XPM: '%c'", *s); + return false; + } + s = skipspace(s + 1); + if (*s != '#') { + esyslog("ERROR: unknown color code in XPM: '%c'", *s); + return false; + } + tColor color = strtoul(++s, NULL, 16) | 0xFF000000; + SetColor(i, color); + } + for (int y = 0; y < h; y++) { + const char *s = *++p; + if (int(strlen(s)) != w * c) { + esyslog("ERROR: faulty pixel line in XPM: %d '%s'", y, s); + return false; + } + for (int x = 0; x < w; x++) { + for (int i = 0; i <= n; i++) { + if (i == n) { + esyslog("ERROR: undefined pixel color in XPM: %d %d '%s'", x, y, s); + return false; + } + if (strncmp(Xpm[i + 1], s, c) == 0) { + SetIndex(x, y, i); + break; + } + } + s += c; + } + } + return true; } -// --- cOsdItem -------------------------------------------------------------- - -cOsdItem::cOsdItem(eOSState State) +void cBitmap::SetIndex(int x, int y, tIndex Index) { - text = NULL; - offset = -1; - state = State; - fresh = false; - userColor = false; - fgColor = clrWhite; - bgColor = clrBackground; + if (bitmap) { + if (0 <= x && x < width && 0 <= y && y < height) { + if (bitmap[width * y + x] != Index) { + bitmap[width * y + x] = Index; + if (dirtyX1 > x) dirtyX1 = x; + if (dirtyY1 > y) dirtyY1 = y; + if (dirtyX2 < x) dirtyX2 = x; + if (dirtyY2 < y) dirtyY2 = y; + } + } + } } -cOsdItem::cOsdItem(const char *Text, eOSState State) +void cBitmap::DrawPixel(int x, int y, tColor Color) { - text = NULL; - offset = -1; - state = State; - fresh = false; - userColor = false; - fgColor = clrWhite; - bgColor = clrBackground; - SetText(Text); + x -= x0; + y -= y0; + SetIndex(x, y, Index(Color)); } -cOsdItem::~cOsdItem() +void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg) { - free(text); + if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) { + x -= x0; + y -= y0; + tIndexes Indexes; + if (ColorFg || ColorBg) { + } + Take(Bitmap, &Indexes, ColorFg, ColorBg); + for (int ix = 0; ix < Bitmap.width; ix++) { + for (int iy = 0; iy < Bitmap.height; iy++) + SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]); + } + } } -void cOsdItem::SetText(const char *Text, bool Copy) -{ - free(text); - text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership! +void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) +{ + if (bitmap) { + int w = Font->Width(s); + int h = Font->Height(); + int limit = 0; + if (Width || Height) { + int cw = Width ? Width : w; + int ch = Height ? Height : h; + if (!Intersects(x, y, x + cw - 1, y + ch - 1)) + return; + DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg); + limit = x + cw - x0; + if (Width) { + if ((Alignment & taLeft) != 0) + ; + else if ((Alignment & taRight) != 0) { + if (w < Width) + x += Width - w; + } + else { // taCentered + if (w < Width) + x += (Width - w) / 2; + } + } + if (Height) { + if ((Alignment & taTop) != 0) + ; + else if ((Alignment & taBottom) != 0) { + if (h < Height) + y += Height - h; + } + else { // taCentered + if (h < Height) + y += (Height - h) / 2; + } + } + } + else if (!Intersects(x, y, x + w - 1, y + h - 1)) + return; + x -= x0; + y -= y0; + tIndex fg = Index(ColorFg); + tIndex bg = Index(ColorBg); + while (s && *s) { + const cFont::tCharData *CharData = Font->CharData(*s++); + if (limit && int(x + CharData->width) > limit) + break; // we don't draw partial characters + if (int(x + CharData->width) > 0) { + for (int row = 0; row < h; row++) { + cFont::tPixelData PixelData = CharData->lines[row]; + for (int col = CharData->width; col-- > 0; ) { + SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg); + PixelData >>= 1; + } + } + } + x += CharData->width; + if (x > width - 1) + break; + } + } +} + +void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) +{ + if (bitmap && Intersects(x1, y1, x2, y2)) { + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + x1 = max(x1, 0); + y1 = max(y1, 0); + x2 = min(x2, width - 1); + y2 = min(y2, height - 1); + if (x1 == 0 && y1 == 0 && x2 == width - 1 && y2 == height - 1) + Reset(); + tIndex c = Index(Color); + for (int y = y1; y <= y2; y++) + for (int x = x1; x <= x2; x++) + SetIndex(x, y, c); + } } -void cOsdItem::SetColor(eDvbColor FgColor, eDvbColor BgColor) +void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants) { - userColor = true; - fgColor = FgColor; - bgColor = BgColor; + if (!Intersects(x1, y1, x2, y2)) + return; + // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF + int rx = x2 - x1; + int ry = y2 - y1; + int cx = (x1 + x2) / 2; + int cy = (y1 + y2) / 2; + switch (abs(Quadrants)) { + case 0: rx /= 2; ry /= 2; break; + case 1: cx = x1; cy = y2; break; + case 2: cx = x2; cy = y2; break; + case 3: cx = x2; cy = y1; break; + case 4: cx = x1; cy = y1; break; + case 5: cx = x1; ry /= 2; break; + case 6: cy = y2; rx /= 2; break; + case 7: cx = x2; ry /= 2; break; + case 8: cy = y1; rx /= 2; break; + } + int TwoASquare = 2 * rx * rx; + int TwoBSquare = 2 * ry * ry; + int x = rx; + int y = 0; + int XChange = ry * ry * (1 - 2 * rx); + int YChange = rx * rx; + int EllipseError = 0; + int StoppingX = TwoBSquare * rx; + int StoppingY = 0; + while (StoppingX >= StoppingY) { + switch (Quadrants) { + case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break + case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break; + case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break + case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break; + case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break; + case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break; + case 0: + case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break; + case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break; + case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break; + case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break; + case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break; + case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break; + } + y++; + StoppingY += TwoASquare; + EllipseError += YChange; + YChange += TwoASquare; + if (2 * EllipseError + XChange > 0) { + x--; + StoppingX -= TwoBSquare; + EllipseError += XChange; + XChange += TwoBSquare; + } + } + x = 0; + y = ry; + XChange = ry * ry; + YChange = rx * rx * (1 - 2 * ry); + EllipseError = 0; + StoppingX = 0; + StoppingY = TwoASquare * ry; + while (StoppingX <= StoppingY) { + switch (Quadrants) { + case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break + case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break; + case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break + case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break; + case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break; + case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break; + case 0: + case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break; + case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break; + case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break; + case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break; + case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break; + case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break; + } + x++; + StoppingX += TwoBSquare; + EllipseError += XChange; + XChange += TwoBSquare; + if (2 * EllipseError + YChange > 0) { + y--; + StoppingY -= TwoASquare; + EllipseError += YChange; + YChange += TwoASquare; + } + } } -void cOsdItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) +void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) { - if (Offset < 0) { - FgColor = clrBlack; - BgColor = clrCyan; + // TODO This is just a quick and dirty implementation of a slope drawing + // machanism. If somebody can come up with a better solution, let's have it! + if (!Intersects(x1, y1, x2, y2)) + return; + bool upper = Type & 0x01; + bool falling = Type & 0x02; + bool vertical = Type & 0x04; + if (vertical) { + for (int y = y1; y <= y2; y++) { + double c = cos((y - y1) * M_PI / (y2 - y1 + 1)); + if (falling) + c = -c; + int x = int((x2 - x1 + 1) * c / 2); + if (upper && !falling || !upper && falling) + DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color); + else + DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color); + } + } + else { + for (int x = x1; x <= x2; x++) { + double c = cos((x - x1) * M_PI / (x2 - x1 + 1)); + if (falling) + c = -c; + int y = int((y2 - y1 + 1) * c / 2); + if (upper) + DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color); + else + DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color); + } } - 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) +const tIndex *cBitmap::Data(int x, int y) { - return Key == kOk ? state : osUnknown; + return &bitmap[y * width + x]; } -// --- cOsdMenu -------------------------------------------------------------- +// --- cOsd ------------------------------------------------------------------ + +bool cOsd::isOpen = false; -cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) +cOsd::cOsd(int Left, int Top) { - 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(); + if (isOpen) + esyslog("ERROR: OSD opened without closing previous OSD!"); + savedRegion = NULL; + numBitmaps = 0; + left = Left; + top = Top; + width = height = 0; + isOpen = true; } -cOsdMenu::~cOsdMenu() +cOsd::~cOsd() { - free(title); - delete subMenu; - free(status); - Interface->Clear(); - Interface->Close(); + for (int i = 0; i < numBitmaps; i++) + delete bitmaps[i]; + delete savedRegion; + isOpen = false; } -const char *cOsdMenu::hk(const char *s) +cBitmap *cOsd::GetBitmap(int Area) { - 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; + return Area < numBitmaps ? bitmaps[Area] : NULL; } -void cOsdMenu::SetHasHotkeys(void) +eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas) { - hasHotkeys = true; - digit = 0; + for (int i = 0; i < NumAreas; i++) { + for (int j = i + 1; j < NumAreas; j++) { + if (Areas[i].Intersects(Areas[j])) + return oeAreasOverlap; + if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0) + return oeWrongAlignment; + } + } + return oeOk; } -void cOsdMenu::SetStatus(const char *s) +eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas) { - free(status); - status = s ? strdup(s) : NULL; - if (visible) - Interface->Status(status); + eOsdError Result = oeUnknown; + if (numBitmaps == 0) { + Result = CanHandleAreas(Areas, NumAreas); + if (Result == oeOk) { + width = height = 0; + for (int i = 0; i < NumAreas; i++) { + bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1); + width = max(width, Areas[i].x2); + height = max(height, Areas[i].y2); + } + } + } + if (Result != oeOk) + esyslog("ERROR: cOsd::SetAreas returned %d\n", Result); + return Result; } -void cOsdMenu::SetTitle(const char *Title, bool ShowDate) +void cOsd::SaveRegion(int x1, int y1, int x2, int y2) { - free(title); - if (ShowDate) - asprintf(&title, "%s\t%s", Title, DayDateTime(time(NULL))); - else - title = strdup(Title); + delete savedRegion; + savedRegion = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1); + for (int i = 0; i < numBitmaps; i++) + savedRegion->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]); } -void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) +void cOsd::RestoreRegion(void) { - // strings are NOT copied - must be constants!!! - helpRed = Red; - helpGreen = Green; - helpYellow = Yellow; - helpBlue = Blue; - if (visible) - Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); + if (savedRegion) { + DrawBitmap(savedRegion->X0(), savedRegion->Y0(), *savedRegion); + delete savedRegion; + savedRegion = NULL; + } } -void cOsdMenu::Del(int Index) +eOsdError cOsd::SetPalette(const cPalette &Palette, int Area) { - cList<cOsdItem>::Del(Get(Index)); - if (current == Count()) - current--; - if (Index == first && first > 0) - first--; + if (Area < numBitmaps) + bitmaps[Area]->Take(Palette); + return oeUnknown; } -void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After) +void cOsd::DrawPixel(int x, int y, tColor Color) { - cList<cOsdItem>::Add(Item, After); - if (Current) - current = Item->Index(); + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawPixel(x, y, Color); } -void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before) +void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg) { - cList<cOsdItem>::Ins(Item, Before); - if (Current) - current = Item->Index(); + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg); } -void cOsdMenu::Display(void) +void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) { - 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) { - int ni = 0; - for (cOsdItem *item = First(); item; item = Next(item)) - cStatus::MsgOsdItem(item->Text(), ni++); - if (current < 0) - current = 0; // just for safety - there HAS to be a current item! - if (current - first >= MAXOSDITEMS || current < first) { - first = current - MAXOSDITEMS / 2; - if (first + MAXOSDITEMS > count) - first = count - MAXOSDITEMS; - if (first < 0) - first = 0; - } - int i = first; - int n = 0; - for (cOsdItem *item = Get(first); item; item = Next(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; - i++; - } - } - if (!isempty(status)) - Interface->Status(status); + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment); } -void cOsdMenu::SetCurrent(cOsdItem *Item) +void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) { - current = Item ? Item->Index() : -1; + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color); } -void cOsdMenu::RefreshCurrent(void) +void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants) { - cOsdItem *item = Get(current); - if (item) - item->Set(); + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants); } -void cOsdMenu::DisplayCurrent(bool Current) +void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) { - cOsdItem *item = Get(current); - if (item) { - item->Display(current - first, Current ? clrBlack : clrWhite, Current ? clrCyan : clrBackground); - if (Current) - cStatus::MsgOsdCurrentItem(item->Text()); - } + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type); } -void cOsdMenu::Clear(void) +void cOsd::Flush(void) { - first = 0; - current = marked = -1; - cList<cOsdItem>::Clear(); } -bool cOsdMenu::SpecialItem(int idx) +// --- cOsdProvider ---------------------------------------------------------- + +cOsdProvider *cOsdProvider::osdProvider = NULL; + +cOsdProvider::cOsdProvider(void) { - cOsdItem *item = Get(idx); - return item && item->HasUserColor(); + delete osdProvider; + osdProvider = this; } -void cOsdMenu::CursorUp(void) +cOsdProvider::~cOsdProvider() { - 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); - } + osdProvider = NULL; } -void cOsdMenu::CursorDown(void) +cOsd *cOsdProvider::NewOsd(int Left, int Top) { - 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); - } + if (osdProvider) + return osdProvider->CreateOsd(Left, Top); + esyslog("ERROR: no OSD provider available - using dummy OSD!"); + return new cOsd(Left, Top); // create a dummy cOsd, so that access won't result in a segfault } -void cOsdMenu::PageUp(void) +void cOsdProvider::Shutdown(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); + delete osdProvider; + osdProvider = NULL; } -void cOsdMenu::PageDown(void) +// --- cTextScroller --------------------------------------------------------- + +cTextScroller::cTextScroller(void) { - current += MAXOSDITEMS; - first += MAXOSDITEMS; - int count = Count(); - 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); + osd = NULL; + left = top = width = height = 0; + font = NULL; + colorFg = 0; + colorBg = 0; + offset = 0; + shown = 0; } -void cOsdMenu::Mark(void) +cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg) { - if (Count() && marked < 0) { - marked = current; - SetStatus(tr("Up/Dn for new location - OK to move")); - } + Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg); } -eOSState cOsdMenu::HotKey(eKeys Key) +void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg) { - 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(); - cRemote::Put(kOk, true); - break; - } - } - } - return osContinue; + osd = Osd; + left = Left; + top = Top; + width = Width; + height = Height; + font = Font; + colorFg = ColorFg; + colorBg = ColorBg; + offset = 0; + textWrapper.Set(Text, Font, Width); + shown = min(Total(), height / font->Height()); + height = shown * font->Height(); // sets height to the actually used height, which may be less than Height + DrawText(); } -eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) +void cTextScroller::Reset(void) { - delete subMenu; - subMenu = SubMenu; - subMenu->Display(); - return osContinue; // convenience return value + osd = NULL; // just makes sure it won't draw anything } -eOSState cOsdMenu::CloseSubMenu() +void cTextScroller::DrawText(void) { - delete subMenu; - subMenu = NULL; - RefreshCurrent(); - Display(); - return osContinue; // convenience return value + if (osd) { + for (int i = 0; i < shown; i++) + osd->DrawText(left, top + i * font->Height(), textWrapper.GetLine(offset + i), colorFg, colorBg, font, width); + } } -eOSState cOsdMenu::ProcessKey(eKeys Key) +void cTextScroller::Scroll(bool Up, bool Page) { - if (subMenu) { - eOSState state = subMenu->ProcessKey(Key); - if (state == osBack) - return CloseSubMenu(); - return state; + if (Up) { + if (CanScrollUp()) { + offset -= Page ? shown : 1; + if (offset < 0) + offset = 0; + DrawText(); + } } - - cOsdItem *item = Get(current); - if (marked < 0 && item) { - eOSState state = item->ProcessKey(Key); - if (state != osUnknown) - return state; + else { + if (CanScrollDown()) { + offset += Page ? shown : 1; + if (offset + shown > Total()) + offset = Total() - shown; + DrawText(); + } } - 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; } - |