[PATCH 1/5] Document how to correctly wrap screen procedures

Keith Packard keithp at keithp.com
Fri Apr 18 14:31:16 PDT 2014

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 at keithp.com>
 include/scrnintstr.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/include/scrnintstr.h b/include/scrnintstr.h
index 6acdadd..cc6e596 100644
--- a/include/scrnintstr.h
+++ b/include/scrnintstr.h
@@ -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;

More information about the xorg-devel mailing list