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();
293 XResizeWindow(sc.dd, tb.win, tb->w, tb->h);
296 if (desk != tb.my_desktop) {
298 tb.my_desktop = desk;
301 win = xprop_get_data(&sc, sc.root, _NET_ACTIVE_WINDOW, XA_WINDOW, &num);
302 if (win && num > 0) {
307 win = xprop_get_data(&sc, sc.root, _NET_CLIENT_LIST, XA_WINDOW, &num);
311 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
314 list->focused = (focus_win == list->win);
318 for (i = num - 1; i >= 0; i--)
319 if (list->win == win[i])
327 /* add any new windows */
328 for (i = 0; i < num; i++) {
329 if (!find_task(win[i]))
330 add_task(win[i], (win[i] == focus_win));
336 static void handle_press(int x, int y, int button, Time time)
341 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
342 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
345 /* clicked left grill */
348 tb.at_top = 1 - tb.at_top;
349 gui_move_taskbar(&sc, &tb);
350 } else if (button == 4) {
352 gui_move_taskbar(&sc, &tb);
356 /* clicked right grill */
357 else if (x + TEXTPAD > tb.w) {
358 if (tb.hidden && (button == 1 || button == 5)) {
360 gui_move_taskbar(&sc, &tb);
361 } else if (!tb.hidden && (button == 1 || button == 4)) {
363 gui_move_taskbar(&sc, &tb);
368 /* clicked on a task button */
369 /* XXX Make this configureable */
370 if (task_shown(tk) && (x > tk->pos_x && x < tk->pos_x + tk->width))
374 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
378 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, XPROP_REMOVE);
380 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, XPROP_ADD);
383 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
386 if (is_shaded(tk->win))
387 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
389 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, XPROP_ADD);
392 if (is_shaded(tk->win))
393 netwm_action(tk->win, _NET_WM_STATE_SHADED, time, XPROP_REMOVE);
395 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
399 netwm_action(tk->win, _NET_WM_STATE_HIDDEN, time, XPROP_REMOVE);
401 netwm_action(tk->win, _OB_FOCUS, 0, 0);
404 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
407 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
413 } /* clicked on the background */
416 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
419 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
422 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
426 gui_move_taskbar(&sc, &tb);
430 gui_move_taskbar(&sc, &tb);
437 static void handle_focusin(Window win)
444 if (tk->win != win) {
449 if (tk->win == win) {
459 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
461 Atom at = *(Atom*)arg;
462 return (e->type == PropertyNotify && e->xproperty.atom == at);
465 static void handle_propertynotify(Window win, Atom at)
469 if (win == sc.root) {
470 /* XXX only redraw the task that got focused/unfocused
471 * when _NET_ACTIVE_WINDOW triggers this,
472 * redraw everything if that task was hidden before though */
473 if (at == sc.atoms[_NET_CLIENT_LIST] || at == sc.atoms[_NET_CURRENT_DESKTOP] ||
474 at == sc.atoms[_NET_ACTIVE_WINDOW])
479 check = sc.atoms[_NET_CLIENT_LIST];
480 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property, (XPointer)&check));
481 check = sc.atoms[_NET_CURRENT_DESKTOP];
482 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property, (XPointer)&check));
483 check = sc.atoms[_NET_ACTIVE_WINDOW];
484 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property, (XPointer)&check));
486 taskbar_read_clientlist();
487 gui_draw_taskbar(&sc, &tb);
492 /* XXX make this work when SKIP_TASKBAR is toggled too */
493 /* show skip_taskbar tasks if they're focused */
498 if (at == sc.atoms[WM_NAME] || at == sc.atoms[_NET_WM_NAME]) {
499 /* window's title changed */
500 /* XXX make a function for this and use from here and add_task */
502 newname = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
503 xprop_get_string(&sc, tk->win, WM_NAME);
505 /* It didn't change */
506 if (tk->name && !strcmp(newname, tk->name)) {
514 gui_draw_task(&sc, &tb, tk, TRUE);
515 } else if (at == sc.atoms[_NET_WM_ICON]) {
516 task_update_icons(tk);
517 gui_draw_task(&sc, &tb, tk, TRUE);
518 } else if (at == sc.atoms[_NET_WM_STATE]) {
519 /* iconified state changed? */
520 if (is_iconified(tk->win) != tk->iconified) {
521 tk->iconified = !tk->iconified;
523 gui_draw_task(&sc, &tb, tk, TRUE);
525 /* shaded state changed? */
526 if (is_shaded(tk->win) != tk->shaded) {
527 tk->shaded = !tk->shaded;
528 gui_draw_task(&sc, &tb, tk, TRUE);
530 if (is_hidden(tk->win) != tk->hidden) {
531 tk->hidden = !tk->hidden;
532 gui_draw_taskbar(&sc, &tb);
534 } else if (at == sc.atoms[_NET_WM_DESKTOP]) {
535 if (find_desktop(win) != get_current_desktop())
540 static void handle_error(Display * d, XErrorEvent * ev)
548 main(int argc, char *argv[])
554 sc.dd = XOpenDisplay(NULL);
557 xfd = ConnectionNumber(sc.dd);
559 sc.num = DefaultScreen(sc.dd);
560 sc.height = DisplayHeight(sc.dd, sc.num);
561 sc.width = DisplayWidth(sc.dd, sc.num);
562 sc.root = RootWindow(sc.dd, sc.num);
563 sc.rr = RrInstanceNew(sc.dd, sc.num);
565 /* helps us catch windows closing/opening */
566 XSelectInput(sc.dd, sc.root, PropertyChangeMask | SubstructureNotifyMask);
568 XSetErrorHandler((XErrorHandler) handle_error);
573 memset(&tb, 0, sizeof(tb));
574 gui_create_taskbar(&sc, &tb);
576 taskbar_read_clientlist();
577 gui_draw_taskbar(&sc, &tb);
585 select(xfd + 1, &fd, 0, 0, 0);
587 while (XPending(sc.dd)) {
588 XNextEvent(sc.dd, &ev);
591 handle_press(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.time);
594 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
597 printf ("unknown evt type: %d\n", ev.type); */
601 /* RrInstanceFree(inst);
602 * XCloseDisplay (dd);