
On Sat, Aug 16, 2008 at 04:07:25PM -0500, Antoine Latter wrote:
Haskellers,
I'm slowly porting XCB (the X C Bindings) to Haskell, and I would like input from any interested parties.
The following is a summary of my plan so far. I'm interested in hearing any suggestions or concerns about what a Haskell library for writing X clients should look like. This is not a release announcement, and I can't make any promises about when this will be delivered.
Code is available in darcs: -- darcs get http://community.haskell.org/~aslatter/code/xhb
Some of the advantages XCB claims over xlib are: + smaller API + latency hiding when communicating with the X server + direct access to the X protocol + improved threading support + easier to extend
What I plan for the X Haskell Bindings (XHB) are as follows:
+ All requests to the server are non-blocking (under most circumstances)
+ Requests which produce a reply from the server return a "token" or "receipt"
+ The caller may then, at a time of their choosing, query the receipt for the response (or error) from the server. This query is blocking.
The API will look something like:
-- | Create a window as specified createWindow :: Connection -> CreateWindow -> IO ()
-- | Instruct the server that it should begin displaying the named window mapWindow :: Connection -> WINDOW -> IO ()
-- | List all of the extensions supported by the server listExtensions :: Connection -> IO (Receipt (ListExtensionsReply))
-- | Query a receipt for a response getReply :: Receipt a -> IO (Either XError a)
Note that the first two requests do not have replies, whereas the third request expects a reply from the server.
Since the request to create a window has so many parameters, these parameters are all wrapped up into a "CreateWindow" data type, which is only ever used by the "createWindow" function. The "mapWindow" request only has one parameter, so it does not need it's own "MapWindow" data type.
I think this is a nice idea. This type signature from the X11 library is absolutely unmanageable: createWindow :: Display -> Window -> Position -> Position -> Dimension -> Dimension -> CInt -> CInt -> WindowClass -> Visual -> AttributeMask -> Ptr SetWindowAttributes -> IO Window
What I don't have planned out is what to do with the stream of events and errors that come back from the server.
If an error is related to an outstanding receipt, it gets dumped there for the caller to examine directly. Other errors go into an error queue.
Events go into a similar event queue.
How should this queue be exposed in the API? Should the user of the library register an error/event callback?
registerErrorCallback :: Connection -> (XError -> IO ()) -> IO ()
Classic libX11 does this, and it is rather inconvenient to program with. Occasionally it is necessary to handle an error thrown by a specific request, yielding code like this: do h <- getHandler c -- save the old handler so we can restore it later setHandler c myHandlingFn performSomeActionsWhichMayFail setHandler c h Not only is this code ugly, it does not work correctly when the connection may be concurrently used by several threads.
Or is something like this enough:
pollForError :: Connection -> IO (Maybe (XError))
waitForError :: Connection -> IO XError
I think XCB's error system is particularly nice. Requests with no corresponding responses have two variants: unchecked (the default), and checked. Unchecked requests have a void return type, and any errors they generate go in the error queue. Checked requests return a cookie (the same mechanism used for requests with responses) which can be used to collect any errors generated by that response.
Each X extension defines its own set of errors and events (potentially). Should all of these be lumped together into one giant sum-type for errors and one for events?
Take care, Antoine
Cheers, Spencer Janssen