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