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>
90 unsigned short red, green, blue;
92 {0xd75c, 0xd75c, 0xe75c}, /* 0. light gray */
93 {0xbefb, 0xbaea, 0xcefb}, /* 1. mid gray */
94 {0xaefb, 0xaaea, 0xfefb}, /* 2. dark gray */
95 {0xefbe, 0xefbe, 0xffbe}, /* 3. white */
96 {0x8617, 0x8207, 0x9617}, /* 4. darkest gray */
97 {0x0000, 0x0000, 0x1000}, /* 5. black */
100 #define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0].red) / 3)
102 unsigned long palette[PALETTE_COUNT];
104 char *atom_names[] = {
110 "_NET_WM_STATE_SKIP_TASKBAR",
111 "_NET_WM_STATE_SHADED",
112 "_NET_WM_STATE_BELOW",
113 "_NET_WM_STATE_HIDDEN",
115 "_NET_WM_WINDOW_TYPE",
116 "_NET_WM_WINDOW_TYPE_DOCK",
121 "_NET_CLIENT_LIST_STACKING",
122 "_NET_NUMBER_OF_DESKTOPS",
123 "_NET_CURRENT_DESKTOP",
127 "_NET_ACTIVE_WINDOW",
128 "_NET_RESTACK_WINDOW",
131 #define ATOM_COUNT (sizeof (atom_names) / sizeof (atom_names[0]))
133 Atom atoms[ATOM_COUNT];
140 _NET_WM_STATE_SKIP_TASKBAR,
141 _NET_WM_STATE_SHADED,
143 _NET_WM_STATE_HIDDEN,
146 _NET_WM_WINDOW_TYPE_DOCK,
150 _NET_CLIENT_LIST_STACKING,
151 _NET_NUMBER_OF_DESKTOPS,
152 _NET_CURRENT_DESKTOP,
166 void *get_prop_data(Window win, Atom prop, Atom type, int *items)
170 unsigned long items_ret;
171 unsigned long after_ret;
172 unsigned char *prop_data;
176 XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False, type, &type_ret,
177 &format_ret, &items_ret, &after_ret, &prop_data);
184 void set_foreground(int index)
186 XSetForeground(dd, fore_gc, palette[index]);
189 void draw_line(int x, int y, int a, int b)
191 XDrawLine(dd, tb.win, fore_gc, x, y, a, b);
194 void fill_rect(int x, int y, int a, int b)
196 XFillRectangle(dd, tb.win, fore_gc, x, y, a, b);
199 void scale_icon(task * tk)
202 unsigned int w, h, d, bw;
203 Pixmap pix, mk = None;
207 XGetGeometry(dd, tk->icon, &pix, &x, &y, &w, &h, &bw, &d);
208 pix = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, scr_depth);
210 if (tk->mask != None) {
211 mk = XCreatePixmap(dd, tk->win, ICONWIDTH, ICONHEIGHT, 1);
212 gcv.subwindow_mode = IncludeInferiors;
213 gcv.graphics_exposures = False;
214 mgc = XCreateGC(dd, mk, GCGraphicsExposures | GCSubwindowMode, &gcv);
219 /* this is my simple & dirty scaling routine */
220 for (y = ICONHEIGHT - 1; y >= 0; y--) {
221 yy = (y * h) / ICONHEIGHT;
222 for (x = ICONWIDTH - 1; x >= 0; x--) {
223 xx = (x * w) / ICONWIDTH;
225 XCopyPlane(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y, 1);
227 XCopyArea(dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y);
229 XCopyArea(dd, tk->mask, mk, mgc, xx, yy, 1, 1, x, y);
241 void get_task_hinticon(task * tk)
245 if (tk->icon != None && tk->icon != generic_icon)
246 if (tk->icon_copied) {
247 XFreePixmap(dd, tk->icon);
248 if (tk->mask != None && tk->mask != generic_mask)
249 XFreePixmap(dd, tk->mask);
255 hin = (XWMHints *)get_prop_data(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
257 if ((hin->flags & IconPixmapHint)) {
258 if ((hin->flags & IconMaskHint)) {
259 tk->mask = hin->icon_mask;
262 tk->icon = hin->icon_pixmap;
269 if (tk->icon == None) {
270 tk->icon = generic_icon;
271 if (tk->mask != None)
272 XFreePixmap(dd, tk->mask);
273 tk->mask = generic_mask;
277 void get_task_kdeicon(task * tk)
281 data = get_prop_data(tk->win, atoms[KWM_WIN_ICON], atoms[KWM_WIN_ICON], 0);
289 int generic_get_int(Window win, Atom at)
294 data = get_prop_data(win, at, XA_CARDINAL, 0);
302 int find_desktop(Window win)
304 return generic_get_int(win, atoms[_NET_WM_DESKTOP]);
307 int is_hidden(Window win)
313 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
317 if ((data[num]) == atoms[_NET_WM_STATE_SKIP_TASKBAR])
325 int is_iconified(Window win)
331 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
335 if ((data[num]) == atoms[_NET_WM_STATE_HIDDEN])
343 int is_shaded(Window win)
349 data = get_prop_data(win, atoms[_NET_WM_STATE], XA_ATOM, &num);
353 if ((data[num]) == atoms[_NET_WM_STATE_SHADED])
361 int get_current_desktop(void)
363 return generic_get_int(root_win, atoms[_NET_CURRENT_DESKTOP]);
366 int get_number_of_desktops(void)
368 return generic_get_int(root_win, atoms[_NET_NUMBER_OF_DESKTOPS]);
371 void add_task(Window win, int focus)
379 /* is this window on a different desktop? */
380 desk = find_desktop(win);
381 if ((desk != 0xffffffff && tb.my_desktop != desk) || is_hidden(win))
384 tk = calloc(1, sizeof(task));
387 tk->name = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
388 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
389 // tk->name = get_prop_data(win, XA_WM_NAME, XA_STRING, 0);
390 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
391 tk->iconified = is_iconified(win);
392 tk->shaded = is_shaded(win);
394 get_task_kdeicon(tk);
395 if (tk->icon == None)
396 get_task_hinticon(tk);
398 XSelectInput(dd, win, PropertyChangeMask | FocusChangeMask
399 | StructureNotifyMask);
401 /* now append it to our linked list */
423 void set_prop(Window win, Atom at, Atom type, long val)
425 XChangeProperty(dd, win, at, type, 32,
426 PropModeReplace, (unsigned char *)&val, 1);
429 Window gui_create_taskbar(void)
433 XSizeHints size_hints;
435 XSetWindowAttributes att;
438 att.background_pixel = palette[0];
439 att.event_mask = ButtonPressMask | ExposureMask;
441 win = rspanelwin = XCreateWindow(/* display */ dd,
442 /* parent */ root_win,
444 /* y */ scr_height - WINHEIGHT,
445 /* width */ WINWIDTH,
446 /* height */ WINHEIGHT,
448 /* depth */ CopyFromParent,
449 /* class */ InputOutput,
450 /* visual */ CopyFromParent,
451 /*value mask*/ CWBackPixel | CWEventMask,
454 /* reside on ALL desktops */
455 set_prop(win, atoms[_NET_WM_DESKTOP], XA_CARDINAL, 0xFFFFFFFF);
456 set_prop(win, atoms[_NET_WM_WINDOW_TYPE], XA_ATOM,
457 atoms[_NET_WM_WINDOW_TYPE_DOCK]);
458 set_prop(win, atoms[_NET_WM_STATE], XA_ATOM, atoms[_NET_WM_STATE_BELOW]);
459 /* use old gnome hint since sawfish doesn't support _NET_WM_STRUT */
460 set_prop(win, atoms[_WIN_HINTS], XA_CARDINAL,
461 WIN_HINTS_SKIP_FOCUS | WIN_HINTS_SKIP_WINLIST |
462 WIN_HINTS_SKIP_TASKBAR | WIN_HINTS_DO_NOT_COVER);
463 XChangeProperty(dd, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
464 (unsigned char *)"rspanel", 7);
466 /* borderless motif hint */
467 bzero(&mwm, sizeof(mwm));
468 mwm.flags = MWM_HINTS_DECORATIONS;
469 XChangeProperty(dd, win, atoms[_MOTIF_WM_HINTS], atoms[_MOTIF_WM_HINTS], 32,
470 PropModeReplace, (unsigned char *)&mwm,
471 sizeof(MWMHints) / 4);
473 /* make sure the WM obays our window position */
474 size_hints.flags = PPosition;
475 /*XSetWMNormalHints (dd, win, &size_hints); */
476 XChangeProperty(dd, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32,
477 PropModeReplace, (unsigned char *)&size_hints,
478 sizeof(XSizeHints) / 4);
480 /* make our window unfocusable */
481 wmhints.flags = InputHint;
482 wmhints.input = False;
483 /*XSetWMHints (dd, win, &wmhints); */
484 XChangeProperty(dd, win, XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
485 (unsigned char *)&wmhints, sizeof(XWMHints) / 4);
487 xclhints.res_name = "rspanel";
488 xclhints.res_class = "RSPanel";
489 XSetClassHint(dd, win, &xclhints);
494 xftdraw = XftDrawCreate(dd, win, DefaultVisual(dd, scr_screen),
495 DefaultColormap(dd, scr_screen));
511 PangoContext *context;
512 PangoFontDescription *pfd;
517 xcl.red = cols[i].red;
518 xcl.green = cols[i].green;
519 xcl.blue = cols[i].blue;
520 XAllocColor(dd, DefaultColormap(dd, scr_screen), &xcl);
521 palette[i] = xcl.pixel;
523 } while (i < PALETTE_COUNT);
527 xfs = XftFontOpenName(dd, scr_screen, XFT_FONT);
529 fontname = FONT_NAME;
531 xfs = XLoadQueryFont(dd, fontname);
536 gcv.graphics_exposures = False;
539 text_y = xfs->ascent + ((WINHEIGHT - (xfs->ascent + xfs->descent)) / 2);
540 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
542 text_y = xfs->ascent + ((WINHEIGHT - xfs->ascent) / 2);
544 fore_gc = XCreateGC(dd, root_win, GCFont | GCGraphicsExposures, &gcv);
547 pfd = pango_font_description_new();
548 pango_font_description_set_absolute_size(pfd, PANGO_FONT_SIZE*PANGO_SCALE);
549 pango_font_description_set_family(pfd, PANGO_FONT_PREF);
550 context = pango_xft_get_context(dd, root_win);
551 pango_context_set_font_description(context, pfd);
552 pl = pango_layout_new(context);
554 pango_layout_set_single_paragraph_mode(pl, TRUE);
555 pango_layout_set_ellipsize(pl, PANGO_ELLIPSIZE_END);
557 text_y = (WINHEIGHT*PANGO_SCALE-7*PANGO_SCALE);
558 pango_font_description_free(pfd);
559 g_object_unref(context);
561 fore_gc = XCreateGC(dd, root_win, GCGraphicsExposures, &gcv);
565 XpmCreatePixmapFromData(dd, root_win, icon_xpm, &generic_icon,
566 &generic_mask, NULL);
572 void gui_draw_vline(int x)
575 draw_line(x, 0, x, WINHEIGHT);
577 draw_line(x + 1, 0, x + 1, WINHEIGHT);
580 void draw_box(int x, int width)
582 set_foreground(1); /* mid gray */
583 fill_rect(x + 3, 2, width - 2, WINHEIGHT - 4);
585 set_foreground(3); /* white */
586 draw_line(x + 3, WINHEIGHT - 2, x + width - 1, WINHEIGHT - 2);
587 draw_line(x + width - 1, 1, x + width - 1, WINHEIGHT - 2);
589 set_foreground(4); /* darkest gray */
590 draw_line(x + 3, 1, x + width - 1, 1);
591 draw_line(x + 3, 2, x + 3, WINHEIGHT - 3);
594 void gui_draw_task(task * tk)
597 int taskw = tk->width;
611 set_foreground(0); /* mid gray */
612 fill_rect(x + 2, 0, taskw - 1, WINHEIGHT);
616 int text_x = x + TEXTPAD + TEXTPAD + (tk->icon ? ICONWIDTH : -1*ICONWIDTH/4);
617 #define SETCOL(x) col.color.red = cols[x].red;\
618 col.color.green = cols[x].green;\
619 col.color.blue = cols[x].blue;
622 pango_layout_set_width(pl, /*-1);*/(taskw - text_x + x) * PANGO_SCALE);
623 pango_layout_set_text(pl, tk->name, -1);
624 col.color.alpha = 0xffff;
628 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x+2)*PANGO_SCALE, text_y + 2);
630 } else if (tk->shaded) {
632 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), (text_x-2)*PANGO_SCALE, text_y - 2);
637 pango_xft_render_layout_line(xftdraw, &col, pango_layout_get_line(pl, 0), text_x*PANGO_SCALE, text_y);
641 /* check how many chars can fit */
642 len = strlen(tk->name);
644 XftTextExtentsUtf8(dd, xfs, tk->name, len, &ext);
645 if (ext.width < taskw - (text_x - x) - 2 || len <= 0)
650 col.color.alpha = 0xffff;
653 /* draw task's name dark (iconified) */
655 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y + 1, tk->name,
658 } else if (tk->shaded) {
659 /* draw task's name dark (shaded) */
661 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y - 1, tk->name,
669 /* draw task's name here */
670 XftDrawStringUtf8(xftdraw, &col, xfs, text_x, text_y, tk->name, len);
673 /* check how many chars can fit */
674 len = strlen(tk->name);
676 while (XTextWidth(xfs, tk->name, len) >= taskw - (text_x - x) - 2
681 /* draw task's name dark (iconified) */
683 XDrawString(dd, tb.win, fore_gc, text_x, text_y + 1, tk->name,
686 } else if (tk->shaded) {
687 /* draw task's name dark (shaded) */
689 XDrawString(dd, tb.win, fore_gc, text_x, text_y - 1, tk->name,
696 /* draw task's name here */
697 XDrawString(dd, tb.win, fore_gc, text_x, text_y, tk->name, len);
706 /* draw the task's icon */
707 XSetClipMask(dd, fore_gc, tk->mask);
708 XSetClipOrigin(dd, fore_gc, x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
709 XCopyArea(dd, tk->icon, tb.win, fore_gc, 0, 0, ICONWIDTH, ICONHEIGHT,
710 x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
711 XSetClipMask(dd, fore_gc, None);
714 void draw_dot(int x, int y)
717 XDrawPoint(dd, tb.win, fore_gc, x, y);
719 XDrawPoint(dd, tb.win, fore_gc, x + 1, y + 1);
722 void draw_grill(int x)
725 while (y < WINHEIGHT - 4) {
732 #ifdef MIKACHU //this only works in mikabox
733 void ob_action(Window win, char *action)
735 XClientMessageEvent xev;
736 char act_name[14+strlen(action)];
737 sprintf(act_name, "_OB_WM_ACTION_%s", action);
739 xev.type = ClientMessage;
741 xev.message_type = atoms[_OB_WM_ACTION];
743 /*strncpy(xev.data.b, action, 20);*/
744 xev.data.l[0] = XInternAtom(dd, act_name, False);
745 XSendEvent(dd, root_win, False, SubstructureRedirectMask, (XEvent *)&xev);
748 void netwm_action(Window win, atom_t atom, Time time, long l)
750 XClientMessageEvent xev = {
751 .type = ClientMessage,
755 .message_type = atoms[atom]
758 if (atom == _NET_ACTIVE_WINDOW) {
760 xev.data.l[1] = time;
762 } else if (atom == _NET_CURRENT_DESKTOP) {
764 xev.data.l[1] = time;
765 } else if (atom == _NET_RESTACK_WINDOW) {
769 xev.message_type = atoms[_NET_WM_STATE];
771 xev.data.l[1] = atoms[atom];
775 XSendEvent(dd, root_win, False, SubstructureNotifyMask|SubstructureRedirectMask, (XEvent *)&xev);
780 void switch_desk(int new_desk, Time time)
782 if (get_number_of_desktops() <= new_desk)
785 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
788 void pager_draw_button(int x, int num)
795 if (num == tb.my_desktop) {
796 /* current desktop */
797 draw_box(x, PAGER_BUTTON_WIDTH);
800 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
806 col.color.alpha = 0xffff;
807 col.color.red = cols[5].red;
808 col.color.green = cols[5].green;
809 col.color.blue = cols[5].blue;
810 XftDrawString8(xftdraw, &col, xfs,
811 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
815 XDrawString(dd, tb.win, fore_gc,
816 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
821 void pager_draw(void)
823 int desks, i, x = GRILL_WIDTH;
825 desks = get_number_of_desktops();
827 for (i = 0; i < desks; i++) {
828 pager_draw_button(x, i);
831 x += PAGER_BUTTON_WIDTH;
839 void gui_draw_taskbar(void)
847 pager_size = TEXTPAD;
850 width = WINWIDTH - (pager_size + GRILL_WIDTH + GRILL_WIDTH);
853 if (tb.num_tasks == 0)
856 taskw = width / tb.num_tasks;
857 if (taskw > MAX_TASK_WIDTH)
858 taskw = MAX_TASK_WIDTH;
863 tk->width = taskw - 1;
869 if (x < (width + pager_size + 2)) {
873 fill_rect(x + 2, 0, WINWIDTH, WINHEIGHT);
877 gui_draw_vline(WINWIDTH - 8);
880 draw_grill(WINWIDTH - 6);
883 task *find_task(Window win)
885 task *list = tb.task_list;
887 if (list->win == win)
894 void del_task(Window win)
896 task *next, *prev = 0, *list = tb.task_list;
900 if (list->win == win) {
901 /* unlink and free this task */
903 if (list->icon_copied) {
904 XFreePixmap(dd, list->icon);
905 if (list->mask != None)
906 XFreePixmap(dd, list->mask);
922 void move_taskbar(void)
929 x = TEXTPAD - WINWIDTH;
932 y = scr_height - WINHEIGHT;
934 XMoveWindow(dd, tb.win, x, y);
937 void taskbar_read_clientlist(void)
939 Window *win, focus_win = 0;
940 int num, i, desk, new_desk = 0;
942 desk = get_current_desktop();
949 XResizeWindow(dd, rspanelwin, WINWIDTH, WINHEIGHT);
952 if (desk != tb.my_desktop) {
954 tb.my_desktop = desk;
957 win = get_prop_data(root_win, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, &num);
961 win = get_prop_data(root_win, atoms[_NET_CLIENT_LIST], XA_WINDOW, &num);
965 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
968 list->focused = (focus_win == list->win);
972 for (i = num - 1; i >= 0; i--)
973 if (list->win == win[i] && !is_hidden(win[i]))
981 /* add any new windows */
982 for (i = 0; i < num; i++) {
983 if (!find_task(win[i]))
984 add_task(win[i], (win[i] == focus_win));
990 void handle_press(int x, int y, int button, Time time)
995 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
996 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
999 /* clicked left grill */
1002 tb.at_top = 1 - tb.at_top;
1004 } else if (button == 4) {
1010 /* clicked right grill */
1011 else if (x + TEXTPAD > WINWIDTH) {
1012 if (tb.hidden && (button == 1 || button == 5)) {
1015 } else if (!tb.hidden && (button == 1 || button == 4)) {
1022 /* clicked on a task button */
1023 if (x > tk->pos_x && x < tk->pos_x + tk->width) {
1026 if (tk->iconified && tk->shaded)
1027 // ob_action(tk->win, "unshade");
1028 // probably not needed netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
1029 //ob_action(tk->win, "activate");
1030 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
1034 //ob_action(tk->win, "deiconify");
1035 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1037 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, ADD);
1038 //ob_action(tk->win, "iconify");
1041 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
1042 //ob_action(tk->win, "raiselower");
1045 if (is_shaded(tk->win))
1046 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1048 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, ADD);
1051 if (is_shaded(tk->win))
1052 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, REMOVE);
1054 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1058 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, REMOVE);
1059 //ob_action(tk->win, "deiconify");
1060 #ifdef MIKACHU //this doesn't work for anyone else anyway
1062 ob_action(tk->win, "focus");
1066 // ob_action(tk->win, "lower");
1067 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
1070 // ob_action(tk->win, "raise");
1071 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
1077 } /* clicked on the background */
1080 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
1081 //ob_action(tb.win, "raise");
1084 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
1085 // ob_action(tb.win, "lower");
1088 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
1089 // ob_action(tb.win, "raiselower");
1103 void handle_focusin(Window win)
1110 if (tk->win != win) {
1115 if (tk->win == win) {
1124 void handle_propertynotify(Window win, Atom at)
1128 if (win == root_win) {
1129 if (at == atoms[_NET_CLIENT_LIST] || at == atoms[_NET_CURRENT_DESKTOP] || at == atoms[_NET_CLIENT_LIST_STACKING]) {
1130 taskbar_read_clientlist();
1136 tk = find_task(win);
1140 if (at == XA_WM_NAME || at == atoms[_NET_WM_NAME]) {
1141 /* window's title changed */
1143 newname = get_prop_data(tk->win, atoms[_NET_WM_NAME], atoms[STRING_UTF8], 0) ?:
1144 get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0);
1146 if (tk->name && !strcmp(newname, tk->name)) {
1155 } else if (at == atoms[_NET_WM_STATE]) {
1156 /* iconified state changed? */
1157 if (is_iconified(tk->win) != tk->iconified) {
1158 tk->iconified = !tk->iconified;
1161 /* shaded state changed? */
1162 if (is_shaded(tk->win) != tk->shaded) {
1163 tk->shaded = !tk->shaded;
1166 } else if (at == XA_WM_HINTS) {
1167 /* some windows set their WM_HINTS icon after mapping */
1168 //if (tk->icon == generic_icon) {
1169 get_task_hinticon(tk);
1172 } else if (at == atoms[_NET_WM_DESKTOP]) {
1173 if (find_desktop(win) != get_current_desktop())
1178 void handle_error(Display * d, XErrorEvent * ev)
1186 main(int argc, char *argv[])
1193 dd = XOpenDisplay(NULL);
1196 scr_screen = DefaultScreen(dd);
1197 scr_depth = DefaultDepth(dd, scr_screen);
1198 scr_height = DisplayHeight(dd, scr_screen);
1199 scr_width = DisplayWidth(dd, scr_screen);
1200 root_win = RootWindow(dd, scr_screen);
1202 /* helps us catch windows closing/opening */
1203 XSelectInput(dd, root_win, PropertyChangeMask);
1205 XSetErrorHandler((XErrorHandler) handle_error);
1207 XInternAtoms(dd, atom_names, ATOM_COUNT, False, atoms);
1210 bzero(&tb, sizeof(struct taskbar));
1211 tb.win = gui_create_taskbar();
1212 xfd = ConnectionNumber(dd);
1218 select(xfd + 1, &fd, 0, 0, 0);
1220 while (XPending(dd)) {
1221 XNextEvent(dd, &ev);
1224 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
1227 del_task(ev.xdestroywindow.window);
1232 case PropertyNotify:
1233 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
1236 if (ev.xfocus.mode != NotifyGrab)
1237 handle_focusin(ev.xfocus.window);
1240 printf ("unknown evt type: %d\n", ev.type); */
1245 /*XCloseDisplay (dd);