1 
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <errno.h>
8 #include <locale.h>
9 #include <string.h>
10 #include <signal.h>
11 #include <time.h>
12 
13 #include "plugin.h"
14 #include "panel.h"
15 #include "misc.h"
16 #include "bg.h"
17 #include "gtkbgbox.h"
18 
19 
20 static gchar version[] = VERSION;
21 static gchar *profile = "default";
22 static gchar *profile_file;
23 
24 guint mwid; // mouse watcher thread id
25 guint hpid; // hide panel thread id
26 
27 
28 FbEv *fbev;
29 gint force_quit = 0;
30 int config;
31 
32 //#define DEBUGPRN
33 #include "dbg.h"
34 
35 /** verbosity level of dbg and log functions */
36 int log_level = LOG_WARN;
37 
38 static panel *p;
39 panel *the_panel;
40 
41 void
panel_set_wm_strut(panel * p)42 panel_set_wm_strut(panel *p)
43 {
44     gulong data[12] = { 0 };
45     int i = 4;
46 
47     ENTER;
48     if (!GTK_WIDGET_MAPPED(p->topgwin))
49         return;
50     /* most wm's tend to ignore struts of unmapped windows, and that's how
51      * fbpanel hides itself. so no reason to set it. */
52     if (p->autohide)
53         return;
54     switch (p->edge) {
55     case EDGE_LEFT:
56         i = 0;
57         data[i] = p->aw;
58         data[4 + i*2] = p->ay;
59         data[5 + i*2] = p->ay + p->ah;
60         if (p->autohide) data[i] = p->height_when_hidden;
61         break;
62     case EDGE_RIGHT:
63         i = 1;
64         data[i] = p->aw;
65         data[4 + i*2] = p->ay;
66         data[5 + i*2] = p->ay + p->ah;
67         if (p->autohide) data[i] = p->height_when_hidden;
68         break;
69     case EDGE_TOP:
70         i = 2;
71         data[i] = p->ah;
72         data[4 + i*2] = p->ax;
73         data[5 + i*2] = p->ax + p->aw;
74         if (p->autohide) data[i] = p->height_when_hidden;
75         break;
76     case EDGE_BOTTOM:
77         i = 3;
78         data[i] = p->ah;
79         data[4 + i*2] = p->ax;
80         data[5 + i*2] = p->ax + p->aw;
81         if (p->autohide) data[i] = p->height_when_hidden;
82         break;
83     default:
84         ERR("wrong edge %d. strut won't be set\n", p->edge);
85         RET();
86     }
87     DBG("type %d. width %ld. from %ld to %ld\n", i, data[i], data[4 + i*2],
88           data[5 + i*2]);
89 
90     /* if wm supports STRUT_PARTIAL it will ignore STRUT */
91     XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
92         XA_CARDINAL, 32, PropModeReplace,  (unsigned char *) data, 12);
93     /* old spec, for wms that do not support STRUT_PARTIAL */
94     XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
95         XA_CARDINAL, 32, PropModeReplace,  (unsigned char *) data, 4);
96 
97     RET();
98 }
99 #if 0
100 static void
101 print_wmdata(panel *p)
102 {
103     int i;
104 
105     ENTER;
106     RET();
107     DBG("desktop %d/%d\n", p->curdesk, p->desknum);
108     DBG("workarea\n");
109     for (i = 0; i < p->wa_len/4; i++)
110         DBG("(%d, %d) x (%d, %d)\n",
111               p->workarea[4*i + 0],
112               p->workarea[4*i + 1],
113               p->workarea[4*i + 2],
114               p->workarea[4*i + 3]);
115     RET();
116 }
117 #endif
118 
119 static GdkFilterReturn
panel_event_filter(GdkXEvent * xevent,GdkEvent * event,panel * p)120 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, panel *p)
121 {
122     Atom at;
123     Window win;
124     XEvent *ev = (XEvent *) xevent;
125 
126     ENTER;
127     DBG("win = 0x%lx\n", ev->xproperty.window);
128     if (ev->type != PropertyNotify )
129         RET(GDK_FILTER_CONTINUE);
130 
131     at = ev->xproperty.atom;
132     win = ev->xproperty.window;
133     DBG("win=%lx at=%ld\n", win, at);
134     if (win == GDK_ROOT_WINDOW()) {
135         if (at == a_NET_CLIENT_LIST) {
136             DBG("A_NET_CLIENT_LIST\n");
137             fb_ev_trigger(fbev, EV_CLIENT_LIST);
138         } else if (at == a_NET_CURRENT_DESKTOP) {
139             DBG("A_NET_CURRENT_DESKTOP\n");
140             p->curdesk = get_net_current_desktop();
141             fb_ev_trigger(fbev, EV_CURRENT_DESKTOP);
142         } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
143             DBG("A_NET_NUMBER_OF_DESKTOPS\n");
144             p->desknum = get_net_number_of_desktops();
145             fb_ev_trigger(fbev, EV_NUMBER_OF_DESKTOPS);
146         } else if (at == a_NET_DESKTOP_NAMES) {
147             DBG("A_NET_DESKTOP_NAMES\n");
148             fb_ev_trigger(fbev, EV_DESKTOP_NAMES);
149         } else if (at == a_NET_ACTIVE_WINDOW) {
150             DBG("A_NET_ACTIVE_WINDOW\n");
151             fb_ev_trigger(fbev, EV_ACTIVE_WINDOW);
152         }else if (at == a_NET_CLIENT_LIST_STACKING) {
153             DBG("A_NET_CLIENT_LIST_STACKING\n");
154             fb_ev_trigger(fbev, EV_CLIENT_LIST_STACKING);
155         } else if (at == a_NET_WORKAREA) {
156             DBG("A_NET_WORKAREA\n");
157             //p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA,
158             //      XA_CARDINAL, &p->wa_len);
159             //print_wmdata(p);
160         } else if (at == a_XROOTPMAP_ID) {
161             if (p->transparent)
162                 fb_bg_notify_changed_bg(p->bg);
163         } else if (at == a_NET_DESKTOP_GEOMETRY) {
164             DBG("a_NET_DESKTOP_GEOMETRY\n");
165             gtk_main_quit();
166         } else
167             RET(GDK_FILTER_CONTINUE);
168         RET(GDK_FILTER_REMOVE);
169     }
170     DBG("non root %lx\n", win);
171     RET(GDK_FILTER_CONTINUE);
172 }
173 
174 /****************************************************
175  *         panel's handlers for GTK events          *
176  ****************************************************/
177 
178 static gint
panel_destroy_event(GtkWidget * widget,GdkEvent * event,gpointer data)179 panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
180 {
181     ENTER;
182     gtk_main_quit();
183     force_quit = 1;
184     RET(FALSE);
185 }
186 
187 static void
panel_size_req(GtkWidget * widget,GtkRequisition * req,panel * p)188 panel_size_req(GtkWidget *widget, GtkRequisition *req, panel *p)
189 {
190     ENTER;
191     DBG("IN req=(%d, %d)\n", req->width, req->height);
192     if (p->widthtype == WIDTH_REQUEST)
193         p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->width : req->height;
194     if (p->heighttype == HEIGHT_REQUEST)
195         p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->height : req->width;
196     calculate_position(p);
197     req->width  = p->aw;
198     req->height = p->ah;
199     DBG("OUT req=(%d, %d)\n", req->width, req->height);
200     RET();
201 }
202 
203 static void
make_round_corners(panel * p)204 make_round_corners(panel *p)
205 {
206     GdkBitmap *b;
207     GdkGC* gc;
208     GdkColor black = { 0, 0, 0, 0};
209     GdkColor white = { 1, 0xffff, 0xffff, 0xffff};
210     int w, h, r, br;
211 
212     ENTER;
213     w = p->aw;
214     h = p->ah;
215     r = p->round_corners_radius;
216     if (2*r > MIN(w, h)) {
217         r = MIN(w, h) / 2;
218         DBG("chaning radius to %d\n", r);
219     }
220     b = gdk_pixmap_new(NULL, w, h, 1);
221     gc = gdk_gc_new(GDK_DRAWABLE(b));
222     gdk_gc_set_foreground(gc, &black);
223     gdk_draw_rectangle(GDK_DRAWABLE(b), gc, TRUE, 0, 0, w, h);
224     gdk_gc_set_foreground(gc, &white);
225     gdk_draw_rectangle(GDK_DRAWABLE(b), gc, TRUE, r, 0, w-2*r, h);
226     gdk_draw_rectangle(GDK_DRAWABLE(b), gc, TRUE, 0, r, r, h-2*r);
227     gdk_draw_rectangle(GDK_DRAWABLE(b), gc, TRUE, w-r, r, r, h-2*r);
228 
229     br = 2 * r;
230     gdk_draw_arc(GDK_DRAWABLE(b), gc, TRUE, 0, 0, br, br, 0*64, 360*64);
231     gdk_draw_arc(GDK_DRAWABLE(b), gc, TRUE, 0, h-br-1, br, br, 0*64, 360*64);
232     gdk_draw_arc(GDK_DRAWABLE(b), gc, TRUE, w-br, 0, br, br, 0*64, 360*64);
233     gdk_draw_arc(GDK_DRAWABLE(b), gc, TRUE, w-br, h-br-1, br, br, 0*64, 360*64);
234 
235     gtk_widget_shape_combine_mask(p->topgwin, b, 0, 0);
236     g_object_unref(gc);
237     g_object_unref(b);
238 
239     RET();
240 }
241 
242 static gboolean
panel_configure_event(GtkWidget * widget,GdkEventConfigure * e,panel * p)243 panel_configure_event(GtkWidget *widget, GdkEventConfigure *e, panel *p)
244 {
245     ENTER;
246     DBG("cur geom: %dx%d+%d+%d\n", e->width, e->height, e->x, e->y);
247     DBG("req geom: %dx%d+%d+%d\n", p->aw, p->ah, p->ax, p->ay);
248     if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y ==
249             p->cy) {
250         DBG("dup. exiting\n");
251         RET(FALSE);
252     }
253     /* save current geometry */
254     p->cw = e->width;
255     p->ch = e->height;
256     p->cx = e->x;
257     p->cy = e->y;
258 
259     /* if panel size is not we have requested, just wait, it will */
260     if (e->width != p->aw || e->height != p->ah) {
261         DBG("size_req not yet ready. exiting\n");
262         RET(FALSE);
263     }
264 
265     /* if panel wasn't at requested position, then send another request */
266     if (e->x != p->ax || e->y != p->ay) {
267         DBG("move %d,%d\n", p->ax, p->ay);
268         gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
269         RET(FALSE);
270     }
271 
272     /* panel is at right place, lets go on */
273     if (p->transparent) {
274         fb_bg_notify_changed_bg(p->bg);
275         DBG("remake bg image\n");
276     }
277     if (p->setstrut) {
278         panel_set_wm_strut(p);
279         DBG("set_wm_strut\n");
280     }
281     if (p->round_corners) {
282         make_round_corners(p);
283         DBG("make_round_corners\n");
284     }
285     gtk_widget_show(p->topgwin);
286     if (p->setstrut) {
287         panel_set_wm_strut(p);
288         DBG("set_wm_strut\n");
289     }
290     RET(FALSE);
291 
292 }
293 
294 /****************************************************
295  *         autohide                                 *
296  ****************************************************/
297 
298 /* Autohide is behaviour when panel hides itself when mouse is "far enough"
299  * and pops up again when mouse comes "close enough".
300  * Formally, it's a state machine with 3 states that driven by mouse
301  * coordinates and timer:
302  * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
303  *      switches to WAITING state
304  * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
305  *      switches to VISIBLE.  If timer expires, switches to HIDDEN
306  * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
307  *
308  * Note 1
309  * Mouse coordinates are queried every PERIOD milisec
310  *
311  * Note 2
312  * If mouse is less then GAP pixels to panel it's considered to be close,
313  * otherwise it's far
314  */
315 
316 #define GAP 2
317 #define PERIOD 300
318 
319 static gboolean ah_state_visible(panel *p);
320 static gboolean ah_state_waiting(panel *p);
321 static gboolean ah_state_hidden(panel *p);
322 
323 static gboolean
panel_mapped(GtkWidget * widget,GdkEvent * event,panel * p)324 panel_mapped(GtkWidget *widget, GdkEvent *event, panel *p)
325 {
326     ENTER;
327     if (p->autohide) {
328         ah_stop(p);
329         ah_start(p);
330     }
331     RET(FALSE);
332 }
333 
334 static gboolean
mouse_watch(panel * p)335 mouse_watch(panel *p)
336 {
337     gint x, y;
338 
339     ENTER;
340     gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
341 
342 /*  Reduce sensitivity area
343     p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
344         || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
345 */
346 
347     gint cx, cy, cw, ch;
348 
349     cx = p->cx;
350     cy = p->cy;
351     cw = p->aw;
352     ch = p->ah;
353 
354     /* reduce area which will raise panel so it does not interfere with apps */
355     if (p->ah_state == ah_state_hidden) {
356         switch (p->edge) {
357         case EDGE_LEFT:
358             cw = GAP;
359             break;
360         case EDGE_RIGHT:
361             cx = cx + cw - GAP;
362             cw = GAP;
363             break;
364         case EDGE_TOP:
365             ch = GAP;
366             break;
367         case EDGE_BOTTOM:
368             cy = cy + ch - GAP;
369             ch = GAP;
370             break;
371        }
372     }
373     p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
374 
375     p->ah_state(p);
376     RET(TRUE);
377 }
378 
379 static gboolean
ah_state_visible(panel * p)380 ah_state_visible(panel *p)
381 {
382     ENTER;
383     if (p->ah_state != ah_state_visible) {
384         p->ah_state = ah_state_visible;
385         gtk_widget_show(p->topgwin);
386         gtk_window_stick(GTK_WINDOW(p->topgwin));
387     } else if (p->ah_far) {
388         ah_state_waiting(p);
389     }
390     RET(FALSE);
391 }
392 
393 static gboolean
ah_state_waiting(panel * p)394 ah_state_waiting(panel *p)
395 {
396     ENTER;
397     if (p->ah_state != ah_state_waiting) {
398         p->ah_state = ah_state_waiting;
399         hpid = g_timeout_add(2 * PERIOD, (GSourceFunc) ah_state_hidden, p);
400     } else if (!p->ah_far) {
401         g_source_remove(hpid);
402         hpid = 0;
403         ah_state_visible(p);
404     }
405     RET(FALSE);
406 }
407 
408 static gboolean
ah_state_hidden(panel * p)409 ah_state_hidden(panel *p)
410 {
411     ENTER;
412     if (p->ah_state != ah_state_hidden) {
413         p->ah_state = ah_state_hidden;
414         gtk_widget_hide(p->topgwin);
415     } else if (!p->ah_far) {
416         ah_state_visible(p);
417     }
418     RET(FALSE);
419 }
420 
421 /* starts autohide behaviour */
422 void
ah_start(panel * p)423 ah_start(panel *p)
424 {
425     ENTER;
426     mwid = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
427     ah_state_visible(p);
428     RET();
429 }
430 
431 /* stops autohide */
432 void
ah_stop(panel * p)433 ah_stop(panel *p)
434 {
435     ENTER;
436     if (mwid) {
437         g_source_remove(mwid);
438         mwid = 0;
439     }
440     if (hpid) {
441         g_source_remove(hpid);
442         hpid = 0;
443     }
444     RET();
445 }
446 
447 /****************************************************
448  *         panel creation                           *
449  ****************************************************/
450 
451 gboolean
panel_button_press_event(GtkWidget * widget,GdkEventButton * event,panel * p)452 panel_button_press_event(GtkWidget *widget, GdkEventButton *event, panel *p)
453 {
454     ENTER;
455     if (event->type == GDK_BUTTON_PRESS && event->button == 3
456           && event->state & GDK_CONTROL_MASK) {
457         DBG("ctrl-btn3\n");
458         configure(p->xc);
459         RET(TRUE);
460     }
461     RET(FALSE);
462 }
463 
464 static gboolean
panel_scroll_event(GtkWidget * widget,GdkEventScroll * event,panel * p)465 panel_scroll_event(GtkWidget *widget, GdkEventScroll *event, panel *p)
466 {
467     int i;
468 
469     ENTER;
470     DBG("scroll direction = %d\n", event->direction);
471     i = p->curdesk;
472     if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
473         i--;
474         if (i < 0)
475             i = p->desknum - 1;
476     } else {
477         i++;
478         if (i >= p->desknum)
479             i = 0;
480     }
481     Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, i, 0, 0, 0, 0);
482     RET(TRUE);
483 }
484 
485 
486 static void
panel_start_gui(panel * p)487 panel_start_gui(panel *p)
488 {
489     ENTER;
490 
491     // main toplevel window
492     p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
493     gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
494     g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
495         (GCallback) panel_destroy_event, p);
496     g_signal_connect(G_OBJECT(p->topgwin), "size-request",
497         (GCallback) panel_size_req, p);
498     g_signal_connect(G_OBJECT(p->topgwin), "map-event",
499         (GCallback) panel_mapped, p);
500     g_signal_connect(G_OBJECT(p->topgwin), "configure-event",
501         (GCallback) panel_configure_event, p);
502     g_signal_connect(G_OBJECT(p->topgwin), "button-press-event",
503         (GCallback) panel_button_press_event, p);
504     g_signal_connect(G_OBJECT(p->topgwin), "scroll-event",
505         (GCallback) panel_scroll_event, p);
506 
507     gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
508     gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "fbpanel");
509     gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
510     gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
511     gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
512     gtk_window_set_accept_focus(GTK_WINDOW(p->topgwin), FALSE);
513     if (p->setdocktype)
514         gtk_window_set_type_hint(GTK_WINDOW(p->topgwin),
515             GDK_WINDOW_TYPE_HINT_DOCK);
516 
517     if (p->layer == LAYER_ABOVE)
518         gtk_window_set_keep_above(GTK_WINDOW(p->topgwin), TRUE);
519     else if (p->layer == LAYER_BELOW)
520         gtk_window_set_keep_below(GTK_WINDOW(p->topgwin), TRUE);
521     gtk_window_stick(GTK_WINDOW(p->topgwin));
522 
523     gtk_widget_realize(p->topgwin);
524     p->topxwin = GDK_WINDOW_XWINDOW(p->topgwin->window);
525     DBG("topxwin = %lx\n", p->topxwin);
526     /* ensure configure event */
527     XMoveWindow(GDK_DISPLAY(), p->topxwin, 20, 20);
528     XSync(GDK_DISPLAY(), False);
529 
530     gtk_widget_set_app_paintable(p->topgwin, TRUE);
531     calculate_position(p);
532     gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
533     gtk_window_resize(GTK_WINDOW(p->topgwin), p->aw, p->ah);
534     DBG("move-resize x %d y %d w %d h %d\n", p->ax, p->ay, p->aw, p->ah);
535     //XSync(GDK_DISPLAY(), False);
536     //gdk_flush();
537 
538     // background box all over toplevel
539     p->bbox = gtk_bgbox_new();
540     gtk_container_add(GTK_CONTAINER(p->topgwin), p->bbox);
541     gtk_container_set_border_width(GTK_CONTAINER(p->bbox), 0);
542     if (p->transparent) {
543         p->bg = fb_bg_get_for_display();
544         gtk_bgbox_set_background(p->bbox, BG_ROOT, p->tintcolor, p->alpha);
545     }
546 
547     // main layout manager as a single child of background widget box
548     p->lbox = p->my_box_new(FALSE, 0);
549     gtk_container_set_border_width(GTK_CONTAINER(p->lbox), 0);
550     gtk_container_add(GTK_CONTAINER(p->bbox), p->lbox);
551 
552     p->box = p->my_box_new(FALSE, p->spacing);
553     gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
554     gtk_box_pack_start(GTK_BOX(p->lbox), p->box, TRUE, TRUE,
555         (p->round_corners) ? p->round_corners_radius : 0);
556     if (p->round_corners) {
557         make_round_corners(p);
558         DBG("make_round_corners\n");
559     }
560     /* start window creation process */
561     gtk_widget_show_all(p->topgwin);
562     /* .. and hide it from user until everything is done */
563     gtk_widget_hide(p->topgwin);
564 
565     if (p->setstrut)
566         panel_set_wm_strut(p);
567 
568     XSelectInput(GDK_DISPLAY(), GDK_ROOT_WINDOW(), PropertyChangeMask);
569     gdk_window_add_filter(gdk_get_default_root_window(),
570           (GdkFilterFunc)panel_event_filter, p);
571     //XSync(GDK_DISPLAY(), False);
572     gdk_flush();
573     RET();
574 }
575 
576 static int
panel_parse_global(xconf * xc)577 panel_parse_global(xconf *xc)
578 {
579     ENTER;
580     /* Set default values */
581     p->allign = ALLIGN_CENTER;
582     p->edge = EDGE_BOTTOM;
583     p->widthtype = WIDTH_PERCENT;
584     p->width = 100;
585     p->heighttype = HEIGHT_PIXEL;
586     p->height = PANEL_HEIGHT_DEFAULT;
587     p->max_elem_height = PANEL_HEIGHT_MAX;
588     p->setdocktype = 1;
589     p->setstrut = 1;
590     p->round_corners = 1;
591     p->round_corners_radius = 7;
592     p->autohide = 0;
593     p->height_when_hidden = 2;
594     p->transparent = 0;
595     p->alpha = 127;
596     p->tintcolor_name = "white";
597     p->spacing = 0;
598     p->setlayer = FALSE;
599     p->layer = LAYER_ABOVE;
600 
601     /* Read config */
602     /* geometry */
603     XCG(xc, "edge", &p->edge, enum, edge_enum);
604     XCG(xc, "allign", &p->allign, enum, allign_enum);
605     XCG(xc, "widthtype", &p->widthtype, enum, widthtype_enum);
606     XCG(xc, "heighttype", &p->heighttype, enum, heighttype_enum);
607     XCG(xc, "width", &p->width, int);
608     XCG(xc, "height", &p->height, int);
609     XCG(xc, "margin", &p->margin, int);
610 
611     /* properties */
612     XCG(xc, "setdocktype", &p->setdocktype, enum, bool_enum);
613     XCG(xc, "setpartialstrut", &p->setstrut, enum, bool_enum);
614     XCG(xc, "autohide", &p->autohide, enum, bool_enum);
615     XCG(xc, "heightwhenhidden", &p->height_when_hidden, int);
616     XCG(xc, "setlayer", &p->setlayer, enum, bool_enum);
617     XCG(xc, "layer", &p->layer, enum, layer_enum);
618 
619     /* effects */
620     XCG(xc, "roundcorners", &p->round_corners, enum, bool_enum);
621     XCG(xc, "roundcornersradius", &p->round_corners_radius, int);
622     XCG(xc, "transparent", &p->transparent, enum, bool_enum);
623     XCG(xc, "alpha", &p->alpha, int);
624     XCG(xc, "tintcolor", &p->tintcolor_name, str);
625     XCG(xc, "maxelemheight", &p->max_elem_height, int);
626 
627     /* Sanity checks */
628     if (!gdk_color_parse(p->tintcolor_name, &p->gtintcolor))
629         gdk_color_parse("white", &p->gtintcolor);
630     p->tintcolor = gcolor2rgb24(&p->gtintcolor);
631     DBG("tintcolor=%x\n", p->tintcolor);
632     if (p->alpha > 255)
633         p->alpha = 255;
634     p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
635         ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
636     if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
637         p->my_box_new = gtk_hbox_new;
638         p->my_separator_new = gtk_vseparator_new;
639     } else {
640         p->my_box_new = gtk_vbox_new;
641         p->my_separator_new = gtk_hseparator_new;
642     }
643     if (p->width < 0)
644         p->width = 100;
645     if (p->widthtype == WIDTH_PERCENT && p->width > 100)
646         p->width = 100;
647     p->heighttype = HEIGHT_PIXEL;
648     if (p->heighttype == HEIGHT_PIXEL) {
649         if (p->height < PANEL_HEIGHT_MIN)
650             p->height = PANEL_HEIGHT_MIN;
651         else if (p->height > PANEL_HEIGHT_MAX)
652             p->height = PANEL_HEIGHT_MAX;
653     }
654     if (p->max_elem_height > p->height ||
655             p->max_elem_height < PANEL_HEIGHT_MIN)
656         p->max_elem_height = p->height;
657     p->curdesk = get_net_current_desktop();
658     p->desknum = get_net_number_of_desktops();
659     //p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA,
660     //    XA_CARDINAL, &p->wa_len);
661     //print_wmdata(p);
662     panel_start_gui(p);
663     RET(1);
664 }
665 
666 static void
panel_parse_plugin(xconf * xc)667 panel_parse_plugin(xconf *xc)
668 {
669     plugin_instance *plug = NULL;
670     gchar *type = NULL;
671 
672     ENTER;
673     xconf_get_str(xconf_find(xc, "type", 0), &type);
674     if (!type || !(plug = plugin_load(type))) {
675         ERR( "fbpanel: can't load %s plugin\n", type);
676         return;
677     }
678     plug->panel = p;
679     XCG(xc, "expand", &plug->expand, enum, bool_enum);
680     XCG(xc, "padding", &plug->padding, int);
681     XCG(xc, "border", &plug->border, int);
682     plug->xc = xconf_find(xc, "config", 0);
683 
684     if (!plugin_start(plug)) {
685         ERR( "fbpanel: can't start plugin %s\n", type);
686         exit(1);
687     }
688     p->plugins = g_list_append(p->plugins, plug);
689 }
690 
691 static void
panel_start(xconf * xc)692 panel_start(xconf *xc)
693 {
694     int i;
695     xconf *pxc;
696 
697     ENTER;
698     fbev = fb_ev_new();
699 
700     //xconf_prn(stdout, xc, 0, FALSE);
701     panel_parse_global(xconf_find(xc, "global", 0));
702     for (i = 0; (pxc = xconf_find(xc, "plugin", i)); i++)
703         panel_parse_plugin(pxc);
704     RET();
705 }
706 
707 static void
delete_plugin(gpointer data,gpointer udata)708 delete_plugin(gpointer data, gpointer udata)
709 {
710     ENTER;
711     plugin_stop((plugin_instance *)data);
712     plugin_put((plugin_instance *)data);
713     RET();
714 }
715 
716 static void
panel_stop(panel * p)717 panel_stop(panel *p)
718 {
719     ENTER;
720 
721     if (p->autohide)
722         ah_stop(p);
723     g_list_foreach(p->plugins, delete_plugin, NULL);
724     g_list_free(p->plugins);
725     p->plugins = NULL;
726 
727     XSelectInput(GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
728     gdk_window_remove_filter(gdk_get_default_root_window(),
729           (GdkFilterFunc)panel_event_filter, p);
730     gtk_widget_destroy(p->topgwin);
731     g_object_unref(fbev);
732     //g_free(p->workarea);
733     gdk_flush();
734     XFlush(GDK_DISPLAY());
735     XSync(GDK_DISPLAY(), True);
736     RET();
737 }
738 
739 void
usage()740 usage()
741 {
742     ENTER;
743     printf("fbpanel %s - lightweight GTK2+ panel for UNIX desktops\n", version);
744     printf("Command line options:\n");
745     printf(" --help      -- print this help and exit\n");
746     printf(" --version   -- print version and exit\n");
747     printf(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n");
748     printf(" --configure -- launch configuration utility\n");
749     printf(" --profile name -- use specified profile\n");
750     printf("\n");
751     printf(" -h  -- same as --help\n");
752     printf(" -p  -- same as --profile\n");
753     printf(" -v  -- same as --version\n");
754     printf(" -C  -- same as --configure\n");
755     printf("\nVisit http://fbpanel.sourceforge.net/ for detailed documentation,\n\n");
756 }
757 
758 void
handle_error(Display * d,XErrorEvent * ev)759 handle_error(Display * d, XErrorEvent * ev)
760 {
761     char buf[256];
762 
763     ENTER;
764     XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
765     DBG("fbpanel : X error: %s\n", buf);
766 
767     RET();
768 }
769 
770 static void
sig_usr1(int signum)771 sig_usr1(int signum)
772 {
773     if (signum != SIGUSR1)
774         return;
775     gtk_main_quit();
776 }
777 
778 static void
sig_usr2(int signum)779 sig_usr2(int signum)
780 {
781     if (signum != SIGUSR2)
782         return;
783     gtk_main_quit();
784     force_quit = 1;
785 }
786 
787 static void
do_argv(int argc,char * argv[])788 do_argv(int argc, char *argv[])
789 {
790     int i;
791 
792     for (i = 1; i < argc; i++) {
793         if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
794             usage();
795             exit(0);
796         } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
797             printf("fbpanel %s\n", version);
798             exit(0);
799         } else if (!strcmp(argv[i], "--log")) {
800             i++;
801             if (i == argc) {
802                 ERR( "fbpanel: missing log level\n");
803                 usage();
804                 exit(1);
805             } else {
806                 log_level = atoi(argv[i]);
807             }
808         } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
809             config = 1;
810         } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
811             i++;
812             if (i == argc) {
813                 ERR( "fbpanel: missing profile name\n");
814                 usage();
815                 exit(1);
816             } else {
817                 profile = g_strdup(argv[i]);
818             }
819         } else {
820             printf("fbpanel: unknown option - %s\n", argv[i]);
821             usage();
822             exit(1);
823         }
824     }
825 }
826 
panel_get_profile()827 gchar *panel_get_profile()
828 {
829     return profile;
830 }
831 
panel_get_profile_file()832 gchar *panel_get_profile_file()
833 {
834     return profile_file;
835 }
836 
837 static void
ensure_profile()838 ensure_profile()
839 {
840     gchar *cmd;
841 
842     if (g_file_test(profile_file,
843             G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
844     {
845         return;
846     }
847     cmd = g_strdup_printf("%s %s", LIBEXECDIR "/fbpanel/make_profile",
848         profile);
849     g_spawn_command_line_sync(cmd, NULL, NULL, NULL, NULL);
850     g_free(cmd);
851     if (g_file_test(profile_file,
852             G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
853     {
854         return;
855     }
856     ERR("Can't open profile %s - %s\n", profile, profile_file);
857     exit(1);
858 }
859 
860 int
main(int argc,char * argv[])861 main(int argc, char *argv[])
862 {
863     setlocale(LC_CTYPE, "");
864     gtk_set_locale();
865     gtk_init(&argc, &argv);
866     XSetLocaleModifiers("");
867     XSetErrorHandler((XErrorHandler) handle_error);
868     fb_init();
869     do_argv(argc, argv);
870     profile_file = g_build_filename(g_get_user_config_dir(),
871         "fbpanel", profile, NULL);
872     ensure_profile();
873     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), IMGPREFIX);
874     signal(SIGUSR1, sig_usr1);
875     signal(SIGUSR2, sig_usr2);
876 
877     do {
878         the_panel = p = g_new0(panel, 1);
879         p->xc = xconf_new_from_file(profile_file, profile);
880         if (!p->xc)
881             exit(1);
882 
883         panel_start(p->xc);
884         if (config)
885             configure(p->xc);
886         gtk_main();
887         panel_stop(p);
888         //xconf_save_to_profile(cprofile, xc);
889         xconf_del(p->xc, FALSE);
890         g_free(p);
891         DBG("force_quit=%d\n", force_quit);
892     } while (force_quit == 0);
893     g_free(profile_file);
894     fb_free();
895     exit(0);
896 }
897 
898