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