The way liblx operates interferes with various debugging operations. In particular, there is no good way to implement anything like a synchronous mode (an analog of what Xlib's XSynchronize turns on or off). (This is most obvious in that it would mean calling a libaio event loop from within a libaio callback.) But there is debugging assist of some other forms. This file describes it. To help deal with data structure corruption, or control-flow bugs, leading to use of unexpected values as resource IDs, there is support for liblx calling an application-provided routine for every resource ID provided to liblx by the application. lx_debug_trace, lx_debug_trace_setting -------------- ---------------------- LX_TRACE_SETTING lx_debug_trace(LX_CONN *conn, int (*fn)(void *, int, int, LX_XID, LX_ID_KIND), void *fnarg) LX_TRACE_SETTING lx_debug_trace_setting(LX_CONN *conn, LX_TRACE_SETTING setting) These set the debug trace function for conn and return the previous setting. LX_TRACE_SETTING is a struct typedef struct lx_trace_setting LX_TRACE_SETTING; struct lx_trace_setting { int (*fn)(void *, int, int, LX_XID, LX_ID_KIND); void *arg; } ; which encapsulates the function-and-argument into a single object, so it can be returned. The only difference lx_debug_trace and lx_debug_trace_setting is that the former takes separate arguments for the function and argument whereas the latter takes an LX_TRACE_SETTING. For each protocol request which takes one or more resource IDs, the trace callback is called. Each call looks like (*fn)(fnarg, req, n, id, kind) where arg is the void * from the lx_debug_trace call or from the LX_TRACE_SETTING; req is the request opcode (eg, LX_CORE_MapWindow for a call to lx_MapWindow - see below for extensions); n is 0 for the first such call for a given request, 1 for the second, etc; id is the LX_XID value in question; and kind is a value indicating what kind of resource ID id refer to (see below). Most requests have only a few resource IDs involved, but some have more; a few (eg, PolyText8) have a potentially very large number. To identify when a call is complete, after all the trace calls for a given request have been made, a call is made with -1 for n; in this call, id and kind are meaningless. This call with -1 for n is made even when the request is one with a wholly predictable number of IDs, such as MapWindow (which always involves exactly one ID). A call which doesn't take any resource IDs (eg, Bell) results in a single call with -1 for n, not no calls at all. For requests which take multiple resource IDs, there is no easy way to tell which call corresponds to which resource. For example, ReparentWindow takes two Window IDs (the window and the new parent), but the call to the trace callback does not identify which is which. For the major intended use case for debug tracing, identifying the call is enough; if there is desire for more detail, I may try to invent something of the sort. For calls that create and return a new resource, the new resource ID is never considered a resource argument for tracing purposes. The possible values for kind are: LX_ID_KIND_Any LX_ID_KIND_Colormap LX_ID_KIND_Cursor LX_ID_KIND_Drawable LX_ID_KIND_Font LX_ID_KIND_Fontable LX_ID_KIND_GC LX_ID_KIND_Pixmap LX_ID_KIND_Visual LX_ID_KIND_Window Most of these refer to the obvious resource type, but a few deserve special mention: - Any can refer to any resource time. It exists for requests, like KillClient, which can operate on any resource type (or at least most resource types). - A Drawable can refer to a Window or Pixmap. The name means, approximately, "anything which can support drawing". Most drawing requests, for example, use them. - A Fontable can refer to a Font or a GC. There are two core requests (QueryFont and QueryTextExtents) which can operate on either fonts or GCs. - A Visual is a somewhat special kind of resource in that they cannot be created or destroyed by clients; they are always created by the server itself. When an argument is a resource ID or one of a fixed set of alternatives (an example is lx_SetInputFocus, which among other things takes an argument which is a LX_XID naming a window or one of the values LX_FOCUS_None, LX_FOCUS_PointerWindow, or LX_FOCUS_InputFocus), the trace function is called even when the argument is one of the fixed alternatives; the ID value in this case is the API value in question (eg, LX_FOCUS_None), not the protocol value it gets mapped to. The kind value is still the kind for the resource alternative (eg, LX_ID_KIND_Window for the focus window). Atoms are not resources and LX_ATOM arguments are never treated as resources by debug tracing. The trace function's return value allows some degree of control over operation. If it returns zero, processing continues as normal. If it returns nonzero, an LX_LE_TRACE_ERR library error is generated, with its u.trace_err.val containing the nonzero return value; if the library error handler returns, the protocol request in question does not happen. (Using this to drop a protocol request made by a convenience function can, and usually will, break the convenience function in question.) A nil pointer for the handler function is semantically equivalent to a function which ignores all its arguments and always returns zero (as this implies, the fnarg pointer is ignored in this case). This is the default setting for a new LX_CONN. I haven't quite figured out how I want to handle extensions; until I do, tracing doesn't work at all for extension requests. To help debugging trace callbacks classify resource values, two functions are provided: lx_my_resource -------------- int lx_my_resource(LX_CONN *xc, LX_XID id) This returns nonzero iff id falls in xc's resource space. (That is, if id could be a resource created by this connection. Whether it actually is is not checked.) lx_not_resource --------------- int lx_not_resource(LX_XID id) This returns nonzero iff id cannot be a resource because it is out of range (resource IDs have a limited range). liblx uses such values for API replacements for special values; LX_FOCUS_None is an example. Note that this does not take an LX_CONN *; this test does not depend on any connection in any respect. Any LX_XID for which neither lx_my_resource nor lx_not_resource returns true is potentially a resource created by another client, or created internally by the server (an example of the latter is the root window of a screen). The reason trace checking is pushed off to the application is that it's inherently application-specific what resource IDs are reasonable. For example, an application that never interacts with other clients' resources at all except for creating children of a root window could use lx_my_resource() on everything, except that parent windows to CreateWindow may be the root. In contrast, an application that uses selections heavily will comparatively frequently be using others' windows in requests such as ChangeProperty. There are a few cases, such as resource IDs embedded in ClientMessage events passed SendEvent request, where tracing is unable to tell whether data constitutes a resource ID. In such cases, the possible IDs are not traced.