Document how to correctly wrap screen procedures

This adds a large comment to include/scrnintstr.h which should serve
to document the correct way to wrap any screen procedure, with a
particular focus on how to dynamically add/remove wrapping layers
while the server is running.

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
Keith Packard 2014-04-18 13:54:11 -07:00
parent 5eb77697ea
commit c75fee79ac

View File

@ -358,6 +358,96 @@ typedef WindowPtr (*XYToWindowProcPtr)(ScreenPtr pScreen,
typedef int (*NameWindowPixmapProcPtr)(WindowPtr, PixmapPtr, CARD32);
/* Wrapping Screen procedures
There are a few modules in the X server which dynamically add and
remove themselves from various screen procedure call chains.
For example, the BlockHandler is dynamically modified by:
* xf86Rotate
* miSprite
* composite
* render (for animated cursors)
Correctly manipulating this chain is complicated by the fact that
the chain is constructed through a sequence of screen private
structures, each holding the next screen->proc pointer.
To add a module to a screen->proc chain is fairly simple; just save
the current screen->proc value in the module screen private
and store the module's function in the screen->proc location.
Removing a screen proc is a bit trickier. It seems like all you
need to do is set the screen->proc pointer back to the value saved
in your screen private. However, if some other module has come
along and wrapped on top of you, then the right place to store the
previous screen->proc value is actually in the wrapping module's
screen private structure(!). Of course, you have no idea what
other module may have wrapped on top, nor could you poke inside
its screen private in any case.
To make this work, we restrict the unwrapping process to happen
during the invocation of the screen proc itself, and then we
require the screen proc to take some care when manipulating the
screen proc functions pointers.
The requirements are:
1) The screen proc must set the screen->proc pointer back to the
value saved in its screen private before calling outside its
module.
2a) If the screen proc wants to be remove itself from the chain,
it must not manipulate screen->proc pointer again before
returning.
2b) If the screen proc wants to remain in the chain, it must:
2b.1) Re-fetch the screen->proc pointer and store that in
its screen private. This ensures that any changes
to the chain will be preserved.
2b.2) Set screen->proc back to itself
One key requirement here is that these steps must wrap not just
any invocation of the nested screen->proc value, but must nest
essentially any calls outside the current module. This ensures
that other modules can reliably manipulate screen->proc wrapping
using these same rules.
For example, the animated cursor code in render has two macros,
Wrap and Unwrap.
#define Unwrap(as,s,elt) ((s)->elt = (as)->elt)
Unwrap takes the screen private (as), the screen (s) and the
member name (elt), and restores screen->proc to that saved in the
screen private.
#define Wrap(as,s,elt,func) (((as)->elt = (s)->elt), (s)->elt = func)
Wrap takes the screen private (as), the screen (s), the member
name (elt) and the wrapping function (func). It saves the
current screen->proc value in the screen private, and then sets the
screen->proc to the local wrapping function.
Within each of these functions, there's a pretty simple pattern:
Unwrap(as, pScreen, UnrealizeCursor);
// Do local stuff, including possibly calling down through
// pScreen->UnrealizeCursor
Wrap(as, pScreen, UnrealizeCursor, AnimCurUnrealizeCursor);
The wrapping block handler is a bit different; it does the Unwrap,
the local operations and then only re-Wraps if the hook is still
required. Unwrap occurrs at the top of each function, just after
entry, and Wrap occurrs at the bottom of each function, just
before returning.
*/
typedef struct _Screen {
int myNum; /* index of this instance in Screens[] */
ATOM id;