]> icculus.org git repositories - dana/openbox.git/blob - openbox/plugin.c
Menu parsing updates for plugins.
[dana/openbox.git] / openbox / plugin.c
1 #include "plugins/interface.h"
2
3 #include <glib.h>
4 #include <gmodule.h>
5
6 typedef struct {
7     GModule *module;
8     char *name;
9
10     PluginSetupConfig config;
11     PluginStartup startup;
12     PluginShutdown shutdown;
13     PluginCreate create;
14     PluginDestroy destroy;
15 } Plugin;
16
17 static gpointer load_sym(GModule *module, char *name, char *symbol,
18                          gboolean allow_fail)
19 {
20     gpointer var;
21     if (!g_module_symbol(module, symbol, &var)) {
22         if (!allow_fail)
23             g_warning("Failed to load symbol '%s' from plugin '%s'",
24                       symbol, name);
25         var = NULL;
26     }
27     return var;
28 }
29
30 static Plugin *plugin_new(char *name)
31 {
32     Plugin *p;
33     char *path;
34    
35     p = g_new(Plugin, 1);
36
37     path = g_build_filename(g_get_home_dir(), ".openbox", "plugins", name,
38                             NULL);
39     p->module = g_module_open(path, 0);
40     g_free(path);
41
42     if (p->module == NULL) {
43         path = g_build_filename(PLUGINDIR, name, NULL);
44         p->module = g_module_open(path, 0);
45         g_free(path);
46     }
47
48     if (p->module == NULL) {
49         g_warning(g_module_error());
50         g_free(p);
51         return NULL;
52     }
53
54     p->config = (PluginSetupConfig)load_sym(p->module, name,
55                                             "plugin_setup_config", FALSE);
56     p->startup = (PluginStartup)load_sym(p->module, name, "plugin_startup",
57                                          FALSE);
58     p->shutdown = (PluginShutdown)load_sym(p->module, name, "plugin_shutdown",
59                                            FALSE);
60     p->create = (PluginCreate)load_sym(p->module, name, "plugin_create", TRUE);
61     p->destroy = (PluginDestroy)load_sym(p->module, name, "plugin_destroy",
62                                          TRUE);
63
64     if (p->config == NULL || p->startup == NULL || p->shutdown == NULL) {
65         g_module_close(p->module);
66         g_free(p);
67         return NULL;
68     }
69
70     p->name = g_strdup(name);
71     return p;
72 }
73
74 static void plugin_free(Plugin *p)
75 {
76     p->shutdown();
77
78     g_free(p->name);
79     g_module_close(p->module);
80 }
81
82
83 static GData *plugins = NULL;
84
85 void plugin_startup()
86 {
87     g_datalist_init(&plugins);
88 }
89
90 void plugin_shutdown()
91 {
92     g_datalist_clear(&plugins);
93 }
94
95 gboolean plugin_open_full(char *name, gboolean reopen)
96 {
97     Plugin *p;
98
99     if (g_datalist_get_data(&plugins, name) != NULL) {
100         if (!reopen) 
101             g_warning("plugin '%s' already loaded, can't load again", name);
102         return TRUE;
103     }
104
105     p = plugin_new(name);
106     if (p == NULL) {
107         g_warning("failed to load plugin '%s'", name);
108         return FALSE;
109     }
110     p->config();
111
112     g_datalist_set_data_full(&plugins, name, p, (GDestroyNotify) plugin_free);
113     return TRUE;
114 }
115
116 gboolean plugin_open(char *name) {
117     return plugin_open_full(name, FALSE);
118 }
119
120 gboolean plugin_open_reopen(char *name) {
121     return plugin_open_full(name, TRUE);
122 }
123
124 void plugin_close(char *name)
125 {
126     g_datalist_remove_data(&plugins, name);
127 }
128
129 static void foreach_start(GQuark key, Plugin *p, gpointer *foo)
130 {
131     p->startup();
132 }
133
134 void plugin_startall()
135 {
136     g_datalist_foreach(&plugins, (GDataForeachFunc)foreach_start, NULL);
137 }
138
139 void plugin_loadall()
140 {
141     GIOChannel *io;
142     GError *err;
143     char *path, *name;
144
145     path = g_build_filename(g_get_home_dir(), ".openbox", "pluginrc", NULL);
146     err = NULL;
147     io = g_io_channel_new_file(path, "r", &err);
148     g_free(path);
149
150     if (io == NULL) {
151         path = g_build_filename(RCDIR, "pluginrc", NULL);
152         err = NULL;
153         io = g_io_channel_new_file(path, "r", &err);
154         g_free(path);
155     }
156
157     if (io == NULL) {
158         /* load the default plugins */
159         plugin_open("keyboard");
160         plugin_open("mouse");
161         plugin_open("placement");
162         plugin_open("resistance");
163
164         /* XXX rm me when the parser loads me magically */
165         plugin_open("client_menu");
166     } else {
167         /* load the plugins in the rc file */
168         while (g_io_channel_read_line(io, &name, NULL, NULL, &err) ==
169                G_IO_STATUS_NORMAL) {
170             g_strstrip(name);
171             if (name[0] != '\0' && name[0] != '#')
172                 plugin_open(name);
173             g_free(name);
174         }
175         g_io_channel_unref(io);
176     }
177 }
178
179 void *plugin_create(char *name, void *data)
180 {
181     Plugin *p = (Plugin *)g_datalist_get_data(&plugins, name);
182
183     if (p == NULL) {
184         g_warning("Unable to find plugin for create: %s", name);
185         return NULL;
186     }
187
188     if (p->create == NULL || p->destroy == NULL) {
189         g_critical("Unsupported create/destroy: %s", name);
190         return NULL;
191     }
192
193     return p->create(data);
194 }
195
196 void plugin_destroy(char *name, void *data)
197 {
198     Plugin *p = (Plugin *)g_datalist_get_data(&plugins, name);
199
200     if (p == NULL) {
201         g_critical("Unable to find plugin for destroy: %s", name);
202         /* really shouldn't happen, but attempt to free something anyway? */
203         g_free(data);
204         return;
205     }
206
207     if (p->destroy == NULL || p->create == NULL) {
208         g_critical("Unsupported create/destroy: %s", name);
209         /* really, really shouldn't happen, but attempt to free anyway? */
210         g_free(data);
211         return;
212     }
213
214     p->destroy(data);
215 }