Fixed panel crashing when a plugin couldn't be loaded.
[lxde/lxpanel.git] / src / panel.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 <stdlib.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <locale.h>
30 #include <string.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkx.h>
33
34 #include "plugin.h"
35 #include "panel.h"
36 #include "misc.h"
37 #include "bg.h"
38 #include "gtkbgbox.h"
39
40 #include "lxpanelctl.h"
41
42 static gchar *cfgfile = NULL;
43 static gchar version[] = VERSION;
44 gchar *cprofile = "default";
45
46 static int config = 0;
47 FbEv *fbev;
48
49 //#define DEBUG
50 #include "dbg.h"
51
52 int log_level;
53 panel *p;
54
55 gboolean is_restarting = FALSE;
56
57 /****************************************************
58 * panel's handlers for WM events *
59 ****************************************************/
60 /*
61 static void
62 panel_del_wm_strut(panel *p)
63 {
64 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
65 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
66 }
67 */
68
69
70 void panel_set_wm_strut(panel *p)
71 {
72 gulong data[12] = { 0 };
73 int i = 4;
74
75 ENTER;
76 if (!GTK_WIDGET_MAPPED (p->topgwin))
77 return;
78 if ( ! p->setstrut )
79 {
80 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
81 /* old spec, for wms that do not support STRUT_PARTIAL */
82 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
83 return;
84 }
85
86 switch (p->edge) {
87 case EDGE_LEFT:
88 i = 0;
89 data[i] = p->aw;
90 data[4 + i*2] = p->ay;
91 data[5 + i*2] = p->ay + p->ah;
92 break;
93 case EDGE_RIGHT:
94 i = 1;
95 data[i] = p->aw;
96 data[4 + i*2] = p->ay;
97 data[5 + i*2] = p->ay + p->ah;
98 break;
99 case EDGE_TOP:
100 i = 2;
101 data[i] = p->ah;
102 data[4 + i*2] = p->ax;
103 data[5 + i*2] = p->ax + p->aw;
104 break;
105 case EDGE_BOTTOM:
106 i = 3;
107 data[i] = p->ah;
108 data[4 + i*2] = p->ax;
109 data[5 + i*2] = p->ax + p->aw;
110 break;
111 default:
112 ERR("wrong edge %d. strut won't be set\n", p->edge);
113 RET();
114 }
115 DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]);
116
117 /* if wm supports STRUT_PARTIAL it will ignore STRUT */
118 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
119 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12);
120 /* old spec, for wms that do not support STRUT_PARTIAL */
121 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
122 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4);
123
124 RET();
125 }
126
127 static void
128 print_wmdata(panel *p)
129 {
130 int i;
131
132 ENTER;
133 RET();
134 DBG("desktop %d/%d\n", p->curdesk, p->desknum);
135 DBG("workarea\n");
136 for (i = 0; i < p->wa_len/4; i++)
137 DBG("(%d, %d) x (%d, %d)\n",
138 p->workarea[4*i + 0],
139 p->workarea[4*i + 1],
140 p->workarea[4*i + 2],
141 p->workarea[4*i + 3]);
142 RET();
143 }
144
145
146 /* defined in plugins/menu.c */
147 gboolean show_system_menu( gpointer system_menu );
148
149 /* built-in commands, defined in configurator.c */
150 void configure(void);
151 void restart(void);
152 void gtk_run(void);
153
154 static void process_client_msg ( panel *p, XClientMessageEvent* ev )
155 {
156 int cmd = ev->data.b[0];
157 switch( cmd )
158 {
159 case LXPANEL_CMD_SYS_MENU:
160 if( p->system_menus )
161 {
162 /* show_system_menu( p->system_menus->data ); */
163 /* FIXME: I've no idea why this doesn't work without timeout
164 under some WMs, like icewm. */
165 g_timeout_add( 200, (GSourceFunc)show_system_menu,
166 p->system_menus->data );
167 }
168 break;
169 case LXPANEL_CMD_RUN:
170 gtk_run();
171 break;
172 case LXPANEL_CMD_CONFIG:
173 configure();
174 break;
175 case LXPANEL_CMD_RESTART:
176 restart();
177 break;
178 case LXPANEL_CMD_EXIT:
179 gtk_main_quit();
180 break;
181 }
182 }
183
184 static GdkFilterReturn
185 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, panel *p)
186 {
187 Atom at;
188 Window win;
189 XEvent *ev = (XEvent *) xevent;
190
191 ENTER;
192 DBG("win = 0x%x\n", ev->xproperty.window);
193 if (ev->type != PropertyNotify ) {
194 /* private client message from lxpanelctl */
195 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
196 {
197 process_client_msg( p, (XClientMessageEvent*)ev );
198 }
199 RET(GDK_FILTER_CONTINUE);
200 }
201
202 at = ev->xproperty.atom;
203 win = ev->xproperty.window;
204 DBG("win=%x at=%d\n", win, at);
205 if (win == GDK_ROOT_WINDOW()) {
206 if (at == a_NET_CLIENT_LIST) {
207 DBG("A_NET_CLIENT_LIST\n");
208 fb_ev_trigger(fbev, EV_CLIENT_LIST);
209 } else if (at == a_NET_CURRENT_DESKTOP) {
210 DBG("A_NET_CURRENT_DESKTOP\n");
211 p->curdesk = get_net_current_desktop();
212 fb_ev_trigger(fbev, EV_CURRENT_DESKTOP);
213 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
214 DBG("A_NET_NUMBER_OF_DESKTOPS\n");
215 p->desknum = get_net_number_of_desktops();
216 fb_ev_trigger(fbev, EV_NUMBER_OF_DESKTOPS);
217 } else if (at == a_NET_DESKTOP_NAMES) {
218 DBG("A_NET_DESKTOP_NAMES\n");
219 fb_ev_trigger(fbev, EV_DESKTOP_NAMES);
220 } else if (at == a_NET_ACTIVE_WINDOW) {
221 DBG("A_NET_ACTIVE_WINDOW\n");
222 fb_ev_trigger(fbev, EV_ACTIVE_WINDOW);
223 }else if (at == a_NET_CLIENT_LIST_STACKING) {
224 DBG("A_NET_CLIENT_LIST_STACKING\n");
225 fb_ev_trigger(fbev, EV_CLIENT_LIST_STACKING);
226 } else if (at == a_XROOTPMAP_ID) {
227 DBG("a_XROOTPMAP_ID\n");
228 if (p->transparent) {
229 fb_bg_notify_changed_bg(p->bg);
230 }
231 } else if (at == a_NET_WORKAREA) {
232 DBG("A_NET_WORKAREA\n");
233 g_free( p->workarea );
234 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
235 print_wmdata(p);
236 } else
237 RET(GDK_FILTER_CONTINUE);
238 RET(GDK_FILTER_REMOVE);
239 }
240 DBG("non root %x\n", win);
241 RET(GDK_FILTER_CONTINUE);
242 }
243
244 /****************************************************
245 * panel's handlers for GTK events *
246 ****************************************************/
247
248
249 static gint
250 panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
251 {
252 ENTER;
253 RET(FALSE);
254 }
255
256 static gint
257 panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
258 {
259 //panel *p = (panel *) data;
260
261 ENTER;
262 //if (!p->self_destroy)
263 gtk_main_quit();
264 RET(FALSE);
265 }
266
267
268 static void
269 panel_realize(GtkWidget *widget, panel *p)
270 {
271 ENTER;
272 RET();
273
274 }
275 static gint
276 panel_size_req(GtkWidget *widget, GtkRequisition *req, panel *p)
277 {
278 ENTER;
279 DBG("IN req=(%d, %d)\n", req->width, req->height);
280 if (p->widthtype == WIDTH_REQUEST)
281 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
282 if (p->heighttype == HEIGHT_REQUEST)
283 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
284 calculate_position(p);
285 req->width = p->aw;
286 req->height = p->ah;
287 DBG("OUT req=(%d, %d)\n", req->width, req->height);
288 RET( TRUE );
289 }
290
291 static gint
292 panel_size_alloc(GtkWidget *widget, GtkAllocation *a, panel *p)
293 {
294 ENTER;
295 DBG("installed alloc: size (%d, %d). pos (%d, %d)\n", a->width, a->height, a->x, a->y);
296 DBG("suggested alloc: size (%d, %d). pos (%d, %d)\n", a->width, a->height, a->x, a->y);
297 DBG("prev pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay);
298 if (p->widthtype == WIDTH_REQUEST)
299 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
300 if (p->heighttype == HEIGHT_REQUEST)
301 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
302 calculate_position(p);
303 DBG("curr pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay);
304 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
305 DBG("actual coords eq to preffered. just returning\n");
306 RET(TRUE);
307 }
308
309 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
310 DBG("moving to %d %d\n", p->ax, p->ay);
311 panel_set_wm_strut(p);
312 RET(TRUE);
313 }
314
315
316 static gboolean
317 panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, panel *p)
318 {
319 ENTER;
320 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
321 RET(TRUE);
322 p->cw = e->width;
323 p->ch = e->height;
324 p->cx = e->x;
325 p->cy = e->y;
326 DBG("here\n");
327 if (p->transparent)
328 fb_bg_notify_changed_bg(p->bg);
329 DBG("here\n");
330 DBG("geom: size (%d, %d). pos (%d, %d)\n", e->width, e->height, e->x, e->y);
331 RET(FALSE);
332
333 }
334
335
336 /****************************************************
337 * panel creation *
338 ****************************************************/
339 static void
340 make_round_corners(panel *p)
341 {
342 /* FIXME: This should be re-written with shape extension of X11 */
343 #if 0
344 GtkWidget *b1, *b2, *img;
345 GtkWidget *(*box_new) (gboolean, gint);
346 void (*box_pack)(GtkBox *, GtkWidget *, gboolean, gboolean, guint);
347 gchar *s1, *s2;
348 #define IMGPREFIX PACKAGE_DATA_DIR "/lxpanel/images/"
349
350 ENTER;
351 if (p->edge == EDGE_TOP) {
352 s1 = IMGPREFIX "top-left.xpm";
353 s2 = IMGPREFIX "top-right.xpm";
354 } else if (p->edge == EDGE_BOTTOM) {
355 s1 = IMGPREFIX "bottom-left.xpm";
356 s2 = IMGPREFIX "bottom-right.xpm";
357 } else if (p->edge == EDGE_LEFT) {
358 s1 = IMGPREFIX "top-left.xpm";
359 s2 = IMGPREFIX "bottom-left.xpm";
360 } else if (p->edge == EDGE_RIGHT) {
361 s1 = IMGPREFIX "top-right.xpm";
362 s2 = IMGPREFIX "bottom-right.xpm";
363 } else
364 RET();
365
366 box_new = (p->orientation == ORIENT_HORIZ) ? gtk_vbox_new : gtk_hbox_new;
367 b1 = box_new(0, FALSE);
368 gtk_widget_show(b1);
369 b2 = box_new(0, FALSE);
370 gtk_widget_show(b2);
371
372 box_pack = (p->edge == EDGE_TOP || p->edge == EDGE_LEFT) ?
373 gtk_box_pack_start : gtk_box_pack_end;
374
375 img = gtk_image_new_from_file(s1);
376 gtk_widget_show(img);
377 box_pack(GTK_BOX(b1), img, FALSE, FALSE, 0);
378 img = gtk_image_new_from_file(s2);
379 gtk_widget_show(img);
380 box_pack(GTK_BOX(b2), img, FALSE, FALSE, 0);
381 gtk_box_pack_start(GTK_BOX(p->lbox), b1, FALSE, FALSE, 0);
382 gtk_box_pack_end(GTK_BOX(p->lbox), b2, FALSE, FALSE, 0);
383 RET();
384 #endif
385 }
386
387 void panel_set_dock_type(panel *p)
388 {
389 if (p->setdocktype) {
390 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
391 XChangeProperty(GDK_DISPLAY(), p->topxwin,
392 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
393 PropModeReplace, (unsigned char *) &state, 1);
394 }
395 else {
396 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
397 }
398 }
399
400 static void
401 panel_start_gui(panel *p)
402 {
403 Atom state[3];
404 XWMHints wmhints;
405 guint32 val;
406
407 ENTER;
408
409 // main toplevel window
410 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
411 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
412 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
413 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
414 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
415 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
416 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
417
418 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
419 G_CALLBACK(panel_delete_event), p);
420 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
421 G_CALLBACK(panel_destroy_event), p);
422 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
423 (GCallback) panel_size_req, p);
424 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
425 (GCallback) panel_size_alloc, p);
426 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
427 (GCallback) panel_configure_event, p);
428 g_signal_connect (G_OBJECT (p->topgwin), "realize",
429 (GCallback) panel_realize, p);
430
431 gtk_widget_realize(p->topgwin);
432 //gdk_window_set_decorations(p->topgwin->window, 0);
433 gtk_widget_set_app_paintable(p->topgwin, TRUE);
434
435 // background box all over toplevel
436 p->bbox = gtk_bgbox_new();
437 gtk_container_add(GTK_CONTAINER(p->topgwin), p->bbox);
438 gtk_widget_show(p->bbox);
439 gtk_container_set_border_width(GTK_CONTAINER(p->bbox), 0);
440 if (p->transparent) {
441 p->bg = fb_bg_get_for_display();
442 gtk_bgbox_set_background(p->bbox, BG_ROOT, p->tintcolor, p->alpha);
443 }
444
445 // main layout manager as a single child of background widget box
446 p->box = p->my_box_new(FALSE, 0);
447 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
448 gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
449 gtk_widget_show(p->box);
450 if (p->round_corners)
451 make_round_corners(p);
452
453 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
454 DBG("topxwin = %x\n", p->topxwin);
455
456 /* the settings that should be done before window is mapped */
457 wmhints.flags = InputHint;
458 wmhints.input = 0;
459 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
460 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
461 val = WIN_HINTS_SKIP_FOCUS;
462 XChangeProperty(GDK_DISPLAY(), p->topxwin,
463 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
464 PropModeReplace, (unsigned char *) &val, 1);
465
466 panel_set_dock_type(p);
467
468 /* window mapping point */
469 gtk_widget_show_all(p->topgwin);
470
471 /* the settings that should be done after window is mapped */
472
473 /* send it to running wm */
474 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
475 /* and assign it ourself just for case when wm is not running */
476 val = 0xFFFFFFFF;
477 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
478 PropModeReplace, (unsigned char *) &val, 1);
479
480 state[0] = a_NET_WM_STATE_SKIP_PAGER;
481 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
482 state[2] = a_NET_WM_STATE_STICKY;
483 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
484 32, PropModeReplace, (unsigned char *) state, 3);
485
486 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), SubstructureNotifyMask|PropertyChangeMask);
487 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, p);
488
489 calculate_position(p);
490 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
491 panel_set_wm_strut(p);
492
493 p->tooltips = gtk_tooltips_new();
494 #if GLIB_CHECK_VERSION( 2, 10, 0 )
495 g_object_ref_sink( p->tooltips );
496 #else
497 g_object_ref( p->tooltips );
498 gtk_object_sink( p->tooltips );
499 #endif
500
501 RET();
502 }
503
504 void panel_set_orientation(panel *p)
505 {
506 GList* l;
507 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
508 ? ORIENT_HORIZ : ORIENT_VERT;
509 if (p->orientation == ORIENT_HORIZ) {
510 p->my_box_new = gtk_hbox_new;
511 p->my_separator_new = gtk_vseparator_new;
512 } else {
513 p->my_box_new = gtk_vbox_new;
514 p->my_separator_new = gtk_hseparator_new;
515 }
516
517 /* recreate the main layout box */
518 if( p->box ) {
519 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
520 if( GTK_WIDGET(newbox) != p->box ) {
521 p->box = GTK_WIDGET(newbox);
522 gtk_container_add( GTK_CONTAINER(p->bbox), GTK_WIDGET(newbox) );
523 }
524 }
525 /* NOTE: This loop won't be executed when panel started since
526 plugins are not loaded at that time.
527 This is used when the orientation of the panel is changed
528 from the config dialog, and plugins should be re-layout.
529 */
530 for( l = p->plugins; l; l = l->next ) {
531 plugin* pl = (plugin*)l->data;
532 if( pl->class->orientation ) {
533 pl->class->orientation( pl );
534 }
535 }
536 }
537
538 static int
539 panel_parse_global(panel *p, char **fp)
540 {
541 line s;
542 s.len = 256;
543
544 ENTER;
545 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
546 if (s.type == LINE_VAR) {
547 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
548 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
549 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
550 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
551 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
552 p->margin = atoi(s.t[1]);
553 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
554 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
555 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
556 p->width = atoi(s.t[1]);
557 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
558 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
559 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
560 p->height = atoi(s.t[1]);
561 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
562 p->spacing = atoi(s.t[1]);
563 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
564 p->setdocktype = str2num(bool_pair, s.t[1], 0);
565 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
566 p->setstrut = str2num(bool_pair, s.t[1], 0);
567 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
568 p->round_corners = str2num(bool_pair, s.t[1], 0);
569 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
570 p->transparent = str2num(bool_pair, s.t[1], 0);
571 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
572 p->alpha = atoi(s.t[1]);
573 if (p->alpha > 255)
574 p->alpha = 255;
575 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
576 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
577 gdk_color_parse ("white", &p->gtintcolor);
578 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
579 DBG("tintcolor=%x\n", p->tintcolor);
580 } else if( !g_ascii_strcasecmp(s.t[0], "FileManager") ) {
581 p->file_manager = g_strdup( s.t[1] );
582 } else if( !g_ascii_strcasecmp(s.t[0], "Terminal") ) {
583 p->terminal = g_strdup( s.t[1] );
584 } else if( !g_ascii_strcasecmp(s.t[0], "LogoutCommand") ) {
585 p->logout_command = g_strdup( s.t[1] );
586 } else {
587 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
588 RET(0);
589 }
590 } else if (s.type == LINE_BLOCK_END) {
591 break;
592 } else {
593 ERR( "lxpanel: illegal in this context %s\n", s.str);
594 RET(0);
595 }
596 }
597 panel_set_orientation( p );
598 if (p->width < 0)
599 p->width = 100;
600 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
601 p->width = 100;
602 p->heighttype = HEIGHT_PIXEL;
603 if (p->heighttype == HEIGHT_PIXEL) {
604 if (p->height < PANEL_HEIGHT_MIN)
605 p->height = PANEL_HEIGHT_MIN;
606 else if (p->height > PANEL_HEIGHT_MAX)
607 p->height = PANEL_HEIGHT_MAX;
608 }
609 p->curdesk = get_net_current_desktop();
610 p->desknum = get_net_number_of_desktops();
611 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
612 print_wmdata(p);
613
614 panel_start_gui(p);
615 RET(1);
616 }
617
618 static int
619 panel_parse_plugin(panel *p, char **fp)
620 {
621 line s;
622 plugin *plug = NULL;
623 gchar *type = NULL;
624 int expand , padding, border;
625 char* pconfig = NULL;
626
627 ENTER;
628 s.len = 256;
629 border = expand = padding = 0;
630 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
631 if (s.type == LINE_NONE) {
632 ERR( "lxpanel: bad line %s\n", s.str);
633 goto error;
634 }
635 if (s.type == LINE_VAR) {
636 if (!g_ascii_strcasecmp(s.t[0], "type")) {
637 type = g_strdup(s.t[1]);
638 DBG("plug %s\n", type);
639 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
640 expand = str2num(bool_pair, s.t[1], 0);
641 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
642 padding = atoi(s.t[1]);
643 else if (!g_ascii_strcasecmp(s.t[0], "border"))
644 border = atoi(s.t[1]);
645 else {
646 ERR( "lxpanel: unknown var %s\n", s.t[0]);
647 goto error;
648 }
649 } else if (s.type == LINE_BLOCK_START) {
650 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
651 pconfig = *fp;
652 int pno = 1;
653 while (pno) {
654 get_line_as_is(fp, &s);
655 if (s.type == LINE_NONE) {
656 ERR( "lxpanel: unexpected eof\n");
657 goto error;
658 } else if (s.type == LINE_BLOCK_START) {
659 pno++;
660 } else if (s.type == LINE_BLOCK_END) {
661 pno--;
662 }
663 }
664 } else {
665 ERR( "lxpanel: unknown block %s\n", s.t[0]);
666 goto error;
667 }
668 } else {
669 ERR( "lxpanel: illegal in this context %s\n", s.str);
670 goto error;
671 }
672 }
673
674 if (!type || !(plug = plugin_load(type))) {
675 ERR( "lxpanel: can't load %s plugin\n", type);
676 goto error;
677 }
678
679 plug->panel = p;
680 plug->expand = expand;
681 plug->padding = padding;
682 plug->border = border;
683 DBG("starting\n");
684 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
685 ERR( "lxpanel: can't start plugin %s\n", type);
686 goto error;
687 }
688 DBG("plug %s\n", type);
689 p->plugins = g_list_append(p->plugins, plug);
690
691 g_free( type );
692 RET(1);
693
694 error:
695 g_free(type);
696 if (plug)
697 plugin_put(plug);
698 RET(0);
699 }
700
701
702 static int
703 panel_start( panel *p, char **fp )
704 {
705 line s;
706
707 /* parse global section */
708 ENTER;
709 s.len = 256;
710 memset(p, 0, sizeof(panel));
711 p->allign = ALLIGN_CENTER;
712 p->edge = EDGE_BOTTOM;
713 p->widthtype = WIDTH_PERCENT;
714 p->width = 100;
715 p->heighttype = HEIGHT_PIXEL;
716 p->height = PANEL_HEIGHT_DEFAULT;
717 p->setdocktype = 1;
718 p->setstrut = 1;
719 p->round_corners = 0;
720 p->transparent = 0;
721 p->alpha = 127;
722 p->tintcolor = 0xFFFFFFFF;
723 p->spacing = 0;
724 fbev = fb_ev_new();
725 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
726 ERR( "lxpanel: config file must start from Global section\n");
727 RET(0);
728 }
729 if (!panel_parse_global(p, fp))
730 RET(0);
731
732 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
733 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
734 ERR( "lxpanel: expecting Plugin section\n");
735 RET(0);
736 }
737 panel_parse_plugin(p, fp);
738 }
739 gtk_widget_show_all(p->topgwin);
740 print_wmdata(p);
741 RET(1);
742 }
743
744 static void
745 delete_plugin(gpointer data, gpointer udata)
746 {
747 ENTER;
748 plugin_stop((plugin *)data);
749 plugin_put((plugin *)data);
750 RET();
751 }
752
753 void panel_stop(panel *p)
754 {
755 ENTER;
756
757 g_list_foreach(p->plugins, delete_plugin, NULL);
758 g_list_free(p->plugins);
759 p->plugins = NULL;
760
761 if( p->system_menus ){
762 do{
763 } while ( g_source_remove_by_user_data( p->system_menus ) );
764 }
765
766 g_object_unref( p->tooltips );
767
768 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
769 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, p);
770 gtk_widget_destroy(p->topgwin);
771 g_object_unref(fbev);
772 g_free(p->workarea);
773 g_free( p->file_manager );
774 g_free( p->terminal );
775 g_free( p->logout_command );
776 g_slist_free( p->system_menus );
777 gdk_flush();
778 XFlush(GDK_DISPLAY());
779 XSync(GDK_DISPLAY(), True);
780 RET();
781 }
782
783
784 static void
785 usage()
786 {
787 ENTER;
788 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
789 g_print(_("Command line options:\n"));
790 g_print(_(" --help -- print this help and exit\n"));
791 g_print(_(" --version -- print version and exit\n"));
792 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
793 g_print(_(" --configure -- launch configuration utility\n"));
794 g_print(_(" --profile name -- use specified profile\n"));
795 g_print("\n");
796 g_print(_(" -h -- same as --help\n"));
797 g_print(_(" -p -- same as --profile\n"));
798 g_print(_(" -v -- same as --version\n"));
799 g_print(_(" -C -- same as --configure\n"));
800 g_print(_("\nVisit http://lxpanel.sourceforge.net/ for detailed documentation,\n\n"));
801 }
802
803 static char*
804 load_profile(gchar *profile)
805 {
806 gchar *fname;
807 char* ret;
808
809 ENTER;
810 LOG(LOG_INFO, "loading %s profile\n", profile);
811 fname = g_strdup_printf("%s/.lxpanel/%s", getenv("HOME"), profile);
812 g_file_get_contents( fname, &ret, NULL, NULL );
813 if (ret) {
814 cfgfile = fname;
815 RET(ret);
816 }
817 //ERR("Can't load %s\n", fname);
818 g_free(fname);
819
820 /* check private configuration directory */
821 fname = g_strdup_printf(PACKAGE_DATA_DIR "/lxpanel/%s", profile);
822 g_file_get_contents( fname, &ret, NULL, NULL );
823 if (ret) {
824 cfgfile = fname;
825 RET(ret);
826 }
827 //ERR("Can't load %s\n", fname);
828 g_free(fname);
829 LOG(LOG_ERR, "Can't open '%s' profile\n", profile);
830 RET(NULL);
831 }
832
833 static void
834 handle_error(Display * d, XErrorEvent * ev)
835 {
836 char buf[256];
837
838 ENTER;
839 if (log_level >= LOG_WARN) {
840 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
841 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
842 }
843 RET();
844 }
845
846 /* Lightweight lock related functions - X clipboard hacks */
847
848 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
849
850 /*
851 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
852 */
853 static void
854 clipboard_get_func(
855 GtkClipboard *clipboard G_GNUC_UNUSED,
856 GtkSelectionData *selection_data G_GNUC_UNUSED,
857 guint info G_GNUC_UNUSED,
858 gpointer user_data_or_owner G_GNUC_UNUSED)
859 {
860 }
861
862 /*
863 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
864 */
865 static void clipboard_clear_func(
866 GtkClipboard *clipboard G_GNUC_UNUSED,
867 gpointer user_data_or_owner G_GNUC_UNUSED)
868 {
869 }
870
871 /*
872 * Lightweight version for checking single instance.
873 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
874 *
875 * Returns TRUE if successfully retrieved and FALSE otherwise.
876 */
877 static gboolean check_main_lock()
878 {
879 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
880 gboolean retval = FALSE;
881 GtkClipboard *clipboard;
882 Atom atom;
883
884 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
885
886 XGrabServer(GDK_DISPLAY());
887
888 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
889 goto out;
890
891 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
892
893 if (gtk_clipboard_set_with_data(clipboard, targets,
894 G_N_ELEMENTS (targets),
895 clipboard_get_func,
896 clipboard_clear_func, NULL))
897 retval = TRUE;
898
899 out:
900 XUngrabServer (GDK_DISPLAY ());
901 gdk_flush ();
902
903 return retval;
904 }
905 #undef CLIPBOARD_NAME
906
907 int
908 main(int argc, char *argv[], char *env[])
909 {
910 int i;
911 void configure();
912 char *fp, *pfp; /* point to current position of profile data in memory */
913
914 ENTER;
915 //printf("sizeof(gulong)=%d\n", sizeof(gulong));
916 setlocale(LC_CTYPE, "");
917
918 gtk_init(&argc, &argv);
919
920 #ifdef ENABLE_NLS
921 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
922 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
923 textdomain ( GETTEXT_PACKAGE );
924 #endif
925
926 XSetLocaleModifiers("");
927 XSetErrorHandler((XErrorHandler) handle_error);
928 resolve_atoms();
929 for (i = 1; i < argc; i++) {
930 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
931 usage();
932 exit(0);
933 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
934 printf("lxpanel %s\n", version);
935 exit(0);
936 } else if (!strcmp(argv[i], "--log")) {
937 i++;
938 if (i == argc) {
939 ERR( "lxpanel: missing log level\n");
940 usage();
941 exit(1);
942 } else {
943 log_level = atoi(argv[i]);
944 }
945 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
946 config = 1;
947 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
948 i++;
949 if (i == argc) {
950 ERR( "lxpanel: missing profile name\n");
951 usage();
952 exit(1);
953 } else {
954 cprofile = g_strdup(argv[i]);
955 }
956 } else {
957 printf("lxpanel: unknown option - %s\n", argv[i]);
958 usage();
959 exit(1);
960 }
961 }
962
963 /* Check for duplicated panel instances */
964 if (!check_main_lock() && !config) {
965 printf("There is alreay an instance of LXPanel. Now to exit\n");
966 exit(1);
967 }
968
969 /* Add our own icons to the search path of icon theme */
970 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
971 PACKAGE_DATA_DIR "/lxpanel/images" );
972
973 restart:
974 is_restarting = FALSE;
975
976 if (!(fp = pfp = load_profile(cprofile)))
977 exit(1);
978 p = g_new0(panel, 1);
979 g_return_val_if_fail (p != NULL, 1);
980 if (!panel_start(p, &pfp)) {
981 ERR( "lxpanel: can't start panel\n");
982 exit(1);
983 }
984 g_free( fp );
985 if (config)
986 configure();
987
988 gtk_main();
989 panel_stop(p);
990 g_free( cfgfile );
991 g_free(p);
992
993 if( is_restarting )
994 goto restart;
995
996 return 0;
997 }
998