diff options
author | Klaus Schmidinger <vdr@tvdr.de> | 2005-11-27 15:57:03 +0100 |
---|---|---|
committer | Klaus Schmidinger <vdr@tvdr.de> | 2005-11-27 15:57:03 +0100 |
commit | 41f718987d625d2938c6ed0827759b217fda63a2 (patch) | |
tree | 4e8c4ac750e83cc9bf49accbc92b5680ac5dd976 | |
parent | 074de78f0430271e5c6e74057f2fc510b9618ac9 (diff) | |
download | vdr-41f718987d625d2938c6ed0827759b217fda63a2.tar.gz vdr-41f718987d625d2938c6ed0827759b217fda63a2.tar.bz2 |
Implemented message queueing1.3.37
-rw-r--r-- | HISTORY | 7 | ||||
-rw-r--r-- | PLUGINS.html | 46 | ||||
-rw-r--r-- | interface.c | 9 | ||||
-rw-r--r-- | skins.c | 129 | ||||
-rw-r--r-- | skins.h | 33 | ||||
-rw-r--r-- | svdrp.c | 32 | ||||
-rw-r--r-- | svdrp.h | 4 | ||||
-rw-r--r-- | thread.c | 3 | ||||
-rw-r--r-- | thread.h | 7 | ||||
-rw-r--r-- | vdr.c | 5 |
10 files changed, 216 insertions, 59 deletions
@@ -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"). diff --git a/PLUGINS.html b/PLUGINS.html index d1c787b3..bdaf1efb 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -14,18 +14,18 @@ Copyright © 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> </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> </td><td width=100%> +<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </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> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </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> </td><td width=100%> +<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </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> </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> </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> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </td><td width=100%> +<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </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); diff --git a/interface.c b/interface.c index 4f18ddda..56084784 100644 --- a/interface.c +++ b/interface.c @@ -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); } @@ -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()) @@ -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. }; @@ -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??? @@ -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 @@ -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) @@ -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 @@ -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); |