]> icculus.org git repositories - mikachu/rspanel.git/blob - rspanel.c
more layout fixing. make the grills smaller. don't add arbirary padding on the left...
[mikachu/rspanel.git] / rspanel.c
1
2 /**********************************************************
3  ** Rather Small Panel 0.8beta1 Copyright (c) 2006       **
4  ** By Mikael Magnusson                                  **
5  ** See file COPYING for license details.                **
6  **********************************************************/
7
8 #include "rspanel.h"
9 #include "gui.h"
10 #include "icon.h"
11 #include "dims.h"
12 #include "xprop.h"
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <X11/Xutil.h>
22 #include <iconv.h>
23
24
25 #include "rspanel.h"
26
27 screen sc;
28 taskbar tb;
29
30 static int find_desktop(Window win)
31 {
32     return xprop_get_num(&sc, win, _NET_WM_DESKTOP);
33 }
34
35 static int find_state(Window win, xprop_t prop)
36 {
37     unsigned long *data;
38     int ret = 0;
39     int num;
40
41     data = xprop_get_data(&sc, win, _NET_WM_STATE, XA_ATOM, &num);
42     if (data) {
43         while (num--) {
44             if ((data[num]) == sc.atoms[prop])
45                 ret = 1;
46         }
47         XFree(data);
48     }
49     return ret;
50 }
51
52 /* this naming is very confusing :) */
53 static int is_hidden(Window win)
54 {
55     return find_state(win, _NET_WM_STATE_SKIP_TASKBAR);
56 }
57
58 static int is_iconified(Window win)
59 {
60     return find_state(win, _NET_WM_STATE_HIDDEN);
61 }
62
63 static int is_shaded(Window win)
64 {
65     return find_state(win, _NET_WM_STATE_SHADED);
66 }
67
68 static char* get_openbox_theme()
69 {
70 }
71
72 static int get_current_desktop(void)
73 {
74     return xprop_get_num(&sc, sc.root, _NET_CURRENT_DESKTOP);
75 }
76
77 #ifdef PAGER
78 static int get_number_of_desktops(void)
79 {
80     return xprop_get_int(sc.root, _NET_NUMBER_OF_DESKTOPS);
81 }
82 #endif
83
84 static void free_icons(task *tk)
85 {
86     unsigned int i;
87
88     for (i = 0; i < tk->nicons; ++i)
89         free(tk->icons[i].data);
90     if (tk->nicons > 0)
91         free(tk->icons);
92     tk->nicons = 0;
93 }
94
95 static void task_update_icons(task *tk)
96 {
97     free_icons(tk);
98     tk->icons = icon_update(&sc, tk->win, &tk->nicons);
99 }
100
101 int task_shown(task *tk)
102 {
103     return tk->focused || !tk->hidden;
104 }
105
106 static void add_task(Window win, int focus)
107 {
108     task *tk, *list;
109     int desk;
110
111     if (win == tb.win)
112         return;
113
114     /* is this window on a different desktop? */
115     desk = find_desktop(win);
116     if ((desk != 0xffffffff && tb.my_desktop != desk))
117         return;
118
119     XSelectInput(sc.dd, win, PropertyChangeMask);
120
121     tk = calloc(1, sizeof(task));
122     tk->win = win;
123     tk->focused = focus;
124     tk->name = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
125                xprop_get_string(&sc, tk->win, WM_NAME);
126     /* XXX use this? */
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);
132
133     /* now append it to our linked list */
134
135     list = tb.task_list;
136     if (!list) {
137         tb.task_list = tk;
138         return;
139     }
140     while (1) {
141         if (!list->next) {
142             list->next = tk;
143             return;
144         }
145         list = list->next;
146     }
147 }
148
149 static void netwm_action(Window win, xprop_t prop, Time time, long l)
150 {
151     XClientMessageEvent xev = {
152     .type = ClientMessage,
153     .window = win,
154     .format = 32,
155     .data.l = {0},
156     .message_type = sc.atoms[prop]
157     };
158     
159     if (prop == _NET_ACTIVE_WINDOW) {
160         xev.data.l[0] = 2;
161         xev.data.l[1] = time;
162         xev.data.l[2] = 0;
163     } else if (prop == _NET_CURRENT_DESKTOP) {
164         xev.data.l[0] = l;
165         xev.data.l[1] = time;
166     } else if (prop == _NET_RESTACK_WINDOW) {
167         xev.data.l[0] = 2;
168         xev.data.l[2] = l;
169     } else if (prop == _OB_FOCUS) {
170     } else {
171         xev.message_type = sc.atoms[_NET_WM_STATE];
172         xev.data.l[0] = l;
173         xev.data.l[1] = sc.atoms[prop];
174         xev.data.l[3] = 2;
175     }
176
177     XSendEvent(sc.dd, sc.root, False,
178                SubstructureNotifyMask|SubstructureRedirectMask,
179                (XEvent *)&xev);
180 }
181
182 #ifdef PAGER
183
184 static void switch_desk(int new_desk, Time time)
185 {
186     if (get_number_of_desktops() <= new_desk)
187         return;
188
189     netwm_action(None, _NET_CURRENT_DESKTOP, time, new_desk);
190 }
191
192 /* This doesn't work with obrender yet */
193 static void pager_draw_button(int x, int num)
194 {
195     char label;
196 #ifdef XFT
197     XftColor col;
198 #endif
199
200     if (num == tb.my_desktop) {
201         /* current desktop */
202         draw_box(x, PAGER_BUTTON_WIDTH);
203     } else {
204         set_foreground(0);
205         fill_rect(x, 1, PAGER_BUTTON_WIDTH + 1, WINHEIGHT - 2);
206     }
207
208     label = '1' + num;
209
210 #ifdef XFT
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,
217                    &label, 1);
218 #else
219     set_foreground(5);
220     XDrawString(sc.dd, tb.win, fore_gc,
221                 x + ((PAGER_BUTTON_WIDTH - PAGER_DIGIT_WIDTH) / 2) - 1,
222                 text_y, &label, 1);
223 #endif
224 }
225
226 static void pager_draw(void)
227 {
228     int desks, i, x = GRILL_WIDTH;
229
230     desks = get_number_of_desktops();
231
232     for (i = 0; i < desks; i++) {
233         pager_draw_button(x, i);
234         if (i > 8)
235             break;
236         x += PAGER_BUTTON_WIDTH;
237     }
238
239     pager_size = x;
240 }
241
242 #endif
243
244 static task *find_task(Window win)
245 {
246     task *list = tb.task_list;
247     while (list) {
248         if (list->win == win)
249             return list;
250         list = list->next;
251     }
252     return 0;
253 }
254
255 static void del_task(Window win)
256 {
257     task *next, *prev = 0, *list = tb.task_list;
258
259     while (list) {
260         next = list->next;
261         if (list->win == win) {
262             /* unlink and free this task */
263             free_icons(list);
264             if (list->name)
265                 XFree(list->name);
266             free(list);
267             if (prev == 0)
268                 tb.task_list = next;
269             else
270                 prev->next = next;
271             return;
272         }
273         prev = list;
274         list = next;
275     }
276 }
277
278 static void taskbar_read_clientlist(void)
279 {
280     Window *win, focus_win = 0;
281     int num, i, desk, new_desk = 0;
282     task *list, *next;
283     desk = get_current_desktop();
284
285 #ifdef MIKACHU
286     if (desk == 0) {
287         if (tb.w == 827)
288             goto skip;
289         tb.w = 827;
290     } else {
291         if (tb.w == 1280)
292             goto skip;
293         tb.w = 1280;
294     }
295     XResizeWindow(sc.dd, tb.win, tb.w, tb.h);
296 skip:
297 #endif
298
299     if (desk != tb.my_desktop) {
300         new_desk = 1;
301         tb.my_desktop = desk;
302     }
303
304     win = xprop_get_data(&sc, sc.root, _NET_ACTIVE_WINDOW, XA_WINDOW, &num);
305     if (win && num > 0) {
306         focus_win = win[0];
307         XFree(win);
308     }
309
310     win = xprop_get_data(&sc, sc.root, _NET_CLIENT_LIST, XA_WINDOW, &num);
311     if (!win)
312         return;
313
314     /* remove windows that arn't in the _NET_CLIENT_LIST anymore */
315     list = tb.task_list;
316     while (list) {
317         list->focused = (focus_win == list->win);
318         next = list->next;
319
320         if (!new_desk)
321             for (i = num - 1; i >= 0; i--)
322                 if (list->win == win[i])
323                     goto dontdel;
324         del_task(list->win);
325 dontdel:
326
327         list = next;
328     }
329
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));
334     }
335
336     XFree(win);
337 }
338
339 static void handle_press(int x, int y, int button, Time time)
340 {
341     task *tk;
342
343 #ifdef PAGER
344     if ((y > 2 && y < WINHEIGHT - 2 && x > GRILL_WIDTH) && button == 1)
345         switch_desk((x - GRILL_WIDTH) / PAGER_BUTTON_WIDTH, time);
346     else
347 #endif
348     /* clicked left grill */
349     if (x < 6) {
350         if (button == 1) {
351             tb.at_top = 1 - tb.at_top;
352             gui_move_taskbar(&sc, &tb);
353         } else if (button == 4) {
354             tb.hidden = 1;
355             gui_move_taskbar(&sc, &tb);
356         }
357     }
358
359     /* clicked right grill */
360     else if (x + TEXTPAD > tb.w) {
361         if (tb.hidden && (button == 1 || button == 5)) {
362             tb.hidden = 0;
363             gui_move_taskbar(&sc, &tb);
364         } else if (!tb.hidden && (button == 1 || button == 4)) {
365             tb.hidden = 1;
366             gui_move_taskbar(&sc, &tb);
367         }
368     } else {
369         tk = tb.task_list;
370         while (tk) {
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))
374             {
375                 switch (button) {
376                 case 1:
377                     if (tk->focused) /* iconify if focused */
378                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
379                                      time, XPROP_ADD);
380                     else
381                         netwm_action(tk->win, _NET_ACTIVE_WINDOW, time, 0);
382                     break;
383                 case 2:
384                     if (tk->iconified)
385                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
386                                      time, XPROP_REMOVE);
387                     else
388                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
389                                      time, XPROP_ADD);
390                     break;
391                 case 3:
392                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Opposite);
393                     break;
394                 case 4:
395                     if (is_shaded(tk->win))
396                             netwm_action(tk->win, _NET_RESTACK_WINDOW,
397                                          time, Below);
398                     else
399                             netwm_action(tk->win, _NET_WM_STATE_SHADED,
400                                          time, XPROP_ADD);
401                     break;
402                 case 5:
403                     if (is_shaded(tk->win))
404                         netwm_action(tk->win, _NET_WM_STATE_SHADED,
405                                      time, XPROP_REMOVE);
406                     else
407                             netwm_action(tk->win, _NET_RESTACK_WINDOW,
408                                          time, Above);
409                     break;
410                 case 9:
411                     if (tk->iconified)
412                         netwm_action(tk->win, _NET_WM_STATE_HIDDEN,
413                                      time, XPROP_REMOVE);
414                     else
415                         netwm_action(tk->win, _OB_FOCUS, 0, 0);
416                     break;
417                 case 6:
418                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Below);
419                     break;
420                 case 7:
421                     netwm_action(tk->win, _NET_RESTACK_WINDOW, time, Above);
422                     break;
423                 }
424                 return;
425             }
426             tk = tk->next;
427         } /* clicked on the background */
428         switch (button) {
429         case 1:
430             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Above);
431             break;
432         case 2:
433             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Below);
434             break;
435         case 3:
436             netwm_action(tb.win, _NET_RESTACK_WINDOW, time, Opposite);
437             break;
438         case 4:
439             tb.hidden = 1;
440             gui_move_taskbar(&sc, &tb);
441             break;
442         case 5:
443             tb.hidden = 0;
444             gui_move_taskbar(&sc, &tb);
445             break;
446         }
447     }
448 }
449
450 #if 0
451 static void handle_focusin(Window win)
452 {
453     task *tk;
454
455     tk = tb.task_list;
456     while (tk) {
457         if (tk->focused) {
458             if (tk->win != win) {
459                 tk->focused = 0;
460                 gui_draw_task(tk);
461             }
462         } else {
463             if (tk->win == win) {
464                 tk->focused = 1;
465                 gui_draw_task(tk);
466             }
467         }
468         tk = tk->next;
469     }
470 }
471 #endif
472
473 static Bool look_for_duplicate_property(Display *d, XEvent *e, XPointer arg)
474 {
475     Atom at = *(Atom*)arg;
476     return (e->type == PropertyNotify && e->xproperty.atom == at);
477 }
478
479 static void handle_propertynotify(Window win, Atom at)
480 {
481     task *tk;
482
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])
490         {
491             XEvent ce;
492             Atom check;
493
494             check = sc.atoms[_NET_CLIENT_LIST];
495             while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
496                                  (XPointer)&check));
497             check = sc.atoms[_NET_CURRENT_DESKTOP];
498             while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
499                                  (XPointer)&check));
500             check = sc.atoms[_NET_ACTIVE_WINDOW];
501             while (XCheckIfEvent(sc.dd, &ce, look_for_duplicate_property,
502                                  (XPointer)&check));
503
504             taskbar_read_clientlist();
505             gui_draw_taskbar(&sc, &tb);
506         }
507
508         if (at == sc.atoms[_OB_THEME]) {
509             gui_load_theme(&sc, &tb);
510             gui_draw_taskbar(&sc, &tb);
511         }
512         return;
513     }
514
515     /* XXX make this work when SKIP_TASKBAR is toggled too */
516     /* show skip_taskbar tasks if they're focused */
517     tk = find_task(win);
518     if (!tk)
519         return;
520
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 */
524         char *newname;
525         newname = xprop_get_utf8(&sc, tk->win, _NET_WM_NAME) ?:
526                   xprop_get_string(&sc, tk->win, WM_NAME);
527         if (newname) {
528             /* It didn't change */
529             if (tk->name && !strcmp(newname, tk->name)) {
530                 XFree(newname);
531                 return;
532             }
533         }
534         if (tk->name)
535             XFree(tk->name);
536         tk->name = newname;
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;
545             if (!tk->hidden)
546                 gui_draw_task(&sc, &tb, tk, TRUE);
547         }
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);
552         }
553         if (is_hidden(tk->win) != tk->hidden) {
554             tk->hidden = !tk->hidden;
555             gui_draw_taskbar(&sc, &tb);
556         }
557     } else if (at == sc.atoms[_NET_WM_DESKTOP]) {
558         if (find_desktop(win) != get_current_desktop())
559             del_task(tk->win);
560     }
561 }
562
563 static void handle_error(Display * d, XErrorEvent * ev)
564 {
565 }
566
567 int
568 #ifdef NOSTDLIB
569 _start(void)
570 #else
571 main(int argc, char *argv[])
572 #endif
573 {
574     XEvent ev;
575     int xfd;
576
577     sc.dd = XOpenDisplay(NULL);
578     if (!sc.dd)
579         return 0;
580     xfd = ConnectionNumber(sc.dd);
581
582     parse_paths_startup();
583
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);
589     sc.theme = NULL;
590     sc.fore_gc = None;
591
592     /* helps us catch windows closing/opening */
593     XSelectInput(sc.dd, sc.root, PropertyChangeMask | SubstructureNotifyMask);
594
595     XSetErrorHandler((XErrorHandler) handle_error);
596
597     xprop_init(&sc);
598
599     gui_init(&sc);
600     memset(&tb, 0, sizeof(tb));
601     gui_create_taskbar(&sc, &tb);
602     XSync(sc.dd, False);
603     taskbar_read_clientlist();
604     gui_draw_taskbar(&sc, &tb);
605     XFlush(sc.dd);
606
607     while (1) {
608         fd_set fd;
609
610         FD_ZERO(&fd);
611         FD_SET(xfd, &fd);
612         select(xfd + 1, &fd, 0, 0, 0);
613
614         while (XPending(sc.dd)) {
615             XNextEvent(sc.dd, &ev);
616             switch (ev.type) {
617             case ButtonPress:
618                 handle_press(ev.xbutton.x, ev.xbutton.y,
619                              ev.xbutton.button, ev.xbutton.time);
620                 break;
621             case PropertyNotify:
622                 handle_propertynotify(ev.xproperty.window, ev.xproperty.atom);
623                 break;
624             /*default:
625                    printf ("unknown evt type: %d\n", ev.type); */
626             }
627         }
628     }
629     /* RrInstanceFree(inst);
630      * parse_paths_shutdown();
631      * XCloseDisplay (dd);
632
633        return 0; */
634
635 }
636