Skip to main content

Plugin Lifecycle

A plugin is a class that implements IPlugin. In practice you derive from PluginBase, which provides ordered scaffolding and a set of named hooks to override. This page covers the full lifecycle and when each hook runs.

IPlugin vs PluginBase

IPlugin is the raw interface the host knows about:

class IPlugin
{
public:
virtual void Install(IPluginContext& ctx) {}
virtual void BindHotkeys(Hotkeys& keys) {}
virtual bool OnEvent(const AppEvent& e); // dispatches to OnKeyDownEvent
virtual bool OnKeyDownEvent(const AppEvent&) { return false; }
virtual void OnMediaEvent(const MediaEvent&) {}
virtual void OnShutdown() {}
};

You can implement IPlugin directly, but then you are responsible for doing setup in the right order. PluginBase seals Install() and BindHotkeys() so the standard scaffolding (storing the context, setting the ImGui context, loading settings, registering keybinds) always runs correctly, and exposes named hooks for your code instead.

Hooks, in order

When the host installs a PluginBase plugin, Install() runs this sequence:

  1. Stores the context pointer in ctx_ (available to all later hooks).
  2. Sets the ImGui context (so your rendering uses the host's UI state).
  3. Calls LoadSettings(ps) — read your fields from your INI section.
  4. Calls RegisterKeybinds(ctx) — declare keybind entries for the UI.
  5. Calls OnInstall(ctx) — your main setup.

Later, separately:

  1. BindHotkeysOnBindHotkeys(keys) — bind action handlers, after all plugins are installed.
  2. OnMediaEvent, OnKeyDownEvent, render hooks — during the main loop.
  3. OnShutdown — once, after the main loop exits.

The hooks you override

HookPurpose
const char* PluginName()Required. Identifies the plugin: INI section, settings page title, log label.
OnInstall(IPluginContext&)Main setup: register services, subscribe to events, add context-menu items.
LoadSettings(IPluginSettings&)Read member fields from your INI section on startup.
SaveSettings(IPluginSettings&)Write member fields back on Save.
RenderSettings(UIContext&)Draw your settings page's widgets.
RegisterKeybinds(IPluginContext&)Register keybind entries shown in the keybind UI.
LoadKeybinds / SaveKeybindsRead/write keybind strings from the shared [keybinds] section.
OnBindHotkeys(Hotkeys&)Bind each keybind to its handler.
OnMediaEvent(const MediaEvent&)React to the player. See Media Events.
OnKeyDownEvent(const AppEvent&)Handle a key press; return true to consume it.
OnShutdown()Teardown after the loop exits (e.g. apply a pending update).

A minimal plugin overrides only PluginName() and OnInstall(). Everything else has an empty default.

A typical OnInstall

void OnInstall(IPluginContext& ctx) override
{
// Look up another plugin's service (null if that plugin isn't loaded).
if (auto* history = ctx.GetService<IHistory>())
resumePos_ = history->GetResumePos(lastPath_);

// React to an app-wide event. Subscribe() is a free helper in
// <framelift/ContextHelpers.h> that wraps a lambda over the POD ABI.
framelift::Subscribe<FileOpenedEvent>(ctx, [](const FileOpenedEvent& e) {
Log::Info("[MyPlugin] now playing {}", e.path);
});

// Add a settings page wired to RenderSettings()/SaveSettings().
SetupSettingsPage(ctx);
}

See Cross-Plugin Communication for GetService and Subscribe, and Settings for SetupSettingsPage.

The ctx_ member

PluginBase stores the context as IPluginContext* ctx_. After Install, any hook can use it — for example to read a setting or publish an event — without it being passed in again.

Shutdown

OnShutdown() runs once, after the main loop drains its remaining events. Use it for teardown that must happen late. The host also clears your subscriptions on unload, invoking the cleanup callbacks you passed to Subscribe, so you do not have to unsubscribe manually.