add the kdetrayproxy tool
[mikachu/openbox.git] / tools / kdetrayproxy / kdetrayproxy.c
1 #include <X11/Xlib.h>
2 #include <X11/Xatom.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/select.h>
7 #include <unistd.h>
8
9 typedef struct IList {
10     Window win;
11     int ignore_unmaps;
12
13     struct IList *next;
14 } IList;
15
16 Display *display;
17 Window root;
18 Atom winhint;
19 Atom roothint;
20 int xfd;
21 IList *list;
22
23 void init();
24 void eventloop();
25 void handleevent(XEvent *e);
26 void addicon(Window win);
27 void removeicon(Window win, int unmap);
28 int issystray(Atom *a, int n);
29 void updatehint();
30 Window findclient(Window win);
31 int ignore_errors(Display *d, XErrorEvent *e);
32 void wait_time(unsigned int t);
33
34 int main()
35 {
36     init();
37     updatehint();
38     eventloop();
39     return 0;
40 }
41
42 void init()
43 {
44     display = XOpenDisplay(NULL);
45     if (!display) {
46         fprintf(stderr, "Could not open display\n");
47         exit(EXIT_FAILURE);
48     }
49
50     xfd = ConnectionNumber(display);
51
52     root = RootWindowOfScreen(DefaultScreenOfDisplay(display));
53
54     winhint = XInternAtom(display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", 0);
55     roothint = XInternAtom(display, "_KDE_NET_SYSTEM_TRAY_WINDOWS", 0);
56
57     XSelectInput(display, root, SubstructureNotifyMask);
58 }
59
60 void eventloop()
61 {
62     XEvent e;
63     fd_set set;
64
65     while (1) {
66         int event = False;
67         while (XPending(display)) {
68             event = True;
69             XNextEvent(display, &e);
70             handleevent(&e);
71         }
72         if (!event) {
73             FD_ZERO(&set);
74             FD_SET(xfd, &set);
75             select(xfd + 1, &set, NULL, NULL, NULL);
76         }
77     }
78 }
79
80 void handleevent(XEvent *e)
81 {
82     switch (e->type) {
83     case MapNotify:
84     {
85         Atom *a;
86         int n;
87         Window w;
88
89         w = findclient(e->xmap.window);
90         if (w) {
91             a = XListProperties(display, w, &n);
92             if (issystray(a, n))
93                 addicon(w);
94             XFree(a);
95         }
96         break;
97     }
98     case UnmapNotify:
99         removeicon(e->xunmap.window, True);
100         break;
101     case DestroyNotify:
102         removeicon(e->xdestroywindow.window, False);
103         break;
104     }
105 }
106
107 int ignore_errors(Display *d, XErrorEvent *e)
108 {
109     (void)d; (void)e;
110     return 1;
111 }
112
113 void addicon(Window win)
114 {
115     IList *it;
116
117     for (it = list; it; it = it->next)
118         if (it->win == win) return; /* duplicate */
119
120     it = list;
121     list = malloc(sizeof(IList));
122     list->win = win;
123     list->ignore_unmaps = 2;
124     list->next = it;
125
126     XSelectInput(display, win, StructureNotifyMask);
127     /* if i set the root hint too fast the dock app can fuck itself up */
128     wait_time(1000000 / 8);
129     updatehint();
130 }
131
132 void removeicon(Window win, int unmap)
133 {
134     IList *it, *last = NULL;
135     void *old;
136
137     for (it = list; it; last = it, it = it->next)
138         if (it->win == win) {
139             if (it->ignore_unmaps && unmap) {
140                 it->ignore_unmaps--;
141                 return;
142             }
143
144             if (!last)
145                 list = it->next;
146             else
147                 last->next = it->next;
148
149             XSync(display, False);
150             old = XSetErrorHandler(ignore_errors);
151             XSelectInput(display, win, NoEventMask);
152             XSync(display, False);
153             XSetErrorHandler(old);
154             free(it);
155
156             updatehint();
157         }
158 }
159
160 int issystray(Atom *a, int n)
161 {
162     int i, r = False;
163
164     for (i = 0; i < n; ++i) { 
165         if (a[i] == winhint) {
166             r = True;
167             break;
168         }
169     }
170     return r;
171 }
172
173 void updatehint()
174 {
175     IList *it;
176     int *wins, n, i;
177
178     for (it = list, n = 0; it; it = it->next, ++n) ;
179     if (n) {
180         wins = malloc(sizeof(int) * n);
181         for (it = list, i = 0; it; it = it->next, ++i)
182             wins[i] = it->win;
183     }
184     XChangeProperty(display, root, roothint, XA_WINDOW, 32, PropModeReplace,
185                     (unsigned char*) wins, n);
186 }
187
188 Window findclient(Window win)
189 {
190   Window r, *children;
191   unsigned int n, i;
192   Atom state = XInternAtom(display, "WM_STATE", True);
193   Atom ret_type;
194   int ret_format;
195   unsigned long ret_items, ret_bytesleft;
196   unsigned long *prop_return;
197
198   XQueryTree(display, win, &r, &r, &children, &n);
199   for (i = 0; i < n; ++i) {
200     Window w = findclient(children[i]);
201     if (w) return w;
202   }
203
204   /* try me */
205   XGetWindowProperty(display, win, state, 0, 1,
206                      False, state, &ret_type, &ret_format,
207                      &ret_items, &ret_bytesleft,
208                      (unsigned char**) &prop_return); 
209   if (ret_type == None || ret_items < 1)
210     return None;
211   return win; /* found it! */
212 }
213
214 void wait_time(unsigned int t)
215 {
216     struct timeval time;
217     time.tv_sec = 0;
218     time.tv_usec = t;
219     select(1, NULL, NULL, NULL, &time);
220 }