1 /**************************************************************************
4 * Copyright (C) 2009 thierry lorthiois (lorthiois@bbsoft.fr)
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.
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 **************************************************************************/
20 #include <X11/Xutil.h>
21 #include <X11/Xatom.h>
28 #include "systraybar.h"
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
40 Window net_sel_win
= None
;
42 // freedesktop specification doesn't allow multi systray
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
;
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;
63 if (systray
.area
.on_screen
)
64 systray
.area
.on_screen
= net_init();
66 if (!systray
.area
.on_screen
)
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;
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
);
79 systray
.area
.redraw
= 1;
83 void cleanup_systray()
85 free_area(&systray
.area
);
87 if (net_sel_win
!= None
) {
88 XDestroyWindow(server
.dsp
, net_sel_win
);
91 if (systray
.list_icons
) {
92 g_slist_free(systray
.list_icons
);
93 systray
.list_icons
= 0;
98 void draw_systray(void *obj
, cairo_t
*c
, int active
)
100 Systraybar
*sysbar
= obj
;
101 Panel
*panel
= sysbar
->area
.panel
;
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
;
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
);
114 // position and size the icon window
115 XMoveResizeWindow(server
.dsp
, traywin
->id
, traywin
->x
, traywin
->y
, icon_size
, icon_size
);
117 // resize our window so that the new window can fit in it
120 // flush before clearing, otherwise the clear isn't effective.
122 // make sure the new child will get the right stuff in its background
123 // for ParentRelative.
124 XClearWindow(server
.dsp
, panel
->main_win
);
127 XMapRaised(server
.dsp
, traywin
->id
);
132 void resize_systray(void *obj
)
134 Systraybar
*sysbar
= obj
;
135 Panel
*panel
= sysbar
->area
.panel
;
138 int count
, posx
, posy
;
141 icon_size
= sysbar
->area
.height
- (2 * sysbar
->area
.pix
.border
.width
) - (2 * sysbar
->area
.paddingy
);
142 count
= g_slist_length(systray
.list_icons
);
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
);
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
);
151 systray
.area
.redraw
= 1;
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
;
160 posx
+= (icon_size
+ systray
.area
.paddingx
);
163 // resize other objects on panel
164 printf("resize_systray %d %d\n", systray
.area
.posx
, systray
.area
.width
);
170 // init systray protocol
171 net_sel_win
= XCreateSimpleWindow(server
.dsp
, server
.root_win
, -1, -1, 1, 1, 0, 0, 0);
173 // v0.2 trayer specification. tint2 always orizontal.
175 XChangeProperty(server
.dsp
, net_sel_win
, server
.atom
._NET_SYSTEM_TRAY_ORIENTATION
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &orient
, 1);
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");
183 XClientMessageEvent ev
;
184 ev
.type
= ClientMessage
;
185 ev
.window
= server
.root_win
;
186 ev
.message_type
= server
.atom
.MANAGER
;
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
;
193 XSendEvent(server
.dsp
, server
.root_win
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
208 Panel
*panel
= systray
.area
.panel
;
210 // find the proper width and height
213 for (it
= icons
; it
!= NULL
; it
= g_slist_next(it
)) {
217 XResizeWindow(server
.dsp
, panel
->main_win
, width
+ border
* 2, height
+ border
* 2);
222 int window_error_handler(Display
*d
, XErrorEvent
*e
)
225 if (e
->error_code
== BadWindow
) {
228 //g_printerr("X ERROR NOT BAD WINDOW!\n");
235 gboolean
icon_swallow(TrayWindow
*traywin
)
238 Panel
*panel
= systray
.area
.panel
;
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
);
250 // The traywin must have its id and type set.
251 gboolean
icon_add(Window id
)
255 traywin
= g_new0(TrayWindow
, 1);
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;
263 // changed in systray force resize on panel
264 Panel
*panel
= systray
.area
.panel
;
265 panel
->area
.resize
= 1;
268 if (!icon_swallow(traywin
)) {
269 printf("not icon_swallow\n");
274 printf("icon_swallow\n");
277 // => calcul x, y, width, height dans resize
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;
286 // add the new icon to the list
287 icons = g_slist_append(icons, traywin);
294 void icon_remove(TrayWindow
*traywin
)
297 Window win_id
= traywin
->id
;
299 XSelectInput(server
.dsp
, traywin
->id
, NoEventMask
);
301 // remove it from our list
302 systray
.list_icons
= g_slist_remove(systray
.list_icons
, traywin
);
304 printf("suppression d'un icone %d\n", g_slist_length(systray
.list_icons
));
305 systray
.area
.resize
= 1;
307 // changed in systray force resize on panel
308 Panel
*panel
= systray
.area
.panel
;
309 panel
->area
.resize
= 1;
314 // reparent it to root
316 old = XSetErrorHandler(window_error_handler);
317 XReparentWindow(server.dsp, win_id, root, 0, 0);
318 XSync(server.dsp, False);
319 XSetErrorHandler(old);
327 void net_message(XClientMessageEvent
*e
)
329 unsigned long opcode
;
332 opcode
= e
->data
.l
[1];
335 case SYSTEM_TRAY_REQUEST_DOCK
:
337 if (id
) icon_add(id
);
340 case SYSTEM_TRAY_BEGIN_MESSAGE
:
341 printf("message from dockapp\n");
345 case SYSTEM_TRAY_CANCEL_MESSAGE
:
346 printf("message cancelled\n");
351 if (opcode
== server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
) {
352 printf("message from dockapp:\n %s\n", e
->data
.b
);
355 // unknown message type. not in the spec
369 while (XPending(server.dsp)) {
370 XNextEvent(display, &e);
375 // systray window list has changed?
376 if (e.xproperty.atom == kde_systray_prop) {
377 XSelectInput(display, win, NoEventMask);
379 XSelectInput(display, win, StructureNotifyMask);
381 while (XCheckTypedEvent(display, PropertyNotify, &e));
386 case ConfigureNotify:
387 if (e.xany.window != win) {
388 // find the icon it pertains to and beat it into submission
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);
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.
406 cover = XCreateSimpleWindow(display, win, 0, 0,
407 border * 2 + width, border * 2 + height,
409 XMapWindow(display, cover);
410 XDestroyWindow(display, cover);
415 if (e.xany.window == win) // reparented to us
419 for (it = icons; it; it = g_slist_next(it)) {
420 if (((TrayWindow*)it->data)->id == e.xany.window) {
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);
440 // remove/unparent all the icons
442 // do the remove here explicitly, cuz the event handler isn't going to