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;
94 /* we draw stuff to this guy then set him as a window
95 * background pixmap, yay no flickering! */
111 unsigned short red, green, blue;
113 {0xd75c, 0xd75c, 0xe75c}, /* 0. light gray */
114 {0xbefb, 0xbaea, 0xcefb}, /* 1. mid gray */
115 {0xaefb, 0xaaea, 0xfefb}, /* 2. dark gray */
116 {0xefbe, 0xefbe, 0xffbe}, /* 3. white */
117 {0x8617, 0x8207, 0x9617}, /* 4. darkest gray */
118 {0x0000, 0x0000, 0x1000}, /* 5. black */
121 #define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0].red) / 3)
123 unsigned long palette[PALETTE_COUNT];
125 char *atom_names[] = {
131 "_NET_WM_STATE_SKIP_TASKBAR",
132 "_NET_WM_STATE_SHADED",
133 "_NET_WM_STATE_BELOW",
134 "_NET_WM_STATE_HIDDEN",
136 "_NET_WM_WINDOW_TYPE",
137 "_NET_WM_WINDOW_TYPE_DOCK",
142 "_NET_CLIENT_LIST_STACKING",
143 "_NET_NUMBER_OF_DESKTOPS",
144 "_NET_CURRENT_DESKTOP",
148 "_NET_ACTIVE_WINDOW",
149 "_NET_RESTACK_WINDOW",
158 _NET_WM_STATE_SKIP_TASKBAR,
159 _NET_WM_STATE_SHADED,
161 _NET_WM_STATE_HIDDEN,
164 _NET_WM_WINDOW_TYPE_DOCK,
168 _NET_CLIENT_LIST_STACKING,
169 _NET_NUMBER_OF_DESKTOPS,
170 _NET_CURRENT_DESKTOP,
180 Atom atoms[ATOM_COUNT];
188 static void *get_prop_data(Window win, Atom prop, Atom type, int *items)
192 unsigned long items_ret;
193 unsigned long after_ret;
194 unsigned char *prop_data;
198 XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False, type, &type_ret,
199 &format_ret, &items_ret, &after_ret, &prop_data);
206 static void set_foreground(int index)
208 XSetForeground(dd, fore_gc, palette[index]);
211 static void draw_line(int x, int y, int a, int b)
213 XDrawLine(dd, tb.win, fore_gc, x, y, a, b);
216 static void fill_rect(int x, int y, int a, int b)
218 XFillRectangle(dd, tb.win, fore_gc, x, y, a, b);
221 static void scale_icon(task * tk)
224 unsigned int w, h, d, bw;
225 Pixmap pix, mk = None;
229 XGetGeometry(dd, tk->icon, &pix, &x, &y, &w, &h, &bw, &d);
230 pix = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, scr_depth);
232 if (tk->mask != None) {
233 mk = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, 1);
234 gcv.subwindow_mode = IncludeInferiors;
235 gcv.graphics_exposures = False;
236 mgc = XCreateGC(dd, mk, GCGraphicsExposures | GCSubwindowMode, &gcv);
241 /* this is my simple & dirty scaling routine */
242 for (y = ICONHEIGHT - 1; y >= 0; y--) {
243 yy = (y * h) / ICONHEIGHT;
244 for (x = ICONWIDTH - 1; x >= 0; x--) {
245 xx = (x * w) / ICONWIDTH;
247 XCopyPlane(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y, 1);
249 XCopyArea(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y);
251 XCopyArea(dd, tk->mask, mk, mgc, xx, yy, 1, 1, x, y);
263 static void get_task_hinticon(task * tk)
267 if (tk->icon != None && tk->icon != generic_icon)
268 if (tk->icon_copied) {
269 XFreePixmap(dd, tk->icon);
270 if (tk->mask != None && tk->mask != generic_mask)
271 XFreePixmap(dd, tk->mask);
277 hin = (XWMHints *)get_prop_data(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
279 if ((hin->flags & IconPixmapHint)) {
280 if ((hin->flags & IconMaskHint)) {
281 tk->mask = hin->icon_mask;
284 tk->icon = hin->icon_pixmap;
291 if (tk->icon == None) {
292 tk->icon = generic_icon;
293 if (tk->mask != None)
294 XFreePixmap(dd, tk->mask);
295 tk->mask = generic_mask;
299 static void get_task_kdeicon(task * tk)
303 data = get_prop_data(tk->win, atoms[KWM_WIN_ICON], atoms[KWM_WIN_ICON], 0);
311 static int generic_get_int(Window win, Atom at)
316 data = get_prop_data(win, at, XA_CARDINAL, 0);
324 static int find_desktop(Window win)
326 return generic_get_int(win, atoms[_NET_WM_DESKTOP]);
329 static int find_state(Window win, atom_t atom)
335 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
338 if ((data[num]) == atoms[atom])
346 /* this naming is very confusing :) */
347 static int is_hidden(Window win)
349 return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
352 static int is_iconified(Window win)
354 return find_state(win, _NET_WM_STATE_HIDDEN);
357 static int is_shaded(Window win)
359 return find_state(win, _NET_WM_STATE_SHADED);
362 static int get_current_desktop(void)
364 return generic_get_int(root_win, atoms[_NET_CURRENT_DESKTOP]);
367 static int get_number_of_desktops(void)
369 return generic_get_int(root_win, atoms[_NET_NUMBER_OF_DESKTOPS]);
372 static void add_task(Window win, int focus)
380 /* is this window on a different desktop? */
381 desk = find_desktop(win);
382 if ((desk != 0xffffffff && tb.my_desktop != desk) || is_hidden(win))
385 tk = calloc(1, sizeof(task));
388 tk->name = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
389 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
390 // tk->name = get_prop_data(win, XA_WM_NAME, XA_STRING, 0);
391 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
392 tk->iconified = is_iconified(win);
393 tk->shaded = is_shaded(win);
395 get_task_kdeicon(tk);
396 if (tk->icon == None)
397 get_task_hinticon(tk);
399 XSelectInput(dd, win, PropertyChangeMask | FocusChangeMask
400 | StructureNotifyMask);
402 /* now append it to our linked list */
419 static void set_prop(Window win, Atom at, Atom type, long val)
421 XChangeProperty(dd, win, at, type, 32,
422 PropModeReplace, (unsigned char *)&val, 1);
425 static Window gui_create_taskbar(void)
429 XSizeHints size_hints;
431 XSetWindowAttributes att;
434 att.background_pixel = palette[0];
435 att.event_mask = ButtonPressMask | ExposureMask;
437 win = rspanelwin = XCreateWindow(/* display */ dd,
438 /* parent */ root_win,
440 /* y */ scr_height - WINHEIGHT,
441 /* XXX Maybe just use scr_width here? */
442 /* width */ WINWIDTH,
443 /* height */ WINHEIGHT,
445 /* depth */ CopyFromParent,
446 /* class */ InputOutput,
447 /* visual */ CopyFromParent,
448 /*value mask*/ CWBackPixel | CWEventMask,
451 /* reside on ALL desktops */
452 set_prop(win, atoms[_NET_WM_DESKTOP], XA_CARDINAL, 0xFFFFFFFF);
453 set_prop(win, atoms[_NET_WM_WINDOW_TYPE], XA_ATOM,
454 atoms[_NET_WM_WINDOW_TYPE_DOCK]);
455 set_prop(win, atoms[_NET_WM_STATE], XA_ATOM, atoms[_NET_WM_STATE_BELOW]);
456 /* use old gnome hint since sawfish doesn't support _NET_WM_STRUT */
457 set_prop(win, atoms[_WIN_HINTS], XA_CARDINAL,
458 WIN_HINTS_SKIP_FOCUS | WIN_HINTS_SKIP_WINLIST |
459 WIN_HINTS_SKIP_TASKBAR | WIN_HINTS_DO_NOT_COVER);
460 XChangeProperty(dd, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
461 (unsigned char *)"rspanel", 7);
463 /* borderless motif hint */
464 bzero(&mwm, sizeof(mwm));
465 mwm.flags = MWM_HINTS_DECORATIONS;
466 XChangeProperty(dd, win, atoms[_MOTIF_WM_HINTS], atoms[_MOTIF_WM_HINTS], 32,
467 PropModeReplace, (unsigned char *)&mwm,
468 sizeof(MWMHints) / 4);
470 /* make sure the WM obays our window position */
471 size_hints.flags = PPosition;
472 /*XSetWMNormalHints (dd, win, &size_hints); */
473 XChangeProperty(dd, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32,
474 PropModeReplace, (unsigned char *)&size_hints,
475 sizeof(XSizeHints) / 4);
477 /* make our window unfocusable */
478 wmhints.flags = InputHint;
479 wmhints.input = False;
480 /*XSetWMHints (dd, win, &wmhints); */
481 XChangeProperty(dd, win, XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
482 (unsigned char *)&wmhints, sizeof(XWMHints) / 4);
484 xclhints.res_name = "rspanel";
485 xclhints.res_class = "RSPanel";
486 XSetClassHint(dd, win, &xclhints);
491 xftdraw = XftDrawCreate(dd, win, DefaultVisual(dd, scr_screen),
492 DefaultColormap(dd, scr_screen));
501 static void gui_init(void)
510 PangoContext *context;
511 PangoFontDescription *pfd;
516 xcl.red = cols[i].red;
517 xcl.green = cols[i].green;
518 xcl.blue = cols[i].blue;
519 XAllocColor(dd, DefaultColormap(dd, scr_screen), &xcl);
520 palette[i] = xcl.pixel;
522 } while (i < PALETTE_COUNT);
526 xfs = XftFontOpenName(dd, scr_screen, XFT_FONT);
528 fontname = FONT_NAME;
530 xfs = XLoadQueryFont(dd, fontname);
535 gcv.graphics_exposures = False;
538 text_y = xfs->ascent + ((WINHEIGHT - (xfs->ascent + xfs->descent)) / 2);
539 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
541 text_y = xfs->ascent + ((WINHEIGHT - xfs->ascent) / 2);
543 fore_gc = XCreateGC(dd, root_win, GCFont | GCGraphicsExposures, &gcv);
546 pfd = pango_font_description_new();
547 pango_font_description_set_absolute_size(pfd, PANGO_FONT_SIZE*PANGO_SCALE);
548 pango_font_description_set_family(pfd, PANGO_FONT_PREF);
549 context = pango_xft_get_context(dd, root_win);
550 pango_context_set_font_description(context, pfd);
551 pl = pango_layout_new(context);
553 pango_layout_set_single_paragraph_mode(pl, TRUE);
554 pango_layout_set_ellipsize(pl, PANGO_ELLIPSIZE_END);
556 text_y = (WINHEIGHT*PANGO_SCALE-7*PANGO_SCALE);
557 pango_font_description_free(pfd);
558 g_object_unref(context);
560 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
564 XpmCreatePixmapFromData(dd, root_win, icon_xpm, &generic_icon,
565 &generic_mask, NULL);
570 inst = RrInstanceNew(dd, scr_screen);
571 background = RrAppearanceNew(inst, 0);
572 focused_task = RrAppearanceNew(inst, 0);
573 unfocused_task = RrAppearanceNew(inst, 0);
574 normal_text = RrAppearanceNew(inst, 1);
576 background->surface.grad = RR_SURFACE_DIAGONAL;
577 background->surface.primary = RrColorNew(inst, 170, 170, 190);
578 background->surface.secondary = RrColorNew(inst, 100, 100, 160);
580 unfocused_task->surface.parent = background;
581 unfocused_task->surface.grad = RR_SURFACE_PARENTREL;
582 unfocused_task->surface.relief = RR_RELIEF_SUNKEN;
584 focused_task->surface.parent = background;
585 focused_task->surface.grad = RR_SURFACE_CROSS_DIAGONAL;
586 focused_task->surface.secondary = RrColorNew(inst, 70, 80, 110);
587 focused_task->surface.primary = RrColorNew(inst, 130, 160, 250);
588 focused_task->surface.relief = RR_RELIEF_RAISED;
590 normal_text->surface.grad = RR_SURFACE_PARENTREL;
591 normal_text->texture[0].type = RR_TEXTURE_TEXT;
592 normal_text->texture[0].data.text.font = RrFontOpenDefault(inst);
593 normal_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
595 iconified_text = RrAppearanceCopy(normal_text);
596 normal_text->texture[0].data.text.color = RrColorNew(inst, 20, 20, 40);
597 focused_text = RrAppearanceCopy(normal_text);
598 iconified_text->texture[0].data.text.shadow_offset_x = 2;
599 iconified_text->texture[0].data.text.shadow_offset_y = 2;
600 iconified_text->texture[0].data.text.shadow_alpha = 100;
601 iconified_text->texture[0].data.text.shadow_color = RrColorNew(inst, 0, 0, 0);
602 shaded_text = RrAppearanceCopy(normal_text);
603 shaded_text->texture[0].data.text.color = RrColorNew(inst, 50, 60, 90);
604 iconified_text->texture[0].data.text.color = RrColorNew(inst, 200, 200, 200);
605 focused_text->texture[0].data.text.color = RrColorNew(inst, 230, 230, 255);
609 static void gui_draw_vline(int x)
612 draw_line(x, 0, x, WINHEIGHT);
614 draw_line(x + 1, 0, x + 1, WINHEIGHT);
617 static void draw_box(int x, int width)
619 set_foreground(1); /* mid gray */
620 fill_rect(x + 3, 2, width - 2, WINHEIGHT - 4);
622 set_foreground(3); /* white */
623 draw_line(x + 3, WINHEIGHT - 2, x + width - 1, WINHEIGHT - 2);
624 draw_line(x + width - 1, 1, x + width - 1, WINHEIGHT - 2);
626 set_foreground(4); /* darkest gray */
627 draw_line(x + 3, 1, x + width - 1, 1);
628 draw_line(x + 3, 2, x + 3, WINHEIGHT - 3);
633 static void gui_draw_task(task *tk, int redraw)
641 else if (tk->focused)
645 b = tk->focused ? focused_task : unfocused_task;
646 a->surface.parent = b;
647 a->surface.parentx = PADDING;
648 a->texture[0].data.text.string = tk->name;
649 b->surface.parentx = tk->pos_x;
650 RrPaintPixmap(b, tk->width, WINHEIGHT);
651 RrPaintPixmap(a, tk->width-2*PADDING, WINHEIGHT);
653 XCopyArea(dd, a->pixmap, b->pixmap, fore_gc, 0, 0, tk->width-2*PADDING, WINHEIGHT, PADDING, 0);
654 XCopyArea(dd, b->pixmap, bgpixmap, fore_gc, 0, 0, tk->width, WINHEIGHT, tk->pos_x, 0);
656 XCopyArea(dd, a->pixmap, bgpixmap, fore_gc, 0, 0, tk->width, WINHEIGHT, tk->pos_x, 0);
659 XFreePixmap(dd, a->pixmap);
660 XFreePixmap(dd, b->pixmap);
662 XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
663 XClearWindow(dd, tb.win);
667 static void gui_draw_task(task * tk, int redraw)
670 int taskw = tk->width;
684 set_foreground(0); /* mid gray */
685 fill_rect(x + 2, 0, taskw - 1, WINHEIGHT);
689 int text_x = x + TEXTPAD + TEXTPAD + (tk->icon ? ICONWIDTH : -1*ICONWIDTH/4);
690 #define SETCOL(x) col.color.red = cols[x].red;\
691 col.color.green = cols[x].green;\
692 col.color.blue = cols[x].blue;
695 pango_layout_set_width(pl, /*-1);*/(taskw - text_x + x) * PANGO_SCALE);
696 pango_layout_set_text(pl, tk->name, -1);
697 col.color.alpha = 0xffff;
701 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x+2)*PANGO_SCALE, text_y + 2);
703 } else if (tk->shaded) {
705 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x-2)*PANGO_SCALE, text_y - 2);
710 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), text_x*PANGO_SCALE, text_y);
714 /* check how many chars can fit */
715 len = strlen(tk->name);
717 XftTextExtentsUtf8(dd, xfs, tk->name, len, &ext);
718 if (ext.width < taskw - (text_x - x) - 2 || len <= 0)
723 col.color.alpha = 0xffff;
726 /* draw task's name dark (iconified) */
728 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y + 1, tk->name,
731 } else if (tk->shaded) {
732 /* draw task's name dark (shaded) */
734 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y - 1, tk->name,
742 /* draw task's name here */
743 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y, tk->name, len);
746 /* check how many chars can fit */
747 len = strlen(tk->name);
749 while (XTextWidth(xfs, tk->name, len) >= taskw - (text_x - x) - 2
754 /* draw task's name dark (iconified) */
756 XDrawString(dd, tb.win, fore_gc, text_x, text_y + 1, tk->name,
759 } else if (tk->shaded) {
760 /* draw task's name dark (shaded) */
762 XDrawString(dd, tb.win, fore_gc, text_x, text_y - 1, tk->name,
769 /* draw task's name here */
770 XDrawString(dd, tb.win, fore_gc, text_x, text_y, tk->name, len);
779 /* draw the task's icon */
780 XSetClipMask(dd, fore_gc, tk->mask);
781 XSetClipOrigin(dd, fore_gc, x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
782 XCopyArea(dd, tk->icon, tb.win, fore_gc, 0, 0, ICONWIDTH, ICONHEIGHT,
783 x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
784 XSetClipMask(dd, fore_gc, None);
788 static void draw_dot(int x, int y)
791 XDrawPoint(dd, tb.win, fore_gc, x, y);
793 XDrawPoint(dd, tb.win, fore_gc, x + 1, y + 1);
796 static void draw_grill(int x)
799 while (y < WINHEIGHT - 4) {
806 static void netwm_action(Window win, atom_t atom, Time time, long l)
808 XClientMessageEvent xev = {
809 .type = ClientMessage,
813 .message_type = atoms[atom]
816 if (atom == _NET_ACTIVE_WINDOW) {
818 xev.data.l[1] = time;
820 } else if (atom == _NET_CURRENT_DESKTOP) {
822 xev.data.l[1] = time;
823 } else if (atom == _NET_RESTACK_WINDOW) {
826 } else if (atom == _OB_FOCUS) {
828 xev.message_type = atoms[_NET_WM_STATE];
830 xev.data.l[1] = atoms[atom];
834 XSendEvent(dd, root_win, False, SubstructureNotifyMask
835 |SubstructureRedirectMask, (XEvent *)&xev);
840 static void switch_desk(int new_desk, Time time)
842 if (get_number_of_desktops() <= new_desk)
845 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
848 /* This doesn't work with pango yet */
849 static void pager_draw_button(int x, int num)
856 if (num == tb.my_desktop) {
857 /* current desktop */
858 draw_box(x, PAGER_BUTTON_WIDTH);
861 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
867 col.color.alpha = 0xffff;
868 col.color.red = cols[5].red;
869 col.color.green = cols[5].green;
870 col.color.blue = cols[5].blue;
871 XftDrawString8(xftdraw, &col, xfs,
872 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
876 XDrawString(dd, tb.win, fore_gc,
877 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
882 static void pager_draw(void)
884 int desks, i, x = GRILL_WIDTH;
886 desks = get_number_of_desktops();
888 for (i = 0; i < desks; i++) {
889 pager_draw_button(x, i);
892 x += PAGER_BUTTON_WIDTH;
900 static void gui_draw_taskbar(void)
908 pager_size = TEXTPAD;
912 width = WINWIDTH - (pager_size + GRILL_WIDTH);
914 #warning only rerender if width changed!
915 if (bgpixmap) XFreePixmap(dd, bgpixmap);
916 bgpixmap = XCreatePixmap(dd, root_win, WINWIDTH, WINHEIGHT, RrDepth(inst));
918 XFreePixmap(dd, RrPaintPixmap(background, WINWIDTH, WINHEIGHT));
919 XCopyArea(dd, background->pixmap, bgpixmap, fore_gc, 0, 0, WINWIDTH, WINHEIGHT, 0, 0);
921 if (tb.num_tasks == 0)
924 taskw = width / tb.num_tasks;
925 if (taskw > MAX_TASK_WIDTH)
926 taskw = MAX_TASK_WIDTH;
931 tk->width = taskw - 1;
932 gui_draw_task(tk, FALSE);
939 XSetWindowBackgroundPixmap(dd, tb.win, bgpixmap);
940 XClearWindow(dd, tb.win);
942 if (x < (width + pager_size + 2)) {
946 fill_rect(x + 2, 0, WINWIDTH, WINHEIGHT);
950 gui_draw_vline(WINWIDTH - 8);
953 draw_grill(WINWIDTH - 6);
957 static task *find_task(Window win)
959 task *list = tb.task_list;
961 if (list->win == win)
968 static void del_task(Window win)
970 task *next, *prev = 0, *list = tb.task_list;
974 if (list->win == win) {
975 /* unlink and free this task */
977 if (list->icon_copied) {
978 XFreePixmap(dd, list->icon);
979 if (list->mask != None)
980 XFreePixmap(dd, list->mask);
996 static void move_taskbar(void)
1003 x = TEXTPAD - WINWIDTH;
1006 y = scr_height - WINHEIGHT;
1008 XMoveWindow(dd, tb.win, x, y);
1011 static void taskbar_read_clientlist(void)
1013 Window *win, focus_win = 0;
1014 int num, i, desk, new_desk = 0;
1016 desk = get_current_desktop();
1023 XResizeWindow(dd, rspanelwin, WINWIDTH, WINHEIGHT);
1026 if (desk != tb.my_desktop) {
1028 tb.my_desktop = desk;
1031 win = get_prop_data(root_win, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, &num);
1035 win = get_prop_data(root_win, atoms[_NET_CLIENT_LIST], XA_WINDOW, &num);
1039 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
1040 list = tb.task_list;
1042 list->focused = (focus_win == list->win);
1046 for (i = num - 1; i >= 0; i--)
1047 if (list->win == win[i] && !is_hidden(win[i]))
1049 del_task(list->win);
1055 /* add any new windows */
1056 for (i = 0; i < num; i++) {
1057 if (!find_task(win[i]))
1058 add_task(win[i], (win[i] == focus_win));
1064 static void handle_press(int x, int y, int button, Time time)
1069 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
1070 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
1073 /* clicked left grill */
1076 tb.at_top = 1 - tb.at_top;
1078 } else if (button == 4) {
1084 /* clicked right grill */
1085 else if (x + TEXTPAD > WINWIDTH) {
1086 if (tb.hidden && (button == 1 || button == 5)) {
1089 } else if (!tb.hidden && (button == 1 || button == 4)) {
1096 /* clicked on a task button */
1097 /* XXX Make this configureable */
1098 if (x > tk->pos_x && x < tk->pos_x + tk->width) {
1101 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
1105 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1107 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, ADD);
1110 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
1113 if (is_shaded(tk->win))
1114 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1116 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, ADD);
1119 if (is_shaded(tk->win))
1120 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
1122 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1126 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1128 netwm_action(tk->win, _OB_FOCUS, 0, 0);
1131 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1134 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1140 } /* clicked on the background */
1143 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
1146 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
1149 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
1164 static void handle_focusin(Window win)
1171 if (tk->win != win) {
1176 if (tk->win == win) {
1186 static void handle_propertynotify(Window win, Atom at)
1190 if (win == root_win) {
1191 /* XXX only redraw the task that got focused/unfocused when _NET_ACTIVE_WINDOW triggers this */
1192 if (at == atoms[_NET_CLIENT_LIST] || at == atoms[_NET_CURRENT_DESKTOP]
1193 || at == atoms[_NET_CLIENT_LIST_STACKING] || at == atoms[_NET_ACTIVE_WINDOW])
1195 taskbar_read_clientlist();
1201 /* XXX make this work when SKIP_TASKBAR is toggled too */
1202 tk = find_task(win);
1206 if (at == XA_WM_NAME || at == atoms[_NET_WM_NAME]) {
1207 /* window's title changed */
1209 newname = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
1210 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
1212 if (tk->name && !strcmp(newname, tk->name)) {
1220 gui_draw_task(tk, TRUE);
1221 } else if (at == atoms[_NET_WM_STATE]) {
1222 /* iconified state changed? */
1223 if (is_iconified(tk->win) != tk->iconified) {
1224 tk->iconified = !tk->iconified;
1225 gui_draw_task(tk, TRUE);
1227 /* shaded state changed? */
1228 if (is_shaded(tk->win) != tk->shaded) {
1229 tk->shaded = !tk->shaded;
1230 gui_draw_task(tk, TRUE);
1232 } else if (at == XA_WM_HINTS) {
1233 /* some windows set their WM_HINTS icon after mapping */
1234 //if (tk->icon == generic_icon) {
1235 get_task_hinticon(tk);
1236 gui_draw_task(tk, TRUE);
1238 } else if (at == atoms[_NET_WM_DESKTOP]) {
1239 if (find_desktop(win) != get_current_desktop())
1244 static void handle_error(Display * d, XErrorEvent * ev)
1252 main(int argc, char *argv[])
1257 dd = XOpenDisplay(NULL);
1260 scr_screen = DefaultScreen(dd);
1261 scr_depth = DefaultDepth(dd, scr_screen);
1262 scr_height = DisplayHeight(dd, scr_screen);
1263 scr_width = DisplayWidth(dd, scr_screen);
1264 root_win = RootWindow(dd, scr_screen);
1266 /* helps us catch windows closing/opening */
1267 XSelectInput(dd, root_win, PropertyChangeMask);
1269 XSetErrorHandler((XErrorHandler) handle_error);
1271 XInternAtoms(dd, atom_names, ATOM_COUNT, False, atoms);
1274 memset(&tb, 0, sizeof(tb));
1275 tb.win = gui_create_taskbar();
1278 while (XNextEvent(dd, &ev) == Success) {
1281 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
1284 del_task(ev.xdestroywindow.window);
1289 case PropertyNotify:
1290 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
1293 printf ("unknown evt type: %d\n", ev.type); */
1296 /* RrThemeFree(theme);
1297 * RrInstanceFree(inst);
1298 * XCloseDisplay (dd);