2 /**********************************************************
3 ** Rather Small Panel 0.8beta1 Copyright (c) 2006 **
4 ** By Mikael Magnusson **
5 ** See file COPYING for license details. **
6 **********************************************************/
12 #include <sys/types.h>
16 #include <X11/Xutil.h>
17 #include <X11/Xatom.h>
20 /* you can edit these */
21 #define MAX_TASK_WIDTH 500
24 /* XXX get this from the font - i think it should be user configurable, most
25 panels provide an option for this */
28 # define WINWIDTH scr_width
33 /* don't edit these */
35 #define GRILL_WIDTH 10
37 #include <openbox/render.h>
38 #include <openbox/theme.h>
55 RrAppearance *background;
56 RrAppearance *focused_task;
57 RrAppearance *iconified_task;
58 RrAppearance *unfocused_task;
59 RrAppearance *normal_text;
60 RrAppearance *iconified_text;
61 RrAppearance *shaded_text;
62 RrAppearance *focused_text;
65 /* we draw stuff to this guy then set him as a window
66 * background pixmap, yay no flickering! */
69 char *atom_names[] = {
75 "_NET_WM_STATE_SKIP_TASKBAR",
76 "_NET_WM_STATE_SHADED",
77 "_NET_WM_STATE_BELOW",
78 "_NET_WM_STATE_HIDDEN",
80 "_NET_WM_WINDOW_TYPE",
81 "_NET_WM_WINDOW_TYPE_DOCK",
86 "_NET_CLIENT_LIST_STACKING",
87 "_NET_NUMBER_OF_DESKTOPS",
88 "_NET_CURRENT_DESKTOP",
93 "_NET_RESTACK_WINDOW",
95 "_NET_WM_ICON_GEOMETRY",
104 _NET_WM_STATE_SKIP_TASKBAR,
105 _NET_WM_STATE_SHADED,
107 _NET_WM_STATE_HIDDEN,
110 _NET_WM_WINDOW_TYPE_DOCK,
114 _NET_CLIENT_LIST_STACKING,
115 _NET_NUMBER_OF_DESKTOPS,
116 _NET_CURRENT_DESKTOP,
123 _NET_WM_ICON_GEOMETRY,
128 Atom atoms[ATOM_COUNT];
136 static void *get_prop_data(Window win, Atom prop, Atom type, int *items)
140 unsigned long items_ret;
141 unsigned long after_ret;
142 unsigned char *prop_data;
146 XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False, type, &type_ret,
147 &format_ret, &items_ret, &after_ret, &prop_data);
154 static int generic_get_int(Window win, Atom at)
159 data = get_prop_data(win, at, XA_CARDINAL, 0);
167 static char* generic_get_utf8(Window win, Atom at)
169 return get_prop_data(win, at, atoms[UTF8_STRING], 0);
172 static char* generic_get_string(Window win, Atom at)
174 return get_prop_data(win, at, XA_STRING, 0);
177 static int find_desktop(Window win)
179 return generic_get_int(win, atoms[_NET_WM_DESKTOP]);
182 static int find_state(Window win, atom_t atom)
188 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
191 if ((data[num]) == atoms[atom])
199 /* this naming is very confusing :) */
200 static int is_hidden(Window win)
202 return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
205 static int is_iconified(Window win)
207 return find_state(win, _NET_WM_STATE_HIDDEN);
210 static int is_shaded(Window win)
212 return find_state(win, _NET_WM_STATE_SHADED);
215 static char* get_openbox_theme()
219 static int get_current_desktop(void)
221 return generic_get_int(root_win, atoms[_NET_CURRENT_DESKTOP]);
225 static int get_number_of_desktops(void)
227 return generic_get_int(root_win, atoms[_NET_NUMBER_OF_DESKTOPS]);
231 static void free_icons(task *tk)
235 for (i = 0; i < tk->nicons; ++i)
236 free(tk->icons[i].data);
242 static void task_update_icon(task *tk)
244 /* these bits are borrowed from openbox' client.c::client_update_icons */
247 unsigned int w, h, i, j;
251 data = get_prop_data(tk->win, atoms[_NET_WM_ICON], XA_CARDINAL, &num);
253 /* figure out how many valid icons are in here */
255 while (num - i > 2) {
259 if (i > num || w*h == 0) break;
263 tk->icons = calloc(tk->nicons, sizeof(icon));
265 /* store the icons */
267 for (j = 0; j < tk->nicons; ++j) {
270 w = tk->icons[j].width = data[i++];
271 h = tk->icons[j].height = data[i++];
273 if (w*h == 0) continue;
275 tk->icons[j].data = g_new(RrPixel32, w * h);
276 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
281 tk->icons[j].data[t] =
282 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
283 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
284 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
285 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
294 if ((hints = XGetWMHints(dd, tk->win))) {
295 if (hints->flags & IconPixmapHint) {
297 tk->icons = g_new(icon, tk->nicons);
298 // xerror_set_ignore(TRUE);
299 if (!RrPixmapToRGBA(inst,
301 (hints->flags & IconMaskHint ?
302 hints->icon_mask : None),
303 &tk->icons[tk->nicons-1].width,
304 &tk->icons[tk->nicons-1].height,
305 &tk->icons[tk->nicons-1].data)){
306 g_free(&tk->icons[tk->nicons-1]);
309 // xerror_set_ignore(FALSE);
316 static void add_task(Window win, int focus)
324 /* is this window on a different desktop? */
325 /* XXX add even if is_hidden, but don't show it later, so we can
326 * unhide it when the prop is removed */
327 desk = find_desktop(win);
328 if ((desk != 0xffffffff && tb.my_desktop != desk))
331 XSelectInput(dd, win, PropertyChangeMask);
333 tk = calloc(1, sizeof(task));
336 tk->name = generic_get_utf8(tk->win, atoms[_NET_WM_NAME]) ?:
337 generic_get_string(tk->win, XA_WM_NAME);
339 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
340 tk->iconified = is_iconified(win);
341 tk->shaded = is_shaded(win);
342 tk->hidden = is_hidden(win);
343 task_update_icon(tk);
345 /* now append it to our linked list */
361 static void set_prop(Window win, Atom at, Atom type, long val)
363 XChangeProperty(dd, win, at, type, 32,
364 PropModeReplace, (unsigned char *)&val, 1);
367 static Window gui_create_taskbar(void)
370 XSizeHints size_hints;
371 XSetWindowAttributes att;
374 att.event_mask = ButtonPressMask;
377 tb.y = scr_height - WINHEIGHT;
379 win = rspanelwin = XCreateWindow(/* display */ dd,
380 /* parent */ root_win,
383 /* XXX Maybe just use scr_width here? */
384 /* width */ WINWIDTH,
385 /* height */ WINHEIGHT,
387 /* depth */ CopyFromParent,
388 /* class */ InputOutput,
389 /* visual */ CopyFromParent,
390 /*value mask*/ CWEventMask,
393 /* reside on ALL desktops */
394 set_prop(win, atoms[_NET_WM_DESKTOP], XA_CARDINAL, 0xFFFFFFFF);
395 set_prop(win, atoms[_NET_WM_WINDOW_TYPE], XA_ATOM,
396 atoms[_NET_WM_WINDOW_TYPE_DOCK]);
397 set_prop(win, atoms[_NET_WM_STATE], XA_ATOM, atoms[_NET_WM_STATE_BELOW]);
398 XChangeProperty(dd, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
399 (unsigned char *)"rspanel", 7);
401 /* make sure the WM obays our window position */
402 size_hints.flags = PPosition;
403 /*XSetWMNormalHints (dd, win, &size_hints); */
404 XChangeProperty(dd, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32,
405 PropModeReplace, (unsigned char *)&size_hints,
406 sizeof(XSizeHints) / 4);
408 xclhints.res_name = "rspanel";
409 xclhints.res_class = "RSPanel";
410 XSetClassHint(dd, win, &xclhints);
417 #define SURF(x) with->surface.x
418 #define TEXT(x) with->texture[0].data.text.x
419 #define USE(x) (with = x)
420 #define SETTEXT(x, y, z) with->texture[0].type = RR_TEXTURE_TEXT; \
421 with->texture[0].data.text.font = x; \
422 with->texture[0].data.text.justify = y; \
423 with->texture[0].data.text.ellipsize = z;
424 #define SETSHADOW(y, z, u, v) with->texture[0].data.text.shadow_offset_x = y; \
425 with->texture[0].data.text.shadow_offset_y = z; \
426 with->texture[0].data.text.shadow_alpha = u; \
427 with->texture[0].data.text.shadow_color = v;
428 static void gui_init(void)
434 gcv.graphics_exposures = False;
436 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
438 inst = RrInstanceNew(dd, scr_screen);
440 /* We don't allow different fonts for various window states... */
441 font = RrFontOpen(inst, "Candara, sans", 10, RR_FONTWEIGHT_NORMAL, RR_FONTSLANT_NORMAL);
443 /* this appearance will be used to draw icons, we don't
444 * set the type of the texture to RGBA here, because we toggle
445 * that so obrender doesn't try to draw an icon when the app
446 * doesn't have one */
447 /* XXX don't do that */
448 a_icon = RrAppearanceNew(inst, 1);
450 SURF(grad) = RR_SURFACE_PARENTREL;
452 /* this is the appearance for the background of the panel */
453 background = RrAppearanceNew(inst, 0);
455 SURF(grad) = RR_SURFACE_DIAGONAL;
456 SURF(primary) = RrColorNew(inst, 170, 170, 190);
457 SURF(secondary) = RrColorNew(inst, 100, 100, 160);
459 /* this is the appearance for unfocused tasks,
460 * text needs to be separate appearances so we can align it correctly */
461 unfocused_task = RrAppearanceNew(inst, 0);
463 SURF(parent) = background;
464 SURF(grad) = RR_SURFACE_PARENTREL;
466 SURF(border_color) = RrColorNew(inst, 0, 0, 80);
468 /* ... for iconified tasks, also used for shaded ones currently */
469 iconified_task = RrAppearanceCopy(unfocused_task);
471 SURF(relief) = RR_RELIEF_SUNKEN;
472 SURF(border) = FALSE;
473 SURF(parent) = background; /* RrAppearanceCopy doesn't copy .parent */
475 /* ... for focused tasks */
476 focused_task = RrAppearanceNew(inst, 0);
478 SURF(grad) = RR_SURFACE_CROSS_DIAGONAL;
479 SURF(secondary) = RrColorNew(inst, 70, 80, 110);
480 SURF(primary) = RrColorNew(inst, 130, 160, 250);
481 SURF(relief) = RR_RELIEF_RAISED;
483 /* this is the text used for all normal unfocused tasks */
484 /* we don't set .parent here, but in draw_task, since we
485 * want to combine _task and _text semirandomly.
486 * XXX plz not when themes are here */
487 normal_text = RrAppearanceNew(inst, 1);
489 SURF(grad) = RR_SURFACE_PARENTREL;
490 SETTEXT(font, RR_JUSTIFY_LEFT, RR_ELLIPSIZE_END);
492 /* ... for iconified tasks */
493 iconified_text = RrAppearanceCopy(normal_text);
494 /* ... and for focused tasks, we copy this here (ie not 5 lines down)
495 * so the color isn't copied i actually don't know if that would
496 * hurt so XXX on that */
497 focused_text = RrAppearanceCopy(normal_text);
499 TEXT(color) = RrColorNew(inst, 230, 230, 255);
502 TEXT(color) = RrColorNew(inst, 20, 20, 40);
505 SETSHADOW(2, 2, 100, RrColorNew(inst, 0, 0, 0));
506 TEXT(color) = RrColorNew(inst, 200, 200, 200);
508 shaded_text = RrAppearanceCopy(normal_text);
510 TEXT(color) = RrColorNew(inst, 50, 60, 90);
513 const icon* best_task_icon(task *tk, gint w, gint h)
517 gulong min_diff, min_i;
519 min_diff = ABS(tk->icons[0].width - w) + ABS(tk->icons[0].height - h);
522 for (i = 1; i < tk->nicons; ++i) {
525 diff = ABS(tk->icons[i].width - w) + ABS(tk->icons[i].height - h);
526 if (diff < min_diff) {
531 return &tk->icons[min_i];
535 #define ICON_SIZE WINHEIGHT-2*PADDING
536 static void gui_draw_task(task *tk, int redraw)
544 else if (tk->focused)
555 else if (tk->focused)
560 i = best_task_icon(tk, ICON_SIZE, ICON_SIZE);
563 RrTexture *c = &a_icon->texture[0];
564 RrTextureRGBA *d = &c->data.rgba;
565 c->type = RR_TEXTURE_RGBA;
566 a_icon->surface.parent = b;
567 a_icon->surface.parentx = PADDING;
568 a_icon->surface.parenty = PADDING;
570 d->height = i->height;
571 d->alpha = tk->iconified ? 0x80 : tk->shaded ? 0xB0 : 0xff;
575 a->surface.parent = b;
576 a->surface.parentx = ICON_SIZE+2*PADDING;
577 a->texture[0].data.text.string = tk->name;
578 b->surface.parentx = tk->pos_x;
580 RrPaintPixmap(b, tk->width, WINHEIGHT);
581 RrPaintPixmap(a, tk->width-3*PADDING-ICON_SIZE, WINHEIGHT);
582 RrPaintPixmap(a_icon, ICON_SIZE, ICON_SIZE);
584 XCopyArea(dd, a_icon->pixmap, b->pixmap, fore_gc, 0, 0,
585 ICON_SIZE, ICON_SIZE, PADDING, PADDING);
586 XCopyArea(dd, a->pixmap, b->pixmap, fore_gc, 0, 0,
587 tk->width-3*PADDING-ICON_SIZE, WINHEIGHT, ICON_SIZE+2*PADDING, 0);
588 XCopyArea(dd, b->pixmap, bgpixmap, fore_gc, 0, 0,
589 tk->width, WINHEIGHT, tk->pos_x, 0);
591 a_icon->texture[1].type = RR_TEXTURE_NONE;
592 XFreePixmap(dd, a->pixmap);
593 XFreePixmap(dd, b->pixmap);
594 XFreePixmap(dd, a_icon->pixmap);
596 XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
597 XClearWindow(dd, tb.win);
601 static void netwm_action(Window win, atom_t atom, Time time, long l)
603 XClientMessageEvent xev = {
604 .type = ClientMessage,
608 .message_type = atoms[atom]
611 if (atom == _NET_ACTIVE_WINDOW) {
613 xev.data.l[1] = time;
615 } else if (atom == _NET_CURRENT_DESKTOP) {
617 xev.data.l[1] = time;
618 } else if (atom == _NET_RESTACK_WINDOW) {
621 } else if (atom == _OB_FOCUS) {
623 xev.message_type = atoms[_NET_WM_STATE];
625 xev.data.l[1] = atoms[atom];
629 XSendEvent(dd, root_win, False, SubstructureNotifyMask
630 |SubstructureRedirectMask, (XEvent *)&xev);
633 static void set_icon_geometry(task *tk)
637 coords[0] = tb.x + tk->pos_x;
639 coords[2] = MAX(tk->width, 1);
640 coords[3] = WINHEIGHT;
642 XChangeProperty(dd, tk->win,
643 atoms[_NET_WM_ICON_GEOMETRY], XA_CARDINAL,
644 32, PropModeReplace, (unsigned char*) &coords, 4);
650 static void switch_desk(int new_desk, Time time)
652 if (get_number_of_desktops() <= new_desk)
655 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
658 /* This doesn't work with obrender yet */
659 static void pager_draw_button(int x, int num)
666 if (num == tb.my_desktop) {
667 /* current desktop */
668 draw_box(x, PAGER_BUTTON_WIDTH);
671 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
677 col.color.alpha = 0xffff;
678 col.color.red = cols[5].red;
679 col.color.green = cols[5].green;
680 col.color.blue = cols[5].blue;
681 XftDrawString8(xftdraw, &col, xfs,
682 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
686 XDrawString(dd, tb.win, fore_gc,
687 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
692 static void pager_draw(void)
694 int desks, i, x = GRILL_WIDTH;
696 desks = get_number_of_desktops();
698 for (i = 0; i < desks; i++) {
699 pager_draw_button(x, i);
702 x += PAGER_BUTTON_WIDTH;
710 static inline int dont_show_task(task *tk)
712 return !tk->focused && tk->hidden;
715 static void gui_draw_taskbar(void)
724 pager_size = TEXTPAD;
728 width = WINWIDTH - (pager_size + GRILL_WIDTH);
729 #warning only rerender if width changed!
730 if (bgpixmap) XFreePixmap(dd, bgpixmap);
731 bgpixmap = XCreatePixmap(dd, root_win, WINWIDTH, WINHEIGHT, RrDepth(inst));
733 XFreePixmap(dd, RrPaintPixmap(background, WINWIDTH, WINHEIGHT));
734 XCopyArea(dd, background->pixmap, bgpixmap, fore_gc, 0, 0, WINWIDTH, WINHEIGHT, 0, 0);
736 /* find the number of visible tasks */
737 for (tk = tb.task_list; tk; tk = tk->next) {
738 if (dont_show_task(tk))
746 taskw = width / num_tasks;
747 if (taskw > MAX_TASK_WIDTH)
748 taskw = MAX_TASK_WIDTH;
750 for (tk = tb.task_list; tk; tk = tk->next) {
751 if (dont_show_task(tk))
754 tk->width = taskw - 1;
755 gui_draw_task(tk, FALSE);
756 set_icon_geometry(tk);
761 XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
762 XClearWindow(dd, tb.win);
766 static task *find_task(Window win)
768 task *list = tb.task_list;
770 if (list->win == win)
777 static void del_task(Window win)
779 task *next, *prev = 0, *list = tb.task_list;
783 if (list->win == win) {
784 /* unlink and free this task */
800 static void move_taskbar(void)
805 tb.x = TEXTPAD - WINWIDTH;
808 tb.y = scr_height - WINHEIGHT;
810 XMoveWindow(dd, tb.win, tb.x, tb.y);
813 static void taskbar_read_clientlist(void)
815 Window *win, focus_win = 0;
816 int num, i, desk, new_desk = 0;
818 desk = get_current_desktop();
826 XResizeWindow(dd, rspanelwin, WINWIDTH, WINHEIGHT);
829 if (desk != tb.my_desktop) {
831 tb.my_desktop = desk;
834 win = get_prop_data(root_win, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, &num);
835 if (win && num > 0) {
840 win = get_prop_data(root_win, atoms[_NET_CLIENT_LIST], XA_WINDOW, &num);
844 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
847 list->focused = (focus_win == list->win);
851 for (i = num - 1; i >= 0; i--)
852 if (list->win == win[i])
860 /* add any new windows */
861 for (i = 0; i < num; i++) {
862 if (!find_task(win[i]))
863 add_task(win[i], (win[i] == focus_win));
869 static void handle_press(int x, int y, int button, Time time)
874 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
875 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
878 /* clicked left grill */
881 tb.at_top = 1 - tb.at_top;
883 } else if (button == 4) {
889 /* clicked right grill */
890 else if (x + TEXTPAD > WINWIDTH) {
891 if (tb.hidden && (button == 1 || button == 5)) {
894 } else if (!tb.hidden && (button == 1 || button == 4)) {
901 /* clicked on a task button */
902 /* XXX Make this configureable */
903 if (!dont_show_task(tk)
904 && (x > tk->pos_x && x < tk->pos_x + tk->width))
908 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
912 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
914 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, ADD);
917 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
920 if (is_shaded(tk->win))
921 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
923 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, ADD);
926 if (is_shaded(tk->win))
927 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
929 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
933 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
935 netwm_action(tk->win, _OB_FOCUS, 0, 0);
938 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
941 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
947 } /* clicked on the background */
950 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
953 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
956 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
971 static void handle_focusin(Window win)
978 if (tk->win != win) {
983 if (tk->win == win) {
993 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
995 Atom at = *(Atom*)arg;
996 return (e->type == PropertyNotify && e->xproperty.atom == at);
999 static void handle_propertynotify(Window win, Atom at)
1003 if (win == root_win) {
1004 /* XXX only redraw the task that got focused/unfocused
1005 * when _NET_ACTIVE_WINDOW triggers this,
1006 * redraw everything if that task was hidden before though */
1007 if (at == atoms[_NET_CLIENT_LIST] || at == atoms[_NET_CURRENT_DESKTOP]
1008 || at == atoms[_NET_ACTIVE_WINDOW])
1013 check = atoms[_NET_CLIENT_LIST];
1014 while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
1015 check = atoms[_NET_CURRENT_DESKTOP];
1016 while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
1017 check = atoms[_NET_ACTIVE_WINDOW];
1018 while (XCheckIfEvent(dd, &ce, look_for_duplicate_property, (XPointer)&check));
1020 taskbar_read_clientlist();
1026 /* XXX make this work when SKIP_TASKBAR is toggled too */
1027 /* show skip_taskbar tasks if they're focused */
1028 tk = find_task(win);
1032 if (at == XA_WM_NAME || at == atoms[_NET_WM_NAME]) {
1033 /* window's title changed */
1034 /* XXX make a function for this and use from here and add_task */
1036 newname = generic_get_utf8(tk->win, atoms[_NET_WM_NAME]) ?:
1037 generic_get_string(tk->win, XA_WM_NAME);
1039 /* It didn't change */
1040 if (tk->name && !strcmp(newname, tk->name)) {
1048 gui_draw_task(tk, TRUE);
1049 } else if (at == atoms[_NET_WM_ICON]) {
1050 task_update_icon(tk);
1051 gui_draw_task(tk, TRUE);
1052 } else if (at == atoms[_NET_WM_STATE]) {
1053 /* iconified state changed? */
1054 if (is_iconified(tk->win) != tk->iconified) {
1055 tk->iconified = !tk->iconified;
1057 gui_draw_task(tk, TRUE);
1059 /* shaded state changed? */
1060 if (is_shaded(tk->win) != tk->shaded) {
1061 tk->shaded = !tk->shaded;
1062 gui_draw_task(tk, TRUE);
1064 if (is_hidden(tk->win) != tk->hidden) {
1065 tk->hidden = !tk->hidden;
1068 } else if (at == atoms[_NET_WM_DESKTOP]) {
1069 if (find_desktop(win) != get_current_desktop())
1074 static void handle_error(Display * d, XErrorEvent * ev)
1082 main(int argc, char *argv[])
1088 dd = XOpenDisplay(NULL);
1091 xfd = ConnectionNumber(dd);
1093 scr_screen = DefaultScreen(dd);
1094 scr_depth = DefaultDepth(dd, scr_screen);
1095 scr_height = DisplayHeight(dd, scr_screen);
1096 scr_width = DisplayWidth(dd, scr_screen);
1097 root_win = RootWindow(dd, scr_screen);
1099 /* helps us catch windows closing/opening */
1100 XSelectInput(dd, root_win, PropertyChangeMask | SubstructureNotifyMask);
1102 XSetErrorHandler((XErrorHandler) handle_error);
1104 XInternAtoms(dd, atom_names, ATOM_COUNT, False, atoms);
1107 memset(&tb, 0, sizeof(tb));
1108 tb.win = gui_create_taskbar();
1110 taskbar_read_clientlist();
1118 select(xfd + 1, &fd, 0, 0, 0);
1120 while (XPending(dd)) {
1121 XNextEvent(dd, &ev);
1124 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
1126 case PropertyNotify:
1127 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
1130 printf ("unknown evt type: %d\n", ev.type); */
1134 /* RrInstanceFree(inst);
1135 * XCloseDisplay (dd);