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>
28 #include <X11/extensions/Xdamage.h>
29 #include <X11/extensions/Xrender.h>
30 #include <X11/extensions/Xcomposite.h>
32 #include "systraybar.h"
38 /* defined in the systray spec */
39 #define SYSTEM_TRAY_REQUEST_DOCK 0
40 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
41 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
44 Window net_sel_win
= None
, hint_win
= None
;
46 // freedesktop specification doesn't allow multi systray
59 systray
.area
._draw_foreground
= draw_systray
;
60 systray
.area
._resize
= resize_systray
;
61 systray
.area
.resize
= 1;
62 systray
.area
.redraw
= 1;
63 systray
.area
.on_screen
= 1;
68 void init_systray_panel(void *p
)
70 Panel
*panel
=(Panel
*)p
;
72 if (panel_horizontal
) {
73 systray
.area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
74 systray
.area
.height
= panel
->area
.height
- (2 * systray
.area
.posy
);
77 systray
.area
.posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
;
78 systray
.area
.width
= panel
->area
.width
- (2 * panel
->area
.pix
.border
.width
) - (2 * panel
->area
.paddingy
);
80 systray
.area
.parent
= p
;
81 systray
.area
.panel
= p
;
85 void cleanup_systray()
88 systray
.area
.on_screen
= 0;
89 free_area(&systray
.area
);
93 void draw_systray(void *obj
, cairo_t
*c
, int active
)
95 // tint2 don't draw systray icons. just the background.
100 void resize_systray(void *obj
)
102 Systraybar
*sysbar
= obj
;
103 Panel
*panel
= sysbar
->area
.panel
;
106 int count
, posx
, posy
;
109 if (panel_horizontal
)
110 icon_size
= sysbar
->area
.height
;
112 icon_size
= sysbar
->area
.width
;
113 icon_size
= icon_size
- (2 * sysbar
->area
.pix
.border
.width
) - (2 * sysbar
->area
.paddingy
);
115 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
116 if (!((TrayWindow
*)l
->data
)->hide
)
119 //printf("count %d\n", count);
121 if (panel_horizontal
) {
122 if (!count
) systray
.area
.width
= 0;
123 else systray
.area
.width
= (2 * systray
.area
.pix
.border
.width
) + (2 * systray
.area
.paddingxlr
) + (icon_size
* count
) + ((count
-1) * systray
.area
.paddingx
);
125 systray
.area
.posx
= panel
->area
.width
- panel
->area
.pix
.border
.width
- panel
->area
.paddingxlr
- systray
.area
.width
;
126 if (panel
->clock
.area
.on_screen
)
127 systray
.area
.posx
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
128 #ifdef ENABLE_BATTERY
129 if (panel
->battery
.area
.on_screen
)
130 systray
.area
.posx
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
134 if (!count
) systray
.area
.height
= 0;
135 else systray
.area
.height
= (2 * systray
.area
.pix
.border
.width
) + (2 * systray
.area
.paddingxlr
) + (icon_size
* count
) + ((count
-1) * systray
.area
.paddingx
);
137 systray
.area
.posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingxlr
;
138 if (panel
->clock
.area
.on_screen
)
139 systray
.area
.posy
+= (panel
->clock
.area
.height
+ panel
->area
.paddingx
);
140 #ifdef ENABLE_BATTERY
141 if (panel
->battery
.area
.on_screen
)
142 systray
.area
.posy
+= (panel
->battery
.area
.height
+ panel
->area
.paddingx
);
146 if (panel_horizontal
) {
147 posy
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingy
;
148 posx
= systray
.area
.posx
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingxlr
;
151 posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingy
;
152 posy
= systray
.area
.posy
+ systray
.area
.pix
.border
.width
+ systray
.area
.paddingxlr
;
154 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
155 traywin
= (TrayWindow
*)l
->data
;
156 if (traywin
->hide
) continue;
160 traywin
->width
= icon_size
;
161 traywin
->height
= icon_size
;
162 if (panel_horizontal
)
163 posx
+= (icon_size
+ systray
.area
.paddingx
);
165 posy
+= (icon_size
+ systray
.area
.paddingx
);
167 // position and size the icon window
168 XMoveResizeWindow(server
.dsp
, traywin
->id
, traywin
->x
, traywin
->y
, icon_size
, icon_size
);
169 XResizeWindow(server
.dsp
, traywin
->tray_id
, icon_size
, icon_size
);
174 // ***********************************************
180 // protocol already started
181 if (!systray_enabled
)
186 if (!systray_enabled
)
189 Window win
= XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
);
191 // freedesktop systray specification
194 Atom _NET_WM_PID
, actual_type
;
196 unsigned long nitems
;
197 unsigned long bytes_after
;
198 unsigned char *prop
= 0;
201 _NET_WM_PID
= XInternAtom(server
.dsp
, "_NET_WM_PID", True
);
202 int ret
= XGetWindowProperty(server
.dsp
, win
, _NET_WM_PID
, 0, 1024, False
, AnyPropertyType
, &actual_type
, &actual_format
, &nitems
, &bytes_after
, &prop
);
204 fprintf(stderr
, "tint2 : another systray is running");
205 if (ret
== Success
&& prop
) {
208 fprintf(stderr
, " pid=%d", pid
);
210 fprintf(stderr
, "\n");
214 // init systray protocol
215 net_sel_win
= XCreateSimpleWindow(server
.dsp
, server
.root_win
, -1, -1, 1, 1, 0, 0, 0);
217 // v0.2 trayer specification. tint2 always horizontal.
218 // Vertical panel will draw the systray horizontal.
220 XChangeProperty(server
.dsp
, net_sel_win
, server
.atom
._NET_SYSTEM_TRAY_ORIENTATION
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &orient
, 1);
221 VisualID vid
= XVisualIDFromVisual(server
.visual
);
222 XChangeProperty(server
.dsp
, net_sel_win
, XInternAtom(server
.dsp
, "_NET_SYSTEM_TRAY_VISUAL", False
), XA_VISUALID
, 32, PropModeReplace
, (unsigned char*)&vid
, 1);
224 XSetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
, net_sel_win
, CurrentTime
);
225 if (XGetSelectionOwner(server
.dsp
, server
.atom
._NET_SYSTEM_TRAY_SCREEN
) != net_sel_win
) {
227 fprintf(stderr
, "tint2 : can't get systray manager\n");
231 //fprintf(stderr, "tint2 : systray started\n");
232 XClientMessageEvent ev
;
233 ev
.type
= ClientMessage
;
234 ev
.window
= server
.root_win
;
235 ev
.message_type
= server
.atom
.MANAGER
;
237 ev
.data
.l
[0] = CurrentTime
;
238 ev
.data
.l
[1] = server
.atom
._NET_SYSTEM_TRAY_SCREEN
;
239 ev
.data
.l
[2] = net_sel_win
;
242 XSendEvent(server
.dsp
, server
.root_win
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
248 //fprintf(stderr, "tint2 : systray stopped\n");
249 if (systray
.list_icons
) {
250 // remove_icon change systray.list_icons
251 while(systray
.list_icons
)
252 remove_icon((TrayWindow
*)systray
.list_icons
->data
);
254 g_slist_free(systray
.list_icons
);
255 systray
.list_icons
= 0;
258 if (net_sel_win
!= None
) {
259 XDestroyWindow(server
.dsp
, net_sel_win
);
266 int window_error_handler(Display
*d
, XErrorEvent
*e
)
270 if (e
->error_code
!= BadWindow
) {
271 printf("error_handler %d\n", e
->error_code
);
277 static gint
compare_traywindows(gconstpointer a
, gconstpointer b
)
279 const TrayWindow
* traywin_a
= (TrayWindow
*)a
;
280 const TrayWindow
* traywin_b
= (TrayWindow
*)b
;
281 XTextProperty name_a
, name_b
;
283 if(XGetWMName(server
.dsp
, traywin_a
->tray_id
, &name_a
) == 0) {
286 else if(XGetWMName(server
.dsp
, traywin_b
->tray_id
, &name_b
) == 0) {
291 gint retval
= g_ascii_strncasecmp((char*)name_a
.value
, (char*)name_b
.value
, -1) * systray
.sort
;
299 gboolean
add_icon(Window id
)
303 Panel
*panel
= systray
.area
.panel
;
307 int wrong_format
= 0;
308 old
= XSetErrorHandler(window_error_handler
);
309 XWindowAttributes attr
;
310 XGetWindowAttributes(server
.dsp
, id
, &attr
);
311 XSetWindowAttributes set_attr
;
312 wrong_format
= (attr
.depth
!= server
.depth
);
313 set_attr
.colormap
= attr
.colormap
;
314 set_attr
.background_pixel
= 0;
315 set_attr
.border_pixel
= 0;
316 unsigned long mask
= CWColormap
|CWBackPixel
|CWBorderPixel
;
317 Window parent_window
;
318 if (real_transparency
)
319 parent_window
= XCreateWindow(server
.dsp
, panel
->main_win
, 0, 0, 30, 30, 0, attr
.depth
, InputOutput
, attr
.visual
, mask
, &set_attr
);
321 parent_window
= panel
->main_win
;
322 XReparentWindow(server
.dsp
, id
, parent_window
, 0, 0);
323 XSync(server
.dsp
, False
);
324 XSetErrorHandler(old
);
325 if (error
!= FALSE
) {
326 fprintf(stderr
, "tint2 : not icon_swallow\n");
333 unsigned long nbitem
, bytes
;
334 unsigned char *data
= 0;
337 ret
= XGetWindowProperty(server
.dsp
, id
, server
.atom
._XEMBED_INFO
, 0, 2, False
, server
.atom
._XEMBED_INFO
, &acttype
, &actfmt
, &nbitem
, &bytes
, &data
);
338 if (ret
== Success
) {
341 //hide = ((data[1] & XEMBED_MAPPED) == 0);
342 //printf("hide %d\n", hide);
348 fprintf(stderr
, "tint2 : xembed error\n");
354 e
.xclient
.type
= ClientMessage
;
355 e
.xclient
.serial
= 0;
356 e
.xclient
.send_event
= True
;
357 e
.xclient
.message_type
= server
.atom
._XEMBED
;
358 e
.xclient
.window
= id
;
359 e
.xclient
.format
= 32;
360 e
.xclient
.data
.l
[0] = CurrentTime
;
361 e
.xclient
.data
.l
[1] = XEMBED_EMBEDDED_NOTIFY
;
362 e
.xclient
.data
.l
[2] = 0;
363 e
.xclient
.data
.l
[3] = parent_window
;
364 e
.xclient
.data
.l
[4] = 0;
365 XSendEvent(server
.dsp
, id
, False
, 0xFFFFFF, &e
);
368 traywin
= g_new0(TrayWindow
, 1);
369 if (real_transparency
)
370 traywin
->id
= parent_window
;
373 traywin
->tray_id
= id
;
374 traywin
->hide
= hide
;
375 traywin
->wrong_format
= wrong_format
;
377 if (systray
.sort
== 3)
378 systray
.list_icons
= g_slist_prepend(systray
.list_icons
, traywin
);
379 else if (systray
.sort
== 2)
380 systray
.list_icons
= g_slist_append(systray
.list_icons
, traywin
);
382 systray
.list_icons
= g_slist_insert_sorted(systray
.list_icons
, traywin
, compare_traywindows
);
383 systray
.area
.resize
= 1;
384 systray
.area
.redraw
= 1;
385 //printf("add_icon id %lx, %d\n", id, g_slist_length(systray.list_icons));
387 // watch for the icon trying to resize itself!
388 XSelectInput(server
.dsp
, traywin
->tray_id
, StructureNotifyMask
);
389 if (real_transparency
) {
390 XDamageCreate(server
.dsp
, traywin
->id
, XDamageReportRawRectangles
);
391 XCompositeRedirectWindow(server
.dsp
, traywin
->id
, CompositeRedirectManual
);
395 if (!traywin
->hide
) {
396 XMapRaised(server
.dsp
, traywin
->id
);
397 XMapRaised(server
.dsp
, traywin
->tray_id
);
400 // changed in systray force resize on panel
401 panel
->area
.resize
= 1;
407 void remove_icon(TrayWindow
*traywin
)
411 // remove from our list
412 systray
.list_icons
= g_slist_remove(systray
.list_icons
, traywin
);
413 systray
.area
.resize
= 1;
414 systray
.area
.redraw
= 1;
415 //printf("remove_icon id %lx, %d\n", traywin->id);
417 XSelectInput(server
.dsp
, traywin
->tray_id
, NoEventMask
);
421 old
= XSetErrorHandler(window_error_handler
);
423 XUnmapWindow(server
.dsp
, traywin
->id
);
424 XReparentWindow(server
.dsp
, traywin
->tray_id
, server
.root_win
, 0, 0);
425 if (traywin
->id
!= traywin
->tray_id
)
426 XDestroyWindow(server
.dsp
, traywin
->id
);
427 XSync(server
.dsp
, False
);
428 XSetErrorHandler(old
);
431 // changed in systray force resize on panel
432 Panel
*panel
= systray
.area
.panel
;
433 panel
->area
.resize
= 1;
438 void net_message(XClientMessageEvent
*e
)
440 unsigned long opcode
;
443 opcode
= e
->data
.l
[1];
445 case SYSTEM_TRAY_REQUEST_DOCK
:
447 if (id
) add_icon(id
);
450 case SYSTEM_TRAY_BEGIN_MESSAGE
:
451 case SYSTEM_TRAY_CANCEL_MESSAGE
:
452 // we don't show baloons messages.
456 if (opcode
== server
.atom
._NET_SYSTEM_TRAY_MESSAGE_DATA
)
457 printf("message from dockapp: %s\n", e
->data
.b
);
459 fprintf(stderr
, "SYSTEM_TRAY : unknown message type\n");
465 void systray_render_icons(TrayWindow
* traywin
)
467 // most systray icons support 32 bit depth, but some icons are still 24 bit.
468 // We create a heuristic mask for these icons, i.e. we get the rgb value in the top left corner, and
469 // mask out all pixel with the same rgb value
471 Picture picture_systray
, picture_tray
, picture_panel
;
472 Drawable mask
, tray_pixmap
;
473 Panel
* panel
= systray
.area
.panel
;
474 XWindowAttributes attr
;
475 XGetWindowAttributes(server
.dsp
, traywin
->id
, &attr
);
476 XRenderPictFormat
*format
= XRenderFindVisualFormat(server
.dsp
, attr
.visual
);
477 XRenderPictFormat
*panel_format
= XRenderFindVisualFormat(server
.dsp
, server
.visual
);
478 if (traywin
->wrong_format
) {
479 imlib_context_set_drawable(traywin
->id
);
480 Imlib_Image image
= imlib_create_image_from_drawable(0, 0, 0, traywin
->width
, traywin
->height
, 0);
481 imlib_context_set_image(image
);
482 imlib_image_set_has_alpha(1);
483 DATA32
* data
= imlib_image_get_data();
484 createHeuristicMask(data
, traywin
->width
, traywin
->height
);
485 imlib_image_put_back_data(data
);
486 imlib_render_pixmaps_for_whole_image(&tray_pixmap
, &mask
);
487 picture_tray
= XRenderCreatePicture( server
.dsp
, tray_pixmap
, panel_format
, 0, 0);
488 Picture mask2
= XRenderCreatePicture( server
.dsp
, mask
, XRenderFindStandardFormat(server
.dsp
, PictStandardA1
), 0, 0);
489 picture_systray
= XRenderCreatePicture( server
.dsp
, systray
.area
.pix
.pmap
, panel_format
, 0, 0);
490 picture_panel
= XRenderCreatePicture(server
.dsp
, panel
->main_win
, panel_format
, 0, 0);
491 XRenderComposite(server
.dsp
, PictOpOver
, picture_tray
, mask2
, picture_systray
, 0, 0, 0, 0, traywin
->x
-systray
.area
.posx
, traywin
->y
-systray
.area
.posy
, traywin
->width
, traywin
->height
);
492 XRenderComposite(server
.dsp
, PictOpOver
, picture_tray
, mask2
, picture_panel
, 0, 0, 0, 0, traywin
->x
, traywin
->y
, traywin
->width
, traywin
->height
);
493 imlib_free_pixmap_and_mask(tray_pixmap
);
497 picture_tray
= XRenderCreatePicture( server
.dsp
, traywin
->id
, format
, 0, 0);
498 picture_systray
= XRenderCreatePicture( server
.dsp
, systray
.area
.pix
.pmap
, panel_format
, 0, 0);
499 picture_panel
= XRenderCreatePicture(server
.dsp
, panel
->main_win
, panel_format
, 0, 0);
500 XRenderComposite(server
.dsp
, PictOpOver
, picture_tray
, None
, picture_systray
, 0, 0, 0, 0, traywin
->x
-systray
.area
.posx
, traywin
->y
-systray
.area
.posy
, traywin
->width
, traywin
->height
);
501 XRenderComposite(server
.dsp
, PictOpOver
, picture_tray
, None
, picture_panel
, 0, 0, 0, 0, traywin
->x
, traywin
->y
, traywin
->width
, traywin
->height
);
503 XRenderFreePicture(server
.dsp
, picture_systray
);
504 XRenderFreePicture(server
.dsp
, picture_tray
);
505 XRenderFreePicture(server
.dsp
, picture_panel
);
509 void refresh_systray_icon()
513 for (l
= systray
.list_icons
; l
; l
= l
->next
) {
514 traywin
= (TrayWindow
*)l
->data
;
515 if (traywin
->hide
) continue;
516 if (real_transparency
) systray_render_icons(traywin
);
517 else XClearArea(server
.dsp
, traywin
->id
, 0, 0, traywin
->width
, traywin
->height
, False
);
519 if (real_transparency
)