summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Tusche <chr13@gmx.net>2009-06-06 23:17:58 +0200
committerThomas Günther <tom@toms-cafe.de>2009-06-06 23:17:58 +0200
commit2a4bf008cd0a14d53fefec0c34573aab12424373 (patch)
tree12ee1c3021b4965f095b5ea81ab3a60f01217449
parent60cd7db51838bb076312fd10e08bca0f4a005f82 (diff)
parent3ab2393b6932b34e7f0e69af7f843d1303104d79 (diff)
downloadvdr-plugin-text2skin-2a4bf008cd0a14d53fefec0c34573aab12424373.tar.gz
vdr-plugin-text2skin-2a4bf008cd0a14d53fefec0c34573aab12424373.tar.bz2
Added chr13-optimizations (thanks to Christian Tusche / closes #39)
- increased efficiency in drawing list items in the main menu - introduce relative Pos and Size of objects to given BasePos, BaseSize (used to draw list items) - increase skin file version to 1.1 - the position of list items is interpreted relative to the "list" container when file version >= 1.1 - when a position is specified for "block" elements, the position of all contained elements is interpreted relative to the container position - selective update of changed objects refresh can be controlled for individual objects by the attributes "refresh" and "changed" default behaviour is to redraw everything (compatible with old skins) - moved state tracking of marquee, blink, scroll from cText2SkinRender to cxObject - fixed use of Update.Lock() in render.h - new: dynamic width/height of objects - new: Option "bgColor" used for items "Text", "Marquee", and "Blink". - remember period to next timeout when doing a non-timeout refresh prevent occasional start/stop of marquee-text
-rw-r--r--HISTORY20
-rw-r--r--Makefile5
-rw-r--r--display.c167
-rw-r--r--loader.c5
-rw-r--r--render.c423
-rw-r--r--render.h52
-rw-r--r--screen.c13
-rw-r--r--screen.h3
-rw-r--r--text2skin.c2
-rw-r--r--xml/display.c3
-rw-r--r--xml/display.h3
-rw-r--r--xml/object.c193
-rw-r--r--xml/object.h64
-rw-r--r--xml/parser.c37
-rw-r--r--xml/skin.c31
-rw-r--r--xml/skin.h45
16 files changed, 779 insertions, 287 deletions
diff --git a/HISTORY b/HISTORY
index 6b203de..aad47bd 100644
--- a/HISTORY
+++ b/HISTORY
@@ -46,6 +46,26 @@ ____-__-__: Version 1.3
RecordingFilename, RecordingPriority, RecordingLifetime
- set EditableWidth. This is important for plugins like 'rotor' or
'extrecmenu'
+- Added chr13-optimizations (thanks to Christian Tusche / closes #39)
+ - increased efficiency in drawing list items in the main menu
+ - introduce relative Pos and Size of objects to given BasePos, BaseSize
+ (used to draw list items)
+ - increase skin file version to 1.1
+ - the position of list items is interpreted relative to the "list" container
+ when file version >= 1.1
+ - when a position is specified for "block" elements, the position of all
+ contained elements is interpreted relative to the container position
+ - selective update of changed objects
+ refresh can be controlled for individual objects by the attributes
+ "refresh" and "changed"
+ default behaviour is to redraw everything (compatible with old skins)
+ - moved state tracking of marquee, blink, scroll from cText2SkinRender
+ to cxObject
+ - fixed use of Update.Lock() in render.h
+ - new: dynamic width/height of objects
+ - new: Option "bgColor" used for items "Text", "Marquee", and "Blink".
+ - remember period to next timeout when doing a non-timeout refresh
+ prevent occasional start/stop of marquee-text
2009-06-01: Version 1.2
diff --git a/Makefile b/Makefile
index 873828d..d149cfb 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,8 @@
#BENCH=1
HAVE_IMAGEMAGICK=1
-#HAVE_IMLIB2=1 # not recommended
+#HAVE_IMLIB2=1
+# not recommended
# comment this out if you don't want to use FreeType font rendering
@@ -99,7 +100,7 @@ ifdef DEBUG
CXXFLAGS += -g -fno-inline
DEFINES += -DDEBUG
else
- CXXFLAGS += -O2 -g
+ CXXFLAGS += -O3
ifdef BENCH
DEFINES += -DBENCH
endif
diff --git a/display.c b/display.c
index 663fd45..4bcce4c 100644
--- a/display.c
+++ b/display.c
@@ -51,7 +51,7 @@ void cText2SkinDisplayChannel::SetChannel(const cChannel *Channel, int Number)
if (mChannel != Channel || mNumber != Number) {
mChannel = Channel;
mNumber = Number;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -67,7 +67,7 @@ void cText2SkinDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Fo
if (mPresent != Present || mFollowing != Following) {
mPresent = Present;
mFollowing = Following;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -84,7 +84,7 @@ void cText2SkinDisplayChannel::SetMessage(eMessageType Type, const char *Text)
if (mType != Type || mText != Text) {
mType = Type;
mText = Text;
- SetDirty();
+ SetDirty(cxRefresh::all);
}
UpdateUnlock();
}
@@ -108,7 +108,7 @@ void cText2SkinDisplayChannel::SetButtons(const char *Red, const char *Green, co
mButtonGreen = Green;
mButtonYellow = Yellow;
mButtonBlue = Blue;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -372,7 +372,7 @@ void cText2SkinDisplayVolume::SetVolume(int Current, int Total, bool Mute)
mCurrent = Current;
mTotal = Total;
mMute = Mute;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -422,7 +422,7 @@ void cText2SkinDisplayReplay::SetTitle(const char *Title)
if (Title == NULL) Title = "";
if (mTitle != Title) {
mTitle = Title;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -436,7 +436,7 @@ void cText2SkinDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
mPlay = Play;
mForward = Forward;
mSpeed = Speed;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -447,7 +447,7 @@ void cText2SkinDisplayReplay::SetProgress(int Current, int Total)
if (mCurrent != Current || mTotal != Total) {
mCurrent = Current;
mTotal = Total;
- // SetDirty(); TODO: let this cause a display update every frame?
+ // SetDirty(cxRefresh::update); TODO: let this cause a display update every frame?
}
UpdateUnlock();
}
@@ -456,7 +456,7 @@ void cText2SkinDisplayReplay::SetMarks(const cMarks *Marks)
{
UpdateLock();
mMarks = Marks;
- SetDirty();
+ SetDirty(cxRefresh::update);
UpdateUnlock();
}
@@ -466,7 +466,7 @@ void cText2SkinDisplayReplay::SetCurrent(const char *Current)
if (Current == NULL) Current = "";
if (mPosition != Current) {
mPosition = Current;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -477,7 +477,7 @@ void cText2SkinDisplayReplay::SetTotal(const char *Total)
if (Total == NULL) Total = "";
if (mDuration != Total) {
mDuration = Total;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -488,7 +488,7 @@ void cText2SkinDisplayReplay::SetJump(const char *Jump)
if (Jump == NULL) Jump = "";
if (mPrompt != Jump) {
mPrompt = Jump;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -500,7 +500,7 @@ void cText2SkinDisplayReplay::SetMessage(eMessageType Type, const char *Text)
if (mType != Type || mText != Text) {
mType = Type;
mText = Text;
- SetDirty();
+ SetDirty(cxRefresh::all);
}
UpdateUnlock();
}
@@ -519,7 +519,7 @@ void cText2SkinDisplayReplay::SetButtons(const char *Red, const char *Green, con
mButtonGreen = Green;
mButtonYellow = Yellow;
mButtonBlue = Blue;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -677,7 +677,7 @@ void cText2SkinDisplayMessage::SetMessage(eMessageType Type, const char *Text)
if (mType != Type || mText != Text) {
mType = Type;
mText = Text;
- SetDirty();
+ SetDirty(cxRefresh::all);
}
UpdateUnlock();
}
@@ -778,7 +778,7 @@ void cText2SkinDisplayMenu::Clear(void)
ExtRecordingDescription = "";
mText = "";
cText2SkinRender::Clear();
- SetDirty();
+ SetDirty(cxRefresh::all);
UpdateUnlock();
}
@@ -797,7 +797,7 @@ void cText2SkinDisplayMenu::SetTitle(const char *Title)
mUpdate.events = true;
mUpdate.resetMarquee = true;
mTitle = Title;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -821,7 +821,7 @@ void cText2SkinDisplayMenu::SetButtons(const char *Red, const char *Green, const
mButtonGreen = Green;
mButtonYellow = Yellow;
mButtonBlue = Blue;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -839,7 +839,7 @@ void cText2SkinDisplayMenu::SetMessage(eMessageType Type, const char *Text)
if (mMessageType != Type || mMessageText != Text) {
mMessageType = Type;
mMessageText = Text;
- SetDirty();
+ SetDirty(cxRefresh::all);
}
UpdateUnlock();
}
@@ -865,16 +865,22 @@ void cText2SkinDisplayMenu::SetItem(const char *Text, int Index, bool Current, b
if (mItems.size() <= (uint)Index) {
mItems.push_back(item);
- SetDirty();
+ mDirtyItems.push_back(Index);
+ SetDirty(cxRefresh::list);
}
else if (mItems[Index] != item) {
mItems[Index] = item;
- SetDirty();
+ // refresh only the changed items
+ mDirtyItems.push_back(Index);
+ SetDirty(cxRefresh::list);
}
if (Current && mCurrentItem != (uint)Index) {
+ // refresh only the changed items
+ mDirtyItems.push_back(mCurrentItem);
+ mDirtyItems.push_back(Index);
+ SetDirty(cxRefresh::list);
mCurrentItem = Index;
- SetDirty();
}
if (Current)
@@ -894,7 +900,7 @@ void cText2SkinDisplayMenu::SetEvent(const cEvent *Event)
mEvent = Event;
ExtPresentDescription = "";
if (mEvent != NULL)
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -912,7 +918,7 @@ void cText2SkinDisplayMenu::SetRecording(const cRecording *Recording)
mRecording = Recording;
ExtRecordingDescription = "";
if (mRecording != NULL)
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -928,7 +934,7 @@ void cText2SkinDisplayMenu::SetText(const char *Text, bool FixedFont)
if (Text == NULL) Text = "";
if (mText != Text) {
mText = Text;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
@@ -954,7 +960,7 @@ void cText2SkinDisplayMenu::Scroll(bool Up, bool Page)
UpdateLock();
cText2SkinRender::Scroll(Up, Page);
- SetDirty();
+ SetDirty(cxRefresh::scroll);
UpdateUnlock();
}
@@ -963,11 +969,8 @@ cxType cText2SkinDisplayMenu::GetTokenData(const txToken &Token)
switch (Token.Type) {
case tMenuItem:
case tMenuGroup:
- if (Token.Index < 0) return false;
case tMenuCurrent:
- if (Token.Index >= 0 && Token.Tab == -1) return false;
break;
-
default:
if (Token.Tab >= 0) return false;
break;
@@ -978,38 +981,61 @@ cxType cText2SkinDisplayMenu::GetTokenData(const txToken &Token)
return mTitle;
case tMenuItem:
- return mItems.size() > (uint)Token.Index && mItems[Token.Index].sel
- && mCurrentItem != (uint)Token.Index
- ? (cxType)mItems[Token.Index].tabs[Token.Tab]
- : (cxType)false;
+ if (Token.Index < 0)
+ return false;
+
+ if (mItems.size() <= (uint)Token.Index || !mItems[Token.Index].sel ||
+ mCurrentItem == (uint)Token.Index)
+ return false;
+
+ if (Token.Tab < 0)
+ return Token.Attrib.Type == aNumber
+ ? (cxType)mItems[Token.Index].tabs[Token.Attrib.Number]
+ : (cxType)mItems[Token.Index].text;
+
+ return (cxType)mItems[Token.Index].tabs[Token.Tab];
case tIsMenuItem:
return mItems.size() > (uint)Token.Index && mItems[Token.Index].sel
&& mCurrentItem != (uint)Token.Index;
case tMenuCurrent:
- if (Token.Index < 0) {
- if (mItems.size() > mCurrentItem)
- return Token.Attrib.Type == aNumber
- ? (cxType)mItems[mCurrentItem].tabs[Token.Attrib.Number]
- : (cxType)mItems[mCurrentItem].text;
- else
- return false;
- }
+ if (mItems.size() <= mCurrentItem)
+ return false;
- return mItems.size() > (uint)Token.Index && mItems[Token.Index].sel
- && mCurrentItem == (uint)Token.Index
- ? (cxType)mItems[Token.Index].tabs[Token.Tab]
- : (cxType)false;
+ if (Token.Index < 0)
+ return Token.Attrib.Type == aNumber
+ ? (cxType)mItems[mCurrentItem].tabs[Token.Attrib.Number]
+ : (cxType)mItems[mCurrentItem].text;
+
+ if (mItems.size() <= (uint)Token.Index || !mItems[Token.Index].sel ||
+ mCurrentItem != (uint)Token.Index)
+ return false;
+
+ if (Token.Tab < 0)
+ return Token.Attrib.Type == aNumber
+ ? (cxType)mItems[Token.Index].tabs[Token.Attrib.Number]
+ : (cxType)mItems[Token.Index].text;
+
+ return (cxType)mItems[Token.Index].tabs[Token.Tab];
case tIsMenuCurrent:
return mItems.size() > (uint)Token.Index && mItems[Token.Index].sel
&& mCurrentItem == (uint)Token.Index;
case tMenuGroup:
- return mItems.size() > (uint)Token.Index && !mItems[Token.Index].sel
- ? (cxType)mItems[Token.Index].tabs[Token.Tab]
- : (cxType)false;
+ if (Token.Index < 0)
+ return false;
+
+ if (mItems.size() <= (uint)Token.Index || mItems[Token.Index].sel)
+ return false;
+
+ if (Token.Tab < 0)
+ return Token.Attrib.Type == aNumber
+ ? (cxType)mItems[Token.Index].tabs[Token.Attrib.Number]
+ : (cxType)mItems[Token.Index].text;
+
+ return (cxType)mItems[Token.Index].tabs[Token.Tab];
case tIsMenuGroup:
return mItems.size() > (uint)Token.Index && !mItems[Token.Index].sel;
@@ -1395,8 +1421,11 @@ void cText2SkinDisplayTracks::SetTrack(int Index, const char * const *Tracks)
UpdateLock();
Dprintf("SetTrack: %d (%s here, %s in array)\n", Index, Tracks[Index], mItems[Index].c_str());
if (mCurrentItem != (uint)Index) {
+ // refresh only the changed items
+ mDirtyItems.push_back(mCurrentItem);
+ mDirtyItems.push_back(Index);
mCurrentItem = Index;
- SetDirty();
+ SetDirty(cxRefresh::list);
}
UpdateUnlock();
}
@@ -1406,24 +1435,15 @@ void cText2SkinDisplayTracks::SetAudioChannel(int AudioChannel)
UpdateLock();
if (mAudioChannel != AudioChannel) {
mAudioChannel = AudioChannel;
- SetDirty();
+ SetDirty(cxRefresh::update);
}
UpdateUnlock();
}
cxType cText2SkinDisplayTracks::GetTokenData(const txToken &Token)
{
- switch (Token.Type) {
- case tMenuItem:
- if (Token.Index < 0) return false;
- case tMenuCurrent:
- if (Token.Index >= 0 && Token.Tab == -1) return false;
- break;
-
- default:
- if (Token.Tab >= 0) return false;
- break;
- }
+ if (Token.Tab >= 0)
+ return false;
int index = Token.Index;
if (index >= 0 && mCurrentItem >= (uint)mMaxItems) {
@@ -1436,17 +1456,28 @@ cxType cText2SkinDisplayTracks::GetTokenData(const txToken &Token)
return mTitle;
case tMenuItem:
- return mItems.size() > (uint)index && mCurrentItem != (uint)index
- ? (cxType)mItems[index]
- : (cxType)false;
-
+ if (index < 0)
+ return false;
+
+ if (mItems.size() <= (uint)index || mCurrentItem == (uint)index)
+ return false;
+
+ return (cxType)mItems[index];
+
case tIsMenuItem:
return mItems.size() > (uint)index && mCurrentItem != (uint)index;
case tMenuCurrent:
- return mItems.size() > (uint)index && mCurrentItem == (uint)index
- ? (cxType)mItems[index]
- : (cxType)false;
+ if (mItems.size() <= mCurrentItem)
+ return false;
+
+ if (index < 0)
+ return (cxType)mItems[mCurrentItem];
+
+ if (mItems.size() <= (uint)index || mCurrentItem != (uint)index)
+ return false;
+
+ return (cxType)mItems[index];
case tIsMenuCurrent:
return mItems.size() > (uint)index && mCurrentItem == (uint)index;
diff --git a/loader.c b/loader.c
index fb98533..c8888c5 100644
--- a/loader.c
+++ b/loader.c
@@ -56,11 +56,12 @@ void cText2SkinLoader::Load(const char *Skin) {
cxSkin *skin = xmlParse(Skin, skinfile, translations, theme);
if (skin) {
- if (skin->Version() == cText2SkinPlugin::SkinVersion()) {
+ if (skin->Version() <= cText2SkinPlugin::SkinVersion()) {
new cText2SkinLoader(skin, translations, theme, Skin, skin->Title());
return;
} else
- esyslog("ERROR: text2skin: Skin is version %s, expecting %s", skin->Version().c_str(),
+ esyslog("ERROR: text2skin: Skin is version %i,%i, expecting <= %s",
+ skin->Version().Major(), skin->Version().Minor(),
cText2SkinPlugin::SkinVersion());
} else
esyslog("ERROR: error in skin file");
diff --git a/render.c b/render.c
index 1c09863..65b0ce7 100644
--- a/render.c
+++ b/render.c
@@ -29,7 +29,7 @@ cText2SkinRender::cText2SkinRender(cText2SkinLoader *Loader, cxDisplay::eType Di
mScreen(NULL),
mScroller(NULL),
mBasePath(BasePath),
- mDirty(true),
+ mDirty(cxRefresh::all),
mFallback(NULL),
mActive(false),
mDoUpdate(),
@@ -37,7 +37,10 @@ cText2SkinRender::cText2SkinRender(cText2SkinLoader *Loader, cxDisplay::eType Di
mStarted(),
mUpdateIn(0),
mNow(0),
- mBaseSize()
+ mFirst(true),
+ mBaseSize(),
+ mTabScale(1.0),
+ mTabScaleSet(false)
{
if (mDisplay == NULL) {
esyslog("ERROR: text2skin: display for %s missing", cxDisplay::GetType(Display).c_str());
@@ -127,17 +130,29 @@ cText2SkinRender::~cText2SkinRender()
void cText2SkinRender::Action(void)
{
+ bool to = true;
+ uint start_time = time_ms();
mActive = true;
UpdateLock();
mStarted.Broadcast();
while (mActive) {
- if (mUpdateIn) mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn);
+ to = true;
+ start_time = mNow;
+
+ if (mUpdateIn) to = mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn);
else mDoUpdate.Wait(mDoUpdateMutex);
if (!mActive) break; // fall out if thread to be stopped
-
- mUpdateIn = 0; // has to be re-set within Update();
mNow = time_ms();
+
+ if (mUpdateIn) {
+ if (!to || mNow >= start_time + mUpdateIn) {
+ SetDirty(cxRefresh::timeout);
+ mUpdateIn = 0; // has to be re-set within Update();
+ } else
+ mUpdateIn -= mNow - start_time;
+ }
+
Update();
}
UpdateUnlock();
@@ -145,203 +160,155 @@ void cText2SkinRender::Action(void)
void cText2SkinRender::Update(void)
{
- //DStartBench(malen);
- //DStartBench(ges);
Dbench(update);
+#ifdef BENCH
+ fprintf(stderr, "mDirty = 0x%04x\n", mDirty);
+#endif
+ if (mFirst) {
+ mDirty = (1<<cxRefresh::all);
+ mFirst = false;
+ }
+ else if (mDirty & (1<<cxRefresh::all))
+ // we need a complete redraw anyway, that is enough
+ mDirty = 1 << cxRefresh::all;
for (uint i = 0; i < mDisplay->Objects(); ++i)
DrawObject(mDisplay->GetObject(i));
- //DShowBench("---\t", malen);
- //DStartBench(flushen);
+ mDirty = 0;
+ while (mDirtyItems.size() > 0)
+ mDirtyItems.pop_back();
+
Dbench(flush);
mScreen->Flush();
Ddiff("flush only", flush);
Ddiff("complete flush", update);
- //DShowBench("===\t", flushen);
- //DShowBench("=== ges\t", ges);
//printf("====\t%d\n", mDisplay->Objects());
}
-void cText2SkinRender::DrawObject(const cxObject *Object)
+void cText2SkinRender::DrawObject(cxObject *Object,
+ const txPoint &BaseOffset /*=txPoint(-1,-1)*/,
+ const txSize &BaseSize /*=txSize(-1,-1)*/,
+ const txSize &VirtSize /*=txSize(-1,-1)*/,
+ int ListItem /*=-1*/,
+ bool ForceUpdate /*=false*/)
{
- if (Object->Condition() != NULL && !Object->Condition()->Evaluate())
+ if (!Object->mRefresh.Dirty(mDirty, mUpdateIn, ForceUpdate, mNow) ||
+ (Object->Condition() != NULL && !Object->Condition()->Evaluate()))
return;
+ txPoint pos;
+ txSize size;
+
+ pos = Object->Pos(BaseOffset, BaseSize, VirtSize);
+
+ if (ListItem >= 0 && !mSkin->Version().Require(1,1))
+ // Object is part of al list
+ // Calculate offset of list item relative to the list offset
+ size = Object->Size();
+ else
+ size = Object->Size(BaseOffset, BaseSize, VirtSize);
+
switch (Object->Type()) {
case cxObject::image:
- DrawImage(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg(), Object->Mask(),
+ DrawImage(pos, size, Object->Bg(), Object->Fg(), Object->Mask(),
Object->Alpha(), Object->Colors(), Object->Path());
break;
case cxObject::text:
- DrawText(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
- Object->Align());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawText(pos, size, Object->Fg(), Object->Bg(), Object->Text(), Object->Font(),
+ Object->Align());
break;
case cxObject::marquee:
- DrawMarquee(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
- Object->Align(), Object->Delay(), Object->Index());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawMarquee(pos, size, Object->Fg(), Object->Bg(), Object->Text(), Object->Font(),
+ Object->Align(), Object->Delay(), Object->State());
break;
case cxObject::blink:
- DrawBlink(Object->Pos(), Object->Size(), Object->Fg(), Object->Bg(), Object->Text(),
- Object->Font(), Object->Align(), Object->Delay(), Object->Index());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawBlink(pos, size, Object->Fg(), Object->Bg(), Object->Bl(), Object->Text(),
+ Object->Font(), Object->Align(), Object->Delay(),
+ Object->State());
break;
case cxObject::rectangle:
- DrawRectangle(Object->Pos(), Object->Size(), Object->Fg());
+ DrawRectangle(pos, size, Object->Fg());
break;
case cxObject::ellipse:
- DrawEllipse(Object->Pos(), Object->Size(), Object->Fg(), Object->Arc());
+ DrawEllipse(pos, size, Object->Fg(), Object->Arc());
break;
case cxObject::slope:
- DrawSlope(Object->Pos(), Object->Size(), Object->Fg(), Object->Arc());
+ DrawSlope(pos, size, Object->Fg(), Object->Arc());
break;
case cxObject::progress:
- DrawProgressbar(Object->Pos(), Object->Size(), Object->Current(), Object->Total(),
- Object->Bg(), Object->Fg(), Object->Keep(), Object->Mark(),
+ DrawProgressbar(pos, size, Object->Current(), Object->Total(),
+ Object->Bg(), Object->Fg(), Object->Keep(), Object->Mark(),
Object->Active(), GetMarks());
break;
case cxObject::scrolltext:
- DrawScrolltext(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
+ DrawScrolltext(pos, size, Object->Fg(), Object->Text(), Object->Font(),
Object->Align());
break;
case cxObject::scrollbar:
- DrawScrollbar(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg());
+ DrawScrollbar(pos, size, Object->Bg(), Object->Fg());
case cxObject::block:
for (uint i = 0; i < Object->Objects(); ++i)
- DrawObject(Object->GetObject(i));
+ DrawObject(Object->GetObject(i), pos, size, Object->mVirtSize, ListItem,
+ ListItem >= 0 ? true : Object->mRefresh.Full());
break;
case cxObject::list: {
- const cxObject *item = Object->GetObject(0);
+ cxObject *item = Object->GetObject(0);
if (item && item->Type() == cxObject::item) {
- txSize areasize = Object->Size();
- uint itemheight = item->Size().h;
- uint maxitems = areasize.h / itemheight;
- uint yoffset = 0;
- bool initialEditableWidthSet = false;
-
+ txSize itemsize = item->Size(pos, size);
+ txPoint itempos = pos;
+ itemsize.w = size.w;
+ uint maxitems = size.h / itemsize.h;
mMenuScrollbar.maxItems = maxitems;
SetMaxItems(maxitems); //Dprintf("setmaxitems %d\n", maxitems);
- for (uint i = 0; i < maxitems; ++i, yoffset += itemheight) {
+ uint index = 0;
+ bool partial = false;
+
+ // is only a partial update needed?
+ if (!Object->mRefresh.Full() &&
+ !(Object->mRefresh.Type() & mDirty & ~(1<<cxRefresh::list))) {
+ maxitems = mDirtyItems.size();
+ partial = true;
+ }
+
+ // draw list items
+ for (uint i = 0; i < maxitems; ++i) {
+ if (!HasTabText(i, -1))
+ continue;
+
+ Dbench(item);
+ if (partial)
+ index = mDirtyItems[i];
+ else
+ index = i;
+
+ itempos.y = pos.y + index * itemsize.h;
for (uint j = 1; j < Object->Objects(); ++j) {
- const cxObject *o = Object->GetObject(j);
- int maxtabs = 1;
-
- if (o->Display()->Type() == cxDisplay::menu)
- maxtabs = cSkinDisplayMenu::MaxTabs;
-
- for (int t = -1; t < maxtabs; ++t) {
- if (!HasTabText(i, t))
- continue;
-
- int thistab = GetTab(t);
- int nexttab = GetTab(t + 1);
-
- cxObject obj(*o);
- obj.SetListIndex(i, t);
- if (obj.Condition() != NULL && !obj.Condition()->Evaluate())
- continue;
-
- obj.mPos1.x += Object->mPos1.x + (t >= 0 ? thistab : 0);
- obj.mPos1.y += Object->mPos1.y + yoffset;
-
- // get end position
- if (t >= 0 && nexttab > 0) {
- // there is a "next tab".. see if it contains text
- int n = t + 1;
- while (n < cSkinDisplayMenu::MaxTabs && !HasTabText(i, n))
- ++n;
- nexttab = GetTab(n);
- }
-
- // set initial EditableWidth
- // this is for plugins like 'extrecmenu' and 'rotor'
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && !initialEditableWidthSet) {
- initialEditableWidthSet = true;
- SetEditableWidth(obj.Size().w);
- }
-
- if (t >= 0 && nexttab > 0 && nexttab < obj.mPos1.x + obj.Size().w - 1)
- // there is a "next tab" with text
- obj.mPos2.x = Object->mPos1.x + o->mPos1.x + nexttab;
- else {
- // there is no "next tab", use the rightmost edge
- obj.mPos2.x += Object->mPos1.x;
- /* not used anymore due to change to fontOsd
- but could be usefull if someone uses a differnt font
-
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1) {
- // VDR assumes, that the font in the menu is fontOsd,
- // so the EditableWidth is not necessarily correct
- // for TTF
- const cFont *defFont = cFont::GetFont(fontOsd);
- const char *dummy = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
- int editableWidth = obj.Size().w;
- if (defFont != obj.Font())
- editableWidth = (int)(editableWidth * defFont->Width(dummy) / (1.1 * obj.Font()->Width(dummy)));
- SetEditableWidth(editableWidth);
- } */
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1)
- SetEditableWidth(obj.Size().w);
- }
-
- obj.mPos2.y += Object->mPos1.y + yoffset;
-
- std::string text = obj.Text();
- bool isprogress = false;
- if (text.length() > 5
- && text[0] == '[' && text[text.length() - 1] == ']') {
- const char *p = text.c_str() + 1;
- isprogress = true;
- for (; *p != ']'; ++p) {
- if (*p != ' ' && *p != '|') {
- isprogress = false;
- break;
- }
- }
- }
-
- if (isprogress) {
- //Dprintf("detected progress bar tab\n");
- if (obj.Condition() == NULL || obj.Condition()->Evaluate()) {
- int total = text.length() - 2;
- int current = 0;
- const char *p = text.c_str() + 1;
- while (*p == '|')
- (++current, ++p);
-
- txPoint pos = obj.Pos();
- txSize size = obj.Size();
-
- DrawRectangle(txPoint(pos.x, pos.y + 4),
- txSize(size.w, 2), obj.Fg());
- DrawRectangle(txPoint(pos.x, pos.y + 4),
- txSize(2, size.h - 8), obj.Fg());
- DrawRectangle(txPoint(pos.x, pos.y + size.h - 6),
- txSize(size.w, 2), obj.Fg());
- DrawRectangle(txPoint(pos.x + size.w - 2, pos.y + 4),
- txSize(2, size.h - 8), obj.Fg());
-
- pos.x += 4;
- pos.y += 8;
- size.w -= 8;
- size.h -= 16;
- DrawProgressbar(pos, size, current, total, obj.Bg(),
- obj.Fg(), NULL, NULL, NULL, NULL);
- }
- } else
- DrawObject(&obj);
- }
+ item = Object->GetObject(j);
+ item->SetListIndex(index, -1);
+ DrawObject(item, itempos, itemsize, Object->mVirtSize, index, true);
}
+ Ddiff("draw item", item);
}
}
}
@@ -354,6 +321,133 @@ void cText2SkinRender::DrawObject(const cxObject *Object)
}
}
+void cText2SkinRender::DrawItemText(cxObject *Object, int i, const txPoint &ListOffset, const txSize &ListSize)
+{
+ bool initialEditableWidthSet = false;
+ int maxtabs = cSkinDisplayMenu::MaxTabs;
+ txPoint Pos = ListOffset;
+ txSize BaseSize = Object->Size(ListOffset, ListSize);
+ txSize Size = BaseSize;
+
+ if (!mTabScaleSet) {
+ // VDR assumes, that the font in the menu is fontOsd,
+ // so the tab width is not necessarily correct
+ // for TTF
+ const cFont *defFont = cFont::GetFont(fontOsd);
+ const char *dummy = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
+ //if (defFont != Object->Font())
+ mTabScale = 1.08 * (float)Object->Font()->Width(dummy) / (float)defFont->Width(dummy);
+ mTabScaleSet = true;
+ }
+
+ // loop over tabs
+ for (int t = 0; t < maxtabs; ++t) {
+ if (!HasTabText(i, t))
+ continue;
+
+ int thistab = (int)(mTabScale * GetTab(t));
+ int nexttab = (int)(mTabScale * GetTab(t + 1));
+
+ Object->SetListIndex(i, t);
+ //if (Object.Condition() != NULL && !Object.Condition()->Evaluate())
+ // continue;
+
+ // set initial EditableWidth
+ // this is for plugins like 'extrecmenu' and 'rotor'
+ if (!initialEditableWidthSet) {
+ initialEditableWidthSet = true;
+ SetEditableWidth((int)(Size.w / mTabScale));
+ }
+
+ // Start position of the tab
+ Pos.x = ListOffset.x + (t >= 0 ? thistab : 0);
+
+ // get end position
+ if (t >= 0 && nexttab > 0) {
+ // there is a "next tab".. see if it contains text
+ int n = t + 1;
+ while (n < cSkinDisplayMenu::MaxTabs && !HasTabText(i, n))
+ ++n;
+ nexttab = (int)(mTabScale * GetTab(n));
+ }
+
+ if (t >= 0 && nexttab > 0 && nexttab < BaseSize.w - 1)
+ // there is a "next tab" with text
+ Size.w = nexttab - thistab;
+ else {
+ // there is no "next tab", use the rightmost edge
+ Size.w = BaseSize.w - thistab;
+ if (t == 1)
+ SetEditableWidth((int)(Size.w / mTabScale));
+ }
+
+ // Does the current tab contain a text-progress bar?
+ std::string text = Object->Text();
+ bool isprogress = false;
+ if (text.length() > 5 && text[0] == '[' && text[text.length() - 1] == ']') {
+ const char *p = text.c_str() + 1;
+ isprogress = true;
+ for (; *p != ']'; ++p) {
+ if (*p != ' ' && *p != '|') {
+ isprogress = false;
+ break;
+ }
+ }
+ }
+
+ if (isprogress) {
+ //Dprintf("detected progress bar tab\n");
+ int total = text.length() - 2;
+ int current = 0;
+ const char *p = text.c_str() + 1;
+ while (*p == '|')
+ (++current, ++p);
+
+ txPoint prog_pos = Pos;
+ txSize prog_size = Size;
+
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + 4),
+ txSize(prog_size.w, 2), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + 4),
+ txSize(2, prog_size.h - 8), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + prog_size.h - 6),
+ txSize(prog_size.w, 2), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x + prog_size.w - 2, prog_pos.y + 4),
+ txSize(2, prog_size.h - 8), Object->Fg());
+
+ prog_pos.x += 4;
+ prog_pos.y += 8;
+ prog_size.w -= 8;
+ prog_size.h -= 16;
+ DrawProgressbar(prog_pos, prog_size, current, total, Object->Bg(),
+ Object->Fg(), NULL, NULL, NULL, NULL);
+ }
+ else {
+ switch (Object->Type()) {
+ case cxObject::text:
+ DrawText(Pos, Size, Object->Fg(), Object->Bg(), Object->Text(),
+ Object->Font(), Object->Align());
+ break;
+
+ case cxObject::marquee:
+ DrawMarquee(Pos, Size, Object->Fg(), Object->Bg(), Object->Text(),
+ Object->Font(), Object->Align(), Object->Delay(),
+ Object->State());
+ break;
+
+ case cxObject::blink:
+ DrawBlink(Pos, Size, Object->Fg(), Object->Bg(), Object->Bl(),
+ Object->Text(), Object->Font(), Object->Align(),
+ Object->Delay(), Object->State());
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+}
+
void cText2SkinRender::DrawImage(const txPoint &Pos, const txSize &Size, const tColor *Bg,
const tColor *Fg, const tColor *Mask, int Alpha, int Colors,
const std::string &Path)
@@ -370,23 +464,25 @@ void cText2SkinRender::DrawImage(const txPoint &Pos, const txSize &Size, const t
}
}
-void cText2SkinRender::DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const std::string &Text, const cFont *Font, int Align)
+void cText2SkinRender::DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const std::string &Text, const cFont *Font, int Align)
{
//Dprintf("trying to draw text %s to %d,%d size %d,%d, color %x\n", Text.c_str(), Pos.x, Pos.y,
// Size.w, Size.h, Fg ? *Fg : 0);
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
mScreen->DrawText(Pos.x, Pos.y, Text.c_str(), Fg ? *Fg : 0, 0, Font, Size.w, Size.h, Align);
}
-void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const std::string &Text, const cFont *Font, int Align,
- uint Delay, uint Index)
+void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const std::string &Text, const cFont *Font,
+ int Align, uint Delay, txState &state)
{
bool scrolling = Font->Width(Text.c_str()) > Size.w;
- tState &state = mStates[Index];
if (state.text != Text) {
- state = tState();
+ state = txState();
state.text = Text;
}
@@ -432,22 +528,23 @@ void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const
}
//Dprintf("drawMarquee text = %s, state.text = %s, offset = %d, index = %d, scrolling = %d, mUpdatteIn = %d, nexttime = %d, delay = %d\n",
// Text.c_str(), state.text.c_str(), state.offset, Index, scrolling, mUpdateIn, state.nexttime, Delay);
-
+
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
mScreen->DrawText(Pos.x, Pos.y, Text.c_str() + state.offset, Fg ? *Fg : 0, clrTransparent, Font,
Size.w, Size.h, Align);
}
-
-void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const tColor *Bg, const std::string &Text, const cFont *Font,
- int Align, uint Delay, uint Index)
+
+void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const tColor *Bl, const std::string &Text,
+ const cFont *Font, int Align, uint Delay, txState &state)
{
- tState &state = mStates[Index];
if (state.text != Text) {
- state = tState();
+ state = txState();
state.text = Text;
}
- Dprintf("drawBlink index = %d, state.text = %s, offset = %d\n", Index, state.text.c_str(),
- state.offset);
+ Dprintf("drawBlink state.text = %s, offset = %d\n", state.text.c_str(), state.offset);
if (state.nexttime == 0 || mNow >= state.nexttime) {
state.nexttime = mNow + Delay;
@@ -458,7 +555,11 @@ void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const t
if (mUpdateIn == 0 || updatein < mUpdateIn)
mUpdateIn = updatein;
- const tColor *col = state.offset == 0 ? Fg : Bg;
+ const tColor *col = state.offset == 0 ? Fg : Bl;
+
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
if (col)
mScreen->DrawText(Pos.x, Pos.y, Text.c_str(), *col, clrTransparent, Font, Size.w, Size.h,
Align);
diff --git a/render.h b/render.h
index 7f257a1..e181719 100644
--- a/render.h
+++ b/render.h
@@ -27,6 +27,7 @@ class cText2SkinRender: public cThread {
friend class cText2SkinDisplayReplay;
friend class cText2SkinDisplayMessage;
friend class cText2SkinDisplayMenu;
+ friend class cText2SkinDisplayTracks;
friend class cText2SkinStatus;
@@ -47,7 +48,8 @@ private:
tTokenCache mTokenCache;
std::string mBasePath;
- bool mDirty;
+ uint mDirty; // bit mask of required updates - set by SetDirty()
+ std::vector<int> mDirtyItems;
uint mMaxItems;
cSkin *mFallback;
@@ -59,23 +61,15 @@ private:
cCondVar mStarted;
uint mUpdateIn;
uint mNow; // timestamp to calculate update timings
+ bool mFirst; // First drawing of the display -> draw everything
// coordinate transformation
txSize mBaseSize;
-
- // state information for marquee, blink, scroll
- struct tState {
- bool scrolling;
- int offset;
- int direction;
- uint nexttime;
- std::string text;
-
- tState(void): scrolling(false), offset(0), direction(1), nexttime(0) {}
- };
- typedef std::map<uint,tState> tStates;
- tStates mStates;
-
+
+ // scalefactor for tabs in the menu list
+ float mTabScale;
+ bool mTabScaleSet;
+
protected:
// Update thread
void UpdateLock(void) { mDoUpdateMutex.Lock(); }
@@ -83,17 +77,22 @@ protected:
virtual void Action(void);
// Drawing operations
- void DrawObject(const cxObject *Object);
+ void DrawObject(cxObject *Object, const txPoint &BaseOffset = txPoint(-1, -1),
+ const txSize &BaseSize = txSize(-1, -1), const txSize &VirtSize = txSize(-1, -1),
+ int ListItem = -1, bool ForceUpdate = false);
+ void DrawItemText(cxObject *o, int i, const txPoint &ListOffset, const txSize &ListSize);
+
void DrawBackground(const txPoint &Pos, const txSize &Size, const tColor *Bg, const tColor *Fg,
int Alpha, const std::string &Path);
void DrawImage(const txPoint &Pos, const txSize &Size, const tColor *Bg, const tColor *Fg,
const tColor *Mask, int Alpha, int Colors, const std::string &Path);
- void DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg, const std::string &Text,
- const cFont *Font, int Align);
- void DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const std::string &Text, const cFont *Font, int Align, uint Delay, uint Index);
+ void DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg, const tColor *Bg,
+ const std::string &Text, const cFont *Font, int Align);
+ void DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg, const tColor *Bg,
+ const std::string &Text, const cFont *Font, int Align, uint Delay, txState &state);
void DrawBlink(const txPoint &Pos, const txSize &Size, const tColor *Fg, const tColor *Bg,
- const std::string &Text, const cFont *Font, int Align, uint Delay, uint Index);
+ const tColor *Bl, const std::string &Text, const cFont *Font, int Align,
+ uint Delay, txState &state);
void DrawRectangle(const txPoint &Pos, const txSize &Size, const tColor *Fg);
void DrawEllipse(const txPoint &Pos, const txSize &Size, const tColor *Fg, int Arc);
void DrawSlope(const txPoint &Pos, const txSize &Size, const tColor *Fg, int Arc);
@@ -121,7 +120,7 @@ protected:
// functions for display renderer to control behaviour
void Flush(bool Force = false);
- void SetDirty(void) { mDirty = true; }
+ void SetDirty(cxRefresh::eRefreshType type = cxRefresh::all) { mDirty |= 1<<type; }
void Scroll(bool Up, bool Page) { if (mScroller != NULL) mScroller->Scroll(Up, Page); }
void Clear(void) { DELETENULL(mScroller); }
cSkin *Fallback(void) const { return mFallback; }
@@ -168,14 +167,15 @@ public:
inline void cText2SkinRender::Flush(bool Force)
{
- if (mDirty || Force) {
- mTokenCache.clear();
+ if (Force)
+ // do a full redraw
+ mDirty = (1 << cxRefresh::all);
+ if (mDirty > 0) {
UpdateLock();
+ mTokenCache.clear();
mDoUpdate.Broadcast();
UpdateUnlock();
-
- mDirty = false;
}
}
diff --git a/screen.c b/screen.c
index 3412da4..f86447f 100644
--- a/screen.c
+++ b/screen.c
@@ -67,7 +67,10 @@ void cText2SkinScreen::DrawBitmap(int x, int y, const cBitmap &Bitmap, const tCo
DrawBitmapOverlay(*mRegions[i], x, y, (cBitmap&)Bitmap, ColorMask);
//mRegions[i]->DrawBitmap(x, y, Bitmap);
#else
- mOsd->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg);
+ // mOsd->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg);
+ cBitmap *bm = NULL;
+ for (int i = 0; (bm = mOsd->GetBitmap(i)) != NULL; ++i)
+ DrawBitmapOverlay(*bm, x, y, (cBitmap&)Bitmap, ColorMask);
#endif
}
@@ -122,6 +125,14 @@ void cText2SkinScreen::Flush(void)
mOsd->DrawBitmap(mRegions[i]->X0(), mRegions[i]->Y0(), *mRegions[i]);
#endif
}
+#ifdef BENCH
+ int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ cBitmap *bm;
+ for (int j = 0; (bm = mOsd->GetBitmap(j)) != NULL; j++)
+ if (bm->Dirty(x1, y1, x2, y2))
+ fprintf(stderr, "Flush dirty screen area %2i: x1=%3i x2=%3i y1=%3i y2=%3i\n",
+ j, x1, x2, y1, y2);
+#endif
if (!mOffScreen)
mOsd->Flush();
}
diff --git a/screen.h b/screen.h
index a5eee36..7cb447a 100644
--- a/screen.h
+++ b/screen.h
@@ -8,7 +8,8 @@
#include "common.h"
#include <vdr/osd.h>
-#undef DIRECTBLIT
+// #undef DIRECTBLIT
+#define DIRECTBLIT
class cText2SkinScreen {
/* Skin Editor */
diff --git a/text2skin.c b/text2skin.c
index 30c806b..bd53f17 100644
--- a/text2skin.c
+++ b/text2skin.c
@@ -15,7 +15,7 @@
#include "status.h"
const char *cText2SkinPlugin::VERSION = "1.2";
-const char *cText2SkinPlugin::SKINVERSION = "1.0";
+const char *cText2SkinPlugin::SKINVERSION = "1.1";
const char *cText2SkinPlugin::DESCRIPTION = trNOOP("Loader for text-based skins");
cText2SkinPlugin::cText2SkinPlugin(void) {
diff --git a/xml/display.c b/xml/display.c
index a8268c4..adf500b 100644
--- a/xml/display.c
+++ b/xml/display.c
@@ -11,7 +11,8 @@ static const std::string DisplayNames[] =
cxDisplay::cxDisplay(cxSkin *parent):
mSkin(parent),
mType((eType)__COUNT_DISPLAY__),
- mNumWindows(0)
+ mNumWindows(0),
+ mRefreshDefault(NULL)
{
}
diff --git a/xml/display.h b/xml/display.h
index 319cc7e..af1f590 100644
--- a/xml/display.h
+++ b/xml/display.h
@@ -35,6 +35,7 @@ private:
int mNumWindows;
int mNumMarquees;
cxObjects mObjects;
+ cxRefresh mRefreshDefault;
public:
cxDisplay(cxSkin *Parent);
@@ -48,7 +49,7 @@ public:
cxSkin *Skin(void) const { return mSkin; }
uint Objects(void) const { return mObjects.size(); }
- const cxObject *GetObject(int n) const { return mObjects[n]; }
+ cxObject *GetObject(int n) const { return mObjects[n]; }
};
class cxDisplays: public std::map<cxDisplay::eType,cxDisplay*> {
diff --git a/xml/object.c b/xml/object.c
index 4a3beec..ec69fe7 100644
--- a/xml/object.c
+++ b/xml/object.c
@@ -16,6 +16,7 @@ cxObject::cxObject(cxDisplay *Parent):
mType((eType)__COUNT_OBJECT__),
mPos1(0, 0),
mPos2(-1, -1),
+ mVirtSize(-1, -1),
mAlpha(255),
mColors(0),
mArc(0),
@@ -30,7 +31,9 @@ cxObject::cxObject(cxDisplay *Parent):
mFontWidth(0),
mDelay(150),
mIndex(0),
- mObjects(NULL)
+ mRefresh(this),
+ mObjects(NULL),
+ mListIndex(0)
{
}
@@ -40,11 +43,13 @@ cxObject::cxObject(const cxObject &Src):
mType(Src.mType),
mPos1(Src.mPos1),
mPos2(Src.mPos2),
+ mVirtSize(Src.mVirtSize),
mAlpha(Src.mAlpha),
mColors(Src.mColors),
mArc(Src.mArc),
mFg(Src.mFg),
mBg(Src.mBg),
+ mBl(Src.mBl),
mMask(Src.mMask),
mMark(Src.mMark),
mActive(Src.mActive),
@@ -59,7 +64,9 @@ cxObject::cxObject(const cxObject &Src):
mFontSize(Src.mFontSize),
mFontWidth(Src.mFontWidth),
mDelay(Src.mDelay),
- mObjects(NULL)
+ mRefresh(Src.mRefresh),
+ mObjects(NULL),
+ mListIndex(Src.mListIndex)
{
if (Src.mCondition)
mCondition = new cxFunction(*Src.mCondition);
@@ -128,7 +135,8 @@ bool cxObject::ParseFontFace(const std::string &Text)
void cxObject::SetListIndex(uint Index, int Tab)
{
- mIndex = mDisplay->Objects() + (Index * cSkinDisplayMenu::MaxTabs + Tab);
+ Tab = (Tab >= 0 ? Tab : -1);
+ mListIndex = 1 + Index * cSkinDisplayMenu::MaxTabs + Tab;
mText.SetListIndex(Index, Tab);
mPath.SetListIndex(Index, Tab);
if (mCondition != NULL)
@@ -154,18 +162,39 @@ const cFont *cxObject::Font(void) const
return cFont::GetFont(fontOsd);
}
-txPoint cxObject::Pos(void) const
+txPoint cxObject::Pos(const txPoint &BaseOffset, const txSize &BaseSize, const txSize &VirtSize) const
{
- return txPoint(mSkin->BaseOffset().x + (mPos1.x < 0 ? Skin()->BaseSize().w + mPos1.x : mPos1.x),
- mSkin->BaseOffset().y + (mPos1.y < 0 ? Skin()->BaseSize().h + mPos1.y : mPos1.y));
+ txPoint bOffset = BaseOffset.x < 0 ? mSkin->BaseOffset() : BaseOffset;
+ txSize bSize = BaseSize.w < 0 ? mSkin->BaseSize() : BaseSize;
+
+ double scale_x = VirtSize.w > 0 ? (double)BaseSize.w / VirtSize.w : 1.0,
+ scale_y = VirtSize.h > 0 ? (double)BaseSize.h / VirtSize.h : 1.0;
+
+ int x1 = mPos1.x < 0 ? (int)((mPos1.x + 1) * scale_x - 1) : (int)(mPos1.x * scale_x);
+ int y1 = mPos1.y < 0 ? (int)((mPos1.y + 1) * scale_x - 1) : (int)(mPos1.y * scale_y);
+
+ return txPoint(bOffset.x + (x1 < 0 ? bSize.w + x1 : x1),
+ bOffset.y + (y1 < 0 ? bSize.h + y1 : y1));
}
-txSize cxObject::Size(void) const
+txSize cxObject::Size(const txPoint &BaseOffset, const txSize &BaseSize, const txSize &VirtSize) const
{
- txPoint p1(mSkin->BaseOffset().x + (mPos1.x < 0 ? Skin()->BaseSize().w + mPos1.x : mPos1.x),
- mSkin->BaseOffset().y + (mPos1.y < 0 ? Skin()->BaseSize().h + mPos1.y : mPos1.y));
- txPoint p2(mSkin->BaseOffset().x + (mPos2.x < 0 ? Skin()->BaseSize().w + mPos2.x : mPos2.x),
- mSkin->BaseOffset().y + (mPos2.y < 0 ? Skin()->BaseSize().h + mPos2.y : mPos2.y));
+ //txPoint bOffset = BaseOffset.x < 0 ? mSkin->BaseOffset() : BaseOffset;
+ txSize bSize = BaseSize.w < 0 ? mSkin->BaseSize() : BaseSize;
+
+ double scale_x = VirtSize.w > 0 ? (double)BaseSize.w / VirtSize.w : 1.0,
+ scale_y = VirtSize.h > 0 ? (double)BaseSize.h / VirtSize.h : 1.0;
+
+ int x1 = mPos1.x < 0 ? (int)((mPos1.x + 1) * scale_x - 1) : (int)(mPos1.x * scale_x);
+ int y1 = mPos1.y < 0 ? (int)((mPos1.y + 1) * scale_x - 1) : (int)(mPos1.y * scale_y);
+ int x2 = mPos2.x < 0 ? (int)((mPos2.x + 1) * scale_x - 1) : (int)(mPos2.x * scale_x);
+ int y2 = mPos2.y < 0 ? (int)((mPos2.y + 1) * scale_x - 1) : (int)(mPos2.y * scale_y);
+
+ txPoint p1(x1 < 0 ? bSize.w + x1 : x1,
+ y1 < 0 ? bSize.h + y1 : y1);
+ txPoint p2(x2 < 0 ? bSize.w + x2 : x2,
+ y2 < 0 ? bSize.h + y2 : y2);
+
return txSize(p2.x - p1.x + 1, p2.y - p1.y + 1);
}
@@ -181,6 +210,12 @@ const tColor *cxObject::Bg(void) const
return cText2SkinRender::ItemColor(mBg, Bg) ? &Bg : NULL;
}
+const tColor *cxObject::Bl(void) const
+{
+ static tColor Bl;
+ return cText2SkinRender::ItemColor(mBl, Bl) ? &Bl : NULL;
+}
+
const tColor *cxObject::Mask(void) const
{
static tColor Mask;
@@ -215,3 +250,139 @@ cxObjects::~cxObjects()
delete operator[](i);
}
+
+///////////////////////////////////////////////////////////////////////////////
+// ---------- class cxRefresh ---------------------------------------------- //
+
+cxRefresh::cxRefresh(cxObject *Object):
+ mRefreshType(0xFF),
+ mText(NULL),
+ mChanged(NULL),
+ mObject(Object),
+ mForce(true),
+ mFull(true)
+{
+}
+
+cxRefresh::~cxRefresh()
+{
+ delete mText;
+}
+
+bool cxRefresh::Dirty(uint dirty, uint &updatein, bool force, uint now)
+{
+ // check if the timeout of the object has expired
+ uint nexttime = mObject->State().nexttime;
+
+ bool to = force || mForce ||
+ mObject->Type() == cxObject::block || mObject->Type() == cxObject::list;
+ bool changed = force || mForce;
+
+ if (now > 0 && nexttime > 0) {
+ // timeout was set
+ if (now >= nexttime)
+ // timeout has expired
+ to = true;
+ else {
+ // time left -> set new update interval
+ uint nextin = nexttime - now;
+ if (updatein == 0 || nextin < updatein)
+ updatein = nextin;
+ }
+ }
+
+ // Object has changed since last redraw
+ if (mChanged != NULL) {
+ mEval = mChanged->Evaluate();
+ if (mEval != mLastEval)
+ changed = true;
+ }
+
+ // refresh
+ if ((mRefreshType & dirty & ~(1<<timeout) & ~(1<<update))) {
+ if (changed)
+ mLastEval = mEval;
+ return true;
+ }
+
+ // timeout
+ if ((mRefreshType & dirty & (1<<timeout)) && to) {
+ if (changed)
+ mLastEval = mEval;
+ return true;
+ }
+
+ // update
+ if ((mRefreshType & dirty & (1<<update)) && changed) {
+ mLastEval = mEval;
+ return true;
+ }
+
+ return false;
+}
+
+bool cxRefresh::Parse(const std::string &Text)
+{
+ uint refresh = 0;
+ bool force = false, full = false;
+
+ if (Text.find("all") != std::string::npos)
+ refresh |= (1<<all);
+
+ if (Text.find("timeout") != std::string::npos)
+ refresh |= (1<<timeout);
+
+ if (Text.find("update") != std::string::npos)
+ refresh |= (1<<update);
+
+ //if (Text.find("message") != std::string::npos)
+ // refresh |= (1<<list);
+
+ if (Text.find("list") != std::string::npos)
+ refresh |= (1<<list);
+
+ if (Text.find("scroll") != std::string::npos)
+ refresh |= (1<<scroll);
+
+ if (Text.find("allways") != std::string::npos)
+ refresh |= 0xFF;
+
+ if (Text.find("full") != std::string::npos)
+ full = true;
+
+ if (Text.find("force") != std::string::npos)
+ force = true;
+
+ if (refresh == 0)
+ return false;
+
+ mForce = force;
+ mFull = full;
+ mRefreshType = refresh;
+
+ return true;
+}
+
+bool cxRefresh::ParseChanged(const std::string &Text)
+{
+ if (mObject == NULL)
+ return false;
+
+ if (mText == NULL)
+ mText = new cxString(mObject, false);
+
+ if (mText->Parse(Text)) {
+ mChanged = mText;
+ return true;
+ }
+
+ return false;
+}
+
+cxRefresh &cxRefresh::operator=(const cxRefresh &a)
+{
+ mRefreshType = a.mRefreshType;
+ mForce = a.mForce;
+ mFull = a.mFull;
+ return *this;
+}
diff --git a/xml/object.h b/xml/object.h
index bbc37a0..18600fe 100644
--- a/xml/object.h
+++ b/xml/object.h
@@ -33,6 +33,50 @@ struct txWindow {
pos1(_x1, _y2), pos2(_x2, _y2), bpp(_bpp) {}
};
+// state information for marquee, blink, scroll
+struct txState {
+ bool scrolling;
+ int offset;
+ int direction;
+ uint nexttime;
+ std::string text;
+ txState(void): scrolling(false), offset(0), direction(1), nexttime(0) {}
+};
+
+class cxObject;
+
+class cxRefresh {
+ friend bool xEndElem(const std::string &name);
+
+public:
+ enum eRefreshType {
+ all, // complete redraw of the screen
+ timeout, // redraw due to a timeout
+ //message, // a message was set or removed
+ update, // update of the osd elements
+ scroll, // a scroll event
+ list, // list items or the current item have changed
+ };
+
+ cxRefresh(cxObject *Object);
+ ~cxRefresh();
+ bool Dirty(uint dirty, uint &updatein, bool force = false, uint now = 0);
+ bool Full(void) const { return mFull; }
+ uint Type(void) const { return mRefreshType; }
+ bool Parse(const std::string &Text);
+ bool ParseChanged(const std::string &Text);
+ cxRefresh &operator=(const cxRefresh &b);
+
+private:
+ uint mRefreshType;
+ cxType mLastEval;
+ cxType mEval;
+ cxString *mText;
+ cxString *mChanged;
+ cxObject *mObject;
+ bool mForce, mFull;
+};
+
class cxObjects;
class cxObject {
@@ -68,11 +112,13 @@ private:
eType mType;
txPoint mPos1;
txPoint mPos2;
+ txSize mVirtSize;
int mAlpha;
int mColors;
int mArc;
std::string mFg;
std::string mBg;
+ std::string mBl;
std::string mMask;
std::string mMark;
std::string mActive;
@@ -88,8 +134,14 @@ private:
int mFontWidth;
uint mDelay;
uint mIndex;
+ cxRefresh mRefresh;
cxObjects *mObjects; // used for block objects such as <list>
+ // state information for marquee, blink, scroll
+ uint mListIndex;
+ typedef std::map<uint,txState> tStates;
+ tStates mStates;
+
public:
cxObject(cxDisplay *parent);
cxObject(const cxObject &Src);
@@ -116,20 +168,24 @@ public:
uint Index(void) const { return mIndex; }
cxDisplay *Display(void) const { return mDisplay; }
cxSkin *Skin(void) const { return mSkin; }
+ txState &State(void) { return mStates[mListIndex]; }
const std::string &TypeName(void) const;
- txPoint Pos(void) const;
- txSize Size(void) const;
+ txPoint Pos(const txPoint &BaseOffset = txPoint(-1, -1), const txSize &BaseSize = txSize(-1, -1),
+ const txSize &VirtSize = txSize(-1, -1)) const;
+ txSize Size(const txPoint &BaseOffset = txPoint(-1, -1), const txSize &BaseSize = txSize(-1, -1),
+ const txSize &VirtSize = txSize(-1, -1)) const;
const cFont *Font(void) const;
const tColor *Fg(void) const;
const tColor *Bg(void) const;
+ const tColor *Bl(void) const;
const tColor *Mask(void) const;
const tColor *Mark(void) const;
const tColor *Active(void) const;
const tColor *Keep(void) const;
uint Objects(void) const;
- const cxObject *GetObject(uint Index) const;
+ cxObject *GetObject(uint Index) const;
};
class cxObjects: public std::vector<cxObject*> {
@@ -144,7 +200,7 @@ inline uint cxObject::Objects(void) const
return mObjects ? mObjects->size() : 0;
}
-inline const cxObject *cxObject::GetObject(uint Index) const
+inline cxObject *cxObject::GetObject(uint Index) const
{
return mObjects ? (*mObjects)[Index] : NULL;
}
diff --git a/xml/parser.c b/xml/parser.c
index f573398..fe93602 100644
--- a/xml/parser.c
+++ b/xml/parser.c
@@ -45,7 +45,7 @@
#define ATTRIB_OPT_NUMBER(_attr,_target) \
if (attrs.find(_attr) != attrs.end()) { \
char *_e; const char *_t = attrs[_attr].c_str(); \
- long _l = strtol(_t, &_e, 10); \
+ long _l = strtol(_t, &_e, 0); \
if (_e ==_t || *_e != '\0') { \
esyslog("ERROR: Text2Skin: Invalid numeric value \"%s\" in attribute %s", \
_t, _attr); \
@@ -91,7 +91,7 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr
if (context.size() == 0) {
if (name == "skin") {
- ATTRIB_MAN_STRING("version", skin->mVersion);
+ ATTRIB_MAN_FUNC ("version", skin->mVersion.Parse);
ATTRIB_MAN_STRING("name", skin->mTitle);
ATTRIB_MAN_FUNC ("screenBase", skin->ParseBase);
}
@@ -102,7 +102,8 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr
if (name == "display") {
display = new cxDisplay(skin);
ATTRIB_MAN_FUNC ("id", display->ParseType);
- }
+ ATTRIB_OPT_FUNC ("refresh", display->mRefreshDefault.Parse);
+ }
else
TAG_ERR_REMAIN("skin");
}
@@ -129,12 +130,18 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr
else {
object = new cxObject(display);
if (object->ParseType(name)) {
+ if (parents.size() > 0)
+ object->mRefresh = parents.back()->mRefresh;
+ else
+ object->mRefresh = display->mRefreshDefault;
+
ATTRIB_OPT_NUMBER("x1", object->mPos1.x);
ATTRIB_OPT_NUMBER("y1", object->mPos1.y);
ATTRIB_OPT_NUMBER("x2", object->mPos2.x);
ATTRIB_OPT_NUMBER("y2", object->mPos2.y);
ATTRIB_OPT_FUNC ("condition", object->ParseCondition);
-
+ ATTRIB_OPT_FUNC ("refresh", object->mRefresh.Parse);
+ ATTRIB_OPT_FUNC ("changed", object->mRefresh.ParseChanged);
if (name == "image") {
ATTRIB_OPT_NUMBER("x", object->mPos1.x);
ATTRIB_OPT_NUMBER("y", object->mPos1.y);
@@ -152,11 +159,12 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr
|| name == "blink"
|| name == "scrolltext") {
ATTRIB_OPT_STRING("color", object->mFg);
+ ATTRIB_OPT_STRING("bgColor", object->mBg);
ATTRIB_OPT_FUNC ("align", object->ParseAlignment);
ATTRIB_OPT_FUNC ("font", object->ParseFontFace);
if (name == "blink") {
- ATTRIB_OPT_STRING("blinkColor", object->mBg);
+ ATTRIB_OPT_STRING("blinkColor", object->mBl);
ATTRIB_OPT_NUMBER("delay", object->mDelay);
if (object->mDelay == 0)
@@ -190,6 +198,11 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr
ATTRIB_MAN_NUMBER("height", object->mPos2.y);
--object->mPos2.y;
}
+ else if (name == "block"
+ || name == "list") {
+ ATTRIB_OPT_NUMBER("w", object->mVirtSize.w);
+ ATTRIB_OPT_NUMBER("h", object->mVirtSize.h);
+ }
} else
TAG_ERR_REMAIN(context[context.size() - 1].c_str());
}
@@ -256,6 +269,20 @@ bool xEndElem(const std::string &name) {
}
}
+ if (object->mRefresh.mChanged == NULL) {
+ switch (object->mType) {
+ case cxObject::text:
+ case cxObject::marquee:
+ case cxObject::blink:
+ case cxObject::scrolltext:
+ object->mRefresh.mChanged = &object->mText;
+ break;
+
+ default:
+ break;
+ }
+ }
+
object->mIndex = oindex++;
if (parents.size() > 0) {
Dprintf("pushing to parent\n");
diff --git a/xml/skin.c b/xml/skin.c
index 6f3e938..2935dee 100644
--- a/xml/skin.c
+++ b/xml/skin.c
@@ -9,6 +9,37 @@
const std::string ScreenBases[] = { "relative", "absolute" };
+cxVersion::cxVersion(int ma, int min):
+ mMajor(ma),
+ mMinor(min)
+{
+}
+
+bool cxVersion::Parse(const std::string &Text)
+{
+ int dot = Text.find(".");
+ std::string ma(Text, 0, dot), min(Text, dot + 1);
+ char *e = NULL;
+ const char *t = NULL;
+ long l = 0;
+
+ t = ma.c_str();
+ l = strtol(t, &e, 10);
+ if (e == t || *e != '\0')
+ return false;
+ else
+ mMajor = l;
+
+ t = min.c_str();
+ l = strtol(t, &e, 10);
+ if (e == t || *e != '\0')
+ return false;
+ else
+ mMinor = l;
+
+ return true;
+}
+
cxSkin::cxSkin(const std::string &Name, cText2SkinI18n *I18n, cText2SkinTheme *Theme):
mName(Name),
mI18n(I18n),
diff --git a/xml/skin.h b/xml/skin.h
index e27f87e..49656ea 100644
--- a/xml/skin.h
+++ b/xml/skin.h
@@ -15,6 +15,45 @@
class cText2SkinI18n;
class cText2SkinTheme;
+class cxVersion {
+public:
+ cxVersion(int ma = 0, int min = 0);
+ bool Parse(const std::string &Text);
+ int Major(void) const { return mMajor; }
+ int Minor(void) const { return mMinor; }
+ bool Require(int ma, int min) const {
+ return mMajor > ma ? true : (mMajor == ma ? mMinor >= min : false);
+ }
+ bool Limit(int ma, int min) const {
+ return mMajor < ma ? true : (mMajor == ma ? mMinor <= min : false);
+ }
+ bool operator==(const cxVersion &v) const {
+ return mMajor == v.mMajor && mMinor == v.mMinor;
+ }
+ bool operator>=(const cxVersion &v) const {
+ return Require(v.mMajor, v.mMinor);
+ }
+ bool operator>=(const char *c) const {
+ cxVersion v;
+ if (!v.Parse(c))
+ return false;
+ return Require(v.mMajor, v.mMinor);
+ }
+ bool operator<=(const cxVersion &v) const {
+ return Limit(v.mMajor, v.mMinor);
+ }
+ bool operator<=(const char *c) const {
+ cxVersion v;
+ if (!v.Parse(c))
+ return false;
+ return Limit(v.mMajor, v.mMinor);
+ }
+
+private:
+ int mMajor;
+ int mMinor;
+};
+
class cxSkin {
friend bool xStartElem(const std::string &name, std::map<std::string,std::string> &attrs);
friend bool xEndElem(const std::string &name);
@@ -35,8 +74,8 @@ private:
txSize mBaseSize;
std::string mName;
std::string mTitle;
- std::string mVersion;
-
+ cxVersion mVersion;
+
cxDisplays mDisplays;
cText2SkinI18n *mI18n; // TODO: should move here completely
@@ -55,7 +94,7 @@ public:
const txSize &BaseSize(void) const { return mBaseSize; }
const std::string &Name(void) const { return mName; }
const std::string &Title(void) const { return mTitle; }
- const std::string &Version(void) const { return mVersion; }
+ const cxVersion &Version(void) const { return mVersion; }
// functions for object classes to obtain dynamic item information
std::string Translate(const std::string &Text);