The best tip I can give you is, look at the existing plugins and at the gmpc source. Internally gmpc uses the plugin structure for almost all parts.

Plugin Example

Here is a simple example of a useless plugin. This can be used as template. The fields of the gmpcPlugin will be explained in the next chapter.

00001: #include <gmpc/plugin.h>
00002:
00003: gmpcPlugin plugin;
00004:
00005: static int plugin_get_enabled(void)
00006: {
00007:     /* Getting the enable state from gmpc's config system */
00008:     return cfg_get_single_value_as_int_with_default(config, plugin.name, "enabled", TRUE);
00009: }
00010:
00011: static void plugin_set_enabled(int enabled)
00012: {
00013:     /* Storing the enable state in gmpc's config system */
00014:     cfg_set_single_value_as_int(config, plugin.name, "enabled", enabled);
00015:     /* Now handle the changes */
00016: }
00017:
00018: static void plugin_init(void)
00019: {
00020:     /* initialize the plugin here */
00021: }
00022: static void plugin_destroy(void)
00023: {
00024:     /* destroy the plugin here */
00025: }
00026: static void plugin_save_myself(void)
00027: {
00028:     /* Save any settings */
00029: }
00030:
00031: /**
00032:  * Signals
00033:  */
00034: static void plugin_connection_changed(MpdObj *mi, int connect, void *userdata)
00035: {
00036:
00037: }
00038: static void plugin_status_changed(MpdObj *mi,ChangedStatusType what , void *userdata)
00039: {
00040:     if(what&MPD_CST_SONGID)
00041:     {
00042:         /* This part of the function is called when mpd changes song */
00043:
00044:     }
00045: }
00046:
00047: gmpcPlugin plugin = {
00048:     /* Required fields */
00049:     .name                   = "The plugin Name",
00050:     .version                = {0,15,5},
00051:     .plugin_type            = GMPC_PLUGIN_NO_GUI,
00052:     .get_enabled            = plugin_get_enabled,
00053:     .set_enabled            = plugin_set_enabled,
00054:
00055:     /* Optional fields */
00056:     .init                   = plugin_init,
00057:     .destroy                = plugin_destroy,
00058:     .save_yourself          = plugin_save_myself,
00059:
00060:     /* Extentions
00061:      * (These are sub-structures)
00062:      */
00063:
00064:      /* A structure that provide the functionality off adding a browser to the main interface,
00065:       * catching keystrokes, add menu options, etc.
00066:       */
00067:     .browser                = NULL,
00068:
00069:     /* A structure that exposes 2 functions that are needed to make the plugin function as metadata provider */
00070:     .metadata               = NULL,
00071:
00072:     /* A structure that allows the plugin to add a panel to the preferences window.*/
00073:     .pref                   = NULL,
00074:
00075:     /* Signals */
00076:     .mpd_connection_changed = plugin_connection_changed,
00077:     .mpd_status_changed     = plugin_status_changed
00078: };
00079:
00080: int plugin_api_version = PLUGIN_API_VERSION;

gmpcPlugin

Every plugin must have a (filled in) version of the gmpcPlugin called plugin. This will be what gmpc tries to load.

It is prefered to use the "named" filling of the plugin ( .name = <value>,), instead of making a long list of values. 2 fields are filled in by gmpc and should be left empty. This is the id field (an number that identifies the plugin) and the path field (is filled in with the path to the executable).

Plugin name and version

A plugin must have a name and a version to be valid. The version consists of 3 numbers, normally plugin numbers are kept in sync with the gmpc release. The name will be used to report error about the plugin and in the preferences window.

Types of plugins

A plugin can have one or several different functions. This is set using the plugin_type field in the gmpcPlugin structure. The following types are available:

  1. GMPC_PLUGIN_DUMMY: This plugin does not have a particular function, and should not be used.

  2. GMPC_PLUGIN_PL_BROWSER: This plugin extends the main window with an extra browser. Therefor the browser field in the gmpcPlugin must be filled in. examples:

    • Magnatune browser

    • Server statistics

    • Wikipedia

  3. GMPC_PLUGIN_NO_GUI: Extends gmpc withouth adding a gui component to the main interface. examples:

    • osd

  4. GMPC_INTERNALL: Should never be used, this is used to mark an internal plugin.

  5. GMPC_DEPRECATED: This used to be the id of plugin for the old metadata system.

  6. GMPC_PLUGIN_META_DATA: This plugin provides gmpc with metadata. the metadata field in gmpcPlugin needs to be filled in. examples:

    • mdcover

    • coveramazon

    • Magnatune browsr.

The Magnatune browser, as visible above, is an example of a plugin that implements 2 types. 1 a GMPC_PLUGIN_PL_BROWSER and 2 a GMPC_PLUGIN_META_DATA. In the plugin_type field those can be added bitwise. for example plugin_type = GMPC_PLUGIN PL_BROWSER|GMPC_PLUGIN_META_DATA. Remeber that both the metadata and the browser entry in the gmpcPlugin needs to be filled in.

Init/Destroy/Save yourself

There are 3 functions used to construct and destroy the plugin.

init

On startup, before gtk enters it mainloop and before the main interface is created. the init() function of the plugin gets called. At this time the config system is working and gtk is initialized. Here plugins can initialize libraries (f.e. libxosd or dbus) that it requires later in the program.

Save yourself

This is called for all plugins, then every plugin is destroyed. F.e. a Browser plugin should save the position of browser here (and not in the destroy handler, because other plugins that are destoyed might cause the location to change).

Destroy

A plugin should destroy itself here.

Get/Set Enabled

[NOTE] These functions are needed for the plugin to be loaded.

Get Enabled

Queries the plugin's enable state.

Set Enabled

Set the plugin enable state. If the plugin add's gui elements to the interface, it should show/hide them. For example the stop button plugin removes the button when set_enabled(FALSE) is called.

Signals

Libmpd can give 3 types of signals to the users, 2 of them are forwarded to the plugins. Namely the status changed and the connection changed. For more information see the libmpd api reference.

Status changed

A bitmask what is passed, telling you how mpd changed. See the libmpd documentation for more information.

Connection Changed

The function is called when the state of the connection changed. The connect field indicates the new state.

Preferences

The gmpcPlugin structure can hold a substructure for preferences: gmpcPrefPlugin.

static GtkWidget *vbox = NULL;
static void preferences_panel_construct(GtkWidget *container)
{
    GtkWidget *label;
    vbox = gtk_vbox_new(FALSE, 6);
    label = gtk_label_new("Testing");
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE,0);
    /* add it to the right part of the preferences window */
    /* vbox has a floating ref. on creation, adding it to the
     * container will give it a real one
     */
    gtk_container_add(GTK_CONTAINER(container), vbox);
}
static void preferences_panel_destroy(GtkWidget *container)
{
    /* Removing the vbox from the container will release the ref. the container has on it.
     * So the vbox will be free-ed
     * If you don't want this, you must add your own ref. to the vbox in the construct function
     */
    gtk_container(remove(GTK_CONTAINER(container), vbox);
    vbox = NULL;
}
gmpcPrefPugin pref = {
    .construct  = preferences_panel_construct,
    .destroy    = preferences_panel_destroy
}

These functions allow you to embed a window in the preferences window. If the structure isn't available, no entry will be made.

construct

Create a widget that contains the panel, add this then to the container using gtk_container_add.

destroy

Remove the added panel from the container, using gtk_container_remove, and cleanup.

Metadata

gmpcMetaDataPlugin

plugin_api_version

Every plugin must have the following define:

int plugin_api_version = PLUGIN_API_VERSION;

This allows gmpc to check if the plugin is compiled against the same plugin interface as gmpc has.