1 /* ************************************
2 * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax
3 * tray.c
4 * Tue, 07 Mar 2006 10:36:10 +0600
5 * ************************************
6 * tray functions
7 * ************************************/
8
9 #include "config.h"
10
11 #include <X11/Xmd.h>
12 #include <X11/Xlib.h>
13 #include <X11/Xutil.h>
14 #include <X11/Xatom.h>
15
16 #ifdef XPM_SUPPORTED
17 #include <X11/xpm.h>
18 #endif
19
20 #include <unistd.h>
21
22 #include "common.h"
23 #include "tray.h"
24 #include "settings.h"
25 #include "xutils.h"
26 #include "wmh.h"
27 #include "embed.h"
28 #include "image.h"
29 #include "icons.h"
30
31 #ifndef NO_NATIVE_KDE
32 #include "kde_tray.h"
33 #endif
34
35 #include "debug.h"
36
tray_init()37 void tray_init()
38 {
39 tray_data.tray = None;
40 tray_data.hint_win = None;
41 tray_data.dpy = NULL;
42 tray_data.terminated = False;
43 tray_data.bg_pmap = None;
44 tray_data.xa_xrootpmap_id = None;
45 tray_data.xa_xsetroot_id = None;
46 tray_data.kde_tray_old_mode = 0;
47 scrollbars_init();
48 }
49
50 #ifdef XPM_SUPPORTED
tray_init_pixmap_bg()51 int tray_init_pixmap_bg()
52 {
53 XpmAttributes xpma;
54 Pixmap mask = None;
55 int rc;
56 xpma.valuemask = 0;
57 rc = XpmReadFileToPixmap(
58 tray_data.dpy,
59 tray_data.tray,
60 settings.bg_pmap_path,
61 &tray_data.bg_pmap,
62 &mask,
63 &xpma);
64 if (rc != XpmSuccess) {
65 DIE(("Could not read background pixmap from %s.\n", settings.bg_pmap_path));
66 return FAILURE;
67 }
68 /* Ignore the mask */
69 if (mask != None) XFreePixmap(tray_data.dpy, mask);
70 tray_data.bg_pmap_dims.x = xpma.height;
71 tray_data.bg_pmap_dims.y = xpma.width;
72 LOG_TRACE(("created background pixmap (%dx%d)\n", xpma.width, xpma.height));
73 return SUCCESS;
74 }
75 #endif
76
77 /* Mostly from FVWM:fvwm/colorset.c:172 */
tray_get_root_pixmap(Atom prop)78 Pixmap tray_get_root_pixmap(Atom prop)
79 {
80 Atom type;
81 int format;
82 unsigned long length, after;
83 unsigned char *reteval = NULL;
84 int ret;
85 Pixmap pix = None;
86 Window root_window = XRootWindow(tray_data.dpy, DefaultScreen(tray_data.dpy));
87 ret = XGetWindowProperty(tray_data.dpy,
88 root_window,
89 prop,
90 0L,
91 1L,
92 False,
93 XA_PIXMAP,
94 &type,
95 &format,
96 &length,
97 &after,
98 &reteval);
99 if (x11_ok() && ret == Success && type == XA_PIXMAP && format == 32 && length == 1 && after == 0)
100 pix = (Pixmap)(*(long *)reteval);
101 if (reteval) XFree(reteval);
102 return pix;
103 }
104
105 /* Originally from FVWM:fvwm/colorset.c:195 */
tray_update_root_bg_pmap(Pixmap * pmap,int * width,int * height)106 int tray_update_root_bg_pmap(Pixmap *pmap, int *width, int *height)
107 {
108 unsigned int w = 0, h = 0;
109 int rc = 0;
110 XID dummy;
111 Pixmap pix = None;
112 /* Retrive root image pixmap */
113 /* Try _XROOTPMAP_ID first */
114 if (tray_data.xa_xrootpmap_id != None)
115 pix = tray_get_root_pixmap(tray_data.xa_xrootpmap_id);
116 /* Else try _XSETROOT_ID */
117 if (!pix && tray_data.xa_xsetroot_id != None)
118 pix = tray_get_root_pixmap(tray_data.xa_xsetroot_id);
119 /* Get root pixmap size */
120 if (pix)
121 rc = XGetGeometry(tray_data.dpy,
122 pix,
123 &dummy,
124 (int *)&dummy,
125 (int *)&dummy,
126 &w,
127 &h,
128 (unsigned int *)&dummy,
129 (unsigned int *)&dummy);
130 if (!x11_ok() || !pix || !rc) {
131 LOG_TRACE(("could not update root background pixmap\n"));
132 return FAILURE;
133 }
134 *pmap = pix;
135 if (width != NULL) *width = w;
136 if (height != NULL) *height = h;
137 LOG_TRACE(("got new root pixmap: 0x%lx (%ix%i)\n", pix, w, h));
138 return SUCCESS;
139 }
140
141 /* XXX: most of Pixmaps are not really needed. Use XImages instead.
142 * GCs, XImages and Pixmaps stay allocated during cleanup, valgrind
143 * complains. Who cares?
144 * TODO: move pixmaps/GCs into tray_data and free them during the exit. */
tray_update_bg(int update_pixmap)145 int tray_update_bg(int update_pixmap)
146 {
147 static Pixmap root_pmap = None;
148 static Pixmap bg_pmap = None;
149 static Pixmap final_pmap = None;
150 static GC root_gc = None;
151 static GC bg_gc = None;
152 static GC final_gc = None;
153 static int old_width = -1, old_height = -1;
154 struct Rect clr; /* clipping rectangle */
155 struct Rect clr_loc; /* clipping rectangle in local coords */
156 XImage *bg_img;
157 int bg_pmap_updated = False;
158 #define make_gc(pmap,gc) do { XGCValues gcv; \
159 if (gc != None) XFreeGC(tray_data.dpy, gc); \
160 gcv.graphics_exposures = False; \
161 gc = XCreateGC(tray_data.dpy, pmap, GCGraphicsExposures, &gcv); \
162 } while(0)
163 #define make_pmap(pmap) do { \
164 if (pmap != None) XFreePixmap(tray_data.dpy, pmap); \
165 pmap = XCreatePixmap(tray_data.dpy, tray_data.tray, \
166 tray_data.xsh.width, tray_data.xsh.height, \
167 DefaultDepth(tray_data.dpy, DefaultScreen(tray_data.dpy))); \
168 } while(0)
169 #define recreate_pixmap(pmap,gc) do { \
170 make_pmap(pmap); \
171 make_gc(pmap, gc); \
172 } while(0)
173 /* No need to update background if it is a color one */
174 if (!settings.transparent && !settings.pixmap_bg) return SUCCESS;
175 /* Calculate clipping rect */
176 clr.x = cutoff(tray_data.xsh.x, 0, tray_data.root_wnd.width);
177 clr.y = cutoff(tray_data.xsh.y, 0, tray_data.root_wnd.height);
178 clr.w = cutoff(tray_data.xsh.x + tray_data.xsh.width, 0, tray_data.root_wnd.width) - clr.x;
179 clr.h = cutoff(tray_data.xsh.y + tray_data.xsh.height, 0, tray_data.root_wnd.height) - clr.y;
180 /* There's no need to update background if tray is not visible */
181 /* TODO: visibility is better to be tracked using some events */
182 if (clr.w == 0 || clr.h == 0) return SUCCESS;
183 /* Calculate local clipping rect */
184 clr_loc.x = clr.x - tray_data.xsh.x;
185 clr_loc.y = clr.y - tray_data.xsh.y;
186 clr_loc.w = clr.w;
187 clr_loc.h = clr.h;
188 if (old_width != tray_data.xsh.width || old_height != tray_data.xsh.height || final_pmap == None)
189 recreate_pixmap(final_pmap, final_gc);
190 /* Update root pixmap if asked and necessary */
191 if ((root_pmap == None || update_pixmap) &&
192 (settings.transparent || settings.fuzzy_edges))
193 {
194 if (!tray_update_root_bg_pmap(&root_pmap, NULL, NULL)) {
195 /* More gracefull solution */
196 LOG_TRACE(("still waiting for root pixmap\n"));
197 return SUCCESS;
198 } else
199 make_gc(root_pmap, root_gc);
200 }
201 /* We don't have to update background if only window position has
202 * changed unless we depend on root pixmap */
203 if (tray_data.xsh.width == old_width && tray_data.xsh.width == old_height &&
204 !settings.transparent && !settings.fuzzy_edges)
205 {
206 return SUCCESS;
207 }
208 /* If pixmap bg is used, bg_pmap holds tinted and tiled background pixmap,
209 * so there's no need to update it unless window size has changed */
210 if (settings.pixmap_bg && (bg_pmap == None ||
211 old_width != tray_data.xsh.width || old_height != tray_data.xsh.height))
212 {
213 int i, j;
214 recreate_pixmap(bg_pmap, bg_gc);
215 for (i = 0; i < tray_data.xsh.width / tray_data.bg_pmap_dims.x + 1; i++)
216 for (j = 0; j < tray_data.xsh.height / tray_data.bg_pmap_dims.y + 1; j++) {
217 XCopyArea(tray_data.dpy, tray_data.bg_pmap, bg_pmap, bg_gc,
218 0, 0, tray_data.bg_pmap_dims.x, tray_data.bg_pmap_dims.y,
219 i * tray_data.bg_pmap_dims.x, j * tray_data.bg_pmap_dims.y);
220 }
221 bg_pmap_updated = True;
222 } else
223 /* If root transparency is enabled, it is neccessary to copy portion of
224 * root pixmap under the window (root_pmap) to bg_pmap */
225 /* XXX: must correctly work around situations when bg pixmap is smaller than root window (but how?) */
226 if (settings.transparent) {
227 recreate_pixmap(bg_pmap, bg_gc);
228 XCopyArea(tray_data.dpy, root_pmap, bg_pmap, bg_gc,
229 tray_data.xsh.x, tray_data.xsh.y,
230 tray_data.xsh.width, tray_data.xsh.height,
231 0, 0);
232 }
233 /* Create an XImage from bg_pmap */
234 bg_img = XGetImage(tray_data.dpy, bg_pmap,
235 0, 0, tray_data.xsh.width, tray_data.xsh.height,
236 XAllPlanes(), ZPixmap);
237 if (bg_img == NULL) return FAILURE;
238 /* Tint the image if necessary. If bg_pmap was not updated, tinting
239 * is not needed, since it has been already done */
240 if (settings.tint_level && ((settings.pixmap_bg && bg_pmap_updated) || settings.transparent)) {
241 image_tint(bg_img, &settings.tint_color, settings.tint_level);
242 XPutImage(tray_data.dpy, bg_pmap, bg_gc, bg_img,
243 0, 0,
244 0, 0, tray_data.xsh.width, tray_data.xsh.height);
245 bg_pmap_updated = False;
246 }
247 /* Apply fuzzy edges */
248 /* XXX: THIS IS UGLY */
249 if (settings.fuzzy_edges) {
250 static CARD8 *alpha_mask = NULL;
251 /* Portion of root pixmap under the tray */
252 XImage *root_img = XGetImage(tray_data.dpy, root_pmap,
253 clr.x, clr.y, clr.w, clr.h,
254 XAllPlanes(), ZPixmap);
255 /* Proxy structure to work on */
256 XImage *tmp_img = NULL;
257 static Pixmap tmp_pmap = None;
258 static GC tmp_gc = None;
259 if (root_img == NULL) {
260 LOG_ERROR(("Failed to get image of root pixmap under tray window\n"));
261 LOG_TRACE(("Clipping rectangle: %dx%d+%d+%d\n", clr.w, clr.h, clr.x, clr.y));
262 return x11_ok();
263 }
264 /* Alpha mask needs to be updated only on size changes */
265 if (old_width != tray_data.xsh.width || old_height != tray_data.xsh.height) {
266 recreate_pixmap(tmp_pmap, tmp_gc);
267 if (alpha_mask != NULL) free(alpha_mask);
268 alpha_mask = image_create_alpha_mask(settings.fuzzy_edges, tray_data.xsh.width, tray_data.xsh.height);
269 }
270 XPutImage(tray_data.dpy, tmp_pmap, tmp_gc, root_img,
271 clr_loc.x, clr_loc.y,
272 0, 0, clr_loc.w, clr_loc.h);
273 tmp_img = XGetImage(tray_data.dpy, tmp_pmap,
274 0, 0, tray_data.xsh.width, tray_data.xsh.height,
275 XAllPlanes(), ZPixmap);
276 if (alpha_mask != NULL && tmp_img != NULL) image_compose(bg_img, tmp_img, alpha_mask);
277 XDestroyImage(root_img);
278 if (tmp_img != NULL) XDestroyImage(tmp_img);
279 }
280 XPutImage(tray_data.dpy, final_pmap, final_gc, bg_img,
281 0, 0,
282 0, 0, tray_data.xsh.width, tray_data.xsh.height);
283 XSetWindowBackgroundPixmap(tray_data.dpy, tray_data.tray, final_pmap);
284 XDestroyImage(bg_img);
285 old_width = tray_data.xsh.width;
286 old_height = tray_data.xsh.height;
287 RETURN_STATUS(x11_ok());
288 }
289
tray_refresh_window(int exposures)290 void tray_refresh_window(int exposures)
291 {
292 LOG_TRACE(("refreshing tray window\n"));
293 icon_list_forall(&embedder_refresh);
294 x11_refresh_window(tray_data.dpy, tray_data.tray, tray_data.xsh.width, tray_data.xsh.height, exposures);
295 scrollbars_refresh(exposures);
296 }
297
tray_calc_window_size(int base_width,int base_height,int * wnd_width,int * wnd_height)298 int tray_calc_window_size(int base_width, int base_height, int *wnd_width, int *wnd_height)
299 {
300 *wnd_width = base_width;
301 *wnd_height = base_height;
302 if (settings.scrollbars_mode & SB_MODE_HORZ) *wnd_width += settings.scrollbars_size * 2;
303 if (settings.scrollbars_mode & SB_MODE_VERT) *wnd_height += settings.scrollbars_size * 2;
304 return SUCCESS;
305 }
306
tray_calc_tray_area_size(int wnd_width,int wnd_height,int * base_width,int * base_height)307 int tray_calc_tray_area_size(int wnd_width, int wnd_height, int *base_width, int *base_height)
308 {
309 *base_width = wnd_width;
310 *base_height = wnd_height;
311 if (settings.scrollbars_mode & SB_MODE_HORZ) *base_width -= settings.scrollbars_size * 2;
312 if (settings.scrollbars_mode & SB_MODE_VERT) *base_height -= settings.scrollbars_size * 2;
313 return SUCCESS;
314 }
315
tray_update_window_strut()316 int tray_update_window_strut()
317 {
318 /* Set window strut */
319 if (settings.wm_strut_mode != WM_STRUT_NONE) {
320 int strut_mode;
321 if (settings.wm_strut_mode == WM_STRUT_AUTO) {
322 /* Do autodetection: if some edge of tray is adjacent to one
323 * of screen edges, we could set window strut to that */
324 int h_strut_mode, v_strut_mode;
325 int p_strut_mode, s_strut_mode;
326 h_strut_mode = (tray_data.xsh.x == 0 ? WM_STRUT_LFT :
327 (tray_data.xsh.x + tray_data.xsh.width == tray_data.root_wnd.width ? WM_STRUT_RHT :
328 WM_STRUT_NONE));
329 v_strut_mode = (tray_data.xsh.y == 0 ? WM_STRUT_TOP :
330 (tray_data.xsh.x + tray_data.xsh.height == tray_data.root_wnd.height ? WM_STRUT_BOT :
331 WM_STRUT_NONE));
332 /* If tray is vertical, horizontal strut mode has higher priority,
333 * else vertical strut mode has higher priority */
334 if (settings.vertical) {
335 p_strut_mode = h_strut_mode;
336 s_strut_mode = v_strut_mode;
337 } else {
338 p_strut_mode = v_strut_mode;
339 s_strut_mode = h_strut_mode;
340 }
341 if (p_strut_mode != WM_STRUT_NONE)
342 strut_mode = p_strut_mode;
343 else
344 strut_mode = s_strut_mode;
345 } else
346 strut_mode = settings.wm_strut_mode;
347 LOG_TRACE(("computed final strut mode: %d\n", strut_mode));
348 /* Update respective window hint */
349 if (strut_mode != WM_STRUT_NONE) {
350 wm_strut_t wm_strut;
351 int base_idx;
352 memset(wm_strut, 0, sizeof(wm_strut));
353 LOG_TRACE(("current tray geometry: %dx%d+%d+%d\n",
354 tray_data.xsh.width, tray_data.xsh.height,
355 tray_data.xsh.x, tray_data.xsh.y));
356 if (strut_mode == WM_STRUT_TOP || strut_mode == WM_STRUT_BOT) {
357 if (strut_mode == WM_STRUT_TOP) {
358 base_idx = WM_STRUT_IDX_TOP;
359 wm_strut[WM_STRUT_IDX_TOP] = tray_data.xsh.y + tray_data.xsh.height;
360 } else {
361 base_idx = WM_STRUT_IDX_BOT;
362 wm_strut[WM_STRUT_IDX_BOT] = tray_data.root_wnd.height - tray_data.xsh.y;
363 }
364 wm_strut[WM_STRUT_IDX_START(base_idx)] = tray_data.xsh.x;
365 wm_strut[WM_STRUT_IDX_END(base_idx)] = tray_data.xsh.x + tray_data.xsh.width - 1;
366 } else {
367 if (strut_mode == WM_STRUT_LFT) {
368 base_idx = WM_STRUT_IDX_LFT;
369 wm_strut[WM_STRUT_IDX_LFT] = tray_data.xsh.x + tray_data.xsh.width;
370 } else {
371 base_idx = WM_STRUT_IDX_RHT;
372 wm_strut[WM_STRUT_IDX_RHT] = tray_data.root_wnd.width - tray_data.xsh.x;
373 }
374 wm_strut[WM_STRUT_IDX_START(base_idx)] = tray_data.xsh.y;
375 wm_strut[WM_STRUT_IDX_END(base_idx)] = tray_data.xsh.y + tray_data.xsh.height - 1;
376 }
377 { int i;
378 for (i = 0; i < _NET_WM_STRUT_PARTIAL_SZ; i++)
379 LOG_TRACE(("computed hints [%d] = %d\n", i, wm_strut[i]));
380 }
381 ewmh_set_window_strut(tray_data.dpy, tray_data.tray, wm_strut);
382 }
383 }
384 return SUCCESS;
385 }
386
tray_update_window_props()387 int tray_update_window_props()
388 {
389 XSizeHints xsh;
390 int cur_base_width, cur_base_height;
391 int new_width, new_height;
392 int layout_width, layout_height;
393 /* Phase 1: calculate new tray window dimensions.
394 * Algorithm summary:
395 * new_dims =
396 * if (layout_dims > max_dims) max_dims;
397 * else if ((shrink_back && layout_dims > orig_dims) ||
398 * (layout_dims > current_dims)) layout_dims;
399 * else if (shrink_back) orig_dims;
400 * else current_dims;
401 */
402 x11_get_window_size(tray_data.dpy, tray_data.tray,
403 &tray_data.xsh.width, &tray_data.xsh.height);
404 layout_get_size(&layout_width, &layout_height);
405 LOG_TRACE(("layout geometry: %dx%d\n", layout_width, layout_height));
406 tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,
407 &cur_base_width, &cur_base_height);
408 LOG_TRACE(("base tray geometry: %dx%d\n", cur_base_width, cur_base_height));
409 LOG_TRACE(("orig tray geometry: %dx%d\n", settings.orig_tray_dims.x, settings.orig_tray_dims.y));
410 LOG_TRACE(("max tray geometry: %dx%d\n", settings.max_tray_dims.x, settings.max_tray_dims.y));
411 #define CALC_DIM(tgt,cur,layout,max,orig) \
412 if (layout > max) tgt = max; \
413 else if ((settings.shrink_back_mode && layout > orig) || layout > cur) tgt = layout; \
414 else if (settings.shrink_back_mode) tgt = orig; \
415 else tgt = cur;
416 CALC_DIM(new_width, cur_base_width, layout_width,
417 settings.max_tray_dims.x, settings.orig_tray_dims.x);
418 CALC_DIM(new_height, cur_base_height, layout_height,
419 settings.max_tray_dims.y, settings.orig_tray_dims.y);
420 LOG_TRACE(("new base tray geometry: %dx%d\n", new_width, new_height));
421 tray_calc_window_size(new_width, new_height, &new_width, &new_height);
422 LOG_TRACE(("new tray geometry: %dx%d\n", new_width, new_height));
423 #if 1
424 /* Not sure if this is really necessary */
425 xsh.x = tray_data.xsh.x;
426 xsh.y = tray_data.xsh.y;
427 xsh.width = new_width;
428 xsh.height = new_height;
429 #endif
430 xsh.min_width = new_width;
431 xsh.min_height = new_height;
432 xsh.max_width = new_width;
433 xsh.max_height = new_height;
434 xsh.width_inc = settings.slot_size;
435 xsh.height_inc = settings.slot_size;
436 tray_calc_window_size(0, 0, &xsh.base_width, &xsh.base_height);
437 xsh.win_gravity = NorthWestGravity;
438 xsh.flags = tray_data.xsh.flags;
439 XSetWMNormalHints(tray_data.dpy, tray_data.tray, &xsh);
440 /* Phase 2: set new window size
441 * This check helps to avod extra (erroneous) moves of the window when
442 * geometry changes are not updated yet, but tray_update_window_props() was
443 * called once again */
444 if (new_width != tray_data.xsh.width || new_height != tray_data.xsh.height) {
445 /* Apparently, not every WM (hello, WindowMaker!) handles gravity the
446 * way I have expected (i.e. using it to calculate reference point as
447 * described in ICCM/WM specs). Perhaps, I was dreaming. So, prior to
448 * resizing trays window, it is necessary to recalculate window
449 * absolute position and shift it according to grow gravity settings */
450 x11_get_window_abs_coords(tray_data.dpy, tray_data.tray,
451 &tray_data.xsh.x, &tray_data.xsh.y);
452 LOG_TRACE(("old tray window geometry: %dx%d+%d+%d\n", new_width, new_height,
453 tray_data.xsh.x, tray_data.xsh.y));
454 if (settings.grow_gravity & GRAV_E)
455 tray_data.xsh.x -= new_width - tray_data.xsh.width;
456 else if (!(settings.grow_gravity & GRAV_H))
457 tray_data.xsh.x -= (new_width - tray_data.xsh.width) / 2;
458 if (settings.grow_gravity & GRAV_S)
459 tray_data.xsh.y -= new_height - tray_data.xsh.height;
460 else if (!(settings.grow_gravity & GRAV_V))
461 tray_data.xsh.y -= (new_height - tray_data.xsh.height) / 2;
462 tray_data.xsh.width = new_width;
463 tray_data.xsh.height = new_height;
464 LOG_TRACE(("new tray window geometry: %dx%d+%d+%d\n", new_width, new_height,
465 tray_data.xsh.x, tray_data.xsh.y));
466 XResizeWindow(tray_data.dpy, tray_data.tray, new_width, new_height);
467 XMoveWindow(tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y);
468 if (!x11_ok()) {
469 LOG_TRACE(("could not update tray window geometry\n"));
470 return FAILURE;
471 }
472 } else {
473 /* XXX: Why do we need this again? */
474 XResizeWindow(tray_data.dpy, tray_data.tray,
475 tray_data.xsh.width, tray_data.xsh.height);
476 }
477 tray_update_window_strut();
478 scrollbars_update();
479 return SUCCESS;
480 }
481
tray_create_window(int argc,char ** argv)482 void tray_create_window(int argc, char **argv)
483 {
484 XTextProperty wm_name;
485 XSetWindowAttributes xswa;
486 XClassHint xch;
487 XWMHints xwmh;
488 Atom net_system_tray_orientation;
489 Atom orient;
490 Atom protocols_atoms[3];
491 /* Create some atoms */
492 tray_data.xa_wm_delete_window =
493 XInternAtom(tray_data.dpy, "WM_DELETE_WINDOW", False);
494 tray_data.xa_net_wm_ping =
495 XInternAtom(tray_data.dpy, "_NET_WM_PING", False);
496 tray_data.xa_wm_take_focus =
497 XInternAtom(tray_data.dpy, "WM_TAKE_FOCUS", False);
498 tray_data.xa_wm_protocols =
499 XInternAtom(tray_data.dpy, "WM_PROTOCOLS", False);
500 tray_data.xa_kde_net_system_tray_windows =
501 XInternAtom(tray_data.dpy, "_KDE_NET_SYSTEM_TRAY_WINDOWS", False);
502 tray_data.xa_net_client_list =
503 XInternAtom(tray_data.dpy, "_NET_CLIENT_LIST", False);
504 /* Create tray window */
505 tray_data.tray = XCreateSimpleWindow(
506 tray_data.dpy,
507 DefaultRootWindow(tray_data.dpy),
508 tray_data.xsh.x, tray_data.xsh.y,
509 tray_data.xsh.width, tray_data.xsh.height,
510 0,
511 settings.bg_color.pixel,
512 settings.bg_color.pixel);
513 LOG_TRACE(("created tray window: 0x%x\n", tray_data.tray));
514 if (settings.dockapp_mode == DOCKAPP_WMAKER) {
515 tray_data.hint_win = XCreateSimpleWindow(
516 tray_data.dpy,
517 DefaultRootWindow(tray_data.dpy),
518 0, 0,
519 1, 1,
520 0,
521 settings.bg_color.pixel,
522 settings.bg_color.pixel);
523 LOG_TRACE(("created hint_win window: 0x%x\n", tray_data.hint_win));
524 }
525 /* Set tray window properties */
526 xswa.bit_gravity = settings.bit_gravity;
527 xswa.win_gravity = settings.win_gravity;
528 xswa.backing_store = settings.parent_bg ? NotUseful : WhenMapped;
529 XChangeWindowAttributes(tray_data.dpy,
530 tray_data.tray,
531 CWBitGravity | CWWinGravity | CWBackingStore,
532 &xswa);
533 {
534 /* XXX: use XStoreName ?*/
535 int numtries = 0;
536 /* First, try user-supplied value */
537 while (1) {
538 if (XmbTextListToTextProperty(tray_data.dpy, &settings.wnd_name, 1, XTextStyle, &wm_name) != Success)
539 /* Retry with default value */
540 settings.wnd_name = PROGNAME;
541 else
542 break;
543 if (numtries++ > 1) DIE(("Invalid window name \"%s\"\n", settings.wnd_name));
544 }
545 }
546 XSetWMName(tray_data.dpy, tray_data.tray, &wm_name);
547 XFree(wm_name.value);
548 /* Setup class hints */
549 xch.res_class = PROGNAME;
550 xch.res_name = PROGNAME;
551 /* Setup window manager hints */
552 xwmh.flags = StateHint | InputHint;
553 xwmh.input = False;
554 xwmh.initial_state = settings.dockapp_mode != DOCKAPP_NONE ? WithdrawnState: NormalState;
555 /* Apply hints */
556 XSetWMHints(tray_data.dpy, tray_data.tray, &xwmh);
557 XSetClassHint(tray_data.dpy, tray_data.tray, &xch);
558 XSetWMNormalHints(tray_data.dpy, tray_data.tray, &tray_data.xsh);
559 XSetCommand(tray_data.dpy, tray_data.tray, argv, argc);
560 /* Set properties of hint window if WindowMaker dockapp mode enabled */
561 if (settings.dockapp_mode == DOCKAPP_WMAKER) {
562 xwmh.flags |= IconWindowHint | IconPositionHint | WindowGroupHint;
563 xwmh.initial_state = WithdrawnState;
564 xwmh.icon_x = tray_data.xsh.x;
565 xwmh.icon_y = tray_data.xsh.y;
566 xwmh.icon_window = tray_data.tray;
567 xwmh.window_group = tray_data.hint_win;
568 XSetClassHint(tray_data.dpy, tray_data.hint_win, &xch);
569 XSetWMHints(tray_data.dpy, tray_data.hint_win, &xwmh);
570 }
571 /* v0.2 tray protocol support */
572 orient =
573 settings.vertical ? _NET_SYSTEM_TRAY_ORIENTATION_HORZ : _NET_SYSTEM_TRAY_ORIENTATION_VERT;
574 net_system_tray_orientation = XInternAtom(tray_data.dpy, TRAY_ORIENTATION_ATOM, False);
575 XChangeProperty(tray_data.dpy, tray_data.tray,
576 net_system_tray_orientation, net_system_tray_orientation, 32,
577 PropModeReplace,
578 (unsigned char *) &orient, 1);
579 /* Ask X server / WM to report certain events */
580 protocols_atoms[0] = tray_data.xa_wm_delete_window;
581 protocols_atoms[1] = tray_data.xa_wm_take_focus;
582 protocols_atoms[2] = tray_data.xa_net_wm_ping;
583 XSetWMProtocols(tray_data.dpy, tray_data.tray, protocols_atoms, 3);
584 XSelectInput(tray_data.dpy, tray_data.tray,
585 StructureNotifyMask | FocusChangeMask | PropertyChangeMask | ExposureMask );
586 x11_extend_root_event_mask(tray_data.dpy, PropertyChangeMask);
587 scrollbars_create();
588 /* Set tray window background if necessary */
589 #ifdef XPM_SUPPORTED
590 if (settings.pixmap_bg) tray_init_pixmap_bg();
591 #endif
592 if (settings.parent_bg)
593 XSetWindowBackgroundPixmap(tray_data.dpy, tray_data.tray, ParentRelative);
594 else if (settings.transparent || settings.fuzzy_edges) {
595 tray_data.xa_xrootpmap_id = XInternAtom(tray_data.dpy, "_XROOTPMAP_ID", False);
596 tray_data.xa_xsetroot_id = XInternAtom(tray_data.dpy, "_XSETROOT_ID", False);
597 }
598 tray_update_bg(True);
599 }
600
tray_create_phony_window()601 void tray_create_phony_window()
602 {
603 /* Create fake tray window */
604 tray_data.tray = XCreateSimpleWindow(
605 tray_data.dpy,
606 DefaultRootWindow(tray_data.dpy),
607 0, 0, 1, 1,
608 0,
609 0, 0);
610 /* Select for PropertyNotify so that x11_get_server_timestamp() works */
611 XSelectInput(tray_data.dpy, tray_data.tray, PropertyChangeMask);
612 }
613
tray_set_wm_hints()614 int tray_set_wm_hints()
615 {
616 int mwm_decor = 0;
617 if (settings.deco_flags & DECO_TITLE)
618 mwm_decor |= MWM_DECOR_TITLE | MWM_DECOR_MENU;
619 if (settings.deco_flags & DECO_BORDER)
620 mwm_decor |= MWM_DECOR_RESIZEH | MWM_DECOR_BORDER;
621 mwm_set_hints(tray_data.dpy, tray_data.tray, mwm_decor, MWM_FUNC_ALL);
622 if (settings.sticky) {
623 ewmh_add_window_state(tray_data.dpy, tray_data.tray, _NET_WM_STATE_STICKY);
624 ewmh_set_window_atom32(tray_data.dpy, tray_data.tray, _NET_WM_DESKTOP, 0xFFFFFFFF);
625 }
626 if (settings.skip_taskbar)
627 ewmh_add_window_state(tray_data.dpy, tray_data.tray, _NET_WM_STATE_SKIP_TASKBAR);
628 if (settings.wnd_layer != NULL)
629 ewmh_add_window_state(tray_data.dpy, tray_data.tray, settings.wnd_layer);
630 if (strcmp(settings.wnd_type, _NET_WM_WINDOW_TYPE_NORMAL) != 0)
631 ewmh_add_window_type(tray_data.dpy, tray_data.tray, settings.wnd_type);
632 /* Alwas add NORMAL window type for WM that do not support (some) special types */
633 ewmh_add_window_type(tray_data.dpy, tray_data.tray, _NET_WM_WINDOW_TYPE_NORMAL);
634 return SUCCESS;
635 }
636
tray_init_selection_atoms()637 void tray_init_selection_atoms()
638 {
639 static char *tray_sel_atom_name = NULL;
640 /* Obtain selection atom name basing on current screen number */
641 if (tray_sel_atom_name == NULL) {
642 tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 10);
643 if (tray_sel_atom_name == NULL) DIE_OOM(("could not allocate memory for selection atom name\n"));
644 snprintf(tray_sel_atom_name,
645 strlen(TRAY_SEL_ATOM) + 10,
646 "%s%u",
647 TRAY_SEL_ATOM,
648 DefaultScreen(tray_data.dpy));
649 }
650 LOG_TRACE(("tray_sel_atom_name=%s\n", tray_sel_atom_name));
651 /* Initialize atom values */
652 tray_data.xa_tray_selection = XInternAtom(tray_data.dpy, tray_sel_atom_name, False);
653 tray_data.xa_tray_opcode = XInternAtom(tray_data.dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
654 tray_data.xa_tray_data = XInternAtom(tray_data.dpy, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
655 }
656
tray_acquire_selection()657 void tray_acquire_selection()
658 {
659 Time timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);
660 tray_init_selection_atoms();
661 /* Save old selection owner */
662 tray_data.old_selection_owner = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);
663 LOG_TRACE(("old selection owner: 0x%x\n", tray_data.old_selection_owner));
664 /* Acquire selection */
665 XSetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection,
666 tray_data.tray, timestamp);
667 /* Check if we have really got the selection */
668 if (XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection) != tray_data.tray) {
669 DIE(("could not set selection owner.\nMay be another (greedy) tray running?\n"));
670 } else {
671 tray_data.is_active = True;
672 LOG_TRACE(("ok, got _NET_SYSTEM_TRAY selection\n"));
673 }
674 /* Send the message notifying about new MANAGER */
675 x11_send_client_msg32(tray_data.dpy, DefaultRootWindow(tray_data.dpy),
676 DefaultRootWindow(tray_data.dpy),
677 XInternAtom(tray_data.dpy, "MANAGER", False),
678 timestamp,
679 tray_data.xa_tray_selection, tray_data.tray, 0, 0);
680 }
681
tray_show_window()682 void tray_show_window()
683 {
684 tray_set_wm_hints();
685 tray_update_window_props();
686 XMapRaised(tray_data.dpy, tray_data.tray);
687 if (settings.dockapp_mode == DOCKAPP_NONE)
688 XMoveWindow(tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y);
689 if (settings.dockapp_mode == DOCKAPP_WMAKER)
690 XMapWindow(tray_data.dpy, tray_data.hint_win);
691 /* XXX: I do not why, but for some WM it is
692 * required to set hints / window properties
693 * after and before window creation */
694 /* TODO: check if this is really necessary */
695 tray_set_wm_hints();
696 tray_update_window_props();
697 }
698
699