make interactive actions a type and not special cases.
[mikachu/openbox.git] / openbox / plugin.c
1 #include "plugins/interface.h"
2 #include "parser/parse.h"
3
4 #include <glib.h>
5 #include <gmodule.h>
6
7 typedef struct {
8     GModule *module;
9     gchar *name;
10
11     gboolean started;
12
13     PluginSetupConfig config;
14     PluginStartup startup;
15     PluginShutdown shutdown;
16 } Plugin;
17
18 static gpointer load_sym(GModule *module, gchar *name, gchar *symbol,
19                          gboolean allow_fail)
20 {
21     gpointer var;
22     if (!g_module_symbol(module, symbol, &var)) {
23         if (!allow_fail)
24             g_warning("Failed to load symbol '%s' from plugin '%s'",
25                       symbol, name);
26         var = NULL;
27     }
28     return var;
29 }
30
31 static Plugin *plugin_new(gchar *name)
32 {
33     Plugin *p;
34     gchar *path;
35    
36     p = g_new(Plugin, 1);
37     p->started = FALSE;
38
39     path = g_build_filename(g_get_home_dir(), ".openbox", "plugins", name,
40                             NULL);
41     p->module = g_module_open(path, 0);
42     g_free(path);
43
44     if (p->module == NULL) {
45         path = g_build_filename(PLUGINDIR, name, NULL);
46         p->module = g_module_open(path, 0);
47         g_free(path);
48     }
49
50     if (p->module == NULL) {
51         g_warning(g_module_error());
52         g_free(p);
53         return NULL;
54     }
55
56     p->config = (PluginSetupConfig)load_sym(p->module, name,
57                                             "plugin_setup_config", FALSE);
58     p->startup = (PluginStartup)load_sym(p->module, name, "plugin_startup",
59                                          FALSE);
60     p->shutdown = (PluginShutdown)load_sym(p->module, name, "plugin_shutdown",
61                                            FALSE);
62
63     if (p->config == NULL || p->startup == NULL || p->shutdown == NULL) {
64         g_module_close(p->module);
65         g_free(p);
66         return NULL;
67     }
68
69     p->name = g_strdup(name);
70     return p;
71 }
72
73 static void plugin_free(Plugin *p)
74 {
75     p->shutdown();
76
77     g_free(p->name);
78     g_module_close(p->module);
79 }
80
81
82 static GData *plugins = NULL;
83
84 void plugin_startup()
85 {
86     g_datalist_init(&plugins);
87 }
88
89 void plugin_shutdown()
90 {
91     g_datalist_clear(&plugins);
92 }
93
94 gboolean plugin_open(gchar *name, ObParseInst *i)
95 {
96     Plugin *p;
97
98     if (g_datalist_get_data(&plugins, name) != NULL)
99         return TRUE;
100
101     p = plugin_new(name);
102     if (p == NULL) {
103         g_warning("failed to load plugin '%s'", name);
104         return FALSE;
105     }
106     p->config(i);
107
108     g_datalist_set_data_full(&plugins, name, p, (GDestroyNotify) plugin_free);
109     return TRUE;
110 }
111
112 static void foreach_start(GQuark key, Plugin *p, gpointer *foo)
113 {
114     if (!p->started) {
115         p->startup();
116         p->started = TRUE;
117     }
118 }
119
120 void plugin_startall()
121 {
122     g_datalist_foreach(&plugins, (GDataForeachFunc)foreach_start, NULL);
123 }
124
125 void plugin_start(gchar *name)
126 {
127     Plugin *p;
128
129     if (!(p = g_datalist_get_data(&plugins, name))) {
130         g_warning("Tried to start plugin '%s' but it is not loaded", name);
131         return;
132     }
133     if (!p->started) {
134         p->startup();
135         p->started = TRUE;
136     }
137 }
138
139 void plugin_loadall(ObParseInst *i)
140 {
141     GIOChannel *io;
142     GError *err;
143     gchar *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("placement", i);
160     } else {
161         /* load the plugins in the rc file */
162         while (g_io_channel_read_line(io, &name, NULL, NULL, &err) ==
163                G_IO_STATUS_NORMAL) {
164             g_strstrip(name);
165             if (name[0] != '\0' && name[0] != '#')
166                 plugin_open(name, i);
167             g_free(name);
168         }
169         g_io_channel_unref(io);
170     }
171 }