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