]> Dogcows Code - chaz/tint2/blob - src/systray/systraybar.c
fixed issue 49 and some systray code (not yet)
[chaz/tint2] / src / systray / systraybar.c
1 /**************************************************************************
2 * Tint2 : systraybar
3 *
4 * Copyright (C) 2009 thierry lorthiois (lorthiois@bbsoft.fr)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 **************************************************************************/
18
19 #include <X11/Xlib.h>
20 #include <X11/Xutil.h>
21 #include <X11/Xatom.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <glib.h>
26 #include <Imlib2.h>
27
28 #include "systraybar.h"
29 #include "server.h"
30 #include "panel.h"
31
32 GSList *icons;
33
34 /* defined in the systray spec */
35 #define SYSTEM_TRAY_REQUEST_DOCK 0
36 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
37 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
38
39 // selection window
40 Window net_sel_win = None;
41
42 // freedesktop specification doesn't allow multi systray
43 Systraybar systray;
44
45
46 void init_systray()
47 {
48 cleanup_systray();
49
50 Panel *panel = &panel1[0];
51 systray.area.parent = panel;
52 systray.area.panel = panel;
53 systray.area._resize = resize_systray;
54
55 if (systray.area.visible) {
56 if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != None) {
57 fprintf(stderr, "tint2 : another systray is running\n");
58 systray.area.visible = 0;
59 }
60 }
61
62 if (systray.area.visible)
63 systray.area.visible = net_init();
64
65 if (!systray.area.visible)
66 return;
67
68 // configure systray
69 // draw only one systray (even with multi panel)
70 systray.area.posy = panel->area.pix.border.width + panel->area.paddingy;
71 systray.area.height = panel->area.height - (2 * systray.area.posy);
72 systray.area.width = 0;
73
74 systray.area.posx = panel->area.width - panel->area.paddingxlr - panel->area.pix.border.width - systray.area.width;
75 if (panel->clock.area.visible)
76 systray.area.posx -= (panel->clock.area.width + panel->area.paddingx);
77
78 systray.area.redraw = 1;
79 }
80
81
82 void cleanup_systray()
83 {
84 free_area(&systray.area);
85
86 if (net_sel_win != None) {
87 XDestroyWindow(server.dsp, net_sel_win);
88 net_sel_win = None;
89 }
90 if (systray.list_icons) {
91 g_slist_free(systray.list_icons);
92 systray.list_icons = 0;
93 }
94 }
95
96
97 int net_init()
98 {
99 // init systray protocol
100 net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
101
102 // v0.2 trayer specification. tint2 always orizontal.
103 int orient = 0;
104 XChangeProperty(server.dsp, net_sel_win, server.atom._NET_SYSTEM_TRAY_ORIENTATION, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &orient, 1);
105
106 XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
107 if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
108 fprintf(stderr, "tint2 : can't get systray manager\n");
109 return 0;
110 }
111
112 XClientMessageEvent ev;
113 ev.type = ClientMessage;
114 ev.window = server.root_win;
115 ev.message_type = server.atom.MANAGER;
116 ev.format = 32;
117 ev.data.l[0] = CurrentTime;
118 ev.data.l[1] = server.atom._NET_SYSTEM_TRAY_SCREEN;
119 ev.data.l[2] = net_sel_win;
120 ev.data.l[3] = 0;
121 ev.data.l[4] = 0;
122 XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, (XEvent*)&ev);
123
124 return 1;
125 }
126
127
128 void resize_systray (void *obj)
129 {
130 Systraybar *sysbar = obj;
131 Panel *panel = sysbar->area.panel;
132 int count = g_slist_length(systray.list_icons);
133
134 if (!count) systray.area.width = 0;
135 else systray.area.width = 30 * count;
136
137 systray.area.posx = panel->area.width - panel->area.paddingxlr - panel->area.pix.border.width - systray.area.width;
138 if (panel->clock.area.visible)
139 systray.area.posx -= (panel->clock.area.width + panel->area.paddingx);
140
141 systray.area.redraw = 1;
142
143 // resize other objects on panel
144 printf("resize_systray %d %d\n", systray.area.posx, systray.area.width);
145 }
146
147
148
149 Window win, root;
150 int width, height;
151 int border;
152 int icon_size;
153
154
155 void fix_geometry()
156 {
157 GSList *it;
158
159 // find the proper width and height
160 width = 0;
161 height = icon_size;
162 for (it = icons; it != NULL; it = g_slist_next(it)) {
163 width += icon_size;
164 }
165
166 XResizeWindow(server.dsp, win, width + border * 2, height + border * 2);
167 }
168
169
170 gboolean error;
171 int window_error_handler(Display *d, XErrorEvent *e)
172 {
173 d=d;e=e;
174 if (e->error_code == BadWindow) {
175 error = TRUE;
176 } else {
177 //g_printerr("X ERROR NOT BAD WINDOW!\n");
178 abort();
179 }
180 return 0;
181 }
182
183
184 gboolean icon_swallow(TrayWindow *traywin)
185 {
186 XErrorHandler old;
187
188 error = FALSE;
189 old = XSetErrorHandler(window_error_handler);
190 XReparentWindow(server.dsp, traywin->id, win, 0, 0);
191 XSync(server.dsp, False);
192 XSetErrorHandler(old);
193
194 return !error;
195 }
196
197
198 // The traywin must have its id and type set.
199 gboolean icon_add(Window id)
200 {
201 TrayWindow *traywin;
202
203 traywin = g_new0(TrayWindow, 1);
204 traywin->id = id;
205
206 systray.list_icons = g_slist_append(systray.list_icons, traywin);
207 printf("ajout d'un icone %d (%lx)\n", g_slist_length(systray.list_icons), id);
208 systray.area.resize = 1;
209
210 // changed in systray force resize on panel
211 Panel *panel = systray.area.panel;
212 panel->area.resize = 1;
213 panel_refresh = 1;
214 return TRUE;
215
216 if (!icon_swallow(traywin)) {
217 printf("not icon_swallow\n");
218 g_free(traywin);
219 return FALSE;
220 }
221
222 // find the positon for the systray app window
223 int count = g_slist_length(icons);
224 traywin->x = border + ((width % icon_size) / 2) +
225 (count % (width / icon_size)) * icon_size;
226 traywin->y = border + ((height % icon_size) / 2) +
227 (count / (height / icon_size)) * icon_size;
228
229 // add the new icon to the list
230 icons = g_slist_append(icons, traywin);
231
232 // watch for the icon trying to resize itself!
233 //XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
234
235 // position and size the icon window
236 XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
237
238 // resize our window so that the new window can fit in it
239 fix_geometry();
240
241 // flush before clearing, otherwise the clear isn't effective.
242 XFlush(server.dsp);
243 // make sure the new child will get the right stuff in its background
244 // for ParentRelative.
245 XClearWindow(server.dsp, win);
246
247 // show the window
248 XMapRaised(server.dsp, traywin->id);
249
250 return TRUE;
251 }
252
253
254 void icon_remove(TrayWindow *traywin)
255 {
256 XErrorHandler old;
257 Window win_id = traywin->id;
258
259 XSelectInput(server.dsp, traywin->id, NoEventMask);
260
261 // remove it from our list
262 systray.list_icons = g_slist_remove(systray.list_icons, traywin);
263 g_free(traywin);
264 printf("suppression d'un icone %d\n", g_slist_length(systray.list_icons));
265 systray.area.resize = 1;
266
267 // changed in systray force resize on panel
268 Panel *panel = systray.area.panel;
269 panel->area.resize = 1;
270 panel_refresh = 1;
271 return;
272
273 /*
274 // reparent it to root
275 error = FALSE;
276 old = XSetErrorHandler(window_error_handler);
277 XReparentWindow(server.dsp, win_id, root, 0, 0);
278 XSync(server.dsp, False);
279 XSetErrorHandler(old);
280
281 reposition_icons();
282 fix_geometry();
283 */
284 }
285
286
287 void net_message(XClientMessageEvent *e)
288 {
289 unsigned long opcode;
290 Window id;
291
292 opcode = e->data.l[1];
293
294 switch (opcode) {
295 case SYSTEM_TRAY_REQUEST_DOCK:
296 panel_refresh = 1;
297 id = e->data.l[2];
298 if (id && icon_add(id)) {
299 XSelectInput(server.dsp, id, StructureNotifyMask);
300 }
301 break;
302
303 case SYSTEM_TRAY_BEGIN_MESSAGE:
304 //g_printerr("Message From Dockapp\n");
305 id = e->window;
306 break;
307
308 case SYSTEM_TRAY_CANCEL_MESSAGE:
309 //g_printerr("Message Cancelled\n");
310 id = e->window;
311 break;
312
313 default:
314 if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
315 printf("message from dockapp:\n %s\n", e->data.b);
316 id = e->window;
317 }
318 // unknown message type. not in the spec
319 break;
320 }
321 }
322
323
324 /*
325 void event_loop()
326 {
327 XEvent e;
328 Window cover;
329 GSList *it;
330
331 while (!exit_app) {
332 while (XPending(server.dsp)) {
333 XNextEvent(display, &e);
334
335 switch (e.type)
336 {
337 case PropertyNotify:
338 // systray window list has changed?
339 if (e.xproperty.atom == kde_systray_prop) {
340 XSelectInput(display, win, NoEventMask);
341 kde_update_icons();
342 XSelectInput(display, win, StructureNotifyMask);
343
344 while (XCheckTypedEvent(display, PropertyNotify, &e));
345 }
346
347 break;
348
349 case ConfigureNotify:
350 if (e.xany.window != win) {
351 // find the icon it pertains to and beat it into submission
352 GSList *it;
353
354 for (it = icons; it != NULL; it = g_slist_next(it)) {
355 TrayWindow *traywin = it->data;
356 if (traywin->id == e.xany.window) {
357 XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
358 icon_size, icon_size);
359 break;
360 }
361 }
362 break;
363 }
364
365 // briefly cover the entire containing window, which causes it and
366 // all of the icons to refresh their windows. finally, they update
367 // themselves when the background of the main window's parent changes.
368
369 cover = XCreateSimpleWindow(display, win, 0, 0,
370 border * 2 + width, border * 2 + height,
371 0, 0, 0);
372 XMapWindow(display, cover);
373 XDestroyWindow(display, cover);
374
375 break;
376
377 case ReparentNotify:
378 if (e.xany.window == win) // reparented to us
379 break;
380 case UnmapNotify:
381 case DestroyNotify:
382 for (it = icons; it; it = g_slist_next(it)) {
383 if (((TrayWindow*)it->data)->id == e.xany.window) {
384 icon_remove(it);
385 break;
386 }
387 }
388 break;
389
390 case ClientMessage:
391 if (e.xclient.message_type == net_opcode_atom &&
392 e.xclient.format == 32 &&
393 e.xclient.window == net_sel_win)
394 net_message(&e.xclient);
395
396 default:
397 break;
398 }
399 }
400 usleep(500000);
401 }
402
403 // remove/unparent all the icons
404 while (icons) {
405 // do the remove here explicitly, cuz the event handler isn't going to
406 // happen anymore.
407 icon_remove(icons);
408 }
409 }
410 */
411
This page took 0.050654 seconds and 5 git commands to generate.