Feature request: suggestion for cPlugin
Commit Message
Udo Richter wrote:
> New patch including docs etc. later this day.
... as promised. Custom plugin services, including PLUGINS.html and
newplugin update. This version still mentions Data=NULL as supported query.
Cheers,
Udo
Comments
Udo Richter wrote:
> Udo Richter wrote:
>
>>New patch including docs etc. later this day.
>
>
> ... as promised. Custom plugin services, including PLUGINS.html and
> newplugin update. This version still mentions Data=NULL as supported query.
> ...
> +To send messages to, or request services from some plugin that offers the protocol, the
> +plugin can call the function <tt>cPluginManager::CallFirstService</tt>. This function
> +will send the request to the first plugin that supports this service protocol. The
Actually it sends the call to _every_ plugin and returns as soon as one
has processed it. Should this be rephrased, or should the implementation
be changed to
cPlugin *cPluginManager::CallFirstService(const char *Id, void *Data)
{
if (pluginManager) {
for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
cPlugin *p = dll->Plugin();
if (p && p->Service(Id, NULL) && p->Service(Id, Data))
return p;
}
}
return NULL;
}
> +function returns a pointer to the plugin that handled the request, or <tt>NULL</tt>
> +if no plugin handles the request.
> +<p>
> +To send a messages to all plugins, the plugin can call the function
> +<tt>cPluginManager::CallAllServices</tt>. This function will send the request to
> +all plugins that support this service protocol.
Actually it sends the request to _all_ plugins, because it doesn't explicitly
check whether a particular plugin supports it. In this case this doesn't make
much difference, but I think the description should reflect what actually happens.
Klaus
Klaus Schmidinger wrote:
> Actually it sends the call to _every_ plugin and returns as soon as one
> has processed it. Should this be rephrased, or should the implementation
> be changed to
>
> if (p && p->Service(Id, NULL) && p->Service(Id, Data))
> return p;
I would prefer to not call the function twice, to avoid the redundant
string compare.
----8<----
To send messages to, or request services from some plugin that offers
the protocol, the plugin can call the function
<tt>cPluginManager::CallFirstService</tt>. This function will send the
request to all plugins until one plugin handles the service call. The
function returns a pointer to the plugin that handled the call, or
<tt>NULL</tt> if no plugin handled the call.
<p>
To send a messages to all plugins, the plugin can call the function
<tt>cPluginManager::CallAllServices</tt>. The function returns
<tt>true</tt> if any plugin handled the service call, or <tt>false</tt>
if no plugin handled the call.
----8<----
Cheers,
Udo
@@ -170,6 +170,7 @@
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
+ virtual bool Service(const char *Id, void *Data = NULL);
};
cPlugin${PLUGIN_CLASS}::cPlugin$PLUGIN_CLASS(void)
@@ -236,6 +237,12 @@
return false;
}
+bool cPlugin${PLUGIN_CLASS}::Service(const char *Id, void *Data)
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+
VDRPLUGINCREATOR(cPlugin$PLUGIN_CLASS); // Don't touch this!
};
@@ -99,6 +99,11 @@
Setup.Store(Name, Value, this->Name());
}
+bool cPlugin::Service(const char *Id, void *Data)
+{
+ return false;
+}
+
void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases)
{
I18nRegister(Phrases, Name());
@@ -372,6 +377,31 @@
return NULL;
}
+cPlugin *cPluginManager::CallFirstService(const char *Id, void *Data)
+{
+ if (pluginManager) {
+ for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
+ cPlugin *p = dll->Plugin();
+ if (p)
+ if (p->Service(Id, Data)) return p;
+ }
+ }
+ return NULL;
+}
+
+bool cPluginManager::CallAllServices(const char *Id, void *Data)
+{
+ bool found=false;
+ if (pluginManager) {
+ for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
+ cPlugin *p = dll->Plugin();
+ if (p)
+ if (p->Service(Id, Data)) found=true;
+ }
+ }
+ return found;
+}
+
void cPluginManager::StopPlugins(void)
{
for (cDll *dll = dlls.Last(); dll; dll = dlls.Prev(dll)) {
@@ -50,6 +50,8 @@
void RegisterI18n(const tI18nPhrase * const Phrases);
+ virtual bool Service(const char *Id, void *Data = NULL);
+
static void SetConfigDirectory(const char *Dir);
static const char *ConfigDirectory(const char *PluginName = NULL);
};
@@ -88,6 +90,8 @@
static bool HasPlugins(void);
static cPlugin *GetPlugin(int Index);
static cPlugin *GetPlugin(const char *Name);
+ static cPlugin *CallFirstService(const char *Id, void *Data = NULL);
+ static bool CallAllServices(const char *Id, void *Data = NULL);
void StopPlugins(void);
void Shutdown(void);
};
@@ -26,6 +26,9 @@
<!--X1.3.21--><table width=100%><tr><td bgcolor=#FF0000> </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=#00AAAA> </td><td width=100%>
+Important modifications introduced in version 1.3.30 are marked like this.
+<!--X1.3.30--></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.
@@ -68,6 +71,9 @@
<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=#00AAAA> </td><td width=100%>
+<li><a href="#Custom services">Custom services</a>
+<!--X1.3.30--></td></tr></table>
<li><a href="#Loading plugins into VDR">Loading plugins into VDR</a>
<li><a href="#Building the distribution package">Building the distribution package</a>
</ul>
@@ -866,6 +872,77 @@
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=#00AAAA> </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>
+
+In some situations, two plugins may want to communicate directly, talking about things
+that VDR doesnt handle yet as mediator. For example, a plugin may want to use features
+that a different plugin offers, or a plugin wants to inform other plugins about important
+things it does. To receive requests or messages, a plugin can implement the
+following function:
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+virtual bool Service(const char *Id, void *Data = NULL);
+</pre></td></tr></table><p>
+
+<tt>Id</tt> is an unique identification string that identifies the service protocol.
+To avoid collisions, the string should contain a service name, the plugin name (unless
+the service is not related to a single plugin) and a protocol version number.
+<tt>Data</tt> points to a custom data structure or is <tt>NULL</tt> to detect whether
+the plugin supports this service. For each id string there should be a specification
+that describes the format of the data structure, and any change to the format should
+be reflected by a change of the id string.
+<p>
+The function shall return true for any service ID string it handles, and false
+otherwise. The function shall not perform any actions as long as <tt>Data</tt> is
+<tt>NULL</tt>. The plugins have to agreee in which situations the service
+may be called, for example whether the service may be called from every thread, or
+just from the main thread. A possible implementation could look like this:
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+struct Hello_SetGreetingTime_v1_0 {
+ int NewGreetingTime;
+};
+
+bool cPluginHello::Service(const char *Id, void *Data)
+{
+ if (strcmp(Id, "Hello-SetGreetingTime-v1.0") == 0)
+ {
+ if (Data == NULL) return true;
+ GreetingTime = ((Hello_SetGreetingTime_v1_0*)Data)->NewGreetingTime;
+ return true;
+ }
+ return false;
+}
+</pre></td></tr></table><p>
+
+<p>
+To send messages to, or request services from a specific plugin, the plugin can directly call its
+service function:
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+Hello_SetGreetingTime_v1_0 hellodata;
+hellodata.NewGreetingTime = 3;
+cPlugin *Plugin = cPluginManager::GetPlugin("hello");
+if (Plugin)
+ Plugin->Service("Hello-SetGreetingTime-v1.0", &hellodata);
+</pre></td></tr></table><p>
+
+To send messages to, or request services from some plugin that offers the protocol, the
+plugin can call the function <tt>cPluginManager::CallFirstService</tt>. This function
+will send the request to the first plugin that supports this service protocol. The
+function returns a pointer to the plugin that handled the request, or <tt>NULL</tt>
+if no plugin handles the request.
+<p>
+To send a messages to all plugins, the plugin can call the function
+<tt>cPluginManager::CallAllServices</tt>. This function will send the request to
+all plugins that support this service protocol. The function returns <tt>true</tt> if
+any plugin handled the request, or <tt>false</tt> if no plugin handled the request.
+
+<!--X1.3.30--></td></tr></table>
+
<a name="Loading plugins into VDR"><hr><h2>Loading plugins into VDR</h2>
<center><i><b>Saddling up!</b></i></center><p>