Some operations cannot complete immediately (the most common example is a request requiring a server round-trip). Something has to be done to deal with the delay. Xlib simply blocks. xcb has the application perform another call to wait for the response (I assume there's a way to test for completion, but I don't actually know). liblx usually uses a callback, though the application can poll instead. There are two general paradigms for requests: open-loop and response-expected. Which paradigm applies depends on the request. As an example of an open-loop request, consider MapWindow; as example of a response-expected request, consider QueryPointer. For open-loop requests, the call (eg, CreateWindow) returns the results, if any, immediately (eg, an LX_XID for CreateWindow, or nothing for Bell) and there is no way to tell when it has completed, except indirectly, such as by a later response-expected request's response arriving. (For applications that want to do such a thing deliberately, GetInputFocus is the canonical request; it forces a server round-trip and is cheap.) For response-expected requests, the call returns an LX_OP pointer, which can be passed to various calls, such as lx_op_callback, to do useful things with the pending request. LX_OP is an opaque type, used to represent such an operation. The calls for working with LX_OPs are: void lx_op_callback(LX_OP *op, void(*cb)(void *), void *cbarg, unsigned int flags) int lx_op_test(LX_OP *op) void lx_op_drop(LX_OP *op) void lx_op_abort(LX_OP *op) void lx_op_set_udata(LX_OP *op, void *udata) void *lx_op_udata(LX_OP *op) LX_OP_ERR_SETTING lx_op_err(LX_OP *op, int (*handler)(LX_CONN *, LX_OP *, const LX_X_ERR *, void *), void *) LX_OP_ERR_SETTING lx_op_err_setting(LX_OP *op, LX_OP_ERR_SETTING) lx_op_err and lx_op_err_setting are not described here; see the errors file for them. lx_op_callback -------------- void lx_op_callback(LX_OP *op, void(*cb)(void *), void *cbarg, unsigned int flags) This arranges for (*cb)(cbarg) to be called when op completes. flags holds flag bits; currently, there is only one flag defined: LX_OP_KEEP By default, once the LX_OP completes, it is dropped with the equivalent of lx_op_drop. If LX_OP_KEEP is set, the LX_OP remains valid. (To dispose of an LX_OP which is done but was kept this way, use lx_op_drop.) For example, to get a window's geometry, an application could do void have_geom(void *wgv) { LX_GET_GEOMETRY *wg; wg = wgv; ...use fields of wg... fre(wg); } { LX_GET_GEOMETRY *wg; ... wg = malloc(sizeof(*wg)); lx_op_callback(lx_GetGeometry(conn,win,&wg),&have_geom,wg,0); } lx_op_test ---------- int lx_op_test(LX_OP *op) This simply tests whether op is done (returns 1) or not (returns 0). It never blocks. lx_op_drop ---------- void lx_op_drop(LX_OP *op) This declares that the application does not want any explicit notification of op's completion. When op completes, it is simply discarded. (This also revokes any previous lx_op_callback call for the same LX_OP.) It does not otherwise affect the operation (compare and contrast with lx_op_abort); output values are still written where they should be. The LX_OP pointer must not be used further; from the application's point of view, it goes invalid at some indeterminate point after entry to lx_op_drop. lx_op_abort ----------- void lx_op_abort(LX_OP *op) This declares that the application no longer is interested in op at all. It is like lx_op_drop, but stronger; any output values op's request would normally write which have not already been written by the point lx_op_abort returns will not be written anywhere. It is like lx_op_drop with respect to the LX_OP pointer's validity. lx_op_set_udata, lx_op_udata --------------- ----------- void lx_op_set_udata(LX_OP *op, void *udata) void *lx_op_udata(LX_OP *op) Every LX_OP has a user data pointer associated with it. When the LX_OP is first created, this pointer is nil; it can be set with lx_op_set_udata and fetched with lx_op_udata. The library does not use this pointer in any other way; its use is entirely up to the application.