My first plugin in C

Plugins consisit of two parts, a plugin description file and the plugin code. The plugin description file is an *.ini like file that contains information about the plugin.

The description file can be named fairly liberally except for the extension. The extension must follow the naming convention "appname-plugin". Therefore, if the application is named Foo, then a valid filename would be "myplugin.foo-plugin".

The content of the plugin description file contains some basic information about the plugin and how to load it. Lets take a look at an example quickly.

1
2
3
4
5
6
7
8
9
10
11
# example.foo-plugin

[Foo Plugin]
Name=Example
Description=My First Plugin
Authors=Plugin Author
IAge=1
Module=example
Icon=ethos-plugin
Copyright=Public Domain
Website=http://example.com

Most of the file, as you can see, is self explanatory. Basic information like Name, Description, Authors, Copyright and Website is used in the plugin overview pages.

The critical bit in the file is the Module line. It specifies which shared library to load the plugin from during runtime. If the module name is "example", then ethos will look for "libexample.so" on Linux. The naming for the file is platform specific, so check GModule for the naming for your platform.

Now lets breifly take a look at the code to implement this plugin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <ethos/ethos.h>
#include <ethos/ethos-ui.h>

#define EXAMPLE_TYPE_PLUGIN		(example_plugin_get_type ())
#define EXAMPLE_PLUGIN(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_PLUGIN, ExamplePlugin))
#define EXAMPLE_PLUGIN_CONST(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_PLUGIN, ExamplePlugin const))
#define EXAMPLE_PLUGIN_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_PLUGIN, ExamplePluginClass))
#define EXAMPLE_IS_PLUGIN(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_PLUGIN))
#define EXAMPLE_IS_PLUGIN_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_PLUGIN))
#define EXAMPLE_PLUGIN_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_PLUGIN, ExamplePluginClass))

typedef struct {
	EthosPlugin parent;
	ExamplePluginPrivate *priv;
} ExamplePlugin;

typedef struct {
	EthosPluginClass parent_class;
} ExamplePluginClass;

typedef struct {
	gpointer dummy;
} ExamplePluginPrivate;

GType        example_plugin_get_type (void) G_GNUC_CONST;
EthosPlugin* example_plugin_new      (void);

static void
destroy (GtkWidget *widget)
{
	gtk_widget_destroy (gtk_widget_get_toplevel (widget));
}

G_DEFINE_TYPE (ExamplePlugin, example_plugin, ETHOS_TYPE_PLUGIN)

static void
example_plugin_finalize (GObject *object)
{
	G_OBJECT_CLASS (example_plugin_parent_class)->finalize (object);
}

static void activated (EthosPlugin *plugin)
{
	g_debug ("plugin activated");
}

static void deactivated (EthosPlugin *plugin)
{
	g_debug ("plugin deactivated");
}

static void
example_plugin_class_init (ExamplePluginClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	EthosPluginClass *plugin_class = ETHOS_PLUGIN_CLASS (klass);

	object_class->finalize = example_plugin_finalize;
	plugin_class->activated = activated;
	plugin_class->deactivated = deactivated;

	g_type_class_add_private (object_class, sizeof(ExamplePluginPrivate));
}

static void
example_plugin_init (ExamplePlugin *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EXAMPLE_TYPE_PLUGIN, ExamplePluginPrivate);
}

EthosPlugin*
example_plugin_new ()
{
	return g_object_new (EXAMPLE_TYPE_PLUGIN, NULL);
}

G_MODULE_EXPORT EthosPlugin*
ethos_plugin_register (void)
{
	return example_plugin_new ();
}

Most of the file is your standard GObject boilerplate code. You will notice that the ExamplePlugin class inherits from EthosPlugin. Your plugin should do the same. At the very bottom of the sample, is a method named "ethos_plugin_register". This method is looked up during runtime to create an instance of the plugin.

When the EthosManager attempts to load a plugin, "ethos_plugin_register" is found within the shared library and executed, expecting the result to be an instance of EthosPlugin.

There are "activated" and "deactivated" signals that can be connected to for setup and destruction of resources for the plugin. You can either connect to the signals with g_signal_connect() or override the slot in the classes vtable.