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