[Copyright status: this file is in the public domain.] There are three kinds of error handlers in the liblx API: X error handlers, library error handlers, and per-request error handlers. Error handling is much like event handling (compare to the event handling paradigm described in events.txt): every LX_CONN has an X error handler and a library error handler, and each LX_OP (see pending-operations.txt) has an optional request-specific error handler. By default, the X and library error handlers are set to handlers which just print complaints to stderr and exit(1); LX_OPs by default have no request-specific error handler. X errors are represented with LX_X_ERR. This is a discriminated union, a struct with a discriminant field and a union, with one member of the union for each possible value of the discriminant. In the case of LX_X_ERR, the discriminant is called type and is an LX_X_ERR_TYPE; the union is called u and has one arm for each error type. The discriminant value and the union arm are named after the error type; the LX_X_ERR_TYPE value is named LX_XE_ followed by the error name, whereas the struct-valued field holding the error-specific details is named with the lowercased error name. For example, a Value error (what Xlib calls BadValue) is represented as an LX_X_ERR with type set to LX_XE_Value and .u.value holding the Value-error-specific info. Library errors are similar, except the discriminated union type is LX_LIB_ERR, the discriminant type is LX_LIB_ERR_TYPE, the type values are called LX_LE_*, the specifics of the various possibilities are all different, and the LX_CONN usually is not usable afterwards. The default X and library error handlers just call lx_default_err, then print a complaint to stderr and exit(1). (The point of lx_default_err is that a single debugger breakpoint can trap both kinds of error.) Pending-operation-specific error handlers are a different case; they will be described later. The X error handler for a connection is set with lx_err_set_X, which returns the previous handler. A nil pointer as the handler pointer will be replaced with the default handler. The handler takes two arguments and returns LX_X_ERR_ACTION: LX_X_ERR_ACTION handler(LX_CONN *c, const LX_X_ERR *e) c is of course the relevant connection and e the error object. The return value can be LX_X_ERR_IGNORE Ignore the error. The application either has done or will do whatever is appropriate in response (which may be `nothing'); liblx will, more or less, carry on as if the error hadn't been received at all. LX_X_ERR_CRASH Print a complaint to stderr, giving bare-bones details of the error, and exit(1). The default X error handler is available under the name lx_default_X_err. The core error types are Access, Alloc, Atom, Colormap, Cursor, Drawable, Font, GContext, IDChoice, Implementation, Length, Match, Name, Pixmap, Request, Value, and Window. The library also has a pseudo-type, Other, which is used for errors whose type is not recognized. The library error handler for a connection is, similarly, set with lx_err_set_lib. The error handler takes two arguments (LX_CONN *, for the connection The library error handler is similar, but there are important differences. It is set with lx_err_set_lib, which returns the previous handler. A nil pointer as the handler pointer will be replaced with the default handler. The handler takes two arguments and returns nothing: void handler(LX_CONN *c, const LX_LIB_ERR *e) In general (there is one exception), if a library error handler returns, the library attempts to unwind far enough to return to its caller, but this is a best-effort attempt, and nothing is guaranteed; if the return succeeds, the LX_CONN usually will not be usable afterwards. Depending on the error, it may not even be safe to lx_close it. The error handler support is provided mostly for applications that want to do some close-down actions before exiting on catastrophic errors. The exception is that if a BAD_CALL handler (see below) returns, the offending call is ignored, as if it had never been made. The library error types, with their meanings, are: LX_LE_NOMEM Out of memory (typically, malloc() failed). LX_LE_SYSERR Unexpected error from a syscall, with errno. LX_LE_OSLIBERR Unexpected error from an OS library call, potentially with a message. LX_LE_BAD_CALL Some API function was called incorrectly. This is an application failure, not strictly a liblx failure. u.bad_call.fn is the name of the incorrectly-called API function. LX_LE_NO_DISPLAY lx_open was passed no display string and thre was no $DISPLAY in the environment. LX_LE_BAD_DISPLAY The display string to lx_open (either passed in or from $DISPLAY) was erroneous in some way. LX_LE_UNX_EOF The library got an unexpected EOF on the X connection. LX_LE_ALL_FAIL lx_open ran out of addresses to try without getting a connection. u.all_fail.msg has a possibly-helpful message. LX_LE_PROTO_ERR The library got an X protocol error. u.proto_err.msg briefly states the error. LX_LE_REJECTED The X server rejected the connection. u.rejected.msg is the message the server returned. Per-operation errors are rather different. They are designed for cases where certain protocol errors make more sense to reflect as API failures rather than normal errors. For example, a LookupColor request can generate Colormap and Name errors. It is usually more useful to treat a Name error as a failure of the call, but a Colormap error like any other X error. This would be handled by making the lx_LookupColor call, then attaching an error handler to the resulting LX_OP which handles Name errors but not Colormap errors. The per-operation error handler is called only when the last-request value in the error matches the operation's request value, which usually means that the error came from the request. The error handler is a handler function pointer and a void * argument. The handler takes four arguments and returns an int: int handler(LX_CONN *c, LX_OP *op, const LX_X_ERR *e, void *arg) c, op, and e are of course the connection, the operation, and the X error; arg is the void * argument. If the handler returns zero, this indicates that the error is not relevant to the request, or that it does not call for special handling; liblx processes the error as if no operation-specific handler had been registered. If the handler returns nonzero, this indicates that the error is relevant to the request and that the handler either has done or will do anything appropriate; liblx treats the request as having completed (including callback and drop processing - see pending-operations.txt). The handler will normally modify some data structure so whatever happens on operation completion will realize that the request completed with an error rather than with success, but nothing compels this; the handler can take whatever action, including no action, is appropriate to the case at hand. If the operation completes normally, its error handler is irrelevant. There are two calls to set an LX_OP's operation-specific error handler. They differ only in how the handler-and-argument are passed in: LX_OP_ERR_SETTING lx_op_err(LX_OP *op, int (*handler)(LX_CONN *, LX_OP *, const LX_X_ERR *, void *), void *arg) LX_OP_ERR_SETTING lx_op_err_setting(LX_OP *op, LX_OP_ERR_SETTING) LX_OP_ERR_SETTING is a struct with two members: int (*fn)(LX_CONN *, LX_OP *, const LX_X_ERR *, void *); void *arg; This exists so lx_op_err can return all the handler state in a single return value; for application code convenience, lx_op_err_setting is provided so handlers can also be passed in in the same form. A handler function pointer of nil semantically means "no handler"; in this case, the arg value is irrelevant. The default for a newly-created LX_OP is "no handler", ie, a nil handler pointer, with an indeterminate arg pointer. There is another thing which can be thought of as an error; see abort.txt for documentation on that. See extensions.txt for how extension errors are handled.