1 
2 
3 #include <X11/Xatom.h>
4 #include <X11/cursorfont.h>
5 
6 #include <gtk/gtk.h>
7 #include <gdk/gdk.h>
8 #include <gdk/gdkx.h>
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
13 
14 #include <glib.h>
15 #include <glib/gprintf.h>
16 
17 #include "misc.h"
18 #include "gtkbgbox.h"
19 
20 //#define DEBUGPRN
21 #include "dbg.h"
22 
23 extern panel *the_panel;
24 
25 GtkIconTheme *icon_theme;
26 
27 /* X11 data types */
28 Atom a_UTF8_STRING;
29 Atom a_XROOTPMAP_ID;
30 
31 /* old WM spec */
32 Atom a_WM_STATE;
33 Atom a_WM_CLASS;
34 Atom a_WM_DELETE_WINDOW;
35 Atom a_WM_PROTOCOLS;
36 
37 /* new NET spec */
38 Atom a_NET_WORKAREA;
39 Atom a_NET_CLIENT_LIST;
40 Atom a_NET_CLIENT_LIST_STACKING;
41 Atom a_NET_NUMBER_OF_DESKTOPS;
42 Atom a_NET_CURRENT_DESKTOP;
43 Atom a_NET_DESKTOP_NAMES;
44 Atom a_NET_DESKTOP_GEOMETRY;
45 Atom a_NET_ACTIVE_WINDOW;
46 Atom a_NET_CLOSE_WINDOW;
47 Atom a_NET_SUPPORTED;
48 Atom a_NET_WM_DESKTOP;
49 Atom a_NET_WM_STATE;
50 Atom a_NET_WM_STATE_SKIP_TASKBAR;
51 Atom a_NET_WM_STATE_SKIP_PAGER;
52 Atom a_NET_WM_STATE_STICKY;
53 Atom a_NET_WM_STATE_HIDDEN;
54 Atom a_NET_WM_STATE_SHADED;
55 Atom a_NET_WM_STATE_ABOVE;
56 Atom a_NET_WM_STATE_BELOW;
57 Atom a_NET_WM_WINDOW_TYPE;
58 Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
59 Atom a_NET_WM_WINDOW_TYPE_DOCK;
60 Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
61 Atom a_NET_WM_WINDOW_TYPE_MENU;
62 Atom a_NET_WM_WINDOW_TYPE_UTILITY;
63 Atom a_NET_WM_WINDOW_TYPE_SPLASH;
64 Atom a_NET_WM_WINDOW_TYPE_DIALOG;
65 Atom a_NET_WM_WINDOW_TYPE_NORMAL;
66 Atom a_NET_WM_DESKTOP;
67 Atom a_NET_WM_NAME;
68 Atom a_NET_WM_VISIBLE_NAME;
69 Atom a_NET_WM_STRUT;
70 Atom a_NET_WM_STRUT_PARTIAL;
71 Atom a_NET_WM_ICON;
72 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
73 
74 xconf_enum allign_enum[] = {
75     { .num = ALLIGN_LEFT, .str = "left" },
76     { .num = ALLIGN_RIGHT, .str = "right" },
77     { .num = ALLIGN_CENTER, .str = "center"},
78     { .num = 0, .str = NULL },
79 };
80 xconf_enum edge_enum[] = {
81     { .num = EDGE_LEFT, .str = "left" },
82     { .num = EDGE_RIGHT, .str = "right" },
83     { .num = EDGE_TOP, .str = "top" },
84     { .num = EDGE_BOTTOM, .str = "bottom" },
85     { .num = 0, .str = NULL },
86 };
87 xconf_enum widthtype_enum[] = {
88     { .num = WIDTH_REQUEST, .str = "request" , .desc = "dynamic" },
89     { .num = WIDTH_PIXEL, .str = "pixel" , .desc = "pixels" },
90     { .num = WIDTH_PERCENT, .str = "percent", .desc = "% of screen" },
91     { .num = 0, .str = NULL },
92 };
93 xconf_enum heighttype_enum[] = {
94     { .num = HEIGHT_PIXEL, .str = "pixel" },
95     { .num = 0, .str = NULL },
96 };
97 xconf_enum bool_enum[] = {
98     { .num = 0, .str = "false" },
99     { .num = 1, .str = "true" },
100     { .num = 0, .str = NULL },
101 };
102 xconf_enum pos_enum[] = {
103     { .num = POS_NONE, .str = "none" },
104     { .num = POS_START, .str = "start" },
105     { .num = POS_END,  .str = "end" },
106     { .num = 0, .str = NULL},
107 };
108 xconf_enum layer_enum[] = {
109     { .num = LAYER_ABOVE, .str = "above" },
110     { .num = LAYER_BELOW, .str = "below" },
111     { .num = 0, .str = NULL},
112 };
113 
114 
115 int
str2num(xconf_enum * p,gchar * str,int defval)116 str2num(xconf_enum *p, gchar *str, int defval)
117 {
118     ENTER;
119     for (;p && p->str; p++) {
120         if (!g_ascii_strcasecmp(str, p->str))
121             RET(p->num);
122     }
123     RET(defval);
124 }
125 
126 gchar *
num2str(xconf_enum * p,int num,gchar * defval)127 num2str(xconf_enum *p, int num, gchar *defval)
128 {
129     ENTER;
130     for (;p && p->str; p++) {
131         if (num == p->num)
132             RET(p->str);
133     }
134     RET(defval);
135 }
136 
137 
resolve_atoms()138 void resolve_atoms()
139 {
140     ENTER;
141 
142     a_UTF8_STRING                = XInternAtom(GDK_DISPLAY(), "UTF8_STRING", False);
143     a_XROOTPMAP_ID               = XInternAtom(GDK_DISPLAY(), "_XROOTPMAP_ID", False);
144     a_WM_STATE                   = XInternAtom(GDK_DISPLAY(), "WM_STATE", False);
145     a_WM_CLASS                   = XInternAtom(GDK_DISPLAY(), "WM_CLASS", False);
146     a_WM_DELETE_WINDOW           = XInternAtom(GDK_DISPLAY(), "WM_DELETE_WINDOW", False);
147     a_WM_PROTOCOLS               = XInternAtom(GDK_DISPLAY(), "WM_PROTOCOLS", False);
148     a_NET_WORKAREA               = XInternAtom(GDK_DISPLAY(), "_NET_WORKAREA", False);
149     a_NET_CLIENT_LIST            = XInternAtom(GDK_DISPLAY(), "_NET_CLIENT_LIST", False);
150     a_NET_CLIENT_LIST_STACKING   = XInternAtom(GDK_DISPLAY(), "_NET_CLIENT_LIST_STACKING", False);
151     a_NET_NUMBER_OF_DESKTOPS     = XInternAtom(GDK_DISPLAY(), "_NET_NUMBER_OF_DESKTOPS", False);
152     a_NET_CURRENT_DESKTOP        = XInternAtom(GDK_DISPLAY(), "_NET_CURRENT_DESKTOP", False);
153     a_NET_DESKTOP_NAMES          = XInternAtom(GDK_DISPLAY(), "_NET_DESKTOP_NAMES", False);
154     a_NET_DESKTOP_GEOMETRY       = XInternAtom(GDK_DISPLAY(), "_NET_DESKTOP_GEOMETRY", False);
155     a_NET_ACTIVE_WINDOW          = XInternAtom(GDK_DISPLAY(), "_NET_ACTIVE_WINDOW", False);
156     a_NET_SUPPORTED              = XInternAtom(GDK_DISPLAY(), "_NET_SUPPORTED", False);
157     a_NET_WM_DESKTOP             = XInternAtom(GDK_DISPLAY(), "_NET_WM_DESKTOP", False);
158     a_NET_WM_STATE               = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE", False);
159     a_NET_WM_STATE_SKIP_TASKBAR  = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SKIP_TASKBAR", False);
160     a_NET_WM_STATE_SKIP_PAGER    = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SKIP_PAGER", False);
161     a_NET_WM_STATE_STICKY        = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_STICKY", False);
162     a_NET_WM_STATE_HIDDEN        = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_HIDDEN", False);
163     a_NET_WM_STATE_SHADED        = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SHADED", False);
164     a_NET_WM_STATE_ABOVE         = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_ABOVE", False);
165     a_NET_WM_STATE_BELOW         = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_BELOW", False);
166     a_NET_WM_STATE_SHADED        = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SHADED", False);
167     a_NET_WM_WINDOW_TYPE         = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE", False);
168 
169     a_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DESKTOP", False);
170     a_NET_WM_WINDOW_TYPE_DOCK    = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DOCK", False);
171     a_NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
172     a_NET_WM_WINDOW_TYPE_MENU    = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_MENU", False);
173     a_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_UTILITY", False);
174     a_NET_WM_WINDOW_TYPE_SPLASH  = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_SPLASH", False);
175     a_NET_WM_WINDOW_TYPE_DIALOG  = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DIALOG", False);
176     a_NET_WM_WINDOW_TYPE_NORMAL  = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_NORMAL", False);
177     a_NET_WM_DESKTOP             = XInternAtom(GDK_DISPLAY(), "_NET_WM_DESKTOP", False);
178     a_NET_WM_NAME                = XInternAtom(GDK_DISPLAY(), "_NET_WM_NAME", False);
179     a_NET_WM_VISIBLE_NAME        = XInternAtom(GDK_DISPLAY(), "_NET_WM_VISIBLE_NAME", False);
180     a_NET_WM_STRUT               = XInternAtom(GDK_DISPLAY(), "_NET_WM_STRUT", False);
181     a_NET_WM_STRUT_PARTIAL       = XInternAtom(GDK_DISPLAY(), "_NET_WM_STRUT_PARTIAL", False);
182     a_NET_WM_ICON                = XInternAtom(GDK_DISPLAY(), "_NET_WM_ICON", False);
183     a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR
184                                  = XInternAtom(GDK_DISPLAY(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
185 
186     RET();
187 }
188 
189 
fb_init()190 void fb_init()
191 {
192     resolve_atoms();
193     icon_theme = gtk_icon_theme_get_default();
194 }
195 
fb_free()196 void fb_free()
197 {
198     // MUST NOT be ref'd or unref'd
199     // g_object_unref(icon_theme);
200 }
201 
202 void
Xclimsg(Window win,long type,long l0,long l1,long l2,long l3,long l4)203 Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
204 {
205     XClientMessageEvent xev;
206 
207     xev.type = ClientMessage;
208     xev.window = win;
209     xev.send_event = True;
210     xev.display = gdk_display;
211     xev.message_type = type;
212     xev.format = 32;
213     xev.data.l[0] = l0;
214     xev.data.l[1] = l1;
215     xev.data.l[2] = l2;
216     xev.data.l[3] = l3;
217     xev.data.l[4] = l4;
218     XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
219           (SubstructureNotifyMask | SubstructureRedirectMask),
220           (XEvent *) & xev);
221 }
222 
223 void
Xclimsgwm(Window win,Atom type,Atom arg)224 Xclimsgwm(Window win, Atom type, Atom arg)
225 {
226     XClientMessageEvent xev;
227 
228     xev.type = ClientMessage;
229     xev.window = win;
230     xev.message_type = type;
231     xev.format = 32;
232     xev.data.l[0] = arg;
233     xev.data.l[1] = GDK_CURRENT_TIME;
234     XSendEvent(GDK_DISPLAY(), win, False, 0L, (XEvent *) &xev);
235 }
236 
237 
238 void *
get_utf8_property(Window win,Atom atom)239 get_utf8_property(Window win, Atom atom)
240 {
241 
242     Atom type;
243     int format;
244     gulong nitems;
245     gulong bytes_after;
246     gchar  *retval;
247     int result;
248     guchar *tmp = NULL;
249 
250     type = None;
251     retval = NULL;
252     result = XGetWindowProperty (GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
253           a_UTF8_STRING, &type, &format, &nitems,
254           &bytes_after, &tmp);
255     if (result != Success)
256         return NULL;
257     if (tmp) {
258         if (type == a_UTF8_STRING && format == 8 && nitems != 0)
259             retval = g_strndup ((gchar *)tmp, nitems);
260         XFree (tmp);
261     }
262     return retval;
263 
264 }
265 
266 char **
get_utf8_property_list(Window win,Atom atom,int * count)267 get_utf8_property_list(Window win, Atom atom, int *count)
268 {
269     Atom type;
270     int format, i;
271     gulong nitems;
272     gulong bytes_after;
273     gchar *s, **retval = NULL;
274     int result;
275     guchar *tmp = NULL;
276 
277     *count = 0;
278     result = XGetWindowProperty(GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
279           a_UTF8_STRING, &type, &format, &nitems,
280           &bytes_after, &tmp);
281     if (result != Success || type != a_UTF8_STRING || tmp == NULL)
282         return NULL;
283 
284     if (nitems) {
285         gchar *val = (gchar *) tmp;
286         DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
287         for (i = 0; i < nitems; i++) {
288             if (!val[i])
289                 (*count)++;
290         }
291         retval = g_new0 (char*, *count + 2);
292         for (i = 0, s = val; i < *count; i++, s = s +  strlen (s) + 1) {
293             retval[i] = g_strdup(s);
294         }
295         if (val[nitems-1]) {
296             result = nitems - (s - val);
297             DBG("val does not ends by 0, moving last %d bytes\n", result);
298             g_memmove(s - 1, s, result);
299             val[nitems-1] = 0;
300             DBG("s=%s\n", s -1);
301             retval[i] = g_strdup(s - 1);
302             (*count)++;
303         }
304     }
305     XFree (tmp);
306 
307     return retval;
308 
309 }
310 
311 void *
get_xaproperty(Window win,Atom prop,Atom type,int * nitems)312 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
313 {
314     Atom type_ret;
315     int format_ret;
316     unsigned long items_ret;
317     unsigned long after_ret;
318     unsigned char *prop_data;
319 
320     ENTER;
321     prop_data = NULL;
322     if (XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False,
323               type, &type_ret, &format_ret, &items_ret,
324               &after_ret, &prop_data) != Success)
325         RET(NULL);
326     DBG("win=%x prop=%d type=%d rtype=%d rformat=%d nitems=%d\n", win, prop,
327             type, type_ret, format_ret, items_ret);
328 
329     if (nitems)
330         *nitems = items_ret;
331     RET(prop_data);
332 }
333 
334 static char*
text_property_to_utf8(const XTextProperty * prop)335 text_property_to_utf8 (const XTextProperty *prop)
336 {
337   char **list;
338   int count;
339   char *retval;
340 
341   ENTER;
342   list = NULL;
343   count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
344                                           prop->format,
345                                           prop->value,
346                                           prop->nitems,
347                                           &list);
348 
349   DBG("count=%d\n", count);
350   if (count == 0)
351     return NULL;
352 
353   retval = list[0];
354   list[0] = g_strdup (""); /* something to free */
355 
356   g_strfreev (list);
357 
358   RET(retval);
359 }
360 
361 char *
get_textproperty(Window win,Atom atom)362 get_textproperty(Window win, Atom atom)
363 {
364     XTextProperty text_prop;
365     char *retval;
366 
367     ENTER;
368     if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
369         DBG("format=%d enc=%d nitems=%d value=%s   \n",
370               text_prop.format,
371               text_prop.encoding,
372               text_prop.nitems,
373               text_prop.value);
374         retval = text_property_to_utf8 (&text_prop);
375         if (text_prop.nitems > 0)
376             XFree (text_prop.value);
377         RET(retval);
378 
379     }
380     RET(NULL);
381 }
382 
383 
384 guint
get_net_number_of_desktops()385 get_net_number_of_desktops()
386 {
387     guint desknum;
388     guint32 *data;
389 
390     ENTER;
391     data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
392           XA_CARDINAL, 0);
393     if (!data)
394         RET(0);
395 
396     desknum = *data;
397     XFree (data);
398     RET(desknum);
399 }
400 
401 
402 guint
get_net_current_desktop()403 get_net_current_desktop ()
404 {
405     guint desk;
406     guint32 *data;
407 
408     ENTER;
409     data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
410     if (!data)
411         RET(0);
412 
413     desk = *data;
414     XFree (data);
415     RET(desk);
416 }
417 
418 guint
get_net_wm_desktop(Window win)419 get_net_wm_desktop(Window win)
420 {
421     guint desk = 0;
422     guint *data;
423 
424     ENTER;
425     data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
426     if (data) {
427         desk = *data;
428         XFree (data);
429     } else
430         DBG("can't get desktop num for win 0x%lx", win);
431     RET(desk);
432 }
433 
434 void
get_net_wm_state(Window win,net_wm_state * nws)435 get_net_wm_state(Window win, net_wm_state *nws)
436 {
437     Atom *state;
438     int num3;
439 
440 
441     ENTER;
442     bzero(nws, sizeof(*nws));
443     if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
444         RET();
445 
446     DBG( "%x: netwm state = { ", (unsigned int)win);
447     while (--num3 >= 0) {
448         if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
449             DBGE("NET_WM_STATE_SKIP_PAGER ");
450             nws->skip_pager = 1;
451         } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
452             DBGE("NET_WM_STATE_SKIP_TASKBAR ");
453             nws->skip_taskbar = 1;
454         } else if (state[num3] == a_NET_WM_STATE_STICKY) {
455             DBGE("NET_WM_STATE_STICKY ");
456             nws->sticky = 1;
457         } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
458             DBGE("NET_WM_STATE_HIDDEN ");
459             nws->hidden = 1;
460         } else if (state[num3] == a_NET_WM_STATE_SHADED) {
461             DBGE("NET_WM_STATE_SHADED ");
462             nws->shaded = 1;
463         } else {
464             DBGE("... ");
465         }
466     }
467     XFree(state);
468     DBGE( "}\n");
469     RET();
470 }
471 
472 
473 
474 
475 void
get_net_wm_window_type(Window win,net_wm_window_type * nwwt)476 get_net_wm_window_type(Window win, net_wm_window_type *nwwt)
477 {
478     Atom *state;
479     int num3;
480 
481 
482     ENTER;
483     bzero(nwwt, sizeof(*nwwt));
484     if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
485         RET();
486 
487     DBG( "%x: netwm state = { ", (unsigned int)win);
488     while (--num3 >= 0) {
489         if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
490             DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
491             nwwt->desktop = 1;
492         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
493             DBG( "NET_WM_WINDOW_TYPE_DOCK ");
494             nwwt->dock = 1;
495         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
496             DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
497             nwwt->toolbar = 1;
498         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
499             DBG( "NET_WM_WINDOW_TYPE_MENU ");
500             nwwt->menu = 1;
501         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
502             DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
503             nwwt->utility = 1;
504         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
505             DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
506             nwwt->splash = 1;
507         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
508             DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
509             nwwt->dialog = 1;
510         } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
511             DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
512             nwwt->normal = 1;
513         } else {
514             DBG( "... ");
515         }
516     }
517     XFree(state);
518     DBG( "}\n");
519     RET();
520 }
521 
522 
523 
524 
525 static void
calculate_width(int scrw,int wtype,int allign,int margin,int * panw,int * x)526 calculate_width(int scrw, int wtype, int allign, int margin,
527       int *panw, int *x)
528 {
529     ENTER;
530     DBG("scrw=%d\n", scrw);
531     DBG("IN panw=%d\n", *panw);
532     //scrw -= 2;
533     if (wtype == WIDTH_PERCENT) {
534         /* sanity check */
535         if (*panw > 100)
536             *panw = 100;
537         else if (*panw < 0)
538             *panw = 1;
539         *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
540     }
541     if (*panw > scrw)
542         *panw = scrw;
543 
544     if (allign != ALLIGN_CENTER) {
545         if (margin > scrw) {
546             ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
547                   margin, scrw);
548             margin = 0;
549         }
550         if (wtype == WIDTH_PERCENT)
551             //*panw = MAX(scrw - margin, *panw);
552             ;
553         else
554             *panw = MIN(scrw - margin, *panw);
555     }
556     DBG("OUT panw=%d\n", *panw);
557     if (allign == ALLIGN_LEFT)
558         *x += margin;
559     else if (allign == ALLIGN_RIGHT) {
560         *x += scrw - *panw - margin;
561         if (*x < 0)
562             *x = 0;
563     } else if (allign == ALLIGN_CENTER)
564         *x += (scrw - *panw) / 2;
565     RET();
566 }
567 
568 
569 void
calculate_position(panel * np)570 calculate_position(panel *np)
571 {
572     int sswidth, ssheight, minx, miny;
573 
574     ENTER;
575     if (0)  {
576         //if (np->curdesk < np->wa_len/4) {
577         minx = np->workarea[np->curdesk*4 + 0];
578         miny = np->workarea[np->curdesk*4 + 1];
579         sswidth  = np->workarea[np->curdesk*4 + 2];
580         ssheight = np->workarea[np->curdesk*4 + 3];
581     } else {
582         minx = miny = 0;
583         sswidth  = gdk_screen_width();
584         ssheight = gdk_screen_height();
585 
586     }
587 
588     if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
589         np->aw = np->width;
590         np->ax = minx;
591         calculate_width(sswidth, np->widthtype, np->allign, np->margin,
592               &np->aw, &np->ax);
593         np->ah = np->height;
594         np->ah = MIN(PANEL_HEIGHT_MAX, np->ah);
595         np->ah = MAX(PANEL_HEIGHT_MIN, np->ah);
596         np->ay = miny + ((np->edge == EDGE_TOP) ? 0 : (ssheight - np->ah));
597 
598     } else {
599         np->ah = np->width;
600         np->ay = miny;
601         calculate_width(ssheight, np->widthtype, np->allign, np->margin,
602               &np->ah, &np->ay);
603         np->aw = np->height;
604         np->aw = MIN(PANEL_HEIGHT_MAX, np->aw);
605         np->aw = MAX(PANEL_HEIGHT_MIN, np->aw);
606         np->ax = minx + ((np->edge == EDGE_LEFT) ? 0 : (sswidth - np->aw));
607     }
608     if (!np->aw)
609         np->aw = 1;
610     if (!np->ah)
611         np->ah = 1;
612 
613     /*
614     if (!np->visible) {
615         DBG("pushing of screen dx=%d dy=%d\n", np->ah_dx, np->ah_dy);
616         np->ax += np->ah_dx;
617         np->ay += np->ah_dy;
618     }
619     */
620     DBG("x=%d y=%d w=%d h=%d\n", np->ax, np->ay, np->aw, np->ah);
621     RET();
622 }
623 
624 
625 
626 gchar *
expand_tilda(gchar * file)627 expand_tilda(gchar *file)
628 {
629     ENTER;
630     if (!file)
631         RET(NULL);
632     RET((file[0] == '~') ?
633         g_strdup_printf("%s%s", getenv("HOME"), file+1)
634         : g_strdup(file));
635 
636 }
637 
638 
639 void
get_button_spacing(GtkRequisition * req,GtkContainer * parent,gchar * name)640 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
641 {
642     GtkWidget *b;
643     //gint focus_width;
644     //gint focus_pad;
645 
646     ENTER;
647     b = gtk_button_new();
648     gtk_widget_set_name(GTK_WIDGET(b), name);
649     GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
650     GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
651     gtk_container_set_border_width (GTK_CONTAINER (b), 0);
652 
653     if (parent)
654         gtk_container_add(parent, b);
655 
656     gtk_widget_show(b);
657     gtk_widget_size_request(b, req);
658 
659     gtk_widget_destroy(b);
660     RET();
661 }
662 
663 
664 guint32
gcolor2rgb24(GdkColor * color)665 gcolor2rgb24(GdkColor *color)
666 {
667     guint32 i;
668     guint16 r, g, b;
669 
670     ENTER;
671 
672     r = color->red * 0xFF / 0xFFFF;
673     g = color->green * 0xFF / 0xFFFF;
674     b = color->blue * 0xFF / 0xFFFF;
675     DBG("%x %x %x ==> %x %x %x\n", color->red, color->green, color->blue, r, g, b);
676 
677     i = (color->red * 0xFF / 0xFFFF) & 0xFF;
678     i <<= 8;
679     i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
680     i <<= 8;
681     i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
682     DBG("i=%x\n", i);
683     RET(i);
684 }
685 
686 gchar *
gdk_color_to_RRGGBB(GdkColor * color)687 gdk_color_to_RRGGBB(GdkColor *color)
688 {
689     static gchar str[10]; // #RRGGBB + \0
690     g_snprintf(str, sizeof(str), "#%02x%02x%02x",
691         color->red >> 8, color->green >> 8, color->blue >> 8);
692     return str;
693 }
694 
695 void
menu_pos(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,GtkWidget * widget)696 menu_pos(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, GtkWidget *widget)
697 {
698     int ox, oy, w, h;
699 
700     ENTER;
701     if (widget) {
702         gdk_window_get_origin(widget->window, &ox, &oy);
703         ox += widget->allocation.x;
704         oy += widget->allocation.y;
705     } else {
706         gdk_display_get_pointer(gdk_display_get_default(), NULL, &ox, &oy, NULL);
707         ox -= 20;
708         if (ox < 0)
709             ox = 0;
710         oy -= 10;
711         if (oy < 0)
712             oy = 0;
713     }
714     w = GTK_WIDGET(menu)->requisition.width;
715     h = GTK_WIDGET(menu)->requisition.height;
716     if (the_panel->orientation == GTK_ORIENTATION_HORIZONTAL) {
717         // x
718         *x = ox;
719         if (*x + w > gdk_screen_width())
720             *x = gdk_screen_width() - w;
721         // y
722         if (the_panel->edge == EDGE_TOP)
723             *y = the_panel->ah;
724         else
725             *y = gdk_screen_height() - the_panel->ah - h;
726     } else {
727         // x
728         if (the_panel->edge == EDGE_LEFT)
729             *x = the_panel->aw;
730         else
731             *x = gdk_screen_width() - the_panel->aw - w;
732         // y
733         *y = oy;
734         if (*y + h > gdk_screen_height())
735             *y = gdk_screen_height() - h;
736     }
737     DBG("w-h %d %d\n", w, h);
738     *push_in = TRUE;
739     RET();
740 }
741 
742 gchar *
indent(int level)743 indent(int level)
744 {
745     static gchar *space[] = {
746         "",
747         "    ",
748         "        ",
749         "            ",
750         "                ",
751     };
752 
753     if (level > sizeof(space))
754         level = sizeof(space);
755     RET(space[level]);
756 }
757 
758 
759 
760 
761 /**********************************************************************
762  * FB Pixbuf                                                          *
763  **********************************************************************/
764 
765 #define MAX_SIZE 192
766 
767 /* Creates a pixbuf. Several sources are tried in these order:
768  *   icon named @iname
769  *   file from @fname
770  *   icon named "missing-image" as a fallabck, if @use_fallback is TRUE.
771  * Returns pixbuf or NULL on failure
772  *
773  * Result pixbuf is always smaller then MAX_SIZE
774  */
775 GdkPixbuf *
fb_pixbuf_new(gchar * iname,gchar * fname,int width,int height,gboolean use_fallback)776 fb_pixbuf_new(gchar *iname, gchar *fname, int width, int height,
777         gboolean use_fallback)
778 {
779     GdkPixbuf *pb = NULL;
780     int size;
781 
782     ENTER;
783     size = MIN(192, MAX(width, height));
784     if (iname && !pb)
785         pb = gtk_icon_theme_load_icon(icon_theme, iname, size,
786             GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
787     if (fname && !pb)
788         pb = gdk_pixbuf_new_from_file_at_size(fname, width, height, NULL);
789     if (use_fallback && !pb)
790         pb = gtk_icon_theme_load_icon(icon_theme, "gtk-missing-image", size,
791             GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
792     RET(pb);
793 }
794 
795 /* Creates hilighted version of front image to reflect mouse enter
796  */
797 static GdkPixbuf *
fb_pixbuf_make_back_image(GdkPixbuf * front,gulong hicolor)798 fb_pixbuf_make_back_image(GdkPixbuf *front, gulong hicolor)
799 {
800     GdkPixbuf *back;
801     guchar *src, *up, extra[3];
802     int i;
803 
804     ENTER;
805     back = gdk_pixbuf_add_alpha(front, FALSE, 0, 0, 0);
806     if (!back) {
807         g_object_ref(G_OBJECT(front));
808         RET(front);
809     }
810     src = gdk_pixbuf_get_pixels (back);
811     for (i = 2; i >= 0; i--, hicolor >>= 8)
812         extra[i] = hicolor & 0xFF;
813     for (up = src + gdk_pixbuf_get_height(back) * gdk_pixbuf_get_rowstride (back);
814             src < up; src+=4) {
815         if (src[3] == 0)
816             continue;
817         for (i = 0; i < 3; i++) {
818             if (src[i] + extra[i] >= 255)
819                 src[i] = 255;
820             else
821                 src[i] += extra[i];
822         }
823     }
824     RET(back);
825 }
826 
827 #define PRESS_GAP 2
828 static GdkPixbuf *
fb_pixbuf_make_press_image(GdkPixbuf * front)829 fb_pixbuf_make_press_image(GdkPixbuf *front)
830 {
831     GdkPixbuf *press, *tmp;
832     int w, h;
833 
834     ENTER;
835     w = gdk_pixbuf_get_width(front) - 2 * PRESS_GAP;
836     h = gdk_pixbuf_get_height(front) - 2 * PRESS_GAP;
837     press = gdk_pixbuf_copy(front);
838     tmp = gdk_pixbuf_scale_simple(front, w, h, GDK_INTERP_HYPER);
839     if (press && tmp) {
840         gdk_pixbuf_fill(press, 0);
841         gdk_pixbuf_copy_area(tmp,
842                 0, 0,  // src_x, src_y
843                 w, h,  // width, height
844                 press, // dest_pixbuf
845                 PRESS_GAP, PRESS_GAP);  // dst_x, dst_y
846         g_object_unref(G_OBJECT(tmp));
847         RET(press);
848     }
849     if (press)
850         g_object_unref(G_OBJECT(press));
851     if (tmp)
852         g_object_unref(G_OBJECT(tmp));
853 
854     g_object_ref(G_OBJECT(front));
855     RET(front);
856 }
857 
858 /**********************************************************************
859  * FB Image                                                           *
860  **********************************************************************/
861 
862 #define PIXBBUF_NUM 3
863 typedef struct {
864     gchar *iname, *fname;
865     int width, height;
866     gulong itc_id; /* icon theme change callback id */
867     gulong hicolor;
868     int i; /* pixbuf index */
869     GdkPixbuf *pix[PIXBBUF_NUM];
870 } fb_image_conf_t;
871 
872 static void fb_image_free(GObject *image);
873 static void fb_image_icon_theme_changed(GtkIconTheme *icon_theme,
874         GtkWidget *image);
875 
876 /* Creates an image widget from fb_pixbuf and updates it on every icon theme
877  * change. To keep its internal state, image allocates some data and frees it
878  * in "destroy" callback
879  */
880 GtkWidget *
fb_image_new(gchar * iname,gchar * fname,int width,int height)881 fb_image_new(gchar *iname, gchar *fname, int width, int height)
882 {
883     GtkWidget *image;
884     fb_image_conf_t *conf;
885 
886     image = gtk_image_new();
887     conf = g_new0(fb_image_conf_t, 1); /* exits if fails */
888     g_object_set_data(G_OBJECT(image), "conf", conf);
889     conf->itc_id = g_signal_connect_after (G_OBJECT(icon_theme),
890             "changed", (GCallback) fb_image_icon_theme_changed, image);
891     g_signal_connect (G_OBJECT(image),
892             "destroy", (GCallback) fb_image_free, NULL);
893     conf->iname = g_strdup(iname);
894     conf->fname = g_strdup(fname);
895     conf->width = width;
896     conf->height = height;
897     conf->pix[0] = fb_pixbuf_new(iname, fname, width, height, TRUE);
898     gtk_image_set_from_pixbuf(GTK_IMAGE(image), conf->pix[0]);
899     gtk_widget_show(image);
900     RET(image);
901 }
902 
903 
904 /* Frees image's resources
905  */
906 static void
fb_image_free(GObject * image)907 fb_image_free(GObject *image)
908 {
909     fb_image_conf_t *conf;
910     int i;
911 
912     ENTER;
913     conf = g_object_get_data(image, "conf");
914     g_signal_handler_disconnect(G_OBJECT(icon_theme), conf->itc_id);
915     g_free(conf->iname);
916     g_free(conf->fname);
917     for (i = 0; i < PIXBBUF_NUM; i++)
918         if (conf->pix[i])
919             g_object_unref(G_OBJECT(conf->pix[i]));
920     g_free(conf);
921     RET();
922 }
923 
924 /* Reloads image's pixbuf upon icon theme change
925  */
926 static void
fb_image_icon_theme_changed(GtkIconTheme * icon_theme,GtkWidget * image)927 fb_image_icon_theme_changed(GtkIconTheme *icon_theme, GtkWidget *image)
928 {
929     fb_image_conf_t *conf;
930     int i;
931 
932     ENTER;
933     conf = g_object_get_data(G_OBJECT(image), "conf");
934     DBG("%s / %s\n", conf->iname, conf->fname);
935     for (i = 0; i < PIXBBUF_NUM; i++)
936         if (conf->pix[i]) {
937             g_object_unref(G_OBJECT(conf->pix[i]));
938 	    conf->pix[i] = NULL;
939 	}
940     conf->pix[0] = fb_pixbuf_new(conf->iname, conf->fname,
941             conf->width, conf->height, TRUE);
942     conf->pix[1] = fb_pixbuf_make_back_image(conf->pix[0], conf->hicolor);
943     conf->pix[2] = fb_pixbuf_make_press_image(conf->pix[1]);
944     gtk_image_set_from_pixbuf(GTK_IMAGE(image), conf->pix[0]);
945     RET();
946 }
947 
948 
949 /**********************************************************************
950  * FB Button                                                          *
951  **********************************************************************/
952 
953 static gboolean fb_button_cross(GtkImage *widget, GdkEventCrossing *event);
954 static gboolean fb_button_pressed(GtkWidget *widget, GdkEventButton *event);
955 
956 /* Creates fb_button - bgbox with fb_image. bgbox provides pseudo transparent
957  * background and event capture. fb_image follows icon theme change.
958  * Additionaly, fb_button highlightes an image on mouse enter and runs simple
959  * animation when clicked.
960  * FIXME: @label parameter is currently ignored
961  */
962 GtkWidget *
fb_button_new(gchar * iname,gchar * fname,int width,int height,gulong hicolor,gchar * label)963 fb_button_new(gchar *iname, gchar *fname, int width, int height,
964       gulong hicolor, gchar *label)
965 {
966     GtkWidget *b, *image;
967     fb_image_conf_t *conf;
968 
969     ENTER;
970     b = gtk_bgbox_new();
971     gtk_container_set_border_width(GTK_CONTAINER(b), 0);
972     GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
973     image = fb_image_new(iname, fname, width, height);
974     gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
975     gtk_misc_set_padding (GTK_MISC(image), 0, 0);
976     conf = g_object_get_data(G_OBJECT(image), "conf");
977     conf->hicolor = hicolor;
978     conf->pix[1] = fb_pixbuf_make_back_image(conf->pix[0], conf->hicolor);
979     conf->pix[2] = fb_pixbuf_make_press_image(conf->pix[1]);
980     gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
981     g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
982             G_CALLBACK (fb_button_cross), image);
983     g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
984             G_CALLBACK (fb_button_cross), image);
985     g_signal_connect_swapped (G_OBJECT (b), "button-release-event",
986           G_CALLBACK (fb_button_pressed), image);
987     g_signal_connect_swapped (G_OBJECT (b), "button-press-event",
988           G_CALLBACK (fb_button_pressed), image);
989     gtk_container_add(GTK_CONTAINER(b), image);
990     gtk_widget_show_all(b);
991     RET(b);
992 }
993 
994 
995 /* Flips front and back images upon mouse cross event - GDK_ENTER_NOTIFY
996  * or GDK_LEAVE_NOTIFY
997  */
998 static gboolean
fb_button_cross(GtkImage * widget,GdkEventCrossing * event)999 fb_button_cross(GtkImage *widget, GdkEventCrossing *event)
1000 {
1001     fb_image_conf_t *conf;
1002     int i;
1003 
1004     ENTER;
1005     conf = g_object_get_data(G_OBJECT(widget), "conf");
1006     if (event->type == GDK_LEAVE_NOTIFY) {
1007         i = 0;
1008     } else {
1009         i = 1;
1010     }
1011     if (conf->i != i) {
1012         conf->i = i;
1013         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), conf->pix[i]);
1014     }
1015     DBG("%s/%s - %s - pix[%d]=%p\n", conf->iname, conf->fname,
1016 	(event->type == GDK_LEAVE_NOTIFY) ? "out" : "in",
1017 	conf->i, conf->pix[conf->i]);
1018     RET(TRUE);
1019 }
1020 
1021 static gboolean
fb_button_pressed(GtkWidget * widget,GdkEventButton * event)1022 fb_button_pressed(GtkWidget *widget, GdkEventButton *event)
1023 {
1024     fb_image_conf_t *conf;
1025     int i;
1026 
1027     ENTER;
1028     conf = g_object_get_data(G_OBJECT(widget), "conf");
1029     if (event->type == GDK_BUTTON_PRESS) {
1030         i = 2;
1031     } else {
1032         if ((event->x >=0 && event->x < widget->allocation.width)
1033                 && (event->y >=0 && event->y < widget->allocation.height))
1034             i = 1;
1035         else
1036             i = 0;
1037     }
1038     if (conf->i != i) {
1039         conf->i = i;
1040         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), conf->pix[i]);
1041     }
1042     RET(FALSE);
1043 }
1044 
1045 
1046