1 /**************************************************************************
4 * Copyright (C) 2009 thierry lorthiois (lorthiois@bbsoft.fr)
5 * based on 'docker-1.5' from Ben Jansens.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **************************************************************************/
21 #include <X11/Xutil.h>
22 #include <X11/Xatom.h>
29 #include "systraybar.h"
35 /* defined in the systray spec */
36 #define SYSTEM_TRAY_REQUEST_DOCK 0
37 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
38 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
41 Window net_sel_win
= None
, hint_win
= None
;
43 // freedesktop specification doesn't allow multi systray
50 if (systray
.area
.on_screen
)
51 systray
.area
.on_screen
= init_net();
53 if (!systray
.area
.on_screen
)
56 systray
.area
._draw_foreground
= draw_systray
;
57 systray
.area
._resize
= resize_systray
;
58 systray
.area
.resize
= 1;
59 systray
.area
.redraw
= 1;
64 void init_systray_panel(void *p
)
66 Panel
*panel
=(Panel
*)p
;
68 if (panel_horizontal
) {
69 systray
.area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
70 systray
.area
.height
= panel
->area
.height
- (2 * systray
.area
.posy
);
73 systray
.area
.posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
74 systray
.area
.width
= panel
->area
.width
- (2 * panel
->area
.pix
.border
.width
) - (2 * panel
->area
.paddingy
);
76 systray
.area
.parent
= p
;
77 systray
.area
.panel
= p
;
81 void cleanup_systray()
83 if (systray
.list_icons
) {
84 // remove_icon change systray.list_icons
85 while(systray
.list_icons
)
86 remove_icon((TrayWindow
*)systray
.list_icons
->data
);
88 g_slist_free(systray
.list_icons
);
89 systray
.list_icons
= 0;
92 free_area(&systray
.area
);
97 void draw_systray(void *obj
, cairo_t
*c
, int active
)
99 // tint2 don't draw systray icons. just the background.
104 void resize_systray(void *obj
)
106 Systraybar
*sysbar
= obj
;
107 Panel
*panel
= sysbar
->area
.panel
;
110 int count
, posx
, posy
;
113 if (panel_horizontal
)
114 icon_size
= sysbar
->area
.height
;
116 icon_size
= sysbar
->area
.width
;
117 icon_size
= icon_size
- (2 * sysbar
->area
.pix
.border
.width
) - (2 * sysbar
->area
.paddingy
);
119 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
120 if (!((TrayWindow
*)l
->data
)->hide
)
123 //printf("count %d\n", count);
125 if (panel_horizontal
) {
126 if (!count
) systray
.area
.width
= 0;
127 else systray
.area
.width
= (2 * systray
.area
.pix
.border
.width
) + (2 * systray
.area
.paddingxlr
) + (icon_size
* count
) + ((count
-1) * systray
.area
.paddingx
);
129 systray
.area
.posx
= panel
->area
.width
- panel
->area
.pix
.border
.width
- panel
->area
.paddingxlr
- systray
.area
.width
;
130 if (panel
->clock
.area
.on_screen
)
131 systray
.area
.posx
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
132 #ifdef ENABLE_BATTERY
133 if (panel
->battery
.area
.on_screen
)
134 systray
.area
.posx
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
138 if (!count
) systray
.area
.height
= 0;
139 else systray
.area
.height
= (2 * systray
.area
.pix
.border
.width
) + (2 * systray
.area
.paddingxlr
) + (icon_size
* count
) + ((count
-1) * systray
.area
.paddingx
);
141 systray
.area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingxlr
;
142 if (panel
->clock
.area
.on_screen
)
143 systray
.area
.posy
+= (panel
->clock
.area
.height
+ panel
->area
.paddingx
);
144 #ifdef ENABLE_BATTERY
145 if (panel
->battery
.area
.on_screen
)
146 systray
.area
.posy
+= (panel
->battery
.area
.height
+ panel
->area
.paddingx
);
150 if (panel_horizontal
) {
151 posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingy
;
152 posx
= systray
.area
.posx
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingxlr
;
155 posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingy
;
156 posy
= systray
.area
.posy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingxlr
;
158 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
159 traywin
= (TrayWindow
*)l
->data
;
160 if (traywin
->hide
) continue;
164 traywin
->width
= icon_size
;
165 traywin
->height
= icon_size
;
166 if (panel_horizontal
)
167 posx
+= (icon_size
+ systray
.area
.paddingx
);
169 posy
+= (icon_size
+ systray
.area
.paddingx
);
171 // position and size the icon window
172 XMoveResizeWindow(server
.dsp
, traywin
->id
, traywin
->x
, traywin
->y
, icon_size
, icon_size
);
177 // ***********************************************
182 Window win
= XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
);
184 // freedesktop systray specification
187 Atom _NET_WM_PID
, actual_type
;
189 unsigned long nitems
;
190 unsigned long bytes_after
;
191 unsigned char *prop
= 0;
194 _NET_WM_PID
= XInternAtom(server
.dsp
, "_NET_WM_PID", True
);
195 int ret
= XGetWindowProperty(server
.dsp
, win
, _NET_WM_PID
, 0, 1024, False
, AnyPropertyType
, &actual_type
, &actual_format
, &nitems
, &bytes_after
, &prop
);
197 fprintf(stderr
, "tint2 : another systray is running");
198 if (ret
== Success
&& prop
) {
201 fprintf(stderr
, " pid=%d", pid
);
203 fprintf(stderr
, "\n");
207 // init systray protocol
208 net_sel_win
= XCreateSimpleWindow(server
.dsp
, server
.root_win
, -1, -1, 1, 1, 0, 0, 0);
210 // v0.2 trayer specification. tint2 always horizontal.
211 // Vertical panel will draw the systray horizontal.
213 XChangeProperty(server
.dsp
, net_sel_win
, server
.atom
._NET_SYSTEM_TRAY_ORIENTATION
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &orient
, 1);
215 XSetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
, net_sel_win
, CurrentTime
);
216 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != net_sel_win
) {
217 fprintf(stderr
, "tint2 : can't get systray manager\n");
221 XClientMessageEvent ev
;
222 ev
.type
= ClientMessage
;
223 ev
.window
= server
.root_win
;
224 ev
.message_type
= server
.atom
.MANAGER
;
226 ev
.data
.l
[0] = CurrentTime
;
227 ev
.data
.l
[1] = server
.atom
._NET_SYSTEM_TRAY_SCREEN
;
228 ev
.data
.l
[2] = net_sel_win
;
231 XSendEvent(server
.dsp
, server
.root_win
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
238 if (net_sel_win
!= None
) {
239 XDestroyWindow(server
.dsp
, net_sel_win
);
246 int window_error_handler(Display
*d
, XErrorEvent
*e
)
250 if (e
->error_code
!= BadWindow
) {
251 printf("error_handler %d\n", e
->error_code
);
257 static gint
compare_traywindows(gconstpointer a
, gconstpointer b
)
259 const TrayWindow
* traywin_a
= (TrayWindow
*)a
;
260 const TrayWindow
* traywin_b
= (TrayWindow
*)b
;
261 XTextProperty name_a
, name_b
;
263 if(XGetWMName(server
.dsp
, traywin_a
->id
, &name_a
) == 0) {
266 else if(XGetWMName(server
.dsp
, traywin_b
->id
, &name_b
) == 0) {
271 gint retval
= g_ascii_strncasecmp((char*)name_a
.value
, (char*)name_b
.value
, -1) * systray
.sort
;
279 gboolean
add_icon(Window id
)
283 Panel
*panel
= systray
.area
.panel
;
287 old
= XSetErrorHandler(window_error_handler
);
288 XReparentWindow(server
.dsp
, id
, panel
->main_win
, 0, 0);
289 XSync(server
.dsp
, False
);
290 XSetErrorHandler(old
);
291 if (error
!= FALSE
) {
292 fprintf(stderr
, "tint2 : not icon_swallow\n");
299 unsigned long nbitem
, bytes
;
300 unsigned char *data
= 0;
303 ret
= XGetWindowProperty(server
.dsp
, id
, server
.atom
._XEMBED_INFO
, 0, 2, False
, server
.atom
._XEMBED_INFO
, &acttype
, &actfmt
, &nbitem
, &bytes
, &data
);
304 if (ret
== Success
) {
307 //hide = ((data[1] & XEMBED_MAPPED) == 0);
308 //printf("hide %d\n", hide);
314 fprintf(stderr
, "tint2 : xembed error\n");
320 e
.xclient
.type
= ClientMessage
;
321 e
.xclient
.serial
= 0;
322 e
.xclient
.send_event
= True
;
323 e
.xclient
.message_type
= server
.atom
._XEMBED
;
324 e
.xclient
.window
= id
;
325 e
.xclient
.format
= 32;
326 e
.xclient
.data
.l
[0] = CurrentTime
;
327 e
.xclient
.data
.l
[1] = XEMBED_EMBEDDED_NOTIFY
;
328 e
.xclient
.data
.l
[2] = 0;
329 e
.xclient
.data
.l
[3] = panel
->main_win
;
330 e
.xclient
.data
.l
[4] = 0;
331 XSendEvent(server
.dsp
, id
, False
, 0xFFFFFF, &e
);
334 traywin
= g_new0(TrayWindow
, 1);
336 traywin
->hide
= hide
;
338 if (systray
.sort
== 3)
339 systray
.list_icons
= g_slist_prepend(systray
.list_icons
, traywin
);
340 else if (systray
.sort
== 2)
341 systray
.list_icons
= g_slist_append(systray
.list_icons
, traywin
);
343 systray
.list_icons
= g_slist_insert_sorted(systray
.list_icons
, traywin
, compare_traywindows
);
344 systray
.area
.resize
= 1;
345 systray
.area
.redraw
= 1;
346 //printf("add_icon id %lx, %d\n", id, g_slist_length(systray.list_icons));
348 // watch for the icon trying to resize itself!
349 XSelectInput(server
.dsp
, traywin
->id
, StructureNotifyMask
);
353 XMapRaised(server
.dsp
, traywin
->id
);
355 // changed in systray force resize on panel
356 panel
->area
.resize
= 1;
362 void remove_icon(TrayWindow
*traywin
)
366 // remove from our list
367 systray
.list_icons
= g_slist_remove(systray
.list_icons
, traywin
);
369 systray
.area
.resize
= 1;
370 systray
.area
.redraw
= 1;
371 //printf("remove_icon id %lx, %d\n", traywin->id);
373 XSelectInput(server
.dsp
, traywin
->id
, NoEventMask
);
377 old
= XSetErrorHandler(window_error_handler
);
379 XUnmapWindow(server
.dsp
, traywin
->id
);
380 XReparentWindow(server
.dsp
, traywin
->id
, server
.root_win
, 0, 0);
381 XSync(server
.dsp
, False
);
382 XSetErrorHandler(old
);
384 // changed in systray force resize on panel
385 Panel
*panel
= systray
.area
.panel
;
386 panel
->area
.resize
= 1;
391 void net_message(XClientMessageEvent
*e
)
393 unsigned long opcode
;
396 opcode
= e
->data
.l
[1];
398 case SYSTEM_TRAY_REQUEST_DOCK
:
400 if (id
) add_icon(id
);
403 case SYSTEM_TRAY_BEGIN_MESSAGE
:
404 case SYSTEM_TRAY_CANCEL_MESSAGE
:
405 // we don't show baloons messages.
409 if (opcode
== server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
)
410 printf("message from dockapp: %s\n", e
->data
.b
);
412 fprintf(stderr
, "SYSTEM_TRAY : unknown message type\n");
418 void refresh_systray_icon()
422 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
423 traywin
= (TrayWindow
*)l
->data
;
424 if (traywin
->hide
) continue;
425 XClearArea(server
.dsp
, traywin
->id
, 0, 0, traywin
->width
, traywin
->height
, True
);