vdr-plugin-skindesigner/coreengine/viewelement.c
kamel5 afa9cb77a3 Add element timeShiftTimes to displayreplay
In displayreplay the tokens recstart, playbacktime and timeshiftrest
added to display start time, actual playback time and the rest of
the actual recording in timeshiftmode.
2021-02-15 14:00:35 +01:00

718 lines
21 KiB
C

#include "viewelement.h"
#include "../config.h"
/******************************************************************
* cViewElement
******************************************************************/
cViewElement::cViewElement(void) {
sdOsd = NULL;
id = -1;
dirty = true;
init = true;
drawn = false;
scrollingStarted = false;
blocked = false;
detached = false;
doAnimOut = false;
doStartAnim = true;
waitOnWakeup = true;
startAnimation = true;
restartAnimation = false;
globals = NULL;
tokenContainer = NULL;
attribs = new cViewElementAttribs((int)eViewElementAttribs::count);
clearAll = false;
detacher = NULL;
shifter = NULL;
fader = NULL;
}
cViewElement::cViewElement(const cViewElement &other) {
sdOsd = other.sdOsd;
id = other.id;
dirty = other.dirty;
init = other.init;
drawn = false;
scrollingStarted = false;
blocked = false;
detached = false;
doAnimOut = other.doAnimOut;
doStartAnim = other.doStartAnim;
waitOnWakeup = true;
startAnimation = true;
restartAnimation = false;
globals = other.globals;
container.Set(other.container.X(), other.container.Y(), other.container.Width(), other.container.Height());
tokenContainer = NULL;
attribs = new cViewElementAttribs(*other.attribs);
clearAll = false;
for (const cAreaNode *node = other.areaNodes.First(); node; node = other.areaNodes.Next(node)) {
if (cArea *a = dynamic_cast<cArea*>((cAreaNode*)node)) {
areaNodes.Add(new cArea(*a));
} else if (cAreaContainer *ac = dynamic_cast<cAreaContainer*>((cAreaNode*)node)) {
areaNodes.Add(new cAreaContainer(*ac));
}
}
detacher = NULL;
shifter = NULL;
fader = NULL;
}
cViewElement::~cViewElement(void) {
delete attribs;
delete detacher;
delete tokenContainer;
}
/******************************************************************
* Public Functions
******************************************************************/
cViewElement *cViewElement::CreateViewElement(const char *name, const char *viewname) {
cViewElement *e = NULL;
//common view elements
if (!strcmp(name, "background") && strcmp(viewname, "displayaudiotracks"))
e = new cViewElement();
else if (!strcmp(name, "datetime"))
e = new cVeDateTime();
else if (!strcmp(name, "time"))
e = new cVeTime();
else if (!strcmp(name, "message"))
e = new cVeMessage();
else if (!strcmp(name, "devices"))
e = new cVeDevices();
else if (!strcmp(name, "currentweather"))
e = new cVeCurrentWeather();
else if (!strcmp(name, "customtokens"))
e = new cVeCustomTokens();
//displaychannel viewelements
else if (!strcmp(name, "channelinfo"))
e = new cVeDcChannelInfo();
else if (!strcmp(name, "channelgroup"))
e = new cVeDcChannelGroup();
else if (!strcmp(name, "epginfo"))
e = new cVeDcEpgInfo();
else if (!strcmp(name, "progressbar") && !strcmp(viewname, "displaychannel"))
e = new cVeDcProgressBar();
else if (!strcmp(name, "statusinfo"))
e = new cVeDcStatusInfo();
else if (!strcmp(name, "audioinfo"))
e = new cVeDcAudioInfo();
else if (!strcmp(name, "screenresolution"))
e = new cVeDcScreenResolution();
else if (!strcmp(name, "signalquality"))
e = new cVeDcSignalQuality();
else if (!strcmp(name, "scrapercontent") && !strcmp(viewname, "displaychannel"))
e = new cVeDcScraperContent();
else if (!strcmp(name, "channelhints"))
e = new cVeDcChannelHints();
else if (!strcmp(name, "channeldetail"))
e = new cVeDcChannelDetail();
else if (!strcmp(name, "channellistback"))
e = new cViewElement();
else if (!strcmp(name, "grouplistback"))
e = new cViewElement();
else if (!strcmp(name, "groupchannellistback"))
e = new cViewElement();
else if (!strcmp(name, "channellistdetail"))
e = new cVeDcChannelListDetail();
else if (!strcmp(name, "groupchannellistdetail"))
e = new cVeDcGroupChannelListDetail();
else if (!strcmp(name, "ecminfo"))
e = new cVeDcEcmInfo();
//displaymenu viewelements
else if (!strcmp(name, "header") && strcmp(viewname, "displayaudiotracks"))
e = new cVeDmHeader();
else if (!strcmp(name, "sortmode"))
e = new cVeDmSortmode();
else if (!strcmp(name, "colorbuttons"))
e = new cVeDmColorbuttons();
else if (!strcmp(name, "scrollbar"))
e = new cVeDmScrollbar();
else if (!strcmp(name, "timers"))
e = new cVeDmTimers();
else if (!strcmp(name, "currentschedule"))
e = new cVeDmCurrentschedule();
else if (!strcmp(name, "discusage"))
e = new cVeDmDiscusage();
else if (!strcmp(name, "systemload"))
e = new cVeDmSystemload();
else if (!strcmp(name, "systemmemory"))
e = new cVeDmSystemmemory();
else if (!strcmp(name, "temperatures"))
e = new cVeDmTemperatures();
else if (!strcmp(name, "vdrstatistics"))
e = new cVeDmVdrstatistics();
else if (!strcmp(name, "lastrecordings"))
e = new cVeDmLastrecordings();
else if (!strcmp(name, "detailheaderepg"))
e = new cVeDmDetailheaderEpg();
else if (!strcmp(name, "detailheaderrec"))
e = new cVeDmDetailheaderRec();
else if (!strcmp(name, "detailheaderplugin"))
e = new cVeDmDetailheaderPlugin();
else if (!strcmp(name, "tablabels"))
e = new cVeDmTablabels();
//displayreplay viewelements
else if (!strcmp(name, "backgroundmodeonly"))
e = new cViewElement();
else if (!strcmp(name, "rectitle"))
e = new cVeDrRecTitle();
else if (!strcmp(name, "recinfo"))
e = new cVeDrRecInfo();
else if (!strcmp(name, "currenttime"))
e = new cVeDrCurrentTime();
else if (!strcmp(name, "totaltime"))
e = new cVeDrTotalTime();
else if (!strcmp(name, "timeshifttimes"))
e = new cVeDrTimeshiftTimes();
else if (!strcmp(name, "endtime"))
e = new cVeDrEndTime();
else if (!strcmp(name, "progressbar") && !strcmp(viewname, "displayreplay"))
e = new cVeDrProgressBar();
else if (!strcmp(name, "cutmarks"))
e = new cVeDrCutMarks();
else if (!strcmp(name, "controlicons"))
e = new cVeDrControlIcons();
else if (!strcmp(name, "controliconsmodeonly"))
e = new cVeDrControlIcons();
else if (!strcmp(name, "progressmodeonly"))
e = new cVeDrProgressModeonly();
else if (!strcmp(name, "jump"))
e = new cVeDrJump();
else if (!strcmp(name, "onpause"))
e = new cVeDrOnPause();
else if (!strcmp(name, "onpausemodeonly"))
e = new cVeDrOnPause();
else if (!strcmp(name, "scrapercontent") && !strcmp(viewname, "displayreplay"))
e = new cVeDrScraperContent();
//displayvolume viewelements
else if (!strcmp(name, "volume"))
e = new cVeVolume();
//displayvolume viewelements
else if (!strcmp(name, "background") && !strcmp(viewname, "displayaudiotracks"))
e = new cVeDtBackground();
else if (!strcmp(name, "header") && !strcmp(viewname, "displayaudiotracks"))
e = new cVeDtHeader();
//default
else {
dsyslog("skindesigner: unknown view element %s", name);
e = new cViewElement();
}
return e;
}
void cViewElement::SetGlobals(cGlobals *globals) {
this->globals = globals;
attribs->SetGlobals(globals);
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetGlobals(globals);
}
}
void cViewElement::SetTokenContainer(void) {
tokenContainer = new skindesignerapi::cTokenContainer();
InheritTokenContainer();
}
bool cViewElement::Detached(void) {
return detached;
}
void cViewElement::SetContainer(int x, int y, int width, int height) {
container.SetX(x);
container.SetY(y);
container.SetWidth(width);
container.SetHeight(height);
}
void cViewElement::SetAttributes(vector<stringpair> &attributes) {
attribs->Set(attributes);
}
void cViewElement::AddArea(cAreaNode *area) {
areaNodes.Add(area);
}
void cViewElement::SetAreaX(int x) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetX(x);
}
}
void cViewElement::SetAreaY(int y) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetY(y);
}
}
void cViewElement::SetAreaWidth(int width) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetWidth(width);
}
}
void cViewElement::SetAreaHeight(int height) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetHeight(height);
}
}
void cViewElement::SetPosition(int newX, int newY, int newWidth, int newHeight) {
cRect newPos(newX, newY, newWidth, newHeight);
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetViewPort(newPos);
}
}
void cViewElement::Cache(void) {
attribs->SetContainer(container.X(), container.Y(), container.Width(), container.Height());
attribs->Cache();
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetContainer(container.X(), container.Y(), container.Width(), container.Height());
node->Cache();
}
}
bool cViewElement::Execute(void) {
return attribs->DoExecute();
}
void cViewElement::Clear(bool forceClearBackground) {
if (scrollingStarted)
StopScrolling();
tokenContainer->Clear();
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->StopBlinkers();
sdOsd->Lock();
node->Clear(forceClearBackground);
sdOsd->Unlock();
}
dirty = false;
drawn = false;
scrollingStarted = false;
}
void cViewElement::Hide(void) {
StopAnimation();
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
sdOsd->Lock();
node->Hide();
sdOsd->Unlock();
}
init = true;
StopScrolling();
}
void cViewElement::Show(void) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
sdOsd->Lock();
node->Show();
sdOsd->Unlock();
}
}
void cViewElement::WakeUp(void) {
if (!detacher || !waitOnWakeup) {
return;
}
detacher->WakeUp();
}
void cViewElement::Close(void) {
StopAnimation();
StopScrolling();
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->StopBlinkers();
sdOsd->Lock();
node->Close();
sdOsd->Unlock();
}
dirty = true;
init = true;
startAnimation = true;
restartAnimation = false;
drawn = false;
scrollingStarted = false;
blocked = false;
}
void cViewElement::StopBlinking(void) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->StopBlinkers();
}
}
void cViewElement::Render(void) {
if (!dirty || blocked)
return;
if (attribs->DoDebug())
Debug();
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
//Check redraw of already scrolling list element
if (drawn && scrollingStarted && node->Scrolling()) {
if (DoScroll()) {
//current list element
continue;
} else {
//not current list element anymore
scrollingStarted = false;
}
}
sdOsd->Lock();
node->Clear();
sdOsd->Unlock();
if (!node->Execute())
continue;
if (node->BackgroundArea() && drawn)
continue;
sdOsd->Lock();
node->Render();
sdOsd->Unlock();
if (DoScroll() && node->Scrolling()) {
cArea *scrollArea = node->ScrollingArea();
if (scrollArea) {
scrollingStarted = true;
cScroller *scroller = new cScroller(scrollArea);
scrollers.push_back(scroller);
cView::AddAnimation(scroller);
}
}
}
dirty = false;
drawn = true;
if (startAnimation || restartAnimation) {
startAnimation = false;
restartAnimation = false;
StartAnimation();
}
}
void cViewElement::StopScrolling(bool deletePixmaps) {
for (list<cScroller*>::iterator it = scrollers.begin(); it != scrollers.end(); it++) {
cView::RemoveAnimation(*it);
}
scrollers.clear();
}
void cViewElement::ParseDetached(void) {
Parse(true);
}
void cViewElement::RenderDetached(void) {
blocked = false;
Render();
}
bool cViewElement::Shifting(void) {
if (attribs->ShiftTime() > 0) {
return true;
}
return false;
}
bool cViewElement::Fading(void) {
if (attribs->FadeTime() > 0) {
return true;
}
return false;
}
int cViewElement::FadeTime(void) {
return attribs->FadeTime();
}
int cViewElement::ShiftTime(void) {
return attribs->ShiftTime();
}
int cViewElement::ShiftMode(void) {
int mode = attribs->ShiftMode();
if (mode < 0) mode = 0;
return mode;
}
void cViewElement::ShiftPositions(cPoint *start, cPoint *end) {
cRect shiftbox = CoveredArea();
cPoint startPoint = ShiftStart(shiftbox);
start->Set(startPoint);
end->Set(shiftbox.X(), shiftbox.Y());
}
void cViewElement::StartAnimation(void) {
shifter = NULL;
fader = NULL;
if (ShiftTime() > 0) {
shifter = new cShifter((cShiftable*)this);
if (doAnimOut)
shifter->SetPersistent();
cView::AddAnimation(shifter, doStartAnim);
} else if (FadeTime() > 0) {
fader = new cFader((cFadable*)this);
if (doAnimOut)
fader->SetPersistent();
cView::AddAnimation(fader, doStartAnim);
}
}
void cViewElement::SetTransparency(int transparency, bool force) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
sdOsd->Lock();
node->SetTransparency(transparency);
sdOsd->Unlock();
}
}
void cViewElement::SetPosition(cPoint &position, cPoint &reference, bool force) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
sdOsd->Lock();
node->SetPosition(position, reference);
sdOsd->Unlock();
}
}
cRect cViewElement::CoveredArea(void) {
cRect unionArea;
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
unionArea.Combine(node->CoveringArea());
}
return unionArea;
}
void cViewElement::Flush(void) {
sdOsd->Flush();
}
bool cViewElement::Parse(bool forced) {
if (blocked && !forced) {
return false;
}
if (!Detached() || !init) {
return true;
}
delete detacher;
bool isAnimated = (FadeTime() > 0) || (ShiftTime() > 0);
detacher = new cDetacher((cDetachable*)this, waitOnWakeup, startAnimation && isAnimated);
detacher->Start();
startAnimation = false;
init = false;
blocked = true;
return false;
}
cFunction *cViewElement::GetFunction(const char *name) {
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
if (cFunction *f = node->GetFunction(name))
return f;
}
return NULL;
}
void cViewElement::Debug(bool full) {
esyslog("skindesigner: ---> viewElement %d", id);
tokenContainer->Debug();
esyslog("skindesigner: container %d %d %dx%d", container.X(), container.Y(), container.Width(), container.Height());
attribs->Debug();
if (!full)
return;
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->Debug(full);
}
}
/******************************************************************
* protected Functions
******************************************************************/
void cViewElement::InheritTokenContainer(void) {
tokenContainer->CreateContainers();
attribs->SetTokenContainer(tokenContainer);
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetTokenContainer(tokenContainer);
}
}
void cViewElement::InheritTokenContainerDeep(void) {
tokenContainer->CreateContainers();
attribs->SetTokenContainerDeep(tokenContainer);
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
node->SetTokenContainerDeep(tokenContainer);
}
}
cPoint cViewElement::ShiftStart(cRect &shiftbox) {
eShiftType type = (eShiftType)attribs->ShiftType();
cPoint start;
if (type == eShiftType::none) {
start = attribs->ShiftStartpoint();
} else if (type == eShiftType::left) {
start.SetX(-shiftbox.Width());
start.SetY(shiftbox.Y());
} else if (type == eShiftType::right) {
start.SetX(cOsd::OsdWidth());
start.SetY(shiftbox.Y());
} else if (type == eShiftType::top) {
start.SetX(shiftbox.X());
start.SetY(-shiftbox.Height());
} else if (type == eShiftType::bottom) {
start.SetX(shiftbox.X());
start.SetY(cOsd::OsdHeight());
}
return start;
}
void cViewElement::StopAnimation(void) {
delete detacher;
detacher = NULL;
if (shifter)
cView::RemoveAnimation(shifter);
if (fader)
cView::RemoveAnimation(fader);
}
/******************************************************************
* helper function (did not find any other common place)
******************************************************************/
bool RecordingIsHD(const cEvent* event) {
// detect HD from 'info'
bool isHD = false;
cComponents *Components = (cComponents *)event->Components();
if (Components) {
// detect HD (see also ETSI EN 300 468)
// Stream: 1 = MPEG2-Video, 2 = MPEG2 Audio, 3 = Untertitel, 4 = AC3-Audio, 5 = H.264-Video, 6 = HEAAC-Audio, 7 = DTS/DTS HD audio, 8 = SRM/CPCM data, 9 = HEVC Video, AC4 Audio
// Stream == Video(1|5): 01 = 05 = 4:3, 02 = 03 = 06 = 07 = 16:9, 04 = 08 = >16:9, 09 = 0D = HD 4:3, 0A = 0B = 0E = 0F = HD 16:9, 0C = 10 = HD >16:9
tComponent *Component;
int type = -1;
// #1: HVEC (stream content: 9)
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
if (Component) {
isHD = true; // HVEC is always HD, type 4|5|6|7 would be even UHD (see below dedicated detection function)
} else {
// #2: H.264 (stream content: 5)
Component = Components->GetComponent(0, 5, 0); // recording info: "X 5 <type>"
if (Component) {
type = Component->type;
} else {
// #3: MPEG2 (stream content: 1)
Component = Components->GetComponent(0, 1, 0); // recording info: "X 1 <type>"
if (Component) {
type = Component->type;
};
};
};
switch (type) {
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
case 0x0E:
case 0x0F:
case 0x10:
isHD = true;
};
};
return isHD;
};
bool RecordingIsUHD(const cEvent* event) {
// detect UHD from 'info'
bool isUHD = false;
cComponents *Components = (cComponents *)event->Components();
if (Components) {
// detect UHD (see also ETSI EN 300 468)
// Stream: 9 = HEVC Video, AC4 Audio
// Stream == Video(9): 00|01|02|03 = HD, 04|05|06|07 = UHD
tComponent *Component;
int type = -1;
// HVEC (stream content: 9)
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
if (Component) {
type = Component->type;
};
switch (type) {
case 0x04:
case 0x05:
case 0x06:
case 0x07:
isUHD = true;
};
};
return isUHD;
};
bool RecordingIsRadio(const cEvent* event, const double FramesPerSecond) {
// detect Radio from 'info'
bool isRadio = false;
bool hasAudio = false;
bool hasVideo = false;
cComponents *Components = (cComponents *)event->Components();
if (Components) {
// Stream: 1 = MPEG2-Video, 2 = MPEG2 Audio, 3 = Untertitel, 4 = AC3-Audio, 5 = H.264-Video, 6 = HEAAC-Audio, 7 = DTS/DTS HD audio, 8 = SRM/CPCM data, 9 = HEVC Video, AC4 Audio
tComponent *Component;
Component = Components->GetComponent(0, 2, 0); // recording info: "X 2 <type>"
if (Component) {
hasAudio = true;
};
Component = Components->GetComponent(0, 4, 0); // recording info: "X 4 <type>"
if (Component) {
hasAudio = true;
};
Component = Components->GetComponent(0, 6, 0); // recording info: "X 6 <type>"
if (Component) {
hasAudio = true;
};
Component = Components->GetComponent(0, 7, 0); // recording info: "X 7 <type>"
if (Component) {
hasAudio = true;
};
Component = Components->GetComponent(0, 1, 0); // recording info: "X 1 <type>"
if (Component) {
hasVideo = true;
};
Component = Components->GetComponent(0, 5, 0); // recording info: "X 5 <type>"
if (Component) {
hasVideo = true;
};
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
if (Component) {
hasVideo = true;
};
};
if ((hasAudio == true) && (hasVideo == false)) {
if (FramesPerSecond < 24) { // workaround for issue of missing "X 1" on some SD channels (e.g. RTL)
isRadio = true;
};
};
return isRadio;
};