From f0ae45c446e4f001cec0a7d6b450133b019f03c5 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 18 Aug 2010 16:13:33 -0400 Subject: [PATCH] Add lots more detail to the xcb.h and xcbext.h API documentation Added another doxygen module for xcbext.h. For low-level type people likely to be using XCB, understanding xcbext.h is important. Copied a few lines from the protocol extension API wiki, http://xcb.freedesktop.org/ProtocolExtensionApi --- src/xcb.h | 134 ++++++++++++++++++++++++++----- src/xcbext.h | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 342 insertions(+), 40 deletions(-) diff --git a/src/xcb.h b/src/xcb.h index b5feb1d..399e3bc 100644 --- a/src/xcb.h +++ b/src/xcb.h @@ -72,7 +72,28 @@ extern "C" { /** * @brief XCB Connection structure. * - * A structure that contain all data that XCB needs to communicate with an X server. + * A structure that contain all data that XCB needs to communicate + * with an X server. + * + * Obtain an XCB Connection from xcb_connect() or + * if using Xlib, from XGetXCBConnection(). + * + * On an X server connection, there are only four kinds of message. + * The client sends requests. The server sends replies, errors, and + * events. Some requests have replies, while others (called "void" + * requests) have no reply. Any request (void or non-void) can + * trigger an error. Events can arrive at any time. + * + * Replies always arrive from the X server in the same order that + * requests were sent, so you will never request A then B and receive + * a reply to B then to A. XCB, however, allows you to block for the + * results of requests in any order - it keeps a client-side buffer of + * replies and you can collect the replies out of order. + * + * Each request has a sequence number one higher than the previous + * request. Errors and replies have the sequence number of the + * corresponding request. Events have the sequence number of the most + * recent request the server has processed. */ typedef struct xcb_connection_t xcb_connection_t; /**< Opaque structure containing all data that XCB needs to communicate with an X server. */ @@ -96,9 +117,9 @@ typedef struct { * A generic reply structure. */ typedef struct { - uint8_t response_type; /**< Type of the response */ + uint8_t response_type; /**< Type of the response (always 1 for replies) */ uint8_t pad0; /**< Padding */ - uint16_t sequence; /**< Sequence number */ + uint16_t sequence; /**< Sequence number (least-significant 16 bits only) */ uint32_t length; /**< Length of the response */ } xcb_generic_reply_t; @@ -106,13 +127,23 @@ typedef struct { * @brief Generic event. * * A generic event structure. + * + * The sequence number of an event will be the sequence number of the + * most recent request the server handled or was in the process of + * handling. + * + * The "sequence" field is the least-significant 16 bits which is what + * X protocol sends on the wire. + * + * The "full_sequence" field is XCB's expansion of the sequence number + * to include 16 more bits. XCB stores 64 bits internally. */ typedef struct { - uint8_t response_type; /**< Type of the response */ + uint8_t response_type; /**< Type of the response (indicates event type) */ uint8_t pad0; /**< Padding */ - uint16_t sequence; /**< Sequence number */ + uint16_t sequence; /**< Sequence number (least-significant 16 bits only) */ uint32_t pad[7]; /**< Padding */ - uint32_t full_sequence; /**< full sequence */ + uint32_t full_sequence; /**< Full sequence number (least-significant 32 bits only) */ } xcb_generic_event_t; /** @@ -120,33 +151,45 @@ typedef struct { * * An event as sent by the XGE extension. The length field specifies the * number of 4-byte blocks trailing the struct. + * + * The "sequence" field is the least-significant 16 bits which is what + * X protocol sends on the wire. + * + * The "full_sequence" field is XCB's expansion of the sequence number + * to include 16 more bits. XCB stores 64 bits internally. */ typedef struct { uint8_t response_type; /**< Type of the response */ uint8_t pad0; /**< Padding */ - uint16_t sequence; /**< Sequence number */ + uint16_t sequence; /**< Sequence number (least-significant 16 bits only) */ uint32_t length; uint16_t event_type; uint16_t pad1; uint32_t pad[5]; /**< Padding */ - uint32_t full_sequence; /**< full sequence */ + uint32_t full_sequence; /**< Full sequence number (least-significant 32 bits only) */ } xcb_ge_event_t; /** * @brief Generic error. * * A generic error structure. + * + * The "sequence" field is the least-significant 16 bits which is what + * X protocol sends on the wire. + * + * The "full_sequence" field is XCB's expansion of the sequence number + * to include 16 more bits. XCB stores 64 bits internally. */ typedef struct { - uint8_t response_type; /**< Type of the response */ + uint8_t response_type; /**< Type of the response (always 0 for errors) */ uint8_t error_code; /**< Error code */ - uint16_t sequence; /**< Sequence number */ + uint16_t sequence; /**< Sequence number (least-significant 16 bits only) */ uint32_t resource_id; /** < Resource ID for requests with side effects only */ uint16_t minor_code; /** < Minor opcode of the failed request */ uint8_t major_code; /** < Major opcode of the failed request */ uint8_t pad0; uint32_t pad[5]; /**< Padding */ - uint32_t full_sequence; /**< full sequence */ + uint32_t full_sequence; /**< Full sequence number (least-significant 32 bits only) */ } xcb_generic_error_t; /** @@ -155,7 +198,7 @@ typedef struct { * A generic cookie structure. */ typedef struct { - unsigned int sequence; /**< Sequence number */ + unsigned int sequence; /**< Sequence number (least-significant 32 bits) */ } xcb_void_cookie_t; @@ -200,6 +243,22 @@ typedef struct xcb_auth_info_t { * * Forces any buffered output to be written to the server. Blocks * until the write is complete. + * + * Most of the time, xcb_send_request() (or the protocol wrappers that + * send requests) will only buffer the request rather than writing it + * to the socket. XCB will flush implicitly if you force a reply to a + * request, or if its fixed-size buffer fills up, or if code such as + * Xlib calls xcb_take_socket(). + * + * Best practice is to xcb_flush() after a block of related requests, + * then allow your app to do other work, then later gather the + * replies to your requests. If you do not flush, the requests + * may not be sent until you go to gather the replies, defeating the + * purpose of sending the requests in advance. + * + * This function may shut down the connection (causing + * xcb_connection_has_error() to return true) if there is a write + * error. */ int xcb_flush(xcb_connection_t *c); @@ -256,14 +315,22 @@ xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c); /** * @brief Returns the next event or error from the server. * @param c: The connection to the X server. - * error status of the operation. - * @return The next event from the server. + * @return The next event from the server or NULL if none * * Returns the next event or error from the server, if one is * available, or returns @c NULL otherwise. If no event is available, that * might be because an I/O error like connection close occurred while * attempting to read the next event, in which case the connection is * shut down when this function returns. + * + * This function invokes read() at most once, so will not block + * if the connection socket is ready for reading. To avoid + * blocking you would need to use poll() or select() to check + * on the socket status prior to checking for an event. + * + * This function may shut down the connection (causing + * xcb_connection_has_error() to return true) if the socket + * is closed or there is a read error. */ xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c); @@ -290,9 +357,17 @@ xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c); * GetInputFocus request). This implicit non-void request is often * undesirable because it will result in a blocking round trip inside * xcb_request_check(). To avoid the round trip, send any request with - * a reply - use GetInputFocus if you have no useful request available - * - and then force that request at the same time you do the - * xcb_request_check(). + * a reply, and then force that request at the same time you do the + * xcb_request_check(). Use GetInputFocus if you have no useful + * request you want to make, since GetInputFocus has no side effects + * and should never result in an error. + * + * xcb_request_check() is identical to xcb_wait_for_reply() except that it + * will send the implicit non-void request for you, if necessary. + * + * This function may shut down the connection (causing + * xcb_connection_has_error() to return true) if the socket + * is closed or there is a read error. */ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t cookie); @@ -301,14 +376,16 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co * @param c: The connection to the X server. * @param sequence: The request sequence number from a cookie. * - * Discards the reply for a request. Additionally, any error generated + * Discards the reply for a request. Any error generated * by the request is also discarded (unless it was an _unchecked request * and the error has already arrived). * * This function will not block even if the reply is not yet available. * - * Note that the sequence really does have to come from an xcb cookie; - * this function is not designed to operate on socket-handoff replies. + * Note that the sequence really does have to come from an + * XCB-generated cookie; this function is not designed to operate on + * replies to requests sent using xcb_writev() after an + * xcb_take_socket(). */ void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence); @@ -394,8 +471,16 @@ int xcb_get_file_descriptor(xcb_connection_t *c); * connection is shut down and further operations on the * xcb_connection_t have no effect. * - * @todo Other functions should document the conditions in - * which they shut down the connection. + * The most common reason for an error is that the X server went away + * (crashed or shut down). Other possible reasons for an error include + * a KillClient request, or when using TCP, a network timeout. + * + * An error state on the connection corresponds to what Xlib calls an + * "IO error" as in XSetIOErrorHandler(). Errors caused by requests + * show up as events (see xcb_poll_for_event(), xcb_wait_for_event()) + * or they are returned when forcing the result of a request (see + * xcb_request_check() and xcb_wait_for_reply()). These errors + * do not set an error state on the connection. */ int xcb_connection_has_error(xcb_connection_t *c); @@ -482,6 +567,11 @@ xcb_connection_t *xcb_connect_to_display_with_auth_info(const char *display, xcb * * Allocates an XID for a new object. Typically used just prior to * various object creation functions, such as xcb_create_window. + * + * X protocol allocates object IDs on the client side to avoid having + * to make blocking round-trip requests to the server to create an + * ID. This means for example that you can create a window then send + * requests related to the window, all without waiting on any replies. */ uint32_t xcb_generate_id(xcb_connection_t *c); diff --git a/src/xcbext.h b/src/xcbext.h index 2e10ba2..48fceba 100644 --- a/src/xcbext.h +++ b/src/xcbext.h @@ -34,59 +34,271 @@ extern "C" { #endif +/** + * @file xcbext.h + */ + +/** + * @defgroup XCB_Protocol_Extension_API XCB Protocol Extension API + * @brief Protocol Extension API of the XCB library. + * + * The protocol extension API is public and has a fixed ABI, + * but normally it's only used by protocol wrappers, rather than + * by applications directly. + * + * @{ + */ + /* xcb_ext.c */ struct xcb_extension_t { - const char *name; - int global_id; + const char *name; /**< the X name of this extension, stored as a null-terminated string */ + + int global_id; /**< a small integer unique across all extensions in the current process + */ }; /* xcb_out.c */ +/** + * @brief Description of a request + * + * Description of a request, passed to xcb_send_request(). + */ typedef struct { - size_t count; - xcb_extension_t *ext; - uint8_t opcode; - uint8_t isvoid; + size_t count; /**< number of elements in the iovec array */ + xcb_extension_t *ext; /**< extension identifier, or null for the core protocol */ + uint8_t opcode; /**< major opcode of the request for the core protocol, or minor opcode for an extension */ + uint8_t isvoid; /**< zero if this request will return a reply; non-zero otherwise */ } xcb_protocol_request_t; +/** + * @brief Flags for a request + * + * Flags controlling how we send a request, passed to xcb_send_request() or xcb_take_socket(). + */ enum xcb_send_request_flags_t { XCB_REQUEST_CHECKED = 1 << 0, XCB_REQUEST_RAW = 1 << 1, XCB_REQUEST_DISCARD_REPLY = 1 << 2 }; +/** + * @brief Queues a request to send to the X server. + * @param c: The connection to the X server. + * @param flags: send flags from xcb_send_request_flags_t + * @param vector: data to send + * @param request: structure describing the request + * @return The sequence number of the request or 0 on error + * + * Generally invoked by a protocol wrapper (xcb_[request_name](), + * xcb_[request_name]_checked(), or + * xcb_[request_name]_unchecked()). This function queues a request + * into the outgoing buffer, or writes it immediately (possibly + * blocking) if the buffer is full. + * + * Call xcb_flush() to block and be sure the request has been written out. + * + * xcb_wait_for_reply() (in most cases) or xcb_request_check() (for + * checked calls to no-reply requests) would be used to collect the + * results of the request. The protocol wrapper for xcb_wait_for_reply() + * will be called xcb_[request_name]_reply(). + * + * The flag XCB_REQUEST_CHECKED specifies that any error from the request + * should be returned to xcb_wait_for_reply() or xcb_request_check() rather + * than delivered as an event. For void requests (those with no reply message) + * the default xcb_[request_name]() wrapper would be unchecked, while the + * alternative xcb_[request_name]_checked() wrapper gives an error reply. + * For non-void requests (those with a reply) the default + * xcb_[request_name]() wrapper is checked and xcb_[request_name]_unchecked() + * is not. + * + * xcb_request_check() should be used to safely collect the error if + * using XCB_REQUEST_CHECK on a void reply. + * + * The flag XCB_REQUEST_RAW allows you to prepare the request + * completely on your own. Normally, xcb_send_request() will fill in + * the extension op code, the length of the request, and the request + * opcode. xcb_send_request() will always fill in the sequence number + * even for raw requests. + * + * The flag XCB_REQUEST_DISCARD_REPLY is not used by protocol wrappers. + * If specified, the reply to a request will be thrown away + * rather than kept, so it will not be possible to get the reply using + * xcb_wait_for_reply(). Any errors will also be discarded. + * xcb_wait_for_reply() would return NULL for the reply and error + * in this case. + * + * Because xcb_send_request() may need to flush the socket (if the + * buffer is too small or full), it can trigger an IO error on write + * and shut down the connection. xcb_send_request() would then return + * zero. xcb_connection_has_error() would return true if the + * connection has been shut down. + */ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request); -/* xcb_take_socket allows external code to ask XCB for permission to +/** + * @brief Allows direct writes to the X socket using xcb_writev() + * @param c: The connection to the X server. + * @param return_socket: function to call when socket is lost + * @param closure: data to pass to return_socket + * @param flags: flags as for xcb_send_request() from xcb_send_request_flags_t + * @param sent: location to store most recent sequence number + * @return TRUE if the connection is still connected + * + * xcb_take_socket() allows external code to ask XCB for permission to * take over the write side of the socket and send raw data with - * xcb_writev. xcb_take_socket provides the sequence number of the last - * request XCB sent. The caller of xcb_take_socket must supply a - * callback which XCB can call when it wants the write side of the + * xcb_writev(). xcb_take_socket() provides the sequence number of the + * last request XCB sent. The caller of xcb_take_socket() must supply + * a callback which XCB can call when it wants the write side of the * socket back to make a request. This callback synchronizes with the - * external socket owner, flushes any output queues if appropriate, and - * then returns the sequence number of the last request sent over the - * socket. */ + * external socket owner and flushes any output queues if appropriate. + * + * Once you xcb_take_socket(), use xcb_writev() to send out requests. + * + * xcb_take_socket() will block in order to flush any queued requests + * (as with xcb_flush()), prior to returning. + * + * xcb_take_socket() fails and returns false if the connection is closed + * before it can be flushed. + * + * The XCB_REQUEST_RAW flag does not mean anything when passed to + * xcb_take_socket() because you will always be creating your own raw + * requests for use with xcb_writev(). XCB_REQUEST_CHECKED and + * XCB_REQUEST_DISCARD_REPLY have the same meaning as with + * xcb_send_request(). + * + * Because xcb_take_socket() needs to flush the socket, it may trigger + * an IO error on write and shut down the connection. + * xcb_take_socket() would then return false. + * xcb_connection_has_error() would return true if the connection has + * been shut down. + */ int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent); -/* You must own the write-side of the socket (you've called - * xcb_take_socket, and haven't returned from return_socket yet) to call - * xcb_writev. Also, the iovec must have at least 1 byte of data in it. - * */ +/** + * @brief write a request directly to the X socket + * @param c: The connection to the X server. + * @param vector: vector of buffers as with writev() system call + * @param count: number of vectors of buffers + * @param requests: number of requests to increment by + * @return TRUE if the connection is still connected + * + * To use xcb_writev(), you must own the write-side of the socket + * (you've called xcb_take_socket(), and haven't returned from your + * return_socket callback yet). + * + * The buffers passed in to xcb_writev() must have at least 1 byte of + * data in them. + * + * xcb_writev() blocks until all data has been written. It only fails + * and returns FALSE if the connection is disconnected. + * + * The "last written sequence number" stored by the xcb_connection_t will be + * incremented by the provided requests count. If you are writing whole + * requests at once, the requests parameter would just be the number of + * requests in the supplied buffer. + * + * If you are writing one request with multiple calls to xcb_writev(), + * only one of the xcb_writev() calls should increment the request + * counter. Note that if you send a request in pieces, you must flush + * the entire request when you lose the socket (i.e. your return_socket + * callback supplied to xcb_take_socket() is invoked). + * + * If xcb_writev() triggers a write error on the socket, it will + * return false and the connection will be in an error state, see + * xcb_connection_has_error(). + */ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t requests); /* xcb_in.c */ +/** + * @brief Returns the reply or error for a request. + * @param c: The connection to the X server. + * @param request: the sequence number of the request + * @param e: location in which to return an error, or NULL + * @return the reply, or NULL on error + * + * Blocks until a reply to the given request has arrived. Either a + * non-NULL reply will be returned, or an error will be returned, but + * not both. + * + * Pass NULL for the error parameter if you are not interested in the + * error object. + * + * If the XCB_DISCARD_REPLY flag was set when calling xcb_send_request(), + * this function returns NULL for both reply and error. + * + * If a request has no reply (i.e. the request is a void request), + * this function will block forever unless you have sent another + * request that does have a reply. xcb_request_check() will implicitly + * send such a non-void request on your behalf, but it would typically + * be more efficient to send it yourself at the same time that + * you send the void request. + * + * In most cases, this function would be used through a protocol + * wrapper. The protocol wrapper for xcb_wait_for_reply() will be + * called xcb_[request_name]_reply(). + * + * If xcb_wait_for_reply() triggers a read error on the socket, it + * will return NULL and the connection will be in an error state, see + * xcb_connection_has_error(). + */ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e); -int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error); +/** + * @brief Returns the reply or error for a request, if already known. + * @param c: The connection to the X server. + * @param request: the sequence number of the request + * @param reply: location in which to return the request's reply + * @param error: location in which to return an error, or NULL + * @return TRUE if the reply or error was available, FALSE if neither has arrived + * + * Checks buffered replies and errors for a reply or error + * corresponding to the given request. + * + * If this function returns true and XCB_DISCARD_REPLY was not set + * when calling xcb_send_request(), either the reply or error will be + * returned, but not both. The returned reply or error must be freed + * with free(). + * + * If this function returns false, neither the reply nor the error + * will be returned. + * + * If the XCB_DISCARD_REPLY flag was set when calling + * xcb_send_request(), this function returns NULL for both reply and + * error. XCB_DISCARD_REPLY would never be set by the protocol wrapper + * APIs. + * + * Pass NULL for the error parameter if you are not interested in the + * error object. + * + * Unlike xcb_poll_for_event(), this function does not call read() on + * the X server socket. The only way to fill the buffer is to invoke + * a blocking call (such as xcb_wait_for_reply()) or to invoke + * xcb_poll_for_event(). These may buffer replies as side effects. + */ +int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error); /* xcb_util.c */ +/** + * @brief compute number of bits set in a mask. + * @param mask: a 32-bit value + * @return the number of bits set + * + * Computes the number of bits in the binary representation of mask + * that are equal to 1. This is also called a "population count". + */ int xcb_popcount(uint32_t mask); +/** + * @} + */ + #ifdef __cplusplus } #endif -- 1.7.0.4