Am 09/01/2009 06:21 PM, Jeremy Huddleston schrieb:
Here is what I meant by the shadow-creator 'daemon': catch MapNotify, UnmapNotify, and maybe ConfigureNotify events for all top-level X11-clients,
That's what a WM is.
and then ask CoreGraphics to put or remove shadow around these.
yeah, that's part of the window frame drawing code in libXplugin.
--- Initially I meant to suggest to extract exactly this part from quartz-wm into a separate program, for everybody's use, regardless if some window manager is running or not. :-)
Let me present the effort of recent few spare evenings on that topic and ask for some further help while finalising/polishing. :-) I have created a really small program to be started from .xinitrc into the background and which has to do more or less only this. (1) Catch the abovementioned X11 events and put the usual Aqua-like shadow around X11-windows. (2) This program has not to rely on something else like some other window manager be in use, i.e. it has to run on plain X11-server as well as while some common window manager is in use like /usr/X11/bin/twm or whatever. (3) Therefore this program has to take the X11-window as it is regarding any existing decorations: it has not to rely on anything at top-level in that respect; e.g. the title bar, if any, has to be provided by some entity as part of the top-level window. First I studied the Xplugin to use but quickly found out, there exists the "Apple-WM" X11-extension which could probably be utilised by that small program; and it appears mostly it is only the XAppleWMFrameDraw() function which does most of the work. In the following may I explain what this program does in particular. First, while starting it does not scan for all top-level windows and put shadow around these. This is intentional, as so one can exclude some clients from being shadowed by putting these in front of this client into .xinitrc. Second, we attempt to track focus (a la "active-window") and utilise the "AppleWMFrameActive" parameter to XAppleWMFrameDraw() to emphasize the client which has focus. Though, if the server is in PointerRoot mode we cannot catch FocusChange events, so the simplest alternative in this case is to track Enter/Leave events and emphasize the client with the mouse inside. Then, there is the "-a" command-line parameter which turns off this "active-window" chasing altogether, which means all clients get the "inactive" shadowing which isn't changed later. There are few aspects concerning the abovementioned XAppleWMFrameDraw() function though, which I have't quite understood yet due to very sparse documentation: (1) What are these inner_x, inner_y, inner_w, inner_h parameters in particular? I observed the CoreGraphics is pretty robust in that respect, e.g. if setting all to zero, in most cases (i.e. usual rectangular windows) nothing unusual can be observed regarding the shadow the CoreGraphics subsystem draws. Exeptions are xeyes and oclock, while having a shaped top-level window, they get undefined shadows if the abovementioned call parameters do not exactly match 0, 0, w, h where w and h are the window width/height without counting the area of the border (border in the sense of the X11-window 'attribute'). Though, it seems the CoreGraphics draws the shadow by taking that border area into account! (I expected these above arguments to be -1, -1, w+2b, h+2b where 'b' is the border_width; but this screwed the CoreGraphics the same way if called with all zeros for xeyes and oclock.) (2) Not knowing any better I chose 'AppleWMFrameClassBorderless' as this seemed most appropriate to denote the CoreGraphics not to put any of the usual Aqua title-bars, drag-knobs, its own border, etc around the final result. In fact, what does this 'class' parameter like 'AppleWMFrameClassDocument' denote, regarding the shadow drawing, if it affects anything at all? (3) The second, the 'attribute' parameter seems clear: it looks like only 'AppleWMFrameActive' and '0' make sense here... ? (4) Most important unresolved issue is termination: how to withdraw the shadows by means of Apple-WM extension? In particular, if quartz-wm is started manually, and terminated by Ctrl-C, then it withdraws all shadows. How does it do that? :-) In fact I would put this code to be executed on 'UnmapNotify' events. Greetings, and huge thanks in advance for any comments and clarifications, Eeri Kask /* gcc -O1 -o ${HBIN:-.}/applewm_xshadow AppleWM_xshadow.c -L/usr/X11R6/lib -lX11 -lXmu -lAppleWM */ #include <stdio.h> #include <time.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <X11/Xlib.h> #include <X11/Xmu/Xmu.h> #if defined (__MACH__) && defined (__APPLE__) #include <X11/extensions/applewm.h> #endif static Display * dsp = 0; static int scr = 0; static void term (int sig) { if (dsp) XCloseDisplay (dsp); exit (0); } static int xerrors (Display *d, XErrorEvent *e) { time_t moment = time (NULL); fprintf (stderr, "AppleWM_xshadow.c: %s", asctime(localtime(&moment))); XmuPrintDefaultErrorMessage (d, e, stderr); fflush (stderr); return 0; } static void PaintShadow (Window win, int a) { Window r; int x, y; unsigned w, h, b, d; if (XGetGeometry(dsp, win, &r, &x, &y, &w, &h, &b, &d)) { #if defined (__MACH__) && defined (__APPLE__) XAppleWMFrameDraw (dsp, scr, win, AppleWMFrameClassBorderless, (a ? AppleWMFrameActive : 0), /*inner*/ 0, 0, w, h, /*outer*/ 0, 0, w, h, /*title*/ 0, NULL); #else XSetWindowBorderWidth (dsp, win, (a ? 2 : 1)); /* event testing on plain X11; else don't use it */ #endif } } int main (int argc, char *argv[]) { dsp = XOpenDisplay (NULL); if (dsp) { int i, j; signal (SIGINT, (void(*)(int))(term)); signal (SIGTERM, (void(*)(int))(term)); #if defined (__MACH__) && defined (__APPLE__) if (True == XAppleWMQueryExtension(dsp, &i, &j)) #endif { int ffm, a; Window root; if (argc > 1 && strcmp (argv[1], "-a") == 0) a = 0; /* disable 'active-client' tracking */ else a = 1; XGetInputFocus (dsp, &root, &i); if (root == PointerRoot) ffm = 1; /* initialise 'focus-follows-mouse' */ else ffm = 0; scr = XDefaultScreen (dsp); root = XDefaultRootWindow (dsp); XSelectInput (dsp, root, (SubstructureNotifyMask | (a ? FocusChangeMask : 0))); XSetErrorHandler (xerrors); for (;;) { XEvent e; XNextEvent (dsp, &e); switch (e.type) { case MapNotify: PaintShadow (e.xmap.window, 0); if (a) XSelectInput (dsp, e.xmap.window, (EnterWindowMask | LeaveWindowMask | FocusChangeMask)); break; case UnmapNotify: XSelectInput (dsp, e.xunmap.window, None); /* here 'failed-request-error' if client already destroyed */ XSync (dsp, False); while (True == XCheckWindowEvent (dsp, e.xunmap.window, ~0, &e)) { continue; /* discard pending FocusOut, LeaveNotify, DestroyNotify, etc */ } break; case EnterNotify: if (ffm) { if ((e.xcrossing.mode == NotifyNormal && e.xcrossing.detail != NotifyInferior) || e.xcrossing.mode == NotifyGrab || e.xcrossing.mode == NotifyUngrab) PaintShadow (e.xcrossing.window, 1); } break; case LeaveNotify: if (ffm) { if ((e.xcrossing.mode == NotifyNormal && e.xcrossing.detail != NotifyInferior) || e.xcrossing.mode == NotifyGrab || e.xcrossing.mode == NotifyUngrab) PaintShadow (e.xcrossing.window, 0); } break; case FocusIn: if (e.xfocus.detail != NotifyPointer) { if (e.xfocus.window == root) { if (e.xfocus.detail == NotifyPointerRoot) ffm = 1; } else { if (((e.xfocus.mode == NotifyNormal || e.xfocus.mode == NotifyWhileGrabbed) && e.xfocus.detail != NotifyInferior) || e.xfocus.mode == NotifyGrab || e.xfocus.mode == NotifyUngrab) { PaintShadow (e.xfocus.window, 1); ffm = 0; } } } break; case FocusOut: if (e.xfocus.detail != NotifyPointer) { if (e.xfocus.window != root) { if (((e.xfocus.mode == NotifyNormal || e.xfocus.mode == NotifyWhileGrabbed) && e.xfocus.detail != NotifyInferior) || e.xfocus.mode == NotifyGrab || e.xfocus.mode == NotifyUngrab) { PaintShadow (e.xfocus.window, 0); } } } break; } } } XCloseDisplay (dsp); } return 1; }