Re: [Xquartz-dev] X11 window appearance
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; }
(1) What are these inner_x, inner_y, inner_w, inner_h parameters in particular?
Those are the parameters of the "inner" window. That is, the contents. The "outer" window is what contains the inner window as well as all the decoration.
(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?
I think it should have no shadow. That's used for menus, tooltips, splash screens, etc.
(3) The second, the 'attribute' parameter seems clear: it looks like only 'AppleWMFrameActive' and '0' make sense here... ?
Yeah, that doesn't matter if there's no frame.
(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? :-)
it unmaps the window and reparents it to the root window.
In fact I would put this code to be executed on 'UnmapNotify' events.
Yeah. I didn't look at the code, but if you still need help, I'll be able to look at it on Monday.
Am 09/12/2009 07:09 PM, Jeremy Huddleston schrieb:
(1) What are these inner_x, inner_y, inner_w, inner_h parameters in particular?
Those are the parameters of the "inner" window. That is, the contents. The "outer" window is what contains the inner window as well as all the decoration.
Unfortunately I still don't grasp the idea... :-) I was in opinion these inner_? (and outer-?) parameters denote some rectangular area, with coordinates relative to the corresponding X11-window origin on the screen; therefore an area sized (w+2b, h+2b) at offset (-b,-b) exactly encloses the X11-window area including its border. But apparently this opinion is wrong.
(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?
I think it should have no shadow. That's used for menus, tooltips, splash screens, etc.
The applewm.h file lists these frame classes: AppleWMFrameClassDocument AppleWMFrameClassDialog AppleWMFrameClassModalDialog AppleWMFrameClassSystemModalDialog AppleWMFrameClassUtility AppleWMFrameClassToolbar AppleWMFrameClassMenu AppleWMFrameClassSplash AppleWMFrameClassBorderless and I originally took the last one. It appears each and everyone of these looks the same-shadowed after being rendered, I did try all of them in PaintShadow() in the aforementioned program and I could not notice any difference in the shadow-decorated window appearance. (They all look the way I wished them to look like.)
(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? :-)
it unmaps the window and reparents it to the root window.
Yes now I understand. The reparented window has an ID unknown to the CoreGraphics and so it doesn't get any shadow while re-mapping.
I didn't look at the code, but if you still need help, I'll be able to look at it on Monday.
Oh thanks for the offer... if you spot any bugs please let me know! :-) (To be serious, I would be glad to exactly understand these inner_? and outer_? parameters; as well as the AppleWMFrameClass??? intended purpose, at least to confirm this parameter is irrelevant to that small program.) Thanks again, Eeri Kask
On Sep 12, 2009, at 13:03, Eeri Kask wrote:
Oh thanks for the offer... if you spot any bugs please let me know! :-) (To be serious, I would be glad to exactly understand these inner_? and outer_? parameters; as well as the AppleWMFrameClass??? intended purpose, at least to confirm this parameter is irrelevant to that small program.)
The inner/outer is really simple once you understand how X11 builds its windows. It took me a while to get to that point, though... so hopefully I can spare you the trouble. Basically, X11 windows with the borders on them are hierarchical. You have one window (outer) which has some pretty widgets, title bar, etc (what the window manager says to draw). This window has a subwindow (inner) which contains all the contents (what the app itself says to draw). As for all the classes, those just correspond to the XP_FRAME_CLASS variables in /usr/include/Xplugin.h. libXplugin uses those to determine how to draw the window, how to tag it with metadata, etc. It looks to me like XP_FRAME_CLASS_SPLASH XP_FRAME_CLASS_BORDERLESS are identical as far as Xplugin is concerned... the distinction is just in quartz-wm... similarly XP_FRAME_CLASS_UTILITY XP_FRAME_CLASS_TOOLBAR XP_FRAME_CLASS_MENU seem to be identical as well. The difference between those is all in the frame size (none, small, large).
On Sep 12, 2009, at 16:03 , Eeri Kask wrote:
Unfortunately I still don't grasp the idea... :-) I was in opinion these inner_? (and outer-?) parameters denote some rectangular area, with coordinates relative to the corresponding X11-window origin on the screen; therefore an area sized (w+2b, h+2b) at offset (-b,-b) exactly encloses the X11-window area including its border. But apparently this opinion is wrong.
By convention a window is represented in terms of internal height and width, excluding the border; you do not use (-bw,-bw) to represent a window offset, but (0,0). The border width comes into play only when representing the window's position relative to its parent.
The applewm.h file lists these frame classes:
AppleWMFrameClassDocument AppleWMFrameClassDialog AppleWMFrameClassModalDialog AppleWMFrameClassSystemModalDialog AppleWMFrameClassUtility AppleWMFrameClassToolbar AppleWMFrameClassMenu AppleWMFrameClassSplash AppleWMFrameClassBorderless
and I originally took the last one. It appears each and everyone of these looks the same-shadowed after being rendered, I did try all of
I would expect these to differ in ways other than drop shadows, i.e. a dialog may not have a maximize button and is often centered in its parent window, while menus are usually placed at the pointer location. (So why does PaintShadow() support them? So you can use the same window type everywhere instead of having to use a different specifier for each API function.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Thanks Jeremy, Brandon, for the explanations, this did resolve the situation a bit.
By convention a window is represented in terms of internal height and width, excluding the border; you do not use (-bw,-bw) to represent a window offset, but (0,0). The border width comes into play only when representing the window's position relative to its parent.
Yes indeed, but only if considered in the X11-windows coordinate system convention. The CoreGraphics-one is apparently a little different. I am afraid I discovered a little tohuwabohu in this regard in Xplugin or whereever. In particular, in applewm.h one finds Bool XAppleWMFrameDraw (Display *dpy, int screen, Window window, unsigned int frame_class, unsigned int frame_attr, short inner_x, short inner_y, short inner_w, short inner_h, short outer_x, short outer_y, short outer_w, short outer_h, unsigned int title_length, const unsigned char * title_bytes); For our purposes, in points where it matters it seems it should read something like Bool XAppleWMFrameDraw (Display *dpy, int screen, Window window, unsigned int frame_class, unsigned int frame_attr, short inner_x, short inner_top_edge_measured_from_outer_top, short inner_w, short inner_h, short outer_x, short outer_y, short outer_w, short outer_top_edge_measured_from_canvas_bottom, unsigned int title_length, const unsigned char * title_bytes); It looks like the CoreGraphics subsystem treats the X11-window area (inclusively the border area!) as its drawing canvas in cartesian coordinate system, i.e. origin at bottom-left, and not in a text-editor coordinate system with origin at top-left. So, shifting "outer_top_edge_measured_from_bottom" one can move the title-bar arbitrarily vertically across the canvas, and by setting it to zero, the title-bar will run along the window bottom edge. The Xplugin (or whatever) seemingly does not look at "outer_y" at all, so nothing goes wrong in setting it beyond the canvas altogether. Completely different story is "outer_x", "outer_w", these denote a distance from the left canvas edge, and the "width" is "width", so only changing "outer_x" shifts the title-bar arbitrarily horizontally across the canvas, the rendered width of this title-bar remains the same. Then, it looks like we have the "inner" area in text-editor coordinates again, i.e. the "inner_y" is to be measured relatively to the "outer" rectangle top edge. This is intuitive, isn't it? :-) To recapitulate, the outer rectangle should have these coordinates in order to cover the X11-window plus border area: 0, 0, w+2b, h+2b-1 where "w+2b" is mathematical "length" as opposed to "h+2b-1" which is mathematical "distance", in integer coordinates. This all above works perfect for "usual" rectangular X11-windows and one has to ensure the "inner_y" parameter is zero, otherwise some part of the Aqua title-bar will appear. Though, for xeyes, oclock and probably other heavily X11-shaped top-level windows one has to set all "inner"-parameters to zero in this configuration in order to have them shadowed too by CoreGraphics.
The applewm.h file lists these frame classes:
AppleWMFrameClassDocument AppleWMFrameClassDialog AppleWMFrameClassModalDialog AppleWMFrameClassSystemModalDialog AppleWMFrameClassUtility AppleWMFrameClassToolbar AppleWMFrameClassMenu AppleWMFrameClassSplash AppleWMFrameClassBorderless
and I originally took the last one. It appears each and everyone of these looks the same-shadowed after being rendered, I did try all of
I would expect these to differ in ways other than drop shadows, i.e. a dialog may not have a maximize button and is often centered in its parent window, while menus are usually placed at the pointer location.
Yes I could confirm this. They differ in top-left buttons sizes and title-text height. Then, interestingly "AppleWMFrameClassBorderless" returns zero for title-bar height if checked by XAppleWMFrameGetRect(), but somebody does render the title-area including the three buttons plus title-text nevertheless!
(So why does PaintShadow() support them? So you can use the same window type everywhere instead of having to use a different specifier for each API function.)
Oh, PaintShadow() doesn't support them; it uses only "AppleWMFrameActive" if the client is focused, and zero otherwise for "inactive". Greetings, Eeri Kask
participants (3)
-
Brandon Allbery
-
Eeri Kask
-
Jeremy Huddleston