2 /**********************************************************
3 ** Rather Small Panel 0.8beta1 Copyright (c) 2006 **
4 ** By Mikael Magnusson **
5 ** See file COPYING for license details. **
6 **********************************************************/
18 #include <sys/types.h>
21 #include <X11/Xutil.h>
30 static int find_desktop(Window win)
32 return xprop_get_num(&sc, win, _NET_WM_DESKTOP);
35 static int find_state(Window win, xprop_t prop)
41 data = xprop_get_data(&sc, win, _NET_WM_STATE, XA_ATOM, &num);
44 if ((data[num]) == sc.atoms[prop])
52 /* this naming is very confusing :) */
53 static int is_hidden(Window win)
55 return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
58 static int is_iconified(Window win)
60 return find_state(win, _NET_WM_STATE_HIDDEN);
63 static int is_shaded(Window win)
65 return find_state(win, _NET_WM_STATE_SHADED);
68 static char* get_openbox_theme()
72 static int get_current_desktop(void)
74 return xprop_get_num(&sc, sc.root, _NET_CURRENT_DESKTOP);
78 static int get_number_of_desktops(void)
80 return xprop_get_int(sc.root, _NET_NUMBER_OF_DESKTOPS);
84 static void free_icons(task *tk)
88 for (i = 0; i < tk->nicons; ++i)
89 free(tk->icons[i].data);
95 static void task_update_icons(task *tk)
98 tk->icons = icon_update(&sc, tk->win, &tk->nicons);
101 int task_shown(task *tk)
103 return tk->focused || !tk->hidden;
106 static void add_task(Window win, int focus)
114 /* is this window on a different desktop? */
115 /* XXX add even if is_hidden, but don't show it later, so we can
116 * unhide it when the prop is removed */
117 desk = find_desktop(win);
118 if ((desk != 0xffffffff && tb.my_desktop != desk))
121 XSelectInput(sc.dd, win, PropertyChangeMask);
123 tk = calloc(1, sizeof(task));
126 tk->name = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
127 xprop_get_string(&sc, tk->win, WM_NAME);
129 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
130 tk->iconified = is_iconified(win);
131 tk->shaded = is_shaded(win);
132 tk->hidden = is_hidden(win);
133 task_update_icons(tk);
135 /* now append it to our linked list */
151 static void netwm_action(Window win, xprop_t prop, Time time, long l)
153 XClientMessageEvent xev = {
154 .type = ClientMessage,
158 .message_type = sc.atoms[prop]
161 if (prop == _NET_ACTIVE_WINDOW) {
163 xev.data.l[1] = time;
165 } else if (prop == _NET_CURRENT_DESKTOP) {
167 xev.data.l[1] = time;
168 } else if (prop == _NET_RESTACK_WINDOW) {
171 } else if (prop == _OB_FOCUS) {
173 xev.message_type = sc.atoms[_NET_WM_STATE];
175 xev.data.l[1] = sc.atoms[prop];
179 XSendEvent(sc.dd, sc.root, False,
180 SubstructureNotifyMask|SubstructureRedirectMask,
186 static void switch_desk(int new_desk, Time time)
188 if (get_number_of_desktops() <= new_desk)
191 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
194 /* This doesn't work with obrender yet */
195 static void pager_draw_button(int x, int num)
202 if (num == tb.my_desktop) {
203 /* current desktop */
204 draw_box(x, PAGER_BUTTON_WIDTH);
207 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
213 col.color.alpha = 0xffff;
214 col.color.red = cols[5].red;
215 col.color.green = cols[5].green;
216 col.color.blue = cols[5].blue;
217 XftDrawString8(xftdraw, &col, xfs,
218 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
222 XDrawString(sc.dd, tb.win, fore_gc,
223 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
228 static void pager_draw(void)
230 int desks, i, x = GRILL_WIDTH;
232 desks = get_number_of_desktops();
234 for (i = 0; i < desks; i++) {
235 pager_draw_button(x, i);
238 x += PAGER_BUTTON_WIDTH;
246 static task *find_task(Window win)
248 task *list = tb.task_list;
250 if (list->win == win)
257 static void del_task(Window win)
259 task *next, *prev = 0, *list = tb.task_list;
263 if (list->win == win) {
264 /* unlink and free this task */
280 static void taskbar_read_clientlist(void)
282 Window *win, focus_win = 0;
283 int num, i, desk, new_desk = 0;
285 desk = get_current_desktop();
297 XResizeWindow(sc.dd, tb.win, tb.w, tb.h);
301 if (desk != tb.my_desktop) {
303 tb.my_desktop = desk;
306 win = xprop_get_data(&sc, sc.root, _NET_ACTIVE_WINDOW, XA_WINDOW, &num);
307 if (win && num > 0) {
312 win = xprop_get_data(&sc, sc.root, _NET_CLIENT_LIST, XA_WINDOW, &num);
316 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
319 list->focused = (focus_win == list->win);
323 for (i = num - 1; i >= 0; i--)
324 if (list->win == win[i])
332 /* add any new windows */
333 for (i = 0; i < num; i++) {
334 if (!find_task(win[i]))
335 add_task(win[i], (win[i] == focus_win));
341 static void handle_press(int x, int y, int button, Time time)
346 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
347 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
350 /* clicked left grill */
353 tb.at_top = 1 - tb.at_top;
354 gui_move_taskbar(&sc, &tb);
355 } else if (button == 4) {
357 gui_move_taskbar(&sc, &tb);
361 /* clicked right grill */
362 else if (x + TEXTPAD > tb.w) {
363 if (tb.hidden && (button == 1 || button == 5)) {
365 gui_move_taskbar(&sc, &tb);
366 } else if (!tb.hidden && (button == 1 || button == 4)) {
368 gui_move_taskbar(&sc, &tb);
373 /* clicked on a task button */
374 /* XXX Make this configureable */
375 if (task_shown(tk) && (x > tk->pos_x && x < tk->pos_x + tk->width))
379 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
383 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
386 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
390 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
393 if (is_shaded(tk->win))
394 netwm_action(tk->win, _NET_RESTACK_WINDOW,
397 netwm_action(tk->win, _NET_WM_STATE_SHADED,
401 if (is_shaded(tk->win))
402 netwm_action(tk->win, _NET_WM_STATE_SHADED,
405 netwm_action(tk->win, _NET_RESTACK_WINDOW,
410 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
413 netwm_action(tk->win, _OB_FOCUS, 0, 0);
416 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
419 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
425 } /* clicked on the background */
428 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
431 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
434 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
438 gui_move_taskbar(&sc, &tb);
442 gui_move_taskbar(&sc, &tb);
449 static void handle_focusin(Window win)
456 if (tk->win != win) {
461 if (tk->win == win) {
471 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
473 Atom at = *(Atom*)arg;
474 return (e->type == PropertyNotify && e->xproperty.atom == at);
477 static void handle_propertynotify(Window win, Atom at)
481 if (win == sc.root) {
482 /* XXX only redraw the task that got focused/unfocused
483 * when _NET_ACTIVE_WINDOW triggers this,
484 * redraw everything if that task was hidden before though */
485 if (at == sc.atoms[_NET_CLIENT_LIST]
486 || at == sc.atoms[_NET_CURRENT_DESKTOP]
487 || at == sc.atoms[_NET_ACTIVE_WINDOW])
492 check = sc.atoms[_NET_CLIENT_LIST];
493 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
495 check = sc.atoms[_NET_CURRENT_DESKTOP];
496 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
498 check = sc.atoms[_NET_ACTIVE_WINDOW];
499 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
502 taskbar_read_clientlist();
503 gui_draw_taskbar(&sc, &tb);
508 /* XXX make this work when SKIP_TASKBAR is toggled too */
509 /* show skip_taskbar tasks if they're focused */
514 if (at == sc.atoms[WM_NAME] || at == sc.atoms[_NET_WM_NAME]) {
515 /* window's title changed */
516 /* XXX make a function for this and use from here and add_task */
518 newname = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
519 xprop_get_string(&sc, tk->win, WM_NAME);
521 /* It didn't change */
522 if (tk->name && !strcmp(newname, tk->name)) {
530 gui_draw_task(&sc, &tb, tk, TRUE);
531 } else if (at == sc.atoms[_NET_WM_ICON]) {
532 task_update_icons(tk);
533 gui_draw_task(&sc, &tb, tk, TRUE);
534 } else if (at == sc.atoms[_NET_WM_STATE]) {
535 /* iconified state changed? */
536 if (is_iconified(tk->win) != tk->iconified) {
537 tk->iconified = !tk->iconified;
539 gui_draw_task(&sc, &tb, tk, TRUE);
541 /* shaded state changed? */
542 if (is_shaded(tk->win) != tk->shaded) {
543 tk->shaded = !tk->shaded;
544 gui_draw_task(&sc, &tb, tk, TRUE);
546 if (is_hidden(tk->win) != tk->hidden) {
547 tk->hidden = !tk->hidden;
548 gui_draw_taskbar(&sc, &tb);
550 } else if (at == sc.atoms[_NET_WM_DESKTOP]) {
551 if (find_desktop(win) != get_current_desktop())
556 static void handle_error(Display * d, XErrorEvent * ev)
564 main(int argc, char *argv[])
570 sc.dd = XOpenDisplay(NULL);
573 xfd = ConnectionNumber(sc.dd);
575 sc.num = DefaultScreen(sc.dd);
576 sc.height = DisplayHeight(sc.dd, sc.num);
577 sc.width = DisplayWidth(sc.dd, sc.num);
578 sc.root = RootWindow(sc.dd, sc.num);
579 sc.rr = RrInstanceNew(sc.dd, sc.num);
581 /* helps us catch windows closing/opening */
582 XSelectInput(sc.dd, sc.root, PropertyChangeMask | SubstructureNotifyMask);
584 XSetErrorHandler((XErrorHandler) handle_error);
589 memset(&tb, 0, sizeof(tb));
590 gui_create_taskbar(&sc, &tb);
592 taskbar_read_clientlist();
593 gui_draw_taskbar(&sc, &tb);
601 select(xfd + 1, &fd, 0, 0, 0);
603 while (XPending(sc.dd)) {
604 XNextEvent(sc.dd, &ev);
607 handle_press(ev.xbutton.x, ev.xbutton.y,
608 ev.xbutton.button, ev.xbutton.time);
611 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
614 printf ("unknown evt type: %d\n", ev.type); */
618 /* RrInstanceFree(inst);
619 * XCloseDisplay (dd);