Allow build without libmenu-cache.
[lxde/lxpanel.git] / src / plugin.c
1 /**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "plugin.h"
24
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
27 #include <gdk/gdk.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "misc.h"
32 #include "bg.h"
33
34 #include <glib-object.h>
35
36 //#define DEBUG
37 #include "dbg.h"
38
39 static GList *pcl = NULL;
40
41 #if 0
42 /* dummy type plugin for dynamic plugins registering new types */
43 static void
44 lx_type_plugin_iface_init(gpointer g_iface, gpointer g_iface_data);
45
46 G_DEFINE_TYPE_EXTENDED ( LXTypePlugin,
47 lx_type_plugin,
48 G_TYPE_OBJECT,
49 0,
50 G_IMPLEMENT_INTERFACE (G_TYPE_TYPE_PLUGIN,
51 lx_type_plugin_iface_init))
52
53 void lx_type_plugin_init( LXTypePlugin* tp )
54 {
55 }
56
57 void lx_type_plugin_class_init(LXTypePluginClass* klass)
58 {
59 }
60
61 void lx_type_plugin_iface_init(gpointer g_iface, gpointer g_iface_data)
62 {
63 }
64
65 GTypePlugin* lx_type_plugin_get(const char* plugin_name)
66 {
67 LXTypePlugin* tp = g_object_new(LX_TYPE_TYPE_PLUGIN, NULL);
68 return tp;
69 }
70 #endif
71
72 /* counter for static (built-in) plugins must be greater then zero
73 * so lxpanel will not try to unload them */
74
75 #define REGISTER_PLUGIN_CLASS(pc, dynamic) \
76 do {\
77 extern PluginClass pc;\
78 register_plugin_class(&pc, dynamic);\
79 } while (0)
80
81
82 static void
83 register_plugin_class(PluginClass *pc, int dynamic)
84 {
85 pcl = g_list_append(pcl, pc);
86 pc->dynamic = dynamic;
87 if (!pc->dynamic)
88 pc->count++;
89 /* reloading netstatus results in segfault due to registering static type in dll.
90 * so keep it always onboard until bug fix */
91 if (!strcmp(pc->type, "netstatus"))
92 pc->count++;
93 }
94
95 static void
96 init_plugin_class_list()
97 {
98 #ifdef STATIC_SEPARATOR
99 REGISTER_PLUGIN_CLASS(separator_plugin_class, 0);
100 #endif
101
102 /* Remove image plugin since it seems to be useless. */
103 /*
104 #ifdef STATIC_IMAGE
105 REGISTER_PLUGIN_CLASS(image_plugin_class, 0);
106 #endif
107 */
108
109 #ifdef STATIC_LAUNCHBAR
110 REGISTER_PLUGIN_CLASS(launchbar_plugin_class, 0);
111 #endif
112
113 #ifdef STATIC_DCLOCK
114 REGISTER_PLUGIN_CLASS(dclock_plugin_class, 0);
115 #endif
116
117 #ifdef STATIC_WINCMD
118 REGISTER_PLUGIN_CLASS(wincmd_plugin_class, 0);
119 #endif
120
121 #ifdef STATIC_DIRMENU
122 REGISTER_PLUGIN_CLASS(dirmenu_plugin_class, 0);
123 #endif
124
125 #ifdef STATIC_TASKBAR
126 REGISTER_PLUGIN_CLASS(taskbar_plugin_class, 0);
127 #endif
128
129 #ifdef STATIC_PAGER
130 REGISTER_PLUGIN_CLASS(pager_plugin_class, 0);
131 #endif
132
133 #ifdef STATIC_TRAY
134 REGISTER_PLUGIN_CLASS(tray_plugin_class, 0);
135 #endif
136
137 #ifndef DISABLE_MENU
138 #ifdef STATIC_MENU
139 REGISTER_PLUGIN_CLASS(menu_plugin_class, 0);
140 #endif
141 #endif
142
143 #ifdef STATIC_SPACE
144 REGISTER_PLUGIN_CLASS(space_plugin_class, 0);
145 #endif
146
147 RET();
148 }
149
150 GList* plugin_find_class( const char* type )
151 {
152 GList *tmp;
153 PluginClass *pc = NULL;
154 for (tmp = pcl; tmp; tmp = g_list_next(tmp)) {
155 pc = (PluginClass *) tmp->data;
156 if (!g_ascii_strcasecmp(type, pc->type)) {
157 LOG(LOG_INFO, " already have it\n");
158 break;
159 }
160 }
161 return tmp;
162 }
163
164 static PluginClass*
165 plugin_load_dynamic( const char* type, const gchar* path )
166 {
167 PluginClass *pc = NULL;
168 GModule *m;
169 gpointer tmpsym;
170 char class_name[ 128 ];
171 m = g_module_open(path, G_MODULE_BIND_LAZY);
172 if (!m) {
173 /* ERR("error is %s\n", g_module_error()); */
174 RET(NULL);
175 }
176 g_snprintf( class_name, 128, "%s_plugin_class", type );
177
178 if (!g_module_symbol(m, class_name, &tmpsym)
179 || (pc = tmpsym) == NULL
180 || strcmp(type, pc->type)) {
181 g_module_close(m);
182 ERR("%s.so is not a lxpanel plugin\n", type);
183 RET(NULL);
184 }
185 pc->gmodule = m;
186 register_plugin_class(pc, 1);
187 return pc;
188 }
189
190 Plugin *
191 plugin_load(char *type)
192 {
193 GList *tmp;
194 PluginClass *pc = NULL;
195 Plugin *plug = NULL;
196
197 ENTER;
198 if (!pcl)
199 init_plugin_class_list();
200
201 tmp = plugin_find_class( type );
202
203 if( tmp ) {
204 pc = (PluginClass *) tmp->data;
205 }
206 #ifndef DISABLE_PLUGINS_LOADING
207 else if ( g_module_supported() ) {
208 gchar path[ PATH_MAX ];
209
210 #if 0 /* put plugins in config dir is too dirty... */
211 g_snprintf(path, PATH_MAX, "%s/.lxpanel/plugins/%s.so", getenv("HOME"), type);
212 pc = plugin_load_dynamic( type, path );
213 #endif
214 if( !pc ) {
215 g_snprintf(path, PATH_MAX, PACKAGE_LIB_DIR "/lxpanel/plugins/%s.so", type);
216 pc = plugin_load_dynamic( type, path );
217 }
218 }
219 #endif /* DISABLE_PLUGINS_LOADING */
220
221 /* nothing was found */
222 if (!pc)
223 RET(NULL);
224
225 plug = g_new0(Plugin, 1);
226 g_return_val_if_fail (plug != NULL, NULL);
227 plug->class = pc;
228 pc->count++;
229 RET(plug);
230 }
231
232
233 void plugin_put(Plugin *this)
234 {
235 PluginClass *pc = this->class;
236 ENTER;
237 plugin_class_unref( pc );
238 g_free(this);
239 RET();
240 }
241
242 int
243 plugin_start(Plugin *this, char** fp)
244 {
245 ENTER;
246
247 DBG("%s\n", this->class->type);
248
249 if (!this->class->constructor(this, fp)) {
250 // if (!this->class->invisible)
251 // gtk_widget_destroy(this->pwid);
252 RET(0);
253 }
254
255 if (!this->class->invisible && this->pwid ) {
256 /* this->pwid is created by the plugin */
257 //this->pwid = gtk_bgbox_new();
258 gtk_widget_set_name(this->pwid, this->class->type);
259 gtk_box_pack_start(GTK_BOX(this->panel->box), this->pwid, this->expand, TRUE,
260 this->padding);
261 gtk_container_set_border_width(GTK_CONTAINER(this->pwid), this->border);
262
263 gtk_widget_show(this->pwid);
264 }
265 RET(1);
266 }
267
268
269 void plugin_stop(Plugin *this)
270 {
271 ENTER;
272 /* g_debug("%s\n", this->class->type); */
273 this->class->destructor(this);
274 this->panel->plug_num--;
275 if (!this->class->invisible && this->pwid )
276 gtk_widget_destroy(this->pwid);
277 /* this->pwid is destroyed in the dtor of plugins */
278 RET();
279 }
280
281 void plugin_class_unref( PluginClass* pc )
282 {
283 --pc->count;
284 if (pc->count == 0 && pc->dynamic) {
285 pcl = g_list_remove(pcl, pc);
286 /* pc points now somewhere inside loaded lib, so if g_module_close
287 * will touch it after dlclose (and 2.6 does) it will result in segfault */
288 g_module_close(pc->gmodule);
289 }
290 }
291
292 /*
293 Get a list of all available plugin classes
294 Return a newly allocated GList which should be freed with
295 plugin_class_list_free( list );
296 */
297 GList* plugin_get_available_classes()
298 {
299 GList* classes = NULL;
300 gchar *path;
301 char *dir_path;
302 const char* file;
303 GDir* dir;
304 GList* l;
305 PluginClass *pc;
306
307 if (!pcl)
308 init_plugin_class_list();
309
310 for( l = pcl; l; l = l->next ) {
311 pc = (PluginClass*)l->data;
312 classes = g_list_prepend( classes, pc );
313 ++pc->count;
314 }
315
316 #ifndef DISABLE_PLUGINS_LOADING
317 #if 0 /* Put plugins in config dir is too dirty... */
318 dir_path = g_build_filename( g_get_home_dir(), ".lxpanel/plugins", NULL );
319 if( dir = g_dir_open( dir_path, 0, NULL ) ) {
320 while( file = g_dir_read_name( dir ) ) {
321 GModule *m;
322 char* type;
323 if( ! g_str_has_suffix( file, ".so" ) )
324 continue;
325 type = g_strndup( file, strlen(file) - 3 );
326 l = plugin_find_class( type );
327 if( l == NULL ) { /* If it has not been loaded */
328 path = g_build_filename( dir_path, file, NULL );
329 if( pc = plugin_load_dynamic( type, path ) ) {
330 ++pc->count;
331 classes = g_list_prepend( classes, pc );
332 }
333 g_free( path );
334 }
335 g_free( type );
336 }
337 g_dir_close( dir );
338 }
339 g_free( dir_path );
340 #endif
341 if( dir = g_dir_open( PACKAGE_LIB_DIR "/lxpanel/plugins", 0, NULL ) ) {
342 while( file = g_dir_read_name( dir ) ) {
343 GModule *m;
344 char* type;
345 if( ! g_str_has_suffix( file, ".so" ) )
346 continue;
347 type = g_strndup( file, strlen(file) - 3 );
348 l = plugin_find_class( type );
349 if( l == NULL ) { /* If it has not been loaded */
350 path = g_build_filename( PACKAGE_LIB_DIR "/lxpanel/plugins", file, NULL );
351 if( pc = plugin_load_dynamic( type, path ) ) {
352 ++pc->count;
353 classes = g_list_prepend( classes, pc );
354 }
355 g_free( path );
356 }
357 g_free( type );
358 }
359 g_dir_close( dir );
360 }
361 #endif
362 /* classes = g_list_reverse( classes ); */
363 return classes;
364 }
365
366 void plugin_class_list_free( GList* classes )
367 {
368 g_list_foreach( classes, (GFunc)plugin_class_unref, NULL );
369 g_list_free( classes );
370 }
371
372 void
373 plugin_widget_set_background( GtkWidget* w, Panel* p )
374 {
375 static gboolean in_tray = FALSE;
376 gboolean is_tray;
377
378 if( ! w )
379 return;
380
381 is_tray = ( GTK_IS_CONTAINER(w) && strcmp( gtk_widget_get_name( w ), "tray" ) == 0 );
382
383 if( ! GTK_WIDGET_NO_WINDOW( w ) )
384 {
385 if( p->background || p->transparent )
386 {
387 gtk_widget_set_app_paintable( w, TRUE );
388 if( GTK_WIDGET_REALIZED(w) )
389 gdk_window_set_back_pixmap( w->window, NULL, TRUE );
390 }
391 else
392 {
393 gtk_widget_set_app_paintable( w, FALSE );
394 if( GTK_WIDGET_REALIZED(w) )
395 {
396 gdk_window_set_back_pixmap( w->window, NULL, FALSE );
397
398 /* dirty hack to fix the background color of tray icons :-( */
399 if( in_tray )
400 gdk_window_set_background( w->window, &w->style->bg[ GTK_STATE_NORMAL ] );
401 }
402 }
403 // g_debug("%s has window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
404 }
405 /*
406 else
407 g_debug("%s has NO window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
408 */
409 if( GTK_IS_CONTAINER( w ) )
410 {
411 if( is_tray )
412 in_tray = TRUE;
413
414 gtk_container_foreach( (GtkContainer*)w,
415 (GtkCallback)plugin_widget_set_background, p );
416
417 if( is_tray )
418 in_tray = FALSE;
419 }
420
421 /* Dirty hack: Force repaint of tray icons!!
422 * Normal gtk+ repaint cannot work across different processes.
423 * So, we need some dirty tricks here.
424 */
425 if( is_tray )
426 {
427 gtk_widget_set_size_request( w, w->allocation.width, w->allocation.height );
428 gtk_widget_hide (gtk_bin_get_child((GtkBin*)w));
429 if( gtk_events_pending() )
430 gtk_main_iteration();
431 gtk_widget_show (gtk_bin_get_child((GtkBin*)w));
432 if( gtk_events_pending() )
433 gtk_main_iteration();
434 gtk_widget_set_size_request( w, -1, -1 );
435 }
436 }
437
438 void plugin_set_background( Plugin* pl, Panel* p )
439 {
440 if( G_UNLIKELY( pl->class->invisible || ! pl->pwid ) )
441 return;
442 plugin_widget_set_background( pl->pwid, p );
443 }