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