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