vdr/skins.c
Klaus Schmidinger 7df66b0587 Version 1.7.20
Original announce message:
VDR developer version 1.7.20 is now available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.20.tar.bz2

A 'diff' against the previous version is available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.19-1.7.20.diff

MD5 checksums:

eda2911fff1715ba5b1482b20ad18188  vdr-1.7.20.tar.bz2
a8f5bcaf3294cc9fce87283a618d5ce1  vdr-1.7.19-1.7.20.diff

WARNING:
========

This is a developer version. Even though I use it in my productive
environment. I strongly recommend that you only use it under controlled
conditions and for testing and debugging.

This version contains functions to determine the "signal strength"
and "signal quality" through cDevice. If you are using a DVB card that
contains an stb0899 frontend chip (like the TT-budget S2-3200) you may
want to apply the patches from

   ftp://ftp.tvdr.de/vdr/Developer/Driver-Patches

to the LinuxDVB driver source in order to receive useful results from
that frontend.

From the HISTORY file:
- Added some missing 'const' to tChannelID (reported by Sundararaj Reel).
- The isnumber() function now checks the given pointer for NULL (thanks to Holger
  Dengler).
- Now checking Setup.InitialChannel for NULL before using it (reported by
  Christoph Haubrich).
- cSkins::Message() now blocks calls from background threads (thanks to Michael
  Eiler for reporting a crash in such a scenario).
- Fixed the return value of the svdrpsend.pl script in case of an error (thanks to
  Jonas Diemer).
- Increased MAXCAIDS to 12 (thanks to Jerome Lacarriere).
- Fixed handling DiSEqC codes (thanks to Mark Hawes for reporting the bug, and
  Udo Richter for suggesting the fix).
- Added a mechanism to defer timer handling in case of problems (reported by
  Frank Niederwipper).
- Fixed distortions that happened when splitting recording into several files
  (was a side effect of "Fixed detecting frames in case the Picture Start Code or
  Access Unit Delimiter extends over TS packet boundaries" in version 1.7.19).
  cRecorder::Action() now buffers TS packets in case the frame type is
  not yet known when a new payload starts. This adds no overhead for channels
  that broadcast the frame type within the first TS packet of a payload; it only
  kicks in if that information is not in the first TS packet.
- Fixed handling the channelID in cMenuEditChanItem (thanks to Udo Richter).
- cStringList::Sort() can now be called with a boolean parameter that controls
  case insensitive sorting (suggested by Sundararaj Reel).
- Now scanning new transponders before old ones, to make sure transponder changes
  are recognized (thanks to Reinhard Nissl).
- Implemented static cIndexFile::IndexFileName().
- The length (as number of frames) of a recording's index file can now be determined
  by a call to cIndexFile::GetLength() (suggested by Christoph Haubrich).
- Fixed some crashes in subtitle display (thanks to Rolf Ahrenberg).
- Made DELETENULL() thread safe (reported by Rolf Ahrenberg).
- The pic2mpg script of the 'pictures' plugin now generates HD images (thanks to
  Andre Weidemann for his support in using convert/ffmpeg). The old SD version is
  still available as pic2mpg-sd.
- Added a mutex to protect cOsd::Osds from simultaneous access from different threads
  (reported by Rolf Ahrenberg).
- The cutter now sets the 'broken link' flag for MPEG2 TS recordings (thanks to
  Oliver Endriss).
- Fixed language code entry for Portuguese.
- The new command line options --filesize (suggested by Marco Göbenich) and --split
  can be used together with --edit to set the maximum video file size and turn on
  splitting edited files at the editing marks. These options must be given before
  --edit to have an effect.
- cTimeMs is no longer initialized to the current time if the value given to the
  constructor is negative (avoids the "cTimeMs: using monotonic clock..." log message
  before VDR's starting log message).
2011-08-16 20:32:01 +02:00

379 lines
9.7 KiB
C

/*
* skins.c: The optical appearance of the OSD
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skins.c 2.2 2011/08/06 09:41:57 kls Exp $
*/
#include "skins.h"
#include "interface.h"
#include "status.h"
// --- cSkinQueuedMessage ----------------------------------------------------
class cSkinQueuedMessage : public cListObject {
friend class cSkins;
private:
eMessageType type;
char *message;
int seconds;
int timeout;
tThreadId threadId;
eKeys key;
int state;
cMutex mutex;
cCondVar condVar;
public:
cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
virtual ~cSkinQueuedMessage();
};
cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
type = Type;
message = s ? strdup(s) : NULL;
seconds = Seconds;
timeout = Timeout;
threadId = cThread::ThreadId();
key = kNone;
state = 0; // waiting
}
cSkinQueuedMessage::~cSkinQueuedMessage()
{
free(message);
}
cList<cSkinQueuedMessage> SkinQueuedMessages;
// --- cSkinDisplay ----------------------------------------------------------
cSkinDisplay *cSkinDisplay::current = NULL;
cSkinDisplay::cSkinDisplay(void)
{
current = this;
editableWidth = 100; //XXX
}
cSkinDisplay::~cSkinDisplay()
{
current = NULL;
}
// --- cSkinDisplayMenu ------------------------------------------------------
cSkinDisplayMenu::cSkinDisplayMenu(void)
{
SetTabs(0);
}
void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5)
{
tabs[0] = 0;
tabs[1] = Tab1 ? tabs[0] + Tab1 : 0;
tabs[2] = Tab2 ? tabs[1] + Tab2 : 0;
tabs[3] = Tab3 ? tabs[2] + Tab3 : 0;
tabs[4] = Tab4 ? tabs[3] + Tab4 : 0;
tabs[5] = Tab5 ? tabs[4] + Tab5 : 0;
int AvgCharWidth = Setup.FontOsdSize * 3 / 5; // just an estimate
for (int i = 1; i < MaxTabs; i++)
tabs[i] *= AvgCharWidth;
}
void cSkinDisplayMenu::Scroll(bool Up, bool Page)
{
textScroller.Scroll(Up, Page);
}
const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab)
{
if (!s)
return NULL;
static char buffer[1000];
const char *a = s;
const char *b = strchrnul(a, '\t');
while (*b && Tab-- > 0) {
a = b + 1;
b = strchrnul(a, '\t');
}
if (!*b)
return (Tab <= 0) ? a : NULL;
unsigned int n = b - a;
if (n >= sizeof(buffer))
n = sizeof(buffer) - 1;
strncpy(buffer, a, n);
buffer[n] = 0;
return buffer;
}
void cSkinDisplayMenu::SetScrollbar(int Total, int Offset)
{
}
int cSkinDisplayMenu::GetTextAreaWidth(void) const
{
return 0;
}
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool) const
{
return NULL;
}
// --- cSkinDisplayReplay::cProgressBar --------------------------------------
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
:cBitmap(Width, Height, 2)
{
total = Total;
if (total > 0) {
int p = Pos(Current);
DrawRectangle(0, 0, p, Height - 1, ColorSeen);
DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
if (Marks) {
bool Start = true;
for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
int p1 = Pos(m->position);
if (Start) {
const cMark *m2 = Marks->Next(m);
int p2 = Pos(m2 ? m2->position : total);
int h = Height / 3;
DrawRectangle(p1, h, p2, Height - h, ColorSelected);
}
Mark(p1, Start, m->position == Current, ColorMark, ColorCurrent);
Start = !Start;
}
}
}
}
void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
{
DrawRectangle(x, 0, x, Height() - 1, ColorMark);
const int d = Height() / (Current ? 3 : 9);
for (int i = 0; i < d; i++) {
int h = Start ? i : Height() - 1 - i;
DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
}
}
// --- cSkinDisplayReplay ----------------------------------------------------
cSkinDisplayReplay::cSkinDisplayReplay(void)
{
marks = NULL;
}
void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
{
marks = Marks;
}
// --- cSkin -----------------------------------------------------------------
cSkin::cSkin(const char *Name, cTheme *Theme)
{
name = strdup(Name);
theme = Theme;
if (theme)
cThemes::Save(name, theme);
Skins.Add(this);
}
cSkin::~cSkin()
{
free(name);
}
// --- cSkins ----------------------------------------------------------------
cSkins Skins;
cSkins::cSkins(void)
{
displayMessage = NULL;
}
cSkins::~cSkins()
{
delete displayMessage;
}
bool cSkins::SetCurrent(const char *Name)
{
if (Name) {
for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
if (strcmp(Skin->Name(), Name) == 0) {
isyslog("setting current skin to \"%s\"", Name);
current = Skin;
return true;
}
}
}
current = First();
if (current)
isyslog("skin \"%s\" not available - using \"%s\" instead", Name, current->Name());
else
esyslog("ERROR: no skin available");
return current != NULL;
}
eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
{
if (!cThread::IsMainThread()) {
dsyslog("cSkins::Message() called from background thread - ignored! (Use cSkins::QueueMessage() instead)");
return kNone;
}
switch (Type) {
case mtInfo: isyslog("info: %s", s); break;
case mtWarning: isyslog("warning: %s", s); break;
case mtError: esyslog("ERROR: %s", s); break;
default: ;
}
if (!Current())
return kNone;
if (!cSkinDisplay::Current()) {
if (displayMessage)
delete displayMessage;
displayMessage = Current()->DisplayMessage();
}
cSkinDisplay::Current()->SetMessage(Type, s);
cSkinDisplay::Current()->Flush();
cStatus::MsgOsdStatusMessage(s);
eKeys k = kNone;
if (Type != mtStatus) {
k = Interface->Wait(Seconds);
if (displayMessage) {
delete displayMessage;
displayMessage = NULL;
cStatus::MsgOsdClear();
}
else {
cSkinDisplay::Current()->SetMessage(Type, NULL);
cStatus::MsgOsdStatusMessage(NULL);
}
}
else if (!s && displayMessage) {
delete displayMessage;
displayMessage = NULL;
cStatus::MsgOsdClear();
}
return k;
}
int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
if (Type == mtStatus) {
dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
return kNone;
}
if (isempty(s)) {
if (!cThread::IsMainThread()) {
queueMessageMutex.Lock();
for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
if (m->threadId == cThread::ThreadId() && m->state == 0)
m->state = 2; // done
}
queueMessageMutex.Unlock();
}
else
dsyslog("cSkins::QueueMessage() called with empty message from main thread - ignored!");
return kNone;
}
int k = kNone;
if (Timeout > 0) {
if (cThread::IsMainThread()) {
dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
return k;
}
cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
queueMessageMutex.Lock();
SkinQueuedMessages.Add(m);
m->mutex.Lock();
queueMessageMutex.Unlock();
if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
k = m->key;
else
k = -1; // timeout, nothing has been displayed
m->state = 2; // done
m->mutex.Unlock();
}
else {
queueMessageMutex.Lock();
// Check if there is a waiting message w/o timeout for this thread:
if (Timeout == -1) {
for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
if (m->threadId == cThread::ThreadId()) {
if (m->state == 0 && m->timeout == -1)
m->state = 2; // done
break;
}
}
}
// Add the new message:
SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
queueMessageMutex.Unlock();
}
return k;
}
void cSkins::ProcessQueuedMessages(void)
{
if (!cThread::IsMainThread()) {
dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
return;
}
cSkinQueuedMessage *msg = NULL;
// Get the first waiting message:
queueMessageMutex.Lock();
for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
if (m->state == 0) { // waiting
m->state = 1; // active
msg = m;
break;
}
}
queueMessageMutex.Unlock();
// Display the message:
if (msg) {
msg->mutex.Lock();
if (msg->state == 1) { // might have changed since we got it
msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
if (msg->timeout == 0)
msg->state = 2; // done
else
msg->condVar.Broadcast();
}
msg->mutex.Unlock();
}
// Remove done messages from the queue:
queueMessageMutex.Lock();
for (;;) {
cSkinQueuedMessage *m = SkinQueuedMessages.First();
if (m && m->state == 2) { // done
SkinQueuedMessages.Del(m);
}
else
break;
}
queueMessageMutex.Unlock();
}
void cSkins::Flush(void)
{
if (cSkinDisplay::Current())
cSkinDisplay::Current()->Flush();
}
void cSkins::Clear(void)
{
if (displayMessage) {
delete displayMessage;
displayMessage = NULL;
}
cList<cSkin>::Clear();
}