Conky 1.5.0 -- client/server prototype
[monky] / src / x11.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * This program is licensed under BSD license, read COPYING
5  *
6  *  $Id$
7  */
8
9
10 #include "conky.h"
11
12 #ifdef X11
13 #include <X11/Xlib.h>
14 #include <X11/Xatom.h>
15 #include <X11/Xmd.h>
16 #include <X11/Xutil.h>
17 #ifdef XFT
18 #include <X11/Xft/Xft.h>
19 #endif
20
21 #ifdef HAVE_XDBE
22 int use_xdbe;
23 #endif
24
25 #ifdef XFT
26 int use_xft = 0;
27 #endif
28
29 /* some basic X11 stuff */
30 Display *display;
31 int display_width;
32 int display_height;
33 int screen;
34 static int set_transparent;
35 static int background_colour;
36
37 /* workarea from _NET_WORKAREA, this is where window / text is aligned */
38 int workarea[4];
39
40 /* Window stuff */
41 struct conky_window window;
42
43 /* local prototypes */
44 static void update_workarea();
45 static Window find_desktop_window(Window *p_root, Window *p_desktop);
46 static Window find_subwindow(Window win, int w, int h);
47
48 /* X11 initializer */
49 void init_X11()
50 {
51         if ((display = XOpenDisplay(0)) == NULL)
52                 CRIT_ERR("can't open display: %s", XDisplayName(0));
53
54         screen = DefaultScreen(display);
55         display_width = DisplayWidth(display, screen);
56         display_height = DisplayHeight(display, screen);
57
58         update_workarea();
59 }
60
61 static void update_workarea()
62 {
63         Window root = RootWindow(display, screen);
64         unsigned long nitems, bytes;
65         unsigned char *buf = NULL;
66         Atom type;
67         int format;
68
69         /* default work area is display */
70         workarea[0] = 0;
71         workarea[1] = 0;
72         workarea[2] = display_width;
73         workarea[3] = display_height;
74
75         /* get current desktop */
76         if (XGetWindowProperty(display, root, ATOM(_NET_CURRENT_DESKTOP),
77                                0, 1, False, XA_CARDINAL, &type, &format,
78                                &nitems, &bytes, &buf) == Success
79             && type == XA_CARDINAL && nitems > 0) {
80
81                 //Currently unused 
82                 /*  long desktop = * (long *) buf; */
83
84                 XFree(buf);
85                 buf = 0;
86
87         }
88
89         if (buf) {
90                 XFree(buf);
91                 buf = 0;
92         }
93 }
94
95 /* Find root window and desktop window.  Return desktop window on success, 
96  * and set root and desktop byref return values.  Return 0 on failure. */  
97 static Window find_desktop_window(Window *p_root, Window *p_desktop)
98 {
99         Atom type;
100         int format, i;
101         unsigned long nitems, bytes;
102         unsigned int n;
103         Window root = RootWindow(display, screen);
104         Window win = root;
105         Window troot, parent, *children;
106         unsigned char *buf = NULL;
107
108         if (!p_root || !p_desktop)
109             return(0);
110
111         /* some window managers set __SWM_VROOT to some child of root window */
112
113         XQueryTree(display, root, &troot, &parent, &children, &n);
114         for (i = 0; i < (int) n; i++) {
115                 if (XGetWindowProperty
116                     (display, children[i], ATOM(__SWM_VROOT), 0, 1, False,
117                      XA_WINDOW, &type, &format, &nitems, &bytes,
118                      &buf) == Success && type == XA_WINDOW) {
119                         win = *(Window *) buf;
120                         XFree(buf);
121                         XFree(children);
122                         fprintf(stderr,
123                                 "Conky: desktop window (%lx) found from __SWM_VROOT property\n", win);
124                         fflush(stderr);
125                         *p_root=win;
126                         *p_desktop=win;
127                         return win;
128                 }
129
130                 if (buf) {
131                         XFree(buf);
132                         buf = 0;
133                 }
134         }
135         XFree(children);
136
137         /* get subwindows from root */
138         win = find_subwindow(root, -1, -1);
139
140         update_workarea();
141
142         win = find_subwindow(win, workarea[2], workarea[3]);
143
144         if (buf) {
145                 XFree(buf);
146                 buf = 0;
147         }
148
149         if (win != root)
150                 fprintf(stderr,
151                         "Conky: desktop window (%lx) is subwindow of root window (%lx)\n",win,root);
152         else
153                 fprintf(stderr, "Conky: desktop window (%lx) is root window\n",win);
154
155         fflush(stderr);
156
157         *p_root=root;
158         *p_desktop=win;
159
160         return win;
161 }
162
163 /* sets background to ParentRelative for the Window and all parents */
164 inline void set_transparent_background(Window win)
165 {
166         static int colour_set = -1;
167         if (set_transparent) {
168                 Window parent = win;
169                 unsigned int i;
170                 for (i = 0; i < 50 && parent != RootWindow(display, screen); i++) {
171                         Window r, *children;
172                         unsigned int n;
173                         
174                         XSetWindowBackgroundPixmap(display, parent, ParentRelative);
175         
176                         XQueryTree(display, parent, &r, &parent, &children, &n);
177                         XFree(children);
178                         }
179         } else if (colour_set != background_colour) {
180                 XSetWindowBackground(display, win, background_colour);
181                 colour_set = background_colour;
182 }
183         //XClearWindow(display, win); not sure why this was here
184 }
185
186 void init_window(int own_window, int w, int h, int set_trans, int back_colour, 
187                              char **argv, int argc)
188 {
189         /* There seems to be some problems with setting transparent background (on
190          * fluxbox this time). It doesn't happen always and I don't know why it
191          * happens but I bet the bug is somewhere here. */
192         set_transparent = set_trans;
193         background_colour = back_colour;
194
195 #ifdef OWN_WINDOW
196         if (own_window) {
197                 
198                 if ( !find_desktop_window( &window.root, &window.desktop ) )
199                         return;
200
201                 if (window.type == TYPE_OVERRIDE) {
202         
203                         /* 
204                            An override_redirect True window.  No WM hints or button processing needed. 
205                         */
206                         XSetWindowAttributes attrs = {
207                                 ParentRelative,0L,0,0L,0,0,Always,0L,0L,False,
208                                 StructureNotifyMask|ExposureMask,
209                                 0L,
210                                 True,
211                                 0,0 };
212
213                         /* Parent is desktop window (which might be a child of root) */
214                         window.window = XCreateWindow(display, 
215                                                    window.desktop, 
216                                                    window.x, window.y, w, h, 0, 
217                                                    CopyFromParent,
218                                                    InputOutput,
219                                                    CopyFromParent,
220                                                    CWBackPixel|CWOverrideRedirect,
221                                                    &attrs);
222
223                         XLowerWindow(display, window.window);   
224
225                         fprintf(stderr, "Conky: window type - override\n"); fflush(stderr);
226
227                 }
228
229                 else { /* window.type != TYPE_OVERRIDE */
230
231                         /* 
232                            A window managed by the window manager.  Process hints and buttons. 
233                         */
234                         XSetWindowAttributes attrs = {
235                                 ParentRelative,0L,0,0L,0,0,Always,0L,0L,False,
236                                 StructureNotifyMask|ExposureMask|ButtonPressMask|ButtonReleaseMask,
237                                 0L,
238                                 False,
239                                 0,0 };
240
241                         XClassHint classHint;
242                         XWMHints wmHint;
243                         Atom xa;
244
245                         /* Parent is root window so WM can take control */
246                         window.window = XCreateWindow(display, 
247                                                    window.root, 
248                                              window.x, window.y, w, h, 0, 
249                                                    CopyFromParent,
250                                                    InputOutput,
251                                                    CopyFromParent,
252                                                    CWBackPixel|CWOverrideRedirect,
253                                                    &attrs);
254
255                         classHint.res_name = window.class_name;
256                         classHint.res_class = classHint.res_name;
257
258                         wmHint.flags = InputHint | StateHint;
259       /* allow decorated windows to be given input focus by WM */
260                         wmHint.input = TEST_HINT(window.hints,HINT_UNDECORATED) ? False : True;
261                         wmHint.initial_state = NormalState;
262
263                         XmbSetWMProperties (display, window.window, window.title, NULL, 
264                                             argv, argc,
265                                             NULL, &wmHint, &classHint);
266
267                         /* Sets an empty WM_PROTOCOLS property */
268                         XSetWMProtocols(display,window.window,NULL,0);
269
270
271                         /* Set window type */
272                         if ( (xa = ATOM(_NET_WM_WINDOW_TYPE)) != None ) 
273                         {
274                                 Atom prop;
275                                 switch(window.type) {
276                                 case TYPE_DESKTOP:
277                                         {
278                                         prop = ATOM(_NET_WM_WINDOW_TYPE_DESKTOP);
279                                         fprintf(stderr, "Conky: window type - desktop\n"); fflush(stderr);
280                                         }
281                                         break;                          
282                                 case TYPE_NORMAL:
283                                 default:
284                                         {
285                                         prop = ATOM(_NET_WM_WINDOW_TYPE_NORMAL);
286                                         fprintf(stderr, "Conky: window type - normal\n"); fflush(stderr);
287                                         }
288                                         break;
289                                 }
290                                 XChangeProperty(display, window.window, xa,
291                                                 XA_ATOM, 32,
292                                                 PropModeReplace,
293                                                 (unsigned char *) &prop, 1);
294                         }
295
296                         /* Set desired hints */
297                         
298                         /* Window decorations */
299                         if (TEST_HINT(window.hints,HINT_UNDECORATED)) {
300                             /*fprintf(stderr, "Conky: hint - undecorated\n"); fflush(stderr);*/
301
302                             xa = ATOM(_MOTIF_WM_HINTS);
303                             if (xa != None) {
304                                 long prop[5] = { 2, 0, 0, 0, 0 };
305                                 XChangeProperty(display, window.window, xa,
306                                                 xa, 32, PropModeReplace,
307                                                 (unsigned char *) prop, 5);
308                             }
309                         }
310
311                         /* Below other windows */
312                         if (TEST_HINT(window.hints,HINT_BELOW)) {
313                             /*fprintf(stderr, "Conky: hint - below\n"); fflush(stderr); */
314
315                             xa = ATOM(_WIN_LAYER);
316                             if (xa != None) {
317                                 long prop = 0;
318                                 XChangeProperty(display, window.window, xa,
319                                                 XA_CARDINAL, 32,
320                                                 PropModeAppend,
321                                                 (unsigned char *) &prop, 1);
322                             }
323                         
324                             xa = ATOM(_NET_WM_STATE);
325                             if (xa != None) {
326                                 Atom xa_prop = ATOM(_NET_WM_STATE_BELOW);
327                                 XChangeProperty(display, window.window, xa,
328                                         XA_ATOM, 32,
329                                         PropModeAppend,
330                                         (unsigned char *) &xa_prop,
331                                         1);
332                             }
333                         }
334
335                         /* Above other windows */
336                         if (TEST_HINT(window.hints,HINT_ABOVE)) {
337                             /*fprintf(stderr, "Conky: hint - above\n"); fflush(stderr);*/
338
339                             xa = ATOM(_WIN_LAYER);
340                             if (xa != None) {
341                                 long prop = 6;
342                                 XChangeProperty(display, window.window, xa,
343                                                 XA_CARDINAL, 32,
344                                                 PropModeAppend,
345                                                 (unsigned char *) &prop, 1);
346                             }
347
348                             xa = ATOM(_NET_WM_STATE);
349                             if (xa != None) {
350                                 Atom xa_prop = ATOM(_NET_WM_STATE_ABOVE);
351                                 XChangeProperty(display, window.window, xa,
352                                         XA_ATOM, 32,
353                                         PropModeAppend,
354                                         (unsigned char *) &xa_prop,
355                                         1);
356                             }
357                         }
358
359                         /* Sticky */
360                         if (TEST_HINT(window.hints,HINT_STICKY)) {
361                             /*fprintf(stderr, "Conky: hint - sticky\n"); fflush(stderr); */
362
363                             xa = ATOM(_NET_WM_DESKTOP);
364                             if (xa != None) {
365                                 CARD32 xa_prop = 0xFFFFFFFF;
366                                 XChangeProperty(display, window.window, xa,
367                                         XA_CARDINAL, 32,
368                                         PropModeAppend,
369                                         (unsigned char *) &xa_prop,
370                                         1);
371                             }
372
373                             xa = ATOM(_NET_WM_STATE);
374                             if (xa != None) {
375                                 Atom xa_prop = ATOM(_NET_WM_STATE_STICKY);
376                                 XChangeProperty(display, window.window, xa,
377                                         XA_ATOM, 32,
378                                         PropModeAppend,
379                                         (unsigned char *) &xa_prop,
380                                         1);
381                             }
382                         }
383
384                         /* Skip taskbar */
385                         if (TEST_HINT(window.hints,HINT_SKIP_TASKBAR)) {
386                             /*fprintf(stderr, "Conky: hint - skip_taskbar\n"); fflush(stderr);*/
387
388                             xa = ATOM(_NET_WM_STATE);
389                             if (xa != None) {
390                                 Atom xa_prop = ATOM(_NET_WM_STATE_SKIP_TASKBAR);
391                                 XChangeProperty(display, window.window, xa,
392                                         XA_ATOM, 32,
393                                         PropModeAppend,
394                                         (unsigned char *) &xa_prop,
395                                         1);
396                             }
397                         }
398
399                         /* Skip pager */
400                         if (TEST_HINT(window.hints,HINT_SKIP_PAGER)) {
401                             /*fprintf(stderr, "Conky: hint - skip_pager\n"); fflush(stderr);*/
402
403                             xa = ATOM(_NET_WM_STATE);
404                             if (xa != None) {
405                                 Atom xa_prop = ATOM(_NET_WM_STATE_SKIP_PAGER);
406                                 XChangeProperty(display, window.window, xa,
407                                         XA_ATOM, 32,
408                                         PropModeAppend,
409                                         (unsigned char *) &xa_prop,
410                                         1);
411                             }
412                         }
413
414                 } /* else { window.type != TYPE_OVERRIDE */
415                         
416                 fprintf(stderr, "Conky: drawing to created window (%lx)\n", window.window);
417                 fflush(stderr);
418
419                 XMapWindow(display, window.window);
420
421         } else /* if (own_window) { */
422 #endif
423                 /* root / desktop window */
424         {
425                 XWindowAttributes attrs;
426
427                 if (!window.window)
428                         window.window = find_desktop_window( &window.root, &window.desktop );
429
430                 if (XGetWindowAttributes(display, window.window, &attrs)) {
431                         window.width = attrs.width;
432                         window.height = attrs.height;
433                 }
434
435                 fprintf(stderr, "Conky: drawing to desktop window\n");
436         }
437
438         /* Drawable is same as window. This may be changed by double buffering. */
439         window.drawable = window.window;
440
441 #ifdef HAVE_XDBE
442         if (use_xdbe) {
443                 int major, minor;
444                 if (!XdbeQueryExtension(display, &major, &minor)) {
445                         use_xdbe = 0;
446                 } else {
447                         window.back_buffer =
448                             XdbeAllocateBackBufferName(display,
449                                                        window.window,
450                                                        XdbeBackground);
451                         if (window.back_buffer != None) {
452                                 window.drawable = window.back_buffer;
453                                 fprintf(stderr,
454                                         "Conky: drawing to double buffer\n");
455                         } else
456                                 use_xdbe = 0;
457                 }
458                 if (!use_xdbe)
459                         ERR("failed to set up double buffer");
460         }
461         if (!use_xdbe)
462                 fprintf(stderr, "Conky: drawing to single buffer\n");
463 #endif
464
465         XFlush(display);
466
467         /*set_transparent_background(window.window); must be done after double buffer stuff? */
468 #ifdef OWN_WINDOW
469         /*if (own_window) {
470         set_transparent_background(window.window);
471                 XClearWindow(display, window.window);
472 }*/
473 #endif
474
475         XSelectInput(display, window.window, ExposureMask
476 #ifdef OWN_WINDOW
477                      | (own_window
478                         ? (StructureNotifyMask | PropertyChangeMask | 
479                            ButtonPressMask | ButtonReleaseMask) : 0)
480 #endif
481             );
482 }
483
484 static Window find_subwindow(Window win, int w, int h)
485 {
486         unsigned int i, j;
487         Window troot, parent, *children;
488         unsigned int n;
489
490         /* search subwindows with same size as display or work area */
491
492         for (i = 0; i < 10; i++) {
493                 XQueryTree(display, win, &troot, &parent, &children, &n);
494
495                 for (j = 0; j < n; j++) {
496                         XWindowAttributes attrs;
497
498                         if (XGetWindowAttributes
499                             (display, children[j], &attrs)) {
500                                 /* Window must be mapped and same size as display or work space */
501                                 if (attrs.map_state != 0 &&
502                                     ((attrs.width == display_width
503                                       && attrs.height == display_height)
504                                      || (attrs.width == w
505                                          && attrs.height == h))) {
506                                         win = children[j];
507                                         break;
508                                 }
509                         }
510                 }
511
512                 XFree(children);
513                 if (j == n)
514                         break;
515         }
516
517         return win;
518 }
519
520 long get_x11_color(const char *name)
521 {
522         XColor color;
523         color.pixel = 0;
524         if (!XParseColor
525             (display, DefaultColormap(display, screen), name, &color)) {
526                 /* lets check if it's a hex colour with the # missing in front
527                  * if yes, then do something about it
528                  */
529                 char newname[64];
530                 newname[0] = '#';
531                 strncpy(&newname[1], name, 62);
532                 /* now lets try again */
533                 if (!XParseColor(display, DefaultColormap(display, screen), &newname[0], &color)) {
534                         ERR("can't parse X color '%s'", name);
535                         return 0xFF00FF;
536                 }
537         }
538         if (!XAllocColor
539             (display, DefaultColormap(display, screen), &color))
540                 ERR("can't allocate X color '%s'", name);
541
542         return (long) color.pixel;
543 }
544
545 void create_gc()
546 {
547         XGCValues values;
548         values.graphics_exposures = 0;
549         values.function = GXcopy;
550         window.gc = XCreateGC(display, window.drawable,
551                               GCFunction | GCGraphicsExposures, &values);
552 }
553
554 #endif /* X11 */