vdr 2.6.1
skins.c
Go to the documentation of this file.
1/*
2 * skins.c: The optical appearance of the OSD
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: skins.c 4.2 2019/05/29 16:43:09 kls Exp $
8 */
9
10#include "skins.h"
11#include "interface.h"
12#include "status.h"
13
14// --- cSkinQueuedMessage ----------------------------------------------------
15
17 friend class cSkins;
18private:
20 char *message;
25 int state;
28public:
29 cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
30 virtual ~cSkinQueuedMessage();
31 };
32
33cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
34{
35 type = Type;
36 message = s ? strdup(s) : NULL;
37 seconds = Seconds;
38 timeout = Timeout;
40 key = kNone;
41 state = 0; // waiting
42}
43
45{
46 free(message);
47}
48
50
51// --- cSkinDisplay ----------------------------------------------------------
52
54
56{
57 current = this;
58 editableWidth = 100; //XXX
59}
60
62{
63 current = NULL;
64}
65
66// --- cSkinDisplayChannel ---------------------------------------------------
67
69{
70 positioner = NULL;
71}
72
74{
75 if (positioner && Positioner != positioner)
76 SetMessage(mtInfo, NULL);
77 positioner = Positioner;
78 if (positioner)
79 SetMessage(mtInfo, cString::sprintf(tr("Moving dish to %.1f..."), double(positioner->TargetLongitude()) / 10));
80}
81
82// --- cSkinDisplayMenu ------------------------------------------------------
83
85{
87 SetTabs(0);
88}
89
91{
93}
94
95void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5)
96{
97 tabs[0] = 0;
98 tabs[1] = Tab1 ? tabs[0] + Tab1 : 0;
99 tabs[2] = Tab2 ? tabs[1] + Tab2 : 0;
100 tabs[3] = Tab3 ? tabs[2] + Tab3 : 0;
101 tabs[4] = Tab4 ? tabs[3] + Tab4 : 0;
102 tabs[5] = Tab5 ? tabs[4] + Tab5 : 0;
103 for (int i = 1; i < MaxTabs; i++)
104 tabs[i] *= AvgCharWidth();
105}
106
107void cSkinDisplayMenu::Scroll(bool Up, bool Page)
108{
109 textScroller.Scroll(Up, Page);
110}
111
112const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab)
113{
114 if (!s)
115 return NULL;
116 static char buffer[1000];
117 const char *a = s;
118 const char *b = strchrnul(a, '\t');
119 while (*b && Tab-- > 0) {
120 a = b + 1;
121 b = strchrnul(a, '\t');
122 }
123 if (!*b)
124 return (Tab <= 0) ? a : NULL;
125 unsigned int n = b - a;
126 if (n >= sizeof(buffer))
127 n = sizeof(buffer) - 1;
128 strncpy(buffer, a, n);
129 buffer[n] = 0;
130 return buffer;
131}
132
133void cSkinDisplayMenu::SetScrollbar(int Total, int Offset)
134{
135}
136
138{
139 return 0;
140}
141
143{
144 return NULL;
145}
146
147// --- cSkinDisplayReplay::cProgressBar --------------------------------------
148
149cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
150:cBitmap(Width, Height, 2)
151{
152 total = Total;
153 if (total > 0) {
154 int p = Pos(Current);
155 DrawRectangle(0, 0, p, Height - 1, ColorSeen);
156 DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
157 if (Marks) {
158 bool Start = true;
159 for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
160 int p1 = Pos(m->Position());
161 if (Start) {
162 const cMark *m2 = Marks->Next(m);
163 int p2 = Pos(m2 ? m2->Position() : total);
164 int h = Height / 3;
165 DrawRectangle(p1, h, p2, Height - h, ColorSelected);
166 }
167 Mark(p1, Start, m->Position() == Current, ColorMark, ColorCurrent);
168 Start = !Start;
169 }
170 }
171 }
172}
173
174void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
175{
176 DrawRectangle(x, 0, x, Height() - 1, ColorMark);
177 const int d = Height() / (Current ? 3 : 9);
178 for (int i = 0; i < d; i++) {
179 int h = Start ? i : Height() - 1 - i;
180 DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
181 }
182}
183
184// --- cSkinDisplayReplay ----------------------------------------------------
185
187{
188 marks = NULL;
189}
190
192{
193 SetTitle(Recording->Title());
194}
195
197{
198 marks = Marks;
199}
200
201// --- cSkin -----------------------------------------------------------------
202
203cSkin::cSkin(const char *Name, cTheme *Theme)
204{
205 name = strdup(Name);
206 theme = Theme;
207 if (theme)
208 cThemes::Save(name, theme);
209 Skins.Add(this);
210}
211
213{
214 free(name);
215}
216
217// --- cSkins ----------------------------------------------------------------
218
220
222{
223 displayMessage = NULL;
224}
225
227{
228 delete displayMessage;
229}
230
231bool cSkins::SetCurrent(const char *Name)
232{
233 if (Name) {
234 for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
235 if (strcmp(Skin->Name(), Name) == 0) {
236 isyslog("setting current skin to \"%s\"", Name);
237 current = Skin;
238 return true;
239 }
240 }
241 }
242 current = First();
243 if (current)
244 isyslog("skin \"%s\" not available - using \"%s\" instead", Name, current->Name());
245 else
246 esyslog("ERROR: no skin available");
247 return current != NULL;
248}
249
250eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
251{
252 if (!cThread::IsMainThread()) {
253 if (Type != mtStatus)
254 QueueMessage(Type, s, Seconds);
255 else
256 dsyslog("cSkins::Message(%d, \"%s\", %d) called from background thread - ignored! (Use cSkins::QueueMessage() instead)", Type, s, Seconds);
257 return kNone;
258 }
259 switch (Type) {
260 case mtInfo: isyslog("info: %s", s); break;
261 case mtWarning: isyslog("warning: %s", s); break;
262 case mtError: esyslog("ERROR: %s", s); break;
263 default: ;
264 }
265 if (!Current())
266 return kNone;
267 if (!cSkinDisplay::Current()) {
268 if (displayMessage)
269 delete displayMessage;
270 displayMessage = Current()->DisplayMessage();
271 }
275 eKeys k = kNone;
276 if (Type != mtStatus) {
277 k = Interface->Wait(Seconds);
278 if (displayMessage) {
279 delete displayMessage;
280 displayMessage = NULL;
282 }
283 else {
284 cSkinDisplay::Current()->SetMessage(Type, NULL);
286 }
287 }
288 else if (!s && displayMessage) {
289 delete displayMessage;
290 displayMessage = NULL;
292 }
293 return k;
294}
295
296int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
297{
298 if (Type == mtStatus) {
299 dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
300 return kNone;
301 }
302 if (isempty(s)) {
303 if (!cThread::IsMainThread()) {
304 queueMessageMutex.Lock();
305 for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
306 if (m->threadId == cThread::ThreadId() && m->state == 0)
307 m->state = 2; // done
308 }
309 queueMessageMutex.Unlock();
310 }
311 else
312 dsyslog("cSkins::QueueMessage() called with empty message from main thread - ignored!");
313 return kNone;
314 }
315 int k = kNone;
316 if (Timeout > 0) {
317 if (cThread::IsMainThread()) {
318 dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
319 return k;
320 }
321 cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
322 queueMessageMutex.Lock();
323 SkinQueuedMessages.Add(m);
324 m->mutex.Lock();
325 queueMessageMutex.Unlock();
326 if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
327 k = m->key;
328 else
329 k = -1; // timeout, nothing has been displayed
330 m->state = 2; // done
331 m->mutex.Unlock();
332 }
333 else {
334 queueMessageMutex.Lock();
335 // Check if there is a waiting message w/o timeout for this thread:
336 if (Timeout == -1) {
337 for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
338 if (m->threadId == cThread::ThreadId()) {
339 if (m->state == 0 && m->timeout == -1)
340 m->state = 2; // done
341 break;
342 }
343 }
344 }
345 // Add the new message:
346 SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
347 queueMessageMutex.Unlock();
348 }
349 return k;
350}
351
353{
354 if (!cThread::IsMainThread()) {
355 dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
356 return;
357 }
358 // Check whether there is a cSkinDisplay object (if any) that implements SetMessage():
360 if (!(dynamic_cast<cSkinDisplayChannel *>(sd) ||
361 dynamic_cast<cSkinDisplayMenu *>(sd) ||
362 dynamic_cast<cSkinDisplayReplay *>(sd) ||
363 dynamic_cast<cSkinDisplayMessage *>(sd)))
364 return;
365 }
366 cSkinQueuedMessage *msg = NULL;
367 // Get the first waiting message:
368 queueMessageMutex.Lock();
369 for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
370 if (m->state == 0) { // waiting
371 m->state = 1; // active
372 msg = m;
373 break;
374 }
375 }
376 queueMessageMutex.Unlock();
377 // Display the message:
378 if (msg) {
379 msg->mutex.Lock();
380 if (msg->state == 1) { // might have changed since we got it
381 msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
382 if (msg->timeout == 0)
383 msg->state = 2; // done
384 else
385 msg->condVar.Broadcast();
386 }
387 msg->mutex.Unlock();
388 }
389 // Remove done messages from the queue:
390 queueMessageMutex.Lock();
391 for (;;) {
393 if (m && m->state == 2) { // done
394 SkinQueuedMessages.Del(m);
395 }
396 else
397 break;
398 }
399 queueMessageMutex.Unlock();
400}
401
403{
406}
407
409{
410 if (displayMessage) {
411 delete displayMessage;
412 displayMessage = NULL;
413 }
415}
Definition: osd.h:169
int Height(void) const
Definition: osd.h:189
void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
Draws a filled rectangle defined by the upper left (x1, y1) and lower right (x2, y2) corners with the...
Definition: osd.c:611
int Width(void) const
Definition: osd.h:188
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:132
void Broadcast(void)
Definition: thread.c:150
Definition: font.h:37
eKeys Wait(int Seconds=0, bool KeepChar=false)
Definition: interface.c:41
virtual void Clear(void)
Definition: tools.c:2261
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2184
Definition: tools.h:641
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:653
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:660
int Position(void) const
Definition: recording.h:363
Definition: thread.h:67
void Lock(void)
Definition: thread.c:222
void Unlock(void)
Definition: thread.c:228
A steerable satellite dish generally points to the south on the northern hemisphere,...
Definition: positioner.h:31
int TargetLongitude(void) const
Returns the longitude the dish is supposed to be moved to.
Definition: positioner.h:105
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
Definition: recording.c:1084
virtual void SetPositioner(const cPositioner *Positioner)
Sets the Positioner used to move the satellite dish.
Definition: skins.c:73
cSkinDisplayChannel(void)
Definition: skins.c:68
const cPositioner * positioner
< This class is used to display the current channel, together with the present and following EPG even...
Definition: skins.h:70
virtual void SetMessage(eMessageType Type, const char *Text)=0
Sets a one line message Text, with the given Type.
eMenuCategory menuCategory
Definition: skins.h:170
virtual void Scroll(bool Up, bool Page)
If this menu contains a text area that can be scrolled, this function will be called to actually scro...
Definition: skins.c:107
virtual void SetTabs(int Tab1, int Tab2=0, int Tab3=0, int Tab4=0, int Tab5=0)
Sets the tab columns to the given values, which are the number of characters in each column.
Definition: skins.c:95
virtual int GetTextAreaWidth(void) const
Returns the width in pixel of the area which is used to display text with SetText().
Definition: skins.c:137
cTextScroller textScroller
Definition: skins.h:173
int Tab(int n)
Returns the offset of the given tab from the left border of the item display area.
Definition: skins.h:174
eMenuCategory MenuCategory(void) const
Returns the menu category, set by a previous call to SetMenuCategory().
Definition: skins.h:183
virtual void SetScrollbar(int Total, int Offset)
Sets the Total number of items in the currently displayed list, and the Offset of the first item that...
Definition: skins.c:133
virtual const cFont * GetTextAreaFont(bool FixedFont) const
Returns a pointer to the font which is used to display text with SetText().
Definition: skins.c:142
cSkinDisplayMenu(void)
Definition: skins.c:84
const char * GetTabbedText(const char *s, int Tab)
Returns the part of the given string that follows the given Tab (where 0 indicates the beginning of t...
Definition: skins.c:112
virtual void SetMenuCategory(eMenuCategory MenuCategory)
Sets the current menu category.
Definition: skins.c:90
int tabs[MaxTabs]
Definition: skins.h:171
void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
Definition: skins.c:174
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
Definition: skins.c:149
virtual void SetMarks(const cMarks *Marks)
Sets the editing marks to Marks, which shall be used to display the progress bar through a cProgressB...
Definition: skins.c:196
virtual void SetTitle(const char *Title)=0
Sets the title of the recording.
virtual void SetRecording(const cRecording *Recording)
Sets the recording that is currently being played.
Definition: skins.c:191
cSkinDisplayReplay(void)
Definition: skins.c:186
const cMarks * marks
< This class implements the progress display used during replay of a recording.
Definition: skins.h:323
static cSkinDisplay * Current(void)
Returns the currently active cSkinDisplay.
Definition: skins.h:61
static cSkinDisplay * current
Definition: skins.h:41
virtual void Flush(void)
Actually draws the OSD display to the output device.
Definition: skins.h:59
cSkinDisplay(void)
Definition: skins.c:55
int editableWidth
Definition: skins.h:42
virtual ~cSkinDisplay()
Definition: skins.c:61
virtual void SetMessage(eMessageType Type, const char *Text)
Sets a one line message Text, with the given Type.
Definition: skins.h:56
static int AvgCharWidth(void)
Returns the average width of a character in pixel (just a raw estimate).
Definition: skins.h:46
virtual ~cSkinQueuedMessage()
Definition: skins.c:44
cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
Definition: skins.c:33
tThreadId threadId
Definition: skins.c:23
eMessageType type
Definition: skins.c:19
char * message
Definition: skins.c:20
cMutex mutex
Definition: skins.c:26
cCondVar condVar
Definition: skins.c:27
Definition: skins.h:402
cSkin(const char *Name, cTheme *Theme=NULL)
Creates a new skin class, with the given Name and Theme.
Definition: skins.c:203
virtual ~cSkin()
Definition: skins.c:212
Definition: skins.h:457
bool SetCurrent(const char *Name=NULL)
Sets the current skin to the one indicated by name.
Definition: skins.c:231
eKeys Message(eMessageType Type, const char *s, int Seconds=0)
Displays the given message, either through a currently visible display object that is capable of doin...
Definition: skins.c:250
void Flush(void)
Flushes the currently active cSkinDisplay, if any.
Definition: skins.c:402
~cSkins()
Definition: skins.c:226
cSkin * Current(void)
Returns a pointer to the current skin.
Definition: skins.h:468
virtual void Clear(void)
Free up all registered skins.
Definition: skins.c:408
void ProcessQueuedMessages(void)
Processes the first queued message, if any.
Definition: skins.c:352
cSkins(void)
Definition: skins.c:221
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
Definition: skins.c:296
static void MsgOsdStatusMessage(const char *Message)
Definition: status.c:98
static void MsgOsdClear(void)
Definition: status.c:86
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1149
void Scroll(bool Up, bool Page)
Definition: osd.c:2376
Definition: themes.h:17
static void Save(const char *SkinName, cTheme *Theme)
Definition: themes.c:309
static tThreadId IsMainThread(void)
Definition: thread.h:131
static tThreadId ThreadId(void)
Definition: thread.c:372
uint32_t tColor
Definition: font.h:30
#define tr(s)
Definition: i18n.h:85
cInterface * Interface
Definition: interface.c:20
eKeys
Definition: keys.h:16
@ kNone
Definition: keys.h:55
static cTheme Theme
Definition: skinclassic.c:21
cList< cSkinQueuedMessage > SkinQueuedMessages
Definition: skins.c:49
cSkins Skins
Definition: skins.c:219
eMenuCategory
Definition: skins.h:104
@ mcUndefined
Definition: skins.h:105
eMessageType
Definition: skins.h:37
@ mtWarning
Definition: skins.h:37
@ mtInfo
Definition: skins.h:37
@ mtError
Definition: skins.h:37
@ mtStatus
Definition: skins.h:37
pid_t tThreadId
Definition: thread.h:17
bool isempty(const char *s)
Definition: tools.c:349
#define dsyslog(a...)
Definition: tools.h:37
#define esyslog(a...)
Definition: tools.h:35
#define isyslog(a...)
Definition: tools.h:36