This is a "patch" for the Video Disk Recorder (VDR). * History 2026-02-07: Version 1.0.5 Update for VDR 2.7.9 (Stefan Hofmann) 2025-02-26: Version 1.0.4 Update for VDR 2.7.4 (Jörg Riechardt) 2013-11-08: Version 1.0.3 Update for VDR 2.1.2 (Jörg Riechardt) 2012-04-06: Version 1.0.2 - Update for recent VDR developer versions (Manuel Reimer) 2010-10-15: Version 1.0.1 - return a cOsdObject instead of its subclass cOsdMenu (thanks to Joe_D@vdrportal) - version number defines in 'config.h' now follow the usual conventions: MAINMENUHOOKSVERSNUM is now a number, the newly added define MAINMENUHOOKSVERSION is a string (suggested by gnapheus@vdrportal) - patch is now based on VDR 1.6.0 - updated documentation 2007-02-26: Version 1.0 - Initial revision. * Authors: Tobias Grimm Martin Prochnow Frank Schmirler Christian Wieninger * Description: This patch allows plugins to replace the VDR main menus "Schedule", "Channels", "Timers" and "Recordings" by a different implementation. The patch is based on a suggestion of Christian Wieninger back in 2006 (http://www.linuxtv.org/pipermail/vdr/2006-March/008234.html). It is meant to be an interim solution for VDR 1.4 until (maybe) VDR 1.5 introduces an official API for this purpose. * Installation Change into the VDR source directory, then issue patch -p1 < path/to/MainMenuHooks-v1_0_1.patch and recompile. * Notes for plugin authors The following code sample shows the required plugin code for replacing the original Schedule menu: bool cMyPlugin::Service(const char *Id, void *Data) { cOsdMenu **menu = (cOsdMenu**) Data; if (MySetup.replaceSchedule && strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0) { if (menu) *menu = (cOsdMenu*) MainMenuAction(); return true; } return false; } Since patch version 1.0.1, the service call may return a cOsdObject instead of a 'cOsdMenu'. Use '#ifdef MAINMENUHOOKSVERSION' to detect version 1.0.1 or later. A plugin can replace more than one menu at a time. Simply replace the call to MainMenuAction() in the sample above by appropriate code. Note that a plugin *should* offer a setup option which allows the user to enable or disable the replacement. "Disabled" would be a reasonable default setting. By testing for a specific 'MAINMENUHOOKSVERSNUM', like '#if MAINMENUHOOKSVERSION >= 10001', plugin can decide at compile time which features and setup options to provide. In case there is an internal problem when trying to open the replacement menu, it is safe to return 'true' even though 'Data' is NULL. However an OSD message should indicate the problem to the user. Feel free to ship this patch along with your plugin. However if you think you need to modify the patch, we'd encourage you to contact the authors first or at least use a service id which differs in more than just the version number. --- config.h | 4 ++++ menu.c | 71 +++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/config.h b/config.h index 96fd5dd7..fddcdc21 100644 --- a/config.h +++ b/config.h @@ -35,6 +35,10 @@ // only when there are changes to the plugin API. This allows compiled // plugins to work with newer versions of the core VDR as long as no // interfaces have changed. APIVERSNUM begins with "300.." for backwards + +// The MainMenuHook Patch's version number: +#define MAINMENUHOOKSVERSION "1.0.5" +#define MAINMENUHOOKSVERSNUM 10005 // Version * 10000 + Major * 100 + Minor // compatibility and can be used in #if preprocessor statements to handle // version dependent code. diff --git a/menu.c b/menu.c index 8798a3f1..57555d3e 100644 --- a/menu.c +++ b/menu.c @@ -4671,15 +4671,31 @@ cMenuMain::cMenuMain(eOSState State, bool OpenSubMenus) // Initial submenus: + cOsdObject *menu = NULL; switch (State) { - case osSchedule: AddSubMenu(new cMenuSchedule); break; - case osChannels: AddSubMenu(new cMenuChannels); break; - case osTimers: AddSubMenu(new cMenuTimers); break; - case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, OpenSubMenus)); break; - case osSetup: AddSubMenu(new cMenuSetup); break; - case osCommands: AddSubMenu(new cMenuCommands(tr("Button$Commands"), &Commands)); break; + case osSchedule: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu)) + menu = new cMenuSchedule; + break; + case osChannels: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu)) + menu = new cMenuChannels; + break; + case osTimers: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu)) + menu = new cMenuTimers; + break; + case osRecordings: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu)) + menu = new cMenuRecordings(NULL, 0, OpenSubMenus); + break; + case osSetup: menu = new cMenuSetup; break; + case osCommands: menu = new cMenuCommands(tr("Button$Commands"), &Commands); break; default: break; } + if (menu) + if (menu->IsMenu()) + AddSubMenu((cOsdMenu *) menu); } cOsdObject *cMenuMain::PluginOsdObject(void) @@ -4808,15 +4824,36 @@ eOSState cMenuMain::ProcessKey(eKeys Key) eOSState state = cOsdMenu::ProcessKey(Key); HadSubMenu |= HasSubMenu(); + cOsdObject *menu = NULL; switch (state) { - case osSchedule: return AddSubMenu(new cMenuSchedule); - case osChannels: return AddSubMenu(new cMenuChannels); - case osTimers: return AddSubMenu(new cMenuTimers); - case osRecordings: return AddSubMenu(new cMenuRecordings); - case osRecsOpen: return AddSubMenu(new cMenuRecordings(NULL, 0, true)); - case osRecsDel: return AddSubMenu(new cMenuRecordings(NULL, 0, true, NULL, true)); - case osSetup: return AddSubMenu(new cMenuSetup); - case osCommands: return AddSubMenu(new cMenuCommands(tr("Button$Commands"), &Commands)); + case osSchedule: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu)) + menu = new cMenuSchedule; + else + state = osContinue; + break; + case osChannels: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu)) + menu = new cMenuChannels; + else + state = osContinue; + break; + case osTimers: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu)) + menu = new cMenuTimers; + else + state = osContinue; + break; + case osRecordings: + if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu)) + menu = new cMenuRecordings; + else + state = osContinue; + break; + case osRecsOpen: menu = new cMenuRecordings(NULL, 0, true); break; + case osRecsDel: menu = new cMenuRecordings(NULL, 0, true, NULL, true); break; + case osSetup: menu = new cMenuSetup; break; + case osCommands: menu = new cMenuCommands(tr("Button$Commands"), &Commands); break; case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) { if (cOsdItem *item = Get(Current())) { cRecordControls::Stop(item->Text() + strlen(tr(STOP_RECORDING))); @@ -4867,6 +4904,12 @@ eOSState cMenuMain::ProcessKey(eKeys Key) default: break; } } + if (menu) { + if (menu->IsMenu()) + return AddSubMenu((cOsdMenu *) menu); + pluginOsdObject = menu; + return osPlugin; + } bool DoDisplay = Update(); if (Key != kNone) { if (I18nCurrentLanguage() != osdLanguage) { -- 2.43.0