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
, hint_win
= None
;
42 // freedesktop specification doesn't allow multi systray
48 Panel
*panel
= &panel1
[0];
49 systray
.area
.parent
= panel
;
50 systray
.area
.panel
= panel
;
51 systray
.area
._draw_foreground
= draw_systray
;
52 systray
.area
._resize
= resize_systray
;
54 if (systray
.area
.on_screen
)
55 systray
.area
.on_screen
= init_net();
57 if (!systray
.area
.on_screen
)
61 // draw only one systray (even with multi panel)
62 systray
.area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
63 systray
.area
.height
= panel
->area
.height
- (2 * systray
.area
.posy
);
64 systray
.area
.width
= 0;
66 systray
.area
.posx
= panel
->area
.width
- panel
->area
.paddingxlr
- panel
->area
.pix
.border
.width
- systray
.area
.width
;
67 if (panel
->clock
.area
.on_screen
)
68 systray
.area
.posx
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
69 if (panel
->battery
.area
.on_screen
)
70 systray
.area
.posx
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
72 systray
.area
.redraw
= 1;
76 void cleanup_systray()
78 if (systray
.list_icons
) {
81 for (it
= systray
.list_icons
; it
; it
= it
->next
)
82 remove_icon((TrayWindow
*)it
->data
);
84 g_slist_free(systray
.list_icons
);
85 systray
.list_icons
= 0;
88 free_area(&systray
.area
);
94 void draw_systray(void *obj
, cairo_t
*c
, int active
)
96 Systraybar
*sysbar
= obj
;
97 Panel
*panel
= sysbar
->area
.panel
;
102 //printf("draw_systray %d %d\n", systray.area.posx, systray.area.width);
103 icon_size
= sysbar
->area
.height
- (2 * sysbar
->area
.pix
.border
.width
) - (2 * sysbar
->area
.paddingy
);
104 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
105 traywin
= (TrayWindow
*)l
->data
;
107 // watch for the icon trying to resize itself!
108 XSelectInput(server
.dsp
, traywin
->id
, StructureNotifyMask
|ResizeRedirectMask
);
110 // position and size the icon window
111 XMoveResizeWindow(server
.dsp
, traywin
->id
, traywin
->x
, traywin
->y
, icon_size
, icon_size
);
112 // ceci intervertie les fonds : le premier icone prend le fond du dernier
113 // le dernier prend le fond de l'avant dernier, ...
114 XSetWindowBackgroundPixmap (server
.dsp
, panel
->main_win
, systray
.area
.pix
.pmap
);
116 // resize our window so that the new window can fit in it
119 // flush before clearing, otherwise the clear isn't effective.
121 // make sure the new child will get the right stuff in its background
122 // for ParentRelative.
123 XClearWindow(server
.dsp
, panel
->main_win
);
126 XMapRaised(server
.dsp
, traywin
->id
);
131 void resize_systray(void *obj
)
133 Systraybar
*sysbar
= obj
;
134 Panel
*panel
= sysbar
->area
.panel
;
137 int count
, posx
, posy
;
140 icon_size
= sysbar
->area
.height
- (2 * sysbar
->area
.pix
.border
.width
) - (2 * sysbar
->area
.paddingy
);
141 count
= g_slist_length(systray
.list_icons
);
143 if (!count
) systray
.area
.width
= 0;
144 else systray
.area
.width
= (2 * systray
.area
.pix
.border
.width
) + (2 * systray
.area
.paddingxlr
) + (icon_size
* count
) + ((count
-1) * systray
.area
.paddingx
);
146 systray
.area
.posx
= panel
->area
.width
- panel
->area
.pix
.border
.width
- panel
->area
.paddingxlr
- systray
.area
.width
;
147 if (panel
->clock
.area
.on_screen
)
148 systray
.area
.posx
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
149 if (panel
->battery
.area
.on_screen
)
150 systray
.area
.posx
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
152 systray
.area
.redraw
= 1;
154 posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingy
;
155 posx
= systray
.area
.posx
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingxlr
;
156 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
157 traywin
= (TrayWindow
*)l
->data
;
161 posx
+= (icon_size
+ systray
.area
.paddingx
);
164 // resize other objects on panel
165 printf("resize_systray %d %d\n", systray
.area
.posx
, systray
.area
.width
);
169 void create_hint_win()
172 XClassHint classhints;
173 Panel *panel = systray.area.panel;
175 hint_win = XCreateSimpleWindow(server.dsp, server.root_win, 0, 0, 1, 1, 0, 0, 0);
177 hints.flags = StateHint | WindowGroupHint | IconWindowHint;
178 hints.initial_state = WithdrawnState;
179 hints.window_group = hint_win;
180 hints.icon_window = panel->main_win;
182 classhints.res_name = "docker";
183 classhints.res_class = "Docker";
185 XSetWMProperties(server.dsp, hint_win, NULL, NULL, NULL, 0,
186 NULL, &hints, &classhints);
188 XMapWindow(server.dsp, hint_win);
194 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != None
) {
195 fprintf(stderr
, "tint2 : another systray is running\n");
201 // init systray protocol
202 net_sel_win
= XCreateSimpleWindow(server
.dsp
, server
.root_win
, -1, -1, 1, 1, 0, 0, 0);
204 // v0.2 trayer specification. tint2 always orizontal.
206 XChangeProperty(server
.dsp
, net_sel_win
, server
.atom
._NET_SYSTEM_TRAY_ORIENTATION
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &orient
, 1);
208 XSetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
, net_sel_win
, CurrentTime
);
209 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != net_sel_win
) {
210 fprintf(stderr
, "tint2 : can't get systray manager\n");
214 XClientMessageEvent ev
;
215 ev
.type
= ClientMessage
;
216 ev
.window
= server
.root_win
;
217 ev
.message_type
= server
.atom
.MANAGER
;
219 ev
.data
.l
[0] = CurrentTime
;
220 ev
.data
.l
[1] = server
.atom
._NET_SYSTEM_TRAY_SCREEN
;
221 ev
.data
.l
[2] = net_sel_win
;
224 XSendEvent(server
.dsp
, server
.root_win
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
232 if (net_sel_win
!= None
) {
233 XDestroyWindow(server
.dsp
, net_sel_win
);
243 Panel *panel = systray.area.panel;
245 // find the proper width and height
248 for (it = icons; it != NULL; it = g_slist_next(it)) {
252 XResizeWindow(server.dsp, panel->main_win, width + border * 2, height + border * 2);
257 int window_error_handler(Display
*d
, XErrorEvent
*e
)
260 if (e
->error_code
== BadWindow
) {
263 //g_printerr("X ERROR NOT BAD WINDOW!\n");
270 gboolean
icon_swallow(Window id
)
273 Panel
*panel
= systray
.area
.panel
;
276 old
= XSetErrorHandler(window_error_handler
);
277 XReparentWindow(server
.dsp
, id
, panel
->main_win
, 0, 0);
278 XSync(server
.dsp
, False
);
279 XSetErrorHandler(old
);
285 // The traywin must have its id and type set.
286 gboolean
add_icon(Window id
)
290 if (!icon_swallow(id
)) {
291 fprintf(stderr
, "tint2 : not icon_swallow\n");
295 traywin
= g_new0(TrayWindow
, 1);
298 systray
.list_icons
= g_slist_prepend(systray
.list_icons
, traywin
);
299 printf("ajout d'un icone %d (%lx)\n", g_slist_length(systray
.list_icons
), id
);
300 systray
.area
.resize
= 1;
301 systray
.area
.redraw
= 1;
303 // changed in systray force resize on panel
304 Panel
*panel
= systray
.area
.panel
;
305 panel
->area
.resize
= 1;
308 // => calcul x, y, width, height dans resize
310 // find the positon for the systray app window
311 int count = g_slist_length(icons);
312 traywin->x = border + ((width % icon_size) / 2) +
313 (count % (width / icon_size)) * icon_size;
314 traywin->y = border + ((height % icon_size) / 2) +
315 (count / (height / icon_size)) * icon_size;
317 // add the new icon to the list
318 icons = g_slist_append(icons, traywin);
325 void remove_icon(TrayWindow
*traywin
)
329 XSelectInput(server
.dsp
, traywin
->id
, NoEventMask
);
333 old
= XSetErrorHandler(window_error_handler
);
334 XReparentWindow(server
.dsp
, traywin
->id
, server
.root_win
, 0, 0);
335 XSync(server
.dsp
, False
);
336 XSetErrorHandler(old
);
338 // remove from our list
339 systray
.list_icons
= g_slist_remove(systray
.list_icons
, traywin
);
341 printf("suppression d'un icone %d\n", g_slist_length(systray
.list_icons
));
342 systray
.area
.resize
= 1;
344 // changed in systray force resize on panel
345 Panel
*panel
= systray
.area
.panel
;
346 panel
->area
.resize
= 1;
352 void net_message(XClientMessageEvent
*e
)
354 unsigned long opcode
;
357 opcode
= e
->data
.l
[1];
360 case SYSTEM_TRAY_REQUEST_DOCK
:
362 if (id
) add_icon(id
);
365 case SYSTEM_TRAY_BEGIN_MESSAGE
:
366 printf("message from dockapp\n");
370 case SYSTEM_TRAY_CANCEL_MESSAGE
:
371 printf("message cancelled\n");
376 if (opcode
== server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
) {
377 printf("message from dockapp:\n %s\n", e
->data
.b
);
380 // unknown message type. not in the spec