X11 programming in Haskell has traditionally been done using the X11 package. Many of these bindings were written by hand and, due to their reliance on FFI, are susceptible to some threading issues. xhb, a newer package, provides low-level bindings to X11 based on the same XML as xcb.

It doesn’t seem as though much has been done with XHB yet. In fact, at the time of writing (Aug 22, 2016), exactly two packages on Hackage depend on xhb. Nevertheless, I believe it hold great promise in the future of X programming in Haskell.

I’ve been working on a set of libraries that allows for layers of abstraction to be stacked on top of the low-level xhb bindings.

Here’s an overview of the libraries and their relationships:

  • xhb-monad - Monad transformer for xhb server connections.
  • xhb-requests - Classes and generated instances for a uniform interface to xhb.
  • xhb-event-queue - Monad transformer over xhb-monad for event handling.
  • xhb-mapping-state - Monad transformer over xhb-monad for keeping track of key, mod, and pointer mappings.
  • wmonad - Prototype of a window manager in the XMonad tradition to demonstrate these libraries.

xhb-requests

(source, docs)

Most generated functions in xhb have types similar to those below:

createWindow :: Connection -> CreateWindow -> IO ()
grabKeyboard :: Connection -> GrabKeyboard -> IO (Receipt GrabStatus)

Those requests which expect responses can wait for them as follows:

getReply :: Receipt a -> IO (Either SomeError a)

Some functions are a bit different (request parameters don’t live inside a dedicated type, but are passed directly to the function):

configureWindow :: Connection -> WINDOW -> ValueParam Word16 -> IO ()
queryTextExtents :: Connection -> FONTABLE -> [CHAR2B] -> IO (Receipt QueryTextExtentsReply)

Nevertheless, thanks to disciplined code generation, xhb has a clean and uniform code base. The (in progress) xhb-requests package wraps all of these generated request functions in two classes:

class Request a where
    requestIO :: a -> Connection -> IO ()

class RequestWithReply a b | a -> b, b -> a where
    requestWithReplyIO :: a -> Connection -> IO (IO (Either SomeError b))

The instances of this class are generated from the xhb code that is itself generated from XML, which sounds ugly, but it worked quite well. This is the foundation for xhb-monad.

xhb-monad

(source, docs)

This package defines the following class:

class Monad x => XContext x where
    request :: Request a => a -> Connection -> x ()
    requestWithReply :: RequestWithReply a b => a -> Connection -> x (x (Either SomeError b))
    awaitEvent :: Connection -> x SomeEvent

instance XContext IO where
    request = requestIO
    requestWithReply = requestWithReplyIO
    awaitEvent = waitForEvent

and the following monad transformer:

class (XContext x, Monad m) => MonadX x m | m -> x where
    liftX :: x a -> m a
    askX :: m Connection
    catchErrorX :: m a -> (SomeError -> m a) -> m a
    throwErrorX :: SomeError -> m a

newtype X m a = X { runX :: ReaderT Connection (ExceptT SomeError m) a }
    deriving (Functor, Applicative, Monad, MonadIO, Typeable)

instance XContext x => MonadX x (X x) where
    liftX = X . lift . lift
    askX = X ask
    catchErrorX m f = X $ catchError (runX m) (runX . f)
    throwErrorX = X . throwError

--- mtl stuff also
instance MonadFoo Bar where...

This abstraction allows for pure X logic. It also lets us isolate unwieldy layers of computation and bits of state in the usual mtl way. xhb-mapping-state is an example of this.