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