]> icculus.org git repositories - mikachu/rspanel.git/blob - rspanel.c
use some defines to set appearence stuff, not 100% sure about this yet
[mikachu/rspanel.git] / rspanel.c
1
2 /**********************************************************
3  ** Rather Small Panel 0.8beta1 Copyright (c) 2006       **
4  ** By Mikael Magnusson                                  **
5  ** See file COPYING for license details.                **
6  **********************************************************/
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 #include <sys/time.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17 #include <X11/Xatom.h>
18 #include <iconv.h>
19
20 /* you can edit these */
21 #define MAX_TASK_WIDTH 500
22 #define ICONWIDTH 16
23 #define ICONHEIGHT 16
24 /* XXX get this from the font */
25 #define WINHEIGHT 24
26 #ifndef MIKACHU
27 #        define WINWIDTH scr_width
28 #else
29 int WINWIDTH = 827;
30 #endif
31
32 /* don't edit these */
33 #define TEXTPAD 6
34 #define GRILL_WIDTH 10
35
36 #include <openbox/render.h>
37 #include <openbox/theme.h>
38
39 #include "rspanel.h"
40
41 Window rspanelwin;
42 Display *dd;
43 Window root_win;
44 GC fore_gc;
45 taskbar tb;
46 int scr_screen;
47 int scr_depth;
48 int scr_width;
49 int scr_height;
50 int text_y;
51 int pager_size;
52
53 RrInstance *inst;
54 RrAppearance *background;
55 RrAppearance *focused_task;
56 RrAppearance *iconified_task;
57 RrAppearance *unfocused_task;
58 RrAppearance *normal_text;
59 RrAppearance *iconified_text;
60 RrAppearance *shaded_text;
61 RrAppearance *focused_text;
62 RrAppearance *a_icon;
63
64 /* we draw stuff to this guy then set him as a window
65  * background pixmap, yay no flickering! */
66 Pixmap bgpixmap;
67
68 char *atom_names[] = {
69     /* clients */
70     "KWM_WIN_ICON",
71     "WM_STATE",
72     "_MOTIF_WM_HINTS",
73     "_NET_WM_STATE",
74     "_NET_WM_STATE_SKIP_TASKBAR",
75     "_NET_WM_STATE_SHADED",
76     "_NET_WM_STATE_BELOW",
77     "_NET_WM_STATE_HIDDEN",
78     "_NET_WM_DESKTOP",
79     "_NET_WM_WINDOW_TYPE",
80     "_NET_WM_WINDOW_TYPE_DOCK",
81     "_NET_WM_STRUT",
82     "_WIN_HINTS",
83     /* root */
84     "_NET_CLIENT_LIST",
85     "_NET_CLIENT_LIST_STACKING",
86     "_NET_NUMBER_OF_DESKTOPS",
87     "_NET_CURRENT_DESKTOP",
88     "_OB_WM_ACTION",
89     "_NET_WM_NAME",
90     "UTF8_STRING",
91     "_NET_ACTIVE_WINDOW",
92     "_NET_RESTACK_WINDOW",
93     "_OB_FOCUS",
94     "_NET_WM_ICON_GEOMETRY",
95     "_NET_WM_ICON",
96 };
97
98 typedef enum {
99 KWM_WIN_ICON,
100 WM_STATE,
101 _MOTIF_WM_HINTS,
102 _NET_WM_STATE,
103 _NET_WM_STATE_SKIP_TASKBAR,
104 _NET_WM_STATE_SHADED,
105 _NET_WM_STATE_BELOW,
106 _NET_WM_STATE_HIDDEN,
107 _NET_WM_DESKTOP,
108 _NET_WM_WINDOW_TYPE,
109 _NET_WM_WINDOW_TYPE_DOCK,
110 _NET_WM_STRUT,
111 _WIN_HINTS,
112 _NET_CLIENT_LIST,
113 _NET_CLIENT_LIST_STACKING,
114 _NET_NUMBER_OF_DESKTOPS,
115 _NET_CURRENT_DESKTOP,
116 _OB_WM_ACTION,
117 _NET_WM_NAME,
118 STRING_UTF8,
119 _NET_ACTIVE_WINDOW,
120 _NET_RESTACK_WINDOW,
121 _OB_FOCUS,
122 _NET_WM_ICON_GEOMETRY,
123 _NET_WM_ICON,
124 ATOM_COUNT,
125 } atom_t;
126
127 Atom atoms[ATOM_COUNT];
128
129 enum {
130     REMOVE = 0,
131     ADD,
132     TOGGLE
133 };
134
135 static void *get_prop_data(Window win, Atom prop, Atom type, int *items)
136 {
137     Atom type_ret;
138     int format_ret;
139     unsigned long items_ret;
140     unsigned long after_ret;
141     unsigned char *prop_data;
142
143     prop_data = 0;
144
145     XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False, type, &type_ret,
146                        &format_ret, &items_ret, &after_ret, &prop_data);
147     if (items)
148         *items = items_ret;
149
150     return prop_data;
151 }
152
153 static int generic_get_int(Window win, Atom at)
154 {
155     int num = 0;
156     unsigned long *data;
157
158     data = get_prop_data(win, at, XA_CARDINAL, 0);
159     if (data) {
160         num = *data;
161         XFree(data);
162     }
163     return num;
164 }
165
166 static int find_desktop(Window win)
167 {
168     return generic_get_int(win, atoms[_NET_WM_DESKTOP]);
169 }
170
171 static int find_state(Window win, atom_t atom)
172 {
173     unsigned long *data;
174     int ret = 0;
175     int num;
176
177     data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
178     if (data) {
179         while (num--) {
180             if ((data[num]) == atoms[atom])
181                 ret = 1;
182         }
183         XFree(data);
184     }
185     return ret;
186 }
187
188 /* this naming is very confusing :) */
189 static int is_hidden(Window win)
190 {
191     return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
192 }
193
194 static int is_iconified(Window win)
195 {
196     return find_state(win, _NET_WM_STATE_HIDDEN);
197 }
198
199 static int is_shaded(Window win)
200 {
201     return find_state(win, _NET_WM_STATE_SHADED);
202 }
203
204 static int get_current_desktop(void)
205 {
206     return generic_get_int(root_win, atoms[_NET_CURRENT_DESKTOP]);
207 }
208
209 #ifdef PAGER
210 static int get_number_of_desktops(void)
211 {
212     return generic_get_int(root_win, atoms[_NET_NUMBER_OF_DESKTOPS]);
213 }
214 #endif
215
216 static void free_icons(task *tk)
217 {
218     unsigned int i;
219
220     for (i = 0; i < tk->nicons; ++i)
221         free(tk->icons[i].data);
222     if (tk->nicons > 0)
223         free(tk->icons);
224     tk->nicons = 0;
225 }
226
227 static void task_update_icon(task *tk)
228 {
229     /* these bits are borrowed from openbox' client.c::client_update_icons */
230     unsigned int num;
231     RrPixel32 *data;
232     unsigned int w, h, i, j;
233
234     free_icons(tk);
235
236     data = get_prop_data(tk->win, atoms[_NET_WM_ICON], XA_CARDINAL, &num);
237     if (num) {
238         /* figure out how many valid icons are in here */
239         i = 0;
240         while (num - i > 2) {
241             w = data[i++];
242             h = data[i++];
243             i += w * h;
244             if (i > num || w*h == 0) break;
245             ++tk->nicons;
246         }
247
248         tk->icons = calloc(tk->nicons, sizeof(icon));
249     
250         /* store the icons */
251         i = 0;
252         for (j = 0; j < tk->nicons; ++j) {
253             guint x, y, t;
254
255             w = tk->icons[j].width = data[i++];
256             h = tk->icons[j].height = data[i++];
257
258             if (w*h == 0) continue;
259
260             tk->icons[j].data = g_new(RrPixel32, w * h);
261             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
262                 if (x >= w) {
263                     x = 0;
264                     ++y;
265                 }
266                 tk->icons[j].data[t] =
267                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
268                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
269                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
270                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
271             }
272             g_assert(i <= num);
273         }
274
275         g_free(data);
276     } else {
277         XWMHints *hints;
278
279         if ((hints = XGetWMHints(dd, tk->win))) {
280             if (hints->flags & IconPixmapHint) {
281                 tk->nicons++;
282                 tk->icons = g_new(icon, tk->nicons);
283 //                xerror_set_ignore(TRUE);
284                 if (!RrPixmapToRGBA(inst,
285                                     hints->icon_pixmap,
286                                     (hints->flags & IconMaskHint ?
287                                      hints->icon_mask : None),
288                                     &tk->icons[tk->nicons-1].width,
289                                     &tk->icons[tk->nicons-1].height,
290                                     &tk->icons[tk->nicons-1].data)){
291                     g_free(&tk->icons[tk->nicons-1]);
292                     tk->nicons--;
293                 }
294 //                xerror_set_ignore(FALSE);
295             }
296             XFree(hints);
297         }
298     }    
299 }
300
301 static void add_task(Window win, int focus)
302 {
303     task *tk, *list;
304     int desk;
305
306     if (win == tb.win)
307         return;
308
309     /* is this window on a different desktop? */
310     /* XXX add even if is_hidden, but don't show it later, so we can
311      * unhide it when the prop is removed */
312     desk = find_desktop(win);
313     if ((desk != 0xffffffff && tb.my_desktop != desk))
314         return;
315
316     XSelectInput(dd, win, PropertyChangeMask | StructureNotifyMask);
317
318     tk = calloc(1, sizeof(task));
319     tk->win = win;
320     tk->focused = focus;
321     tk->name = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
322                get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
323     /* XXX use this? */
324     //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
325     tk->iconified = is_iconified(win);
326     tk->shaded = is_shaded(win);
327     tk->hidden = is_hidden(win);
328     task_update_icon(tk);
329
330     /* now append it to our linked list */
331
332     list = tb.task_list;
333     if (!list) {
334         tb.task_list = tk;
335         return;
336     }
337     while (1) {
338         if (!list->next) {
339             list->next = tk;
340             return;
341         }
342         list = list->next;
343     }
344 }
345
346 static void set_prop(Window win, Atom at, Atom type, long val)
347 {
348     XChangeProperty(dd, win, at, type, 32,
349                     PropModeReplace, (unsigned char *)&val, 1);
350 }
351
352 static Window gui_create_taskbar(void)
353 {
354     Window win;
355     XSizeHints size_hints;
356     XSetWindowAttributes att;
357     XClassHint xclhints;
358
359     att.event_mask = ButtonPressMask;
360
361     tb.x = 0;
362     tb.y = scr_height - WINHEIGHT;
363
364     win = rspanelwin = XCreateWindow(/* display  */ dd,
365                                      /* parent   */ root_win,
366                                      /* x        */ tb.x,
367                                      /* y        */ tb.y,
368 /* XXX Maybe just use scr_width here? */
369                                      /* width    */ WINWIDTH,
370                                      /* height   */ WINHEIGHT,
371                                      /* border   */ 0,
372                                      /* depth    */ CopyFromParent,
373                                      /* class    */ InputOutput,
374                                      /* visual   */ CopyFromParent,
375                                      /*value mask*/ CWEventMask,
376                                      /* attribs  */ &att);
377
378     /* reside on ALL desktops */
379     set_prop(win, atoms[_NET_WM_DESKTOP], XA_CARDINAL, 0xFFFFFFFF);
380     set_prop(win, atoms[_NET_WM_WINDOW_TYPE], XA_ATOM,
381              atoms[_NET_WM_WINDOW_TYPE_DOCK]);
382     set_prop(win, atoms[_NET_WM_STATE], XA_ATOM, atoms[_NET_WM_STATE_BELOW]);
383     XChangeProperty(dd, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
384                     (unsigned char *)"rspanel", 7);
385
386     /* make sure the WM obays our window position */
387     size_hints.flags = PPosition;
388     /*XSetWMNormalHints (dd, win, &size_hints); */
389     XChangeProperty(dd, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32,
390                     PropModeReplace, (unsigned char *)&size_hints,
391                     sizeof(XSizeHints) / 4);
392
393     xclhints.res_name = "rspanel";
394     xclhints.res_class = "RSPanel";
395     XSetClassHint(dd, win, &xclhints);
396
397     XMapWindow(dd, win);
398
399     return win;
400 }
401
402 #define SURF(x) with->surface.x
403 #define TEXT(x) with->texture[0].data.text.x
404 #define USE(x) (with = x)
405 #define SETTEXT(x, y, z) with->texture[0].type = RR_TEXTURE_TEXT; \
406                          with->texture[0].data.text.font = x; \
407                          with->texture[0].data.text.justify = y; \
408                          with->texture[0].data.text.ellipsize = z;
409 #define SETSHADOW(y, z, u, v) with->texture[0].data.text.shadow_offset_x = y; \
410                               with->texture[0].data.text.shadow_offset_y = z; \
411                               with->texture[0].data.text.shadow_alpha = u; \
412                               with->texture[0].data.text.shadow_color = v;
413 static void gui_init(void)
414 {
415     XGCValues gcv;
416     RrAppearance *with;
417
418     gcv.graphics_exposures = False;
419
420     fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
421
422     inst = RrInstanceNew(dd, scr_screen);
423
424     /* this appearance will be used to draw icons, we don't
425      * set the type of the texture to RGBA here, because we toggle
426      * that so obrender doesn't try to draw an icon when the app
427      * doesn't have one */
428     /* XXX don't do that */
429     a_icon = RrAppearanceNew(inst, 1);
430     USE(a_icon);
431     SURF(grad) = RR_SURFACE_PARENTREL;
432
433     /* this is the appearance for the background of the panel */
434     background = RrAppearanceNew(inst, 0);
435     USE(background);
436     SURF(grad) = RR_SURFACE_DIAGONAL;
437     SURF(primary) = RrColorNew(inst, 170, 170, 190);
438     SURF(secondary) = RrColorNew(inst, 100, 100, 160);
439
440     /* this is the appearance for unfocused tasks,
441      * text needs to be separate appearances so we can align it correctly */
442     unfocused_task = RrAppearanceNew(inst, 0);
443     USE(unfocused_task);
444     SURF(parent) = background;
445     SURF(grad) = RR_SURFACE_PARENTREL;
446     SURF(border) = TRUE;
447     SURF(border_color) = RrColorNew(inst, 0, 0, 80);
448
449     /* ... for iconified tasks, also used for shaded ones currently */
450     iconified_task = RrAppearanceCopy(unfocused_task);
451     USE(iconified_task);
452     SURF(relief) = RR_RELIEF_SUNKEN;
453     SURF(parent) = background;
454
455     /* ... for focused tasks */
456     focused_task = RrAppearanceNew(inst, 0);
457     USE(focused_task);
458     SURF(grad) = RR_SURFACE_CROSS_DIAGONAL;
459     SURF(secondary) = RrColorNew(inst, 70, 80, 110);
460     SURF(primary) = RrColorNew(inst, 130, 160, 250);
461     SURF(relief) = RR_RELIEF_RAISED;
462
463     /* this is the text used for all normal unfocused tasks */
464     normal_text = RrAppearanceNew(inst, 1);
465     USE(normal_text);
466     SURF(grad) = RR_SURFACE_PARENTREL;
467     SETTEXT(RrFontOpen(inst, "Candara, sans", 10, RR_FONTWEIGHT_NORMAL, RR_FONTSLANT_NORMAL),
468             RR_JUSTIFY_LEFT, RR_ELLIPSIZE_END);
469
470     /* ... for iconified tasks */
471     iconified_text = RrAppearanceCopy(normal_text);
472     /* ... and for focused tasks, we copy this here (ie not 5 lines down)
473      * so the color isn't copied i actually don't know if that would
474      * hurt so XXX on that */
475     focused_text = RrAppearanceCopy(normal_text);
476     USE(focused_text);
477     TEXT(color) = RrColorNew(inst, 230, 230, 255);
478
479     USE(normal_text);
480     TEXT(color) = RrColorNew(inst, 20, 20, 40);
481
482     USE(iconified_text);
483     SETSHADOW(2, 2, 100, RrColorNew(inst, 0, 0, 0));
484     TEXT(color) = RrColorNew(inst, 200, 200, 200);
485
486     shaded_text = RrAppearanceCopy(normal_text);
487     USE(shaded_text);
488     TEXT(color) = RrColorNew(inst, 50, 60, 90);
489 }
490
491 const icon* best_task_icon(task *tk, gint w, gint h)
492 {
493     static icon deficon;
494     guint i;
495     gulong min_diff, min_i;
496
497     min_diff = ABS(tk->icons[0].width - w) + ABS(tk->icons[0].height - h);
498     min_i = 0;
499
500     for (i = 1; i < tk->nicons; ++i) {
501         gulong diff;
502
503         diff = ABS(tk->icons[i].width - w) + ABS(tk->icons[i].height - h);
504         if (diff < min_diff) {
505             min_diff = diff;
506             min_i = i;
507         }
508     }
509     return &tk->icons[min_i];
510 }
511
512 #define PADDING 2
513 #define ICON_SIZE WINHEIGHT-2*PADDING
514 static void gui_draw_task(task *tk, int redraw)
515 {
516     RrAppearance *a;
517     RrAppearance *b;
518     const icon *i;
519
520     if (tk->iconified)
521         b = iconified_task;
522     else if (tk->focused)
523         b = focused_task;
524     else if (tk->shaded)
525         b = iconified_task;
526     else
527         b = unfocused_task;
528
529     if (tk->iconified)
530         a = iconified_text;
531     else if (tk->shaded)
532         a = shaded_text;
533     else if (tk->focused)
534         a = focused_text;
535     else
536         a = normal_text;
537
538     i = best_task_icon(tk, ICON_SIZE, ICON_SIZE);
539
540     if (i) {
541         RrTexture *c = &a_icon->texture[0];
542         RrTextureRGBA *d = &c->data.rgba;
543         c->type = RR_TEXTURE_RGBA;
544         a_icon->surface.parent = b;
545         a_icon->surface.parentx = PADDING;
546         a_icon->surface.parenty = PADDING;
547         d->width = i->width;
548         d->height = i->height;
549         d->alpha = tk->iconified ? 0x80 : tk->shaded ? 0xB0 : 0xff;
550         d->data = i->data;
551     }
552
553     a->surface.parent = b;
554     a->surface.parentx = ICON_SIZE+2*PADDING;
555     a->texture[0].data.text.string = tk->name;
556     b->surface.parentx = tk->pos_x;
557
558     RrPaintPixmap(b, tk->width, WINHEIGHT);
559     RrPaintPixmap(a, tk->width-3*PADDING-ICON_SIZE, WINHEIGHT);
560     RrPaintPixmap(a_icon, ICON_SIZE, ICON_SIZE);
561
562     XCopyArea(dd, a_icon->pixmap, b->pixmap, fore_gc, 0, 0,
563               ICON_SIZE, ICON_SIZE, PADDING, PADDING);
564     XCopyArea(dd, a->pixmap, b->pixmap, fore_gc, 0, 0,
565               tk->width-3*PADDING-ICON_SIZE, WINHEIGHT, ICON_SIZE+2*PADDING, 0);
566     XCopyArea(dd, b->pixmap, bgpixmap, fore_gc, 0, 0,
567               tk->width, WINHEIGHT, tk->pos_x, 0);
568
569     a_icon->texture[1].type = RR_TEXTURE_NONE;
570     XFreePixmap(dd, a->pixmap);
571     XFreePixmap(dd, b->pixmap);
572     XFreePixmap(dd, a_icon->pixmap);
573     if (redraw) {
574         XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
575         XClearWindow(dd, tb.win);
576     }
577 }
578
579 static void netwm_action(Window win, atom_t atom, Time time, long l)
580 {
581     XClientMessageEvent xev = {
582     .type = ClientMessage,
583     .window = win,
584     .format = 32,
585     .data.l = {0},
586     .message_type = atoms[atom]
587     };
588     
589     if (atom == _NET_ACTIVE_WINDOW) {
590         xev.data.l[0] = 2;
591         xev.data.l[1] = time;
592         xev.data.l[2] = 0;
593     } else if (atom == _NET_CURRENT_DESKTOP) {
594         xev.data.l[0] = l;
595         xev.data.l[1] = time;
596     } else if (atom == _NET_RESTACK_WINDOW) {
597         xev.data.l[0] = 2;
598         xev.data.l[2] = l;
599     } else if (atom == _OB_FOCUS) {
600     } else {
601         xev.message_type = atoms[_NET_WM_STATE];
602         xev.data.l[0] = l;
603         xev.data.l[1] = atoms[atom];
604         xev.data.l[3] = 2;
605     }
606
607     XSendEvent(dd, root_win, False, SubstructureNotifyMask
608                |SubstructureRedirectMask, (XEvent *)&xev);
609 }
610
611 static void set_icon_geometry(task *tk)
612 {
613     long coords[4];
614
615     coords[0] = tb.x + tk->pos_x;
616     coords[1] = tb.y;
617     coords[2] = MAX(tk->width, 1);
618     coords[3] = WINHEIGHT;
619
620     XChangeProperty(dd, tk->win,
621                     atoms[_NET_WM_ICON_GEOMETRY], XA_CARDINAL,
622                     32, PropModeReplace, (unsigned char*) &coords, 4);
623 }
624
625
626 #ifdef PAGER
627
628 static void switch_desk(int new_desk, Time time)
629 {
630     if (get_number_of_desktops() <= new_desk)
631         return;
632
633     netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
634 }
635
636 /* This doesn't work with obrender yet */
637 static void pager_draw_button(int x, int num)
638 {
639     char label;
640 #ifdef XFT
641     XftColor col;
642 #endif
643
644     if (num == tb.my_desktop) {
645         /* current desktop */
646         draw_box(x, PAGER_BUTTON_WIDTH);
647     } else {
648         set_foreground(0);
649         fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
650     }
651
652     label = '1' + num;
653
654 #ifdef XFT
655     col.color.alpha = 0xffff;
656     col.color.red = cols[5].red;
657     col.color.green = cols[5].green;
658     col.color.blue = cols[5].blue;
659     XftDrawString8(xftdraw, &col, xfs,
660                    x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
661                    &label, 1);
662 #else
663     set_foreground(5);
664     XDrawString(dd, tb.win, fore_gc,
665                 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
666                 text_y, &label, 1);
667 #endif
668 }
669
670 static void pager_draw(void)
671 {
672     int desks, i, x = GRILL_WIDTH;
673
674     desks = get_number_of_desktops();
675
676     for (i = 0; i < desks; i++) {
677         pager_draw_button(x, i);
678         if (i > 8)
679             break;
680         x += PAGER_BUTTON_WIDTH;
681     }
682
683     pager_size = x;
684 }
685
686 #endif
687
688 static inline int dont_show_task(task *tk)
689 {
690     return !tk->focused && tk->hidden;
691 }
692
693 static void gui_draw_taskbar(void)
694 {
695     task *tk;
696     int x, width, taskw;
697     int num_tasks = 0;
698
699 #ifdef PAGER
700     pager_draw();
701 #else
702     pager_size = TEXTPAD;
703 #endif
704
705     x = pager_size + 2;
706     width = WINWIDTH - (pager_size + GRILL_WIDTH);
707 #warning only rerender if width changed!
708     if (bgpixmap) XFreePixmap(dd, bgpixmap);
709     bgpixmap = XCreatePixmap(dd, root_win, WINWIDTH, WINHEIGHT, RrDepth(inst));
710
711     XFreePixmap(dd, RrPaintPixmap(background, WINWIDTH, WINHEIGHT));
712     XCopyArea(dd, background->pixmap, bgpixmap, fore_gc, 0, 0, WINWIDTH, WINHEIGHT, 0, 0);
713
714     /* find the number of visible tasks */
715     for (tk = tb.task_list; tk; tk = tk->next) {
716         if (dont_show_task(tk))
717             continue;
718         num_tasks++;
719     }
720
721     if (num_tasks == 0)
722         goto clear;
723
724     taskw = width / num_tasks;
725     if (taskw > MAX_TASK_WIDTH)
726         taskw = MAX_TASK_WIDTH;
727
728     for (tk = tb.task_list; tk; tk = tk->next) {
729         if (dont_show_task(tk))
730             continue;
731         tk->pos_x = x;
732         tk->width = taskw - 1;
733         gui_draw_task(tk, FALSE);
734         set_icon_geometry(tk);
735         x += taskw;
736     }
737
738 clear:
739     XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
740     XClearWindow(dd, tb.win);
741 }
742
743 static task *find_task(Window win)
744 {
745     task *list = tb.task_list;
746     while (list) {
747         if (list->win == win)
748             return list;
749         list = list->next;
750     }
751     return 0;
752 }
753
754 static void del_task(Window win)
755 {
756     task *next, *prev = 0, *list = tb.task_list;
757
758     while (list) {
759         next = list->next;
760         if (list->win == win) {
761             /* unlink and free this task */
762             free_icons(list);
763             if (list->name)
764                 XFree(list->name);
765             free(list);
766             if (prev == 0)
767                 tb.task_list = next;
768             else
769                 prev->next = next;
770             return;
771         }
772         prev = list;
773         list = next;
774     }
775 }
776
777 static void move_taskbar(void)
778 {
779     tb.x = tb.y = 0;
780
781     if (tb.hidden)
782         tb.x = TEXTPAD - WINWIDTH;
783
784     if (!tb.at_top)
785         tb.y = scr_height - WINHEIGHT;
786
787     XMoveWindow(dd, tb.win, tb.x, tb.y);
788 }
789
790 static void taskbar_read_clientlist(void)
791 {
792     Window *win, focus_win = 0;
793     int num, i, desk, new_desk = 0;
794     task *list, *next;
795     desk = get_current_desktop();
796
797 #ifdef MIKACHU
798     if (desk == 0)
799         WINWIDTH = 827;
800     else
801         WINWIDTH = 1280;
802
803     XResizeWindow(dd, rspanelwin, WINWIDTH, WINHEIGHT);
804 #endif
805
806     if (desk != tb.my_desktop) {
807         new_desk = 1;
808         tb.my_desktop = desk;
809     }
810
811     win = get_prop_data(root_win, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, &num);
812     if (win && num > 0) {
813         focus_win = win[0];
814         XFree(win);
815     }
816
817     win = get_prop_data(root_win, atoms[_NET_CLIENT_LIST], XA_WINDOW, &num);
818     if (!win)
819         return;
820
821     /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
822     list = tb.task_list;
823     while (list) {
824         list->focused = (focus_win == list->win);
825         next = list->next;
826
827         if (!new_desk)
828             for (i = num - 1; i >= 0; i--)
829                 if (list->win == win[i])
830                     goto dontdel;
831         del_task(list->win);
832 dontdel:
833
834         list = next;
835     }
836
837     /* add any new windows */
838     for (i = 0; i < num; i++) {
839         if (!find_task(win[i]))
840             add_task(win[i], (win[i] == focus_win));
841     }
842
843     XFree(win);
844 }
845
846 static void handle_press(int x, int y, int button, Time time)
847 {
848     task *tk;
849
850 #ifdef PAGER
851     if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
852         switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
853     else
854 #endif
855     /* clicked left grill */
856     if (x < 6) {
857         if (button == 1) {
858             tb.at_top = 1 - tb.at_top;
859             move_taskbar();
860         } else if (button == 4) {
861             tb.hidden = 1;
862             move_taskbar();
863         }
864     }
865
866     /* clicked right grill */
867     else if (x + TEXTPAD > WINWIDTH) {
868         if (tb.hidden && (button == 1 || button == 5)) {
869             tb.hidden = 0;
870             move_taskbar();
871         } else if (!tb.hidden && (button == 1 || button == 4)) {
872             tb.hidden = 1;
873             move_taskbar();
874         }
875     } else {
876         tk = tb.task_list;
877         while (tk) {
878             /* clicked on a task button */
879             /* XXX Make this configureable */
880             if (!dont_show_task(tk)
881                 && (x > tk->pos_x && x < tk->pos_x + tk->width))
882             {
883                 switch (button) {
884                 case 1:
885                     netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
886                     break;
887                 case 2:
888                     if (tk->iconified)
889                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
890                     else
891                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, ADD);
892                     break;
893                 case 3:
894                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
895                     break;
896                 case 4:
897                     if (is_shaded(tk->win))
898                             netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
899                     else
900                             netwm_action(tk->win, _NET_WM_STATE_SHADED, time, ADD);
901                     break;
902                 case 5:
903                     if (is_shaded(tk->win))
904                         netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
905                     else
906                             netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
907                     break;
908                 case 9:
909                     if (tk->iconified)
910                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
911                     else
912                         netwm_action(tk->win, _OB_FOCUS, 0, 0);
913                     break;
914                 case 6:
915                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
916                     break;
917                 case 7:
918                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
919                     break;
920                 }
921                 return;
922             }
923             tk = tk->next;
924         } /* clicked on the background */
925         switch (button) {
926         case 1:
927             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
928             break;
929         case 2:
930             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
931             break;
932         case 3:
933             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
934             break;
935         case 4:
936             tb.hidden = 1;
937             move_taskbar();
938             break;
939         case 5:
940             tb.hidden = 0;
941             move_taskbar();
942             break;
943         }
944     }
945 }
946
947 #if 0
948 static void handle_focusin(Window win)
949 {
950     task *tk;
951
952     tk = tb.task_list;
953     while (tk) {
954         if (tk->focused) {
955             if (tk->win != win) {
956                 tk->focused = 0;
957                 gui_draw_task(tk);
958             }
959         } else {
960             if (tk->win == win) {
961                 tk->focused = 1;
962                 gui_draw_task(tk);
963             }
964         }
965         tk = tk->next;
966     }
967 }
968 #endif
969
970 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
971 {
972     Atom at = *(Atom*)arg;
973     return (e->type == PropertyNotify && e->xproperty.atom == at);
974 }
975
976 static void handle_propertynotify(Window win, Atom at)
977 {
978     task *tk;
979
980     if (win == root_win) {
981         /* XXX only redraw the task that got focused/unfocused
982          * when _NET_ACTIVE_WINDOW triggers this,
983          * redraw everything if that task was hidden before though */
984         if (at == atoms[_NET_CLIENT_LIST] || at == atoms[_NET_CURRENT_DESKTOP]
985             || at == atoms[_NET_ACTIVE_WINDOW])
986         {
987             XEvent ce;
988             Atom check;
989
990             check = atoms[_NET_CLIENT_LIST];
991             while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
992             check = atoms[_NET_CURRENT_DESKTOP];
993             while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
994             check = atoms[_NET_ACTIVE_WINDOW];
995             while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
996
997             taskbar_read_clientlist();
998             gui_draw_taskbar();
999         }
1000         return;
1001     }
1002
1003     /* XXX make this work when SKIP_TASKBAR is toggled too */
1004     /* show skip_taskbar tasks if they're focused */
1005     tk = find_task(win);
1006     if (!tk)
1007         return;
1008
1009     if (at == XA_WM_NAME || at == atoms[_NET_WM_NAME]) {
1010         /* window's title changed */
1011         /* XXX make a function for this and use from here and add_task */
1012         char *newname;
1013         newname = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
1014                   get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
1015         if (newname) {
1016             /* It didn't change */
1017             if (tk->name && !strcmp(newname, tk->name)) {
1018                 XFree(newname);
1019                 return;
1020             }
1021         }
1022         if (tk->name)
1023             XFree(tk->name);
1024         tk->name = newname;
1025         gui_draw_task(tk, TRUE);
1026     } else if (at == atoms[_NET_WM_ICON]) {
1027         task_update_icon(tk);
1028         gui_draw_task(tk, TRUE);
1029     } else if (at == atoms[_NET_WM_STATE]) {
1030         /* iconified state changed? */
1031         if (is_iconified(tk->win) != tk->iconified) {
1032             tk->iconified = !tk->iconified;
1033             if (!tk->hidden)
1034                 gui_draw_task(tk, TRUE);
1035         }
1036         /* shaded state changed? */
1037         if (is_shaded(tk->win) != tk->shaded) {
1038             tk->shaded = !tk->shaded;
1039             gui_draw_task(tk, TRUE);
1040         }
1041         if (is_hidden(tk->win) != tk->hidden) {
1042             tk->hidden = !tk->hidden;
1043             gui_draw_taskbar();
1044         }
1045     } else if (at == atoms[_NET_WM_DESKTOP]) {
1046         if (find_desktop(win) != get_current_desktop())
1047             del_task(tk->win);
1048     }
1049 }
1050
1051 static void handle_error(Display * d, XErrorEvent * ev)
1052 {
1053 }
1054
1055 int
1056 #ifdef NOSTDLIB
1057 _start(void)
1058 #else
1059 main(int argc, char *argv[])
1060 #endif
1061 {
1062     XEvent ev;
1063     fd_set fd;
1064     int xfd;
1065
1066     dd = XOpenDisplay(NULL);
1067     if (!dd)
1068         return 0;
1069     scr_screen = DefaultScreen(dd);
1070     scr_depth = DefaultDepth(dd, scr_screen);
1071     scr_height = DisplayHeight(dd, scr_screen);
1072     scr_width = DisplayWidth(dd, scr_screen);
1073     root_win = RootWindow(dd, scr_screen);
1074
1075     /* helps us catch windows closing/opening */
1076     XSelectInput(dd, root_win, PropertyChangeMask);
1077
1078     XSetErrorHandler((XErrorHandler) handle_error);
1079
1080     XInternAtoms(dd, atom_names, ATOM_COUNT, False, atoms);
1081
1082     gui_init();
1083     memset(&tb, 0, sizeof(tb));
1084     tb.win = gui_create_taskbar();
1085     xfd = ConnectionNumber(dd);
1086     XSync(dd, False);
1087     taskbar_read_clientlist();
1088     gui_draw_taskbar();
1089
1090     while (1) {
1091         FD_ZERO(&fd);
1092         FD_SET(xfd, &fd);
1093         select(xfd + 1, &fd, 0, 0, 0);
1094
1095         while (XPending(dd)) {
1096             XNextEvent(dd, &ev);
1097             switch (ev.type) {
1098             case ButtonPress:
1099                 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
1100                 break;
1101             case PropertyNotify:
1102                 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
1103                 break;
1104             /*default:
1105                    printf ("unknown evt type: %d\n", ev.type); */
1106             }
1107         }
1108     }
1109     /* RrInstanceFree(inst);
1110      * XCloseDisplay (dd);
1111
1112        return 0; */
1113 }
1114