vdr  2.7.6
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 5.6 2025/03/02 11:03:35 kls Exp $
8  */
9 
10 #include "skins.h"
11 #include "interface.h"
12 #include "status.h"
13 
14 // --- cSkinQueuedMessage ----------------------------------------------------
15 
17  friend class cSkins;
18 private:
20  char *message;
21  int seconds;
22  int timeout;
25  int state;
28 public:
29  cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
30  virtual ~cSkinQueuedMessage() override;
31  };
32 
33 cSkinQueuedMessage::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 
95 void 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 
107 void cSkinDisplayMenu::Scroll(bool Up, bool Page)
108 {
109  textScroller.Scroll(Up, Page);
110 }
111 
112 const 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 
133 void cSkinDisplayMenu::SetScrollbar(int Total, int Offset)
134 {
135 }
136 
138 {
139  return 0;
140 }
141 
142 const cFont *cSkinDisplayMenu::GetTextAreaFont(bool FixedFont) const
143 {
144  return cFont::GetFont(FixedFont ? fontFix : fontOsd);
145 }
146 
147 // --- cSkinDisplayReplay::cProgressBar --------------------------------------
148 
149 cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
150 :cSkinDisplayReplay::cProgressBar::cProgressBar(Width, Height, Current, Total, Marks, NULL, ColorSeen, ColorRest, ColorSelected, ColorMark, ColorCurrent, clrBlack)
151 {
152 }
153 
154 cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, const cErrors *Errors, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent, tColor ColorError)
155 :cBitmap(Width, Height, 4)
156 {
157  total = Total;
158  if (total > 0) {
159  int p = Pos(Current);
160  DrawRectangle(0, 0, p, Height - 1, ColorSeen);
161  DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
162  if (Marks) {
163  bool Start = true;
164  for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
165  int p1 = Pos(m->Position());
166  if (Start) {
167  const cMark *m2 = Marks->Next(m);
168  int p2 = Pos(m2 ? m2->Position() : total);
169  int h = Height / 3;
170  DrawRectangle(p1, h, p2, Height - h - 1, ColorSelected);
171  }
172  Mark(p1, Start, m->Position() == Current, ColorMark, ColorCurrent);
173  Start = !Start;
174  }
175  }
176  if (Errors) {
177  int LastPos = -1;
178  for (int i = 0; i < Errors->Size(); i++) {
179  int p1 = Errors->At(i);
180  if (p1 != LastPos) {
181  Error(Pos(Errors->At(i)), ColorError);
182  LastPos = p1;
183  }
184  }
185  }
186  }
187 }
188 
189 void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
190 {
191  DrawRectangle(x, 0, x, Height() - 1, ColorMark);
192  const int d = Height() / (Current ? 3 : 9);
193  for (int i = 0; i < d; i++) {
194  int h = Start ? i : Height() - 1 - i;
195  DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
196  }
197 }
198 
200 {
201  const int d = (Height() / 9) & ~0x01; // must be even
202  const int h = Height() / 2;
203  const int e = Height() / 4;
204  DrawRectangle(x, e, x, Height() -e - 1, ColorError);
205  DrawRectangle(x - d, h, x + d, h, ColorError);
206  for (int i = 1; i <= d; i++) {
207  DrawRectangle(x - d + i, h - i, x + d - i, h - i, ColorError);
208  DrawRectangle(x - d + i, h + i, x + d - i, h + i, ColorError);
209  }
210 }
211 
212 // --- cSkinDisplayReplay ----------------------------------------------------
213 
215 {
216  marks = NULL;
217  errors = NULL;
218 }
219 
221 {
222  SetTitle(Recording->Title());
223 }
224 
226 {
227  marks = Marks;
228 }
229 
231 {
232  errors = Errors;
233 }
234 
235 // --- cSkin -----------------------------------------------------------------
236 
237 cSkin::cSkin(const char *Name, cTheme *Theme)
238 {
239  name = strdup(Name);
240  theme = Theme;
241  if (theme)
242  cThemes::Save(name, theme);
243  Skins.Add(this);
244 }
245 
247 {
248  free(name);
249 }
250 
251 // --- cSkins ----------------------------------------------------------------
252 
254 
256 {
257  displayMessage = NULL;
258 }
259 
261 {
262  delete displayMessage;
263 }
264 
265 bool cSkins::SetCurrent(const char *Name)
266 {
267  if (Name) {
268  for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
269  if (strcmp(Skin->Name(), Name) == 0) {
270  isyslog("setting current skin to \"%s\"", Name);
271  current = Skin;
272  return true;
273  }
274  }
275  }
276  current = First();
277  if (current)
278  isyslog("skin \"%s\" not available - using \"%s\" instead", Name, current->Name());
279  else
280  esyslog("ERROR: no skin available");
281  return current != NULL;
282 }
283 
284 eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
285 {
286  if (!cThread::IsMainThread()) {
287  if (Type != mtStatus)
288  QueueMessage(Type, s, Seconds);
289  else
290  dsyslog("cSkins::Message(%d, \"%s\", %d) called from background thread - ignored! (Use cSkins::QueueMessage() instead)", Type, s, Seconds);
291  return kNone;
292  }
293  switch (Type) {
294  case mtInfo: isyslog("info: %s", s); break;
295  case mtWarning: isyslog("warning: %s", s); break;
296  case mtError: esyslog("ERROR: %s", s); break;
297  default: ;
298  }
299  if (!Current())
300  return kNone;
301  if (!cSkinDisplay::Current()) {
302  if (displayMessage)
303  delete displayMessage;
304  displayMessage = Current()->DisplayMessage();
305  }
306  cSkinDisplay::Current()->SetMessage(Type, s);
309  eKeys k = kNone;
310  if (Type != mtStatus) {
311  k = Interface->Wait(Seconds);
312  if (displayMessage) {
313  delete displayMessage;
314  displayMessage = NULL;
316  }
317  else {
318  cSkinDisplay::Current()->SetMessage(Type, NULL);
319  cStatus::MsgOsdStatusMessage(Type, NULL);
320  }
321  }
322  else if (!s && displayMessage) {
323  delete displayMessage;
324  displayMessage = NULL;
326  }
327  return k;
328 }
329 
330 int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
331 {
332  if (Type == mtStatus) {
333  dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
334  return kNone;
335  }
336  if (isempty(s)) {
337  if (!cThread::IsMainThread()) {
339  for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
340  if (m->threadId == cThread::ThreadId() && m->state == 0)
341  m->state = 2; // done
342  }
344  }
345  else
346  dsyslog("cSkins::QueueMessage() called with empty message from main thread - ignored!");
347  return kNone;
348  }
349  int k = kNone;
350  if (Timeout > 0) {
351  if (cThread::IsMainThread()) {
352  dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
353  return k;
354  }
355  cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
357  SkinQueuedMessages.Add(m);
358  m->mutex.Lock();
360  if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
361  k = m->key;
362  else
363  k = -1; // timeout, nothing has been displayed
364  m->state = 2; // done
365  m->mutex.Unlock();
366  }
367  else {
369  // Check if there is a waiting message w/o timeout for this thread:
370  if (Timeout == -1) {
371  for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
372  if (m->threadId == cThread::ThreadId()) {
373  if (m->state == 0 && m->timeout == -1)
374  m->state = 2; // done
375  break;
376  }
377  }
378  }
379  // Add the new message:
380  SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
382  }
383  return k;
384 }
385 
387 {
388  if (!cThread::IsMainThread()) {
389  dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
390  return;
391  }
392  // Check whether there is a cSkinDisplay object (if any) that implements SetMessage():
393  if (cSkinDisplay *sd = cSkinDisplay::Current()) {
394  if (!(dynamic_cast<cSkinDisplayChannel *>(sd) ||
395  dynamic_cast<cSkinDisplayMenu *>(sd) ||
396  dynamic_cast<cSkinDisplayReplay *>(sd) ||
397  dynamic_cast<cSkinDisplayMessage *>(sd)))
398  return;
399  }
400  cSkinQueuedMessage *msg = NULL;
401  // Get the first waiting message:
403  for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
404  if (m->state == 0) { // waiting
405  m->state = 1; // active
406  msg = m;
407  break;
408  }
409  }
411  // Display the message:
412  if (msg) {
413  msg->mutex.Lock();
414  if (msg->state == 1) { // might have changed since we got it
415  msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
416  if (msg->timeout == 0)
417  msg->state = 2; // done
418  else
419  msg->condVar.Broadcast();
420  }
421  msg->mutex.Unlock();
422  }
423  // Remove done messages from the queue:
425  for (;;) {
427  if (m && m->state == 2) { // done
428  SkinQueuedMessages.Del(m);
429  }
430  else
431  break;
432  }
434 }
435 
436 void cSkins::Flush(void)
437 {
438  if (cSkinDisplay::Current())
440 }
441 
442 void cSkins::Clear(void)
443 {
444  if (displayMessage) {
445  delete displayMessage;
446  displayMessage = NULL;
447  }
449 }
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:133
void Broadcast(void)
Definition: thread.c:151
Definition: font.h:37
static const cFont * GetFont(eDvbFont Font)
Gets the given Font, which was previously set by a call to SetFont().
Definition: font.c:412
eKeys Wait(int Seconds=0, bool KeepChar=false)
Definition: interface.c:41
virtual void Clear(void)
Definition: tools.c:2252
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2175
Definition: tools.h:631
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:650
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:643
int Position(void) const
Definition: recording.h:389
Definition: thread.h:67
void Lock(void)
Definition: thread.c:223
void Unlock(void)
Definition: thread.c:229
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:1176
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) override=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...
Definition: skins.h:70
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:189
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
void Error(int x, tColor ColorError)
Definition: skins.c:199
virtual void SetErrors(const cErrors *Errors)
Sets the errors found in the recording to Errors, which shall be used to display the progress bar thr...
Definition: skins.c:230
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:225
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:220
const cErrors * errors
Definition: skins.h:318
cSkinDisplayReplay(void)
Definition: skins.c:214
const cMarks * marks
< This class implements the progress display used during replay of a recording.
Definition: skins.h:317
static cSkinDisplay * current
Definition: skins.h:41
virtual void Flush(void)
Actually draws the OSD display to the output device.
Definition: skins.h:59
static cSkinDisplay * Current(void)
Returns the currently active cSkinDisplay.
Definition: skins.h:61
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
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
virtual ~cSkinQueuedMessage() override
Definition: skins.c:44
cCondVar condVar
Definition: skins.c:27
Definition: skins.h:402
virtual ~cSkin() override
Definition: skins.c:246
cSkin(const char *Name, cTheme *Theme=NULL)
Creates a new skin class, with the given Name and Theme.
Definition: skins.c:237
Definition: skins.h:457
bool SetCurrent(const char *Name=NULL)
Sets the current skin to the one indicated by name.
Definition: skins.c:265
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:284
void Flush(void)
Flushes the currently active cSkinDisplay, if any.
Definition: skins.c:436
~cSkins()
Definition: skins.c:260
cSkin * Current(void)
Returns a pointer to the current skin.
Definition: skins.h:468
cMutex queueMessageMutex
Definition: skins.h:461
cSkinDisplayMessage * displayMessage
Definition: skins.h:460
void ProcessQueuedMessages(void)
Processes the first queued message, if any.
Definition: skins.c:386
cSkins(void)
Definition: skins.c:255
virtual void Clear(void) override
Free up all registered skins.
Definition: skins.c:442
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:330
static void MsgOsdStatusMessage(const char *Message)
Definition: status.h:125
static void MsgOsdClear(void)
Definition: status.c:93
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1195
void Scroll(bool Up, bool Page)
Definition: osd.c:2451
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:373
int Size(void) const
Definition: tools.h:754
T & At(int Index) const
Definition: tools.h:731
@ fontOsd
Definition: font.h:22
@ fontFix
Definition: font.h:23
uint32_t tColor
Definition: font.h:29
#define tr(s)
Definition: i18n.h:85
cInterface * Interface
Definition: interface.c:20
eKeys
Definition: keys.h:16
@ kNone
Definition: keys.h:55
@ clrBlack
Definition: osd.h:34
static cTheme Theme
Definition: skinclassic.c:21
cList< cSkinQueuedMessage > SkinQueuedMessages
Definition: skins.c:49
cSkins Skins
Definition: skins.c:253
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:357
#define dsyslog(a...)
Definition: tools.h:37
#define esyslog(a...)
Definition: tools.h:35
#define isyslog(a...)
Definition: tools.h:36