35ebb914633599b7f0df6fc2f8bd31af62ad9107
[debian/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 ENTER;
99 #ifdef STATIC_SEPARATOR
100 REGISTER_PLUGIN_CLASS(separator_plugin_class, 0);
101 #endif
102
103 /* Remove image plugin since it seems to be useless. */
104 /*
105 #ifdef STATIC_IMAGE
106 REGISTER_PLUGIN_CLASS(image_plugin_class, 0);
107 #endif
108 */
109
110 #ifdef STATIC_LAUNCHBAR
111 REGISTER_PLUGIN_CLASS(launchbar_plugin_class, 0);
112 #endif
113
114 #ifdef STATIC_DCLOCK
115 REGISTER_PLUGIN_CLASS(dclock_plugin_class, 0);
116 #endif
117
118 #ifdef STATIC_WINCMD
119 REGISTER_PLUGIN_CLASS(wincmd_plugin_class, 0);
120 #endif
121
122 #ifdef STATIC_DIRMENU
123 REGISTER_PLUGIN_CLASS(dirmenu_plugin_class, 0);
124 #endif
125
126 #ifdef STATIC_TASKBAR
127 REGISTER_PLUGIN_CLASS(taskbar_plugin_class, 0);
128 #endif
129
130 #ifdef STATIC_PAGER
131 REGISTER_PLUGIN_CLASS(pager_plugin_class, 0);
132 #endif
133
134 #ifdef STATIC_TRAY
135 REGISTER_PLUGIN_CLASS(tray_plugin_class, 0);
136 #endif
137
138 #ifdef STATIC_MENU
139 REGISTER_PLUGIN_CLASS(menu_plugin_class, 0);
140 #endif
141
142 #ifdef STATIC_SPACE
143 REGISTER_PLUGIN_CLASS(space_plugin_class, 0);
144 #endif
145
146 RET();
147 }
148
149 GList* plugin_find_class( const char* type )
150 {
151 GList *tmp;
152 PluginClass *pc = NULL;
153 for (tmp = pcl; tmp; tmp = g_list_next(tmp)) {
154 pc = (PluginClass *) tmp->data;
155 if (!g_ascii_strcasecmp(type, pc->type)) {
156 LOG(LOG_INFO, " already have it\n");
157 break;
158 }
159 }
160 return tmp;
161 }
162
163 static PluginClass*
164 plugin_load_dynamic( const char* type, const char* path )
165 {
166 PluginClass *pc = NULL;
167 GModule *m;
168 gpointer tmpsym;
169 char class_name[ 128 ];
170 m = g_module_open(path, G_MODULE_BIND_LAZY);
171 if (!m) {
172 /* ERR("error is %s\n", g_module_error()); */
173 RET(NULL);
174 }
175 g_snprintf( class_name, 128, "%s_plugin_class", type );
176
177 if (!g_module_symbol(m, class_name, &tmpsym)
178 || (pc = tmpsym) == NULL
179 || strcmp(type, pc->type)) {
180 g_module_close(m);
181 ERR("%s.so is not a lxpanel plugin\n", type);
182 RET(NULL);
183 }
184 pc->gmodule = m;
185 register_plugin_class(pc, 1);
186 return pc;
187 }
188
189 Plugin *
190 plugin_load(char *type)
191 {
192 GList *tmp;
193 PluginClass *pc = NULL;
194 Plugin *plug = NULL;
195
196 ENTER;
197 if (!pcl)
198 init_plugin_class_list();
199
200 tmp = plugin_find_class( type );
201
202 if( tmp ) {
203 pc = (PluginClass *) tmp->data;
204 }
205 #ifndef DISABLE_PLUGINS_LOADING
206 else if ( g_module_supported() ) {
207 char* path[ PATH_MAX ];
208 #if 0 /* put plugins in config dir is too dirty... */
209 g_snprintf(path, PATH_MAX, "%s/.lxpanel/plugins/%s.so", getenv("HOME"), type);
210 pc = plugin_load_dynamic( type, path );
211 #endif
212 if( !pc ) {
213 g_snprintf(path, PATH_MAX, PACKAGE_LIB_DIR "/lxpanel/plugins/%s.so", type);
214 pc = plugin_load_dynamic( type, path );
215 }
216 }
217 #endif /* DISABLE_PLUGINS_LOADING */
218
219 /* nothing was found */
220 if (!pc)
221 RET(NULL);
222
223 plug = g_new0(Plugin, 1);
224 g_return_val_if_fail (plug != NULL, NULL);
225 plug->class = pc;
226 pc->count++;
227 RET(plug);
228 }
229
230
231 void plugin_put(Plugin *this)
232 {
233 PluginClass *pc = this->class;
234 ENTER;
235 plugin_class_unref( pc );
236 g_free(this);
237 RET();
238 }
239
240 int
241 plugin_start(Plugin *this, char** fp)
242 {
243 ENTER;
244
245 DBG("%s\n", this->class->type);
246
247 if (!this->class->constructor(this, fp)) {
248 // if (!this->class->invisible)
249 // gtk_widget_destroy(this->pwid);
250 RET(0);
251 }
252
253 if (!this->class->invisible && this->pwid ) {
254 /* this->pwid is created by the plugin */
255 //this->pwid = gtk_bgbox_new();
256 gtk_widget_set_name(this->pwid, this->class->type);
257 gtk_box_pack_start(GTK_BOX(this->panel->box), this->pwid, this->expand, TRUE,
258 this->padding);
259 gtk_container_set_border_width(GTK_CONTAINER(this->pwid), this->border);
260
261 gtk_widget_show(this->pwid);
262 }
263 RET(1);
264 }
265
266
267 void plugin_stop(Plugin *this)
268 {
269 ENTER;
270 DBG("%s\n", this->class->type);
271 this->class->destructor(this);
272 this->panel->plug_num--;
273 if (!this->class->invisible && this->pwid )
274 gtk_widget_destroy(this->pwid);
275 /* this->pwid is destroyed in the dtor of plugins */
276 RET();
277 }
278
279 void plugin_class_unref( PluginClass* pc )
280 {
281 --pc->count;
282 if (pc->count == 0 && pc->dynamic) {
283 pcl = g_list_remove(pcl, pc);
284 /* pc points now somewhere inside loaded lib, so if g_module_close
285 * will touch it after dlclose (and 2.6 does) it will result in segfault */
286 g_module_close(pc->gmodule);
287 }
288 }
289
290 /*
291 Get a list of all available plugin classes
292 Return a newly allocated GList which should be freed with
293 plugin_class_list_free( list );
294 */
295 GList* plugin_get_available_classes()
296 {
297 GList* classes = NULL;
298 char *path, *dir_path;
299 const char* file;
300 GDir* dir;
301 GList* l;
302 PluginClass *pc;
303
304 for( l = pcl; l; l = l->next ) {
305 pc = (PluginClass*)l->data;
306 classes = g_list_prepend( classes, pc );
307 ++pc->count;
308 }
309
310 #ifndef DISABLE_PLUGINS_LOADING
311 #if 0 /* Put plugins in config dir is too dirty... */
312 dir_path = g_build_filename( g_get_home_dir(), ".lxpanel/plugins", NULL );
313 if( dir = g_dir_open( dir_path, 0, NULL ) ) {
314 while( file = g_dir_read_name( dir ) ) {
315 GModule *m;
316 char* type;
317 if( ! g_str_has_suffix( file, ".so" ) )
318 continue;
319 type = g_strndup( file, strlen(file) - 3 );
320 l = plugin_find_class( type );
321 if( l == NULL ) { /* If it has not been loaded */
322 path = g_build_filename( dir_path, file, NULL );
323 if( pc = plugin_load_dynamic( type, path ) ) {
324 ++pc->count;
325 classes = g_list_prepend( classes, pc );
326 }
327 g_free( path );
328 }
329 g_free( type );
330 }
331 g_dir_close( dir );
332 }
333 g_free( dir_path );
334 #endif
335 if( dir = g_dir_open( PACKAGE_LIB_DIR "/lxpanel/plugins", 0, NULL ) ) {
336 while( file = g_dir_read_name( dir ) ) {
337 GModule *m;
338 char* type;
339 if( ! g_str_has_suffix( file, ".so" ) )
340 continue;
341 type = g_strndup( file, strlen(file) - 3 );
342 l = plugin_find_class( type );
343 if( l == NULL ) { /* If it has not been loaded */
344 path = g_build_filename( PACKAGE_LIB_DIR "/lxpanel/plugins", file, NULL );
345 if( pc = plugin_load_dynamic( type, path ) ) {
346 ++pc->count;
347 classes = g_list_prepend( classes, pc );
348 }
349 g_free( path );
350 }
351 g_free( type );
352 }
353 g_dir_close( dir );
354 }
355 #endif
356 /* classes = g_list_reverse( classes ); */
357 return classes;
358 }
359
360 void plugin_class_list_free( GList* classes )
361 {
362 g_list_foreach( classes, plugin_class_unref, NULL );
363 g_list_free( classes );
364 }
365
366 void
367 plugin_widget_set_background( GtkWidget* w, Panel* p )
368 {
369 static gboolean in_tray = FALSE;
370 gboolean is_tray = FALSE;
371
372 if( ! w )
373 return;
374
375 if( ! GTK_WIDGET_NO_WINDOW( w ) )
376 {
377 if( p->background || p->transparent )
378 {
379 gtk_widget_set_app_paintable( w, TRUE );
380 if( GTK_WIDGET_REALIZED(w) )
381 gdk_window_set_back_pixmap( w->window, NULL, TRUE );
382 }
383 else
384 {
385 gtk_widget_set_app_paintable( w, FALSE );
386 if( GTK_WIDGET_REALIZED(w) )
387 {
388 gdk_window_set_back_pixmap( w->window, NULL, FALSE );
389
390 /* dirty hack to fix the background color of tray icons :-( */
391 if( in_tray )
392 gdk_window_set_background( w->window, &w->style->bg[ GTK_STATE_NORMAL ] );
393 }
394 }
395 // g_debug("%s has window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
396 }
397 /*
398 else
399 g_debug("%s has NO window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
400 */
401 if( GTK_IS_CONTAINER( w ) )
402 {
403 is_tray = ( strcmp( gtk_widget_get_name( w ), "tray" ) == 0 );
404 if( is_tray )
405 in_tray = TRUE;
406
407 gtk_container_foreach( w, plugin_widget_set_background, p );
408
409 if( is_tray )
410 in_tray = FALSE;
411 }
412
413 /* Dirty hack: Force repaint of tray icons!!
414 * Normal gtk+ repaint cannot work across different processes.
415 * So, we need some dirty tricks here.
416 */
417 if( is_tray )
418 {
419 gtk_widget_set_size_request( w, w->allocation.width, w->allocation.height );
420 gtk_widget_hide (gtk_bin_get_child((GtkBin*)w));
421 if( gtk_events_pending() )
422 gtk_main_iteration();
423 gtk_widget_show (gtk_bin_get_child((GtkBin*)w));
424 if( gtk_events_pending() )
425 gtk_main_iteration();
426 gtk_widget_set_size_request( w, -1, -1 );
427 }
428 }
429
430 void plugin_set_background( Plugin* pl, Panel* p )
431 {
432 if( G_UNLIKELY( pl->class->invisible || ! pl->pwid ) )
433 return;
434 plugin_widget_set_background( pl->pwid, p );
435 }