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