Share one tooltip object among several plugins to save resource usage.
[lxde/lxpanel.git] / src / panel.c
CommitLineData
16fb8c2e 1/**
e68b47dc
JH
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
a52c2257
HJYP
55/****************************************************
56 * panel's handlers for WM events *
57 ****************************************************/
58/*
59static void
60panel_del_wm_strut(panel *p)
61{
62 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
63 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
64}
65*/
66
67
bee4c26e 68void panel_set_wm_strut(panel *p)
a52c2257
HJYP
69{
70 gulong data[12] = { 0 };
71 int i = 4;
72
73 ENTER;
74 if (!GTK_WIDGET_MAPPED (p->topgwin))
75 return;
bee4c26e
HJYP
76 if ( ! p->setstrut )
77 {
78 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
79 /* old spec, for wms that do not support STRUT_PARTIAL */
80 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
81 return;
82 }
83
a52c2257
HJYP
84 switch (p->edge) {
85 case EDGE_LEFT:
86 i = 0;
87 data[i] = p->aw;
88 data[4 + i*2] = p->ay;
89 data[5 + i*2] = p->ay + p->ah;
90 break;
91 case EDGE_RIGHT:
92 i = 1;
93 data[i] = p->aw;
94 data[4 + i*2] = p->ay;
95 data[5 + i*2] = p->ay + p->ah;
96 break;
97 case EDGE_TOP:
98 i = 2;
99 data[i] = p->ah;
100 data[4 + i*2] = p->ax;
101 data[5 + i*2] = p->ax + p->aw;
102 break;
103 case EDGE_BOTTOM:
104 i = 3;
105 data[i] = p->ah;
106 data[4 + i*2] = p->ax;
107 data[5 + i*2] = p->ax + p->aw;
108 break;
109 default:
110 ERR("wrong edge %d. strut won't be set\n", p->edge);
111 RET();
bee4c26e 112 }
a52c2257 113 DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]);
bee4c26e 114
a52c2257 115 /* if wm supports STRUT_PARTIAL it will ignore STRUT */
bee4c26e 116 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
a52c2257
HJYP
117 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12);
118 /* old spec, for wms that do not support STRUT_PARTIAL */
bee4c26e
HJYP
119 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
120 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4);
a52c2257
HJYP
121
122 RET();
123}
124
125static void
126print_wmdata(panel *p)
127{
128 int i;
129
130 ENTER;
131 RET();
132 DBG("desktop %d/%d\n", p->curdesk, p->desknum);
133 DBG("workarea\n");
134 for (i = 0; i < p->wa_len/4; i++)
135 DBG("(%d, %d) x (%d, %d)\n",
136 p->workarea[4*i + 0],
137 p->workarea[4*i + 1],
138 p->workarea[4*i + 2],
139 p->workarea[4*i + 3]);
140 RET();
141}
142
143
e996608e 144/* defined in plugins/menu.c */
8c44345a 145gboolean show_system_menu( gpointer system_menu );
e996608e
HJYP
146
147/* built-in commands, defined in configurator.c */
77886b88
HJYP
148void configure(void);
149void restart(void);
150void gtk_run(void);
151
8c44345a 152static void process_client_msg ( panel *p, XClientMessageEvent* ev )
77886b88 153{
8c44345a 154 int cmd = ev->data.b[0];
77886b88
HJYP
155 switch( cmd )
156 {
157 case LXPANEL_CMD_SYS_MENU:
5297da29 158 if( p->system_menus )
8c44345a 159 {
5297da29 160 /* show_system_menu( p->system_menus->data ); */
8c44345a
HJYP
161 /* FIXME: I've no idea why this doesn't work without timeout
162 under some WMs, like icewm. */
5297da29
HJYP
163 g_timeout_add( 200, (GSourceFunc)show_system_menu,
164 p->system_menus->data );
8c44345a 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
239cb032
HJYP
491 p->tooltips = gtk_tooltips_new();
492#if GLIB_CHECK_VERSION( 2, 10, 0 )
493 g_object_ref_sink( p->tooltips );
494#else
495 g_object_ref( p->tooltips );
496 gtk_object_sink( p->tooltips );
497#endif
498
a52c2257
HJYP
499 RET();
500}
501
a97d06a6
HJYP
502void panel_set_orientation(panel *p)
503{
504 GList* l;
505 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
506 ? ORIENT_HORIZ : ORIENT_VERT;
507 if (p->orientation == ORIENT_HORIZ) {
508 p->my_box_new = gtk_hbox_new;
509 p->my_separator_new = gtk_vseparator_new;
510 } else {
511 p->my_box_new = gtk_vbox_new;
512 p->my_separator_new = gtk_hseparator_new;
513 }
514
515 /* recreate the main layout box */
516 if( p->box ) {
5a343ad5
JH
517 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
518 if( GTK_WIDGET(newbox) != p->box ) {
519 p->box = GTK_WIDGET(newbox);
520 gtk_container_add( GTK_CONTAINER(p->bbox), GTK_WIDGET(newbox) );
a97d06a6
HJYP
521 }
522 }
523 /* NOTE: This loop won't be executed when panel started since
524 plugins are not loaded at that time.
525 This is used when the orientation of the panel is changed
526 from the config dialog, and plugins should be re-layout.
527 */
528 for( l = p->plugins; l; l = l->next ) {
529 plugin* pl = (plugin*)l->data;
530 if( pl->class->orientation ) {
531 pl->class->orientation( pl );
532 }
533 }
534}
535
a52c2257 536static int
db449f6e 537panel_parse_global(panel *p, char **fp)
a52c2257
HJYP
538{
539 line s;
540 s.len = 256;
6db11841 541
a52c2257 542 ENTER;
c69ac68e 543 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
544 if (s.type == LINE_VAR) {
545 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
546 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
547 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
548 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
549 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
550 p->margin = atoi(s.t[1]);
551 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
552 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
553 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
554 p->width = atoi(s.t[1]);
555 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
556 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
557 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
558 p->height = atoi(s.t[1]);
559 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
560 p->spacing = atoi(s.t[1]);
561 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
562 p->setdocktype = str2num(bool_pair, s.t[1], 0);
563 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
564 p->setstrut = str2num(bool_pair, s.t[1], 0);
565 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
566 p->round_corners = str2num(bool_pair, s.t[1], 0);
567 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
568 p->transparent = str2num(bool_pair, s.t[1], 0);
569 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
570 p->alpha = atoi(s.t[1]);
571 if (p->alpha > 255)
572 p->alpha = 255;
573 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
574 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
575 gdk_color_parse ("white", &p->gtintcolor);
576 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
577 DBG("tintcolor=%x\n", p->tintcolor);
239cb032
HJYP
578 } else if( !g_ascii_strcasecmp(s.t[0], "FileManager") ) {
579 p->file_manager = g_strdup( s.t[1] );
580 } else if( !g_ascii_strcasecmp(s.t[0], "Terminal") ) {
581 p->terminal = g_strdup( s.t[1] );
389975e0
HJYP
582 } else if( !g_ascii_strcasecmp(s.t[0], "LogoutCommand") ) {
583 p->logout_command = g_strdup( s.t[1] );
a52c2257
HJYP
584 } else {
585 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
586 RET(0);
587 }
588 } else if (s.type == LINE_BLOCK_END) {
589 break;
590 } else {
591 ERR( "lxpanel: illegal in this context %s\n", s.str);
592 RET(0);
593 }
594 }
a97d06a6 595 panel_set_orientation( p );
a52c2257
HJYP
596 if (p->width < 0)
597 p->width = 100;
598 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
599 p->width = 100;
600 p->heighttype = HEIGHT_PIXEL;
601 if (p->heighttype == HEIGHT_PIXEL) {
602 if (p->height < PANEL_HEIGHT_MIN)
603 p->height = PANEL_HEIGHT_MIN;
604 else if (p->height > PANEL_HEIGHT_MAX)
605 p->height = PANEL_HEIGHT_MAX;
606 }
607 p->curdesk = get_net_current_desktop();
608 p->desknum = get_net_number_of_desktops();
609 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
610 print_wmdata(p);
239cb032
HJYP
611
612 if( !p->file_manager )
613 p->file_manager = g_strdup( "pcmanfm %s" );
614 if( !p->terminal )
615 p->terminal = g_strdup( "x-terminal-emulator" );
616
a52c2257
HJYP
617 panel_start_gui(p);
618 RET(1);
619}
620
621static int
db449f6e 622panel_parse_plugin(panel *p, char **fp)
a52c2257
HJYP
623{
624 line s;
625 plugin *plug = NULL;
626 gchar *type = NULL;
a52c2257 627 int expand , padding, border;
db449f6e
HJYP
628 char* pconfig = NULL;
629
a52c2257
HJYP
630 ENTER;
631 s.len = 256;
a52c2257 632 border = expand = padding = 0;
c69ac68e 633 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
634 if (s.type == LINE_NONE) {
635 ERR( "lxpanel: bad line %s\n", s.str);
636 goto error;
637 }
638 if (s.type == LINE_VAR) {
639 if (!g_ascii_strcasecmp(s.t[0], "type")) {
640 type = g_strdup(s.t[1]);
641 DBG("plug %s\n", type);
642 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
643 expand = str2num(bool_pair, s.t[1], 0);
644 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
645 padding = atoi(s.t[1]);
646 else if (!g_ascii_strcasecmp(s.t[0], "border"))
647 border = atoi(s.t[1]);
648 else {
649 ERR( "lxpanel: unknown var %s\n", s.t[0]);
650 goto error;
651 }
652 } else if (s.type == LINE_BLOCK_START) {
653 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 654 pconfig = *fp;
a52c2257
HJYP
655 int pno = 1;
656 while (pno) {
657 get_line_as_is(fp, &s);
658 if (s.type == LINE_NONE) {
659 ERR( "lxpanel: unexpected eof\n");
660 goto error;
661 } else if (s.type == LINE_BLOCK_START) {
662 pno++;
663 } else if (s.type == LINE_BLOCK_END) {
664 pno--;
bee4c26e 665 }
db449f6e 666 }
a52c2257
HJYP
667 } else {
668 ERR( "lxpanel: unknown block %s\n", s.t[0]);
669 goto error;
670 }
671 } else {
672 ERR( "lxpanel: illegal in this context %s\n", s.str);
673 goto error;
674 }
675 }
db449f6e 676
a52c2257
HJYP
677 if (!type || !(plug = plugin_load(type))) {
678 ERR( "lxpanel: can't load %s plugin\n", type);
679 goto error;
680 }
db449f6e 681
a52c2257 682 plug->panel = p;
a52c2257
HJYP
683 plug->expand = expand;
684 plug->padding = padding;
685 plug->border = border;
a52c2257 686 DBG("starting\n");
db449f6e 687 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
688 ERR( "lxpanel: can't start plugin %s\n", type);
689 goto error;
690 }
691 DBG("plug %s\n", type);
692 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
693
694 g_free( type );
a52c2257 695 RET(1);
db449f6e 696
a52c2257 697 error:
a52c2257
HJYP
698 g_free(type);
699 if (plug)
700 plugin_put(plug);
701 RET(0);
a52c2257
HJYP
702}
703
704
0defe4b9 705static int
db449f6e 706panel_start( panel *p, char **fp )
a52c2257
HJYP
707{
708 line s;
db449f6e 709
a52c2257
HJYP
710 /* parse global section */
711 ENTER;
712 s.len = 256;
713 memset(p, 0, sizeof(panel));
714 p->allign = ALLIGN_CENTER;
715 p->edge = EDGE_BOTTOM;
716 p->widthtype = WIDTH_PERCENT;
717 p->width = 100;
718 p->heighttype = HEIGHT_PIXEL;
719 p->height = PANEL_HEIGHT_DEFAULT;
720 p->setdocktype = 1;
721 p->setstrut = 1;
722 p->round_corners = 0;
723 p->transparent = 0;
724 p->alpha = 127;
725 p->tintcolor = 0xFFFFFFFF;
726 p->spacing = 0;
727 fbev = fb_ev_new();
c69ac68e 728 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
729 ERR( "lxpanel: config file must start from Global section\n");
730 RET(0);
731 }
732 if (!panel_parse_global(p, fp))
733 RET(0);
734
c69ac68e 735 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
736 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
737 ERR( "lxpanel: expecting Plugin section\n");
738 RET(0);
739 }
bee4c26e 740 if (!panel_parse_plugin(p, fp))
a52c2257
HJYP
741 RET(0);
742 }
743 gtk_widget_show_all(p->topgwin);
744 print_wmdata(p);
745 RET(1);
746}
747
748static void
749delete_plugin(gpointer data, gpointer udata)
750{
751 ENTER;
752 plugin_stop((plugin *)data);
753 plugin_put((plugin *)data);
754 RET();
a52c2257
HJYP
755}
756
757void panel_stop(panel *p)
758{
759 ENTER;
760
761 g_list_foreach(p->plugins, delete_plugin, NULL);
762 g_list_free(p->plugins);
763 p->plugins = NULL;
8c44345a 764
5297da29 765 if( p->system_menus ){
8c44345a 766 do{
5297da29 767 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
768 }
769
239cb032
HJYP
770 g_object_unref( p->tooltips );
771
a52c2257
HJYP
772 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
773 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, p);
774 gtk_widget_destroy(p->topgwin);
775 g_object_unref(fbev);
776 g_free(p->workarea);
239cb032
HJYP
777 g_free( p->file_manager );
778 g_free( p->terminal );
389975e0 779 g_free( p->logout_command );
5297da29 780 g_slist_free( p->system_menus );
a52c2257
HJYP
781 gdk_flush();
782 XFlush(GDK_DISPLAY());
783 XSync(GDK_DISPLAY(), True);
784 RET();
785}
786
787
0defe4b9 788static void
a52c2257
HJYP
789usage()
790{
791 ENTER;
e7cb732b
HJYP
792 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
793 g_print(_("Command line options:\n"));
794 g_print(_(" --help -- print this help and exit\n"));
795 g_print(_(" --version -- print version and exit\n"));
796 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
797 g_print(_(" --configure -- launch configuration utility\n"));
798 g_print(_(" --profile name -- use specified profile\n"));
799 g_print("\n");
800 g_print(_(" -h -- same as --help\n"));
801 g_print(_(" -p -- same as --profile\n"));
802 g_print(_(" -v -- same as --version\n"));
803 g_print(_(" -C -- same as --configure\n"));
804 g_print(_("\nVisit http://lxpanel.sourceforge.net/ for detailed documentation,\n\n"));
a52c2257
HJYP
805}
806
e68b47dc 807static char*
db449f6e 808load_profile(gchar *profile)
a52c2257
HJYP
809{
810 gchar *fname;
db449f6e 811 char* ret;
a52c2257
HJYP
812
813 ENTER;
814 LOG(LOG_INFO, "loading %s profile\n", profile);
815 fname = g_strdup_printf("%s/.lxpanel/%s", getenv("HOME"), profile);
db449f6e
HJYP
816 g_file_get_contents( fname, &ret, NULL, NULL );
817 if (ret) {
a52c2257 818 cfgfile = fname;
db449f6e 819 RET(ret);
a52c2257
HJYP
820 }
821 //ERR("Can't load %s\n", fname);
822 g_free(fname);
db449f6e 823
a52c2257
HJYP
824 /* check private configuration directory */
825 fname = g_strdup_printf(PACKAGE_DATA_DIR "/lxpanel/%s", profile);
db449f6e
HJYP
826 g_file_get_contents( fname, &ret, NULL, NULL );
827 if (ret) {
a52c2257 828 cfgfile = fname;
db449f6e 829 RET(ret);
a52c2257
HJYP
830 }
831 //ERR("Can't load %s\n", fname);
832 g_free(fname);
833 LOG(LOG_ERR, "Can't open '%s' profile\n", profile);
834 RET(NULL);
835}
836
e68b47dc 837static void
a52c2257
HJYP
838handle_error(Display * d, XErrorEvent * ev)
839{
840 char buf[256];
841
842 ENTER;
843 if (log_level >= LOG_WARN) {
844 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
845 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
846 }
847 RET();
848}
849
e68b47dc
JH
850/* Lightweight lock related functions - X clipboard hacks */
851
852#define CLIPBOARD_NAME "LXPANEL_SELECTION"
853
854/*
855 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
856 */
857static void
858clipboard_get_func(
859 GtkClipboard *clipboard G_GNUC_UNUSED,
860 GtkSelectionData *selection_data G_GNUC_UNUSED,
861 guint info G_GNUC_UNUSED,
862 gpointer user_data_or_owner G_GNUC_UNUSED)
863{
864}
865
866/*
867 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
868 */
869static void clipboard_clear_func(
870 GtkClipboard *clipboard G_GNUC_UNUSED,
871 gpointer user_data_or_owner G_GNUC_UNUSED)
872{
873}
874
875/*
876 * Lightweight version for checking single instance.
877 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
878 *
879 * Returns TRUE if successfully retrieved and FALSE otherwise.
880 */
16fb8c2e 881static gboolean check_main_lock()
e68b47dc
JH
882{
883 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
884 gboolean retval = FALSE;
885 GtkClipboard *clipboard;
886 Atom atom;
887
888 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
889
890 XGrabServer(GDK_DISPLAY());
891
892 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
893 goto out;
894
895 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
896
897 if (gtk_clipboard_set_with_data(clipboard, targets,
898 G_N_ELEMENTS (targets),
899 clipboard_get_func,
900 clipboard_clear_func, NULL))
901 retval = TRUE;
902
903out:
904 XUngrabServer (GDK_DISPLAY ());
905 gdk_flush ();
906
907 return retval;
908}
909#undef CLIPBOARD_NAME
910
a52c2257
HJYP
911int
912main(int argc, char *argv[], char *env[])
913{
914 int i;
a52c2257 915 void configure();
db449f6e 916 char *fp, *pfp; /* point to current position of profile data in memory */
f277dbb7 917
a52c2257
HJYP
918 ENTER;
919 //printf("sizeof(gulong)=%d\n", sizeof(gulong));
920 setlocale(LC_CTYPE, "");
f277dbb7 921
a52c2257
HJYP
922 gtk_init(&argc, &argv);
923
924#ifdef ENABLE_NLS
925 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
926 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
927 textdomain ( GETTEXT_PACKAGE );
928#endif
929
930 XSetLocaleModifiers("");
931 XSetErrorHandler((XErrorHandler) handle_error);
932 resolve_atoms();
933 for (i = 1; i < argc; i++) {
934 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
935 usage();
936 exit(0);
937 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
938 printf("lxpanel %s\n", version);
939 exit(0);
940 } else if (!strcmp(argv[i], "--log")) {
941 i++;
942 if (i == argc) {
943 ERR( "lxpanel: missing log level\n");
944 usage();
945 exit(1);
946 } else {
947 log_level = atoi(argv[i]);
948 }
949 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
950 config = 1;
951 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
952 i++;
953 if (i == argc) {
954 ERR( "lxpanel: missing profile name\n");
955 usage();
956 exit(1);
957 } else {
958 cprofile = g_strdup(argv[i]);
959 }
960 } else {
961 printf("lxpanel: unknown option - %s\n", argv[i]);
962 usage();
963 exit(1);
964 }
965 }
f277dbb7 966
e68b47dc
JH
967 /* Check for duplicated panel instances */
968 if (!check_main_lock() && !config) {
969 printf("There is alreay an instance of LXPanel. Now to exit\n");
970 exit(1);
971 }
972
f277dbb7
HJYP
973 /* Add our own icons to the search path of icon theme */
974 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
975 PACKAGE_DATA_DIR "/lxpanel/images" );
976
6a6ad54e
HJYP
977 if (!(fp = pfp = load_profile(cprofile)))
978 exit(1);
979 p = g_new0(panel, 1);
980 g_return_val_if_fail (p != NULL, 1);
981 if (!panel_start(p, &pfp)) {
982 ERR( "lxpanel: can't start panel\n");
983 exit(1);
5541b8d2 984 }
6a6ad54e
HJYP
985 g_free( fp );
986 if (config)
987 configure();
988
989 gtk_main();
990 panel_stop(p);
991 g_free( cfgfile );
992 g_free(p);
5541b8d2
JH
993
994 return 0;
a52c2257
HJYP
995}
996