diff src/rootvis/getroot.c @ 900:d985f0dcdeb0 trunk

[svn] - add a starting point for xmms-rootvis port. giacomo will need to finish this up, as my XLib skills are not enough at this time.
author nenolod
date Mon, 26 Mar 2007 01:19:26 -0700
parents
children f1b6f1b2cdb3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/getroot.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,400 @@
+/*  I've modified the following file and renamed it. My modifications were just
+ *  about getting rid of some toon.h dependants. If you're interested in the
+ *  original version of the file, you can find it either in the sources of
+ *  xpenguins or of xsnow.
+ *  http://xpenguins.seul.org/ is my actual source.
+ *
+ *  Johannes Jordan
+ */
+
+/* toon_root.c - finding the correct background window / virtual root
+ * Copyright (C) 1999-2001  Robin Hogan
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Since xpenguins version 2.1, the ToonGetRootWindow() function
+ * attempts to find the window IDs of
+ *
+ * 1) The background window that is behind the toplevel client
+ *    windows; this is the window that we draw the toons on.
+ *
+ * 2) The parent window of the toplevel client windows; this is used
+ *    by ToonLocateWindows() to build up a map of the space that the
+ *    toons can occupy.
+ *
+ * In simple (sensible?) window managers (e.g. blackbox, sawfish, fvwm
+ * and countless others), both of these are the root window. The other
+ * more complex scenarios that ToonGetRootWindow() attempts to cope
+ * with are:
+ *
+ * Some `virtual' window managers (e.g. amiwm, swm and tvtwm) that
+ * reparent all client windows to a desktop window that sits on top of
+ * the root window. This desktop window is easy to find - we just look
+ * for a property __SWM_VROOT in the immediate children of the root
+ * window that contains the window ID of this desktop window. The
+ * desktop plays both roles (1 and 2 above). This functionality was
+ * detected in xpenguins 1.x with the vroot.h header file.
+ *
+ * Enlightenment (0.16) can have a number of desktops with different
+ * backgrounds; client windows on these are reparented, except for
+ * Desktop 0 which is the root window. Therefore versions less than
+ * 2.1 of xpenguins worked on Desktop 0 but not on any others. To fix
+ * this we look for a root-window property _WIN_WORKSPACE which
+ * contains the numerical index of the currently active desktop. The
+ * active desktop is then simply the immediate child of the root
+ * window that has a property ENLIGHTENMENT_DESKTOP set to this value.
+ *
+ * KDE 2.0: Oh dear. The kdesktop is a program separate from the
+ * window manager that launches a window which sits behind all the
+ * other client windows and has all the icons on it. Thus the other
+ * client windows are still children of the root window, but we want
+ * to draw to the uppermost window of the kdesktop. This is difficult
+ * to find - it is the great-great-grandchild of the root window and
+ * in KDE 2.0 has nothing to identify it from its siblings other than
+ * its size. KDE 2.1+ usefully implements the __SWM_VROOT property in
+ * a child of the root window, but the client windows are still
+ * children of the root window. A problem is that the penguins erase
+ * the desktop icons when they walk which is a bit messy. The icons
+ * are not lost - they reappear when the desktop window gets an expose
+ * event (i.e. move some windows over where they were and back again).
+ *
+ * Nautilus (GNOME 1.4+): Creates a background window to draw icons
+ * on, but does not reparent the client windows. The toplevel window
+ * of the desktop is indicated by the root window property
+ * NAUTILUS_DESKTOP_WINDOW_ID, but then we must descend down the tree
+ * from this toplevel window looking for subwindows that are the same
+ * size as the screen. The bottom one is the one to draw to. Hopefully
+ * one day Nautilus will implement __SWM_VROOT in exactly the same way
+ * as KDE 2.1+.
+ *
+ * Other cases: CDE, the common desktop environment. This is a
+ * commercial product that has been packaged with Sun (and other)
+ * workstations. It typically implements four virtual desktops but
+ * provides NO properties at all for apps such as xpenguins to use to
+ * work out where to draw to. Seeing as Sun are moving over to GNOME,
+ * CDE use is on the decline so I don't have any current plans to try
+ * and get xpenguins to work with it.
+ *
+ * As a note to developers of window managers and big screen hoggers
+ * like kdesktop, please visit www.freedesktop.org and implement their
+ * Extended Window Manager Hints spec that help pagers and apps like
+ * xpenguins and xearth to find their way around. In particular,
+ * please use the _NET_CURRENT_DESKTOP and _NET_VIRTUAL_ROOTS
+ * properties if you reparent any windows (e.g. Enlightenment). Since
+ * no window managers that I know yet use these particular hints, I
+ * haven't yet added any code to parse them.  */
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#include <string.h>
+
+/* Time to throw up. Here is a kludgey function that recursively calls
+ * itself (up to a limit) to find the window ID of the KDE desktop to
+ * draw on. It works with KDE 2.0, but since KDE 2.0 is less stable
+ * than Windows 95, I don't expect many people to remain using it now
+ * that 2.1 is available, which implements __SWM_VROOT and makes this
+ * function redundant. This is the hierarchy we're trying to traverse:
+ *
+ * -> The root window
+ * 0 -> window with name="KDE Desktop"
+ * 1   -> window with no name
+ * 2     -> window with name="KDE Desktop" & _NET_WM_WINDOW_TYPE_DESKTOP
+ * 3       -> window with no name and width >= width of screen
+ *
+ * The last window in the hierarchy is the one to draw to.  The
+ * numbers show the value of the `depth' argument.  */
+static
+Window
+__ToonGetKDEDesktop(Display *display, int screen, Window window,
+		    Atom atom, char *atomname, int depth)
+{
+  char *name = NULL;
+  unsigned char *wintype = NULL;
+  Window winreturn = 0;
+  unsigned long nitems, bytesafter;
+  Atom actual_type;
+  int actual_format;
+  Window rootReturn, parentReturn, *children;
+  unsigned int nChildren;
+  char go_deeper = 0;
+
+  if (XFetchName(display, window, &name)) {
+    if (strcasecmp(name, "KDE Desktop") == 0) {
+      /* Presumably either at depth 0 or 2 */
+      if (XGetWindowProperty(display, window, atom, 0, 1,
+			     False, XA_ATOM,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &wintype) == Success
+	  && wintype) {
+	char *tmpatomname = XGetAtomName(display, *(Atom*)wintype);
+	if (tmpatomname) {
+	  if (strcmp(atomname, tmpatomname) == 0 && depth == 2) {
+	    /* OK, at depth 2 */
+	    go_deeper = 1;
+	  }
+	  XFree((char *) tmpatomname);
+	}
+      }
+      else if (depth < 2) {
+	go_deeper = 1;
+      }
+    }
+    else if (depth == 1) {
+      go_deeper = 1;
+    }
+    XFree((char *) name);
+  }
+  else if (depth == 1) {
+    go_deeper = 1;
+  }
+
+  /* If go_deeper is 1 then there is a possibility that the background
+   * window is a descendant of the current window; otherwise we're
+   * barking up the wrong tree. */
+  if (go_deeper && XQueryTree(display, window, &rootReturn,
+			      &parentReturn, &children,
+			      &nChildren)) {
+    int i;
+    for (i = 0; i < nChildren; ++i) {
+      /* children[i] is now at depth 3 */
+      if (depth == 2) {
+	XWindowAttributes attributes;
+	if (XGetWindowAttributes(display, children[i], &attributes)) {
+	  if (attributes.width >= DisplayWidth(display, screen)/2
+	      && attributes.height > 0) {
+	    /* Found it! */
+	    winreturn = children[i];
+	    break;
+	  }
+	}
+      }
+      else if ((winreturn = __ToonGetKDEDesktop(display, screen,
+						children[i],
+						atom, atomname,
+						depth+1))) {
+	break;
+      }
+    }
+    XFree((char *) children);
+  }
+
+  return winreturn;
+}
+
+/* Look for the Nautilus desktop window to draw to, given the toplevel
+ * window of the Nautilus desktop. Basically recursively calls itself
+ * looking for subwindows the same size as the root window. */
+static
+Window
+__ToonGetNautilusDesktop(Display *display, int screen, Window window,
+			 int depth)
+{
+  Window rootReturn, parentReturn, *children;
+  Window winreturn = window;
+  unsigned int nChildren;
+
+  if (depth > 5) {
+    return ((Window) 0);
+  }
+  else if (XQueryTree(display, window, &rootReturn, &parentReturn,
+		 &children, &nChildren)) {
+    int i;
+    for (i = 0; i < nChildren; ++i) {
+      XWindowAttributes attributes;
+      if (XGetWindowAttributes(display, children[i], &attributes)) {
+	if (attributes.width == DisplayWidth(display, screen)
+	    && attributes.height == DisplayHeight(display, screen)) {
+	  /* Found a possible desktop window */
+	  winreturn = __ToonGetNautilusDesktop(display, screen,
+					       children[i], depth+1);
+	}
+      }
+    }
+    XFree((char *) children);
+  }
+  return winreturn;
+}
+
+
+/*
+ * Returns the window ID of the `background' window on to which the
+ * toons should be drawn. Also returned (in clientparent) is the ID of
+ * the parent of all the client windows, since this may not be the
+ * same as the background window. If no recognised virtual window
+ * manager or desktop environment is found then the root window is
+ * returned in both cases. The string toon_message contains
+ * information about the window manager that was found.
+ */
+Window
+ToonGetRootWindow(Display *display, int screen, Window *clientparent)
+{
+  Window background = 0; /* The return value */
+  Window root = RootWindow(display, screen);
+  Window rootReturn, parentReturn, *children;
+  unsigned char *toplevel = NULL;
+  unsigned int nChildren;
+  unsigned long nitems, bytesafter;
+  Atom actual_type;
+  int actual_format;
+  unsigned char *workspace = NULL;
+  unsigned char *desktop = NULL;
+  Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom(display,
+			   "NAUTILUS_DESKTOP_WINDOW_ID",
+						False);
+
+  *clientparent = root;
+
+  if (XGetWindowProperty(display, root,
+			 NAUTILUS_DESKTOP_WINDOW_ID,
+			 0, 1, False, XA_WINDOW,
+			 &actual_type, &actual_format,
+			 &nitems, &bytesafter,
+			 &toplevel) == Success
+      && toplevel) {
+    /* Nautilus is running */
+    background = __ToonGetNautilusDesktop(display, screen,
+					*(Window*)toplevel, 0);
+    XFree(toplevel);
+  }
+
+  /* Next look for a virtual root or a KDE Desktop */
+  if (!background
+      && XQueryTree(display, root, &rootReturn, &parentReturn,
+		    &children, &nChildren)) {
+    int i;
+    Atom _NET_WM_WINDOW_TYPE = XInternAtom(display,
+			     "_NET_WM_WINDOW_TYPE",
+					   False);
+    Atom __SWM_VROOT = XInternAtom(display, "__SWM_VROOT", False);
+
+    for (i = 0; i < nChildren && !background; ++i) {
+      unsigned char *newroot = NULL;
+      if (XGetWindowProperty(display, children[i],
+			     __SWM_VROOT, 0, 1, False, XA_WINDOW,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &newroot) == Success
+	  && newroot) {
+	/* Found a window with a __SWM_VROOT property that contains
+	 * the window ID of the virtual root. Now we must check
+	 * whether it is KDE (2.1+) or not. If it is KDE then it does
+	 * not reparent the clients. If the root window has the
+	 * _NET_SUPPORTED property but not the _NET_VIRTUAL_ROOTS
+	 * property then we assume it is KDE. */
+	Atom _NET_SUPPORTED = XInternAtom(display,
+					  "_NET_SUPPORTED",
+					  False);
+	unsigned char *tmpatom;
+	if (XGetWindowProperty(display, root,
+			       _NET_SUPPORTED, 0, 1, False,
+			       XA_ATOM, &actual_type, &actual_format,
+			       &nitems, &bytesafter,
+			       &tmpatom) == Success
+	    && tmpatom) {
+	  unsigned char *tmpwindow = NULL;
+	  Atom _NET_VIRTUAL_ROOTS = XInternAtom(display,
+						"_NET_VIRTUAL_ROOTS",
+						False);
+	  XFree(tmpatom);
+	  if (XGetWindowProperty(display, root,
+				 _NET_VIRTUAL_ROOTS, 0, 1, False,
+				 XA_WINDOW, &actual_type, &actual_format,
+				 &nitems, &bytesafter,
+				 &tmpwindow) != Success
+	      || !tmpwindow) {
+	    /* Must be KDE 2.1+ */
+	    background = *(Window*)newroot;
+	  }
+	  else if (tmpwindow) {
+	    XFree(tmpwindow);
+	  }
+	}
+
+	if (!background) {
+	  /* Not KDE: assume windows are reparented */
+	  background = *clientparent = *newroot;
+	}
+	XFree((char *) newroot);
+      }
+      else background = __ToonGetKDEDesktop(display, screen, children[i],
+						 _NET_WM_WINDOW_TYPE,
+						 "_NET_WM_WINDOW_TYPE_DESKTOP",
+						 0);
+    }
+    XFree((char *) children);
+  }
+
+  if (!background) {
+    /* Look for a _WIN_WORKSPACE property, used by Enlightenment */
+    Atom _WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
+    if (XGetWindowProperty(display, root, _WIN_WORKSPACE,
+			   0, 1, False, XA_CARDINAL,
+			   &actual_type, &actual_format,
+			   &nitems, &bytesafter,
+			   &workspace) == Success
+	&& workspace) {
+      /* Found a _WIN_WORKSPACE property - this is the desktop to look for.
+       * For now assume that this is Enlightenment.
+       * We're looking for a child of the root window that has an
+       * ENLIGHTENMENT_DESKTOP atom with a value equal to the root window's
+       * _WIN_WORKSPACE atom. */
+      Atom ENLIGHTENMENT_DESKTOP = XInternAtom(display,
+					       "ENLIGHTENMENT_DESKTOP",
+					       False);
+      /* First check to see if the root window is the current desktop... */
+      if (XGetWindowProperty(display, root,
+			     ENLIGHTENMENT_DESKTOP, 0, 1,
+			     False, XA_CARDINAL,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &desktop) == Success
+	  && desktop && *desktop == *workspace) {
+	/* The root window is the current Enlightenment desktop */
+	background = root;
+	XFree(desktop);
+      }
+      /* Now look at each immediate child window of root to see if it is
+       * the current desktop */
+      else if (XQueryTree(display, root, &rootReturn, &parentReturn,
+			  &children, &nChildren)) {
+	int i;
+	for (i = 0; i < nChildren; ++i) {
+	  if (XGetWindowProperty(display, children[i],
+				 ENLIGHTENMENT_DESKTOP, 0, 1,
+				 False, XA_CARDINAL,
+				 &actual_type, &actual_format,
+				 &nitems, &bytesafter,
+				 &desktop) == Success
+	      && desktop && *desktop == *workspace) {
+	    /* Found current Enlightenment desktop */
+	    background = *clientparent = children[i];
+	    XFree(desktop);
+	  }
+	}
+	XFree((char *) children);
+      }
+      XFree(workspace);
+    }
+  }
+  if (background) {
+    return background;
+  }
+  else {
+    return root;
+  }
+}