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
39 Window net_sel_win
= None
;
51 for (i
=0 ; i
< nb_panel
; i
++) {
52 if (panel1
[i
].systray
.area
.visible
) {
58 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != None
) {
59 fprintf(stderr
, "tint2 warning : another systray is running\n");
65 run_systray
= net_init();
67 for (i
=0 ; i
< nb_panel
; i
++) {
69 sysbar
= &panel
->systray
;
72 sysbar
->area
.visible
= 0;
75 if (!sysbar
->area
.visible
)
78 sysbar
->area
.parent
= panel
;
79 sysbar
->area
.panel
= panel
;
81 sysbar
->area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
82 sysbar
->area
.height
= panel
->area
.height
- (2 * sysbar
->area
.posy
);
83 sysbar
->area
.width
= 100;
85 sysbar
->area
.posx
= panel
->area
.width
- panel
->area
.paddingxlr
- panel
->area
.pix
.border
.width
- sysbar
->area
.width
;
86 if (panel
->clock
.area
.visible
)
87 sysbar
->area
.posx
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
89 sysbar
->area
.redraw
= 1;
94 void cleanup_systray()
99 for (i
=0 ; i
< nb_panel
; i
++) {
101 if (!panel
->systray
.area
.visible
) continue;
103 free_area(&panel
->systray
.area
);
106 if (net_sel_win
!= None
) {
107 XDestroyWindow(server
.dsp
, net_sel_win
);
113 int resize_systray (Systraybar
*sysbar
)
130 //* find the proper width and height
133 for (it
= icons
; it
!= NULL
; it
= g_slist_next(it
)) {
137 XResizeWindow(server
.dsp
, win
, width
+ border
* 2, height
+ border
* 2);
142 int window_error_handler(Display
*d
, XErrorEvent
*e
)
145 if (e
->error_code
== BadWindow
) {
148 //g_printerr("X ERROR NOT BAD WINDOW!\n");
155 gboolean
icon_swallow(TrayWindow
*traywin
)
160 old
= XSetErrorHandler(window_error_handler
);
161 XReparentWindow(server
.dsp
, traywin
->id
, win
, 0, 0);
162 XSync(server
.dsp
, False
);
163 XSetErrorHandler(old
);
169 // The traywin must have its id and type set.
170 gboolean
icon_add(Window id
)
174 traywin
= g_new0(TrayWindow
, 1);
177 if (!icon_swallow(traywin
)) {
182 // find the positon for the systray app window
183 int count
= g_slist_length(icons
);
184 traywin
->x
= border
+ ((width
% icon_size
) / 2) +
185 (count
% (width
/ icon_size
)) * icon_size
;
186 traywin
->y
= border
+ ((height
% icon_size
) / 2) +
187 (count
/ (height
/ icon_size
)) * icon_size
;
189 // add the new icon to the list
190 icons
= g_slist_append(icons
, traywin
);
192 // watch for the icon trying to resize itself!
193 XSelectInput(server
.dsp
, traywin
->id
, StructureNotifyMask
);
195 // position and size the icon window
196 XMoveResizeWindow(server
.dsp
, traywin
->id
, traywin
->x
, traywin
->y
, icon_size
, icon_size
);
198 // resize our window so that the new window can fit in it
201 // flush before clearing, otherwise the clear isn't effective.
203 // make sure the new child will get the right stuff in its background
204 // for ParentRelative.
205 XClearWindow(server
.dsp
, win
);
208 XMapRaised(server
.dsp
, traywin
->id
);
216 // init systray protocol
217 net_sel_win
= XCreateSimpleWindow(server
.dsp
, server
.root_win
, -1, -1, 1, 1, 0, 0, 0);
219 XSetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
, net_sel_win
, CurrentTime
);
220 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != net_sel_win
) {
221 fprintf(stderr
, "tint2 warning : can't get systray manager\n");
226 m
.type
= ClientMessage
;
227 m
.xclient
.message_type
= server
.atom
.MANAGER
;
228 m
.xclient
.format
= 32;
229 m
.xclient
.data
.l
[0] = CurrentTime
;
230 m
.xclient
.data
.l
[1] = server
.atom
._NET_SYSTEM_TRAY_SCREEN
;
231 m
.xclient
.data
.l
[2] = net_sel_win
;
232 m
.xclient
.data
.l
[3] = 0;
233 m
.xclient
.data
.l
[4] = 0;
234 XSendEvent(server
.dsp
, server
.root_win
, False
, StructureNotifyMask
, &m
);
239 void net_message(XClientMessageEvent
*e
)
241 unsigned long opcode
;
244 opcode
= e
->data
.l
[1];
248 case SYSTEM_TRAY_REQUEST_DOCK
: /* dock a new icon */
250 if (id
&& icon_add(id
))
251 XSelectInput(server
.dsp
, id
, StructureNotifyMask
);
254 case SYSTEM_TRAY_BEGIN_MESSAGE
:
255 //g_printerr("Message From Dockapp\n");
259 case SYSTEM_TRAY_CANCEL_MESSAGE
:
260 //g_printerr("Message Cancelled\n");
265 if (opcode
== server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
) {
266 //g_printerr("Text For Message From Dockapp:\n%s\n", e->data.b);
271 /* unknown message type. not in the spec. */
272 //g_printerr("Warning: Received unknown client message to System Tray selection window.\n");
286 while (XPending(server.dsp)) {
287 XNextEvent(display, &e);
292 // systray window list has changed?
293 if (e.xproperty.atom == kde_systray_prop) {
294 XSelectInput(display, win, NoEventMask);
296 XSelectInput(display, win, StructureNotifyMask);
298 while (XCheckTypedEvent(display, PropertyNotify, &e));
303 case ConfigureNotify:
304 if (e.xany.window != win) {
305 // find the icon it pertains to and beat it into submission
308 for (it = icons; it != NULL; it = g_slist_next(it)) {
309 TrayWindow *traywin = it->data;
310 if (traywin->id == e.xany.window) {
311 XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
312 icon_size, icon_size);
319 // briefly cover the entire containing window, which causes it and
320 // all of the icons to refresh their windows. finally, they update
321 // themselves when the background of the main window's parent changes.
323 cover = XCreateSimpleWindow(display, win, 0, 0,
324 border * 2 + width, border * 2 + height,
326 XMapWindow(display, cover);
327 XDestroyWindow(display, cover);
332 if (e.xany.window == win) // reparented to us
336 for (it = icons; it; it = g_slist_next(it)) {
337 if (((TrayWindow*)it->data)->id == e.xany.window) {
345 if (e.xclient.message_type == net_opcode_atom &&
346 e.xclient.format == 32 &&
347 e.xclient.window == net_sel_win)
348 net_message(&e.xclient);
357 // remove/unparent all the icons
359 // do the remove here explicitly, cuz the event handler isn't going to