1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented message queueing

This commit is contained in:
Klaus Schmidinger 2005-11-27 15:57:03 +01:00
parent 074de78f04
commit 41f718987d
10 changed files with 216 additions and 59 deletions

View File

@ -3934,7 +3934,7 @@ Video Disk Recorder Revision History
layers of subdirectories.
- Removed EPG bugfix #0, because it removed actually important data.
2005-11-26: Version 1.3.37
2005-11-27: Version 1.3.37
- Added compiler options "-fPIC -g" to all plugins (thanks to Rolf Ahrenberg).
- Fixed initializing the day index when editing the weekday parameter of a
@ -3957,3 +3957,8 @@ Video Disk Recorder Revision History
by Stefan Huelswitt).
- Added a copy constructor to cString and fixed its assignment operator
(thanks to Holger Brunn).
- The new function Skins.QueueMessage() can be called from a background thread
to queue a message for display. See VDR/skins.h for details.
- The SVDRP command MESG uses the new message queueing facility, so MESG
commands may now be executed at any time, and the message will be displayed
(no more "pending message").

View File

@ -14,18 +14,18 @@ Copyright &copy; 2005 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.20 are marked like this.
<!--X1.3.20--></td></tr></table>
<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.21 are marked like this.
<!--X1.3.21--></td></tr></table>
<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.30 are marked like this.
<!--X1.3.30--></td></tr></table>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.31 are marked like this.
<!--X1.3.31--></td></tr></table>
<!--X1.3.37--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.37 are marked like this.
<!--X1.3.37--></td></tr></table>
<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
@ -58,9 +58,7 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Command line arguments">Command line arguments</a>
<li><a href="#Command line help">Command line help</a>
<li><a href="#Getting started">Getting started</a>
<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<li><a href="#Shutting down">Shutting down</a>
<!--X1.3.20--></td></tr></table>
<li><a href="#Main menu entry">Main menu entry</a>
<li><a href="#User interaction">User interaction</a>
<li><a href="#Housekeeping">Housekeeping</a>
@ -68,10 +66,10 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#The Setup menu">The Setup menu</a>
<li><a href="#Configuration files">Configuration files</a>
<li><a href="#Internationalization">Internationalization</a>
<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<li><a href="#Custom services">Custom services</a>
<!--X1.3.30--></td></tr></table>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<li><a href="#SVDRP commands">SVDRP commands</a>
<!--X1.3.31--></td></tr></table>
<li><a href="#Loading plugins into VDR">Loading plugins into VDR</a>
@ -87,7 +85,7 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Skins">Skins</a>
<li><a href="#Themes">Themes</a>
<li><a href="#Devices">Devices</a>
<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<li><a href="#Audio">Audio</a>
<!--X1.3.21--></td></tr></table>
<li><a href="#Remote Control">Remote Control</a>
@ -314,10 +312,8 @@ since VDR, for instance, has to create the plugin objects in order to get their
command line help - and after that immediately destroys them again.
<p>
The <b>destructor</b> has to clean up any data created by the plugin.
<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Any threads the plugin may have created shall be stopped in the
<a href="#Shutting down"><tt>Stop()</tt></a> function.
<!--X1.3.20--></td></tr></table>
<p>
Of course, if your plugin doesn't define any member variables that need to be
initialized (and deleted), you don't need to implement either of these functions.
@ -512,7 +508,6 @@ VDR to exit.
If the plugin doesn't implement any background functionality or internationalized
texts, it doesn't need to implement either of these functions.
<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<a name="Shutting down"><hr><h2>Shutting down</h2>
<center><i><b>Stop it, right there!</b></i></center><p>
@ -529,7 +524,6 @@ The <tt>Stop()</tt> function will only be called if a previous call to the
<a href="#Getting started"><tt>Start()</tt></a> function of that plugin has
returned <i>true</i>. The <tt>Stop()</tt> functions are called in the reverse order
as the <a href="#Getting started"><tt>Start()</tt></a> functions were called.
<!--X1.3.20--></td></tr></table>
<a name="Main menu entry"><hr><h2>Main menu entry</h2>
@ -872,7 +866,7 @@ Texts are first searched for in the <i>Phrases</i> registered for this plugin (i
and then in the global VDR texts. So a plugin can make use of texts defined by the
core VDR code.
<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<a name="Custom services"><hr><h2>Custom services</h2>
<center><i><b>What can I do for you?</b></i></center><p>
@ -943,7 +937,7 @@ any plugin handled the request, or <tt>false</tt> if no plugin handled the reque
<!--X1.3.30--></td></tr></table>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<a name="SVDRP commands"><hr><h2>SVDRP commands</h2>
<center><i><b>Infinite Diversity in Infinite Combinations</b></i></center><p>
@ -1521,6 +1515,22 @@ with the full required resolution. Only if this fails shall it use alternate
areas. Drawing areas are always rectangular and may not overlap (but do not need
to be adjacent).
<!--X1.3.37--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<p>
Directly accessing the OSD is only allowed from the foreground thread, which
restricts this to a <tt>cOsdObject</tt> returned from the plugin's <tt>MainMenuAction()</tt>
function, or any of the skin classes a plugin might implement.
<p>
If a plugin runs a separate thread and wants to issue a message directly from
within that tread, it can call
<p><table><tr><td bgcolor=#F0F0F0><pre>
int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds = 0, int Timeout = 0);
</pre></td></tr></table><p>
to queue that message for display. See <tt>VDR/skins.h</tt> for details.
<!--X1.3.37--></td></tr></table>
<a name="Skins"><hr><h2>Skins</h2>
<center><i><b>The emperor's new clothes</b></i></center><p>
@ -1830,7 +1840,7 @@ private:
virtual void Action(void);
public:
cMyAudio(void);
<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
virtual void Play(const uchar *Data, int Length, uchar Id);
<!--X1.3.21--></td></tr></table>
virtual void Mute(bool On);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.c 1.69 2005/09/03 09:07:23 kls Exp $
* $Id: interface.c 1.70 2005/11/27 15:31:06 kls Exp $
*/
#include "interface.h"
@ -36,13 +36,6 @@ eKeys cInterface::GetKey(bool Wait)
if (SVDRP) {
if (SVDRP->Process())
Wait = false;
if (!Skins.IsOpen()) {
char *message = SVDRP->GetMessage();
if (message) {
Skins.Message(mtInfo, message);
free(message);
}
}
}
return cRemote::Get(Wait ? 1000 : 10);
}

129
skins.c
View File

@ -4,13 +4,49 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skins.c 1.5 2005/10/02 10:12:10 kls Exp $
* $Id: skins.c 1.6 2005/11/27 15:52:25 kls Exp $
*/
#include "skins.h"
#include "interface.h"
#include "status.h"
#include "tools.h"
// --- cSkinQueuedMessage ----------------------------------------------------
class cSkinQueuedMessage : public cListObject {
friend class cSkins;
private:
eMessageType type;
char *message;
int seconds;
int timeout;
tThreadId threadId;
eKeys key;
int state;
cMutex mutex;
cCondVar condVar;
public:
cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
virtual ~cSkinQueuedMessage();
};
cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
type = Type;
message = s ? strdup(s) : NULL;
seconds = Seconds;
timeout = Timeout;
threadId = cThread::ThreadId();
key = kNone;
state = 0; // waiting
}
cSkinQueuedMessage::~cSkinQueuedMessage()
{
free(message);
}
cList<cSkinQueuedMessage> SkinQueuedMessages;
// --- cSkinDisplay ----------------------------------------------------------
@ -202,6 +238,95 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
return k;
}
int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
if (Type == mtStatus) {
dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
return kNone;
}
if (isempty(s)) {
dsyslog("cSkins::QueueMessage() called with empty message - ignored!");
return kNone;
}
int k = kNone;
if (Timeout > 0) {
if (cThread::IsMainThread()) {
dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
return k;
}
cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
queueMessageMutex.Lock();
SkinQueuedMessages.Add(m);
m->mutex.Lock();
queueMessageMutex.Unlock();
if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
k = m->key;
else
k = -1; // timeout, nothing has been displayed
m->state = 2; // done
m->mutex.Unlock();
}
else {
queueMessageMutex.Lock();
// Check if there is a waiting message w/o timeout for this thread:
if (Timeout == -1) {
for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
if (m->threadId == cThread::ThreadId()) {
if (m->state == 0 && m->timeout == -1)
m->state = 2; // done
break;
}
}
}
// Add the new message:
SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
queueMessageMutex.Unlock();
}
return k;
}
void cSkins::ProcessQueuedMessages(void)
{
if (!cThread::IsMainThread()) {
dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
return;
}
cSkinQueuedMessage *msg = NULL;
// Get the first waiting message:
queueMessageMutex.Lock();
for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
if (m->state == 0) { // waiting
m->state = 1; // active
msg = m;
break;
}
}
queueMessageMutex.Unlock();
// Display the message:
if (msg) {
msg->mutex.Lock();
if (msg->state == 1) { // might have changed since we got it
msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
if (msg->timeout == 0)
msg->state = 2; // done
else
msg->condVar.Broadcast();
}
msg->mutex.Unlock();
}
// Remove done messages from the queue:
queueMessageMutex.Lock();
for (;;) {
cSkinQueuedMessage *m = SkinQueuedMessages.First();
if (m && m->state == 2) { // done
SkinQueuedMessages.Del(m);
}
else
break;
}
queueMessageMutex.Unlock();
}
void cSkins::Flush(void)
{
if (cSkinDisplay::Current())

33
skins.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skins.h 1.8 2005/05/15 14:41:41 kls Exp $
* $Id: skins.h 1.9 2005/11/27 15:41:44 kls Exp $
*/
#ifndef __SKINS_H
@ -16,6 +16,7 @@
#include "osd.h"
#include "recording.h"
#include "themes.h"
#include "thread.h"
#include "tools.h"
enum eMessageType { mtStatus = 0, mtInfo, mtWarning, mtError }; // will be used to calculate color offsets!
@ -298,6 +299,7 @@ class cSkins : public cList<cSkin> {
private:
cSkin *current;
cSkinDisplayMessage *displayMessage;
cMutex queueMessageMutex;
public:
cSkins(void);
~cSkins();
@ -312,6 +314,35 @@ public:
///< Displays the given message, either through a currently visible
///< display object that is capable of doing so, or by creating a
///< temporary cSkinDisplayMessage object.
///< The return value is the key pressed by the user. If no user input
///< has been received within Seconds (the default value of 0 results
///< in the ///< value defined for "Message time" in the setup), kNone
///< will be returned.
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. The given message is put into a queue and the main program
///< loop will display it as soon as this is suitable. If Timeout is 0,
///< QueueMessage() returns immediately and the return value will be kNone.
///< If a positive Timeout is given, the thread will wait at most the given
///< number of seconds for the message to be actually displayed (note that
///< the user may currently be doing something that doesn't allow for
///< queued messages to be displayed immediately). If the timeout expires
///< and the message hasn't been displayed yet, the return value is -1
///< and the message will be removed from the queue without being displayed.
///< Positive values of Timeout are only allowed for background threads.
///< If QueueMessage() is called from the foreground thread with a Timeout
///< greater than 0, the call is ignored and nothing is displayed.
///< Queued messages will be displayed in the sequence they have been
///< put into the queue, so messages from different threads may appear
///< mingled. If a particular thread queues a message with a Timeout of
///< -1, and the previous message from the same thread also had a Timeout
///< of -1, only the last message will be displayed. This can be used for
///< progress displays, where only the most recent message is actually
///< important.
///< Type may only be mtInfo, mtWarning or mtError. A call with mtStatus
///< will be ignored, as will be one with an empty message.
void ProcessQueuedMessages(void);
///< Processes the first queued message, if any.
void Flush(void);
///< Flushes the currently active cSkinDisplay, if any.
};

32
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
* $Id: svdrp.c 1.83 2005/11/05 11:21:38 kls Exp $
* $Id: svdrp.c 1.84 2005/11/27 15:29:28 kls Exp $
*/
#include "svdrp.h"
@ -35,6 +35,7 @@
#include "menu.h"
#include "plugin.h"
#include "remote.h"
#include "skins.h"
#include "timers.h"
#include "tools.h"
#include "videodir.h"
@ -225,12 +226,9 @@ const char *HelpPages[] = {
"LSTT [ <number> ]\n"
" List timers. Without option, all timers are listed. Otherwise\n"
" only the given timer is listed.",
"MESG [ <message> ]\n"
" Displays the given message on the OSD. If message is omitted, the\n"
" currently pending message (if any) will be returned. The message\n"
" will be displayed for a few seconds as soon as the OSD has become\n"
" idle. If a new MESG command is entered while the previous message\n"
" has not yet been displayed, the old message will be overwritten.",
"MESG <message>\n"
" Displays the given message on the OSD. The message will be queued\n"
" and displayed whenever this is suitable.\n",
"MODC <number> <settings>\n"
" Modify a channel. Settings must be in the same format as returned\n"
" by the LSTC command.",
@ -363,7 +361,6 @@ cSVDRP::cSVDRP(int Port)
numChars = 0;
length = BUFSIZ;
cmdLine = MALLOC(char, length);
message = NULL;
lastActivity = 0;
isyslog("SVDRP listening on port %d", Port);
}
@ -371,7 +368,6 @@ cSVDRP::cSVDRP(int Port)
cSVDRP::~cSVDRP()
{
Close();
free(message);
free(cmdLine);
}
@ -954,15 +950,12 @@ void cSVDRP::CmdLSTT(const char *Option)
void cSVDRP::CmdMESG(const char *Option)
{
if (*Option) {
free(message);
message = strdup(Option);
isyslog("SVDRP message: '%s'", message);
Reply(250, "Message stored");
isyslog("SVDRP message: '%s'", Option);
Skins.QueueMessage(mtInfo, Option);
Reply(250, "Message queued");
}
else if (message)
Reply(250, "%s", message);
else
Reply(550, "No pending message");
Reply(501, "Missing message");
}
void cSVDRP::CmdMODC(const char *Option)
@ -1489,11 +1482,4 @@ bool cSVDRP::Process(void)
return false;
}
char *cSVDRP::GetMessage(void)
{
char *s = message;
message = NULL;
return s;
}
//TODO more than one connection???

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: svdrp.h 1.25 2005/11/05 10:54:22 kls Exp $
* $Id: svdrp.h 1.26 2005/11/27 15:26:42 kls Exp $
*/
#ifndef __SVDRP_H
@ -48,7 +48,6 @@ private:
int numChars;
int length;
char *cmdLine;
char *message;
time_t lastActivity;
void Close(bool Timeout = false);
bool Send(const char *s, int length = -1);
@ -88,7 +87,6 @@ public:
~cSVDRP();
bool HasConnection(void) { return file.IsOpen(); }
bool Process(void);
char *GetMessage(void);
};
#endif //__SVDRP_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.c 1.45 2005/08/14 11:15:42 kls Exp $
* $Id: thread.c 1.46 2005/11/27 15:15:53 kls Exp $
*/
#include "thread.h"
@ -193,6 +193,7 @@ void cMutex::Unlock(void)
// --- cThread ---------------------------------------------------------------
tThreadId cThread::mainThreadId = cThread::ThreadId();
bool cThread::emergencyExitRequested = false;
cThread::cThread(const char *Description)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.h 1.31 2005/10/09 11:12:32 kls Exp $
* $Id: thread.h 1.32 2005/11/27 15:16:50 kls Exp $
*/
#ifndef __THREAD_H
@ -72,6 +72,8 @@ public:
void Unlock(void);
};
typedef pthread_t tThreadId;
class cThread {
friend class cThreadLock;
private:
@ -80,6 +82,7 @@ private:
pthread_t childTid;
cMutex mutex;
char *description;
static tThreadId mainThreadId;
static bool emergencyExitRequested;
static void *StartThread(cThread *Thread);
protected:
@ -112,6 +115,8 @@ public:
bool Active(void);
///< Checks whether the thread is still alive.
static bool EmergencyExit(bool Request = false);
static tThreadId ThreadId(void) { return pthread_self(); }
static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
};
// cMutexLock can be used to easily set a lock on mutex and make absolutely

5
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.219 2005/11/04 13:48:39 kls Exp $
* $Id: vdr.c 1.220 2005/11/27 15:56:18 kls Exp $
*/
#include <getopt.h>
@ -677,6 +677,9 @@ int main(int argc, char *argv[])
else if (!LastCamMenu)
LastCamMenu = time(NULL);
}
// Queued messages:
if (!Skins.IsOpen())
Skins.ProcessQueuedMessages();
// User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control();
eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);