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