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 xprop_t get_window_type(Window win)
72 xprop_t known_types[] = { _NET_WM_WINDOW_TYPE_NORMAL,
73 _NET_WM_WINDOW_TYPE_DIALOG,
74 _NET_WM_WINDOW_TYPE_MENU,
75 _NET_WM_WINDOW_TYPE_TOOLBAR,
76 _NET_WM_WINDOW_TYPE_UTILITY,
77 _NET_WM_WINDOW_TYPE_SPLASH,
78 _NET_WM_WINDOW_TYPE_DOCK,
79 _NET_WM_WINDOW_TYPE_DESKTOP, 0 };
81 types = xprop_get_data(&sc, win, _NET_WM_WINDOW_TYPE, XA_ATOM, &ntypes);
82 for (i = 0; i < ntypes; ++i)
83 for (j = 0; known_types[j]; ++j)
84 if (types[i] == sc.atoms[known_types[j]])
85 return known_types[j];
87 /* default to normal */
88 return _NET_WM_WINDOW_TYPE_NORMAL;
91 static int get_current_desktop(void)
93 return xprop_get_num(&sc, sc.root, _NET_CURRENT_DESKTOP);
97 static int get_number_of_desktops(void)
99 return xprop_get_int(sc.root, _NET_NUMBER_OF_DESKTOPS);
103 static void free_icons(task *tk)
107 for (i = 0; i < tk->nicons; ++i)
108 free(tk->icons[i].data);
114 static void task_update_icons(task *tk)
117 tk->icons = icon_update(&sc, tk->win, &tk->nicons);
120 int task_shown(task *tk)
122 return (tk->focused || !tk->hidden) &&
123 (tk->type == _NET_WM_WINDOW_TYPE_NORMAL ||
124 tk->type == _NET_WM_WINDOW_TYPE_DIALOG ||
125 tk->type == _NET_WM_WINDOW_TYPE_UTILITY);
128 static void add_task(Window win, int focus)
136 /* is this window on a different desktop? */
137 desk = find_desktop(win);
138 if ((desk != 0xffffffff && tb.my_desktop != desk))
141 XSelectInput(sc.dd, win, PropertyChangeMask);
143 tk = calloc(1, sizeof(task));
146 tk->name = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
147 xprop_get_string(&sc, tk->win, WM_NAME);
149 //tk->locale = get_prop_data(win, XA_WM_LOCALE_NAME, XA_STRING, 0);
150 tk->iconified = is_iconified(win);
151 tk->shaded = is_shaded(win);
152 tk->hidden = is_hidden(win);
153 tk->type = get_window_type(win);
154 task_update_icons(tk);
156 /* now append it to our linked list */
172 static void netwm_action(Window win, xprop_t prop, Time time, long l)
174 XClientMessageEvent xev = {
175 .type = ClientMessage,
179 .message_type = sc.atoms[prop]
182 if (prop == _NET_ACTIVE_WINDOW) {
184 xev.data.l[1] = time;
186 } else if (prop == _NET_CURRENT_DESKTOP) {
188 xev.data.l[1] = time;
189 } else if (prop == _NET_RESTACK_WINDOW) {
192 } else if (prop == _OB_FOCUS) {
194 xev.message_type = sc.atoms[_NET_WM_STATE];
196 xev.data.l[1] = sc.atoms[prop];
200 XSendEvent(sc.dd, sc.root, False,
201 SubstructureNotifyMask|SubstructureRedirectMask,
207 static void switch_desk(int new_desk, Time time)
209 if (get_number_of_desktops() <= new_desk)
212 netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
215 /* This doesn't work with obrender yet */
216 static void pager_draw_button(int x, int num)
223 if (num == tb.my_desktop) {
224 /* current desktop */
225 draw_box(x, PAGER_BUTTON_WIDTH);
228 fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
234 col.color.alpha = 0xffff;
235 col.color.red = cols[5].red;
236 col.color.green = cols[5].green;
237 col.color.blue = cols[5].blue;
238 XftDrawString8(xftdraw, &col, xfs,
239 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2), text_y,
243 XDrawString(sc.dd, tb.win, fore_gc,
244 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
249 static void pager_draw(void)
251 int desks, i, x = GRILL_WIDTH;
253 desks = get_number_of_desktops();
255 for (i = 0; i < desks; i++) {
256 pager_draw_button(x, i);
259 x += PAGER_BUTTON_WIDTH;
267 static task *find_task(Window win)
269 task *list = tb.task_list;
271 if (list->win == win)
278 static void del_task(Window win)
280 task *next, *prev = 0, *list = tb.task_list;
284 if (list->win == win) {
285 /* unlink and free this task */
301 static void taskbar_read_clientlist(void)
303 Window *win, focus_win = 0;
304 int num, i, desk, new_desk = 0;
306 desk = get_current_desktop();
318 XResizeWindow(sc.dd, tb.win, tb.w, tb.h);
322 if (desk != tb.my_desktop) {
324 tb.my_desktop = desk;
327 win = xprop_get_data(&sc, sc.root, _NET_ACTIVE_WINDOW, XA_WINDOW, &num);
328 if (win && num > 0) {
333 win = xprop_get_data(&sc, sc.root, _NET_CLIENT_LIST, XA_WINDOW, &num);
337 /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
340 list->focused = (focus_win == list->win);
344 for (i = num - 1; i >= 0; i--)
345 if (list->win == win[i])
353 /* add any new windows */
354 for (i = 0; i < num; i++) {
355 if (!find_task(win[i]))
356 add_task(win[i], (win[i] == focus_win));
362 static void handle_press(int x, int y, int button, Time time)
367 if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
368 switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
371 /* clicked left grill */
374 tb.at_top = 1 - tb.at_top;
375 gui_move_taskbar(&sc, &tb);
376 } else if (button == 4) {
378 gui_move_taskbar(&sc, &tb);
382 /* clicked right grill */
383 else if (x + TEXTPAD > tb.w) {
384 if (tb.hidden && (button == 1 || button == 5)) {
386 gui_move_taskbar(&sc, &tb);
387 } else if (!tb.hidden && (button == 1 || button == 4)) {
389 gui_move_taskbar(&sc, &tb);
394 /* clicked on a task button */
395 /* XXX Make this configureable */
396 if (task_shown(tk) && (x > tk->pos_x && x < tk->pos_x + tk->width))
400 if (tk->focused) /* iconify if focused */
401 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
404 netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
408 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
411 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
415 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
418 if (is_shaded(tk->win))
419 netwm_action(tk->win, _NET_RESTACK_WINDOW,
422 netwm_action(tk->win, _NET_WM_STATE_SHADED,
426 if (is_shaded(tk->win))
427 netwm_action(tk->win, _NET_WM_STATE_SHADED,
430 netwm_action(tk->win, _NET_RESTACK_WINDOW,
435 netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
438 netwm_action(tk->win, _OB_FOCUS, 0, 0);
441 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
444 netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
450 } /* clicked on the background */
453 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
456 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
459 netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
463 gui_move_taskbar(&sc, &tb);
467 gui_move_taskbar(&sc, &tb);
474 static void handle_focusin(Window win)
481 if (tk->win != win) {
486 if (tk->win == win) {
496 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
498 Atom at = *(Atom*)arg;
499 return (e->type == PropertyNotify && e->xproperty.atom == at);
502 static void handle_propertynotify(Window win, Atom at)
506 if (win == sc.root) {
507 /* XXX only redraw the task that got focused/unfocused
508 * when _NET_ACTIVE_WINDOW triggers this,
509 * redraw everything if that task was hidden before though */
510 if (at == sc.atoms[_NET_CLIENT_LIST]
511 || at == sc.atoms[_NET_CURRENT_DESKTOP]
512 || at == sc.atoms[_NET_ACTIVE_WINDOW])
517 check = sc.atoms[_NET_CLIENT_LIST];
518 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
520 check = sc.atoms[_NET_CURRENT_DESKTOP];
521 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
523 check = sc.atoms[_NET_ACTIVE_WINDOW];
524 while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
527 taskbar_read_clientlist();
528 gui_draw_taskbar(&sc, &tb);
531 if (at == sc.atoms[_OB_THEME]) {
532 gui_load_theme(&sc, &tb);
533 gui_draw_taskbar(&sc, &tb);
538 /* XXX make this work when SKIP_TASKBAR is toggled too */
539 /* show skip_taskbar tasks if they're focused */
544 if (at == sc.atoms[WM_NAME] || at == sc.atoms[_NET_WM_NAME]) {
545 /* window's title changed */
546 /* XXX make a function for this and use from here and add_task */
548 newname = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
549 xprop_get_string(&sc, tk->win, WM_NAME);
551 /* It didn't change */
552 if (tk->name && !strcmp(newname, tk->name)) {
560 gui_draw_task(&sc, &tb, tk, TRUE);
561 } else if (at == sc.atoms[_NET_WM_ICON]) {
562 task_update_icons(tk);
563 gui_draw_task(&sc, &tb, tk, TRUE);
564 } else if (at == sc.atoms[_NET_WM_STATE]) {
565 /* iconified state changed? */
566 if (is_iconified(tk->win) != tk->iconified) {
567 tk->iconified = !tk->iconified;
569 gui_draw_task(&sc, &tb, tk, TRUE);
571 /* shaded state changed? */
572 if (is_shaded(tk->win) != tk->shaded) {
573 tk->shaded = !tk->shaded;
574 gui_draw_task(&sc, &tb, tk, TRUE);
576 if (is_hidden(tk->win) != tk->hidden) {
577 tk->hidden = !tk->hidden;
578 gui_draw_taskbar(&sc, &tb);
580 } else if (at == sc.atoms[_NET_WM_DESKTOP]) {
581 if (find_desktop(win) != get_current_desktop())
586 static void handle_error(Display * d, XErrorEvent * ev)
594 main(int argc, char *argv[])
600 sc.dd = XOpenDisplay(NULL);
603 xfd = ConnectionNumber(sc.dd);
605 parse_paths_startup();
607 sc.num = DefaultScreen(sc.dd);
608 sc.height = DisplayHeight(sc.dd, sc.num);
609 sc.width = DisplayWidth(sc.dd, sc.num);
610 sc.root = RootWindow(sc.dd, sc.num);
611 sc.rr = RrInstanceNew(sc.dd, sc.num);
615 /* helps us catch windows closing/opening */
616 XSelectInput(sc.dd, sc.root, PropertyChangeMask | SubstructureNotifyMask);
618 XSetErrorHandler((XErrorHandler) handle_error);
623 memset(&tb, 0, sizeof(tb));
624 gui_create_taskbar(&sc, &tb);
626 taskbar_read_clientlist();
627 gui_draw_taskbar(&sc, &tb);
635 select(xfd + 1, &fd, 0, 0, 0);
637 while (XPending(sc.dd)) {
638 XNextEvent(sc.dd, &ev);
641 handle_press(ev.xbutton.x, ev.xbutton.y,
642 ev.xbutton.button, ev.xbutton.time);
645 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
648 printf ("unknown evt type: %d\n", ev.type); */
652 /* RrInstanceFree(inst);
653 * parse_paths_shutdown();
654 * XCloseDisplay (dd);