]> Dogcows Code - chaz/tint2/blob - src/systray/systraybar.c
basic systray with some bugs, update tintrc sample file
[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._draw_foreground = draw_systray;
54 systray.area._resize = resize_systray;
55
56 if (systray.area.on_screen) {
57 if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != None) {
58 fprintf(stderr, "tint2 : another systray is running\n");
59 systray.area.on_screen = 0;
60 }
61 }
62
63 if (systray.area.on_screen)
64 systray.area.on_screen = net_init();
65
66 if (!systray.area.on_screen)
67 return;
68
69 // configure systray
70 // draw only one systray (even with multi panel)
71 systray.area.posy = panel->area.pix.border.width + panel->area.paddingy;
72 systray.area.height = panel->area.height - (2 * systray.area.posy);
73 systray.area.width = 0;
74
75 systray.area.posx = panel->area.width - panel->area.paddingxlr - panel->area.pix.border.width - systray.area.width;
76 if (panel->clock.area.on_screen)
77 systray.area.posx -= (panel->clock.area.width + panel->area.paddingx);
78
79 systray.area.redraw = 1;
80 }
81
82
83 void cleanup_systray()
84 {
85 free_area(&systray.area);
86
87 if (net_sel_win != None) {
88 XDestroyWindow(server.dsp, net_sel_win);
89 net_sel_win = None;
90 }
91 if (systray.list_icons) {
92 g_slist_free(systray.list_icons);
93 systray.list_icons = 0;
94 }
95 }
96
97
98 void draw_systray(void *obj, cairo_t *c, int active)
99 {
100 Systraybar *sysbar = obj;
101 Panel *panel = sysbar->area.panel;
102 TrayWindow *traywin;
103 GSList *l;
104 int icon_size;
105
106 icon_size = sysbar->area.height - (2 * sysbar->area.pix.border.width) - (2 * sysbar->area.paddingy);
107 for (l = systray.list_icons; l ; l = l->next) {
108 traywin = (TrayWindow*)l->data;
109
110 printf("draw_systray %d %d\n", systray.area.posx, systray.area.width);
111 // watch for the icon trying to resize itself!
112 XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
113
114 // position and size the icon window
115 XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
116
117 // resize our window so that the new window can fit in it
118 //fix_geometry();
119
120 // flush before clearing, otherwise the clear isn't effective.
121 XFlush(server.dsp);
122 // make sure the new child will get the right stuff in its background
123 // for ParentRelative.
124 XClearWindow(server.dsp, panel->main_win);
125
126 // show the window
127 XMapRaised(server.dsp, traywin->id);
128 }
129 }
130
131
132 void resize_systray(void *obj)
133 {
134 Systraybar *sysbar = obj;
135 Panel *panel = sysbar->area.panel;
136 TrayWindow *traywin;
137 GSList *l;
138 int count, posx, posy;
139 int icon_size;
140
141 icon_size = sysbar->area.height - (2 * sysbar->area.pix.border.width) - (2 * sysbar->area.paddingy);
142 count = g_slist_length(systray.list_icons);
143
144 if (!count) systray.area.width = 0;
145 else systray.area.width = (2 * systray.area.pix.border.width) + (2 * systray.area.paddingxlr) + (icon_size * count) + ((count-1) * systray.area.paddingx);
146
147 systray.area.posx = panel->area.width - panel->area.pix.border.width - panel->area.paddingxlr - systray.area.width;
148 if (panel->clock.area.on_screen)
149 systray.area.posx -= (panel->clock.area.width + panel->area.paddingx);
150
151 systray.area.redraw = 1;
152
153 posy = panel->area.pix.border.width + panel->area.paddingy + systray.area.pix.border.width + systray.area.paddingy;
154 posx = systray.area.posx + systray.area.pix.border.width + systray.area.paddingxlr;
155 for (l = systray.list_icons; l ; l = l->next) {
156 traywin = (TrayWindow*)l->data;
157
158 traywin->y = posy;
159 traywin->x = posx;
160 posx += (icon_size + systray.area.paddingx);
161 }
162
163 // resize other objects on panel
164 printf("resize_systray %d %d\n", systray.area.posx, systray.area.width);
165 }
166
167
168 int net_init()
169 {
170 // init systray protocol
171 net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
172
173 // v0.2 trayer specification. tint2 always orizontal.
174 int orient = 0;
175 XChangeProperty(server.dsp, net_sel_win, server.atom._NET_SYSTEM_TRAY_ORIENTATION, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &orient, 1);
176
177 XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
178 if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
179 fprintf(stderr, "tint2 : can't get systray manager\n");
180 return 0;
181 }
182
183 XClientMessageEvent ev;
184 ev.type = ClientMessage;
185 ev.window = server.root_win;
186 ev.message_type = server.atom.MANAGER;
187 ev.format = 32;
188 ev.data.l[0] = CurrentTime;
189 ev.data.l[1] = server.atom._NET_SYSTEM_TRAY_SCREEN;
190 ev.data.l[2] = net_sel_win;
191 ev.data.l[3] = 0;
192 ev.data.l[4] = 0;
193 XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, (XEvent*)&ev);
194
195 return 1;
196 }
197
198
199 //Window win, root;
200 int width, height;
201 int border;
202 int icon_size;
203
204
205 void fix_geometry()
206 {
207 GSList *it;
208 Panel *panel = systray.area.panel;
209
210 // find the proper width and height
211 width = 0;
212 height = icon_size;
213 for (it = icons; it != NULL; it = g_slist_next(it)) {
214 width += icon_size;
215 }
216
217 XResizeWindow(server.dsp, panel->main_win, width + border * 2, height + border * 2);
218 }
219
220
221 gboolean error;
222 int window_error_handler(Display *d, XErrorEvent *e)
223 {
224 d=d;e=e;
225 if (e->error_code == BadWindow) {
226 error = TRUE;
227 } else {
228 //g_printerr("X ERROR NOT BAD WINDOW!\n");
229 abort();
230 }
231 return 0;
232 }
233
234
235 gboolean icon_swallow(TrayWindow *traywin)
236 {
237 XErrorHandler old;
238 Panel *panel = systray.area.panel;
239
240 error = FALSE;
241 old = XSetErrorHandler(window_error_handler);
242 XReparentWindow(server.dsp, traywin->id, panel->main_win, 0, 0);
243 XSync(server.dsp, False);
244 XSetErrorHandler(old);
245
246 return !error;
247 }
248
249
250 // The traywin must have its id and type set.
251 gboolean icon_add(Window id)
252 {
253 TrayWindow *traywin;
254
255 traywin = g_new0(TrayWindow, 1);
256 traywin->id = id;
257
258 systray.list_icons = g_slist_prepend(systray.list_icons, traywin);
259 printf("ajout d'un icone %d (%lx)\n", g_slist_length(systray.list_icons), id);
260 systray.area.resize = 1;
261 systray.area.redraw = 1;
262
263 // changed in systray force resize on panel
264 Panel *panel = systray.area.panel;
265 panel->area.resize = 1;
266 panel_refresh = 1;
267
268 if (!icon_swallow(traywin)) {
269 printf("not icon_swallow\n");
270 g_free(traywin);
271 return FALSE;
272 }
273 else
274 printf("icon_swallow\n");
275 //return TRUE;
276
277 // => calcul x, y, width, height dans resize
278 /*
279 // find the positon for the systray app window
280 int count = g_slist_length(icons);
281 traywin->x = border + ((width % icon_size) / 2) +
282 (count % (width / icon_size)) * icon_size;
283 traywin->y = border + ((height % icon_size) / 2) +
284 (count / (height / icon_size)) * icon_size;
285
286 // add the new icon to the list
287 icons = g_slist_append(icons, traywin);
288 */
289
290 return TRUE;
291 }
292
293
294 void icon_remove(TrayWindow *traywin)
295 {
296 XErrorHandler old;
297 Window win_id = traywin->id;
298
299 XSelectInput(server.dsp, traywin->id, NoEventMask);
300
301 // remove it from our list
302 systray.list_icons = g_slist_remove(systray.list_icons, traywin);
303 g_free(traywin);
304 printf("suppression d'un icone %d\n", g_slist_length(systray.list_icons));
305 systray.area.resize = 1;
306
307 // changed in systray force resize on panel
308 Panel *panel = systray.area.panel;
309 panel->area.resize = 1;
310 panel_refresh = 1;
311 return;
312
313 /*
314 // reparent it to root
315 error = FALSE;
316 old = XSetErrorHandler(window_error_handler);
317 XReparentWindow(server.dsp, win_id, root, 0, 0);
318 XSync(server.dsp, False);
319 XSetErrorHandler(old);
320
321 reposition_icons();
322 fix_geometry();
323 */
324 }
325
326
327 void net_message(XClientMessageEvent *e)
328 {
329 unsigned long opcode;
330 Window id;
331
332 opcode = e->data.l[1];
333
334 switch (opcode) {
335 case SYSTEM_TRAY_REQUEST_DOCK:
336 id = e->data.l[2];
337 if (id) icon_add(id);
338 break;
339
340 case SYSTEM_TRAY_BEGIN_MESSAGE:
341 printf("message from dockapp\n");
342 id = e->window;
343 break;
344
345 case SYSTEM_TRAY_CANCEL_MESSAGE:
346 printf("message cancelled\n");
347 id = e->window;
348 break;
349
350 default:
351 if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
352 printf("message from dockapp:\n %s\n", e->data.b);
353 id = e->window;
354 }
355 // unknown message type. not in the spec
356 break;
357 }
358 }
359
360
361 /*
362 void event_loop()
363 {
364 XEvent e;
365 Window cover;
366 GSList *it;
367
368 while (!exit_app) {
369 while (XPending(server.dsp)) {
370 XNextEvent(display, &e);
371
372 switch (e.type)
373 {
374 case PropertyNotify:
375 // systray window list has changed?
376 if (e.xproperty.atom == kde_systray_prop) {
377 XSelectInput(display, win, NoEventMask);
378 kde_update_icons();
379 XSelectInput(display, win, StructureNotifyMask);
380
381 while (XCheckTypedEvent(display, PropertyNotify, &e));
382 }
383
384 break;
385
386 case ConfigureNotify:
387 if (e.xany.window != win) {
388 // find the icon it pertains to and beat it into submission
389 GSList *it;
390
391 for (it = icons; it != NULL; it = g_slist_next(it)) {
392 TrayWindow *traywin = it->data;
393 if (traywin->id == e.xany.window) {
394 XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
395 icon_size, icon_size);
396 break;
397 }
398 }
399 break;
400 }
401
402 // briefly cover the entire containing window, which causes it and
403 // all of the icons to refresh their windows. finally, they update
404 // themselves when the background of the main window's parent changes.
405
406 cover = XCreateSimpleWindow(display, win, 0, 0,
407 border * 2 + width, border * 2 + height,
408 0, 0, 0);
409 XMapWindow(display, cover);
410 XDestroyWindow(display, cover);
411
412 break;
413
414 case ReparentNotify:
415 if (e.xany.window == win) // reparented to us
416 break;
417 case UnmapNotify:
418 case DestroyNotify:
419 for (it = icons; it; it = g_slist_next(it)) {
420 if (((TrayWindow*)it->data)->id == e.xany.window) {
421 icon_remove(it);
422 break;
423 }
424 }
425 break;
426
427 case ClientMessage:
428 if (e.xclient.message_type == net_opcode_atom &&
429 e.xclient.format == 32 &&
430 e.xclient.window == net_sel_win)
431 net_message(&e.xclient);
432
433 default:
434 break;
435 }
436 }
437 usleep(500000);
438 }
439
440 // remove/unparent all the icons
441 while (icons) {
442 // do the remove here explicitly, cuz the event handler isn't going to
443 // happen anymore.
444 icon_remove(icons);
445 }
446 }
447 */
448
This page took 0.055992 seconds and 5 git commands to generate.