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 desk = find_desktop(win);
116 if ((desk != 0xffffffff && tb.my_desktop != desk))
119 XSelectInput(sc.dd, win, PropertyChangeMask);
121 tk = calloc(1, sizeof(task));
124 tk->name = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
125 xprop_get_string(&sc, tk->win, WM_NAME);
127 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
128 tk->iconified = is_iconified(win);
129 tk->shaded = is_shaded(win);
130 tk->hidden = is_hidden(win);
131 task_update_icons(tk);
133 /* now append it to our linked list */
149 static void netwm_action(Window win, xprop_t prop, Time time, long l)
151 XClientMessageEvent xev = {
152 .type = ClientMessage,
156 .message_type = sc.atoms[prop]
159 if (prop == _NET_ACTIVE_WINDOW) {
161 xev.data.l[1] = time;
163 } else if (prop == _NET_CURRENT_DESKTOP) {
165 xev.data.l[1] = time;
166 } else if (prop == _NET_RESTACK_WINDOW) {
169 } else if (prop == _OB_FOCUS) {
171 xev.message_type = sc.atoms[_NET_WM_STATE];
173 xev.data.l[1] = sc.atoms[prop];
177 XSendEvent(sc.dd, sc.root, False,
178 SubstructureNotifyMask|SubstructureRedirectMask,
184 static void switch_desk(int new_desk, Time time)
186 if (get_number_of_desktops() <= new_desk)
189 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
192 /* This doesn't work with obrender yet */
193 static void pager_draw_button(int x, int num)
200 if (num == tb.my_desktop) {
201 /* current desktop */
202 draw_box(x, PAGER_BUTTON_WIDTH);
205 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
211 col.color.alpha = 0xffff;
212 col.color.red = cols[5].red;
213 col.color.green = cols[5].green;
214 col.color.blue = cols[5].blue;
215 XftDrawString8(xftdraw, &col, xfs,
216 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
220 XDrawString(sc.dd, tb.win, fore_gc,
221 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
226 static void pager_draw(void)
228 int desks, i, x = GRILL_WIDTH;
230 desks = get_number_of_desktops();
232 for (i = 0; i < desks; i++) {
233 pager_draw_button(x, i);
236 x += PAGER_BUTTON_WIDTH;
244 static task *find_task(Window win)
246 task *list = tb.task_list;
248 if (list->win == win)
255 static void del_task(Window win)
257 task *next, *prev = 0, *list = tb.task_list;
261 if (list->win == win) {
262 /* unlink and free this task */
278 static void taskbar_read_clientlist(void)
280 Window *win, focus_win = 0;
281 int num, i, desk, new_desk = 0;
283 desk = get_current_desktop();
295 XResizeWindow(sc.dd, tb.win, tb.w, tb.h);
299 if (desk != tb.my_desktop) {
301 tb.my_desktop = desk;
304 win = xprop_get_data(&sc, sc.root, _NET_ACTIVE_WINDOW, XA_WINDOW, &num);
305 if (win && num > 0) {
310 win = xprop_get_data(&sc, sc.root, _NET_CLIENT_LIST, XA_WINDOW, &num);
314 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
317 list->focused = (focus_win == list->win);
321 for (i = num - 1; i >= 0; i--)
322 if (list->win == win[i])
330 /* add any new windows */
331 for (i = 0; i < num; i++) {
332 if (!find_task(win[i]))
333 add_task(win[i], (win[i] == focus_win));
339 static void handle_press(int x, int y, int button, Time time)
344 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
345 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
348 /* clicked left grill */
351 tb.at_top = 1 - tb.at_top;
352 gui_move_taskbar(&sc, &tb);
353 } else if (button == 4) {
355 gui_move_taskbar(&sc, &tb);
359 /* clicked right grill */
360 else if (x + TEXTPAD > tb.w) {
361 if (tb.hidden && (button == 1 || button == 5)) {
363 gui_move_taskbar(&sc, &tb);
364 } else if (!tb.hidden && (button == 1 || button == 4)) {
366 gui_move_taskbar(&sc, &tb);
371 /* clicked on a task button */
372 /* XXX Make this configureable */
373 if (task_shown(tk) && (x > tk->pos_x && x < tk->pos_x + tk->width))
377 if (tk->focused) /* iconify if focused */
378 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
381 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
385 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
388 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
392 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
395 if (is_shaded(tk->win))
396 netwm_action(tk->win, _NET_RESTACK_WINDOW,
399 netwm_action(tk->win, _NET_WM_STATE_SHADED,
403 if (is_shaded(tk->win))
404 netwm_action(tk->win, _NET_WM_STATE_SHADED,
407 netwm_action(tk->win, _NET_RESTACK_WINDOW,
412 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
415 netwm_action(tk->win, _OB_FOCUS, 0, 0);
418 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
421 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
427 } /* clicked on the background */
430 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
433 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
436 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
440 gui_move_taskbar(&sc, &tb);
444 gui_move_taskbar(&sc, &tb);
451 static void handle_focusin(Window win)
458 if (tk->win != win) {
463 if (tk->win == win) {
473 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
475 Atom at = *(Atom*)arg;
476 return (e->type == PropertyNotify && e->xproperty.atom == at);
479 static void handle_propertynotify(Window win, Atom at)
483 if (win == sc.root) {
484 /* XXX only redraw the task that got focused/unfocused
485 * when _NET_ACTIVE_WINDOW triggers this,
486 * redraw everything if that task was hidden before though */
487 if (at == sc.atoms[_NET_CLIENT_LIST]
488 || at == sc.atoms[_NET_CURRENT_DESKTOP]
489 || at == sc.atoms[_NET_ACTIVE_WINDOW])
494 check = sc.atoms[_NET_CLIENT_LIST];
495 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
497 check = sc.atoms[_NET_CURRENT_DESKTOP];
498 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
500 check = sc.atoms[_NET_ACTIVE_WINDOW];
501 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
504 taskbar_read_clientlist();
505 gui_draw_taskbar(&sc, &tb);
508 if (at == sc.atoms[_OB_THEME]) {
509 gui_load_theme(&sc, &tb);
510 gui_draw_taskbar(&sc, &tb);
515 /* XXX make this work when SKIP_TASKBAR is toggled too */
516 /* show skip_taskbar tasks if they're focused */
521 if (at == sc.atoms[WM_NAME] || at == sc.atoms[_NET_WM_NAME]) {
522 /* window's title changed */
523 /* XXX make a function for this and use from here and add_task */
525 newname = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
526 xprop_get_string(&sc, tk->win, WM_NAME);
528 /* It didn't change */
529 if (tk->name && !strcmp(newname, tk->name)) {
537 gui_draw_task(&sc, &tb, tk, TRUE);
538 } else if (at == sc.atoms[_NET_WM_ICON]) {
539 task_update_icons(tk);
540 gui_draw_task(&sc, &tb, tk, TRUE);
541 } else if (at == sc.atoms[_NET_WM_STATE]) {
542 /* iconified state changed? */
543 if (is_iconified(tk->win) != tk->iconified) {
544 tk->iconified = !tk->iconified;
546 gui_draw_task(&sc, &tb, tk, TRUE);
548 /* shaded state changed? */
549 if (is_shaded(tk->win) != tk->shaded) {
550 tk->shaded = !tk->shaded;
551 gui_draw_task(&sc, &tb, tk, TRUE);
553 if (is_hidden(tk->win) != tk->hidden) {
554 tk->hidden = !tk->hidden;
555 gui_draw_taskbar(&sc, &tb);
557 } else if (at == sc.atoms[_NET_WM_DESKTOP]) {
558 if (find_desktop(win) != get_current_desktop())
563 static void handle_error(Display * d, XErrorEvent * ev)
571 main(int argc, char *argv[])
577 sc.dd = XOpenDisplay(NULL);
580 xfd = ConnectionNumber(sc.dd);
582 parse_paths_startup();
584 sc.num = DefaultScreen(sc.dd);
585 sc.height = DisplayHeight(sc.dd, sc.num);
586 sc.width = DisplayWidth(sc.dd, sc.num);
587 sc.root = RootWindow(sc.dd, sc.num);
588 sc.rr = RrInstanceNew(sc.dd, sc.num);
592 /* helps us catch windows closing/opening */
593 XSelectInput(sc.dd, sc.root, PropertyChangeMask | SubstructureNotifyMask);
595 XSetErrorHandler((XErrorHandler) handle_error);
600 memset(&tb, 0, sizeof(tb));
601 gui_create_taskbar(&sc, &tb);
603 taskbar_read_clientlist();
604 gui_draw_taskbar(&sc, &tb);
612 select(xfd + 1, &fd, 0, 0, 0);
614 while (XPending(sc.dd)) {
615 XNextEvent(sc.dd, &ev);
618 handle_press(ev.xbutton.x, ev.xbutton.y,
619 ev.xbutton.button, ev.xbutton.time);
622 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
625 printf ("unknown evt type: %d\n", ev.type); */
629 /* RrInstanceFree(inst);
630 * parse_paths_shutdown();
631 * XCloseDisplay (dd);