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()) {
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 }
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);
323 SkinQueuedMessages.Add(m);
324 m->mutex.Lock();
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 {
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));
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:
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 }
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:
391 for (;;) {
393 if (m && m->state == 2) { // done
394 SkinQueuedMessages.Del(m);
395 }
396 else
397 break;
398 }
400}
401
403{
406}
407
409{
410 if (displayMessage) {
411 delete displayMessage;
412 displayMessage = NULL;
413 }
415}
int Height(void) const
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
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
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
int Position(void) const
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,...
int TargetLongitude(void) const
Returns the longitude the dish is supposed to be moved to.
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
virtual void SetMessage(eMessageType Type, const char *Text)=0
Sets a one line message Text, with the given Type.
const cPositioner * positioner
< This class is used to display the current channel, together with the present and following EPG even...
eMenuCategory menuCategory
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
int Tab(int n)
Returns the offset of the given tab from the left border of the item display area.
eMenuCategory MenuCategory(void) const
Returns the menu category, set by a previous call to SetMenuCategory().
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
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
const cMarks * marks
< This class implements the progress display used during replay of a recording.
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
static cSkinDisplay * Current(void)
Returns the currently active cSkinDisplay.
virtual void Flush(void)
Actually draws the OSD display to the output device.
cSkinDisplay(void)
Definition: skins.c:55
static cSkinDisplay * current
virtual ~cSkinDisplay()
Definition: skins.c:61
virtual void SetMessage(eMessageType Type, const char *Text)
Sets a one line message Text, with the given Type.
static int AvgCharWidth(void)
Returns the average width of a character in pixel (just a raw estimate).
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
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
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
cSkinDisplayMessage * displayMessage
cSkin * Current(void)
Returns a pointer to the current skin.
cMutex queueMessageMutex
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
static void Save(const char *SkinName, cTheme *Theme)
Definition: themes.c:309
static tThreadId IsMainThread(void)
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
@ kNone
eMenuCategory
@ mcUndefined
eMessageType
@ mtWarning
@ mtInfo
@ mtError
@ mtStatus
pid_t tThreadId
bool isempty(const char *s)
Definition: tools.c:349
#define dsyslog(a...)
#define esyslog(a...)
#define isyslog(a...)
static cTheme Theme
Definition: skinclassic.c:21
cList< cSkinQueuedMessage > SkinQueuedMessages
Definition: skins.c:49
cSkins Skins
Definition: skins.c:219