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>
26 /* you can edit these */
27 #define MAX_TASK_WIDTH 500
32 # define WINWIDTH 1280
36 #define FONT_NAME "-*-Technical*-m*-r-*-*-14-*-*"
37 #define XFT_FONT "Arial Unicode MS-12"
38 #define PANGO_FONT_PREF "Utopia"
39 //#define PANGO_FONT_PREF "Technical"
40 #define PANGO_FONT_SIZE 13
41 //#define PAGER /* use a desktop pager? */
42 #define PAGER_DIGIT_WIDTH 6
43 #define PAGER_BUTTON_WIDTH 20
48 /* don't edit these */
50 #define GRILL_WIDTH 10
55 #include <pango/pango.h>
56 #include <pango/pangoxft.h>
59 #include <X11/Xft/Xft.h>
64 #include <openbox/render.h>
65 #include <openbox/theme.h>
86 RrAppearance *background;
87 RrAppearance *focused_task;
88 RrAppearance *unfocused_task;
89 RrAppearance *normal_text;
90 RrAppearance *iconified_text;
91 RrAppearance *shaded_text;
92 RrAppearance *focused_text;
107 unsigned short red, green, blue;
109 {0xd75c, 0xd75c, 0xe75c}, /* 0. light gray */
110 {0xbefb, 0xbaea, 0xcefb}, /* 1. mid gray */
111 {0xaefb, 0xaaea, 0xfefb}, /* 2. dark gray */
112 {0xefbe, 0xefbe, 0xffbe}, /* 3. white */
113 {0x8617, 0x8207, 0x9617}, /* 4. darkest gray */
114 {0x0000, 0x0000, 0x1000}, /* 5. black */
117 #define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0].red) / 3)
119 unsigned long palette[PALETTE_COUNT];
121 char *atom_names[] = {
127 "_NET_WM_STATE_SKIP_TASKBAR",
128 "_NET_WM_STATE_SHADED",
129 "_NET_WM_STATE_BELOW",
130 "_NET_WM_STATE_HIDDEN",
132 "_NET_WM_WINDOW_TYPE",
133 "_NET_WM_WINDOW_TYPE_DOCK",
138 "_NET_CLIENT_LIST_STACKING",
139 "_NET_NUMBER_OF_DESKTOPS",
140 "_NET_CURRENT_DESKTOP",
144 "_NET_ACTIVE_WINDOW",
145 "_NET_RESTACK_WINDOW",
154 _NET_WM_STATE_SKIP_TASKBAR,
155 _NET_WM_STATE_SHADED,
157 _NET_WM_STATE_HIDDEN,
160 _NET_WM_WINDOW_TYPE_DOCK,
164 _NET_CLIENT_LIST_STACKING,
165 _NET_NUMBER_OF_DESKTOPS,
166 _NET_CURRENT_DESKTOP,
176 Atom atoms[ATOM_COUNT];
184 static void *get_prop_data(Window win, Atom prop, Atom type, int *items)
188 unsigned long items_ret;
189 unsigned long after_ret;
190 unsigned char *prop_data;
194 XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False, type, &type_ret,
195 &format_ret, &items_ret, &after_ret, &prop_data);
202 static void set_foreground(int index)
204 XSetForeground(dd, fore_gc, palette[index]);
207 static void draw_line(int x, int y, int a, int b)
209 XDrawLine(dd, tb.win, fore_gc, x, y, a, b);
212 static void fill_rect(int x, int y, int a, int b)
214 XFillRectangle(dd, tb.win, fore_gc, x, y, a, b);
217 static void scale_icon(task * tk)
220 unsigned int w, h, d, bw;
221 Pixmap pix, mk = None;
225 XGetGeometry(dd, tk->icon, &pix, &x, &y, &w, &h, &bw, &d);
226 pix = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, scr_depth);
228 if (tk->mask != None) {
229 mk = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, 1);
230 gcv.subwindow_mode = IncludeInferiors;
231 gcv.graphics_exposures = False;
232 mgc = XCreateGC(dd, mk, GCGraphicsExposures | GCSubwindowMode, &gcv);
237 /* this is my simple & dirty scaling routine */
238 for (y = ICONHEIGHT - 1; y >= 0; y--) {
239 yy = (y * h) / ICONHEIGHT;
240 for (x = ICONWIDTH - 1; x >= 0; x--) {
241 xx = (x * w) / ICONWIDTH;
243 XCopyPlane(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y, 1);
245 XCopyArea(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y);
247 XCopyArea(dd, tk->mask, mk, mgc, xx, yy, 1, 1, x, y);
259 static void get_task_hinticon(task * tk)
263 if (tk->icon != None && tk->icon != generic_icon)
264 if (tk->icon_copied) {
265 XFreePixmap(dd, tk->icon);
266 if (tk->mask != None && tk->mask != generic_mask)
267 XFreePixmap(dd, tk->mask);
273 hin = (XWMHints *)get_prop_data(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
275 if ((hin->flags & IconPixmapHint)) {
276 if ((hin->flags & IconMaskHint)) {
277 tk->mask = hin->icon_mask;
280 tk->icon = hin->icon_pixmap;
287 if (tk->icon == None) {
288 tk->icon = generic_icon;
289 if (tk->mask != None)
290 XFreePixmap(dd, tk->mask);
291 tk->mask = generic_mask;
295 static void get_task_kdeicon(task * tk)
299 data = get_prop_data(tk->win, atoms[KWM_WIN_ICON], atoms[KWM_WIN_ICON], 0);
307 static int generic_get_int(Window win, Atom at)
312 data = get_prop_data(win, at, XA_CARDINAL, 0);
320 static int find_desktop(Window win)
322 return generic_get_int(win, atoms[_NET_WM_DESKTOP]);
325 static int find_state(Window win, atom_t atom)
331 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
334 if ((data[num]) == atoms[atom])
342 /* this naming is very confusing :) */
343 static int is_hidden(Window win)
345 return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
348 static int is_iconified(Window win)
350 return find_state(win, _NET_WM_STATE_HIDDEN);
353 static int is_shaded(Window win)
355 return find_state(win, _NET_WM_STATE_SHADED);
358 static int get_current_desktop(void)
360 return generic_get_int(root_win, atoms[_NET_CURRENT_DESKTOP]);
363 static int get_number_of_desktops(void)
365 return generic_get_int(root_win, atoms[_NET_NUMBER_OF_DESKTOPS]);
368 static void add_task(Window win, int focus)
376 /* is this window on a different desktop? */
377 desk = find_desktop(win);
378 if ((desk != 0xffffffff && tb.my_desktop != desk) || is_hidden(win))
381 tk = calloc(1, sizeof(task));
384 tk->name = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
385 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
386 // tk->name = get_prop_data(win, XA_WM_NAME, XA_STRING, 0);
387 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
388 tk->iconified = is_iconified(win);
389 tk->shaded = is_shaded(win);
391 get_task_kdeicon(tk);
392 if (tk->icon == None)
393 get_task_hinticon(tk);
395 XSelectInput(dd, win, PropertyChangeMask | FocusChangeMask
396 | StructureNotifyMask);
398 /* now append it to our linked list */
415 static void set_prop(Window win, Atom at, Atom type, long val)
417 XChangeProperty(dd, win, at, type, 32,
418 PropModeReplace, (unsigned char *)&val, 1);
421 static Window gui_create_taskbar(void)
425 XSizeHints size_hints;
427 XSetWindowAttributes att;
430 att.background_pixel = palette[0];
431 att.event_mask = ButtonPressMask | ExposureMask;
433 win = rspanelwin = XCreateWindow(/* display */ dd,
434 /* parent */ root_win,
436 /* y */ scr_height - WINHEIGHT,
437 /* XXX Maybe just use scr_width here? */
438 /* width */ WINWIDTH,
439 /* height */ WINHEIGHT,
441 /* depth */ CopyFromParent,
442 /* class */ InputOutput,
443 /* visual */ CopyFromParent,
444 /*value mask*/ CWBackPixel | CWEventMask,
447 /* reside on ALL desktops */
448 set_prop(win, atoms[_NET_WM_DESKTOP], XA_CARDINAL, 0xFFFFFFFF);
449 set_prop(win, atoms[_NET_WM_WINDOW_TYPE], XA_ATOM,
450 atoms[_NET_WM_WINDOW_TYPE_DOCK]);
451 set_prop(win, atoms[_NET_WM_STATE], XA_ATOM, atoms[_NET_WM_STATE_BELOW]);
452 /* use old gnome hint since sawfish doesn't support _NET_WM_STRUT */
453 set_prop(win, atoms[_WIN_HINTS], XA_CARDINAL,
454 WIN_HINTS_SKIP_FOCUS | WIN_HINTS_SKIP_WINLIST |
455 WIN_HINTS_SKIP_TASKBAR | WIN_HINTS_DO_NOT_COVER);
456 XChangeProperty(dd, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
457 (unsigned char *)"rspanel", 7);
459 /* borderless motif hint */
460 bzero(&mwm, sizeof(mwm));
461 mwm.flags = MWM_HINTS_DECORATIONS;
462 XChangeProperty(dd, win, atoms[_MOTIF_WM_HINTS], atoms[_MOTIF_WM_HINTS], 32,
463 PropModeReplace, (unsigned char *)&mwm,
464 sizeof(MWMHints) / 4);
466 /* make sure the WM obays our window position */
467 size_hints.flags = PPosition;
468 /*XSetWMNormalHints (dd, win, &size_hints); */
469 XChangeProperty(dd, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32,
470 PropModeReplace, (unsigned char *)&size_hints,
471 sizeof(XSizeHints) / 4);
473 /* make our window unfocusable */
474 wmhints.flags = InputHint;
475 wmhints.input = False;
476 /*XSetWMHints (dd, win, &wmhints); */
477 XChangeProperty(dd, win, XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
478 (unsigned char *)&wmhints, sizeof(XWMHints) / 4);
480 xclhints.res_name = "rspanel";
481 xclhints.res_class = "RSPanel";
482 XSetClassHint(dd, win, &xclhints);
487 xftdraw = XftDrawCreate(dd, win, DefaultVisual(dd, scr_screen),
488 DefaultColormap(dd, scr_screen));
497 static void gui_init(void)
506 PangoContext *context;
507 PangoFontDescription *pfd;
512 xcl.red = cols[i].red;
513 xcl.green = cols[i].green;
514 xcl.blue = cols[i].blue;
515 XAllocColor(dd, DefaultColormap(dd, scr_screen), &xcl);
516 palette[i] = xcl.pixel;
518 } while (i < PALETTE_COUNT);
522 xfs = XftFontOpenName(dd, scr_screen, XFT_FONT);
524 fontname = FONT_NAME;
526 xfs = XLoadQueryFont(dd, fontname);
531 gcv.graphics_exposures = False;
534 text_y = xfs->ascent + ((WINHEIGHT - (xfs->ascent + xfs->descent)) / 2);
535 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
537 text_y = xfs->ascent + ((WINHEIGHT - xfs->ascent) / 2);
539 fore_gc = XCreateGC(dd, root_win, GCFont | GCGraphicsExposures, &gcv);
542 pfd = pango_font_description_new();
543 pango_font_description_set_absolute_size(pfd, PANGO_FONT_SIZE*PANGO_SCALE);
544 pango_font_description_set_family(pfd, PANGO_FONT_PREF);
545 context = pango_xft_get_context(dd, root_win);
546 pango_context_set_font_description(context, pfd);
547 pl = pango_layout_new(context);
549 pango_layout_set_single_paragraph_mode(pl, TRUE);
550 pango_layout_set_ellipsize(pl, PANGO_ELLIPSIZE_END);
552 text_y = (WINHEIGHT*PANGO_SCALE-7*PANGO_SCALE);
553 pango_font_description_free(pfd);
554 g_object_unref(context);
556 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
560 XpmCreatePixmapFromData(dd, root_win, icon_xpm, &generic_icon,
561 &generic_mask, NULL);
566 inst = RrInstanceNew(dd, scr_screen);
567 background = RrAppearanceNew(inst, 0);
568 focused_task = RrAppearanceNew(inst, 0);
569 unfocused_task = RrAppearanceNew(inst, 0);
570 normal_text = RrAppearanceNew(inst, 1);
572 background->surface.grad = RR_SURFACE_DIAGONAL;
573 background->surface.primary = RrColorNew(inst, 170, 170, 190);
574 background->surface.secondary = RrColorNew(inst, 100, 100, 160);
576 unfocused_task->surface.parent = background;
577 unfocused_task->surface.grad = RR_SURFACE_PARENTREL;
578 unfocused_task->surface.relief = RR_RELIEF_SUNKEN;
580 focused_task->surface.parent = background;
581 focused_task->surface.grad = RR_SURFACE_CROSS_DIAGONAL;
582 focused_task->surface.secondary = RrColorNew(inst, 70, 80, 110);
583 focused_task->surface.primary = RrColorNew(inst, 130, 160, 250);
584 focused_task->surface.relief = RR_RELIEF_RAISED;
586 normal_text->surface.grad = RR_SURFACE_PARENTREL;
587 normal_text->texture[0].type = RR_TEXTURE_TEXT;
588 normal_text->texture[0].data.text.font = RrFontOpenDefault(inst);
589 normal_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
591 iconified_text = RrAppearanceCopy(normal_text);
592 normal_text->texture[0].data.text.color = RrColorNew(inst, 20, 20, 40);
593 focused_text = RrAppearanceCopy(normal_text);
594 iconified_text->texture[0].data.text.shadow_offset_x = 2;
595 iconified_text->texture[0].data.text.shadow_offset_y = 2;
596 iconified_text->texture[0].data.text.shadow_alpha = 100;
597 iconified_text->texture[0].data.text.shadow_color = RrColorNew(inst, 0, 0, 0);
598 shaded_text = RrAppearanceCopy(normal_text);
599 shaded_text->texture[0].data.text.color = RrColorNew(inst, 50, 60, 90);
600 iconified_text->texture[0].data.text.color = RrColorNew(inst, 200, 200, 200);
601 focused_text->texture[0].data.text.color = RrColorNew(inst, 230, 230, 255);
605 static void gui_draw_vline(int x)
608 draw_line(x, 0, x, WINHEIGHT);
610 draw_line(x + 1, 0, x + 1, WINHEIGHT);
613 static void draw_box(int x, int width)
615 set_foreground(1); /* mid gray */
616 fill_rect(x + 3, 2, width - 2, WINHEIGHT - 4);
618 set_foreground(3); /* white */
619 draw_line(x + 3, WINHEIGHT - 2, x + width - 1, WINHEIGHT - 2);
620 draw_line(x + width - 1, 1, x + width - 1, WINHEIGHT - 2);
622 set_foreground(4); /* darkest gray */
623 draw_line(x + 3, 1, x + width - 1, 1);
624 draw_line(x + 3, 2, x + 3, WINHEIGHT - 3);
629 static void gui_draw_task(task *tk)
637 else if (tk->focused)
641 b = tk->focused ? focused_task : unfocused_task;
642 a->surface.parent = b;
643 a->surface.parentx = PADDING;
644 a->texture[0].data.text.string = tk->name;
645 b->surface.parentx = tk->pos_x;
646 RrPaintPixmap(b, tk->width, WINHEIGHT);
647 RrPaintPixmap(a, tk->width-2*PADDING, WINHEIGHT);
649 XCopyArea(dd, a->pixmap, b->pixmap, fore_gc, 0, 0, tk->width-2*PADDING, WINHEIGHT, PADDING, 0);
650 XCopyArea(dd, b->pixmap, tb.win, fore_gc, 0, 0, tk->width, WINHEIGHT, tk->pos_x, 0);
652 XCopyArea(dd, a->pixmap, tb.win, fore_gc, 0, 0, tk->width, WINHEIGHT, tk->pos_x, 0);
655 XFreePixmap(dd, a->pixmap);
656 XFreePixmap(dd, b->pixmap);
659 static void gui_draw_task(task * tk)
662 int taskw = tk->width;
676 set_foreground(0); /* mid gray */
677 fill_rect(x + 2, 0, taskw - 1, WINHEIGHT);
681 int text_x = x + TEXTPAD + TEXTPAD + (tk->icon ? ICONWIDTH : -1*ICONWIDTH/4);
682 #define SETCOL(x) col.color.red = cols[x].red;\
683 col.color.green = cols[x].green;\
684 col.color.blue = cols[x].blue;
687 pango_layout_set_width(pl, /*-1);*/(taskw - text_x + x) * PANGO_SCALE);
688 pango_layout_set_text(pl, tk->name, -1);
689 col.color.alpha = 0xffff;
693 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x+2)*PANGO_SCALE, text_y + 2);
695 } else if (tk->shaded) {
697 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x-2)*PANGO_SCALE, text_y - 2);
702 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), text_x*PANGO_SCALE, text_y);
706 /* check how many chars can fit */
707 len = strlen(tk->name);
709 XftTextExtentsUtf8(dd, xfs, tk->name, len, &ext);
710 if (ext.width < taskw - (text_x - x) - 2 || len <= 0)
715 col.color.alpha = 0xffff;
718 /* draw task's name dark (iconified) */
720 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y + 1, tk->name,
723 } else if (tk->shaded) {
724 /* draw task's name dark (shaded) */
726 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y - 1, tk->name,
734 /* draw task's name here */
735 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y, tk->name, len);
738 /* check how many chars can fit */
739 len = strlen(tk->name);
741 while (XTextWidth(xfs, tk->name, len) >= taskw - (text_x - x) - 2
746 /* draw task's name dark (iconified) */
748 XDrawString(dd, tb.win, fore_gc, text_x, text_y + 1, tk->name,
751 } else if (tk->shaded) {
752 /* draw task's name dark (shaded) */
754 XDrawString(dd, tb.win, fore_gc, text_x, text_y - 1, tk->name,
761 /* draw task's name here */
762 XDrawString(dd, tb.win, fore_gc, text_x, text_y, tk->name, len);
771 /* draw the task's icon */
772 XSetClipMask(dd, fore_gc, tk->mask);
773 XSetClipOrigin(dd, fore_gc, x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
774 XCopyArea(dd, tk->icon, tb.win, fore_gc, 0, 0, ICONWIDTH, ICONHEIGHT,
775 x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
776 XSetClipMask(dd, fore_gc, None);
780 static void draw_dot(int x, int y)
783 XDrawPoint(dd, tb.win, fore_gc, x, y);
785 XDrawPoint(dd, tb.win, fore_gc, x + 1, y + 1);
788 static void draw_grill(int x)
791 while (y < WINHEIGHT - 4) {
798 static void netwm_action(Window win, atom_t atom, Time time, long l)
800 XClientMessageEvent xev = {
801 .type = ClientMessage,
805 .message_type = atoms[atom]
808 if (atom == _NET_ACTIVE_WINDOW) {
810 xev.data.l[1] = time;
812 } else if (atom == _NET_CURRENT_DESKTOP) {
814 xev.data.l[1] = time;
815 } else if (atom == _NET_RESTACK_WINDOW) {
818 } else if (atom == _OB_FOCUS) {
820 xev.message_type = atoms[_NET_WM_STATE];
822 xev.data.l[1] = atoms[atom];
826 XSendEvent(dd, root_win, False, SubstructureNotifyMask
827 |SubstructureRedirectMask, (XEvent *)&xev);
832 static void switch_desk(int new_desk, Time time)
834 if (get_number_of_desktops() <= new_desk)
837 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
840 /* This doesn't work with pango yet */
841 static void pager_draw_button(int x, int num)
848 if (num == tb.my_desktop) {
849 /* current desktop */
850 draw_box(x, PAGER_BUTTON_WIDTH);
853 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
859 col.color.alpha = 0xffff;
860 col.color.red = cols[5].red;
861 col.color.green = cols[5].green;
862 col.color.blue = cols[5].blue;
863 XftDrawString8(xftdraw, &col, xfs,
864 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
868 XDrawString(dd, tb.win, fore_gc,
869 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
874 static void pager_draw(void)
876 int desks, i, x = GRILL_WIDTH;
878 desks = get_number_of_desktops();
880 for (i = 0; i < desks; i++) {
881 pager_draw_button(x, i);
884 x += PAGER_BUTTON_WIDTH;
892 static void gui_draw_taskbar(void)
900 pager_size = TEXTPAD;
904 width = WINWIDTH - (pager_size + GRILL_WIDTH);
906 #warning only rerender if width changed!
907 XFreePixmap(dd, RrPaintPixmap(background, WINWIDTH, WINHEIGHT));
908 XSetWindowBackgroundPixmap(dd, tb.win, background->pixmap);
909 XClearWindow(dd, tb.win);
911 if (tb.num_tasks == 0)
914 taskw = width / tb.num_tasks;
915 if (taskw > MAX_TASK_WIDTH)
916 taskw = MAX_TASK_WIDTH;
921 tk->width = taskw - 1;
930 if (x < (width + pager_size + 2)) {
934 fill_rect(x + 2, 0, WINWIDTH, WINHEIGHT);
938 gui_draw_vline(WINWIDTH - 8);
941 draw_grill(WINWIDTH - 6);
945 static task *find_task(Window win)
947 task *list = tb.task_list;
949 if (list->win == win)
956 static void del_task(Window win)
958 task *next, *prev = 0, *list = tb.task_list;
962 if (list->win == win) {
963 /* unlink and free this task */
965 if (list->icon_copied) {
966 XFreePixmap(dd, list->icon);
967 if (list->mask != None)
968 XFreePixmap(dd, list->mask);
984 static void move_taskbar(void)
991 x = TEXTPAD - WINWIDTH;
994 y = scr_height - WINHEIGHT;
996 XMoveWindow(dd, tb.win, x, y);
999 static void taskbar_read_clientlist(void)
1001 Window *win, focus_win = 0;
1002 int num, i, desk, new_desk = 0;
1004 desk = get_current_desktop();
1011 XResizeWindow(dd, rspanelwin, WINWIDTH, WINHEIGHT);
1014 if (desk != tb.my_desktop) {
1016 tb.my_desktop = desk;
1019 win = get_prop_data(root_win, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, &num);
1023 win = get_prop_data(root_win, atoms[_NET_CLIENT_LIST], XA_WINDOW, &num);
1027 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
1028 list = tb.task_list;
1030 list->focused = (focus_win == list->win);
1034 for (i = num - 1; i >= 0; i--)
1035 if (list->win == win[i] && !is_hidden(win[i]))
1037 del_task(list->win);
1043 /* add any new windows */
1044 for (i = 0; i < num; i++) {
1045 if (!find_task(win[i]))
1046 add_task(win[i], (win[i] == focus_win));
1052 static void handle_press(int x, int y, int button, Time time)
1057 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
1058 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
1061 /* clicked left grill */
1064 tb.at_top = 1 - tb.at_top;
1066 } else if (button == 4) {
1072 /* clicked right grill */
1073 else if (x + TEXTPAD > WINWIDTH) {
1074 if (tb.hidden && (button == 1 || button == 5)) {
1077 } else if (!tb.hidden && (button == 1 || button == 4)) {
1084 /* clicked on a task button */
1085 /* XXX Make this configureable */
1086 if (x > tk->pos_x && x < tk->pos_x + tk->width) {
1089 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
1093 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1095 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, ADD);
1098 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
1101 if (is_shaded(tk->win))
1102 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1104 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, ADD);
1107 if (is_shaded(tk->win))
1108 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
1110 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1114 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1116 netwm_action(tk->win, _OB_FOCUS, 0, 0);
1119 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1122 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1128 } /* clicked on the background */
1131 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
1134 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
1137 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
1152 static void handle_focusin(Window win)
1159 if (tk->win != win) {
1164 if (tk->win == win) {
1174 static void handle_propertynotify(Window win, Atom at)
1178 if (win == root_win) {
1179 /* XXX only redraw the task that got focused/unfocused when _NET_ACTIVE_WINDOW triggers this */
1180 if (at == atoms[_NET_CLIENT_LIST] || at == atoms[_NET_CURRENT_DESKTOP]
1181 || at == atoms[_NET_CLIENT_LIST_STACKING] || at == atoms[_NET_ACTIVE_WINDOW])
1183 taskbar_read_clientlist();
1189 /* XXX make this work when SKIP_TASKBAR is toggled too */
1190 tk = find_task(win);
1194 if (at == XA_WM_NAME || at == atoms[_NET_WM_NAME]) {
1195 /* window's title changed */
1197 newname = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
1198 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
1200 if (tk->name && !strcmp(newname, tk->name)) {
1209 } else if (at == atoms[_NET_WM_STATE]) {
1210 /* iconified state changed? */
1211 if (is_iconified(tk->win) != tk->iconified) {
1212 tk->iconified = !tk->iconified;
1215 /* shaded state changed? */
1216 if (is_shaded(tk->win) != tk->shaded) {
1217 tk->shaded = !tk->shaded;
1220 } else if (at == XA_WM_HINTS) {
1221 /* some windows set their WM_HINTS icon after mapping */
1222 //if (tk->icon == generic_icon) {
1223 get_task_hinticon(tk);
1226 } else if (at == atoms[_NET_WM_DESKTOP]) {
1227 if (find_desktop(win) != get_current_desktop())
1232 static void handle_error(Display * d, XErrorEvent * ev)
1240 main(int argc, char *argv[])
1245 dd = XOpenDisplay(NULL);
1248 scr_screen = DefaultScreen(dd);
1249 scr_depth = DefaultDepth(dd, scr_screen);
1250 scr_height = DisplayHeight(dd, scr_screen);
1251 scr_width = DisplayWidth(dd, scr_screen);
1252 root_win = RootWindow(dd, scr_screen);
1254 /* helps us catch windows closing/opening */
1255 XSelectInput(dd, root_win, PropertyChangeMask);
1257 XSetErrorHandler((XErrorHandler) handle_error);
1259 XInternAtoms(dd, atom_names, ATOM_COUNT, False, atoms);
1262 memset(&tb, 0, sizeof(tb));
1263 tb.win = gui_create_taskbar();
1266 while (XNextEvent(dd, &ev) == Success) {
1269 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
1272 del_task(ev.xdestroywindow.window);
1277 case PropertyNotify:
1278 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
1281 printf ("unknown evt type: %d\n", ev.type); */
1284 /* RrThemeFree(theme);
1285 * RrInstanceFree(inst);
1286 * XCloseDisplay (dd);